Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | update rl_json to version 0.11.0 |
---|---|
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA1: |
1a97144602fe5f2236bb739a8e5ebb43 |
User & Date: | chw 2020-02-10 09:19:41.201 |
Context
2020-02-10
| ||
10:28 | tweak rl_json for Win32 builds check-in: 428dbafaa6 user: chw tags: trunk | |
09:21 | merge with trunk check-in: 54008bb933 user: chw tags: wtf-8-experiment | |
09:19 | update rl_json to version 0.11.0 check-in: 1a97144602 user: chw tags: trunk | |
06:45 | add tk upstream changes check-in: a211db3b97 user: chw tags: trunk | |
Changes
Added assets/rl_json0.11/pkgIndex.tcl.
> > | 1 2 | package ifneeded rl_json 0.11.0 \ [list load librl_json[info sharedlibextension] rl_json] |
Deleted assets/rl_json0.9.13/pkgIndex.tcl.
|
| < < |
Changes to jni/rl_json/Android.mk.
︙ | ︙ | |||
19 20 21 22 23 24 25 26 27 28 29 | LOCAL_C_INCLUDES := $(tcl_includes) $(tk_includes) $(LOCAL_PATH)/generic LOCAL_EXPORT_C_INCLUDES := $(LOCAL_C_INCLUDES) LOCAL_SRC_FILES := \ generic/parser.c \ generic/rl_json.c \ generic/rl_jsonStubInit.c LOCAL_CFLAGS := $(tcl_cflags) $(tk_cflags) \ -DPACKAGE_NAME="\"rl_json\"" \ | > > > | > | 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | LOCAL_C_INCLUDES := $(tcl_includes) $(tk_includes) $(LOCAL_PATH)/generic LOCAL_EXPORT_C_INCLUDES := $(LOCAL_C_INCLUDES) LOCAL_SRC_FILES := \ generic/parser.c \ generic/rl_json.c \ generic/json_types.c \ generic/dedup.c \ generic/api.c \ generic/rl_jsonStubInit.c LOCAL_CFLAGS := $(tcl_cflags) $(tk_cflags) \ -DPACKAGE_NAME="\"rl_json\"" \ -DPACKAGE_VERSION="\"0.11.0\"" \ -DTIP445_SHIM=1 -DDEDUP=1 \ -O2 LOCAL_SHARED_LIBRARIES := libtcl LOCAL_LDLIBS := -llog include $(BUILD_SHARED_LIBRARY) |
Changes to jni/rl_json/Makefile.in.
︙ | ︙ | |||
199 200 201 202 203 204 205 | #======================================================================== # Your doc target should differentiate from doc builds (by the developer) # and doc installs (see install-doc), which just install the docs on the # end user machine when building from source. #======================================================================== doc: | | | < | | 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 | #======================================================================== # Your doc target should differentiate from doc builds (by the developer) # and doc installs (see install-doc), which just install the docs on the # end user machine when building from source. #======================================================================== doc: doc/json.html: cd doc && man2html -r json.n > json.html install: all install-binaries install-libraries install-doc install-binaries: binaries install-lib-binaries install-bin-binaries #======================================================================== # This rule installs platform-independent files, such as header files. |
︙ | ︙ |
Changes to jni/rl_json/README.md.
︙ | ︙ | |||
26 27 28 29 30 31 32 | dictionary which hold the values to interpolate. When interpolating from variables in the current scope, they name scalar or array variables which hold the values to interpolate. In either case if the named key or variable doesn't exist, a JSON null is interpolated in its place. Quick Reference --------------- | | < < | < < | | > > > > > > > > > > > > | | | | | | < | < | > > | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 | dictionary which hold the values to interpolate. When interpolating from variables in the current scope, they name scalar or array variables which hold the values to interpolate. In either case if the named key or variable doesn't exist, a JSON null is interpolated in its place. Quick Reference --------------- * [json get *json_val* ?*key* ...?] - Extract the value of a portion of the *json_val*, returns the closest native Tcl type (other than JSON) for the extracted portion. * [json extract *json_val* ?*key* ...?] - Extract the value of a portion of the *json_val*, returns the JSON fragment. * [json exists *json_val* ?*key* ...?] - Tests whether the supplied key path resolve to something that exists in *json_val* * [json set *json_variable_name* ?*key* ...? *value*] - Updates the JSON value stored in the variable *json_variable_name*, replacing the value referenced by *key* ... with the JSON value *value*. * [json unset *json_variable_name* ?*key* ...?] - Updates the JSON value stored in the variable *json_variable_name*, removing the value referenced by *key* ... * [json normalize *json_val*] - Return a "normalized" version of the input *json_val* - all optional whitespace trimmed. * [json template *json_val* ?*dictionary*?] - Return a JSON value by interpolating the values from *dictionary* into the template, or from variables in the current scope if *dictionary* is not supplied, in the manner described above. * [json string *value*] - Return a JSON string with the value *value*. * [json number *value*] - Return a JSON number with the value *value*. * [json boolean *value*] - Return a JSON boolean with the value *value*. Any of the forms accepted by Tcl_GetBooleanFromObj are accepted and normalized. * [json object ?*key* *value* ?*key* *value* ...??] - Return a JSON object with the keys and values specified. *value* is a list of two elements, the first being the type {string, number, boolean, null, object, array, json}, and the second being the value. * [json object *packed_value*] - An alternate syntax that takes the list of keys and values as a single arg instead of a list of args, but is otherwise the same. * [json array ?*elem* ...?] - Return a JSON array containing each of the elements given. *elem* is a list of two elements, the first being the type {string, number, boolean, null, object, array, json}, and the second being the value. * [json foreach *varlist1* *json_val1* ?*varlist2* *json_val2* ...? *script*] - Evaluate *script* in a loop in a similar way to the [foreach] command. In each iteration, the values stored in the iterator variables in *varlist* are the JSON fragments from *json_val*. Supports iterating over JSON arrays and JSON objects. In the JSON object case, *varlist* must be a two element list, with the first specifiying the variable to hold the key and the second the value. In the JSON array case, the rules are the same as the [foreach] command. * [json lmap *varlist1* *json_val1* ?*varlist2* *json_val2* ...? *script*] - As for [json foreach], except that it is collecting - the result from each evaluation of *script* is added to a list and returned as the result of the [json lmap] command. If the *script* results in a TCL_CONTINUE code, that iteration is skipped and no element is added to the result list. If it results in TCL_BREAK the iterations are stopped and the results accumulated so far are returned. * [json amap *varlist1* *json_val1* ?*varlist2* *json_val2* ...? *script*] - As for [json lmap], but the result is a JSON array rather than a list. If the result of each iteration is a JSON value it is added to the array as-is, otherwise it is converted to a JSON string. * [json omap *varlist1* *json_val1* ?*varlist2* *json_val2* ...? *script*] - As for [json lmap], but the result is a JSON object rather than a list. The result of each iteration must be a dictionary (or a list of 2n elements, including n = 0). Tcl_ObjType snooping is done to ensure that the iteration over the result is efficient for both dict and list cases. Each entry in the dictionary will be added to the result object. If the value for each key in the iteration result is a JSON value it is added to the array as-is, otherwise it is converted to a JSON string. * [json isnull *json_val* ?*key* ...?] - Return a boolean indicating whether the named JSON value is null. * [json type *json_val* ?*key* ...?] - Return the type of the named JSON value, one of "object", "array", "string", "number", "boolean" or "null". * [json length *json_val* ?*key* ...?] - Return the length of the of the named JSON array, number of entries in the named JSON object, or number of characters in the named JSON string. Other JSON value types are not supported. * [json keys *json_val* ?*key* ...?] - Return the keys in the of the named JSON object, found by following the path of *key*s. * [json pretty *json_val*] - Returns a pretty-printed string representation of *json_val*. Useful for debugging or inspecting the structure of JSON data. * [json decode *bytes* ?*encoding*?] - Decode the binary *bytes* into a character string according to the JSON standards. The optional *encoding* arg can be one of *utf-8*, *utf-16le*, *utf-16be*, *utf-32le*, *utf-32be*. The encoding is guessed from the BOM (byte order mark) if one is present and *encoding* isn't specified. * [json valid ?*-extensions* *extensionlist*? ?*-details* *detailsvar*? *json_val*] - Return true if *json_val* conforms to the JSON grammar with the extensions in *extensionlist*. Currently only one extension is supported: *comments*, and is the default. To reject comments, use *-extensions {}*. If *-details detailsvar* is supplied and the validation fails, the variable *detailsvar* is set to a dictionary with the keys *errmsg*, *doc* and *char_ofs*. *errmsg* contains the reason for the failure, *doc* contains the failing json value, and *char_ofs* is the character index into *doc* of the first invalid character. Paths ----- The commands [json get], [json extract], [json set], [json unset] and [json exists] accept a path specification that names some subset of the supplied *json_val*. The rules are similar to the equivalent concept in the [dict] command, except that the paths used by [json] allow indexing into JSON arrays by the integer key (or a string matching the regex "^end(-[0-9]+)?$"). If a path to [json set] includes a key within an object that doesn't exist, it and all later elements of the path are created as nested keys into (new) objects. If a path element into an array is outside the current bounds of the array, it resolves to a JSON null (for [json get], [json extract], [json exists]), or appends or prepends null elements to resolve the path (for [json set], or does nothing ([json unset]). ~~~tcl json get { { "foo": [ { "name": "first" }, { "name": "second" }, { "name": "third" } } } } foo end-1 name ~~~ Returns "second" Properly Interpreting JSON from Other Systems --------------------------------------------- Rl_json operates on characters, not bytes, and so considerations of encoding are strictly out of scope. However, interoperating with other systems properly in a way that conforms to the standards is a bit tricky, and requires support for encodings Tcl currently doesn't natively support, like utf-32be. encodings Tcl currently doesn't natively support, like utf-32be. To ease this burden and take care of things like replacing broken encoding sequences, the [json decode] subcommand is provided. Using it in an application would look something like: ~~~tcl proc readjson file { set h [open $file rb] ;# Note that the file is opened in binary mode try { json decode [read $h] } finally { close $h } } ~~~ If the encoding is known via some out-of-band channel (like headers in an HTTP response), it can be supplied to override the BOM-based detection. The supported encodings are those listed in the JSON standards: utf-8 (the default), utf-16le, utf-16be, utf-32le and utf-32be. Examples -------- ## Creating a document from a template Produce a JSON value from a template: ~~~tcl json template { { "thing1": "~S:val1", "thing2": ["a", "~N:val2", "~S:val2", "~B:val2", "~S:val3", "~L:~S:val1"], |
︙ | ︙ | |||
95 96 97 98 99 100 101 102 103 104 105 106 107 108 | } } ~~~ Result: ~~~json {"thing1":"hello","thing2":["a",1000000.0,"1e6",true,null,"~S:val1"],"subdoc1":{"thing3":"~S:val1"},"subdoc2":{"thing3":"hello"}} ~~~ ## Performance Good performance was a requirement for rl_json, because it is used to handle large volumes of data flowing to and from various JSON based REST apis. It's generally the fastest option for working with JSON values in Tcl from the options I've tried, with the next closest being yajltcl. These benchmarks | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 | } } ~~~ Result: ~~~json {"thing1":"hello","thing2":["a",1000000.0,"1e6",true,null,"~S:val1"],"subdoc1":{"thing3":"~S:val1"},"subdoc2":{"thing3":"hello"}} ~~~ ## Construct a JSON array from a SQL result set ~~~tcl # Given: # sqlite> select * from languages; # 'Tcl',1,'http://core.tcl-lang.org/' # 'Node.js',1,'https://nodejs.org/' # 'Python',1,'https://www.python.org/' # 'INTERCAL',0,'http://www.catb.org/~esr/intercal/' # 'Unlambda',0,NULL set langs {[]} sqlite3 db languages.sqlite3 db eval { select rowid, name, active, url from languages } { if {$url eq ""} {unset url} json set langs end+1 [json template { { "id": "~N:rowid", "name": "~S:name", "details": { "active": "~B:active", // Template values can be nested anywhere "url": "~S:url" /* Both types of comments are allowed but stripped at parse-time */ } } }] } puts [json pretty $langs] ~~~ Result: ~~~json [ { "id": 1, "name": "Tcl", "details": { "active": true, "url": "http://core.tcl-lang.org/" } }, { "id": 2, "name": "Node.js", "details": { "active": true, "url": "https://nodejs.org/" } }, { "id": 3, "name": "Python", "details": { "active": true, "url": "https://www.python.org/" } }, { "id": 4, "name": "INTERCAL", "details": { "active": false, "url": "http://www.catb.org/~esr/intercal/" } }, { "id": 5, "name": "Unlambda", "details": { "active": false, "url": null } } ] ~~~ ## Performance Good performance was a requirement for rl_json, because it is used to handle large volumes of data flowing to and from various JSON based REST apis. It's generally the fastest option for working with JSON values in Tcl from the options I've tried, with the next closest being yajltcl. These benchmarks |
︙ | ︙ | |||
144 145 146 147 148 149 150 151 152 153 154 155 156 157 | old_json_parse | 241.595 rl_json_parse | 5.540 rl_json_get | 4.950 yajltcl | 8.800 rl_json_get_native | 0.800 ``` ### Generating This benchmark compares the relative performance of various ways of dynamically generating a JSON document. Although all the methods produce the same string, only the "template" and "template_dict" variants handle nulls in the general case - the others manually test for null only for the one field that is known to be null, so the performance of these variants would be worse | > > > > > > > > > > | 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 | old_json_parse | 241.595 rl_json_parse | 5.540 rl_json_get | 4.950 yajltcl | 8.800 rl_json_get_native | 0.800 ``` ### Validating If the requirement is to validate a JSON value, the [json valid] command is a light-weight version of the parsing engine that skips allocating values from the document and only returns whether the parsing succeeded or failed, and optionally a description of the failure. It takes about a third of the time to validate a document as parsing it, so the performance win is substantial. On a relatively modern CPU validation takes about 11 cycles per byte, or around 200MB of JSON per second on a 2.3 GHz Intel i7. ### Generating This benchmark compares the relative performance of various ways of dynamically generating a JSON document. Although all the methods produce the same string, only the "template" and "template_dict" variants handle nulls in the general case - the others manually test for null only for the one field that is known to be null, so the performance of these variants would be worse |
︙ | ︙ | |||
202 203 204 205 206 207 208 209 210 211 212 213 214 215 | rl_json_new | 10.240 template | 4.520 yajltcl | 7.700 template_dict | 2.500 yajltcl_dict | 7.530 ``` Under the Hood -------------- Older versions used the yajl c library to parse the JSON string and properly quote generated strings when serializing JSON values, but currently a custom built parser and string quoter is used, removing the libyajl dependency. JSON values are parsed to an internal format using Tcl_Objs and stored as the | > > > > > > > > > > > > > > > > > > > > > > > > > | 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 | rl_json_new | 10.240 template | 4.520 yajltcl | 7.700 template_dict | 2.500 yajltcl_dict | 7.530 ``` Deprecations ------------ Version 0.10.0 deprecates various subcommands and features, which will be removed in a near future version: * [json get_type *json_val* ?*key* ...?] - Removed * lassign [json get_type *json_val* ?*key* ...?] val type -> set val [json get *json_val* ?*key* ...?]; set type [json type *json_val* ?*key* ...?] * [json parse *json_val*] - A deprecated synonym for [json get *json_val*]. * [json fmt *type* *value*] - A deprecated synonym for [json new *type* *value*], which is itself deprecated, see below. * [json new *type* *value*] - Use direct subcommands of [json]: * [json new string *value*] -> [json string *value*] * [json new number *value*] -> [json number *value*] * [json new boolean *value*] -> [json boolean *value*] * [json new true] -> true * [json new false] -> false * [json new null] -> null * [json new json *value*] -> *value* * [json new object ...] -> [json object ...] (but consider [json template]) * [json new array ...] -> [json array ...] (but consider [json template]) * modifiers at the end of a path - Modifiers like [json get *json_val* foo ?type] are deprecated. Replacements are: * ?type - use [json type *json_val* ?*key* ...?] * ?length - use [json length *json_val* ?*key* ...?] * ?size - use [json length *json_val* ?*key* ...?] * ?keys - use [json keys *json_val* ?*key* ...?] Under the Hood -------------- Older versions used the yajl c library to parse the JSON string and properly quote generated strings when serializing JSON values, but currently a custom built parser and string quoter is used, removing the libyajl dependency. JSON values are parsed to an internal format using Tcl_Objs and stored as the |
︙ | ︙ |
Changes to jni/rl_json/aclocal.m4.
︙ | ︙ | |||
112 113 114 115 116 117 118 119 120 121 122 | AC_DEFUN([TEAX_CONFIG_LINK_LINE], [ AS_IF([test ${TCL_LIB_VERSIONS_OK} = nodots], [ eval "$1=\"-L[]CygPath($2) -l$3${TCL_TRIM_DOTS}\"" ], [ eval "$1=\"-L[]CygPath($2) -l$3${PACKAGE_VERSION}\"" ]) AC_SUBST($1)]) dnl Local Variables: dnl mode: autoconf dnl End: | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 | AC_DEFUN([TEAX_CONFIG_LINK_LINE], [ AS_IF([test ${TCL_LIB_VERSIONS_OK} = nodots], [ eval "$1=\"-L[]CygPath($2) -l$3${TCL_TRIM_DOTS}\"" ], [ eval "$1=\"-L[]CygPath($2) -l$3${PACKAGE_VERSION}\"" ]) AC_SUBST($1)]) # # Add here whatever m4 macros you want to define for your package # AC_DEFUN([ENABLE_ENSEMBLE], [ #trap 'echo "val: (${enable_ensemble+set}), ensemble_ok: ($ensemble_ok), ensemble: ($ENSEMBLE)"' DEBUG AC_MSG_CHECKING([whether to provide the json command as an ensemble]) AC_ARG_ENABLE(ensemble, AC_HELP_STRING([--enable-ensemble], [Provide the json command using a proper ensemble, otherwise handle the subcommand dispatch internally (default: no)]), [ensemble_ok=$enableval], [ensemble_ok=no]) if test "$ensemble_ok" = "yes" -o "${ENSEMBLE}" = 1; then ENSEMBLE=1 AC_MSG_RESULT([yes]) else ENSEMBLE=0 AC_MSG_RESULT([no]) fi AC_DEFINE_UNQUOTED([ENSEMBLE], [$ENSEMBLE], [Ensemble enabled?]) #trap '' DEBUG ]) AC_DEFUN([ENABLE_DEDUP], [ #trap 'echo "val: (${enable_dedup+set}), dedup_ok: ($dedup_ok), DEDUP: ($DEDUP)"' DEBUG AC_MSG_CHECKING([whether to use a string deduplication mechanism for short strings]) AC_ARG_ENABLE(dedup, AC_HELP_STRING([--enable-dedup], [Parsing JSON involves allocating a lot of small string Tcl_Objs, many of which are duplicates. This mechanism helps reduce that duplication (default: yes)]), [dedup_ok=$enableval], [dedup_ok=yes]) if test "$dedup_ok" = "yes" -o "${DEDUP}" = 1; then DEDUP=1 AC_MSG_RESULT([yes]) else DEDUP=0 AC_MSG_RESULT([no]) fi AC_DEFINE_UNQUOTED([DEDUP], [$DEDUP], [Dedup enabled?]) #trap '' DEBUG ]) AC_DEFUN([TIP445], [ AC_MSG_CHECKING([whether we need to polyfill TIP 445]) saved_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $TCL_INCLUDE_SPEC" AC_TRY_COMPILE([#include <tcl.h>], [Tcl_ObjIntRep ir;], have_tcl_objintrep=yes, have_tcl_objintrep=no) CFLAGS="$saved_CFLAGS" if test "$have_tcl_objintrep" = yes; then AC_DEFINE(TIP445_SHIM, 0, [Do we need to polyfill TIP 445?]) AC_MSG_RESULT([no]) else AC_DEFINE(TIP445_SHIM, 1, [Do we need to polyfill TIP 445?]) AC_MSG_RESULT([yes]) fi ]) dnl Local Variables: dnl mode: autoconf dnl End: |
Added jni/rl_json/bench/as_json.bench.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 | if {"bench" ni [info commands bench]} { package require bench namespace import bench::* } package require rl_json namespace import rl_json::json proc main {} { bench as_json-1.1 {[json boolean] vs implicit} -setup { #<<< } -compare { explicit { set doc {[]} for {set i 0} {$i < 10} {incr i} { json set doc end+1 [json boolean true] } set doc } implicit_literal { set doc {[]} for {set i 0} {$i < 10} {incr i} { json set doc end+1 true } set doc } implicit_var { set doc {[]} set v true for {set i 0} {$i < 10} {incr i} { json set doc end+1 $v } set doc } implicit_parsed { set doc {[]} for {set i 0} {$i < 10} {incr i} { json set doc end+1 [string trim " true"] } set doc } } -cleanup { unset -nocomplain doc i v } -result {[true,true,true,true,true,true,true,true,true,true]} #>>> bench as_json-2.1 {[json number] vs implicit} -setup { #<<< } -compare { explicit { set doc {[]} for {set i 0} {$i < 10} {incr i} { json set doc end+1 [json number $i] } set doc } implicit_var { set doc {[]} for {set i 0} {$i < 10} {incr i} { json set doc end+1 $i } set doc } implicit_parsed { set doc {[]} for {set i 0} {$i < 10} {incr i} { json set doc end+1 " $i" } set doc } } -cleanup { unset -nocomplain doc i } -result {[0,1,2,3,4,5,6,7,8,9]} #>>> bench as_json-2.2 {[json number] vs implicit} -setup { #<<< } -compare { explicit { set doc {[]} for {set i 0} {$i < 10} {incr i} { json set doc end+1 [json number 1e$i] } set doc } implicit_var { set doc {[]} for {set i 0} {$i < 10} {incr i} { json set doc end+1 1e$i } set doc } } -cleanup { unset -nocomplain doc i } -match glob -result * # -result {[1e0,1e1,1e2,1e3,1e4,1e5,1e6,1e7,1e8,1e9]} # -result {[1.0,10.0,100.0,1000.0,10000.0,100000.0,1000000.0,10000000.0,100000000.0,1000000000.0]} #>>> bench as_json-3.2 {[json string] vs implicit} -setup { #<<< } -compare { explicit { set doc {[]} for {set i 0} {$i < 10} {incr i} { json set doc end+1 [json string false$i] } set doc } implicit_var { set doc {[]} for {set i 0} {$i < 10} {incr i} { json set doc end+1 false$i } set doc } } -cleanup { unset -nocomplain doc i } -result {["false0","false1","false2","false3","false4","false5","false6","false7","false8","false9"]} } main # vim: ft=tcl foldmethod=marker foldmarker=<<<,>>> ts=4 shiftwidth=4 |
Changes to jni/rl_json/bench/bench-0.1.tm.
︙ | ︙ | |||
66 67 68 69 70 71 72 | close $h } } #>>> proc _run_if_set script { #<<< if {$script eq ""} return | | | 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 | close $h } } #>>> proc _run_if_set script { #<<< if {$script eq ""} return uplevel 2 [list if 1 $script] } #>>> proc _verify_res {variant retcodes expected match_mode got options} { #<<< if {[dict get $options -code] ni $retcodes} { bench::output error "Error: $got" throw [list BENCH BAD_CODE $variant $retcodes [dict get $options -code]] \ |
︙ | ︙ | |||
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 | } val [math::statistics::basic-stats $times] { dict set res $stat $val } dict set res median [math::statistics::median $times] dict set res harmonic_mean [/ [llength $times] [+ {*}[lmap time $times { / 1.0 $time }]]] } #>>> proc bench {name desc args} { #<<< variable match variable run variable skipped variable output array set opts { -setup {} -compare {} -cleanup {} | > > > > > > | > > > > > | | 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 | } val [math::statistics::basic-stats $times] { dict set res $stat $val } dict set res median [math::statistics::median $times] dict set res harmonic_mean [/ [llength $times] [+ {*}[lmap time $times { / 1.0 $time }]]] dict set res cv [expr {[dict get $res population_stddev] / [dict get $res arithmetic_mean]}] } #>>> proc bench {name desc args} { #<<< variable match variable run variable skipped variable output # -target_cv - Run until the coefficient of variation is below this, up to -max_time # -max_time - Maximum number of seconds to keep running while the cv is converging # -min_time - Keep accumulating samples for at least this many seconds # -batch - The number of samples to take in a tight loop and average to count as a single sample. "auto" guesses a reasonable value to make a batch take at least 1000 usec. # -window - Consider at most the previous -window measurements for target_cv and the results array set opts { -setup {} -compare {} -cleanup {} -batch auto -match exact -returnCodes {ok return} -target_cv {0.0015} -min_time 0.0 -max_time 4.0 -min_it 30 -window 30 } array set opts $args set badargs [lindex [_intersect3 [array names opts] { -setup -compare -cleanup -batch -match -result -returnCodes -target_cv -min_time -max_time -min_it -window }] 0] if {[llength $badargs] > 0} { error "Unrecognised arguments: [join $badargs {, }]" } if {![string match $match $name]} { |
︙ | ︙ | |||
150 151 152 153 154 155 156 | continue {list 4} default {set e} } }] set make_script { {batch script} { | | < < < < < < < < < < < < < > > > > > > | > > > > > > > > | > > | > > > > > > > > > > > > > > > | > | > > | > | > > > | > > > > | > > > | > > | > > > > > > > > > | > > > > > > | > > > | > | > > > > > > | > > > > | 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 | continue {list 4} default {set e} } }] set make_script { {batch script} { format {set __bench_i %d; while {[incr __bench_i -1] >= 0} %s} \ [list $batch] [list $script] } } set variant_stats {} _run_if_set $opts(-setup) try { dict for {variant script} $opts(-compare) { set hint [lindex [time { catch {uplevel 1 $script} r o }] 0] if {[info exists opts(-result)]} { _verify_res $variant $normalized_codes $opts(-result) $opts(-match) $r $o } set single_empty { uplevel 1 [list if 1 {}] } set single_ex_s { uplevel 1 [list if 1 $script] } if 1 $single_empty ;# throw the first away if 1 $single_ex_s ;# throw the first away set single_overhead [lindex [time $single_empty 1000] 0] #puts stderr "single overhead: $single_overhead" # Verify the first result against -result (if given), and estimate an appropriate batchsize to target a batch time of 1 ms to reduce quantization noise <<< set est_it [expr { max(1, int(round( 100.0/$hint ))) }] #puts stderr "hint: $hint, est_it: $est_it" set extime [lindex [time $single_ex_s $est_it] 0] set extime_comp [expr {$extime - $single_overhead}] #puts stderr "extime: $extime, extime comp: $extime_comp" if {$opts(-batch) eq "auto"} { set batch [expr {int(round(max(1, 1000.0/$extime_comp)))}] #puts stderr "Guessed batch size of $batch based on sample execution time $extime_comp usec" } else { set batch $opts(-batch) } #>>> # Measure the instrumentation overhead to compensate for it <<< set times {} set start [clock microseconds] ;# Prime [clock microseconds], start var set bscript [apply $make_script $batch {}] uplevel 1 [list if 1 $script] for {set i 0} {$i < int(100000 / ($batch*0.15))} {incr i} { set start [clock microseconds] uplevel 1 [list if 1 $bscript] lappend times [- [clock microseconds] $start] } set overhead [::tcl::mathfunc::min {*}[lmap e $times {expr {$e / double($batch)}}]] #apply $output debug [format {Overhead: %.3f usec, mean: %.3f for batch %d} $overhead [expr {double([+ {*}$times]) / ([llength $times]*$batch)}] $batch] # Measure the instrumentation overhead to compensate for it >>> set cv {data { # Calculate the coefficient of variation of $data <<< lassign [::math::statistics::basic-stats $data] \ arithmetic_mean min max number_of_data sample_stddev sample_var population_stddev population_var expr { $population_stddev / double($arithmetic_mean) } }} #>>> set begin [clock microseconds] ;# Don't count the first run time or the overhead measurement into the total elapsed time set it 0 set times {} set means {} set cvmeans {} set cvtimes {} set elapsed 0 set bscript [apply $make_script $batch $script] #puts stderr "bscript $variant: $bscript" # Run at least: # - -min_it times # - for half a second # - until the coefficient of variability of the means has fallen below -target_cv, or a max of -max_time seconds while { [llength $times] < $opts(-min_it) || $elapsed < $opts(-min_time) || ($elapsed < $opts(-max_time) && $cvmeans > $opts(-target_cv)) } { set start [clock microseconds] uplevel 1 [list if 1 $bscript] set batchtime [- [clock microseconds] $start] lappend times [expr { $batchtime / double($batch) - $overhead }] set elapsed [expr {([clock microseconds] - $begin)/1e6}] set cvtimes [lrange $times end-[+ 1 $opts(-window)] end] ;# Consider the last $opts(-window) data in estimating the variation lappend means [expr {[+ {*}$cvtimes]/[llength $cvtimes]}] set _cv [apply $cv $cvtimes] set cvmeans [apply $cv [lrange $means end-[+ 1 $opts(-window)] end]] #puts stderr "Got time for $variant batch($batch), batchtime $batchtime usec: [format %.4f [lindex $times end]], elapsed: [format %.3f $elapsed] sec[if {[info exists cvmeans]} {format {, cvmeans: %.3f} $cvmeans}][if {[info exists _cv]} {format {, cv: %.3f} $_cv}], mean: [format %.5f [lindex $means end]]" } dict set variant_stats $variant [_make_stats $cvtimes] dict set variant_stats $variant cvmeans $cvmeans dict set variant_stats $variant cv [apply $cv $cvtimes] dict set variant_stats $variant runtime $elapsed dict set variant_stats $variant it [llength $cvtimes] } lappend run $name $desc $variant_stats } finally { _run_if_set $opts(-cleanup) } }; namespace export bench |
︙ | ︙ | |||
247 248 249 250 251 252 253 | if {![dict exists $stats $variant]} { #string cat -- set _ -- } else { set val [dict get $stats $variant $pick] if {![info exists baseline]} { set baseline $val | | > > | > > | 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 | if {![dict exists $stats $variant]} { #string cat -- set _ -- } else { set val [dict get $stats $variant $pick] if {![info exists baseline]} { set baseline $val format {%.3f%s} $val [expr { [dict exists $stats $variant cv] ? [format { cv:%.1f%%} [expr {100*[dict get $stats $variant cv]}]] : "" }] } elseif {$baseline == 0} { format x%s inf } else { format {x%.3f%s} [/ $val $baseline] [expr { [dict exists $stats $variant cv] ? [format { cv:%.1f%%} [expr {100*[dict get $stats $variant cv]}]] : "" }] } } }] }] # Determine the column widths set colsize {} |
︙ | ︙ | |||
311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 | upvar 1 i i args args set from $i incr i $count lrange $args $from [+ $from $count -1] } [namespace current] \ ] set i 0 while {$i < [llength $args]} { lassign [apply $consume_args 1] next switch -- $next { -match { lassign [apply $consume_args 1] match } -relative { lassign [apply $consume_args 2] label rel_fn | > > > > > > | > | 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 | upvar 1 i i args args set from $i incr i $count lrange $args $from [+ $from $count -1] } [namespace current] \ ] # Automatically save and compare with the previous run set args [list {*}{ -relative last last } {*}$args] set i 0 while {$i < [llength $args]} { lassign [apply $consume_args 1] next switch -- $next { -match { lassign [apply $consume_args 1] match } -relative { lassign [apply $consume_args 2] label rel_fn if {[file readable $rel_fn]} { dict set relative $label [_readfile $rel_fn] } } -save { lassign [apply $consume_args 1] save_fn } -display { |
︙ | ︙ | |||
346 347 348 349 350 351 352 | "Invalid argument: \"$next\"" } } } set stats {} foreach f [glob -nocomplain -type f -dir $dir -tails *.bench] { | | > > > > > > > > > > > > > > > > > | | 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 | "Invalid argument: \"$next\"" } } } set stats {} foreach f [glob -nocomplain -type f -dir $dir -tails *.bench] { uplevel 1 [list if 1 [list source [file join $dir $f]]] } set save {{save_fn run} { set save_data $run if {[file readable $save_fn]} { # If the save file already exists, merge this run's data with it # rather than replacing it (keeps old tests that weren't executed # in this run) set newkeys [lmap {relname - -} $save_data {set relname}] set old [_readfile $save_fn] foreach {relname reldesc relstats} $old { if {$relname in $newkeys} continue lappend save_data $relname $reldesc $relstats } } _writefile $save_fn $save_data }} apply $save last $run ;# Always save as "last", even if explicitly saving as something else too if {[info exists save_fn]} { apply $save $save_fn $run } foreach {name desc variant_stats} $run { set relative_stats {} foreach {label relinfo} $relative { foreach {relname reldesc relstats} $relinfo { if {$relname eq $name} { |
︙ | ︙ |
Changes to jni/rl_json/bench/new.bench.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | if {"bench" ni [info commands bench]} { package require bench namespace import bench::* } package require rl_json package require yajltcl package require json_old namespace import rl_json::json bench new-1.1 {Various ways of dynamically assembling a JSON doc} -setup { #<<< array set a { x "str\"foo\nbar" y 123 on yes off 0 subdoc {{"inner": "~S:bar"}} | > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | if {"bench" ni [info commands bench]} { package require bench namespace import bench::* } package require rl_json package require yajltcl package require json_old namespace import rl_json::json proc main {} { bench new-1.1 {Various ways of dynamically assembling a JSON doc} -setup { #<<< array set a { x "str\"foo\nbar" y 123 on yes off 0 subdoc {{"inner": "~S:bar"}} |
︙ | ︙ | |||
56 57 58 59 60 61 62 63 64 65 66 67 68 69 | [expr {[info exists a(not_defined)] ? [list s $a(not_defined)] : [list null]}] \ [list s "~S:not a subst"] \ [list o inner [list s $bar]] \ [list o inner2 [list s $bar]] \ ] \ ] } template { json template { { "foo": "~S:bar", "baz": [ "~S:a(x)", | > > > > > > > > > > > > > > > > > > > | 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 | [expr {[info exists a(not_defined)] ? [list s $a(not_defined)] : [list null]}] \ [list s "~S:not a subst"] \ [list o inner [list s $bar]] \ [list o inner2 [list s $bar]] \ ] \ ] } template_string { json template_string { { "foo": "~S:bar", "baz": [ "~S:a(x)", "~N:a(y)", 123.4, "~B:a(on)", "~B:a(off)", "~S:a(not_defined)", "~L:~S:not a subst", "~T:a(subdoc)", "~T:a(subdoc2)" ] } } } template { json template { { "foo": "~S:bar", "baz": [ "~S:a(x)", |
︙ | ︙ | |||
109 110 111 112 113 114 115 | y get } finally { y delete } } | | | | 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 | y get } finally { y delete } } template_string_dict { json template_string { { "foo": "~S:bar", "baz": [ "~S:a(x)", "~N:a(y)", 123.4, "~B:a(on)", |
︙ | ︙ | |||
164 165 166 167 168 169 170 | y delete } } } -cleanup { unset -nocomplain a d } -result {{"foo":"Bar","baz":["str\"foo\nbar",123,123.4,true,false,null,"~S:not a subst",{"inner":"Bar"},{"inner2":"Bar"}]}} #>>> | | > > | 184 185 186 187 188 189 190 191 192 193 194 | y delete } } } -cleanup { unset -nocomplain a d } -result {{"foo":"Bar","baz":["str\"foo\nbar",123,123.4,true,false,null,"~S:not a subst",{"inner":"Bar"},{"inner2":"Bar"}]}} #>>> } main # vim: ft=tcl foldmethod=marker foldmarker=<<<,>>> ts=4 shiftwidth=4 |
Changes to jni/rl_json/bench/normalize.bench.
1 2 3 4 5 6 | if {"bench" ni [info commands bench]} { package require bench namespace import bench::* } package require rl_json | | > | > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | if {"bench" ni [info commands bench]} { package require bench namespace import bench::* } package require rl_json #package require yajltcl namespace import rl_json::json proc main {} { bench normalize-1.1 {Normalize a JSON doc} -setup { #<<< set json { { "foo": "bar", "baz": ["str", 123, 123.4, true, false, null, {"inner": "obj"}] } } } -compare { ours { json normalize [string trim $json] } } -cleanup { unset -nocomplain json } -result {{"foo":"bar","baz":["str",123,123.4,true,false,null,{"inner":"obj"}]}} #>>> } main # vim: ft=tcl foldmethod=marker foldmarker=<<<,>>> ts=4 shiftwidth=4 |
Changes to jni/rl_json/bench/parse.bench.
︙ | ︙ | |||
16 17 18 19 20 21 22 23 24 25 26 27 28 29 | read $h } finally { close $h } } #>>> bench parse-1.1 {Parse a small JSON doc and extract a field} -setup { #<<< set json { { "foo": "bar", "baz": ["str", 123, 123.4, true, false, null, {"inner": "obj"}] } } | > | 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | read $h } finally { close $h } } #>>> proc main {} { bench parse-1.1 {Parse a small JSON doc and extract a field} -setup { #<<< set json { { "foo": "bar", "baz": ["str", 123, 123.4, true, false, null, {"inner": "obj"}] } } |
︙ | ︙ | |||
60 61 62 63 64 65 66 67 68 69 70 71 72 73 | bench parse-2.1 {Parse a few MB of json} -batch 3 -setup { #<<< set json [readfile [file join [file dirname [file normalize [info script]]] items1.json]] } -compare { just_parse { json normalize $json list 83000 5434 } ours { set c 0 set nrpics 0 set p [string trim $json] json foreach hit [json extract $p hits hits] { if {[json exists $hit _source shopstatus]} { json get $hit _source shopstatus | > > > > > > > > | 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 | bench parse-2.1 {Parse a few MB of json} -batch 3 -setup { #<<< set json [readfile [file join [file dirname [file normalize [info script]]] items1.json]] } -compare { just_parse { json normalize $json list 83000 5434 } valid { json valid $json list 83000 5434 } valid_no_comments { json valid -extensions {} $json list 83000 5434 } ours { set c 0 set nrpics 0 set p [string trim $json] json foreach hit [json extract $p hits hits] { if {[json exists $hit _source shopstatus]} { json get $hit _source shopstatus |
︙ | ︙ | |||
98 99 100 101 102 103 104 | } if {[file readable [file join [file dirname [file normalize [info script]]] sample.json]]} { bench parse-3.1 {Parse a large json doc with deep nesting and many UTF-8 chars} -batch 3 -setup { #<<< # Sample from the json-test-suite project: https://code.google.com/p/json-test-suite/downloads/detail?name=sample.zip set json [readfile [file join [file dirname [file normalize [info script]]] sample.json]] } -compare { ours { | | | > > > > | | > > | 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 | } if {[file readable [file join [file dirname [file normalize [info script]]] sample.json]]} { bench parse-3.1 {Parse a large json doc with deep nesting and many UTF-8 chars} -batch 3 -setup { #<<< # Sample from the json-test-suite project: https://code.google.com/p/json-test-suite/downloads/detail?name=sample.zip set json [readfile [file join [file dirname [file normalize [info script]]] sample.json]] } -compare { ours { json length [string trim $json] } yajltcl { dict size [yajl::json2dict [string trim $json]] } valid { json valid $json return -level 0 3 } } -cleanup { unset -nocomplain json } -result 3 #>>> } } main # vim: ft=tcl foldmethod=marker foldmarker=<<<,>>> ts=4 shiftwidth=4 |
Added jni/rl_json/bench/piecewise_template.bench.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 | if {"bench" ni [info commands bench]} { package require bench namespace import bench::* } package require rl_json namespace import rl_json::json proc main {} { bench piecewise_template-1.1 {Templated document creation in multiple steps} -setup { #<<< set tmpl { { "foo": "~S:foo", "bar": "~S:a(bar)", "baz": "~N:baz", "recurse": "~J:last" } } set foo Foo array set a { foo X bar Bar } } -compare { serialize { unset -nocomplain last for {set i 0} {$i < 6} {incr i} { set last [json template_string $tmpl] } set last } direct { unset -nocomplain last for {set i 0} {$i < 6} {incr i} { set last [json template $tmpl] } set last } } -cleanup { unset -nocomplain tmpl a foo i last } -result [json normalize { { "foo": "Foo", "bar": "Bar", "baz": null, "recurse": { "foo": "Foo", "bar": "Bar", "baz": null, "recurse": { "foo": "Foo", "bar": "Bar", "baz": null, "recurse": { "foo": "Foo", "bar": "Bar", "baz": null, "recurse": { "foo": "Foo", "bar": "Bar", "baz": null, "recurse": { "foo": "Foo", "bar": "Bar", "baz": null, "recurse": null } } } } } } }] #>>> bench piecewise_template-1.2 {Templated document creation in a loop} -setup { #<<< set tmpl { { "foo": "~S:foo", "bar": "~S:a(bar)", "baz": "~N:baz", "recurse": "~J:last" } } set foo Foo array set a { foo X bar Bar } } -compare { serialize { set res {[]} for {set i 0} {$i < 20} {incr i} { json set res end+1 [json template_string $tmpl] } set res } direct { set res {[]} for {set i 0} {$i < 20} {incr i} { json set res end+1 [json template $tmpl] } set res } } -cleanup { unset -nocomplain tmpl a foo i last res } -result [json normalize { [ { "foo": "Foo", "bar": "Bar", "baz": null, "recurse": null }, { "foo": "Foo", "bar": "Bar", "baz": null, "recurse": null }, { "foo": "Foo", "bar": "Bar", "baz": null, "recurse": null }, { "foo": "Foo", "bar": "Bar", "baz": null, "recurse": null }, { "foo": "Foo", "bar": "Bar", "baz": null, "recurse": null }, { "foo": "Foo", "bar": "Bar", "baz": null, "recurse": null }, { "foo": "Foo", "bar": "Bar", "baz": null, "recurse": null }, { "foo": "Foo", "bar": "Bar", "baz": null, "recurse": null }, { "foo": "Foo", "bar": "Bar", "baz": null, "recurse": null }, { "foo": "Foo", "bar": "Bar", "baz": null, "recurse": null }, { "foo": "Foo", "bar": "Bar", "baz": null, "recurse": null }, { "foo": "Foo", "bar": "Bar", "baz": null, "recurse": null }, { "foo": "Foo", "bar": "Bar", "baz": null, "recurse": null }, { "foo": "Foo", "bar": "Bar", "baz": null, "recurse": null }, { "foo": "Foo", "bar": "Bar", "baz": null, "recurse": null }, { "foo": "Foo", "bar": "Bar", "baz": null, "recurse": null }, { "foo": "Foo", "bar": "Bar", "baz": null, "recurse": null }, { "foo": "Foo", "bar": "Bar", "baz": null, "recurse": null }, { "foo": "Foo", "bar": "Bar", "baz": null, "recurse": null }, { "foo": "Foo", "bar": "Bar", "baz": null, "recurse": null } ] }] #>>> } main # vim: ft=tcl foldmethod=marker foldmarker=<<<,>>> ts=4 shiftwidth=4 |
Changes to jni/rl_json/bench/run.tcl.
1 2 3 4 5 6 7 8 9 | #!/usr/bin/env cfkit8.6 # vim: ft=tcl foldmethod=marker foldmarker=<<<,>>> ts=4 shiftwidth=4 if {[file system [info script]] eq "native"} { package require platform foreach platform [platform::patterns [platform::identify]] { set tm_path [file join $env(HOME) .tbuild repo tm $platform] set pkg_path [file join $env(HOME) .tbuild repo pkg $platform] | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 | #!/usr/bin/env cfkit8.6 # vim: ft=tcl foldmethod=marker foldmarker=<<<,>>> ts=4 shiftwidth=4 set big [string repeat a [expr {int(1e8)}]] ;# Allocate 100MB to pre-expand the zippy pool unset big if {[file system [info script]] eq "native"} { package require platform foreach platform [platform::patterns [platform::identify]] { set tm_path [file join $env(HOME) .tbuild repo tm $platform] set pkg_path [file join $env(HOME) .tbuild repo pkg $platform] |
︙ | ︙ | |||
17 18 19 20 21 22 23 24 25 | } set here [file dirname [file normalize [info script]]] set parent [file dirname $here] tcl::tm::path add $here lappend auto_path $parent package require bench | > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > | | | > > > > > | > > > | 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 | } set here [file dirname [file normalize [info script]]] set parent [file dirname $here] tcl::tm::path add $here lappend auto_path $parent package require platform package require bench proc with_chan {var create use} { upvar 1 $var h set h [uplevel 1 [list if 1 $create]] try { uplevel 1 [list if 1 $use] } on return {r o} - on break {r o} - on continue {r o} { dict incr o -level 1 return -options $o $r } finally { if {[info exists h] && $h in [chan names]} { catch {close $h} } } } proc readtext fn { with_chan h {open $fn r} {read $h} } proc benchmark_mode script { set restore_state_actions {} with_chan root {open |[list sudo [info nameofexecutable]] rb+} { chan configure $root -encoding binary -translation binary proc sudo args { upvar 1 root root puts $root [encoding convertto utf-8 [string map [list %script% [list $args]] { catch %script% r o set resp [encoding convertto utf-8 [list $r $o]] puts -nonewline [string length $resp]\n$resp flush stdout }]] flush $root set bytes [gets $root] if {$bytes eq ""} { if {[eof $root]} { puts stderr "Root child died" close $root unset root return } } if {![string is integer -strict $bytes]} { error "Root child sync error" } lassign [encoding convertfrom utf-8 [read $root $bytes]] r o return -options $o $r } sudo eval { #chan configure stdin -buffering none -encoding utf-8 chan configure stdout -buffering none -encoding binary -translation binary proc with_chan {var create use} { upvar 1 $var h set h [uplevel 1 [list if 1 $create]] try { uplevel 1 [list if 1 $use] } on return {r o} - on break {r o} - on continue {r o} { dict incr o -level 1 return -options $o $r } finally { if {[info exists h] && $h in [chan names]} { close $h } } } proc readtext fn { with_chan h {open $fn r} {read $h} } proc writetext {fn value} { with_chan h {open $fn w} {puts -nonewline $h $value} } proc set_turbo state { if {$state} { set mode "turbo enable\nquit" } else { set mode "turbo disable\nquit" } #with_chan h {open |[list i7z_rw_registers 2>@ stderr] w} { # chan configure $h -buffering none -encoding binary -translation binary -blocking 1 # puts $h $mode #} set res [exec echo $mode | i7z_rw_registers] if {[regexp {Turbo is now (Enabled|Disabled)} $res - newstate]} { set newstate } else { set res } } proc get_turbo {} { expr { ![exec rdmsr 0x1a0 --bitfield 38:38] } } } try { switch -glob -- [platform::generic] { linux-* { # Disable turbo boost <<< #lappend restore_state_actions [list sudo set_turbo 1] lappend restore_state_actions [list sudo set_turbo [sudo get_turbo]] sudo set_turbo off # Disable turbo boost >>> # Disable frequency scaling <<< foreach governor [glob -nocomplain -type f /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor] { lappend restore_state_actions [list sudo writetext $governor [string trim [readtext $governor]]] #lappend restore_state_actions [list sudo writetext $governor powersave] sudo writetext $governor performance } # Disable frequency scaling >>> # Set highest scheduling priority lappend restore_state_actions [list sudo exec renice --priority [exec nice] [pid]] sudo exec renice --priority -20 [pid] # Disable hyperthreading (effectively by disabling sibling cores, preventing the kernel from scheduling tasks for them) set siblings {} foreach core [glob -nocomplain -type d -directory /sys/devices/system/cpu -tails cpu*] { if {![regexp {^cpu[0-9]+$} $core]} continue if {[file readable /sys/devices/system/cpu/$core/online]} { lappend restore_state_actions [list sudo writetext /sys/devices/system/cpu/$core/online [string trim [readtext /sys/devices/system/cpu/$core/online]]] #lappend restore_state_actions [list sudo writetext /sys/devices/system/cpu/$core/online 1] } set sibs [readtext /sys/devices/system/cpu/$core/topology/thread_siblings_list] dict lappend siblings $sibs $core } dict for {group cores} $siblings { set keep [lmap core $cores { if {[file readable /sys/devices/system/cpu/$core/online]} continue set core }] if {$keep eq {}} { set keep [list [lindex $cores 0]] } foreach core $cores { if {$core in $keep} continue sudo writetext /sys/devices/system/cpu/$core/online 0 } } } default { puts stderr "Don't know the magic to set [platform::generic] up for repeatable benchmarking" } } uplevel 1 [list if 1 $script] } finally { foreach action [lreverse $restore_state_actions] { #puts stderr "Running restore action:\t$action" set errors 0 try $action on error {errmsg options} { puts stderr "Error processing restore action \"$action\": [dict get $options -errorinfo]" set errors 1 } if {$errors} { throw exit 2 } } } close $root write read $root } } proc main {} { try { set here [file dirname [file normalize [info script]]] benchmark_mode { puts "[string repeat - 80]\nStarting benchmarks\n" bench::run_benchmarks $here {*}$::argv } } on ok {} { exit 0 } trap {BENCH INVALID_ARG} {errmsg options} { puts stderr $errmsg exit 1 } trap exit code { exit $code } on error {errmsg options} { puts stderr "Unhandled error from benchmark_mode: [dict get $options -errorinfo]" exit 2 } } main # vim: ft=tcl foldmethod=marker foldmarker=<<<,>>> ts=4 shiftwidth=4 |
Changes to jni/rl_json/bench/template.bench.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | if {"bench" ni [info commands bench]} { package require bench namespace import bench::* } package require rl_json namespace import rl_json::json bench template-1.1 {Basic templated document creation} -setup { #<<< set tmpl { { "foo": "~S:foo", "bar": "~S:a(bar)", "baz": "~N:baz" } } set foo Foo array set a { foo X bar Bar } } -compare { dict { | > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 | if {"bench" ni [info commands bench]} { package require bench namespace import bench::* } package require rl_json namespace import rl_json::json proc main {} { bench template-1.1 {Basic templated document creation} -setup { #<<< set tmpl { { "foo": "~S:foo", "bar": "~S:a(bar)", "baz": "~N:baz" } } set foo Foo array set a { foo X bar Bar } } -compare { dict { json template_string $tmpl { foo Foo a(bar) Bar } } dict_lit { json template_string { { "foo": "~S:foo", "bar": "~S:a(bar)", "baz": "~N:baz" } } { foo Foo a(bar) Bar } } variables { json template_string $tmpl } dict_direct { json template $tmpl { foo Foo a(bar) Bar } } dict_lit_direct { json template { { "foo": "~S:foo", "bar": "~S:a(bar)", "baz": "~N:baz" } } { foo Foo a(bar) Bar } } variables_direct { json template $tmpl } } -cleanup { unset -nocomplain tmpl a foo } -result [json normalize { { "foo": "Foo", "bar": "Bar", "baz": null } }] #>>> bench template-2.1 {Test templated doc creation, including numbers} -setup { #<<< set foo 1 set bar 42.5 set baz 1e6 set quux 0x42 set octal 077 set space " 42" set d { foo 1 bar 42.5 baz 1e6 quux 0x42 octal 077 space " 42" } } -compare { template_string { json template_string { { "foo": "~N:foo", "bar": "~N:bar", //"baz": "~N:baz", "quux": "~N:quux", "octal": "~N:octal", "space": "~N:space" } } } template_string_dict { json template_string { { "foo": "~N:foo", "bar": "~N:bar", //"baz": "~N:baz", "quux": "~N:quux", "octal": "~N:octal", "space": "~N:space" } } $d } template { json template { { "foo": "~N:foo", "bar": "~N:bar", //"baz": "~N:baz", "quux": "~N:quux", "octal": "~N:octal", "space": "~N:space" } } } template_dict { json template_string { { "foo": "~N:foo", "bar": "~N:bar", //"baz": "~N:baz", "quux": "~N:quux", "octal": "~N:octal", "space": "~N:space" } } $d } } -cleanup { unset -nocomplain foo bar baz d } -result [json normalize { { "foo": 1, "bar": 42.5, //"baz": 1e6, "quux": 66, "octal": 63, "space": 42 } }] #>>> bench template-3.1 {Test templated doc creation, including numbers, json set on result} -setup { #<<< set foo 1 set bar 42.5 set baz 1e6 set quux 0x42 set octal 077 set space " 42" set d { foo 1 bar 42.5 baz 1e6 quux 0x42 octal 077 space " 42" } } -compare { template_string { set j [json template_string { { "foo": "~N:foo", "bar": "~N:bar", //"baz": "~N:baz", "quux": "~N:quux", "octal": "~N:octal", "space": "~N:space" } }] json set j new null } template_string_dict { set j [json template_string { { "foo": "~N:foo", "bar": "~N:bar", //"baz": "~N:baz", "quux": "~N:quux", "octal": "~N:octal", "space": "~N:space" } } $d] json set j new null } template { set j [json template { { "foo": "~N:foo", "bar": "~N:bar", //"baz": "~N:baz", "quux": "~N:quux", "octal": "~N:octal", "space": "~N:space" } }] json set j new null } template_dict { set j [json template { { "foo": "~N:foo", "bar": "~N:bar", //"baz": "~N:baz", "quux": "~N:quux", "octal": "~N:octal", "space": "~N:space" } } $d] json set j new null } } -cleanup { unset -nocomplain foo bar baz d } -result [json normalize { { "foo": 1, "bar": 42.5, //"baz": 1e6, "quux": 66, "octal": 63, "space": 42, "new": null } }] #>>> } main # vim: ft=tcl foldmethod=marker foldmarker=<<<,>>> ts=4 shiftwidth=4 |
Changes to jni/rl_json/configure.
1 2 | #! /bin/sh # Guess values for system-dependent variables and create Makefiles. | | | 1 2 3 4 5 6 7 8 9 10 | #! /bin/sh # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.63 for rl_json 0.11.0. # # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, # 2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. ## --------------------- ## ## M4sh Initialization. ## |
︙ | ︙ | |||
590 591 592 593 594 595 596 | MFLAGS= MAKEFLAGS= SHELL=${CONFIG_SHELL-/bin/sh} # Identity of this package. PACKAGE_NAME='rl_json' PACKAGE_TARNAME='rl_json' | | | | 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 | MFLAGS= MAKEFLAGS= SHELL=${CONFIG_SHELL-/bin/sh} # Identity of this package. PACKAGE_NAME='rl_json' PACKAGE_TARNAME='rl_json' PACKAGE_VERSION='0.11.0' PACKAGE_STRING='rl_json 0.11.0' PACKAGE_BUGREPORT='' # Factoring default headers for most tests. ac_includes_default="\ #include <stdio.h> #ifdef HAVE_SYS_TYPES_H # include <sys/types.h> |
︙ | ︙ | |||
749 750 751 752 753 754 755 756 757 758 759 760 761 762 | PACKAGE_NAME PATH_SEPARATOR SHELL' ac_subst_files='' ac_user_opts=' enable_option_checking with_tcl with_tclinclude enable_threads enable_shared enable_64bit enable_64bit_vis enable_rpath enable_wince | > > | 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 | PACKAGE_NAME PATH_SEPARATOR SHELL' ac_subst_files='' ac_user_opts=' enable_option_checking with_tcl enable_ensemble enable_dedup with_tclinclude enable_threads enable_shared enable_64bit enable_64bit_vis enable_rpath enable_wince |
︙ | ︙ | |||
1321 1322 1323 1324 1325 1326 1327 | # # Report the --help message. # if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF | | | 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 | # # Report the --help message. # if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF \`configure' configures rl_json 0.11.0 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... To assign environment variables (e.g., CC, CFLAGS...), specify them as VAR=VALUE. See below for descriptions of some of the useful variables. Defaults for the options are specified in brackets. |
︙ | ︙ | |||
1382 1383 1384 1385 1386 1387 1388 | cat <<\_ACEOF _ACEOF fi if test -n "$ac_init_help"; then case $ac_init_help in | | > > > > > > > | 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 | cat <<\_ACEOF _ACEOF fi if test -n "$ac_init_help"; then case $ac_init_help in short | recursive ) echo "Configuration of rl_json 0.11.0:";; esac cat <<\_ACEOF Optional Features: --disable-option-checking ignore unrecognized --enable/--with options --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) --enable-FEATURE[=ARG] include FEATURE [ARG=yes] --enable-ensemble Provide the json command using a proper ensemble, otherwise handle the subcommand dispatch internally (default: no) --enable-dedup Parsing JSON involves allocating a lot of small string Tcl_Objs, many of which are duplicates. This mechanism helps reduce that duplication (default: yes) --enable-threads build with threads --enable-shared build and link with shared libraries (default: on) --enable-64bit enable 64bit support (default: off) --enable-64bit-vis enable 64bit Sparc VIS support (default: off) --disable-rpath disable rpath support (default: on) --enable-wince enable Win/CE support (where applicable) --enable-symbols build with debugging symbols (default: off) |
︙ | ︙ | |||
1483 1484 1485 1486 1487 1488 1489 | cd "$ac_pwd" || { ac_status=$?; break; } done fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF | | | | 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 | cd "$ac_pwd" || { ac_status=$?; break; } done fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF rl_json configure 0.11.0 generated by GNU Autoconf 2.63 Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. _ACEOF exit fi cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. It was created by rl_json $as_me 0.11.0, which was generated by GNU Autoconf 2.63. Invocation command line was $ $0 $@ _ACEOF exec 5>>config.log { |
︙ | ︙ | |||
7600 7601 7602 7603 7604 7605 7606 7607 7608 7609 7610 7611 7612 7613 7614 7615 7616 7617 7618 | # Let the user call this, because if it triggers, they will # need a compat/strtod.c that is correct. Users can also # use Tcl_GetDouble(FromObj) instead. #TEA_BUGGY_STRTOD fi #----------------------------------------------------------------------- # __CHANGE__ # Specify the C source files to compile in TEA_ADD_SOURCES, # public headers that need to be installed in TEA_ADD_HEADERS, # stub library C source files to compile in TEA_ADD_STUB_SOURCES, # and runtime Tcl library files in TEA_ADD_TCL_SOURCES. # This defines PKG(_STUB)_SOURCES, PKG(_STUB)_OBJECTS, PKG_HEADERS # and PKG_TCL_SOURCES. #----------------------------------------------------------------------- | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | 7609 7610 7611 7612 7613 7614 7615 7616 7617 7618 7619 7620 7621 7622 7623 7624 7625 7626 7627 7628 7629 7630 7631 7632 7633 7634 7635 7636 7637 7638 7639 7640 7641 7642 7643 7644 7645 7646 7647 7648 7649 7650 7651 7652 7653 7654 7655 7656 7657 7658 7659 7660 7661 7662 7663 7664 7665 7666 7667 7668 7669 7670 7671 7672 7673 7674 7675 7676 7677 7678 7679 7680 7681 7682 7683 7684 7685 7686 7687 7688 7689 7690 7691 7692 7693 7694 7695 | # Let the user call this, because if it triggers, they will # need a compat/strtod.c that is correct. Users can also # use Tcl_GetDouble(FromObj) instead. #TEA_BUGGY_STRTOD fi # Check for feature toggles #trap 'echo "val: (${enable_ensemble+set}), ensemble_ok: ($ensemble_ok), ensemble: ($ENSEMBLE)"' DEBUG { $as_echo "$as_me:$LINENO: checking whether to provide the json command as an ensemble" >&5 $as_echo_n "checking whether to provide the json command as an ensemble... " >&6; } # Check whether --enable-ensemble was given. if test "${enable_ensemble+set}" = set; then enableval=$enable_ensemble; ensemble_ok=$enableval else ensemble_ok=no fi if test "$ensemble_ok" = "yes" -o "${ENSEMBLE}" = 1; then ENSEMBLE=1 { $as_echo "$as_me:$LINENO: result: yes" >&5 $as_echo "yes" >&6; } else ENSEMBLE=0 { $as_echo "$as_me:$LINENO: result: no" >&5 $as_echo "no" >&6; } fi cat >>confdefs.h <<_ACEOF #define ENSEMBLE $ENSEMBLE _ACEOF #trap '' DEBUG #trap 'echo "val: (${enable_dedup+set}), dedup_ok: ($dedup_ok), DEDUP: ($DEDUP)"' DEBUG { $as_echo "$as_me:$LINENO: checking whether to use a string deduplication mechanism for short strings" >&5 $as_echo_n "checking whether to use a string deduplication mechanism for short strings... " >&6; } # Check whether --enable-dedup was given. if test "${enable_dedup+set}" = set; then enableval=$enable_dedup; dedup_ok=$enableval else dedup_ok=yes fi if test "$dedup_ok" = "yes" -o "${DEDUP}" = 1; then DEDUP=1 { $as_echo "$as_me:$LINENO: result: yes" >&5 $as_echo "yes" >&6; } else DEDUP=0 { $as_echo "$as_me:$LINENO: result: no" >&5 $as_echo "no" >&6; } fi cat >>confdefs.h <<_ACEOF #define DEDUP $DEDUP _ACEOF #trap '' DEBUG #----------------------------------------------------------------------- # __CHANGE__ # Specify the C source files to compile in TEA_ADD_SOURCES, # public headers that need to be installed in TEA_ADD_HEADERS, # stub library C source files to compile in TEA_ADD_STUB_SOURCES, # and runtime Tcl library files in TEA_ADD_TCL_SOURCES. # This defines PKG(_STUB)_SOURCES, PKG(_STUB)_OBJECTS, PKG_HEADERS # and PKG_TCL_SOURCES. #----------------------------------------------------------------------- vars="parser.c rl_json.c json_types.c dedup.c api.c rl_jsonStubInit.c" for i in $vars; do case $i in \$*) # allow $-var names PKG_SOURCES="$PKG_SOURCES $i" PKG_OBJECTS="$PKG_OBJECTS $i" ;; |
︙ | ︙ | |||
7649 7650 7651 7652 7653 7654 7655 | ;; esac done | | | 7718 7719 7720 7721 7722 7723 7724 7725 7726 7727 7728 7729 7730 7731 7732 | ;; esac done vars="generic/rl_jsonDecls.h generic/rl_json.h" for i in $vars; do # check for existence, be strict because it is installed if test ! -f "${srcdir}/$i" ; then { { $as_echo "$as_me:$LINENO: error: could not find header file '${srcdir}/$i'" >&5 $as_echo "$as_me: error: could not find header file '${srcdir}/$i'" >&2;} { (exit 1); exit 1; }; } fi |
︙ | ︙ | |||
7735 7736 7737 7738 7739 7740 7741 | # Add pkgIndex.tcl if it is generated in the Makefile instead of ./configure # and change Makefile.in to move it from CONFIG_CLEAN_FILES to BINARIES var. # # A few miscellaneous platform-specific items: # TEA_ADD_* any platform specific compiler/build info here. #-------------------------------------------------------------------- | | | 7804 7805 7806 7807 7808 7809 7810 7811 7812 7813 7814 7815 7816 7817 7818 | # Add pkgIndex.tcl if it is generated in the Makefile instead of ./configure # and change Makefile.in to move it from CONFIG_CLEAN_FILES to BINARIES var. # # A few miscellaneous platform-specific items: # TEA_ADD_* any platform specific compiler/build info here. #-------------------------------------------------------------------- #CLEANFILES="$CLEANFILES pkgIndex.tcl doc/json.html" if test "${TEA_PLATFORM}" = "windows" ; then # Ensure no empty if clauses : #TEA_ADD_SOURCES([win/winFile.c]) #TEA_ADD_INCLUDES([-I\"$(${CYGPATH} ${srcdir}/win)\"]) else # Ensure no empty else clauses |
︙ | ︙ | |||
11834 11835 11836 11837 11838 11839 11840 11841 11842 11843 11844 11845 11846 11847 | else { $as_echo "$as_me:$LINENO: result: no" >&5 $as_echo "no" >&6; } fi fi #-------------------------------------------------------------------- # Set the default compiler switches based on the --enable-symbols option. #-------------------------------------------------------------------- | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 11903 11904 11905 11906 11907 11908 11909 11910 11911 11912 11913 11914 11915 11916 11917 11918 11919 11920 11921 11922 11923 11924 11925 11926 11927 11928 11929 11930 11931 11932 11933 11934 11935 11936 11937 11938 11939 11940 11941 11942 11943 11944 11945 11946 11947 11948 11949 11950 11951 11952 11953 11954 11955 11956 11957 11958 11959 11960 11961 11962 11963 11964 11965 11966 11967 11968 11969 11970 11971 11972 11973 11974 11975 11976 11977 11978 11979 11980 11981 11982 11983 11984 11985 | else { $as_echo "$as_me:$LINENO: result: no" >&5 $as_echo "no" >&6; } fi fi # Check for required polyfill { $as_echo "$as_me:$LINENO: checking whether we need to polyfill TIP 445" >&5 $as_echo_n "checking whether we need to polyfill TIP 445... " >&6; } saved_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $TCL_INCLUDE_SPEC" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include <tcl.h> int main () { Tcl_ObjIntRep ir; ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" $as_echo "$ac_try_echo") >&5 (eval "$ac_compile") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then have_tcl_objintrep=yes else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 have_tcl_objintrep=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CFLAGS="$saved_CFLAGS" if test "$have_tcl_objintrep" = yes; then cat >>confdefs.h <<\_ACEOF #define TIP445_SHIM 0 _ACEOF { $as_echo "$as_me:$LINENO: result: no" >&5 $as_echo "no" >&6; } else cat >>confdefs.h <<\_ACEOF #define TIP445_SHIM 1 _ACEOF { $as_echo "$as_me:$LINENO: result: yes" >&5 $as_echo "yes" >&6; } fi #-------------------------------------------------------------------- # Set the default compiler switches based on the --enable-symbols option. #-------------------------------------------------------------------- |
︙ | ︙ | |||
12723 12724 12725 12726 12727 12728 12729 | exec 6>&1 # Save the log message, to keep $[0] and so on meaningful, and to # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" | | | 12861 12862 12863 12864 12865 12866 12867 12868 12869 12870 12871 12872 12873 12874 12875 | exec 6>&1 # Save the log message, to keep $[0] and so on meaningful, and to # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" This file was extended by rl_json $as_me 0.11.0, which was generated by GNU Autoconf 2.63. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS CONFIG_LINKS = $CONFIG_LINKS CONFIG_COMMANDS = $CONFIG_COMMANDS $ $0 $@ |
︙ | ︙ | |||
12773 12774 12775 12776 12777 12778 12779 | $config_files Report bugs to <bug-autoconf@gnu.org>." _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_version="\\ | | | 12911 12912 12913 12914 12915 12916 12917 12918 12919 12920 12921 12922 12923 12924 12925 | $config_files Report bugs to <bug-autoconf@gnu.org>." _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_version="\\ rl_json config.status 0.11.0 configured by $0, generated by GNU Autoconf 2.63, with options \\"`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\" Copyright (C) 2008 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it." |
︙ | ︙ |
Changes to jni/rl_json/configure.ac.
1 2 3 4 5 6 | #!/bin/bash -norc dnl This file is an input file used by the GNU "autoconf" program to dnl generate the file "configure", which is run during Tcl installation dnl to configure the system for the local environment. #----------------------------------------------------------------------- | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | #!/bin/bash -norc dnl This file is an input file used by the GNU "autoconf" program to dnl generate the file "configure", which is run during Tcl installation dnl to configure the system for the local environment. #----------------------------------------------------------------------- # Sample configure.ac for Tcl Extensions. The only places you should # need to modify this file are marked by the string __CHANGE__ #----------------------------------------------------------------------- #----------------------------------------------------------------------- # __CHANGE__ # Set your package name and version numbers here. # # This initializes the environment with PACKAGE_NAME and PACKAGE_VERSION # set as provided. These will also be added as -D defs in your Makefile # so you can encode the package version directly into the source files. # This will also define a special symbol for Windows (BUILD_<PACKAGE_NAME> # so that we create the export library with the dll. #----------------------------------------------------------------------- AC_PREREQ(2.61) AC_INIT([rl_json], [0.11.0]) #-------------------------------------------------------------------- # Call TEA_INIT as the first TEA_ macro to set up initial vars. # This will define a ${TEA_PLATFORM} variable == "unix" or "windows" # as well as PKG_LIB_FILE and PKG_STUB_LIB_FILE. #-------------------------------------------------------------------- |
︙ | ︙ | |||
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | # Standard compiler checks. # This sets up CC by using the CC env var, or looks for gcc otherwise. # This also calls AC_PROG_CC and a few others to create the basic setup # necessary to compile executables. #----------------------------------------------------------------------- TEA_SETUP_COMPILER #----------------------------------------------------------------------- # __CHANGE__ # Specify the C source files to compile in TEA_ADD_SOURCES, # public headers that need to be installed in TEA_ADD_HEADERS, # stub library C source files to compile in TEA_ADD_STUB_SOURCES, # and runtime Tcl library files in TEA_ADD_TCL_SOURCES. # This defines PKG(_STUB)_SOURCES, PKG(_STUB)_OBJECTS, PKG_HEADERS # and PKG_TCL_SOURCES. #----------------------------------------------------------------------- | > > > > | | | | 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 | # Standard compiler checks. # This sets up CC by using the CC env var, or looks for gcc otherwise. # This also calls AC_PROG_CC and a few others to create the basic setup # necessary to compile executables. #----------------------------------------------------------------------- TEA_SETUP_COMPILER # Check for feature toggles ENABLE_ENSEMBLE ENABLE_DEDUP #----------------------------------------------------------------------- # __CHANGE__ # Specify the C source files to compile in TEA_ADD_SOURCES, # public headers that need to be installed in TEA_ADD_HEADERS, # stub library C source files to compile in TEA_ADD_STUB_SOURCES, # and runtime Tcl library files in TEA_ADD_TCL_SOURCES. # This defines PKG(_STUB)_SOURCES, PKG(_STUB)_OBJECTS, PKG_HEADERS # and PKG_TCL_SOURCES. #----------------------------------------------------------------------- TEA_ADD_SOURCES([parser.c rl_json.c json_types.c dedup.c api.c rl_jsonStubInit.c]) TEA_ADD_HEADERS([generic/rl_jsonDecls.h generic/rl_json.h]) TEA_ADD_INCLUDES([]) TEA_ADD_LIBS([]) TEA_ADD_CFLAGS([]) TEA_ADD_STUB_SOURCES([rl_jsonStubLib.c]) TEA_ADD_TCL_SOURCES([]) #-------------------------------------------------------------------- # __CHANGE__ # # You can add more files to clean if your extension creates any extra # files by extending CLEANFILES. # Add pkgIndex.tcl if it is generated in the Makefile instead of ./configure # and change Makefile.in to move it from CONFIG_CLEAN_FILES to BINARIES var. # # A few miscellaneous platform-specific items: # TEA_ADD_* any platform specific compiler/build info here. #-------------------------------------------------------------------- #CLEANFILES="$CLEANFILES pkgIndex.tcl doc/json.html" if test "${TEA_PLATFORM}" = "windows" ; then # Ensure no empty if clauses : #TEA_ADD_SOURCES([win/winFile.c]) #TEA_ADD_INCLUDES([-I\"$(${CYGPATH} ${srcdir}/win)\"]) else # Ensure no empty else clauses |
︙ | ︙ | |||
138 139 140 141 142 143 144 145 146 147 148 149 150 151 | #-------------------------------------------------------------------- # This macro figures out what flags to use with the compiler/linker # when building shared/static debug/optimized objects. This information # can be taken from the tclConfig.sh file, but this figures it all out. #-------------------------------------------------------------------- TEA_CONFIG_CFLAGS #-------------------------------------------------------------------- # Set the default compiler switches based on the --enable-symbols option. #-------------------------------------------------------------------- TEA_ENABLE_SYMBOLS | > > > | 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 | #-------------------------------------------------------------------- # This macro figures out what flags to use with the compiler/linker # when building shared/static debug/optimized objects. This information # can be taken from the tclConfig.sh file, but this figures it all out. #-------------------------------------------------------------------- TEA_CONFIG_CFLAGS # Check for required polyfill TIP445 #-------------------------------------------------------------------- # Set the default compiler switches based on the --enable-symbols option. #-------------------------------------------------------------------- TEA_ENABLE_SYMBOLS |
︙ | ︙ |
Changes to jni/rl_json/doc/json.n.
1 2 3 4 5 6 | '\" '\" Copyright (c) 2015 Ruby Lane '\" '\" See the file "LICENSE" for information on usage and redistribution '\" of this file, and for a DISCLAIMER OF ALL WARRANTIES. '\" | | | | | | > > > | | | > | > > > > < < | | > > > > | < < < < < < < | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | < < < < < > > > > | | > > > > > > > > > > > > > > > > > > > < < < > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 | '\" '\" Copyright (c) 2015 Ruby Lane '\" '\" See the file "LICENSE" for information on usage and redistribution '\" of this file, and for a DISCLAIMER OF ALL WARRANTIES. '\" .TH json n 0.11.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 .SH SYNOPSIS .nf \fBpackage require rl_json\fR ?\fB0.11.0\fR? \fBjson get \fIjsonValue\fR ?\fIkey ...\fR? \fBjson extract \fIjsonValue\fR ?\fIkey ...\fR? \fBjson exists \fIjsonValue\fR ?\fIkey ...\fR? \fBjson set \fIjsonVariableName\fR ?\fIkey ...\fR? \fIvalue\fR \fBjson unset \fIjsonVariableName\fR ?\fIkey ...\fR? \fBjson foreach \fIvarlist1 jsonValue1\fR ?\fIvarlist2 jsonValue2 ...\fR? \fIscript\fR \fBjson lmap \fIvarlist1 jsonValue1\fR ?\fIvarlist2 jsonValue2 ...\fR? \fIscript\fR \fBjson amap \fIvarlist1 jsonValue1\fR ?\fIvarlist2 jsonValue2 ...\fR? \fIscript\fR \fBjson omap \fIvarlist1 jsonValue1\fR ?\fIvarlist2 jsonValue2 ...\fR? \fIscript\fR \fBjson string \fIvalue\fR \fBjson number \fIvalue\fR \fBjson boolean \fIvalue\fR \fBjson object \fI?key value ?key value ...??\fR \fBjson array \fIelem ...\fR \fBjson bool \fIvalue\fR \fBjson normalize \fIjsonValue\fR \fBjson pretty \fIjsonValue\fR \fBjson template \fIjsonValue\fR ?\fIdictionary\fR? \fBjson isnull \fIjsonValue\fR ?\fIkey ...\fR? \fBjson type \fIjsonValue\fR ?\fIkey ...\fR? \fBjson length \fIjsonValue\fR ?\fIkey ...\fR? \fBjson keys \fIjsonValue\fR ?\fIkey ...\fR? \fBjson decode \fIbytes\fR ?\fIencoding\fR? \fBjson valid ?\fB-extensions\fR \fIextensionlist\fR? ?\fB-details\fR \fIdetailsvar\fR? \fIjsonValue\fR .fi .BE .SH DESCRIPTION .PP This package adds a command \fBjson\fR to the interpreter, and defines a new Tcl_Obj type to store the parsed JSON document. The \fBjson\fR command directly manipulates values whose string representation is valid JSON, in a similar way to how the \fBdict\fR command directly manipulates values whose string representation is a valid dictionary. It is similar to \fBdict\fR in performance. .TP \fBjson get \fIjsonValue\fR ?\fIkey ...\fR? . Extract the value of a portion of the \fIjsonValue\fR, returns the closest native Tcl type (other than JSON) for the extracted portion. The \fIkey ...\fR arguments are a path, as described in \fBPATHS\fR below. '\" TODO: describe what happens with a null .TP \fBjson extract \fIjsonValue\fR ?\fIkey ...\fR? . Extract the value of a portion of the \fIjsonValue\fR, returns the JSON fragment. The \fIkey ...\fR arguments are a path, as described in \fBPATHS\fR below. .TP \fBjson exists \fIjsonValue\fR ?\fIkey ...\fR? . Tests whether the supplied key path (see \fBPATHS\fR below) resolves to something that exists in \fIjsonValue\fR (i.e., that it can be used with \fBjson get\fR without error) and is not null. Returns false if the value named by the path \fIkey ...\fR is null. .TP \fBjson set \fIjsonVariableName\fR ?\fIkey ...\fR? \fIvalue\fR . Updates the JSON value stored in the variable \fIjsonVariableName\fR, replacing the value referenced by \fIkey ...\fR (a path as described in \fBPATHS\fR below) with the JSON value \fIvalue\fR. If \fIvalue\fR is a valid JSON as given by the JSON grammar, it is added as that JSON type, otherwise it is converted to a JSON string. Thus the following are equivalent (modulo efficiency): .CS json set doc foo [json string baz] json set doc bar [json number 123] json set doc baz [json boolean true] #------------------------------------------ json set doc foo baz json set doc bar 123 json set doc baz true .CE Watch out for unintended behaviour if the value might look like a boolean or number but not meet the JSON grammar for those types, in which case the value is converted to a JSON string: .CS json set doc foo [json boolean yes] # Key "foo" contains the JSON boolean value "true" json set doc foo yes # Key "foo" contains the JSON string value "yes" .CE Constructing the values using [\fBjson \fItype\fR] forces the conversion to the specified JSON type, or throws an exception if that can't be done. Which is more efficent will depend on the situation: .CS set doc {[]} for {set i 0} {$i < 100} {incr i} { json set doc end+1 [json boolean true] ;# 1 json set doc end+1 true ;# 2 } # 2 will be faster since "true" will be stored as a literal, and converted # to a JSON boolean. Each loop iteration will just append another reference # to this static value to the array, whereas 1 will call [json boolean] each # iteration. set doc {[]} for {set i 0} {$i < 100} {incr i} { json set doc end+1 [json string false$i] ;# 1 json set doc end+1 false$i ;# 2 } # 1 will be faster since [json string] knows what the type is and directly # creates the new element as that type. 2 Needs to parse the string to # determine the type. .CE .TP \fBjson unset \fIjsonVariableName\fR ?\fIkey ...\fR? . Updates the JSON value stored in the variable \fIjsonVariableName\fR, removing the value referenced by \fIkey ...\fR, a path as described in \fBPATHS\fR below. If the path names a entry in an object then that key is removed from the object. If the path names an element in an array, that element is removed and all later elements are moved up. .TP \fBjson template \fIjsonValue\fR ?\fIdictionary\fR? . Return a JSON value by interpolating the values from \fIdictionary\fR into the template, or from variables in the current scope if \fIdictionary\fR is not supplied, in the manner described in the section \fBTEMPLATES\fR. .TP \fBjson string \fIvalue\fR . Return a JSON string with the value \fIvalue\fR. .TP \fBjson number \fIvalue\fR . Return a JSON number with the value \fIvalue\fR. .TP \fBjson boolean \fIvalue\fR . Return a JSON boolean with the value \fIvalue\fR. Any of the forms accepted by Tcl_GetBooleanFromObj are accepted and normalized. .TP \fBjson object \fI?key value ?key value ...??\fR -or- \fBjson object \fIpacked_value\fR . Return a JSON object with the each of the keys and values given. \fIvalue\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. The alternate syntax \fBjson object \fIpacked_value\fR takes the list of keys and values as a single arg instead of a list of args, but is otherwise the same. .TP \fBjson array \fI?elem ...?\fR . 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 foreach \fIvarList1 jsonValue1\fR ?\fIvarList2 jsonValue2 ...\fR? \fIscript\fR . Evaluate \fIscript\fR in a loop in a similar way to the \fBforeach\fR command. In each iteration, the values stored in the iterator variables in each \fIvarList\fR are the JSON fragments from \fIjsonValue\fR. This command supports iterating over JSON arrays and JSON objects. In the JSON object case, the corresponding \fIvarList\fR must be a two element list, with the first specifiying the variable to hold the key and the second the value. In the JSON array case, the rules are the same as the \fBforeach\fR command. .TP \fBjson lmap \fIvarList1 jsonValue1\fR ?\fIvarList2 jsonValue2 ...\fR? \fIscript\fR . As for \fBjson foreach\fR, except that it is collecting; the result from each evaluation of \fIscript\fR is added to a Tcl list and returned as the result of the \fBjson lmap\fR command. If the \fIscript\fR results in a TCL_CONTINUE code (e.g., the script does \fBcontinue\fR), that iteration is skipped and no element is added to the result list. If it results in TCL_BREAK (e.g., the script does \fBbreak\fR) the iterations are stopped and the results accumulated so far are returned. .TP \fBjson amap \fIvarList1 jsonValue1\fR ?\fIvarList2 jsonValue2 ...\fR? \fIscript\fR . As for \fBjson lmap\fR, but the result is a JSON array rather than a list. If the result of each iteration is a JSON value it is added to the array as-is, otherwise it is converted to a JSON string. .TP \fBjson omap \fIvarList1 jsonValue1\fR ?\fIvarList2 jsonValue2 ...\fR? \fIscript\fR . As for \fBjson lmap\fR, but the result is a JSON object rather than a list. The result of each iteration must be a dictionary (or a list of 2n elements, including n = 0). Tcl_ObjType snooping is done to ensure that the iteration over the result is efficient for both dict and list cases. Each entry in the dictionary will be added to the result object. If the value for each key in the iteration result is a JSON value it is added to the array as-is, otherwise it is converted to a JSON string. .TP \fBjson isnull \fIjsonVariableName\fR ?\fIkey ...\fR? . Return a boolean indicating whether the named JSON value is null. .TP \fBjson type \fIjsonVariableName\fR ?\fIkey ...\fR? . Return the type of the named JSON value, one of "object", "array", "string", "number", "boolean" or "null". .TP \fBjson length \fIjsonVariableName\fR ?\fIkey ...\fR? . Return the length of the of the named JSON array, number of entries in the named JSON object, or number of characters in the named JSON string. Other value types aren't supported. .TP \fBjson keys \fIjsonVariableName\fR ?\fIkey ...\fR? . Return the keys in the of the named JSON object, found by following the path of \fIkey\fRs. .TP \fBjson normalize \fIjsonValue\fR . Return a .QW normalized version of the input \fIjsonValue\fR, i.e., with all optional whitespace trimmed. .TP \fBjson pretty \fIjsonValue\fR . Returns a pretty-printed string representation of \fIjsonValue\fR. Useful for debugging or inspecting the structure of JSON data. .TP \fBjson decode \fIbytes\fR ?\fIencoding\fR? . Rl_json operates on characters, as returned from Tcl's Tcl_GetStringFromObj, not raw bytes, so considerations of encoding are strictly outside of its scope (other than ignoring a byte order mark if the string starts with one). The JSON RFC lays out some behaviour for conforming implementations regarding character encoding, and ensuring that an application using rl_json meets that standard would be up to the application. Some aspects are not straightforward, so rl_json provides this utility subcommand that takes binary data in \fIbytes\fR and returns a character string according to the RFC specified behaviour. If the optional \fIencoding\fR argument is given, that encoding will be used to interpret \fIbytes\fR. The supported encodings are those specified in the RFC: utf-8, utf-16le, utf-16be, utf-32le, utf-32be. If the string starts with a BOM (byte order mark (U+FFFE)), and no encoding is given, it will be determined from the encoding of the BOM. All the encodings listed are supported, even if Tcl lacks support for the utf-16 and utf-32 encodings natively. However, without native support the conversion will be slow. This might look something like this in an application: .CS proc readjson file { set h [open $file rb] ;# Note that the file is opened in binary mode - no encoding try { json decode [read $h] } finally { close $h } } .CE .TP \fBjson valid\fR ?\fB-extensions\fR \fIextensionlist\fR? ?\fB-details\fR \fIdetails\fR? \fIjsonValue\fR . Validate \fBjsonValue\fR against the JSON grammar, returning true of it conforms and false otherwise. A list of extensions to accept can be supplied with \fB-extensions\fR, with only one currently supported extension: \fBcomments\fR, which accepts JSON documents containing \fB// foo\fR and \fB/* foo */\fR style comments anywhere whitespace would be valid. To reject documents containing comments, set \fIextensionlist\fR to {}. Validation using this subcommand is about 3 times faster than parsing and catching a parsing exception, and it allows strict validation against the RFC without comments. If validation fails and \fB-details\fR \fIdetailsvar\fR is supplied, the variable \fIdetailsvar\fR is set to a dictionary containing the keys: .RS 10 .IP \fBerrmsg\fR 10 A reason for the failure. .IP \fBdoc\fR 10 The document that failed validation .IP \fBchar_ofs\fR 10 The character offset into \fBdoc\fR that caused validation to fail. .RE .SH PATHS .PP Several of the commands (e.g., \fBjson get\fR, \fBjson exists\fR, \fBjson set\fR and \fBjson unset\fR) accept a path specification that names some subset of the supplied \fIjsonValue\fR. The rules are similar to the equivalent concept in the \fBdict\fR command, except that the paths used by \fBjson\fR allow indexing into JSON arrays by the integer key (or a string matching the regex .QW "^end(-[0-9]+)?$" ). .SH TEMPLATES .PP The command \fBjson template\fR generates JSON documents by interpolating values into a template from a supplied dictionary or variables in the current call frame, a flexible mechanism for generating complex documents. The templates are valid JSON documents containing string values which match the regex |
︙ | ︙ | |||
259 260 261 262 263 264 265 | .CE .PP Incrementally append an element to an array (similar to \fBdict lappend\fR): .PP .CS set doc {{"foo":[]}} for {set i 0} {$i < 4} {incr i} { | | | | 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 | .CE .PP Incrementally append an element to an array (similar to \fBdict lappend\fR): .PP .CS set doc {{"foo":[]}} for {set i 0} {$i < 4} {incr i} { json set doc foo end+1 [json string "elem: $i"] } # $doc is {"foo":["elem 0","elem 1","elem 2","elem 3"]} .CE .PP Similar to the above, but prepend the elements instead: .PP .CS set doc {{"foo":[]}} for {set i 0} {$i < 4} {incr i} { json set doc foo -1 [json string "elem: $i"] } # $doc is {"foo":["elem 3","elem 2","elem 1","elem 0"]} .CE .PP Trim an element out of an array: .PP .CS |
︙ | ︙ |
Added jni/rl_json/fetch_test_cases.tcl.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 | #!/usr/bin/env tclsh # Doesn't work. It should, but github is serving the binary .json files as utf-8, which breaks all the carefully # crafted encoding tests # Dependencies: # rl_json: https://github.com/RubyLane/rl_json # parse_args: https://github.com/RubyLane/parse_args # rl_http: https://github.com/RubyLane/rl_http # urlencode: copied to support/ # uri: tcllib # Thread: https://core.tcl-lang.org/thread # tls: https://core.tcl-lang.org/tcltls/index set here [file dirname [file normalize [info script]]] tcl::tm::path add [file join $here support] lappend auto_path $here package require rl_json ;# yeah... package require rl_http package require parse_args package require urlencode interp alias {} json {} ::rl_json::json interp alias {} parse_args {} ::parse_args::parse_args proc http {method url args} { #<<< parse_args $args { -log {-default {{lvl msg} {}}} -if_modified_since {-# {seconds since 1970-01-01 00:00:00 UTC}} } set headers {} if {[info exists if_modified_since]} { lappend headers If-Modified-Since [clock format $if_modified_since -format {%a, %d %b %Y %H:%M:%S GMT} -gmt 1] #puts "If-Modified-Since: [lindex $headers end]" } while {[incr tries] <= 3} { try { apply $log notice "Fetching $method $url" rl_http instvar h $method $url -headers $headers } on error {errmsg options} { throw [list HTTP RL_HTTP {*}[dict get $options -errorcode]] $errmsg } switch -glob -- [$h code] { 2* { #puts "Headers:\n\t[join [lmap {k v} [$h headers] {format {%s: %s} $k $v}] \n\t]" return [$h body] } 304 {throw [list HTTP CODE [$h code]] "Not modified"} 301 - 302 - 307 {set url [lindex [dict get [$h headers] location] 0]} 403 { if {[dict exists [$h headers] x-ratelimit-limit]} { # Rate limiting <<< set limit [lindex [dict get [$h headers] x-ratelimit-limit] 0] set remaining [lindex [dict get [$h headers] x-ratelimit-remaining] 0] set reset [lindex [dict get [$h headers] x-ratelimit-reset] 0] throw [list HTTP RATELIMIT $reset $limit $remaining] "Rate limited until [clock format $reset]" #>>> } throw [list HTTP CODE [$h code]] [$h body] } 503 { apply $log warning "Got 503 error, waiting and trying again" after 2000 } default { throw [list HTTP CODE [$h code]] "Error fetching $method $url: [$h code]\n[$h body]" } } } throw [list HTTP TOO_MANY_TRIES [expr {$tries-1}]] "Ran out of patience fetching $method $endoint after $tries failures" } #>>> namespace eval ::github { namespace export * namespace ensemble create -prefixes no proc endpoint args { #<<< parse_args $args { -owner {-required} -repo {-required} args {-name path_parts} } string cat \ https://api.github.com/repos/ \ [urlencode rfc_urlencode -part path -- $owner] \ / \ [urlencode rfc_urlencode -part path -- $repo] \ /contents/ \ [join [lmap e $path_parts {urlencode rfc_urlencode -part path -- $e}] /] } #>>> proc api {method args} { #<<< set endpoint [github endpoint {*}$args] http $method $endpoint } #>>> } proc writetext {fn data} { #<<< set h [open $fn w] try { puts -nonewline $h $data } finally { close $h } } #>>> proc writebin {fn data} { #<<< set h [open $fn wb] try { puts -nonewline $h $data } finally { close $h } } #>>> proc fetch_file {dest file} { #<<< set fn [file join $dest [json get $file name]] if {[file exists $fn]} { set mtime [file mtime $fn] try { puts -nonewline "Fetching [json get $file name] -if_modified_since $mtime" http GET [json get $file download_url] -if_modified_since $mtime } on ok contents { puts " [string length $contents] bytes" writebin [file join $dest [json get $file name]] $contents } trap {HTTP CODE 304} {} { puts " not modified" return } on error {errmsg options} { puts " Error: $errmsg" } } else { try { puts -nonewline "Fetching [json get $file name]" http GET [json get $file download_url] } on ok contents { puts " [string length $contents] bytes" writebin [file join $dest [json get $file name]] $contents } on error {errmsg options} { puts " Error: $errmsg" } } } #>>> set dest [file join $here tests JSONTestSuite test_parsing] file mkdir $dest set listing [github api GET -owner nst -repo JSONTestSuite test_parsing] #puts [json pretty $listing] json foreach file $listing { fetch_file $dest $file } set dest [file join $here tests JSONTestSuite test_transform] file mkdir $dest set listing [github api GET -owner nst -repo JSONTestSuite test_transform] #puts [json pretty $listing] json foreach file $listing { fetch_file $dest $file } # vim: foldmethod=marker foldmarker=<<<,>>> ts=4 shiftwidth=4 |
Added jni/rl_json/generic/api.c.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 | #include "rl_jsonInt.h" #include "parser.h" Tcl_Obj* JSON_NewJSONObj(Tcl_Interp* interp, Tcl_Obj* from) //{{{ { return as_json(interp, from); } //}}} int JSON_NewJStringObj(Tcl_Interp* interp, Tcl_Obj* string, Tcl_Obj** new) //{{{ { replace_tclobj(new, JSON_NewJvalObj(JSON_STRING, string)); return TCL_OK; } //}}} int JSON_NewJNumberObj(Tcl_Interp* interp, Tcl_Obj* number, Tcl_Obj** new) //{{{ { Tcl_Obj* forced = NULL; struct interp_cx* l = Tcl_GetAssocData(interp, "rl_json", NULL); TEST_OK(force_json_number(interp, l, number, &forced)); replace_tclobj(new, JSON_NewJvalObj(JSON_NUMBER, forced)); release_tclobj(&forced); return TCL_OK; } //}}} int JSON_NewJBooleanObj(Tcl_Interp* interp, Tcl_Obj* boolean, Tcl_Obj** new) //{{{ { struct interp_cx* l = Tcl_GetAssocData(interp, "rl_json", NULL); int bool; TEST_OK(Tcl_GetBooleanFromObj(interp, boolean, &bool)); replace_tclobj(new, bool ? l->json_true : l->json_false); return TCL_OK; } //}}} int JSON_NewJNullObj(Tcl_Interp* interp, Tcl_Obj* *new) //{{{ { struct interp_cx* l = Tcl_GetAssocData(interp, "rl_json", NULL); replace_tclobj(new, l->json_null); return TCL_OK; } //}}} int JSON_NewJObjectObj(Tcl_Interp* interp, Tcl_Obj** new) //{{{ { struct interp_cx* l = Tcl_GetAssocData(interp, "rl_json", NULL); replace_tclobj(new, JSON_NewJvalObj(JSON_OBJECT, l->tcl_empty_dict)); return TCL_OK; } //}}} int JSON_NewJArrayObj(Tcl_Interp* interp, int objc, Tcl_Obj* objv[], Tcl_Obj** new) //{{{ { struct interp_cx* l = Tcl_GetAssocData(interp, "rl_json", NULL); if (objc == 0) { replace_tclobj(new, JSON_NewJvalObj(JSON_OBJECT, l->tcl_empty_list)); } else { int i; for (i=0; i<objc; i++) TEST_OK(JSON_ForceJSON(interp, objv[i])); replace_tclobj(new, JSON_NewJvalObj(JSON_OBJECT, Tcl_NewListObj(objc, objv))); } return TCL_OK; } //}}} int JSON_NewTemplateObj(Tcl_Interp* interp, enum json_types type, Tcl_Obj* key, Tcl_Obj** new) //{{{ { if (!type_is_dynamic(type)) { Tcl_SetObjResult(interp, Tcl_ObjPrintf("Requested type is not a valid template type: %s", type_names_int[type])); return TCL_ERROR; } replace_tclobj(new, JSON_NewJvalObj(type, key)); return TCL_OK; } //}}} int JSON_ForceJSON(Tcl_Interp* interp, Tcl_Obj* obj) // Force a conversion to a JSON objtype, or throw an exception {{{ { Tcl_ObjIntRep* ir; enum json_types type; TEST_OK(JSON_GetIntrepFromObj(interp, obj, &type, &ir)); return TCL_OK; } //}}} enum json_types JSON_GetJSONType(Tcl_Obj* obj) //{{{ { Tcl_ObjIntRep* ir = NULL; enum json_types t; for (t=JSON_OBJECT; t<JSON_TYPE_MAX && ir==NULL; t++) ir = Tcl_FetchIntRep(obj, g_objtype_for_type[t]); return (ir == NULL) ? JSON_UNDEF : t-1; } //}}} int JSON_GetObjFromJStringObj(Tcl_Interp* interp, Tcl_Obj* jstringObj, Tcl_Obj** stringObj) //{{{ { enum json_types type; Tcl_Obj* val = NULL; TEST_OK(JSON_GetJvalFromObj(interp, jstringObj, &type, &val)); if (type_is_dynamic(type)) { replace_tclobj(stringObj, Tcl_ObjPrintf("%s%s", get_dyn_prefix(type), Tcl_GetString(val))); return TCL_OK; } else if (type == JSON_STRING) { replace_tclobj(stringObj, val); return TCL_OK; } else { Tcl_SetObjResult(interp, Tcl_ObjPrintf("Asked for string from json string but supplied json %s", get_type_name(type))); return TCL_ERROR; } } //}}} int JSON_GetObjFromJNumberObj(Tcl_Interp* interp, Tcl_Obj* jnumberObj, Tcl_Obj** numberObj) //{{{ { enum json_types type; Tcl_Obj* val = NULL; TEST_OK(JSON_GetJvalFromObj(interp, jnumberObj, &type, &val)); if (type != JSON_NUMBER) { Tcl_SetObjResult(interp, Tcl_ObjPrintf("Asked for number from json number but supplied json %s", get_type_name(type))); return TCL_ERROR; } replace_tclobj(numberObj, val); return TCL_OK; } //}}} int JSON_GetObjFromJBooleanObj(Tcl_Interp* interp, Tcl_Obj* jbooleanObj, Tcl_Obj** booleanObj) //{{{ { enum json_types type; Tcl_Obj* val = NULL; TEST_OK(JSON_GetJvalFromObj(interp, jbooleanObj, &type, &val)); if (type != JSON_BOOL) { Tcl_SetObjResult(interp, Tcl_ObjPrintf("Asked for boolean from json boolean but supplied json %s", get_type_name(type))); return TCL_ERROR; } replace_tclobj(booleanObj, val); return TCL_OK; } //}}} int JSON_JArrayObjAppendElement(Tcl_Interp* interp, Tcl_Obj* arrayObj, Tcl_Obj* elem) //{{{ { enum json_types type; Tcl_ObjIntRep* ir = NULL; Tcl_Obj* val = NULL; if (Tcl_IsShared(arrayObj)) { // Tcl_Panic? Tcl_SetObjResult(interp, Tcl_ObjPrintf("JSON_JArrayObjAppendElement called with shared object")); return TCL_ERROR; } TEST_OK(JSON_GetIntrepFromObj(interp, arrayObj, &type, &ir)); if (type != JSON_ARRAY) // Turn it into one by creating a new array with a single element containing the old value TEST_OK(JSON_SetIntRep(arrayObj, JSON_ARRAY, Tcl_NewListObj(1, &val))); val = get_unshared_val(ir); TEST_OK(Tcl_ListObjAppendElement(interp, val, as_json(interp, elem))); release_tclobj((Tcl_Obj**)&ir->twoPtrValue.ptr2); Tcl_InvalidateStringRep(arrayObj); return TCL_OK; } //}}} int JSON_JArrayObjAppendList(Tcl_Interp* interp, Tcl_Obj* arrayObj, Tcl_Obj* elems /* a JArrayObj or ListObj */ ) //{{{ { enum json_types type, elems_type; Tcl_ObjIntRep* ir = NULL; Tcl_Obj* val = NULL; Tcl_Obj* elems_val = NULL; int retval = TCL_OK; if (Tcl_IsShared(arrayObj)) { // Tcl_Panic? Tcl_SetObjResult(interp, Tcl_ObjPrintf("JSON_JArrayObjAppendElement called with shared object")); return TCL_ERROR; } TEST_OK(JSON_GetIntrepFromObj(interp, arrayObj, &type, &ir)); if (type != JSON_ARRAY) // Turn it into one by creating a new array with a single element containing the old value TEST_OK(JSON_SetIntRep(arrayObj, JSON_ARRAY, Tcl_NewListObj(1, &val))); val = get_unshared_val(ir); if (JSON_GetJvalFromObj(interp, elems, &elems_type, &elems_val) == TCL_OK) { switch (elems_type) { case JSON_ARRAY: // Given a JSON array, append its elements TEST_OK(Tcl_ListObjAppendList(interp, val, elems_val)); break; case JSON_OBJECT: // Given a JSON object, append its keys as strings and values as whatever they were { Tcl_DictSearch search; Tcl_Obj* k = NULL; Tcl_Obj* kjstring = NULL; Tcl_Obj* v = NULL; int done; TEST_OK(Tcl_DictObjFirst(interp, elems_val, &search, &k, &v, &done)); for (; !done; Tcl_DictObjNext(&search, &k, &v, &done)) { TEST_OK_BREAK(retval, JSON_NewJStringObj(interp, k, &kjstring)); TEST_OK_BREAK(retval, Tcl_ListObjAppendElement(interp, val, kjstring)); TEST_OK_BREAK(retval, Tcl_ListObjAppendElement(interp, val, v)); } release_tclobj(&kjstring); Tcl_DictObjDone(&search); } break; default: // elems is JSON, but not a sensible type for this call Tcl_SetObjResult(interp, Tcl_ObjPrintf("Could not append elements - not a JSON array, JSON object or list: %s", get_type_name(elems_type))); return TCL_ERROR; } } else { TEST_OK(Tcl_ListObjAppendList(interp, val, elems)); } return retval; } //}}} int JSON_SetJArrayObj(Tcl_Interp* interp, Tcl_Obj* obj, const int objc, Tcl_Obj* objv[]) //{{{ { enum json_types type; Tcl_ObjIntRep* ir = NULL; Tcl_Obj* val = NULL; int i, retval = TCL_OK; Tcl_Obj** jov = NULL; Tcl_Obj* newlist = NULL; if (Tcl_IsShared(obj)) { // Tcl_Panic? Tcl_SetObjResult(interp, Tcl_ObjPrintf("JSON_SetJArrayObj called with shared object")); return TCL_ERROR; } jov = ckalloc(sizeof(Tcl_Obj*) * objc); for (i=0; i<objc; i++) Tcl_IncrRefCount(jov[i] = as_json(interp, objv[i])); // Possibly silly optimization: if obj is already a JSON array, call Tcl_SetListObj on its intrep list. // All this saves is freeing the old intrep list and creating a fresh one, at the cost of some other overhead if (JSON_IsJSON(obj, &type, &ir)) { val = get_unshared_val(ir); Tcl_SetListObj(val, objc, jov); } else { replace_tclobj(&newlist, Tcl_NewListObj(objc, jov)); retval = JSON_SetIntRep(obj, JSON_ARRAY, newlist); } release_tclobj(&newlist); if (jov) { for (i=0; i<objc; i++) release_tclobj(&jov[i]); ckfree(jov); jov = NULL; } return retval; } //}}} int JSON_JArrayObjGetElements(Tcl_Interp* interp, Tcl_Obj* arrayObj, int* objc, Tcl_Obj*** objv) //{{{ { enum json_types type; Tcl_Obj* val = NULL; TEST_OK(JSON_GetJvalFromObj(interp, arrayObj, &type, &val)); if (type != JSON_ARRAY) { Tcl_SetObjResult(interp, Tcl_ObjPrintf("Expecting a JSON array, but got a JSON %s", get_type_name(type))); return TCL_ERROR; } TEST_OK(Tcl_ListObjGetElements(interp, val, objc, objv)); return TCL_OK; } //}}} int JSON_JArrayObjIndex(Tcl_Interp* interp, Tcl_Obj* arrayObj, int index, Tcl_Obj** elem) //{{{ { enum json_types type; Tcl_Obj* val = NULL; TEST_OK(JSON_GetJvalFromObj(interp, arrayObj, &type, &val)); if (type != JSON_ARRAY) { Tcl_SetObjResult(interp, Tcl_ObjPrintf("Expecting a JSON array, but got a JSON %s", get_type_name(type))); return TCL_ERROR; } TEST_OK(Tcl_ListObjIndex(interp, val, index, elem)); return TCL_OK; } //}}} int JSON_JArrayObjReplace(Tcl_Interp* interp, Tcl_Obj* arrayObj, int first, int count, int objc, Tcl_Obj* objv[]) //{{{ { enum json_types type; Tcl_Obj* val = NULL; Tcl_Obj** jov = NULL; int i, retval=TCL_OK; TEST_OK(JSON_GetJvalFromObj(interp, arrayObj, &type, &val)); if (type != JSON_ARRAY) { Tcl_SetObjResult(interp, Tcl_ObjPrintf("Expecting a JSON array, but got a JSON %s", get_type_name(type))); return TCL_ERROR; } jov = ckalloc(sizeof(Tcl_Obj*) * objc); for (i=0; i<objc; i++) Tcl_IncrRefCount(jov[i] = as_json(interp, objv[i])); retval = Tcl_ListObjReplace(interp, val, first, count, objc, jov); if (jov) { for (i=0; i<objc; i++) release_tclobj(&jov[i]); ckfree(jov); jov = NULL; } return retval; } //}}} // TODO: JObject interface, similar to DictObj int JSON_Get(Tcl_Interp* interp, Tcl_Obj* obj, Tcl_Obj* path, Tcl_Obj** res) //{{{ { int retval = TCL_OK; Tcl_Obj* jval = NULL; Tcl_Obj* astcl = NULL; retval = JSON_Extract(interp, obj, path, &jval); if (retval == TCL_OK) retval = convert_to_tcl(interp, jval, &astcl); if (retval == TCL_OK) replace_tclobj(res, astcl); release_tclobj(&astcl); release_tclobj(&jval); return retval; } //}}} int JSON_Extract(Tcl_Interp* interp, Tcl_Obj* obj, Tcl_Obj* path, Tcl_Obj** res) //{{{ { Tcl_Obj* target = NULL; int retval=TCL_OK; Tcl_Obj** pathv = NULL; int pathc; TEST_OK(Tcl_ListObjGetElements(interp, path, &pathc, &pathv)); if (pathc > 0) { TEST_OK(resolve_path(interp, obj, pathv, pathc, &target, 0, 0)); } else { TEST_OK(JSON_ForceJSON(interp, obj)); replace_tclobj(&target, obj); } if (retval == TCL_OK) replace_tclobj(res, target); release_tclobj(&target); return retval; } //}}} int JSON_Exists(Tcl_Interp* interp, Tcl_Obj* obj, Tcl_Obj* path, int* exists) //{{{ { struct interp_cx* l = Tcl_GetAssocData(interp, "rl_json", NULL); Tcl_Obj* target = NULL; Tcl_Obj** pathv = NULL; int pathc; TEST_OK(Tcl_ListObjGetElements(interp, path, &pathc, &pathv)); if (pathc > 0) { TEST_OK(resolve_path(interp, obj, pathv, pathc, &target, 1, 0)); release_tclobj(&target); // resolve_path sets the interp result in exists mode *exists = (Tcl_GetObjResult(interp) == l->json_true); Tcl_ResetResult(interp); } else { enum json_types type = JSON_GetJSONType(obj); *exists = (type != JSON_UNDEF && type != JSON_NULL); } return TCL_OK; } //}}} int JSON_Set(Tcl_Interp* interp, Tcl_Obj* obj, Tcl_Obj *path, Tcl_Obj* replacement) //{{{ { int i, pathc; enum json_types type, newtype; Tcl_ObjIntRep* ir = NULL; Tcl_Obj* val = NULL; Tcl_Obj* step; Tcl_Obj* src; Tcl_Obj* target; Tcl_Obj* newval; Tcl_Obj* rep = NULL; Tcl_Obj** pathv = NULL; src = Tcl_ObjGetVar2(interp, obj, NULL, 0); if (src == NULL) { src = Tcl_ObjSetVar2(interp, obj, NULL, JSON_NewJvalObj(JSON_OBJECT, Tcl_NewDictObj()), TCL_LEAVE_ERR_MSG); } if (Tcl_IsShared(src)) { src = Tcl_ObjSetVar2(interp, obj, NULL, Tcl_DuplicateObj(src), TCL_LEAVE_ERR_MSG); if (src == NULL) return TCL_ERROR; } /* fprintf(stderr, "JSON_Set, obj: \"%s\", src: \"%s\"\n", Tcl_GetString(obj), Tcl_GetString(src)); */ target = src; TEST_OK(JSON_GetIntrepFromObj(interp, target, &type, &ir)); val = get_unshared_val(ir); TEST_OK(Tcl_ListObjGetElements(interp, path, &pathc, &pathv)); // Walk the path as far as it exists in src //fprintf(stderr, "set, initial type %s\n", type_names[type]); for (i=0; i<pathc; i++) { step = pathv[i]; //fprintf(stderr, "looking at step %s, cx type: %s\n", Tcl_GetString(step), type_names_int[type]); switch (type) { case JSON_UNDEF: //{{{ THROW_ERROR("Found JSON_UNDEF type jval following path"); //}}} case JSON_OBJECT: //{{{ TEST_OK(Tcl_DictObjGet(interp, val, step, &target)); if (target == NULL) { //fprintf(stderr, "Path element %d: \"%s\" doesn't exist creating a new key for it and storing a null\n", // i, Tcl_GetString(step)); target = JSON_NewJvalObj(JSON_NULL, NULL); TEST_OK(Tcl_DictObjPut(interp, val, step, target)); i++; goto followed_path; } if (Tcl_IsShared(target)) { //fprintf(stderr, "Path element %d: \"%s\" exists but the TclObj is shared (%d), replacing it with an unshared duplicate\n", // i, Tcl_GetString(step), target->refCount); target = Tcl_DuplicateObj(target); TEST_OK(Tcl_DictObjPut(interp, val, step, target)); } break; //}}} case JSON_ARRAY: //{{{ { int ac, index_str_len, ok=1; long index; const char* index_str; char* end; Tcl_Obj** av; TEST_OK(Tcl_ListObjGetElements(interp, val, &ac, &av)); //fprintf(stderr, "descending into array of length %d\n", ac); if (Tcl_GetLongFromObj(NULL, step, &index) != TCL_OK) { // Index isn't an integer, check for end(+/-int)? index_str = Tcl_GetStringFromObj(step, &index_str_len); if (index_str_len < 3 || strncmp("end", index_str, 3) != 0) ok = 0; if (ok) { index = ac-1; if (index_str_len >= 4) { if (index_str[3] != '-' && index_str[3] != '+') { ok = 0; } else { // errno is magically thread-safe on POSIX // systems (it's thread-local) errno = 0; index += strtol(index_str+3, &end, 10); if (errno != 0 || *end != 0) ok = 0; } } } if (!ok) THROW_ERROR("Expected an integer index or end(+/-integer)?, got ", Tcl_GetString(step)); //fprintf(stderr, "Resolved index of %ld from \"%s\"\n", index, index_str); } else { //fprintf(stderr, "Explicit index: %ld\n", index); } if (index < 0) { // Prepend element to the array target = JSON_NewJvalObj(JSON_NULL, NULL); TEST_OK(Tcl_ListObjReplace(interp, val, -1, 0, 1, &target)); i++; goto followed_path; } else if (index >= ac) { int new_i; for (new_i=ac; new_i<index; new_i++) { TEST_OK(Tcl_ListObjAppendElement(interp, val, JSON_NewJvalObj(JSON_NULL, NULL))); } target = JSON_NewJvalObj(JSON_NULL, NULL); TEST_OK(Tcl_ListObjAppendElement(interp, val, target)); i++; goto followed_path; } else { target = av[index]; if (Tcl_IsShared(target)) { target = Tcl_DuplicateObj(target); TEST_OK(Tcl_ListObjReplace(interp, val, index, 1, 1, &target)); } //fprintf(stderr, "extracted index %ld: (%s)\n", index, Tcl_GetString(target)); } } break; //}}} case JSON_STRING: case JSON_NUMBER: case JSON_BOOL: case JSON_NULL: case JSON_DYN_STRING: case JSON_DYN_NUMBER: case JSON_DYN_BOOL: case JSON_DYN_JSON: case JSON_DYN_TEMPLATE: case JSON_DYN_LITERAL: THROW_ERROR("Attempt to index into atomic type ", get_type_name(type), " at path key \"", Tcl_GetString(step), "\""); /* i++; goto followed_path; */ default: THROW_ERROR("Unhandled type: ", Tcl_GetString(Tcl_NewIntObj(type))); } TEST_OK(JSON_GetIntrepFromObj(interp, target, &type, &ir)); val = get_unshared_val(ir); } goto set_val; followed_path: TEST_OK(JSON_GetIntrepFromObj(interp, target, &type, &ir)); val = get_unshared_val(ir); // target points at the (first) object to replace. It and its internalRep // are unshared // If any path elements remain then they need to be created as object // keys //fprintf(stderr, "After walking path, %d elements remain to be created\n", pathc-i); for (; i<pathc; i++) { //fprintf(stderr, "create walk %d: %s, cx type: %s\n", i, Tcl_GetString(pathv[i]), type_names_int[type]); if (type != JSON_OBJECT) { //fprintf(stderr, "Type isn't JSON_OBJECT: %s, replacing with a JSON_OBJECT\n", type_names_int[type]); if (val != NULL) Tcl_DecrRefCount(val); val = Tcl_NewDictObj(); TEST_OK(JSON_SetIntRep(target, JSON_OBJECT, val)); } target = JSON_NewJvalObj(JSON_OBJECT, Tcl_NewDictObj()); //fprintf(stderr, "Adding key \"%s\"\n", Tcl_GetString(pathv[i])); TEST_OK(Tcl_DictObjPut(interp, val, pathv[i], target)); TEST_OK(JSON_GetJvalFromObj(interp, target, &type, &val)); //fprintf(stderr, "Newly added key \"%s\" is of type %s\n", Tcl_GetString(pathv[i]), type_names_int[type]); // This was just created - it can't be shared } set_val: //fprintf(stderr, "Reached end of path, calling JSON_SetIntRep for replacement value %s (%s), target is %s\n", // type_names_int[newtype], Tcl_GetString(replacement), type_names_int[type]); rep = as_json(interp, replacement); TEST_OK(JSON_GetJvalFromObj(interp, rep, &newtype, &newval)); TEST_OK(JSON_SetIntRep(target, newtype, newval)); Tcl_InvalidateStringRep(src); if (interp) Tcl_SetObjResult(interp, src); return TCL_OK; } //}}} int JSON_Unset(Tcl_Interp* interp, Tcl_Obj* obj, Tcl_Obj *path) //{{{ { enum json_types type; int i; Tcl_Obj* val; Tcl_Obj* step; Tcl_Obj* src; Tcl_Obj* target; int pathc; Tcl_Obj** pathv = NULL; src = Tcl_ObjGetVar2(interp, obj, NULL, TCL_LEAVE_ERR_MSG); if (src == NULL) return TCL_ERROR; TEST_OK(Tcl_ListObjGetElements(interp, path, &pathc, &pathv)); if (pathc == 0) { Tcl_SetObjResult(interp, src); return TCL_OK; // Do Nothing Gracefully } if (Tcl_IsShared(src)) { src = Tcl_ObjSetVar2(interp, obj, NULL, Tcl_DuplicateObj(src), TCL_LEAVE_ERR_MSG); if (src == NULL) return TCL_ERROR; } /* fprintf(stderr, "JSON_Set, obj: \"%s\", src: \"%s\"\n", Tcl_GetString(obj), Tcl_GetString(src)); */ target = src; { Tcl_ObjIntRep* ir = NULL; TEST_OK(JSON_GetIntrepFromObj(interp, target, &type, &ir)); val = get_unshared_val(ir); } // Walk the path as far as it exists in src //fprintf(stderr, "set, initial type %s\n", type_names[type]); for (i=0; i<pathc-1; i++) { step = pathv[i]; //fprintf(stderr, "looking at step %s, cx type: %s\n", Tcl_GetString(step), type_names_int[type]); switch (type) { case JSON_UNDEF: //{{{ THROW_ERROR("Found JSON_UNDEF type jval following path"); //}}} case JSON_OBJECT: //{{{ TEST_OK(Tcl_DictObjGet(interp, val, step, &target)); if (target == NULL) { goto bad_path; } if (Tcl_IsShared(target)) { //fprintf(stderr, "Path element %d: \"%s\" exists but the TclObj is shared (%d), replacing it with an unshared duplicate\n", // i, Tcl_GetString(step), target->refCount); target = Tcl_DuplicateObj(target); TEST_OK(Tcl_DictObjPut(interp, val, step, target)); } break; //}}} case JSON_ARRAY: //{{{ { int ac, index_str_len, ok=1; long index; const char* index_str; char* end; Tcl_Obj** av; TEST_OK(Tcl_ListObjGetElements(interp, val, &ac, &av)); //fprintf(stderr, "descending into array of length %d\n", ac); if (Tcl_GetLongFromObj(NULL, step, &index) != TCL_OK) { // Index isn't an integer, check for end(+/-int)? index_str = Tcl_GetStringFromObj(step, &index_str_len); if (index_str_len < 3 || strncmp("end", index_str, 3) != 0) ok = 0; if (ok) { index = ac-1; if (index_str_len >= 4) { if (index_str[3] != '-' && index_str[3] != '+') { ok = 0; } else { // errno is magically thread-safe on POSIX // systems (it's thread-local) errno = 0; index += strtol(index_str+3, &end, 10); if (errno != 0 || *end != 0) ok = 0; } } } if (!ok) THROW_ERROR("Expected an integer index or end(+/-integer)?, got ", Tcl_GetString(step)); //fprintf(stderr, "Resolved index of %ld from \"%s\"\n", index, index_str); } else { //fprintf(stderr, "Explicit index: %ld\n", index); } if (index < 0) { goto bad_path; } else if (index >= ac) { goto bad_path; } else { target = av[index]; if (Tcl_IsShared(target)) { target = Tcl_DuplicateObj(target); TEST_OK(Tcl_ListObjReplace(interp, val, index, 1, 1, &target)); } //fprintf(stderr, "extracted index %ld: (%s)\n", index, Tcl_GetString(target)); } } break; //}}} case JSON_STRING: case JSON_NUMBER: case JSON_BOOL: case JSON_NULL: case JSON_DYN_STRING: case JSON_DYN_NUMBER: case JSON_DYN_BOOL: case JSON_DYN_JSON: case JSON_DYN_TEMPLATE: case JSON_DYN_LITERAL: THROW_ERROR("Attempt to index into atomic type ", get_type_name(type), " at path key \"", Tcl_GetString(step), "\""); /* i++; goto bad_path; */ default: THROW_ERROR("Unhandled type: ", Tcl_GetString(Tcl_NewIntObj(type))); } { Tcl_ObjIntRep* ir = NULL; TEST_OK(JSON_GetIntrepFromObj(interp, target, &type, &ir)); val = get_unshared_val(ir); } //fprintf(stderr, "Walked on to new type %s\n", type_names[type]); } //fprintf(stderr, "Reached end of path, calling JSON_SetIntRep for replacement value %s (%s), target is %s\n", // type_names_int[newtype], Tcl_GetString(replacement), type_names_int[type]); step = pathv[i]; // This names the key / element to unset //fprintf(stderr, "To replace: path step %d: \"%s\"\n", i, Tcl_GetString(step)); switch (type) { case JSON_UNDEF: //{{{ THROW_ERROR("Found JSON_UNDEF type jval following path"); //}}} case JSON_OBJECT: //{{{ TEST_OK(Tcl_DictObjRemove(interp, val, step)); break; //}}} case JSON_ARRAY: //{{{ { int ac, index_str_len, ok=1; long index; const char* index_str; char* end; Tcl_Obj** av; TEST_OK(Tcl_ListObjGetElements(interp, val, &ac, &av)); //fprintf(stderr, "descending into array of length %d\n", ac); if (Tcl_GetLongFromObj(NULL, step, &index) != TCL_OK) { // Index isn't an integer, check for end(+/-int)? index_str = Tcl_GetStringFromObj(step, &index_str_len); if (index_str_len < 3 || strncmp("end", index_str, 3) != 0) ok = 0; if (ok) { index = ac-1; if (index_str_len >= 4) { if (index_str[3] != '-' && index_str[3] != '+') { ok = 0; } else { // errno is magically thread-safe on POSIX // systems (it's thread-local) errno = 0; index += strtol(index_str+3, &end, 10); if (errno != 0 || *end != 0) ok = 0; } } } if (!ok) THROW_ERROR("Expected an integer index or end(+/-integer)?, got ", Tcl_GetString(step)); //fprintf(stderr, "Resolved index of %ld from \"%s\"\n", index, index_str); } else { //fprintf(stderr, "Explicit index: %ld\n", index); } //fprintf(stderr, "Removing array index %d of %d\n", index, ac); if (index < 0) { break; } else if (index >= ac) { break; } else { TEST_OK(Tcl_ListObjReplace(interp, val, index, 1, 0, NULL)); //fprintf(stderr, "extracted index %ld: (%s)\n", index, Tcl_GetString(target)); } } break; //}}} case JSON_STRING: case JSON_NUMBER: case JSON_BOOL: case JSON_NULL: case JSON_DYN_STRING: case JSON_DYN_NUMBER: case JSON_DYN_BOOL: case JSON_DYN_JSON: case JSON_DYN_TEMPLATE: case JSON_DYN_LITERAL: { Tcl_Obj* bad_path = NULL; Tcl_IncrRefCount(bad_path = Tcl_NewListObj(i+1, pathv)); Tcl_SetErrorCode(interp, "RL", "JSON", "BAD_PATH", Tcl_GetString(bad_path), NULL); Tcl_SetObjResult(interp, Tcl_ObjPrintf("Attempt to index into atomic type %s at path \"%s\"", get_type_name(type), Tcl_GetString(bad_path))); Tcl_DecrRefCount(bad_path); bad_path = NULL; return TCL_ERROR; } default: THROW_ERROR("Unhandled type: ", Tcl_GetString(Tcl_NewIntObj(type))); } Tcl_InvalidateStringRep(src); if (interp) Tcl_SetObjResult(interp, src); return TCL_OK; bad_path: { Tcl_Obj* bad_path = NULL; Tcl_IncrRefCount(bad_path = Tcl_NewListObj(i+1, pathv)); Tcl_SetErrorCode(interp, "RL", "JSON", "BAD_PATH", Tcl_GetString(bad_path), NULL); Tcl_SetObjResult(interp, Tcl_ObjPrintf("Path element \"%s\" doesn't exist", Tcl_GetString(bad_path))); Tcl_DecrRefCount(bad_path); bad_path = NULL; return TCL_ERROR; } } //}}} int JSON_Normalize(Tcl_Interp* interp, Tcl_Obj* obj, Tcl_Obj** normalized) //{{{ { int retval = TCL_OK; Tcl_Obj* json = NULL; enum json_types type; type = JSON_GetJSONType(obj); if (type != JSON_UNDEF && !Tcl_HasStringRep(obj)) return TCL_OK; // Nothing to do - already parsed as json and have no string rep if (Tcl_IsShared(obj)) { replace_tclobj(&json, Tcl_DuplicateObj(obj)); } else { replace_tclobj(&json, obj); } retval = JSON_ForceJSON(interp, json); Tcl_InvalidateStringRep(json); // Defer string rep generation to our caller if (retval == TCL_OK) replace_tclobj(normalized, json); release_tclobj(&json); return retval; } //}}} int JSON_Pretty(Tcl_Interp* interp, Tcl_Obj* obj, Tcl_Obj* indent, Tcl_Obj** prettyString) //{{{ { int retval = TCL_OK; Tcl_DString ds; Tcl_Obj* pad = NULL; struct interp_cx* l = Tcl_GetAssocData(interp, "rl_json", NULL); if (indent == NULL) replace_tclobj(&indent, get_string(l, " ", 4)); replace_tclobj(&pad, l->tcl_empty); Tcl_DStringInit(&ds); retval = json_pretty(interp, obj, indent, pad, &ds); if (retval == TCL_OK) replace_tclobj(prettyString, Tcl_NewStringObj(Tcl_DStringValue(&ds), Tcl_DStringLength(&ds))); Tcl_DStringFree(&ds); release_tclobj(&pad); release_tclobj(&indent); return retval; } //}}} int JSON_Template(Tcl_Interp* interp, Tcl_Obj* template, Tcl_Obj* dict, Tcl_Obj** res) //{{{ { //struct interp_cx* l = Tcl_GetAssocData(interp, "rl_json", NULL); Tcl_Obj* actions = NULL; int retcode = TCL_OK; Tcl_ObjIntRep* ir; enum json_types type; TEST_OK(JSON_GetIntrepFromObj(interp, template, &type, &ir)); replace_tclobj(&actions, ir->twoPtrValue.ptr2); if (actions == NULL) { TEST_OK(build_template_actions(interp, template, &actions)); replace_tclobj((Tcl_Obj**)&ir->twoPtrValue.ptr2, actions); } retcode = apply_template_actions(interp, template, actions, dict, res); release_tclobj(&actions); return retcode; } //}}} int JSON_IsNULL(Tcl_Interp* interp, Tcl_Obj* obj, Tcl_Obj* path, int* isnull) //{{{ { int retval = TCL_OK; Tcl_Obj* jval = NULL; retval = JSON_Extract(interp, obj, path, &jval); if (retval == TCL_OK) *isnull = (JSON_NULL == JSON_GetJSONType(jval)); release_tclobj(&jval); return retval; } //}}} int JSON_Type(Tcl_Interp* interp, Tcl_Obj* obj, Tcl_Obj* path, enum json_types* type) //{{{ { int retval = TCL_OK; Tcl_Obj* jval = NULL; retval = JSON_Extract(interp, obj, path, &jval); if (retval == TCL_OK) *type = JSON_GetJSONType(jval); release_tclobj(&jval); return retval; } //}}} int JSON_Length(Tcl_Interp* interp, Tcl_Obj* obj, Tcl_Obj* path, int* length) //{{{ { enum json_types type; int retval = TCL_OK; Tcl_Obj* val = NULL; Tcl_Obj* target = NULL; TEST_OK_LABEL(finally, retval, JSON_Extract(interp, obj, path, &target)); TEST_OK_LABEL(finally, retval, JSON_GetJvalFromObj(interp, target, &type, &val)); switch (type) { case JSON_ARRAY: retval = Tcl_ListObjLength(interp, val, length); break; case JSON_OBJECT: retval = Tcl_DictObjSize(interp, val, length); break; case JSON_DYN_STRING: case JSON_DYN_NUMBER: case JSON_DYN_BOOL: case JSON_DYN_JSON: case JSON_DYN_TEMPLATE: case JSON_DYN_LITERAL: *length = Tcl_GetCharLength(val) + 3; break; // dynamic types have a 3 character prefix case JSON_STRING: *length = Tcl_GetCharLength(val); break; default: Tcl_SetObjResult(interp, Tcl_ObjPrintf("Named JSON value type isn't supported: %s", get_type_name(type))); retval = TCL_ERROR; } finally: release_tclobj(&target); return retval; } //}}} int JSON_Keys(Tcl_Interp* interp, Tcl_Obj* obj, Tcl_Obj* path, Tcl_Obj** keyslist) //{{{ { enum json_types type; int retval = TCL_OK; Tcl_Obj* val = NULL; Tcl_Obj* target = NULL; TEST_OK_LABEL(finally, retval, JSON_Extract(interp, obj, path, &target)); TEST_OK_LABEL(finally, retval, JSON_GetJvalFromObj(interp, target, &type, &val)); if (type != JSON_OBJECT) { Tcl_SetObjResult(interp, Tcl_ObjPrintf("Named JSON value type isn't supported: %s", get_type_name(type))); retval = TCL_ERROR; } else { Tcl_Obj* res = NULL; Tcl_Obj* k = NULL; Tcl_Obj* v = NULL; Tcl_DictSearch search; int done; replace_tclobj(&res, Tcl_NewListObj(0, NULL)); TEST_OK_LABEL(finally, retval, Tcl_DictObjFirst(interp, val, &search, &k, &v, &done)); for (; !done; Tcl_DictObjNext(&search, &k, &v, &done)) TEST_OK_BREAK(retval, Tcl_ListObjAppendElement(interp, res, k)); Tcl_DictObjDone(&search); if (retval == TCL_OK) replace_tclobj(keyslist, res); release_tclobj(&res); } finally: release_tclobj(&target); return retval; } //}}} int JSON_Decode(Tcl_Interp* interp, Tcl_Obj* bytes, Tcl_Obj* encoding, Tcl_Obj** decodedstring) //{{{ { struct interp_cx* l = Tcl_GetAssocData(interp, "rl_json", NULL); Tcl_Obj* ov[4]; int i, retval; ov[0] = l->apply; ov[1] = l->decode_bytes; ov[2] = bytes; ov[3] = encoding; for (i=0; i<4 && ov[i]; i++) if (ov[i]) Tcl_IncrRefCount(ov[i]); retval = Tcl_EvalObjv(interp, i, ov, TCL_EVAL_GLOBAL); for (i=0; i<4 && ov[i]; i++) release_tclobj(&ov[i]); if (retval == TCL_OK) { replace_tclobj(decodedstring, Tcl_GetObjResult(interp)); Tcl_ResetResult(interp); } return retval; } //}}} int JSON_Foreach(Tcl_Interp* interp, Tcl_Obj* iterators, int* body, enum collecting_mode collect, Tcl_Obj** res, ClientData cdata) { THROW_ERROR("Not implemented yet"); } int JSON_Valid(Tcl_Interp* interp, Tcl_Obj* json, int* valid, enum extensions extensions, struct parse_error* details) { struct interp_cx* l = NULL; const unsigned char* err_at = NULL; const char* errmsg = "Illegal character"; size_t char_adj = 0; // Offset addjustment to account for multibyte UTF-8 sequences const unsigned char* doc; enum json_types type; const unsigned char* p; const unsigned char* e; const unsigned char* val_start; int len; struct parse_context cx[CX_STACK_SIZE]; if (interp) l = Tcl_GetAssocData(interp, "rl_json", NULL); #if 1 // Snoop on the intrep for clues on optimized conversions {{{ { if ( l && ( (l->typeInt && Tcl_FetchIntRep(json, l->typeInt) != NULL) || (l->typeDouble && Tcl_FetchIntRep(json, l->typeDouble) != NULL) || (l->typeBignum && Tcl_FetchIntRep(json, l->typeBignum) != NULL) ) ) { *valid = 1; return TCL_OK; } } // Snoop on the intrep for clues on optimized conversions }}} #endif cx[0].prev = NULL; cx[0].last = cx; cx[0].hold_key = NULL; cx[0].container = JSON_UNDEF; cx[0].val = NULL; cx[0].char_ofs = 0; cx[0].closed = 0; cx[0].l = l; cx[0].mode = VALIDATE; p = doc = (const unsigned char*)Tcl_GetStringFromObj(json, &len); e = p + len; // Skip BOM if ( len >= 3 && p[0] == 0xef && p[1] == 0xbb && p[2] == 0xbf ) { p += 3; } // Skip leading whitespace and comments if (skip_whitespace(&p, e, &errmsg, &err_at, &char_adj, extensions) != 0) goto whitespace_err; if (unlikely(p >= e)) { err_at = p; errmsg = "No JSON value found"; goto whitespace_err; } while (p < e) { if (cx[0].last->container == JSON_OBJECT) { // Read the key if in object mode {{{ const unsigned char* key_start = p; size_t key_start_char_adj = char_adj; if (value_type(l, doc, p, e, &char_adj, &p, &type, NULL, details) != TCL_OK) goto invalid; switch (type) { case JSON_DYN_STRING: case JSON_DYN_NUMBER: case JSON_DYN_BOOL: case JSON_DYN_JSON: case JSON_DYN_TEMPLATE: case JSON_DYN_LITERAL: case JSON_STRING: break; default: parse_error(details, "Object key is not a string", doc, (key_start-doc) - key_start_char_adj); goto invalid; } if (unlikely(skip_whitespace(&p, e, &errmsg, &err_at, &char_adj, extensions) != 0)) goto whitespace_err; if (unlikely(*p != ':')) { parse_error(details, "Expecting : after object key", doc, (p-doc) - char_adj); goto invalid; } p++; if (unlikely(skip_whitespace(&p, e, &errmsg, &err_at, &char_adj, extensions) != 0)) goto whitespace_err; } //}}} val_start = p; if (value_type(l, doc, p, e, &char_adj, &p, &type, NULL, details) != TCL_OK) goto invalid; switch (type) { case JSON_OBJECT: push_parse_context(cx, JSON_OBJECT, (val_start - doc) - char_adj); if (unlikely(skip_whitespace(&p, e, &errmsg, &err_at, &char_adj, extensions) != 0)) goto whitespace_err; if (*p == '}') { pop_parse_context(cx); p++; goto after_value; } continue; case JSON_ARRAY: push_parse_context(cx, JSON_ARRAY, (val_start - doc) - char_adj); if (unlikely(skip_whitespace(&p, e, &errmsg, &err_at, &char_adj, extensions) != 0)) goto whitespace_err; if (*p == ']') { pop_parse_context(cx); p++; goto after_value; } continue; case JSON_DYN_STRING: case JSON_DYN_NUMBER: case JSON_DYN_BOOL: case JSON_DYN_JSON: case JSON_DYN_TEMPLATE: case JSON_DYN_LITERAL: case JSON_STRING: case JSON_BOOL: case JSON_NULL: case JSON_NUMBER: if (unlikely(cx->last->container != JSON_OBJECT && cx->last->container != JSON_ARRAY)) cx->last->container = type; // Record our type (at the document top-level) break; default: Tcl_SetObjResult(interp, Tcl_ObjPrintf("Unexpected json value type: %d", type)); goto err; } after_value: // Yeah, goto. But the alternative abusing loops was worse if (unlikely(skip_whitespace(&p, e, &errmsg, &err_at, &char_adj, extensions) != 0)) goto whitespace_err; if (p >= e) break; if (unlikely(cx[0].last->closed)) { parse_error(details, "Trailing garbage after value", doc, (p-doc) - char_adj); goto invalid; } switch (cx[0].last->container) { // Handle eof and end-of-context or comma for array and object {{{ case JSON_OBJECT: if (*p == '}') { pop_parse_context(cx); p++; goto after_value; } else if (unlikely(*p != ',')) { parse_error(details, "Expecting } or ,", doc, (p-doc) - char_adj); goto invalid; } p++; break; case JSON_ARRAY: if (*p == ']') { pop_parse_context(cx); p++; goto after_value; } else if (unlikely(*p != ',')) { parse_error(details, "Expecting ] or ,", doc, (p-doc) - char_adj); goto invalid; } p++; break; default: if (unlikely(p < e)) { parse_error(details, "Trailing garbage after value", doc, (p - doc) - char_adj); goto invalid; } } if (unlikely(skip_whitespace(&p, e, &errmsg, &err_at, &char_adj, extensions) != 0)) goto whitespace_err; //}}} } if (unlikely(cx != cx[0].last || !cx[0].closed)) { // Unterminated object or array context {{{ switch (cx[0].last->container) { case JSON_OBJECT: parse_error(details, "Unterminated object", doc, cx[0].last->char_ofs); goto invalid; case JSON_ARRAY: parse_error(details, "Unterminated array", doc, cx[0].last->char_ofs); goto invalid; default: // Suppress compiler warning break; } } //}}} *valid = 1; return TCL_OK; whitespace_err: parse_error(details, errmsg, doc, (err_at - doc) - char_adj); invalid: free_cx(cx); // This was a parse error, which is a successful outcome for us *valid = 0; return TCL_OK; err: free_cx(cx); return TCL_ERROR; } /* Local Variables: */ /* tab-width: 4 */ /* c-basic-offset: 4 */ /* End: */ // vim: foldmethod=marker foldmarker={{{,}}} ts=4 shiftwidth=4 |
Added jni/rl_json/generic/dedup.c.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 | #if DEDUP #if FFS == ffsll #define _GNU_SOURCE // For glibc extension ffsll #endif #include "rl_jsonInt.h" static int first_free(FREEMAP_TYPE* freemap) //{{{ { int i=0, bit, res; FFS_TMP_STORAGE; while ((bit = FFS(freemap[i])) == 0) i++; res = i * (sizeof(FREEMAP_TYPE)*8) + (bit-1); return res; } //}}} static void mark_used(FREEMAP_TYPE* freemap, int idx) //{{{ { int i = idx / (sizeof(FREEMAP_TYPE)*8); int bit = idx - (i * (sizeof(FREEMAP_TYPE)*8)); freemap[i] &= ~(1LL << bit); } //}}} static void mark_free(FREEMAP_TYPE* freemap, int idx) //{{{ { int i = idx / (sizeof(FREEMAP_TYPE)*8); int bit = idx - (i * (sizeof(FREEMAP_TYPE)*8)); freemap[i] |= 1LL << bit; } //}}} void free_cache(struct interp_cx* l) //{{{ { Tcl_HashEntry* he; Tcl_HashSearch search; struct kc_entry* e; he = Tcl_FirstHashEntry(&l->kc, &search); while (he) { ptrdiff_t idx = (ptrdiff_t)Tcl_GetHashValue(he); //if (idx >= KC_ENTRIES) Tcl_Panic("age_cache: idx (%ld) is out of bounds, KC_ENTRIES: %d", idx, KC_ENTRIES); //printf("age_cache: kc_count: %d", l->kc_count); e = &l->kc_entries[idx]; Tcl_DeleteHashEntry(he); Tcl_DecrRefCount(e->val); Tcl_DecrRefCount(e->val); // Two references - one for the cache table and one on loan to callers' interim processing mark_free(l->freemap, idx); e->val = NULL; he = Tcl_NextHashEntry(&search); } l->kc_count = 0; } //}}} static void age_cache(struct interp_cx* l) //{{{ { Tcl_HashEntry* he; Tcl_HashSearch search; struct kc_entry* e; he = Tcl_FirstHashEntry(&l->kc, &search); while (he) { ptrdiff_t idx = (ptrdiff_t)Tcl_GetHashValue(he); //if (idx >= KC_ENTRIES) Tcl_Panic("age_cache: idx (%ld) is out of bounds, KC_ENTRIES: %d", idx, KC_ENTRIES); //printf("age_cache: kc_count: %d", l->kc_count); e = &l->kc_entries[idx]; if (e->hits < 1) { Tcl_DeleteHashEntry(he); Tcl_DecrRefCount(e->val); Tcl_DecrRefCount(e->val); // Two references - one for the cache table and one on loan to callers' interim processing mark_free(l->freemap, idx); e->val = NULL; } else { e->hits >>= 1; } he = Tcl_NextHashEntry(&search); } l->kc_count = 0; } //}}} Tcl_Obj* new_stringobj_dedup(struct interp_cx* l, const char* bytes, int length) //{{{ { char buf[STRING_DEDUP_MAX + 1]; const char *keyname; int is_new; struct kc_entry* kce; Tcl_Obj* out; Tcl_HashEntry* entry = NULL; if (l == NULL) return Tcl_NewStringObj(bytes, length); if (length == 0) { return l->tcl_empty; } else if (length < 0) { length = strlen(bytes); } if (length > STRING_DEDUP_MAX) return Tcl_NewStringObj(bytes, length); if (likely(bytes[length] == 0)) { keyname = bytes; } else { memcpy(buf, bytes, length); buf[length] = 0; keyname = buf; } entry = Tcl_CreateHashEntry(&l->kc, keyname, &is_new); if (is_new) { ptrdiff_t idx = first_free(l->freemap); if (unlikely(idx >= KC_ENTRIES)) { // Cache overflow Tcl_DeleteHashEntry(entry); age_cache(l); return Tcl_NewStringObj(bytes, length); } kce = &l->kc_entries[idx]; kce->hits = 0; out = kce->val = Tcl_NewStringObj(bytes, length); Tcl_IncrRefCount(out); // Two references - one for the cache table and one on loan to callers' interim processing. Tcl_IncrRefCount(out); // Without this, values not referenced elsewhere could reach callers with refCount 1, allowing // the value to be mutated in place and corrupt the state of the cache (hash key not matching obj value) mark_used(l->freemap, idx); Tcl_SetHashValue(entry, (void*)idx); l->kc_count++; if (unlikely(l->kc_count > (int)(KC_ENTRIES/2.5))) { kce->hits++; // Prevent the just-created entry from being pruned age_cache(l); } } else { ptrdiff_t idx = (ptrdiff_t)Tcl_GetHashValue(entry); kce = &l->kc_entries[idx]; out = kce->val; if (kce->hits < 255) kce->hits++; } return out; } //}}} #endif |
Added jni/rl_json/generic/dedup.h.
> > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | #ifndef _DEDUP_H #define _DEDUP_H #if DEDUP #define STRING_DEDUP_MAX 16 void free_cache(struct interp_cx* l); Tcl_Obj* new_stringobj_dedup(struct interp_cx* l, const char* bytes, int length); # define get_string(l, bytes, length) new_stringobj_dedup(l, bytes, length) #else # define free_cache(l) // nop # define get_string(l, bytes, length) Tcl_NewStringObj(bytes, length) #endif #endif |
Added jni/rl_json/generic/json_types.c.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 | #include "rl_jsonInt.h" #include "parser.h" static void free_internal_rep(Tcl_Obj* obj, Tcl_ObjType* objtype); static void dup_internal_rep(Tcl_Obj* src, Tcl_Obj* dest, Tcl_ObjType* objtype); static void update_string_rep(Tcl_Obj* obj, Tcl_ObjType* objtype); static int set_from_any(Tcl_Interp* interp, Tcl_Obj* obj, Tcl_ObjType** objtype, enum json_types* type); extern Tcl_ObjType json_object; extern Tcl_ObjType json_array; extern Tcl_ObjType json_string; extern Tcl_ObjType json_number; extern Tcl_ObjType json_bool; extern Tcl_ObjType json_null; extern Tcl_ObjType json_dyn_string; extern Tcl_ObjType json_dyn_number; extern Tcl_ObjType json_dyn_bool; extern Tcl_ObjType json_dyn_json; extern Tcl_ObjType json_dyn_template; extern Tcl_ObjType json_dyn_literal; static void free_internal_rep_object(Tcl_Obj* obj) { free_internal_rep(obj, &json_object); } static void dup_internal_rep_object(Tcl_Obj* src, Tcl_Obj* dest) { dup_internal_rep(src, dest, &json_object); } static void update_string_rep_object(Tcl_Obj* obj) { update_string_rep(obj, &json_object); } static void free_internal_rep_array(Tcl_Obj* obj) { free_internal_rep(obj, &json_array); } static void dup_internal_rep_array(Tcl_Obj* src, Tcl_Obj* dest) { dup_internal_rep(src, dest, &json_array); } static void update_string_rep_array(Tcl_Obj* obj) { update_string_rep(obj, &json_array); } static void free_internal_rep_string(Tcl_Obj* obj) { free_internal_rep(obj, &json_string); } static void dup_internal_rep_string(Tcl_Obj* src, Tcl_Obj* dest) { dup_internal_rep(src, dest, &json_string); } static void update_string_rep_string(Tcl_Obj* obj); static void free_internal_rep_number(Tcl_Obj* obj) { free_internal_rep(obj, &json_number); } static void dup_internal_rep_number(Tcl_Obj* src, Tcl_Obj* dest) { dup_internal_rep(src, dest, &json_number); } static void update_string_rep_number(Tcl_Obj* obj); static void free_internal_rep_bool(Tcl_Obj* obj) { free_internal_rep(obj, &json_bool); } static void dup_internal_rep_bool(Tcl_Obj* src, Tcl_Obj* dest) { dup_internal_rep(src, dest, &json_bool); } static void update_string_rep_bool(Tcl_Obj* obj); static void free_internal_rep_null(Tcl_Obj* obj) { free_internal_rep(obj, &json_null); } static void dup_internal_rep_null(Tcl_Obj* src, Tcl_Obj* dest) { dup_internal_rep(src, dest, &json_null); } static void update_string_rep_null(Tcl_Obj* obj); static void free_internal_rep_dyn_string(Tcl_Obj* obj) { free_internal_rep(obj, &json_dyn_string); } static void dup_internal_rep_dyn_string(Tcl_Obj* src, Tcl_Obj* dest) { dup_internal_rep(src, dest, &json_dyn_string); } static void update_string_rep_dyn_string(Tcl_Obj* obj) { update_string_rep(obj, &json_dyn_string); } static void free_internal_rep_dyn_number(Tcl_Obj* obj) { free_internal_rep(obj, &json_dyn_number); } static void dup_internal_rep_dyn_number(Tcl_Obj* src, Tcl_Obj* dest) { dup_internal_rep(src, dest, &json_dyn_number); } static void update_string_rep_dyn_number(Tcl_Obj* obj) { update_string_rep(obj, &json_dyn_number); } static void free_internal_rep_dyn_bool(Tcl_Obj* obj) { free_internal_rep(obj, &json_dyn_bool); } static void dup_internal_rep_dyn_bool(Tcl_Obj* src, Tcl_Obj* dest) { dup_internal_rep(src, dest, &json_dyn_bool); } static void update_string_rep_dyn_bool(Tcl_Obj* obj) { update_string_rep(obj, &json_dyn_bool); } static void free_internal_rep_dyn_json(Tcl_Obj* obj) { free_internal_rep(obj, &json_dyn_json); } static void dup_internal_rep_dyn_json(Tcl_Obj* src, Tcl_Obj* dest) { dup_internal_rep(src, dest, &json_dyn_json); } static void update_string_rep_dyn_json(Tcl_Obj* obj) { update_string_rep(obj, &json_dyn_json); } static void free_internal_rep_dyn_template(Tcl_Obj* obj) { free_internal_rep(obj, &json_dyn_template); } static void dup_internal_rep_dyn_template(Tcl_Obj* src, Tcl_Obj* dest) { dup_internal_rep(src, dest, &json_dyn_template); } static void update_string_rep_dyn_template(Tcl_Obj* obj) { update_string_rep(obj, &json_dyn_template); } static void free_internal_rep_dyn_literal(Tcl_Obj* obj) { free_internal_rep(obj, &json_dyn_literal); } static void dup_internal_rep_dyn_literal(Tcl_Obj* src, Tcl_Obj* dest) { dup_internal_rep(src, dest, &json_dyn_literal); } static void update_string_rep_dyn_literal(Tcl_Obj* obj); Tcl_ObjType json_object = { "JSON_object", free_internal_rep_object, dup_internal_rep_object, update_string_rep_object, NULL }; Tcl_ObjType json_array = { "JSON_array", free_internal_rep_array, dup_internal_rep_array, update_string_rep_array, NULL }; Tcl_ObjType json_string = { "JSON_string", free_internal_rep_string, dup_internal_rep_string, update_string_rep_string, NULL }; Tcl_ObjType json_number = { "JSON_number", free_internal_rep_number, dup_internal_rep_number, update_string_rep_number, NULL }; Tcl_ObjType json_bool = { "JSON_bool", free_internal_rep_bool, dup_internal_rep_bool, update_string_rep_bool, NULL }; Tcl_ObjType json_null = { "JSON_null", free_internal_rep_null, dup_internal_rep_null, update_string_rep_null, NULL }; Tcl_ObjType json_dyn_string = { "JSON_dyn_string", free_internal_rep_dyn_string, dup_internal_rep_dyn_string, update_string_rep_dyn_string, NULL }; Tcl_ObjType json_dyn_number = { "JSON_dyn_number", free_internal_rep_dyn_number, dup_internal_rep_dyn_number, update_string_rep_dyn_number, NULL }; Tcl_ObjType json_dyn_bool = { "JSON_dyn_bool", free_internal_rep_dyn_bool, dup_internal_rep_dyn_bool, update_string_rep_dyn_bool, NULL }; Tcl_ObjType json_dyn_json = { "JSON_dyn_json", free_internal_rep_dyn_json, dup_internal_rep_dyn_json, update_string_rep_dyn_json, NULL }; Tcl_ObjType json_dyn_template = { "JSON_dyn_template", free_internal_rep_dyn_template, dup_internal_rep_dyn_template, update_string_rep_dyn_template, NULL }; Tcl_ObjType json_dyn_literal = { "JSON_dyn_literal", free_internal_rep_dyn_literal, dup_internal_rep_dyn_literal, update_string_rep_dyn_literal, NULL }; Tcl_ObjType* g_objtype_for_type[JSON_TYPE_MAX]; int JSON_IsJSON(Tcl_Obj* obj, enum json_types* type, Tcl_ObjIntRep** ir) //{{{ { enum json_types t; Tcl_ObjIntRep* _ir = NULL; for (t=JSON_OBJECT; t<JSON_TYPE_MAX && _ir==NULL; t++) _ir = Tcl_FetchIntRep(obj, g_objtype_for_type[t]); t--; if (_ir == NULL) return 0; *ir = _ir; *type = t; return 1; } //}}} int JSON_GetIntrepFromObj(Tcl_Interp* interp, Tcl_Obj* obj, enum json_types* type, Tcl_ObjIntRep** ir) //{{{ { enum json_types t; Tcl_ObjIntRep* _ir = NULL; Tcl_ObjType* objtype = NULL; if (!JSON_IsJSON(obj, &t, &_ir)) { TEST_OK(set_from_any(interp, obj, &objtype, &t)); _ir = Tcl_FetchIntRep(obj, objtype); if (_ir == NULL) Tcl_Panic("Could not retrieve the intrep we just created"); } *type = t; *ir = _ir; return TCL_OK; } //}}} int JSON_GetJvalFromObj(Tcl_Interp* interp, Tcl_Obj* obj, enum json_types* type, Tcl_Obj** val) //{{{ { Tcl_ObjIntRep* ir = NULL; TEST_OK(JSON_GetIntrepFromObj(interp, obj, type, &ir)); *val = ir->twoPtrValue.ptr1; return TCL_OK; } //}}} int JSON_SetIntRep(Tcl_Obj* target, enum json_types type, Tcl_Obj* replacement) //{{{ { Tcl_ObjIntRep intrep; Tcl_ObjType* objtype = NULL; if (Tcl_IsShared(target)) Tcl_Panic("Called JSON_SetIntRep on a shared object"); objtype = g_objtype_for_type[type]; Tcl_FreeIntRep(target); intrep.twoPtrValue.ptr1 = replacement; // ptr1 is the Tcl_Obj holding the Tcl structure for this value if (replacement) Tcl_IncrRefCount((Tcl_Obj*)intrep.twoPtrValue.ptr1); intrep.twoPtrValue.ptr2 = NULL; // ptr2 holds the template actions, if any have been generated for this value Tcl_StoreIntRep(target, objtype, &intrep); Tcl_InvalidateStringRep(target); return TCL_OK; } //}}} #ifdef TCL_MEM_DEBUG Tcl_Obj* JSON_DbNewJvalObj(enum json_types type, Tcl_Obj* val, const char* file, int line) #else Tcl_Obj* JSON_NewJvalObj(enum json_types type, Tcl_Obj* val) #endif { //{{{ #ifdef TCL_MEM_DEBUG Tcl_Obj* res = Tcl_DbNewObj(file, line); #else Tcl_Obj* res = Tcl_NewObj(); #endif /* switch (type) { case JSON_OBJECT: case JSON_ARRAY: case JSON_STRING: case JSON_NUMBER: case JSON_BOOL: case JSON_NULL: case JSON_DYN_STRING: case JSON_DYN_NUMBER: case JSON_DYN_BOOL: case JSON_DYN_JSON: case JSON_DYN_TEMPLATE: case JSON_DYN_LITERAL: break; default: Tcl_Panic("JSON_NewJvalObj, unhandled type: %d", type); } */ if (JSON_SetIntRep(res, type, val) != TCL_OK) Tcl_Panic("Couldn't set JSON intrep"); return res; } //}}} static void free_internal_rep(Tcl_Obj* obj, Tcl_ObjType* objtype) //{{{ { Tcl_ObjIntRep* ir = NULL; ir = Tcl_FetchIntRep(obj, objtype); if (ir != NULL) { release_tclobj((Tcl_Obj**)&ir->twoPtrValue.ptr1); release_tclobj((Tcl_Obj**)&ir->twoPtrValue.ptr2); #if 0 //Tcl_Obj* ir_obj = ir->twoPtrValue.ptr1; Tcl_Obj* actions = ir->twoPtrValue.ptr2; if (ir->twoPtrValue.ptr1) { /* fprintf(stderr, "%s Releasing ptr1 %p, refcount %d, which is %s\n", objtype->name, ir->twoPtrValue.ptr1, ir_obj == NULL ? -42 : ir_obj->refCount, ir_obj->typePtr ? ir_obj->typePtr->name : "pure string" ); */ Tcl_DecrRefCount((Tcl_Obj*)ir->twoPtrValue.ptr1); ir->twoPtrValue.ptr1 = NULL;} if (ir->twoPtrValue.ptr2 && actions->refCount > 0) { /* fprintf(stderr, "%s Releasing ptr2 %p, refcount %d, which is %s\n", objtype->name, ir->twoPtrValue.ptr2, actions == NULL ? -42 : actions->refCount, actions->typePtr ? actions->typePtr->name : "pure string" ); */ Tcl_DecrRefCount((Tcl_Obj*)ir->twoPtrValue.ptr2); ir->twoPtrValue.ptr2 = NULL;} #endif } } //}}} static void dup_internal_rep(Tcl_Obj* src, Tcl_Obj* dest, Tcl_ObjType* objtype) //{{{ { Tcl_ObjIntRep* srcir = NULL; Tcl_ObjIntRep destir; srcir = Tcl_FetchIntRep(src, objtype); if (srcir == NULL) Tcl_Panic("dup_internal_rep asked to duplicate for type, but that type wasn't available on the src object"); if (src == srcir->twoPtrValue.ptr1) { int len; const char* str = Tcl_GetStringFromObj((Tcl_Obj*)srcir->twoPtrValue.ptr1, &len); // Don't know how this happens yet, but it's bad news - we get into an endless recursion of duplicateobj calls until the stack blows up // Panic and go via the string rep Tcl_IncrRefCount((Tcl_Obj*)(destir.twoPtrValue.ptr1 = Tcl_NewStringObj(str, len))); } else { destir.twoPtrValue.ptr1 = srcir->twoPtrValue.ptr1; } destir.twoPtrValue.ptr2 = srcir->twoPtrValue.ptr2; if (destir.twoPtrValue.ptr1) Tcl_IncrRefCount((Tcl_Obj*)destir.twoPtrValue.ptr1); if (destir.twoPtrValue.ptr2) Tcl_IncrRefCount((Tcl_Obj*)destir.twoPtrValue.ptr2); Tcl_StoreIntRep(dest, objtype, &destir); } //}}} static void update_string_rep(Tcl_Obj* obj, Tcl_ObjType* objtype) //{{{ { Tcl_ObjIntRep* ir = Tcl_FetchIntRep(obj, objtype); struct serialize_context scx; Tcl_DString ds; if (ir == NULL) Tcl_Panic("dup_internal_rep asked to duplicate for type, but that type wasn't available on the src object"); Tcl_DStringInit(&ds); scx.ds = &ds; scx.serialize_mode = SERIALIZE_NORMAL; scx.fromdict = NULL; scx.l = NULL; scx.allow_null = 1; serialize(NULL, &scx, obj); obj->length = Tcl_DStringLength(&ds); obj->bytes = ckalloc(obj->length + 1); memcpy(obj->bytes, Tcl_DStringValue(&ds), obj->length); obj->bytes[obj->length] = 0; Tcl_DStringFree(&ds); scx.ds = NULL; } //}}} static void update_string_rep_string(Tcl_Obj* obj) //{{{ { update_string_rep(obj, &json_string); /* Tcl_ObjIntRep* ir = Tcl_FetchIntRep(obj, &json_string); const char* str; int len; str = Tcl_GetStringFromObj((Tcl_Obj*)ir->twoPtrValue.ptr1, &len); obj->bytes = ckalloc(len+3); obj->bytes[0] = '"'; memcpy(obj->bytes+1, str, len); obj->bytes[len+1] = '"'; obj->bytes[len+2] = 0; obj->length = len+2; */ } //}}} static void update_string_rep_number(Tcl_Obj* obj) //{{{ { Tcl_ObjIntRep* ir = Tcl_FetchIntRep(obj, &json_number); const char* str; int len; if (ir->twoPtrValue.ptr1 == obj) Tcl_Panic("Turtles all the way down!"); str = Tcl_GetStringFromObj((Tcl_Obj*)ir->twoPtrValue.ptr1, &len); obj->bytes = ckalloc(len+1); memcpy(obj->bytes, str, len+1); obj->length = len; } //}}} static void update_string_rep_bool(Tcl_Obj* obj) //{{{ { Tcl_ObjIntRep* ir = Tcl_FetchIntRep(obj, &json_bool); int boolval; if (Tcl_GetBooleanFromObj(NULL, (Tcl_Obj*)ir->twoPtrValue.ptr1, &boolval) != TCL_OK) Tcl_Panic("json_bool's intrep tclobj is not a boolean"); if (boolval) { obj->bytes = ckalloc(5); memcpy(obj->bytes, "true", 5); obj->length = 4; } else { obj->bytes = ckalloc(6); memcpy(obj->bytes, "false", 6); obj->length = 5; } } //}}} static void update_string_rep_null(Tcl_Obj* obj) //{{{ { obj->bytes = ckalloc(5); memcpy(obj->bytes, "null", 5); obj->length = 4; } //}}} static void update_string_rep_dyn_literal(Tcl_Obj* obj) //{{{ { update_string_rep(obj, &json_dyn_literal); /* Tcl_ObjIntRep* ir = Tcl_FetchIntRep(obj, &json_dyn_literal); const char* str; int len; str = Tcl_GetStringFromObj((Tcl_Obj*)ir->twoPtrValue.ptr1, &len); obj->bytes = ckalloc(len+6); obj->bytes[0] = '"'; obj->bytes[1] = '~'; obj->bytes[2] = 'L'; obj->bytes[3] = ':'; memcpy(obj->bytes+4, str, len); obj->bytes[len+4] = '"'; obj->bytes[len+5] = 0; obj->length = len+5; */ } //}}} static int set_from_any(Tcl_Interp* interp, Tcl_Obj* obj, Tcl_ObjType** objtype, enum json_types* out_type) //{{{ { struct interp_cx* l = NULL; const unsigned char* err_at = NULL; const char* errmsg = "Illegal character"; size_t char_adj = 0; // Offset addjustment to account for multibyte UTF-8 sequences const unsigned char* doc; enum json_types type; Tcl_Obj* val = NULL; const unsigned char* p; const unsigned char* e; const unsigned char* val_start; int len; struct parse_context cx[CX_STACK_SIZE]; enum extensions extensions = EXT_COMMENTS; struct parse_error details = {}; if (interp) l = Tcl_GetAssocData(interp, "rl_json", NULL); #if 1 // Snoop on the intrep for clues on optimized conversions {{{ { if ( l && ( (l->typeInt && Tcl_FetchIntRep(obj, l->typeInt) != NULL) || (l->typeDouble && Tcl_FetchIntRep(obj, l->typeDouble) != NULL) || (l->typeBignum && Tcl_FetchIntRep(obj, l->typeBignum) != NULL) ) ) { Tcl_ObjIntRep ir = {.twoPtrValue = {}}; // Must dup because obj will soon be us, creating a circular ref replace_tclobj((Tcl_Obj**)&ir.twoPtrValue.ptr1, Tcl_DuplicateObj(obj)); release_tclobj((Tcl_Obj**)&ir.twoPtrValue.ptr2); *out_type = JSON_NUMBER; *objtype = g_objtype_for_type[JSON_NUMBER]; Tcl_StoreIntRep(obj, *objtype, &ir); return TCL_OK; } } // Snoop on the intrep for clues on optimized conversions }}} #endif cx[0].prev = NULL; cx[0].last = cx; cx[0].hold_key = NULL; cx[0].container = JSON_UNDEF; cx[0].val = NULL; cx[0].char_ofs = 0; cx[0].closed = 0; cx[0].l = l; cx[0].mode = PARSE; p = doc = (const unsigned char*)Tcl_GetStringFromObj(obj, &len); e = p + len; // Skip BOM if ( len >= 3 && p[0] == 0xef && p[1] == 0xbb && p[2] == 0xbf ) { p += 3; } // Skip leading whitespace and comments if (skip_whitespace(&p, e, &errmsg, &err_at, &char_adj, extensions) != 0) goto whitespace_err; while (p < e) { if (cx[0].last->container == JSON_OBJECT) { // Read the key if in object mode {{{ const unsigned char* key_start = p; size_t key_start_char_adj = char_adj; if (value_type(l, doc, p, e, &char_adj, &p, &type, &val, &details) != TCL_OK) goto err; switch (type) { case JSON_DYN_STRING: case JSON_DYN_NUMBER: case JSON_DYN_BOOL: case JSON_DYN_JSON: case JSON_DYN_TEMPLATE: case JSON_DYN_LITERAL: /* Add back the template format prefix, since we can't store the type * in the dict key. The template generation code reparses it later. */ { Tcl_Obj* new = Tcl_ObjPrintf("~%c:%s", key_start[2], Tcl_GetString(val)); replace_tclobj(&val, new); // Can do this because val's ref is on loan from new_stringobj_dedup //val = Tcl_ObjPrintf("~%c:%s", key_start[2], Tcl_GetString(val)); } // Falls through case JSON_STRING: replace_tclobj(&cx[0].last->hold_key, val); break; default: parse_error(&details, "Object key is not a string", doc, (key_start-doc) - key_start_char_adj); goto err; } if (unlikely(skip_whitespace(&p, e, &errmsg, &err_at, &char_adj, extensions) != 0)) goto whitespace_err; if (unlikely(*p != ':')) { parse_error(&details, "Expecting : after object key", doc, (p-doc) - char_adj); goto err; } p++; if (unlikely(skip_whitespace(&p, e, &errmsg, &err_at, &char_adj, extensions) != 0)) goto whitespace_err; } //}}} val_start = p; if (value_type(l, doc, p, e, &char_adj, &p, &type, &val, &details) != TCL_OK) goto err; switch (type) { case JSON_OBJECT: push_parse_context(cx, JSON_OBJECT, (val_start - doc) - char_adj); if (unlikely(skip_whitespace(&p, e, &errmsg, &err_at, &char_adj, extensions) != 0)) goto whitespace_err; if (*p == '}') { pop_parse_context(cx); p++; goto after_value; } continue; case JSON_ARRAY: push_parse_context(cx, JSON_ARRAY, (val_start - doc) - char_adj); if (unlikely(skip_whitespace(&p, e, &errmsg, &err_at, &char_adj, extensions) != 0)) goto whitespace_err; if (*p == ']') { pop_parse_context(cx); p++; goto after_value; } continue; case JSON_DYN_STRING: case JSON_DYN_NUMBER: case JSON_DYN_BOOL: case JSON_DYN_JSON: case JSON_DYN_TEMPLATE: case JSON_DYN_LITERAL: case JSON_STRING: case JSON_BOOL: case JSON_NULL: case JSON_NUMBER: append_to_cx(cx->last, JSON_NewJvalObj(type, val)); if (unlikely(cx->last->container != JSON_OBJECT && cx->last->container != JSON_ARRAY)) cx->last->container = type; // Record our type (at the document top-level) break; default: Tcl_SetObjResult(interp, Tcl_ObjPrintf("Unexpected json value type: %d", type)); goto err; } after_value: // Yeah, goto. But the alternative abusing loops was worse if (unlikely(skip_whitespace(&p, e, &errmsg, &err_at, &char_adj, extensions) != 0)) goto whitespace_err; if (p >= e) break; if (unlikely(cx[0].last->closed)) { parse_error(&details, "Trailing garbage after value", doc, (p-doc) - char_adj); goto err; } switch (cx[0].last->container) { // Handle eof and end-of-context or comma for array and object {{{ case JSON_OBJECT: if (*p == '}') { pop_parse_context(cx); p++; goto after_value; } else if (unlikely(*p != ',')) { parse_error(&details, "Expecting } or ,", doc, (p-doc) - char_adj); goto err; } p++; break; case JSON_ARRAY: if (*p == ']') { pop_parse_context(cx); p++; goto after_value; } else if (unlikely(*p != ',')) { parse_error(&details, "Expecting ] or ,", doc, (p-doc) - char_adj); goto err; } p++; break; default: if (unlikely(p < e)) { parse_error(&details, "Trailing garbage after value", doc, (p - doc) - char_adj); goto err; } } if (unlikely(skip_whitespace(&p, e, &errmsg, &err_at, &char_adj, extensions) != 0)) goto whitespace_err; //}}} } if (unlikely(cx != cx[0].last || !cx[0].closed)) { // Unterminated object or array context {{{ switch (cx[0].last->container) { case JSON_OBJECT: parse_error(&details, "Unterminated object", doc, cx[0].last->char_ofs); goto err; case JSON_ARRAY: parse_error(&details, "Unterminated array", doc, cx[0].last->char_ofs); goto err; default: // Suppress compiler warning break; } } //}}} if (unlikely(cx[0].val == NULL)) { err_at = doc; errmsg = "No JSON value found"; goto whitespace_err; } //Tcl_FreeIntRep(obj); { Tcl_ObjType* top_objtype = g_objtype_for_type[cx[0].container]; Tcl_ObjIntRep* top_ir = Tcl_FetchIntRep(cx[0].val, top_objtype); Tcl_ObjIntRep ir = {.twoPtrValue = {}}; if (unlikely(top_ir == NULL)) Tcl_Panic("Can't get intrep for the top container"); // We're transferring the ref from cx[0].val to our intrep replace_tclobj((Tcl_Obj**)&ir.twoPtrValue.ptr1, top_ir->twoPtrValue.ptr1); release_tclobj((Tcl_Obj**)&ir.twoPtrValue.ptr2); release_tclobj(&cx[0].val); Tcl_StoreIntRep(obj, top_objtype, &ir); *objtype = top_objtype; *out_type = cx[0].container; } release_tclobj(&val); return TCL_OK; whitespace_err: parse_error(&details, errmsg, doc, (err_at - doc) - char_adj); err: if (details.errmsg) throw_parse_error(interp, &details); release_tclobj(&val); free_cx(cx); return TCL_ERROR; } //}}} int type_is_dynamic(const enum json_types type) //{{{ { switch (type) { case JSON_DYN_STRING: case JSON_DYN_NUMBER: case JSON_DYN_BOOL: case JSON_DYN_JSON: case JSON_DYN_TEMPLATE: case JSON_DYN_LITERAL: return 1; default: return 0; } } //}}} Tcl_Obj* get_unshared_val(Tcl_ObjIntRep* ir) //{{{ { if (ir->twoPtrValue.ptr1 != NULL && Tcl_IsShared((Tcl_Obj*)ir->twoPtrValue.ptr1)) { replace_tclobj((Tcl_Obj**)&ir->twoPtrValue.ptr1, Tcl_DuplicateObj(ir->twoPtrValue.ptr1)); } if (ir->twoPtrValue.ptr2) { // The caller wants val unshared, which implies that they intend to // change it, which would invalidate our cached template actions, so // release those if we have them release_tclobj((Tcl_Obj**)&ir->twoPtrValue.ptr2); } return ir->twoPtrValue.ptr1; } //}}} int init_types(Tcl_Interp* interp) //{{{ { // We don't define set_from_any callbacks for our types, so they must not be Tcl_RegisterObjType'ed g_objtype_for_type[JSON_UNDEF] = NULL; g_objtype_for_type[JSON_OBJECT] = &json_object; g_objtype_for_type[JSON_ARRAY] = &json_array; g_objtype_for_type[JSON_STRING] = &json_string; g_objtype_for_type[JSON_NUMBER] = &json_number; g_objtype_for_type[JSON_BOOL] = &json_bool; g_objtype_for_type[JSON_NULL] = &json_null; g_objtype_for_type[JSON_DYN_STRING] = &json_dyn_string; g_objtype_for_type[JSON_DYN_NUMBER] = &json_dyn_number; g_objtype_for_type[JSON_DYN_BOOL] = &json_dyn_bool; g_objtype_for_type[JSON_DYN_JSON] = &json_dyn_json; g_objtype_for_type[JSON_DYN_TEMPLATE] = &json_dyn_template; g_objtype_for_type[JSON_DYN_LITERAL] = &json_dyn_literal; return TCL_OK; } //}}} /* Local Variables: */ /* tab-width: 4 */ /* c-basic-offset: 4 */ /* End: */ // vim: foldmethod=marker foldmarker={{{,}}} ts=4 shiftwidth=4 |
Changes to jni/rl_json/generic/parser.c.
|
| | > > > > > > | > | > > > | > > > > > > > > | | | < < < > | > > > > > > > > > > > > > < | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 | #include "rl_jsonInt.h" #include "parser.h" enum char_advance_status { CHAR_ADVANCE_OK, CHAR_ADVANCE_UNESCAPED_NULL }; void parse_error(struct parse_error* details, const char* errmsg, const unsigned char* doc, size_t char_ofs) //{{{ { if (details == NULL) return; details->errmsg = errmsg; details->doc = (const char*)doc; details->char_ofs = char_ofs; } //}}} 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", (long) details->char_ofs); Tcl_SetObjResult(interp, Tcl_ObjPrintf("Error parsing JSON value: %s at offset %ld", details->errmsg, (long) details->char_ofs)); Tcl_SetErrorCode(interp, "RL", "JSON", "PARSE", details->errmsg, details->doc, char_ofs_buf, NULL); } //}}} struct parse_context* push_parse_context(struct parse_context* cx, const enum json_types container, const size_t char_ofs) //{{{ { struct parse_context* last = cx->last; struct parse_context* new; if (last->container == JSON_UNDEF) { new = last; } else if (likely((ptrdiff_t)last >= (ptrdiff_t)cx && (ptrdiff_t)last < (ptrdiff_t)(cx + CX_STACK_SIZE - 1))) { // Space remains on the cx array stack new = cx->last+1; } else { new = (struct parse_context*)malloc(sizeof(*new)); } new->prev = last; if (last->mode == VALIDATE) { new->val = NULL; } else { Tcl_IncrRefCount( new->val = JSON_NewJvalObj(container, container == JSON_OBJECT ? (cx->l ? cx->l->tcl_empty_dict : Tcl_NewDictObj()) : (cx->l ? cx->l->tcl_empty_list : Tcl_NewListObj(0, NULL)) )); } new->hold_key = NULL; new->char_ofs = char_ofs; new->container = container; new->closed = 0; new->objtype = g_objtype_for_type[container]; new->l = last->l; new->mode = last->mode; cx->last = new; return new; } //}}} struct parse_context* pop_parse_context(struct parse_context* cx) //{{{ { struct parse_context* last = cx->last; cx->last->closed = 1; if (unlikely((ptrdiff_t)cx == (ptrdiff_t)last)) { return cx->last; } if (likely(last->val != NULL)) { if (last->prev->val && Tcl_IsShared(last->prev->val)) replace_tclobj(&last->prev->val, Tcl_DuplicateObj(last->prev->val)); append_to_cx(last->prev, last->val); release_tclobj(&last->val); } if (likely((ptrdiff_t)last >= (ptrdiff_t)cx && (ptrdiff_t)last < (ptrdiff_t)(cx + CX_STACK_SIZE))) { // last is on the cx array stack cx->last--; } else { if (last->prev) { |
︙ | ︙ | |||
71 72 73 74 75 76 77 | //}}} void free_cx(struct parse_context* cx) //{{{ { struct parse_context* tail = cx->last; while (1) { | | < | | < | < < | < < < | > > > > > > > > | | | | > | | | | > | 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 | //}}} void free_cx(struct parse_context* cx) //{{{ { struct parse_context* tail = cx->last; while (1) { release_tclobj(&tail->hold_key); release_tclobj(&tail->val); if (tail == cx) break; tail = pop_parse_context(cx); } } //}}} static int is_whitespace(const unsigned char c) //{{{ { switch (c) { case 0x20: case 0x09: case 0x0A: case 0x0D: return 1; default: return 0; } } //}}} static enum char_advance_status char_advance(const unsigned char** p, size_t* char_adj) //{{{ { // TODO: use Tcl_UtfNext instead? // This relies on some properties from the utf-8 returned by Tcl_GetString: // - no invalid encodings (partial utf-8 sequences, etc) // - not truncated in the middle of a char const unsigned char first = **p; unsigned int eat; (*p)++; if (unlikely(first >= 0xC0)) { // Advance to next UTF-8 character // TODO: detect invalid sequences? if (first == 0xC0 && **p == 0x80) { (*p)--; // Have to check for this here - other unescaped control chars are handled by the < 0x1F // test, but 0x00 is transformed to 0xC0 0x80 by Tcl (MUTF-8 rather than UTF-8) return CHAR_ADVANCE_UNESCAPED_NULL; } if (first < 0xe0 /* 0b11100000 */) { eat = 1; #if TCL_UTF_MAX == 3 } else { eat = 2; #else } else if (first < 0xf0 /* 0b11110000 */) { eat = 2; } else if (first < 0xf8 /* 0b11111000 */) { eat = 3; } else if (first < 0xfc /* 0b11111100 */) { eat = 4; } else { eat = 5; #endif } *p += eat; *char_adj += eat; } return CHAR_ADVANCE_OK; } //}}} int skip_whitespace(const unsigned char** s, const unsigned char* e, const char** errmsg, const unsigned char** err_at, size_t* char_adj, enum extensions extensions) //{{{ { const unsigned char* p = *s; const unsigned char* start; size_t start_char_adj; enum char_advance_status status = CHAR_ADVANCE_OK; consume_space_or_comment: while (is_whitespace(*p)) p++; if (unlikely((extensions & EXT_COMMENTS) && *p == '/')) { start = p; start_char_adj = *char_adj; p++; if (*p == '/') { p++; while (likely(p < e && (*p > 0x1f || *p == 0x09) && status == CHAR_ADVANCE_OK)) status = char_advance(&p, char_adj); } else if (*p == '*') { p++; while (likely(p < e-2 && *p != '*')) { if (unlikely(*p <= 0x1f && !is_whitespace(*p))) goto err_illegal_char; status = char_advance(&p, char_adj); if (unlikely(status != CHAR_ADVANCE_OK)) goto err_illegal_char; } if (unlikely(*p++ != '*' || *p++ != '/')) goto err_unterminated; } else { goto err_illegal_char; } |
︙ | ︙ | |||
190 191 192 193 194 195 196 | *err_at = p; *errmsg = "Illegal character"; *s = p; return 1; } //}}} | > > > > > > > > > > > > > > > > > > > > > > | > | > < | | | | > < < < < < < | < < < < < | | | | | | 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 | *err_at = p; *errmsg = "Illegal character"; *s = p; return 1; } //}}} int is_template(const char* s, int len) //{{{ { if ( len >= 3 && s[0] == '~' && s[2] == ':' ) { switch (s[1]) { case 'S': case 'N': case 'B': case 'J': case 'T': case 'L': return 1; } } return 0; } //}}} int value_type(struct interp_cx* l, const unsigned char* doc, const unsigned char* p, const unsigned char* e, size_t* char_adj, const unsigned char** next, enum json_types *type, Tcl_Obj** val, struct parse_error* details) //{{{ { const unsigned char* err_at = NULL; const char* errmsg = NULL; Tcl_Obj* out = NULL; if (val) release_tclobj(val); if (unlikely(p >= e)) goto err; switch (*p) { case '"': p++; // Advance past the " to the first byte of the string { const unsigned char* chunk; size_t len; char mapped; enum json_types stype = JSON_STRING; enum char_advance_status status = CHAR_ADVANCE_OK; // Peek ahead to detect template subst markers. TEMPLATE_TYPE(p, e-p, stype); while (1) { chunk = p; // These tests are where the majority of the parsing time is spent while (likely(p < e && *p != '"' && *p != '\\' && *p > 0x1f && status == CHAR_ADVANCE_OK)) status = char_advance(&p, char_adj); if (unlikely(p >= e)) goto err; len = p-chunk; if (likely(out == NULL)) { replace_tclobj(&out, get_string(l, (const char*)chunk, len)); } else if (len > 0) { if (unlikely(Tcl_IsShared(out))) replace_tclobj(&out, Tcl_DuplicateObj(out)); Tcl_AppendToObj(out, (const char*)chunk, len); } if (likely(*p == '"')) { p++; // Point at the first byte after the string break; } if (unlikely(*p != '\\')) goto err; p++; // Advance to the backquoted byte if (unlikely(Tcl_IsShared(out))) replace_tclobj(&out, Tcl_DuplicateObj(out)); switch (*p) { // p could point at the NULL terminator at this point case '\\': case '"': case '/': // RFC4627 allows this for some reason mapped = *p; goto append_mapped; |
︙ | ︙ | |||
359 360 361 362 363 364 365 | default: goto err; } p++; // Advance to the first byte after the backquoted sequence } *type = stype; | | > > | > | > | < > > > > > > > > > > > > > > > | 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 | default: goto err; } p++; // Advance to the first byte after the backquoted sequence } *type = stype; if (val) replace_tclobj(val, out); } break; case '{': *type = JSON_OBJECT; p++; break; case '[': *type = JSON_ARRAY; p++; break; case 't': if (unlikely(e-p < 4 || *(uint32_t*)p != *(uint32_t*)"true")) goto err; // Evil endian-compensated trick *type = JSON_BOOL; if (val) replace_tclobj(val, l ? l->tcl_true : Tcl_NewBooleanObj(1)); p += 4; break; case 'f': if (unlikely(e-p < 5 || *(uint32_t*)(p+1) != *(uint32_t*)"alse")) goto err; // Evil endian-compensated trick *type = JSON_BOOL; if (val) replace_tclobj(val, l ? l->tcl_false : Tcl_NewBooleanObj(0)); p += 5; break; case 'n': if (unlikely(e-p < 4 || *(uint32_t*)p != *(uint32_t*)"null")) goto err; // Evil endian-compensated trick *type = JSON_NULL; if (val) replace_tclobj(val, l ? l->tcl_empty : Tcl_NewStringObj("", 0)); p += 4; break; default: { const unsigned char* start = p; const unsigned char* t; if (*p == '-') p++; if (unlikely( *p == '0' && ( // Only 3 characters can legally follow a leading '0' according to the spec: // . - fraction, e or E - exponent (p[1] >= '0' && p[1] <= '9') // Octal, hex, or decimal with leading 0 ) )) { // Indexing one beyond p is safe - all the strings we // receive are guaranteed to be null terminated by Tcl, and // *p here is '0' err_at = p; errmsg = "Leading 0 not allowed for numbers"; goto err; } t = p; while (*p >= '0' && *p <= '9') p++; if (unlikely(p == t)) goto err; // No integer part after the decimal point if (*p == '.') { // p could point at the NULL terminator at this point p++; |
︙ | ︙ | |||
425 426 427 428 429 430 431 | if (*p == '+' || *p == '-') p++; t = p; while (*p >= '0' && *p <= '9') p++; if (unlikely(p == t)) goto err; // No integer part after the exponent symbol } *type = JSON_NUMBER; | > | > > > | | 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 | if (*p == '+' || *p == '-') p++; t = p; while (*p >= '0' && *p <= '9') p++; if (unlikely(p == t)) goto err; // No integer part after the exponent symbol } *type = JSON_NUMBER; if (val) replace_tclobj(val, get_string(l, (const char*)start, p-start)); } } release_tclobj(&out); *next = p; return TCL_OK; err: release_tclobj(&out); if (err_at == NULL) err_at = p; if (errmsg == NULL) errmsg = (err_at == e) ? "Document truncated" : "Illegal character"; parse_error(details, errmsg, doc, (err_at - doc) - *char_adj); return TCL_ERROR; } //}}} // vim: foldmethod=marker foldmarker={{{,}}} ts=4 shiftwidth=4 |
Changes to jni/rl_json/generic/parser.h.
1 2 3 | #ifndef _JSON_PARSER_H #define _JSON_PARSER_H | < < < < < < < < | < < < < < < < < < | < | < < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | #ifndef _JSON_PARSER_H #define _JSON_PARSER_H #include "rl_jsonInt.h" int skip_whitespace(const unsigned char** s, const unsigned char* e, const char** errmsg, const unsigned char** err_at, size_t* char_adj, enum extensions extensions); int value_type(struct interp_cx* l, const unsigned char* doc, const unsigned char* p, const unsigned char* e, size_t* char_adj, const unsigned char** next, enum json_types *type, Tcl_Obj** val, struct parse_error* details); void parse_error(struct parse_error* details, const char* errmsg, const unsigned char* doc, size_t char_ofs); void throw_parse_error(Tcl_Interp* interp, struct parse_error* details); struct parse_context* push_parse_context(struct parse_context* cx, const enum json_types container, const size_t char_ofs); struct parse_context* pop_parse_context(struct parse_context* cx); void free_cx(struct parse_context* cx); int test_parse(ClientData cdata, Tcl_Interp* interp, int objc, Tcl_Obj *const objv[]); #endif |
Changes to jni/rl_json/generic/rl_json.c.
|
| | > > > > < < < < < < < < < < < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | #include "rl_jsonInt.h" #ifndef ENSEMBLE #define ENSEMBLE 0 #endif #if defined(_WIN32) #define snprintf _snprintf #endif #ifdef WIN32 #define _DLLEXPORT extern DLLEXPORT #else #define _DLLEXPORT #endif static const char* dyn_prefix[] = { NULL, // JSON_UNDEF NULL, // JSON_OBJECT NULL, // JSON_ARRAY NULL, // JSON_STRING NULL, // JSON_NUMBER NULL, // JSON_BOOL |
︙ | ︙ | |||
69 70 71 72 73 74 75 | "string", // JSON_DYN_STRING "string", // JSON_DYN_NUMBER "string", // JSON_DYN_BOOL "string", // JSON_DYN_JSON "string", // JSON_DYN_TEMPLATE "string" // JSON_DYN_LITERAL }; | < | | | < | | | | | < | < | | > > < < < < | < < | < < < < | < < < < < < < | < < < < < < < < < < < < < < < < < < | < < < | | < < < < | < < < < < < < < < < | | | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < < | < | < < | < < | < < < < < < < < | < < | < < < < < < | < < < < < < | < < < | < < < < < < | < < < < < | < | < | < < < < | < < < < < < < < < < < | < | < < < < < < < < < < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | > > | > | > > > | 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 | "string", // JSON_DYN_STRING "string", // JSON_DYN_NUMBER "string", // JSON_DYN_BOOL "string", // JSON_DYN_JSON "string", // JSON_DYN_TEMPLATE "string" // JSON_DYN_LITERAL }; const char* type_names_int[] = { // Must match the order of the json_types enum "JSON_UNDEF", "JSON_OBJECT", "JSON_ARRAY", "JSON_STRING", "JSON_NUMBER", "JSON_BOOL", "JSON_NULL", "JSON_DYN_STRING", "JSON_DYN_NUMBER", "JSON_DYN_BOOL", "JSON_DYN_JSON", "JSON_DYN_TEMPLATE", "JSON_DYN_LITERAL" }; static const char* action_opcode_str[] = { // Must match the order of the action_opcode enum "NOP", "ALLOCATE", "FETCH_VALUE", "DECLARE_LITERAL", "STORE_STRING", "STORE_NUMBER", "STORE_BOOLEAN", "STORE_JSON", "STORE_TEMPLATE", "PUSH_TARGET", "POP_TARGET", "REPLACE_VAL", "REPLACE_ARR", "REPLACE_ATOM", "REPLACE_KEY", (char*)NULL }; static const char *extension_str[] = { "", "comments", (char*)NULL }; static int new_json_value_from_list(Tcl_Interp* interp, int objc, Tcl_Obj *const objv[], Tcl_Obj** res); static int NRforeach_next_loop_bottom(ClientData cdata[], Tcl_Interp* interp, int retcode); static int json_pretty_dbg(Tcl_Interp* interp, Tcl_Obj* json, Tcl_Obj* indent, Tcl_Obj* pad, Tcl_DString* ds); const char* get_dyn_prefix(enum json_types type) //{{{ { if (!type_is_dynamic(type)) { return ""; } else { return dyn_prefix[type]; } } //}}} const char* get_type_name(enum json_types type) //{{{ { return type_names[type]; } //}}} Tcl_Obj* as_json(Tcl_Interp* interp, Tcl_Obj* from) //{{{ { Tcl_ObjIntRep* ir = NULL; enum json_types type; if (JSON_GetIntrepFromObj(interp, from, &type, &ir) == TCL_OK) { // Already a JSON value, use it directly return from; } else { // Anything else, use it as a JSON string return JSON_NewJvalObj(JSON_STRING, from); // EIAS, so we can use whatever $from is as the intrep for a JSON_STRING value } } //}}} int force_json_number(Tcl_Interp* interp, struct interp_cx* l, Tcl_Obj* obj, Tcl_Obj** forced) //{{{ { int res = TCL_OK; // TODO: investigate a direct bytecode version? /* display *obj */ if (l) { // Attempt to snoop on the intrep to verify that it is one of the numeric types if ( obj->typePtr && ( (l->typeInt && Tcl_FetchIntRep(obj, l->typeInt) != NULL) || (l->typeDouble && Tcl_FetchIntRep(obj, l->typeDouble) != NULL) || (l->typeBignum && Tcl_FetchIntRep(obj, l->typeBignum) != NULL) ) ) { // It's a known number type, we can safely use it directly //fprintf(stderr, "force_json_number fastpath, verified obj to be a number type\n"); if (forced == NULL) return TCL_OK; if (Tcl_HasStringRep(obj)) { // Has a string rep already, make sure it's not hex or octal, and not padded with whitespace const char* s; int len, start=0; s = Tcl_GetStringFromObj(obj, &len); if (len >= 1 && s[0] == '-') start++; if (unlikely( (len+start >= 1 && ( (s[start] == '0' && len+start >= 2 && s[start+1] != '.') || // Octal or hex, or double with leading zero not immediately followed by .) s[start] == ' ' || s[start] == '\n' || s[start] == '\t' || s[start] == '\v' || s[start] == '\r' || s[start] == '\f' )) || (len-start >= 2 && ( s[len-1] == ' ' || s[len-1] == '\n' || s[len-1] == '\t' || s[len-1] == '\v' || s[len-1] == '\r' || s[len-1] == '\f' )) )) { // The existing string rep is one of the cases // (octal / hex / whitespace padded) that is not a // valid JSON number. Duplicate the obj and // invalidate the string rep Tcl_IncrRefCount(*forced = Tcl_DuplicateObj(obj)); Tcl_InvalidateStringRep(*forced); } else { // String rep is safe as a JSON number Tcl_IncrRefCount(*forced = obj); //fprintf(stderr, "force_json_number obj stringrep is safe json number: (%s), intrep: (%s)\n", Tcl_GetString(obj), obj->typePtr->name); } } else { // Pure number - no string rep Tcl_IncrRefCount(*forced = obj); } return TCL_OK; } else { // Could be a string that is a valid number representation, or // something that will convert to a valid number. Add 0 to it to // check (all valid numbers succeed at this, and are unchanged by // it). Use the cached objs Tcl_IncrRefCount(l->force_num_cmd[2] = obj); res = Tcl_EvalObjv(interp, 3, l->force_num_cmd, TCL_EVAL_DIRECT); Tcl_DecrRefCount(l->force_num_cmd[2]); l->force_num_cmd[2] = NULL; } } else { Tcl_Obj* cmd; cmd = Tcl_NewListObj(0, NULL); TEST_OK(Tcl_ListObjAppendElement(interp, cmd, Tcl_NewStringObj("::tcl::mathop::+", -1))); TEST_OK(Tcl_ListObjAppendElement(interp, cmd, Tcl_NewIntObj(0))); TEST_OK(Tcl_ListObjAppendElement(interp, cmd, obj)); Tcl_IncrRefCount(cmd); res = Tcl_EvalObjEx(interp, cmd, TCL_EVAL_DIRECT); Tcl_DecrRefCount(cmd); } if (res == TCL_OK) { if (forced != NULL) Tcl_IncrRefCount(*forced = Tcl_GetObjResult(interp)); Tcl_ResetResult(interp); } return res; } //}}} static void append_json_string(const struct serialize_context* scx, Tcl_Obj* obj) //{{{ { |
︙ | ︙ | |||
418 419 420 421 422 423 424 | case 0x8: Tcl_DStringAppend(ds, "\\b", 2); break; case 0xC: Tcl_DStringAppend(ds, "\\f", 2); break; case 0xA: Tcl_DStringAppend(ds, "\\n", 2); break; case 0xD: Tcl_DStringAppend(ds, "\\r", 2); break; case 0x9: Tcl_DStringAppend(ds, "\\t", 2); break; default: | < < < < < < < < < | 270 271 272 273 274 275 276 277 278 279 280 281 282 283 | case 0x8: Tcl_DStringAppend(ds, "\\b", 2); break; case 0xC: Tcl_DStringAppend(ds, "\\f", 2); break; case 0xA: Tcl_DStringAppend(ds, "\\n", 2); break; case 0xD: Tcl_DStringAppend(ds, "\\r", 2); break; case 0x9: Tcl_DStringAppend(ds, "\\t", 2); break; default: snprintf(ustr, 7, "\\u%04X", c); Tcl_DStringAppend(ds, ustr, 6); break; } p += adv; chunk = p; } else { |
︙ | ︙ | |||
461 462 463 464 465 466 467 | //}}} case JSON_OBJECT: //{{{ { int done, first=1; Tcl_DictSearch search; Tcl_Obj* k; Tcl_Obj* v; | | | | | | < > > | > > | 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 | //}}} case JSON_OBJECT: //{{{ { int done, first=1; Tcl_DictSearch search; Tcl_Obj* k; Tcl_Obj* v; enum json_types v_type = JSON_UNDEF; Tcl_Obj* iv = NULL; TEST_OK(Tcl_DictObjFirst(interp, val, &search, &k, &v, &done)); Tcl_DStringAppend(ds, "{", 1); for (; !done; Tcl_DictObjNext(&search, &k, &v, &done)) { if (!first) { Tcl_DStringAppend(ds, ",", 1); } else { first = 0; } // Have to do the template subst here rather than at // parse time since the dict keys would be broken otherwise if (scx->serialize_mode == SERIALIZE_TEMPLATE) { int len, stype; const char* s; s = Tcl_GetStringFromObj(k, &len); if ( len >= 3 && s[0] == '~' && s[2] == ':' ) { switch (s[1]) { case 'S': stype = JSON_DYN_STRING; break; case 'L': stype = JSON_DYN_LITERAL; break; case 'N': case 'B': case 'J': case 'T': Tcl_SetObjResult(interp, Tcl_ObjPrintf("Only strings allowed as object keys, got %s", s)); res = TCL_ERROR; goto done; default: stype = JSON_UNDEF; break; } if (stype != JSON_UNDEF) { int hold = scx->allow_null; scx->allow_null = 0; if (serialize_json_val(interp, scx, stype, Tcl_GetRange(k, 3, len-1)) != TCL_OK) { scx->allow_null = hold; res = TCL_ERROR; break; } scx->allow_null = hold; } else { append_json_string(scx, k); } } else { append_json_string(scx, k); } } else { |
︙ | ︙ | |||
532 533 534 535 536 537 538 | Tcl_DStringAppend(ds, "}", 1); Tcl_DictObjDone(&search); } break; //}}} case JSON_ARRAY: //{{{ { | | | | | | | 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 | Tcl_DStringAppend(ds, "}", 1); Tcl_DictObjDone(&search); } break; //}}} case JSON_ARRAY: //{{{ { int i, oc, first=1; Tcl_Obj** ov; Tcl_Obj* iv = NULL; enum json_types v_type = JSON_UNDEF; TEST_OK(Tcl_ListObjGetElements(interp, val, &oc, &ov)); Tcl_DStringAppend(ds, "[", 1); for (i=0; i<oc; i++) { if (!first) { Tcl_DStringAppend(ds, ",", 1); } else { first = 0; } JSON_GetJvalFromObj(interp, ov[i], &v_type, &iv); TEST_OK(serialize_json_val(interp, scx, v_type, iv)); } Tcl_DStringAppend(ds, "]", 1); } break; //}}} case JSON_NUMBER: //{{{ |
︙ | ︙ | |||
595 596 597 598 599 600 601 | if (scx->serialize_mode == SERIALIZE_NORMAL) { Tcl_Obj* tmp = Tcl_ObjPrintf("%s%s", dyn_prefix[type], Tcl_GetString(val)); Tcl_IncrRefCount(tmp); append_json_string(scx, tmp); Tcl_DecrRefCount(tmp); } else { | | | | < | | > | > > | < < | < > | < | 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 | if (scx->serialize_mode == SERIALIZE_NORMAL) { Tcl_Obj* tmp = Tcl_ObjPrintf("%s%s", dyn_prefix[type], Tcl_GetString(val)); Tcl_IncrRefCount(tmp); append_json_string(scx, tmp); Tcl_DecrRefCount(tmp); } else { Tcl_Obj* subst_val = NULL; enum json_types subst_type; int reset_mode = 0; if (type == JSON_DYN_LITERAL) { append_json_string(scx, val); break; } if (scx->fromdict != NULL) { TEST_OK(Tcl_DictObjGet(interp, scx->fromdict, val, &subst_val)); } else { subst_val = Tcl_ObjGetVar2(interp, val, NULL, 0); } if (subst_val == NULL) { subst_type = JSON_NULL; } else { subst_type = from_dyn[type]; Tcl_IncrRefCount(subst_val); } if (subst_type == JSON_DYN_JSON) { if (subst_val != NULL) Tcl_DecrRefCount(subst_val); res = JSON_GetJvalFromObj(interp, subst_val, &subst_type, &subst_val); if (subst_val != NULL) Tcl_IncrRefCount(subst_val); scx->serialize_mode = SERIALIZE_NORMAL; reset_mode = 1; } else if (subst_type == JSON_DYN_TEMPLATE) { if (subst_val != NULL) Tcl_DecrRefCount(subst_val); res = JSON_GetJvalFromObj(interp, subst_val, &subst_type, &subst_val); if (subst_val != NULL) Tcl_IncrRefCount(subst_val); } else if (subst_type == JSON_NUMBER) { Tcl_Obj* forced = NULL; if ((res = force_json_number(interp, scx->l, subst_val, &forced)) == TCL_OK) replace_tclobj(&subst_val, forced); release_tclobj(&forced); if (res != TCL_OK) { Tcl_SetObjResult(interp, Tcl_ObjPrintf("Error substituting value from \"%s\" into template, not a number: \"%s\"", Tcl_GetString(val), Tcl_GetString(subst_val))); return TCL_ERROR; } } if (subst_type == JSON_NULL && !scx->allow_null) THROW_ERROR("Only strings allowed as object keys"); if (res == TCL_OK) res = serialize_json_val(interp, scx, subst_type, subst_val); if (subst_val != NULL) Tcl_DecrRefCount(subst_val); if (reset_mode) |
︙ | ︙ | |||
669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 | return res; } //}}} void append_to_cx(struct parse_context* cx, Tcl_Obj* val) //{{{ { /* fprintf(stderr, "append_to_cx, storing %s: \"%s\"\n", type_names[val->internalRep.ptrAndLongRep.value], val->internalRep.ptrAndLongRep.ptr == NULL ? "NULL" : Tcl_GetString((Tcl_Obj*)val->internalRep.ptrAndLongRep.ptr)); */ switch (cx->container) { case JSON_OBJECT: //fprintf(stderr, "append_to_cx, cx->hold_key->refCount: %d (%s)\n", cx->hold_key->refCount, Tcl_GetString(cx->hold_key)); | > > > > > > > > | > < | > > > | > | < | > | | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 | return res; } //}}} void append_to_cx(struct parse_context* cx, Tcl_Obj* val) //{{{ { Tcl_ObjIntRep* ir = NULL; Tcl_Obj* ir_val = NULL; /* fprintf(stderr, "append_to_cx, storing %s: \"%s\"\n", type_names[val->internalRep.ptrAndLongRep.value], val->internalRep.ptrAndLongRep.ptr == NULL ? "NULL" : Tcl_GetString((Tcl_Obj*)val->internalRep.ptrAndLongRep.ptr)); */ if (cx->mode == VALIDATE) return; switch (cx->container) { case JSON_OBJECT: //fprintf(stderr, "append_to_cx, cx->hold_key->refCount: %d (%s)\n", cx->hold_key->refCount, Tcl_GetString(cx->hold_key)); ir = Tcl_FetchIntRep(cx->val, cx->objtype); if (ir == NULL) Tcl_Panic("Can't get intrep for container"); ir_val = get_unshared_val(ir); Tcl_DictObjPut(NULL, ir_val, cx->hold_key, val); if (ir->twoPtrValue.ptr2) {release_tclobj((Tcl_Obj**)&ir->twoPtrValue.ptr2);} Tcl_InvalidateStringRep(cx->val); release_tclobj(&cx->hold_key); break; case JSON_ARRAY: ir = Tcl_FetchIntRep(cx->val, cx->objtype); if (ir == NULL) Tcl_Panic("Can't get intrep for container"); ir_val = get_unshared_val(ir); //fprintf(stderr, "append_to_cx, appending to list: (%s)\n", Tcl_GetString(val)); Tcl_ListObjAppendElement(NULL, ir_val, val); if (ir->twoPtrValue.ptr2) {release_tclobj((Tcl_Obj**)&ir->twoPtrValue.ptr2);} Tcl_InvalidateStringRep(cx->val); break; default: replace_tclobj(&cx->val, val); } } //}}} int serialize(Tcl_Interp* interp, struct serialize_context* scx, Tcl_Obj* obj) //{{{ { enum json_types type = JSON_UNDEF; int res; Tcl_Obj* val = NULL; TEST_OK(JSON_GetJvalFromObj(interp, obj, &type, &val)); res = serialize_json_val(interp, scx, type, val); // The result of the serialization is left in scx->ds. Once the caller // is done with this value it must be freed with Tcl_DStringFree() return res; } //}}} static int get_modifier(Tcl_Interp* interp, Tcl_Obj* modobj, enum modifiers* modifier) //{{{ { // This must be kept in sync with the modifiers enum static CONST char *modstrings[] = { "", |
︙ | ︙ | |||
998 999 1000 1001 1002 1003 1004 | TEST_OK(Tcl_GetIndexFromObj(interp, modobj, modstrings, "modifier", TCL_EXACT, &index)); *modifier = index; return TCL_OK; } //}}} | < < < < < < < < | < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < < | < < < < < < < < < < < < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | | | > | | < | 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 | TEST_OK(Tcl_GetIndexFromObj(interp, modobj, modstrings, "modifier", TCL_EXACT, &index)); *modifier = index; return TCL_OK; } //}}} int resolve_path(Tcl_Interp* interp, Tcl_Obj* src, Tcl_Obj *const pathv[], int pathc, Tcl_Obj** target, const int exists, const int modifiers) //{{{ { int i, modstrlen; enum json_types type; struct interp_cx* l = Tcl_GetAssocData(interp, "rl_json", NULL); const char* modstr; enum modifiers modifier; Tcl_Obj* val; Tcl_Obj* step; #define EXISTS(bool) \ if (exists) { \ Tcl_SetObjResult(interp, (bool) ? l->tcl_true : l->tcl_false); \ return TCL_OK; \ } replace_tclobj(target, src); if (unlikely(JSON_GetJvalFromObj(interp, *target, &type, &val) != TCL_OK)) { if (exists) { Tcl_ResetResult(interp); // [dict exists] considers any test to be false when applied to an invalid value, so we do the same EXISTS(0); } return TCL_ERROR; } //fprintf(stderr, "resolve_path, initial type %s\n", type_names[type]); for (i=0; i<pathc; i++) { step = pathv[i]; |
︙ | ︙ | |||
1503 1504 1505 1506 1507 1508 1509 | switch (type) { case JSON_ARRAY: { int ac; Tcl_Obj** av; TEST_OK(Tcl_ListObjGetElements(interp, val, &ac, &av)); EXISTS(1); | | | | | | | | > > | | < < | < | > > > > | > > > | < < < | 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 | switch (type) { case JSON_ARRAY: { int ac; Tcl_Obj** av; TEST_OK(Tcl_ListObjGetElements(interp, val, &ac, &av)); EXISTS(1); replace_tclobj(target, Tcl_NewIntObj(ac)); } break; case JSON_STRING: EXISTS(1); replace_tclobj(target, Tcl_NewIntObj(Tcl_GetCharLength(val))); break; case JSON_DYN_STRING: case JSON_DYN_NUMBER: case JSON_DYN_BOOL: case JSON_DYN_JSON: case JSON_DYN_TEMPLATE: case JSON_DYN_LITERAL: EXISTS(1); replace_tclobj(target, Tcl_NewIntObj(Tcl_GetCharLength(val) + 3)); break; default: EXISTS(0); THROW_ERROR(Tcl_GetString(step), " modifier is not supported for type ", type_names[type]); } break; //}}} case MODIFIER_SIZE: //{{{ if (type != JSON_OBJECT) { EXISTS(0); THROW_ERROR(Tcl_GetString(step), " modifier is not supported for type ", type_names[type]); } { int size; TEST_OK(Tcl_DictObjSize(interp, val, &size)); EXISTS(1); replace_tclobj(target, Tcl_NewIntObj(size)); } break; //}}} case MODIFIER_TYPE: //{{{ EXISTS(1); replace_tclobj(target, l->type[type]); break; //}}} case MODIFIER_KEYS: //{{{ if (type != JSON_OBJECT) { EXISTS(0); THROW_ERROR(Tcl_GetString(step), " modifier is not supported for type ", type_names[type]); } { Tcl_DictSearch search; Tcl_Obj* k; Tcl_Obj* v; int done, retval=TCL_OK; Tcl_Obj* res = NULL; TEST_OK(Tcl_DictObjFirst(interp, val, &search, &k, &v, &done)); if (exists) { Tcl_DictObjDone(&search); EXISTS(1); } replace_tclobj(&res, Tcl_NewListObj(0, NULL)); for (; !done; Tcl_DictObjNext(&search, &k, &v, &done)) TEST_OK_BREAK(retval, Tcl_ListObjAppendElement(interp, res, k)); Tcl_DictObjDone(&search); if (retval == TCL_OK) replace_tclobj(target, res); release_tclobj(&res); if (retval != TCL_OK) return retval; } break; //}}} default: THROW_ERROR("Unhandled modifier type: ", Tcl_GetString(Tcl_NewIntObj(modifier))); } //fprintf(stderr, "Handled modifier, skipping descent check\n"); break; } } } switch (type) { case JSON_UNDEF: //{{{ THROW_ERROR("Found JSON_UNDEF type jval following path"); //}}} case JSON_OBJECT: //{{{ { Tcl_Obj* new = NULL; TEST_OK(Tcl_DictObjGet(interp, val, step, &new)); replace_tclobj(target, new); } if (*target == NULL) { EXISTS(0); Tcl_SetObjResult(interp, Tcl_ObjPrintf("Path element %d: \"%s\" not found", pathc+1, Tcl_GetString(step))); return TCL_ERROR; } //TEST_OK(JSON_GetJvalFromObj(interp, src, &type, &val)); //fprintf(stderr, "Descended into object, new type: %s, val: (%s)\n", type_names[type], Tcl_GetString(val)); break; //}}} case JSON_ARRAY: //{{{ |
︙ | ︙ | |||
1644 1645 1646 1647 1648 1649 1650 | //fprintf(stderr, "Explicit index: %ld\n", index); } if (index < 0 || index >= ac) { // Soft error - set target to an NULL object in // keeping with [lindex] behaviour EXISTS(0); | | | > | > | | | < > > > | > | > | | | > | | | < < | | < < < | < > > > | | > | < | | > > > | > | > > | < | | | | < | | | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < < < | < < < < < < < < < < | < < < < < < < < < < | | 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 | //fprintf(stderr, "Explicit index: %ld\n", index); } if (index < 0 || index >= ac) { // Soft error - set target to an NULL object in // keeping with [lindex] behaviour EXISTS(0); replace_tclobj(target, l->json_null); //fprintf(stderr, "index %ld is out of range [0, %d], setting target to a synthetic null\n", index, ac); } else { replace_tclobj(target, av[index]); //fprintf(stderr, "extracted index %ld: (%s)\n", index, Tcl_GetString(*target)); } } break; //}}} case JSON_STRING: case JSON_NUMBER: case JSON_BOOL: case JSON_NULL: case JSON_DYN_STRING: case JSON_DYN_NUMBER: case JSON_DYN_BOOL: case JSON_DYN_JSON: case JSON_DYN_TEMPLATE: case JSON_DYN_LITERAL: { EXISTS(0); Tcl_SetObjResult(interp, Tcl_ObjPrintf( "Cannot descend into atomic type \"%s\" with path element %d: \"%s\"", type_names[type], pathc, Tcl_GetString(step) )); return TCL_ERROR; } default: Tcl_SetObjResult(interp, Tcl_ObjPrintf("Unhandled type: %d", type)); return TCL_ERROR; } TEST_OK(JSON_GetJvalFromObj(interp, *target, &type, &val)); //fprintf(stderr, "Walked on to new type %s\n", type_names[type]); } //fprintf(stderr, "Returning target: (%s)\n", Tcl_GetString(*target)); EXISTS(type != JSON_NULL); return TCL_OK; } //}}} int convert_to_tcl(Tcl_Interp* interp, Tcl_Obj* obj, Tcl_Obj** out) //{{{ { enum json_types type; int res = TCL_OK; Tcl_Obj* val = NULL; TEST_OK(JSON_GetJvalFromObj(interp, obj, &type, &val)); /* fprintf(stderr, "Retrieved internal rep of jval: type: %s, intrep Tcl_Obj type: %s, object: %p\n", type_names[type], val && val->typePtr ? val->typePtr->name : "<no type>", val); */ switch (type) { case JSON_OBJECT: { int done; Tcl_DictSearch search; Tcl_Obj* k = NULL; Tcl_Obj* v = NULL; Tcl_Obj* vo = NULL; Tcl_Obj* new = NULL; replace_tclobj(&new, Tcl_NewDictObj()); TEST_OK(Tcl_DictObjFirst(interp, val, &search, &k, &v, &done)); for (; !done; Tcl_DictObjNext(&search, &k, &v, &done)) { TEST_OK_BREAK(res, convert_to_tcl(interp, v, &vo)); TEST_OK_BREAK(res, Tcl_DictObjPut(interp, new, k, vo)); } Tcl_DictObjDone(&search); release_tclobj(&vo); if (res == TCL_OK) replace_tclobj(out, new); release_tclobj(&new); } break; case JSON_ARRAY: { int i, oc; Tcl_Obj** ov = NULL; Tcl_Obj* elem = NULL; Tcl_Obj* new = NULL; replace_tclobj(&new, Tcl_NewListObj(0, NULL)); TEST_OK(Tcl_ListObjGetElements(interp, val, &oc, &ov)); for (i=0; i<oc; i++) { TEST_OK_BREAK(res, convert_to_tcl(interp, ov[i], &elem)); TEST_OK_BREAK(res, Tcl_ListObjAppendElement(interp, new, elem)); } release_tclobj(&elem); if (res == TCL_OK) replace_tclobj(out, new); release_tclobj(&new); } break; case JSON_STRING: case JSON_NUMBER: case JSON_BOOL: replace_tclobj(out, val); break; case JSON_NULL: { struct interp_cx* l = Tcl_GetAssocData(interp, "rl_json", NULL); replace_tclobj(out, l->tcl_empty); } break; // These are all just semantically normal JSON string values in this context case JSON_DYN_STRING: case JSON_DYN_NUMBER: case JSON_DYN_BOOL: case JSON_DYN_JSON: case JSON_DYN_TEMPLATE: case JSON_DYN_LITERAL: replace_tclobj(out, Tcl_ObjPrintf("%s%s", dyn_prefix[type], Tcl_GetString(val))); break; default: THROW_ERROR("Invalid value type"); } return res; } //}}} static int _new_object(Tcl_Interp* interp, int objc, Tcl_Obj *const objv[], Tcl_Obj** res) //{{{ { int i, ac, retval = TCL_OK; Tcl_Obj** av; Tcl_Obj* k; Tcl_Obj* v; Tcl_Obj* new_val; Tcl_Obj* val; if (objc % 2 != 0) THROW_ERROR("json new object needs an even number of arguments"); Tcl_IncrRefCount(val = Tcl_NewDictObj()); for (i=0; i<objc; i+=2) { k = objv[i]; v = objv[i+1]; TEST_OK_LABEL(end, retval, Tcl_ListObjGetElements(interp, v, &ac, &av)); TEST_OK_LABEL(end, retval, new_json_value_from_list(interp, ac, av, &new_val)); TEST_OK_LABEL(end, retval, Tcl_DictObjPut(interp, val, k, new_val)); } Tcl_IncrRefCount(*res = JSON_NewJvalObj(JSON_OBJECT, val)); end: release_tclobj(&val); return retval; } //}}} static void foreach_state_free(struct foreach_state* state) //{{{ { unsigned int i, j; |
︙ | ︙ | |||
2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 | return Tcl_NREvalObj(interp, state->script, 0); } //}}} static int NRforeach_next_loop_bottom(ClientData cdata[], Tcl_Interp* interp, int retcode) //{{{ { struct foreach_state* state = (struct foreach_state*)cdata[0]; switch (retcode) { case TCL_OK: | > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 | return Tcl_NREvalObj(interp, state->script, 0); } //}}} static int NRforeach_next_loop_bottom(ClientData cdata[], Tcl_Interp* interp, int retcode) //{{{ { struct foreach_state* state = (struct foreach_state*)cdata[0]; struct interp_cx* l = Tcl_GetAssocData(interp, "rl_json", NULL); Tcl_Obj* it_res = NULL; switch (retcode) { case TCL_OK: switch (state->collecting) { case COLLECT_NONE: break; case COLLECT_LIST: Tcl_IncrRefCount(it_res = Tcl_GetObjResult(interp)); Tcl_ResetResult(interp); TEST_OK_LABEL(done, retcode, Tcl_ListObjAppendElement(interp, state->res, it_res)); Tcl_DecrRefCount(it_res); it_res = NULL; break; case COLLECT_ARRAY: case COLLECT_OBJECT: { enum json_types type; Tcl_Obj* val = NULL; // Intrep of state->res if (Tcl_IsShared(state->res)) { Tcl_Obj* new = NULL; Tcl_IncrRefCount(new = Tcl_DuplicateObj(state->res)); Tcl_DecrRefCount(state->res); state->res = new; // Transfers ref from new to state->res } TEST_OK_LABEL(done, retcode, JSON_GetJvalFromObj(interp, state->res, &type, &val)); Tcl_IncrRefCount(it_res = Tcl_GetObjResult(interp)); Tcl_ResetResult(interp); switch (state->collecting) { case COLLECT_ARRAY: TEST_OK_LABEL(done, retcode, Tcl_ListObjAppendElement(interp, val, as_json(interp, it_res))); break; case COLLECT_OBJECT: if (it_res->typePtr == l->typeDict) { // Iterate over it_res as a dictionary {{{ Tcl_DictSearch search; Tcl_Obj* k = NULL; Tcl_Obj* v = NULL; int done; TEST_OK_LABEL(done, retcode, Tcl_DictObjFirst(interp, it_res, &search, &k, &v, &done)); for (; !done; Tcl_DictObjNext(&search, &k, &v, &done)) { TEST_OK_LABEL(cleanup_search, retcode, Tcl_DictObjPut(interp, val, k, as_json(interp, v))); } cleanup_search: Tcl_DictObjDone(&search); if (retcode != TCL_OK) goto done; break; //}}} } else { // Iterate over it_res as a list {{{ int oc, i; Tcl_Obj** ov = NULL; TEST_OK_LABEL(done, retcode, Tcl_ListObjGetElements(interp, it_res, &oc, &ov)); if (oc % 2 != 0) THROW_ERROR_LABEL(done, retcode, "Iteration result must be a list with an even number of elements"); for (i=0; i<oc; i+=2) TEST_OK_LABEL(done, retcode, Tcl_DictObjPut(interp, val, ov[i], as_json(interp, ov[i+1]))); //}}} } break; default: THROW_ERROR_LABEL(done, retcode, "Unexpect value for collecting"); } if (it_res) Tcl_DecrRefCount(it_res); it_res = NULL; } break; } case TCL_CONTINUE: retcode = TCL_OK; break; case TCL_BREAK: retcode = TCL_OK; |
︙ | ︙ | |||
2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 | if (state->res != NULL) { Tcl_SetObjResult(interp, state->res); } } done: //fprintf(stderr, "done\n"); if (retcode == TCL_OK && state->res != NULL /* collecting */) Tcl_SetObjResult(interp, state->res); foreach_state_free(state); Tcl_Free((char*)state); state = NULL; return retcode; } //}}} | > > > > > | > | > > > > | > | > > > | > > > | > | | | 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 | if (state->res != NULL) { Tcl_SetObjResult(interp, state->res); } } done: //fprintf(stderr, "done\n"); if (it_res != NULL) { Tcl_DecrRefCount(it_res); it_res = NULL; } if (retcode == TCL_OK && state->res != NULL /* collecting */) Tcl_SetObjResult(interp, state->res); foreach_state_free(state); Tcl_Free((char*)state); state = NULL; return retcode; } //}}} static int foreach(Tcl_Interp* interp, int objc, Tcl_Obj *const objv[], enum collecting_mode collecting) //{{{ { // Caller must ensure that objc is valid unsigned int i; int retcode=TCL_OK; struct foreach_state* state = NULL; state = (struct foreach_state*)Tcl_Alloc(sizeof(*state)); state->iterators = (objc-1)/2; state->it = (struct foreach_iterator*)Tcl_Alloc(sizeof(struct foreach_iterator) * state->iterators); state->max_loops = 0; state->loop_num = 0; state->collecting = collecting; Tcl_IncrRefCount(state->script = objv[objc-1]); switch (state->collecting) { case COLLECT_NONE: state->res = NULL; break; case COLLECT_LIST: Tcl_IncrRefCount(state->res = Tcl_NewListObj(0, NULL)); break; case COLLECT_ARRAY: Tcl_IncrRefCount(state->res = JSON_NewJvalObj(JSON_ARRAY, Tcl_NewListObj(0, NULL))); break; case COLLECT_OBJECT: Tcl_IncrRefCount(state->res = JSON_NewJvalObj(JSON_OBJECT, Tcl_NewDictObj())); break; default: THROW_ERROR_LABEL(done, retcode, "Unhandled value for collecting"); } for (i=0; i<state->iterators; i++) { state->it[i].search.dictionaryPtr = NULL; state->it[i].data_v = NULL; state->it[i].is_array = 0; state->it[i].var_v = NULL; state->it[i].varlist = NULL; } for (i=0; i<state->iterators; i++) { int loops, j; enum json_types type; Tcl_Obj* val; Tcl_Obj* varlist = objv[i*2]; if (Tcl_IsShared(varlist)) varlist = Tcl_DuplicateObj(varlist); Tcl_IncrRefCount(state->it[i].varlist = varlist); TEST_OK_LABEL(done, retcode, Tcl_ListObjGetElements(interp, state->it[i].varlist, &state->it[i].var_c, &state->it[i].var_v)); |
︙ | ︙ | |||
2154 2155 2156 2157 2158 2159 2160 | } if (state->loop_num < state->max_loops) return NRforeach_next_loop_top(interp, state); done: //fprintf(stderr, "done\n"); | | | | > > | 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 | } if (state->loop_num < state->max_loops) return NRforeach_next_loop_top(interp, state); done: //fprintf(stderr, "done\n"); if (retcode == TCL_OK && state->collecting != COLLECT_NONE) Tcl_SetObjResult(interp, state->res); foreach_state_free(state); Tcl_Free((char*)state); state = NULL; return retcode; } //}}} int json_pretty(Tcl_Interp* interp, Tcl_Obj* json, Tcl_Obj* indent, Tcl_Obj* pad, Tcl_DString* ds) //{{{ { int indent_len, pad_len, next_pad_len, count; enum json_types type; const char* pad_str; const char* next_pad_str; Tcl_Obj* next_pad; Tcl_Obj* val; struct serialize_context scx; scx.ds = ds; scx.serialize_mode = SERIALIZE_NORMAL; scx.fromdict = NULL; scx.l = Tcl_GetAssocData(interp, "rl_json", NULL); scx.allow_null = 1; TEST_OK(JSON_GetJvalFromObj(interp, json, &type, &val)); Tcl_GetStringFromObj(indent, &indent_len); pad_str = Tcl_GetStringFromObj(pad, &pad_len); switch (type) { |
︙ | ︙ | |||
2288 2289 2290 2291 2292 2293 2294 | serialize(interp, &scx, json); } return TCL_OK; } //}}} | < | > > | 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 | serialize(interp, &scx, json); } return TCL_OK; } //}}} static int json_pretty_dbg(Tcl_Interp* interp, Tcl_Obj* json, Tcl_Obj* indent, Tcl_Obj* pad, Tcl_DString* ds) //{{{ { int indent_len, pad_len, next_pad_len, count; enum json_types type; const char* pad_str; const char* next_pad_str; Tcl_Obj* next_pad; Tcl_Obj* val; struct serialize_context scx; scx.ds = ds; scx.serialize_mode = SERIALIZE_NORMAL; scx.fromdict = NULL; scx.l = Tcl_GetAssocData(interp, "rl_json", NULL); scx.allow_null = 1; TEST_OK(JSON_GetJvalFromObj(interp, json, &type, &val)); Tcl_GetStringFromObj(indent, &indent_len); pad_str = Tcl_GetStringFromObj(pad, &pad_len); if (type == JSON_NULL) { |
︙ | ︙ | |||
2419 2420 2421 2422 2423 2424 2425 | default: serialize_json_val(interp, &scx, type, val); } return TCL_OK; } | | | 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 | default: serialize_json_val(interp, &scx, type, val); } return TCL_OK; } //}}} #if 0 static int merge(Tcl_Interp* interp, int deep, Tcl_Obj *const orig, Tcl_Obj *const patch, Tcl_Obj **const res) //{{{ { Tcl_Obj* val; Tcl_Obj* pval; int type, ptype, done, retcode=TCL_OK; |
︙ | ︙ | |||
2501 2502 2503 2504 2505 2506 2507 | default: THROW_ERROR("Unsupported JSON type: ", Tcl_GetString(Tcl_NewIntObj(type))); } } //}}} #endif | | | | < | | < | < | < | < < < < < < < | < < < < < | < < < < < | < | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | | > > | | | < < < < < < < < < < < | < < < | < < < < < < < < < < < < < < < < < < < < < < | > > | < < | | 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 | default: THROW_ERROR("Unsupported JSON type: ", Tcl_GetString(Tcl_NewIntObj(type))); } } //}}} #endif static int prev_opcode(const struct template_cx *const cx) //{{{ { int len, opcode; Tcl_Obj* last = NULL; TEST_OK(Tcl_ListObjLength(cx->interp, cx->actions, &len)); if (len == 0) return NOP; TEST_OK(Tcl_ListObjIndex(cx->interp, cx->actions, len-3, &last)); TEST_OK(Tcl_GetIndexFromObj(cx->interp, last, action_opcode_str, "opcode", TCL_EXACT, &opcode)); return opcode; } //}}} static int emit_action(const struct template_cx* cx, enum action_opcode opcode, Tcl_Obj *const a, Tcl_Obj *const slot) // TODO: inline? {{{ { /* fprintf(stderr, "opcode %s: %s %s\n", Tcl_GetString(cx->l->action[opcode]), a == NULL ? "NULL" : Tcl_GetString(a), b == NULL ? "NULL" : Tcl_GetString(b)); */ TEST_OK(Tcl_ListObjAppendElement(cx->interp, cx->actions, cx->l->action[opcode])); TEST_OK(Tcl_ListObjAppendElement(cx->interp, cx->actions, a==NULL ? cx->l->tcl_empty : a)); TEST_OK(Tcl_ListObjAppendElement(cx->interp, cx->actions, slot==NULL ? cx->l->tcl_empty : slot)); return TCL_OK; } //}}} static int emit_fetches(const struct template_cx *const cx) //{{{ { Tcl_DictSearch search; Tcl_Obj* elem; Tcl_Obj* v; int done, retcode=TCL_OK; TEST_OK(Tcl_DictObjFirst(cx->interp, cx->map, &search, &elem, &v, &done)); for (; !done; Tcl_DictObjNext(&search, &elem, &v, &done)) { int len, fetch_idx, types_search_done=0, used_fetch=0; Tcl_DictSearch types_search; Tcl_Obj* type; Tcl_Obj* slot; TEST_OK_LABEL(done, retcode, emit_action(cx, FETCH_VALUE, elem, NULL) ); TEST_OK_LABEL(done, retcode, Tcl_ListObjLength(cx->interp, cx->actions, &len) ); fetch_idx = len-3; // Record the position of the fetch, in case we need to remove it later (DYN_LITERAL) TEST_OK_LABEL(done, retcode, Tcl_DictObjFirst(cx->interp, v, &types_search, &type, &slot, &done)); for (; !types_search_done; Tcl_DictObjNext(&types_search, &type, &slot, &types_search_done)) { int subst_type; TEST_OK_LABEL(done2, retcode, lookup_type(cx->interp, type, &subst_type)); if (subst_type != JSON_DYN_LITERAL) used_fetch = 1; // Each of these actions checks for NULL in value and inserts a JSON null in that case switch (subst_type) { case JSON_DYN_STRING: TEST_OK_LABEL(done2, retcode, emit_action(cx, STORE_STRING, NULL, slot) ); break; case JSON_DYN_JSON: TEST_OK_LABEL(done2, retcode, emit_action(cx, STORE_JSON, NULL, slot) ); break; case JSON_DYN_TEMPLATE: TEST_OK_LABEL(done2, retcode, emit_action(cx, STORE_TEMPLATE, NULL, slot) ); break; case JSON_DYN_NUMBER: TEST_OK_LABEL(done2, retcode, emit_action(cx, STORE_NUMBER, NULL, slot) ); break; case JSON_DYN_BOOL: TEST_OK_LABEL(done2, retcode, emit_action(cx, STORE_BOOLEAN, NULL, slot) ); break; case JSON_DYN_LITERAL: { const char* s; int len; enum json_types type; s = Tcl_GetStringFromObj(elem, &len); TEMPLATE_TYPE(s, len, type); // s is advanced past prefix if (type == JSON_STRING) { TEST_OK_LABEL(done2, retcode, emit_action(cx, DECLARE_LITERAL, elem, NULL) ); TEST_OK_LABEL(done2, retcode, emit_action(cx, STORE_STRING, NULL, slot) ); } else { TEST_OK_LABEL(done2, retcode, emit_action(cx, DECLARE_LITERAL, JSON_NewJvalObj(type, get_string(cx->l, s, len-3)), NULL) ); TEST_OK_LABEL(done2, retcode, emit_action(cx, STORE_JSON, NULL, slot) ); } } break; default: Tcl_SetObjResult(cx->interp, Tcl_ObjPrintf("Invalid type \"%s\"", Tcl_GetString(type))); retcode = TCL_ERROR; goto done2; } } if (!used_fetch) // Value from fetch wasn't used, drop the FETCH_VALUE opcode TEST_OK_LABEL(done, retcode, Tcl_ListObjReplace(cx->interp, cx->actions, fetch_idx, 3, 0, NULL) ); done2: Tcl_DictObjDone(&types_search); if (retcode != TCL_OK) break; } done: Tcl_DictObjDone(&search); return retcode; } //}}} static int get_subst_slot(struct template_cx* cx, Tcl_Obj* elem, Tcl_Obj* type, int subst_type, Tcl_Obj** slot) //{{{ { int retcode = TCL_OK; Tcl_Obj* keydict = NULL; Tcl_Obj* slotobj = NULL; // Find the map for this key TEST_OK(Tcl_DictObjGet(cx->interp, cx->map, elem, &keydict)); if (keydict == NULL) { keydict = Tcl_NewDictObj(); TEST_OK_LABEL(finally, retcode, Tcl_DictObjPut(cx->interp, cx->map, elem, keydict)); } //fprintf(stderr, "get_subst_slot (%s) %s:\n%s\n", Tcl_GetString(elem), Tcl_GetString(type), Tcl_GetString(keydict)); // Find the allocated slot for this type for this key TEST_OK_LABEL(finally, retcode, Tcl_DictObjGet(cx->interp, keydict, type, &slotobj)); if (slotobj != NULL) { Tcl_IncrRefCount(slotobj); } else { replace_tclobj(&slotobj, Tcl_NewIntObj(cx->slots_used++)); TEST_OK_LABEL(finally, retcode, Tcl_DictObjPut(cx->interp, keydict, type, slotobj)); /* fprintf(stderr, "Allocated new slot for %s %s: %s\n", Tcl_GetString(elem), Tcl_GetString(type), Tcl_GetString(*slot)); } else { fprintf(stderr, "Found slot for %s %s: %s\n", Tcl_GetString(elem), Tcl_GetString(type), Tcl_GetString(*slot)); */ } if (retcode == TCL_OK) replace_tclobj(slot, slotobj); finally: release_tclobj(&slotobj); return retcode; } //}}} /* static int record_subst_location(Tcl_Interp* interp, Tcl_Obj* parent, Tcl_Obj* elem, Tcl_Obj* registry, Tcl_Obj* slot) //{{{ { Tcl_Obj* path_info = NULL; |
︙ | ︙ | |||
2643 2644 2645 2646 2647 2648 2649 | TEST_OK(Tcl_DictObjPut(interp, path_info, elem, slot)); return TCL_OK; } //}}} */ | > > > > > > > > > > > > > | | | | > | < < < | | > | < < | < < < < < < < < > | > > > | | | > > > > > | | > > | | > | | < | | < < < < < < < < < < < < < < < < < < < > > > > > | < > | < | < | | < < < | > | < < < | > > > | | | > < < < < | | > | > | | > | < | | < < | | < < < | < < > | < < < < < | > > > | < < | | | | | > > | > | < > | > | | > | > | < < < < < < < < < < < < < < < < | > > | < | | | < | > > | < < | | < < < < < < < < < < < | < > > | > > > | | < < > > < | | | > | < | | | < < | < | | < < | | < > > | < | < | < < < | < | > > > > | < < < < | | | | > | > | > | | > | > | > | | < < < > > | | > | > > | | | | > | > | | < > | > | > | | | | < < < < | < < | > > | > > | > | > > | > | > > > < < < | > | | | > | > > > > > > > > > > > > > > > | > > > > > > > | | | < | > | | > < < < < < < | < | > > | < < < | | < > > > < < | > > | | | < < > > | | > > > > > > > | | > > > | | > > | > | < > | > > | | | < > | > > > | > | < > > > > > | | | > | > | < > > | < < < | | | > > > > | | | < < | > > | | < | < | > | | > > > | | > > > | < < | < < < > | < < | | | | < < < | | < < | < | > > > > > > | | | > > > | > | > > > > > > > > > > | > > | > > | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < | > > > > > > | > > > | | | > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > < | | | > > > > > > > > > > > > > > > > > > > < | > > > > | < | < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | < | < < < | < < < < | < < < < < | < | | < | < < < < < < < < | < < < < < < < < < < < < < < < < < | < < < < < < < < < < < < < < < < < < < | < < < < < < < | < < < < | < < < < < < < < | < < < < < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < | < < < < | < | | | < < < | < < | | | | < < < | < < < > | | < < | < | < > > > | < | | | < | | | < < < | < | | < | | < | | < | > | > > < | | < | | < < < < < < < | < < < < < < < | < < < < < < < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < | < < | < < < < < < < < | < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | | > > > | < | < < | < < < < < < < > < | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | > | > | < | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 2912 2913 2914 2915 2916 2917 2918 2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958 2959 2960 2961 2962 2963 2964 2965 2966 2967 2968 2969 2970 2971 2972 2973 2974 2975 2976 2977 2978 2979 2980 2981 2982 2983 2984 2985 2986 2987 2988 2989 2990 2991 2992 2993 2994 2995 2996 2997 2998 2999 3000 3001 3002 3003 3004 3005 3006 3007 3008 3009 3010 3011 3012 3013 3014 3015 3016 3017 3018 3019 3020 3021 3022 3023 3024 3025 3026 3027 3028 3029 3030 3031 3032 3033 3034 3035 3036 3037 3038 3039 3040 3041 3042 3043 3044 3045 3046 3047 3048 3049 3050 3051 3052 3053 3054 3055 3056 3057 3058 3059 3060 3061 3062 3063 3064 3065 3066 3067 3068 3069 3070 3071 3072 3073 3074 3075 3076 3077 3078 3079 3080 3081 3082 3083 3084 3085 3086 3087 3088 3089 3090 3091 3092 3093 3094 3095 3096 3097 3098 3099 3100 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110 3111 3112 3113 3114 3115 3116 3117 3118 3119 3120 3121 3122 3123 3124 3125 3126 3127 3128 3129 3130 3131 3132 3133 3134 3135 3136 3137 3138 3139 3140 3141 3142 3143 3144 3145 3146 3147 3148 3149 3150 3151 3152 3153 3154 3155 3156 3157 3158 3159 3160 3161 3162 3163 3164 3165 3166 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189 3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240 3241 3242 3243 3244 3245 3246 3247 3248 3249 3250 3251 3252 3253 3254 3255 3256 3257 3258 3259 3260 3261 3262 3263 3264 3265 3266 3267 3268 3269 3270 3271 3272 3273 3274 3275 3276 3277 3278 3279 3280 3281 3282 3283 3284 3285 3286 3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 3307 3308 3309 3310 3311 3312 3313 3314 3315 3316 3317 3318 3319 3320 3321 3322 3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 3361 3362 3363 3364 3365 3366 3367 3368 3369 3370 3371 3372 3373 3374 3375 3376 3377 3378 3379 3380 3381 3382 3383 3384 3385 3386 3387 3388 3389 3390 3391 3392 3393 3394 3395 3396 3397 3398 3399 3400 3401 3402 3403 3404 3405 3406 3407 3408 3409 3410 3411 3412 3413 3414 3415 3416 3417 3418 3419 3420 3421 3422 3423 3424 3425 3426 3427 3428 3429 3430 3431 3432 3433 3434 3435 3436 3437 3438 3439 3440 3441 3442 3443 3444 3445 3446 3447 3448 3449 3450 3451 3452 3453 3454 3455 3456 3457 3458 3459 3460 3461 3462 3463 3464 3465 3466 3467 3468 3469 3470 3471 3472 3473 3474 3475 3476 3477 3478 3479 3480 3481 3482 3483 3484 3485 3486 3487 3488 3489 3490 3491 3492 3493 3494 3495 3496 3497 3498 3499 3500 3501 3502 3503 3504 3505 3506 3507 3508 3509 3510 3511 3512 3513 3514 3515 3516 3517 3518 3519 3520 3521 3522 3523 3524 3525 3526 3527 3528 3529 3530 3531 3532 3533 3534 3535 3536 3537 3538 3539 3540 3541 3542 3543 3544 3545 3546 3547 3548 3549 3550 3551 3552 3553 3554 3555 3556 3557 3558 3559 3560 3561 3562 3563 3564 3565 3566 3567 3568 3569 3570 3571 3572 3573 3574 3575 3576 3577 3578 3579 3580 3581 3582 3583 3584 3585 3586 3587 3588 3589 3590 3591 3592 3593 3594 3595 3596 3597 3598 3599 3600 3601 3602 3603 3604 3605 3606 3607 3608 3609 3610 3611 3612 3613 3614 3615 3616 3617 3618 3619 3620 3621 3622 3623 3624 3625 3626 3627 3628 3629 3630 3631 3632 3633 3634 3635 3636 3637 3638 3639 3640 3641 3642 3643 3644 3645 3646 3647 3648 3649 3650 3651 3652 3653 3654 3655 3656 3657 3658 3659 3660 3661 3662 3663 3664 3665 3666 3667 3668 3669 3670 3671 3672 3673 3674 3675 3676 3677 3678 3679 3680 3681 3682 3683 3684 3685 3686 3687 3688 3689 3690 3691 3692 3693 3694 3695 3696 3697 3698 3699 3700 3701 3702 3703 3704 3705 3706 3707 3708 3709 3710 3711 3712 3713 3714 3715 3716 3717 3718 3719 3720 3721 3722 3723 3724 3725 3726 3727 3728 3729 3730 3731 3732 3733 3734 3735 3736 3737 3738 3739 3740 3741 3742 3743 3744 3745 3746 3747 3748 3749 3750 3751 3752 3753 3754 3755 3756 3757 3758 3759 3760 3761 3762 3763 3764 3765 3766 3767 3768 3769 3770 3771 3772 3773 3774 3775 3776 3777 3778 3779 3780 3781 3782 3783 3784 3785 3786 3787 3788 3789 3790 3791 3792 3793 3794 3795 3796 3797 3798 3799 3800 3801 3802 3803 3804 3805 3806 3807 3808 3809 3810 3811 3812 3813 3814 3815 3816 3817 3818 3819 3820 3821 3822 3823 3824 3825 3826 3827 3828 3829 3830 3831 3832 3833 3834 3835 3836 3837 3838 3839 3840 3841 3842 3843 3844 3845 3846 3847 3848 3849 3850 3851 3852 3853 3854 3855 3856 3857 3858 3859 3860 | TEST_OK(Tcl_DictObjPut(interp, path_info, elem, slot)); return TCL_OK; } //}}} */ static int remove_action(Tcl_Interp* interp, struct template_cx* cx, int idx) //{{{ { idx *= 3; if (idx < 0) { int len; TEST_OK(Tcl_ListObjLength(interp, cx->actions, &len)); idx += len; } return Tcl_ListObjReplace(interp, cx->actions, idx, 3, 0, NULL); } //}}} static int template_actions(struct template_cx* cx, Tcl_Obj* template, enum action_opcode rep_action, Tcl_Obj* elem) //{{{ { enum json_types type; Tcl_Obj* val = NULL; Tcl_Interp* interp = cx->interp; int retval = TCL_OK; TEST_OK(JSON_GetJvalFromObj(interp, template, &type, &val)); switch (type) { case JSON_STRING: case JSON_NUMBER: case JSON_BOOL: case JSON_NULL: break; case JSON_OBJECT: { int done, retval = TCL_OK; Tcl_DictSearch search; Tcl_Obj* k; Tcl_Obj* v; TEST_OK(emit_action(cx, PUSH_TARGET, template, NULL)); TEST_OK(Tcl_DictObjFirst(interp, val, &search, &k, &v, &done)); for (; !done; Tcl_DictObjNext(&search, &k, &v, &done)) { int len; enum json_types stype; const char* s = Tcl_GetStringFromObj(k, &len); TEST_OK_LABEL(free_search, retval, template_actions(cx, v, REPLACE_VAL, k)); TEMPLATE_TYPE(s, len, stype); // s is advanced past prefix switch (stype) { case JSON_STRING: break; case JSON_DYN_STRING: case JSON_DYN_LITERAL: { Tcl_Obj* slot = NULL; //fprintf(stderr, "Found key subst at \"%s\": (%s) %s %s, allocated slot %s\n", Tcl_GetString(path), Tcl_GetString(k), type_names_int[stype], s+3, Tcl_GetString(slot)); retval = get_subst_slot(cx, get_string(cx->l, s, len-3), cx->l->type_int[stype], stype, &slot); if (retval == TCL_OK) retval = emit_action(cx, REPLACE_KEY, k, slot); release_tclobj(&slot); if (retval != TCL_OK) goto free_search; } break; default: THROW_ERROR("Only strings allowed as object keys"); } } free_search: Tcl_DictObjDone(&search); if (prev_opcode(cx) == PUSH_TARGET) { remove_action(interp, cx, -1); } else { TEST_OK(emit_action(cx, POP_TARGET, NULL, NULL)); TEST_OK(emit_action(cx, rep_action, elem, cx->l->tcl_zero)); } if (retval != TCL_OK) return retval; } break; case JSON_ARRAY: { int i, oc; Tcl_Obj** ov; Tcl_Obj* arr_elem = NULL; TEST_OK(emit_action(cx, PUSH_TARGET, template, NULL)); TEST_OK(Tcl_ListObjGetElements(interp, val, &oc, &ov)); for (i=0; i<oc; i++) { replace_tclobj(&arr_elem, Tcl_NewIntObj(i)); if (TCL_OK != (retval = template_actions(cx, ov[i], REPLACE_ARR, arr_elem))) break; } release_tclobj(&arr_elem); if (retval != TCL_OK) return retval; if (prev_opcode(cx) == PUSH_TARGET) { remove_action(interp, cx, -1); } else { TEST_OK(emit_action(cx, POP_TARGET, NULL, NULL)); TEST_OK(emit_action(cx, rep_action, elem, cx->l->tcl_zero)); } } break; case JSON_DYN_STRING: case JSON_DYN_NUMBER: case JSON_DYN_BOOL: case JSON_DYN_JSON: case JSON_DYN_TEMPLATE: case JSON_DYN_LITERAL: { Tcl_Obj* slot = NULL; //fprintf(stderr, "Found value subst at \"%s\": (%s) %s: %s, allocated slot %s\n", Tcl_GetString(parent), Tcl_GetString(elem), type_names_int[type], Tcl_GetString(val), Tcl_GetString(slot)); retval = get_subst_slot(cx, val, cx->l->type_int[type], type, &slot); if (retval == TCL_OK) retval = emit_action(cx, rep_action, elem, slot); release_tclobj(&slot); } break; default: THROW_ERROR("unhandled type: %d", type); } return retval; } //}}} int build_template_actions(Tcl_Interp* interp, Tcl_Obj* template, Tcl_Obj** actions) //{{{ { int retcode=TCL_OK; struct template_cx cx = { // Unspecified members are initialized to 0 .interp = interp, .l = Tcl_GetAssocData(interp, "rl_json", NULL), .slots_used = 1 // slot 0 is the scratch space, where completed targets go when popped }; release_tclobj(actions); replace_tclobj(&cx.map, Tcl_NewDictObj()); replace_tclobj(&cx.actions, Tcl_NewListObj(0, NULL)); TEST_OK_LABEL(done, retcode, template_actions(&cx, template, REPLACE_ATOM, cx.l->tcl_empty) ); if (cx.slots_used > 1) { // Prepend the template action to allocate the slots Tcl_Obj* actions_tail=NULL; int maxdepth=0; // Save the current actions (containing the interpolate and traversal // actions), and swap in a new one that we will populate with some // prefix actions (allocation and fetching) replace_tclobj(&actions_tail, cx.actions); replace_tclobj(&cx.actions, Tcl_NewListObj(0, NULL)); { // Find max cx stack depth int depth=0, actionc, i; Tcl_Obj** actionv; TEST_OK_LABEL(actions_done, retcode, Tcl_ListObjGetElements(interp, actions_tail, &actionc, &actionv) ); for (i=0; i<actionc; i+=3) { int opcode; TEST_OK_LABEL(actions_done, retcode, Tcl_GetIndexFromObj(interp, actionv[i], action_opcode_str, "opcode", TCL_EXACT, &opcode) ); switch (opcode) { case PUSH_TARGET: if (++depth > maxdepth) maxdepth = depth; break; case POP_TARGET: depth--; break; } } } TEST_OK_LABEL(actions_done, retcode, emit_action(&cx, ALLOCATE, Tcl_NewIntObj(maxdepth), Tcl_NewIntObj(cx.slots_used)) ); TEST_OK_LABEL(actions_done, retcode, emit_fetches(&cx) ); // Add back on the actions tail TEST_OK_LABEL(actions_done, retcode, Tcl_ListObjAppendList(interp, cx.actions, actions_tail) ); actions_done: release_tclobj(&actions_tail); if (retcode != TCL_OK) goto done; } replace_tclobj(actions, cx.actions); Tcl_IncrRefCount(*actions); // DEBUG: trigger obj leak done: release_tclobj(&cx.map); release_tclobj(&cx.actions); return retcode; } //}}} int lookup_type(Tcl_Interp* interp, Tcl_Obj* typeobj, int* type) //{{{ { return Tcl_GetIndexFromObj(interp, typeobj, type_names_int, "type", TCL_EXACT, type); } //}}} static inline void fill_slot(Tcl_Obj** slots, int slot, Tcl_Obj* value) //{{{ { replace_tclobj(&slots[slot], value); } //}}} int apply_template_actions(Tcl_Interp* interp, Tcl_Obj* template, Tcl_Obj* actions, Tcl_Obj* dict, Tcl_Obj** res) // dict may be null, which means lookup vars {{{ { struct interp_cx* l = NULL; #define STATIC_SLOTS 10 Tcl_Obj* stackslots[STATIC_SLOTS]; Tcl_Obj** slots = NULL; int slotslen = 0; int retcode = TCL_OK; Tcl_Obj** actionv; int actionc, i; #define STATIC_STACK 8 Tcl_Obj* stackstack[STATIC_STACK]; Tcl_Obj** stack = NULL; int stacklevel = 0; Tcl_Obj* subst_val = NULL; Tcl_Obj* key = NULL; int slot, stacklevels=0; Tcl_Obj* target = NULL; TEST_OK_LABEL(finally, retcode, Tcl_ListObjGetElements(interp, actions, &actionc, &actionv)); if (actionc == 0) { replace_tclobj(res, Tcl_DuplicateObj(template)); Tcl_InvalidateStringRep(*res); // Some code relies on the fact that the result of the template command is a normalized json doc (no unnecessary whitespace / newlines) return TCL_OK; } if (actionc % 3 != 0) THROW_ERROR_LABEL(finally, retcode, "Invalid actions (odd number of elements)"); l = Tcl_GetAssocData(interp, "rl_json", NULL); for (i=0; i<actionc; i+=3) { int tmp; enum action_opcode opcode; Tcl_Obj* a = actionv[i+1]; Tcl_Obj* b = actionv[i+2]; TEST_OK_LABEL(finally, retcode, Tcl_GetIndexFromObj(interp, actionv[i], action_opcode_str, "opcode", TCL_EXACT, &tmp)); opcode = tmp; //fprintf(stderr, "%s (%s) (%s)\n", Tcl_GetString(actionv[i]), Tcl_GetString(a), Tcl_GetString(b)); switch (opcode) { case ALLOCATE: //{{{ { // slots is in b, stack is in a TEST_OK_LABEL(finally, retcode, Tcl_GetIntFromObj(interp, b, &slotslen)); if (slotslen > STATIC_SLOTS) { slots = ckalloc(sizeof(Tcl_Obj*) * slotslen); } else { slots = stackslots; } if (slotslen > 0) memset(slots, 0, sizeof(Tcl_Obj*) * slotslen); TEST_OK_LABEL(finally, retcode, Tcl_GetIntFromObj(interp, a, &stacklevels)); if (stacklevels > STATIC_STACK) { stack = ckalloc(sizeof(struct Tcl_Obj*) * stacklevels); } else { stack = stackstack; // Use the space allocated on the c stack } if (stacklevels > 0) memset(stack, 0, sizeof(Tcl_Obj*) * stacklevels); } break; //}}} case FETCH_VALUE: //{{{ replace_tclobj(&key, a); // Keep a reference in case we need it for an error message shortly if (dict) { Tcl_Obj* new = NULL; TEST_OK_LABEL(finally, retcode, Tcl_DictObjGet(interp, dict, a, &new)); replace_tclobj(&subst_val, new); } else { replace_tclobj(&subst_val, Tcl_ObjGetVar2(interp, a, NULL, 0)); } break; //}}} case DECLARE_LITERAL: //{{{ replace_tclobj(&subst_val, a); break; //}}} case STORE_STRING: //{{{ TEST_OK_LABEL(finally, retcode, Tcl_GetIntFromObj(interp, b, &slot)); if (subst_val == NULL) { fill_slot(slots, slot, l->json_null); } else { const char* str; int len; Tcl_Obj* jval=NULL; str = Tcl_GetStringFromObj(subst_val, &len); if (len == 0) { replace_tclobj(&jval, l->json_empty_string); } else if (len < 3) { replace_tclobj(&jval, JSON_NewJvalObj(JSON_STRING, subst_val)); } else { enum json_types type; TEMPLATE_TYPE(str, len, type); // str is advanced to after the prefix if (type == JSON_STRING) { replace_tclobj(&jval, JSON_NewJvalObj(JSON_STRING, subst_val)); } else { replace_tclobj(&jval, JSON_NewJvalObj(type, get_string(l, str, len-3))); } } fill_slot(slots, slot, jval); release_tclobj(&jval); } break; //}}} case STORE_NUMBER: //{{{ TEST_OK_LABEL(finally, retcode, Tcl_GetIntFromObj(interp, b, &slot)); if (subst_val == NULL) { fill_slot(slots, slot, l->json_null); } else { Tcl_Obj* forced = NULL; if (likely((retcode = force_json_number(interp, l, subst_val, &forced)) == TCL_OK)) fill_slot(slots, slot, JSON_NewJvalObj(JSON_NUMBER, forced)); release_tclobj(&forced); if (unlikely(retcode != TCL_OK)) { Tcl_SetObjResult(interp, Tcl_ObjPrintf("Error substituting value from \"%s\" into template, not a number: \"%s\"", Tcl_GetString(key), Tcl_GetString(subst_val))); retcode = TCL_ERROR; goto finally; } } break; //}}} case STORE_BOOLEAN: //{{{ TEST_OK_LABEL(finally, retcode, Tcl_GetIntFromObj(interp, b, &slot)); if (subst_val == NULL) { fill_slot(slots, slot, l->json_null); } else { int is_true; TEST_OK_LABEL(finally, retcode, Tcl_GetBooleanFromObj(interp, subst_val, &is_true)); fill_slot(slots, slot, is_true ? l->json_true : l->json_false); } break; //}}} case STORE_JSON: //{{{ TEST_OK_LABEL(finally, retcode, Tcl_GetIntFromObj(interp, b, &slot)); if (subst_val == NULL) { fill_slot(slots, slot, l->json_null); } else { TEST_OK_LABEL(finally, retcode, JSON_ForceJSON(interp, subst_val)); fill_slot(slots, slot, subst_val); } break; //}}} case STORE_TEMPLATE: //{{{ { Tcl_Obj* sub_template_actions = NULL; Tcl_Obj* new = NULL; int slot; TEST_OK_LABEL(finally, retcode, Tcl_GetIntFromObj(interp, b, &slot)); if (subst_val == NULL) { fill_slot(slots, slot, l->json_null); } else { // recursively fill out sub template if ( TCL_OK == (retcode = build_template_actions(interp, subst_val, &sub_template_actions)) && TCL_OK == (retcode = apply_template_actions(interp, subst_val, sub_template_actions, dict, &new)) ) { // Result of a template substitution is guaranteed to be JSON if the return was TCL_OK //TEST_OK_LABEL(finally, retcode, JSON_ForceJSON(interp, new)); fill_slot(slots, slot, new); release_tclobj(&new); } release_tclobj(&sub_template_actions); if (retcode != TCL_OK) goto finally; } break; } //}}} case PUSH_TARGET: if (target) Tcl_IncrRefCount(stack[stacklevel++] = target); /* if (Tcl_IsShared(a)) a = Tcl_DuplicateObj(a); replace_tclobj(&target, a); */ replace_tclobj(&target, Tcl_DuplicateObj(a)); break; case POP_TARGET: // save target to slot[0] and pop the parent target off the stack fill_slot(slots, 0, target); if (stacklevel > 0) { Tcl_Obj* popped = stack[--stacklevel]; replace_tclobj(&target, popped); release_tclobj(&stack[stacklevel]); } else { release_tclobj(&target); } break; case REPLACE_ARR: { int slot, idx; Tcl_ObjIntRep* ir = NULL; Tcl_Obj* ir_obj = NULL; // a is idx, b is slot TEST_OK_LABEL(finally, retcode, Tcl_GetIntFromObj(interp, b, &slot)); TEST_OK_LABEL(finally, retcode, Tcl_GetIntFromObj(interp, a, &idx)); if (Tcl_IsShared(target)) { THROW_ERROR_LABEL(finally, retcode, "target is shared for REPLACE_ARR"); } ir = Tcl_FetchIntRep(target, g_objtype_for_type[JSON_ARRAY]); if (ir == NULL) { Tcl_SetObjResult(interp, Tcl_ObjPrintf("Could not fetch array intrep for target array %s", Tcl_GetString(target))); retcode = TCL_ERROR; goto finally; } ir_obj = get_unshared_val(ir); TEST_OK_LABEL(finally, retcode, Tcl_ListObjReplace(interp, ir_obj, idx, 1, 1, &slots[slot])); Tcl_InvalidateStringRep(target); release_tclobj((Tcl_Obj**)&ir->twoPtrValue.ptr2); } break; case REPLACE_VAL: { int slot; Tcl_ObjIntRep* ir = NULL; Tcl_Obj* ir_obj = NULL; // a is key, b is slot TEST_OK_LABEL(finally, retcode, Tcl_GetIntFromObj(interp, b, &slot)); ir = Tcl_FetchIntRep(target, g_objtype_for_type[JSON_OBJECT]); if (ir == NULL) { Tcl_SetObjResult(interp, Tcl_ObjPrintf("Could not fetch array intrep for target object %s", Tcl_GetString(target))); retcode = TCL_ERROR; goto finally; } ir_obj = get_unshared_val(ir); TEST_OK_LABEL(finally, retcode, Tcl_DictObjPut(interp, ir_obj, a, slots[slot])); Tcl_InvalidateStringRep(target); release_tclobj((Tcl_Obj**)&ir->twoPtrValue.ptr2); } break; case REPLACE_ATOM: { int slot; // b is slot TEST_OK_LABEL(finally, retcode, Tcl_GetIntFromObj(interp, b, &slot)); replace_tclobj(&target, slots[slot]); } break; case REPLACE_KEY: { int slot; Tcl_ObjIntRep* ir = NULL; Tcl_Obj* ir_obj = NULL; Tcl_Obj* hold = NULL; // a is key, b is slot (which holds the new key name) TEST_OK_LABEL(finally, retcode, Tcl_GetIntFromObj(interp, b, &slot)); ir = Tcl_FetchIntRep(target, g_objtype_for_type[JSON_OBJECT]); if (ir == NULL) { Tcl_SetObjResult(interp, Tcl_ObjPrintf("Could not fetch array intrep for target object %s", Tcl_GetString(target))); retcode = TCL_ERROR; goto finally; } ir_obj = get_unshared_val(ir); TEST_OK_LABEL(finally, retcode, Tcl_DictObjGet(interp, ir_obj, a, &hold)); Tcl_IncrRefCount(hold); TEST_OK_LABEL(finally, retcode, Tcl_DictObjRemove(interp, ir_obj, a)); { Tcl_Obj* key_ir_obj = NULL; enum json_types key_type; // The value in the slot is a JSON value (JSON_STRING or JSON_DYN_LITERAL), so we need to // fetch it's Tcl string (from its intrep) TEST_OK_LABEL(finally, retcode, JSON_GetJvalFromObj(interp, slots[slot], &key_type, &key_ir_obj)); switch (key_type) { case JSON_STRING: TEST_OK_LABEL(finally, retcode, Tcl_DictObjPut(interp, ir_obj, key_ir_obj, hold)); break; case JSON_DYN_STRING: case JSON_DYN_NUMBER: case JSON_DYN_BOOL: case JSON_DYN_JSON: case JSON_DYN_TEMPLATE: case JSON_DYN_LITERAL: TEST_OK_LABEL(finally, retcode, Tcl_DictObjPut(interp, ir_obj, Tcl_ObjPrintf("%s%s", dyn_prefix[key_type], Tcl_GetString(key_ir_obj)), hold)); break; default: Tcl_SetObjResult(interp, Tcl_ObjPrintf( "Only strings allowed as object keys, got: %s for key \"%s\"", Tcl_GetString(slots[slot]), Tcl_GetString(a) )); //Tcl_SetObjErrorCode(interp, actions); retcode = TCL_ERROR; goto finally; } } release_tclobj(&hold); Tcl_InvalidateStringRep(target); release_tclobj((Tcl_Obj**)&ir->twoPtrValue.ptr2); } break; default: Tcl_SetObjResult(interp, Tcl_ObjPrintf("Unhandled opcode: %s", Tcl_GetString(actionv[i]))); retcode = TCL_ERROR; goto finally; } } replace_tclobj(res, target); finally: if (slots) { for (i=0; i<slotslen; i++) release_tclobj(&slots[i]); if (slots != stackslots) ckfree(slots); slots = NULL; } release_tclobj(&key); release_tclobj(&subst_val); if (stack) { for (i=0; i<stacklevel; i++) release_tclobj(&stack[i]); if (stack != stackstack) ckfree(stack); stack = NULL; } return retcode; } //}}} // Ensemble subcommands static int jsonParse(ClientData cdata, Tcl_Interp* interp, int objc, Tcl_Obj *const objv[]) //{{{ { Tcl_Obj* res = NULL; if (objc != 2) { Tcl_WrongNumArgs(interp, 1, objv, "json_val"); return TCL_ERROR; } TEST_OK(JSON_ForceJSON(interp, objv[1])); // Force parsing objv[1] as JSON TEST_OK(convert_to_tcl(interp, objv[1], &res)); Tcl_SetObjResult(interp, res); release_tclobj(&res); return TCL_OK; } //}}} static int jsonNormalize(ClientData cdata, Tcl_Interp* interp, int objc, Tcl_Obj *const objv[]) //{{{ { Tcl_Obj* json = NULL; if (objc != 2) { Tcl_WrongNumArgs(interp, 1, objv, "json_val"); return TCL_ERROR; } json = objv[1]; if (Tcl_IsShared(json)) json = Tcl_DuplicateObj(json); TEST_OK(JSON_ForceJSON(interp, json)); Tcl_InvalidateStringRep(json); // Defer string rep generation to our caller Tcl_SetObjResult(interp, json); return TCL_OK; } //}}} static int jsonType(ClientData cdata, Tcl_Interp* interp, int objc, Tcl_Obj *const objv[]) //{{{ { enum json_types type; Tcl_Obj* val; Tcl_Obj* target = NULL; int retval = TCL_OK; struct interp_cx* l = (struct interp_cx*)cdata; if (objc < 2) { Tcl_WrongNumArgs(interp, 1, objv, "json_val ?path ...?"); return TCL_ERROR; } if (objc >= 3) { TEST_OK(resolve_path(interp, objv[1], objv+2, objc-2, &target, 0, 0)); } else { replace_tclobj(&target, objv[1]); } retval = JSON_GetJvalFromObj(interp, target, &type, &val); release_tclobj(&target); if (retval == TCL_OK) Tcl_SetObjResult(interp, l->type[type]); return retval; } //}}} static int jsonLength(ClientData cdata, Tcl_Interp* interp, int objc, Tcl_Obj *const objv[]) //{{{ { struct interp_cx* l = (struct interp_cx*)cdata; int length; int retval = TCL_OK; Tcl_Obj* target = NULL; Tcl_Obj* path = NULL; if (objc < 2) CHECK_ARGS(2, "length json_val ?path ...?"); replace_tclobj(&path, objc >= 3 ? Tcl_NewListObj(objc-2, objv+2) : l->tcl_empty_list ); retval = JSON_Length(interp, objv[1], path, &length); if (retval == TCL_OK) { switch (length) { case 0: Tcl_SetObjResult(interp, l->tcl_zero); break; case 1: Tcl_SetObjResult(interp, l->tcl_one); break; default: Tcl_SetObjResult(interp, Tcl_NewIntObj(length)); break; } } release_tclobj(&target); return retval; } //}}} static int jsonKeys(ClientData cdata, Tcl_Interp* interp, int objc, Tcl_Obj *const objv[]) //{{{ { struct interp_cx* l = (struct interp_cx*)cdata; Tcl_Obj* path = NULL; Tcl_Obj* keylist = NULL; int retval = TCL_OK; if (objc < 2) { Tcl_WrongNumArgs(interp, 1, objv, "json_val ?path ...?"); return TCL_ERROR; } if (objc >= 3) { replace_tclobj(&path, Tcl_NewListObj(objc-2, objv+2)); } else { replace_tclobj(&path, l->tcl_empty_list); } retval = JSON_Keys(interp, objv[1], path, &keylist); if (retval == TCL_OK) Tcl_SetObjResult(interp, keylist); release_tclobj(&keylist); return retval; } //}}} static int jsonExists(ClientData cdata, Tcl_Interp* interp, int objc, Tcl_Obj *const objv[]) //{{{ { Tcl_Obj* target = NULL; if (objc < 2) { Tcl_WrongNumArgs(interp, 1, objv, "json_val ?path ...?"); return TCL_ERROR; } if (objc >= 3) { TEST_OK(resolve_path(interp, objv[1], objv+2, objc-2, &target, 1, 1)); release_tclobj(&target); // resolve_path sets the interp result in exists mode } else { enum json_types type; Tcl_Obj* val; TEST_OK(JSON_GetJvalFromObj(interp, objv[1], &type, &val)); Tcl_SetObjResult(interp, Tcl_NewBooleanObj(type != JSON_NULL)); } return TCL_OK; } //}}} static int jsonGet(ClientData cdata, Tcl_Interp* interp, int objc, Tcl_Obj *const objv[]) //{{{ { Tcl_Obj* target = NULL; Tcl_Obj* res = NULL; int convert=1, retval=TCL_OK; if (objc < 2) { Tcl_WrongNumArgs(interp, 1, objv, "json_val ?path ...?"); return TCL_ERROR; } if (objc >= 3) { const char* s = NULL; int l; TEST_OK(resolve_path(interp, objv[1], objv+2, objc-2, &target, 0, 1)); s = Tcl_GetStringFromObj(objv[objc-1], &l); if (s[0] == '?' && s[1] != '?') { // If the last element of the path is an unquoted // modifier, we need to skip the conversion from JSON // (it won't be json, but the modifier result) convert = 0; } } else { enum json_types type; Tcl_ObjIntRep* ir; replace_tclobj(&target, objv[1]); TEST_OK_LABEL(finally, retval, JSON_GetIntrepFromObj(interp, target, &type, &ir)); // Force parsing objv[2] as JSON } if (convert) { TEST_OK_LABEL(finally, retval, convert_to_tcl(interp, target, &res)); } else { replace_tclobj(&res, target); } if (retval == TCL_OK) Tcl_SetObjResult(interp, res); finally: release_tclobj(&target); release_tclobj(&res); return retval; } //}}} static int jsonExtract(ClientData cdata, Tcl_Interp* interp, int objc, Tcl_Obj *const objv[]) //{{{ { Tcl_Obj* target = NULL; if (objc < 2) { Tcl_WrongNumArgs(interp, 1, objv, "json_val ?path ...?"); return TCL_ERROR; } if (objc >= 3) { TEST_OK(resolve_path(interp, objv[1], objv+2, objc-2, &target, 0, 0)); } else { enum json_types type; Tcl_Obj* val; TEST_OK(JSON_GetJvalFromObj(interp, objv[1], &type, &val)); // Just a validation, keeps the contract that we return JSON replace_tclobj(&target, objv[1]); } Tcl_SetObjResult(interp, target); release_tclobj(&target); return TCL_OK; } //}}} static int jsonSet(ClientData cdata, Tcl_Interp* interp, int objc, Tcl_Obj *const objv[]) //{{{ { Tcl_Obj* path = NULL; if (objc < 3) { Tcl_WrongNumArgs(interp, 1, objv, "varname ?path ...? json_val"); return TCL_ERROR; } // -3, +2: last element of objv is the replacement, not part of the path replace_tclobj(&path, Tcl_NewListObj(objc-3, objv+2)); TEST_OK(JSON_Set(interp, objv[1], path, objv[objc-1])); release_tclobj(&path); return TCL_OK; } //}}} static int jsonUnset(ClientData cdata, Tcl_Interp* interp, int objc, Tcl_Obj *const objv[]) //{{{ { Tcl_Obj* path = NULL; if (objc < 2) { Tcl_WrongNumArgs(interp, 1, objv, "varname ?path ...?"); return TCL_ERROR; } replace_tclobj(&path, Tcl_NewListObj(objc-2, objv+2)); TEST_OK(JSON_Unset(interp, objv[1], path)); release_tclobj(&path); return TCL_OK; } //}}} static int jsonNew(ClientData cdata, Tcl_Interp* interp, int objc, Tcl_Obj *const objv[]) //{{{ { if (objc < 2) { Tcl_WrongNumArgs(interp, 1, objv, "type ?val?"); return TCL_ERROR; } Tcl_Obj* res = NULL; TEST_OK(new_json_value_from_list(interp, objc-1, objv+1, &res)); Tcl_SetObjResult(interp, res); release_tclobj(&res); return TCL_OK; } //}}} static int jsonString(ClientData cdata, Tcl_Interp* interp, int objc, Tcl_Obj *const objv[]) //{{{ { #if DEDUP struct interp_cx* l = (struct interp_cx*)cdata; #endif int len; const char* s; enum json_types type; if (objc != 2) { Tcl_WrongNumArgs(interp, 1, objv, "value"); return TCL_ERROR; } s = Tcl_GetStringFromObj(objv[1], &len); TEMPLATE_TYPE(s, len, type); // s is advanced past prefix if (type == JSON_STRING) { Tcl_SetObjResult(interp, JSON_NewJvalObj(JSON_STRING, get_string(l, s, len))); } else { Tcl_SetObjResult(interp, JSON_NewJvalObj(type, get_string(l, s, len-3))); } return TCL_OK; } //}}} static int jsonNumber(ClientData cdata, Tcl_Interp* interp, int objc, Tcl_Obj *const objv[]) //{{{ { Tcl_Obj* forced = NULL; struct interp_cx* l = (struct interp_cx*)cdata; int res = TCL_OK; if (objc != 2) { Tcl_WrongNumArgs(interp, 1, objv, "value"); return TCL_ERROR; } if (likely((res = force_json_number(interp, l, objv[1], &forced)) == TCL_OK)) { Tcl_SetObjResult(interp, JSON_NewJvalObj(JSON_NUMBER, forced)); } release_tclobj(&forced); return res; } //}}} static int jsonBoolean(ClientData cdata, Tcl_Interp* interp, int objc, Tcl_Obj *const objv[]) //{{{ { int b; if (objc != 2) { Tcl_WrongNumArgs(interp, 1, objv, "value"); return TCL_ERROR; } TEST_OK(Tcl_GetBooleanFromObj(interp, objv[1], &b)); Tcl_SetObjResult(interp, JSON_NewJvalObj(JSON_BOOL, Tcl_NewBooleanObj(b))); return TCL_OK; } //}}} static int jsonObject(ClientData cdata, Tcl_Interp* interp, int objc, Tcl_Obj *const objv[]) //{{{ { int oc; Tcl_Obj** ov; Tcl_Obj* res = NULL; if (objc == 2) { TEST_OK(Tcl_ListObjGetElements(interp, objv[1], &oc, &ov)); TEST_OK(_new_object(interp, oc, ov, &res)); } else { TEST_OK(_new_object(interp, objc-1, objv+1, &res)); } Tcl_SetObjResult(interp, res); release_tclobj(&res); return TCL_OK; } //}}} static int jsonArray(ClientData cdata, Tcl_Interp* interp, int objc, Tcl_Obj *const objv[]) //{{{ { int i, ac, retval = TCL_OK;; Tcl_Obj** av; Tcl_Obj* elem = NULL; Tcl_Obj* val = NULL; Tcl_IncrRefCount(val = Tcl_NewListObj(0, NULL)); for (i=1; i<objc; i++) { TEST_OK_LABEL(end_new_array, retval, Tcl_ListObjGetElements(interp, objv[i], &ac, &av)); TEST_OK_LABEL(end_new_array, retval, new_json_value_from_list(interp, ac, av, &elem)); TEST_OK_LABEL(end_new_array, retval, Tcl_ListObjAppendElement(interp, val, elem)); } Tcl_SetObjResult(interp, JSON_NewJvalObj(JSON_ARRAY, val)); end_new_array: release_tclobj(&val); return retval; } //}}} static int jsonDecode(ClientData cdata, Tcl_Interp* interp, int objc, Tcl_Obj *const objv[]) //{{{ { Tcl_Obj* encoding = NULL; Tcl_Obj* res = NULL; int retval = TCL_OK; if (objc < 2 || objc > 3) { Tcl_WrongNumArgs(interp, 1, objv, "bytes ?encoding?"); return TCL_ERROR; } if (objc >= 3) replace_tclobj(&encoding, objv[2]); retval = JSON_Decode(interp, objv[1], encoding, &res); if (retval == TCL_OK) Tcl_SetObjResult(interp, res); release_tclobj(&res); release_tclobj(&encoding); return retval; } //}}} static int jsonIsNull(ClientData cdata, Tcl_Interp* interp, int objc, Tcl_Obj *const objv[]) //{{{ { Tcl_Obj* target = NULL; Tcl_Obj* val; enum json_types type; int retval = TCL_OK; if (objc < 2) { Tcl_WrongNumArgs(interp, 1, objv, "json_val ?path ...?"); return TCL_ERROR; } if (objc >= 3) { TEST_OK(resolve_path(interp, objv[1], objv+2, objc-2, &target, 0, 0)); } else { replace_tclobj(&target, objv[1]); } retval = JSON_GetJvalFromObj(interp, target, &type, &val); if (retval == TCL_OK) Tcl_SetObjResult(interp, Tcl_NewBooleanObj(type == JSON_NULL)); release_tclobj(&target); return retval; } //}}} static int jsonTemplateString(ClientData cdata, Tcl_Interp* interp, int objc, Tcl_Obj *const objv[]) //{{{ { int res; struct serialize_context scx; Tcl_DString ds; if (objc < 2 || objc > 3) { Tcl_WrongNumArgs(interp, 1, objv, "json_template ?source_dict?"); return TCL_ERROR; } Tcl_DStringInit(&ds); scx.ds = &ds; scx.serialize_mode = SERIALIZE_TEMPLATE; scx.fromdict = NULL; scx.l = (struct interp_cx*)cdata; scx.allow_null = 1; if (objc >= 3) Tcl_IncrRefCount(scx.fromdict = objv[2]); res = serialize(interp, &scx, objv[1]); release_tclobj(&scx.fromdict); if (res == TCL_OK) Tcl_DStringResult(interp, scx.ds); Tcl_DStringFree(scx.ds); scx.ds = NULL; return res == TCL_OK ? TCL_OK : TCL_ERROR; } //}}} static int jsonTemplate(ClientData cdata, Tcl_Interp* interp, int objc, Tcl_Obj *const objv[]) //{{{ { Tcl_Obj* res = NULL; if (objc < 2 || objc > 3) { Tcl_WrongNumArgs(interp, 1, objv, "json_template ?source_dict?"); return TCL_ERROR; } TEST_OK(JSON_Template(interp, objv[1], objc >= 3 ? objv[2] : NULL, &res)); if (res == NULL) { Tcl_SetObjResult(interp, Tcl_ObjPrintf("res is NULL from JSON_Template!")); return TCL_ERROR; } Tcl_SetObjResult(interp, res); release_tclobj(&res); return TCL_OK; } //}}} static int _foreach(ClientData cdata, Tcl_Interp* interp, int objc, Tcl_Obj *const objv[], enum collecting_mode mode) //{{{ { if (objc < 4 || (objc-4) % 2 != 0) { Tcl_WrongNumArgs(interp, 1, objv, "?varlist datalist ...? script"); return TCL_ERROR; } return foreach(interp, objc-1, objv+1, mode); } //}}} static int jsonNRForeach(ClientData cdata, Tcl_Interp* interp, int objc, Tcl_Obj *const objv[]) //{{{ { return _foreach(cdata, interp, objc, objv, COLLECT_NONE); } //}}} static int jsonForeach(ClientData cdata, Tcl_Interp* interp, int objc, Tcl_Obj *const objv[]) //{{{ { return Tcl_NRCallObjProc(interp, jsonNRForeach, cdata, objc, objv); } //}}} static int jsonNRLmap(ClientData cdata, Tcl_Interp* interp, int objc, Tcl_Obj *const objv[]) //{{{ { return _foreach(cdata, interp, objc, objv, COLLECT_LIST); } //}}} static int jsonLmap(ClientData cdata, Tcl_Interp* interp, int objc, Tcl_Obj *const objv[]) //{{{ { return Tcl_NRCallObjProc(interp, jsonNRLmap, cdata, objc, objv); } //}}} static int jsonNRAmap(ClientData cdata, Tcl_Interp* interp, int objc, Tcl_Obj *const objv[]) //{{{ { return _foreach(cdata, interp, objc, objv, COLLECT_ARRAY); } //}}} static int jsonAmap(ClientData cdata, Tcl_Interp* interp, int objc, Tcl_Obj *const objv[]) //{{{ { return Tcl_NRCallObjProc(interp, jsonNRAmap, cdata, objc, objv); } //}}} static int jsonNROmap(ClientData cdata, Tcl_Interp* interp, int objc, Tcl_Obj *const objv[]) //{{{ { return _foreach(cdata, interp, objc, objv, COLLECT_OBJECT); } //}}} static int jsonOmap(ClientData cdata, Tcl_Interp* interp, int objc, Tcl_Obj *const objv[]) //{{{ { return Tcl_NRCallObjProc(interp, jsonNROmap, cdata, objc, objv); } //}}} static int jsonFreeCache(ClientData cdata, Tcl_Interp* interp, int objc, Tcl_Obj *const objv[]) //{{{ { #if DEDUP struct interp_cx* l = (struct interp_cx*)cdata; #endif if (objc != 1) { Tcl_WrongNumArgs(interp, 1, objv, ""); return TCL_ERROR; } free_cache(l); return TCL_OK; } //}}} static int jsonNop(ClientData cdata, Tcl_Interp* interp, int objc, Tcl_Obj *const objv[]) //{{{ { return TCL_OK; } //}}} static int jsonPretty(ClientData cdata, Tcl_Interp* interp, int objc, Tcl_Obj *const objv[]) //{{{ { Tcl_Obj* pretty = NULL; int retval = TCL_OK; Tcl_Obj* indent = NULL; if (objc < 2 || objc > 3) CHECK_ARGS(2, "pretty json_val ?indent?"); if (objc >= 3) replace_tclobj(&indent, objv[2]); retval = JSON_Pretty(interp, objv[1], indent, &pretty); if (retval == TCL_OK) Tcl_SetObjResult(interp, pretty); release_tclobj(&pretty); return retval; } //}}} static int jsonValid(ClientData cdata, Tcl_Interp* interp, int objc, Tcl_Obj *const objv[]) //{{{ { struct interp_cx* l = (struct interp_cx*)cdata; int i, valid, retval=TCL_OK; struct parse_error details = {}; Tcl_Obj* detailsvar = NULL; enum extensions extensions = EXT_COMMENTS; // By default, use the default set of extensions we accept static const char *options[] = { "-extensions", "-details", (char*)NULL }; enum { O_EXTENSIONS, O_DETAILS }; if (objc < 2) { Tcl_WrongNumArgs(interp, 1, objv, "?-extensions extensionslist -details detailsvar? json_val"); return TCL_ERROR; } for (i=1; i<objc-1; i++) { int option; TEST_OK(Tcl_GetIndexFromObj(interp, objv[i], options, "option", TCL_EXACT, &option)); switch (option) { case O_EXTENSIONS: { Tcl_Obj** ov; int oc, idx; extensions = 0; // An explicit list was supplied, reset the extensions if (i >= objc-2) { // Missing value for -extensions Tcl_WrongNumArgs(interp, i+1, objv, "extensionslist json_val"); return TCL_ERROR; } i++; // Point at the next arg: the extensionslist TEST_OK(Tcl_ListObjGetElements(interp, objv[i], &oc, &ov)); for (idx=0; idx<oc; idx++) { int ext; TEST_OK(Tcl_GetIndexFromObj(interp, ov[idx], extension_str, "extension", TCL_EXACT, &ext)); extensions |= ext; } } break; case O_DETAILS: { if (i >= objc-2) { // Missing value for -extensions Tcl_WrongNumArgs(interp, i+1, objv, "detailsvar json_val"); return TCL_ERROR; } i++; // Point at the next arg: the extensionslist detailsvar = objv[i]; } break; default: Tcl_SetObjResult(interp, Tcl_ObjPrintf("Unexpected option %d", option)); return TCL_ERROR; } } TEST_OK(JSON_Valid(interp, objv[objc-1], &valid, extensions, &details)); Tcl_SetObjResult(interp, valid ? l->tcl_true : l->tcl_false); if (!valid && detailsvar) { Tcl_Obj* details_obj = NULL; Tcl_Obj* k = NULL; Tcl_Obj* v = NULL; replace_tclobj(&details_obj, Tcl_NewDictObj()); replace_tclobj(&k, get_string(l, "errmsg", 6)); replace_tclobj(&v, Tcl_NewStringObj(details.errmsg, -1)); TEST_OK_LABEL(finally, retval, Tcl_DictObjPut(interp, details_obj, k, v)); replace_tclobj(&k, get_string(l, "doc", 3)); replace_tclobj(&v, Tcl_NewStringObj(details.doc, -1)); TEST_OK_LABEL(finally, retval, Tcl_DictObjPut(interp, details_obj, k, v)); replace_tclobj(&k, get_string(l, "char_ofs", 8)); replace_tclobj(&v, Tcl_NewIntObj(details.char_ofs)); TEST_OK_LABEL(finally, retval, Tcl_DictObjPut(interp, details_obj, k, v)); if (NULL == Tcl_ObjSetVar2(interp, detailsvar, NULL, details_obj, TCL_LEAVE_ERR_MSG)) retval = TCL_ERROR; finally: release_tclobj(&details_obj); release_tclobj(&k); release_tclobj(&v); } return retval; } //}}} static int jsonDebug(ClientData cdata, Tcl_Interp* interp, int objc, Tcl_Obj *const objv[]) //{{{ { struct interp_cx* l = (struct interp_cx*)cdata; int retval = TCL_OK; Tcl_DString ds; Tcl_Obj* indent = NULL; Tcl_Obj* pad = NULL; if (objc < 2 || objc > 3) CHECK_ARGS(2, "pretty json_val ?indent?"); if (objc >= 3) { indent = objv[2]; } else { indent = get_string(l, " ", 4); } Tcl_IncrRefCount(indent); Tcl_IncrRefCount(pad = l->tcl_empty); Tcl_DStringInit(&ds); if ((retval = json_pretty_dbg(interp, objv[1], indent, pad, &ds)) == TCL_OK) Tcl_DStringResult(interp, &ds); release_tclobj(&pad); release_tclobj(&indent); Tcl_DStringFree(&ds); return retval; } //}}} static int jsonTemplateActions(ClientData cdata, Tcl_Interp* interp, int objc, Tcl_Obj *const objv[]) //{{{ { Tcl_Obj* actions = NULL; Tcl_ObjIntRep* ir; enum json_types type; if (objc != 2) { Tcl_WrongNumArgs(interp, 1, objv, "json_template"); return TCL_ERROR; } TEST_OK(JSON_GetIntrepFromObj(interp, objv[1], &type, &ir)); replace_tclobj(&actions, ir->twoPtrValue.ptr2); if (actions == NULL) { TEST_OK(build_template_actions(interp, objv[1], &actions)); replace_tclobj((Tcl_Obj**)&ir->twoPtrValue.ptr2, actions); } Tcl_SetObjResult(interp, actions); release_tclobj(&actions); return TCL_OK; } //}}} #if 0 static int jsonMerge(ClientData cdata, Tcl_Interp* interp, int objc, Tcl_Obj *const objv[]) //{{{ { int i=2, deep=0, checking_flags=1, str_len; const char* str; Tcl_Obj* res = NULL; Tcl_Obj* patch; Tcl_Obj* new; static const char* flags[] = { "--", "-deep", (char*)NULL }; enum { FLAG_ENDARGS, FLAG_DEEP }; int index; THROW_ERROR("merge method is not functional yet, sorry"); if (objc < 1) CHECK_ARGS(0, "?flag ...? ?json_val ...?"); while (i < objc) { patch = objv[i++]; // Nasty optimization - prevent generating string rep of // a pure JSON value to check if it is a flag (can never // be: "-" isn't valid as the first char of a JSON value) if (patch->typePtr == &json_type) checking_flags = 0; if (checking_flags) { str = Tcl_GetStringFromObj(patch, &str_len); if (str_len > 0 && str[0] == '-') { TEST_OK(Tcl_GetIndexFromObj(interp, patch, flags, "flag", TCL_EXACT, &index)); switch (index) { case FLAG_ENDARGS: checking_flags = 0; break; case FLAG_DEEP: deep = 1; break; default: THROW_ERROR("Invalid flag"); } continue; } } if (res == NULL) { res = patch; } else { TEST_OK(merge(interp, deep, res, patch, &new)); if (new != res) res = new; } } if (res != NULL) Tcl_SetObjResult(interp, res); } //}}} #endif static int new_json_value_from_list(Tcl_Interp* interp, int objc, Tcl_Obj *const objv[], Tcl_Obj** res) //{{{ { struct interp_cx* l = Tcl_GetAssocData(interp, "rl_json", NULL); int new_type, retval=TCL_OK; static const char* types[] = { "string", "object", "array", "number", "true", "false", "null", "boolean", "json", (char*)NULL }; enum { NEW_STRING, NEW_OBJECT, NEW_ARRAY, NEW_NUMBER, NEW_TRUE, NEW_FALSE, NEW_NULL, NEW_BOOL, NEW_JSON }; if (objc < 1) CHECK_ARGS(0, "type ?val?"); TEST_OK(Tcl_GetIndexFromObj(interp, objv[0], types, "type", 0, &new_type)); switch (new_type) { case NEW_STRING: retval = jsonString(l, interp, objc, objv); break; case NEW_OBJECT: retval = jsonObject(l, interp, objc, objv); break; case NEW_ARRAY: retval = jsonArray( l, interp, objc, objv); break; case NEW_NUMBER: retval = jsonNumber(l, interp, objc, objv); break; case NEW_TRUE: //{{{ CHECK_ARGS(0, "true"); Tcl_IncrRefCount(*res = l->json_true); return TCL_OK; //}}} case NEW_FALSE: //{{{ CHECK_ARGS(0, "false"); Tcl_IncrRefCount(*res = l->json_false); return TCL_OK; //}}} case NEW_NULL: //{{{ CHECK_ARGS(0, "null"); Tcl_IncrRefCount(*res = l->json_null); return TCL_OK; //}}} case NEW_BOOL: //{{{ { int b; CHECK_ARGS(1, "boolean val"); TEST_OK(Tcl_GetBooleanFromObj(interp, objv[1], &b)); Tcl_IncrRefCount(*res = b ? l->json_true : l->json_false); } return TCL_OK; //}}} case NEW_JSON: //{{{ CHECK_ARGS(1, "json val"); TEST_OK(JSON_ForceJSON(interp, objv[1])); Tcl_IncrRefCount(*res = objv[1]); return TCL_OK; //}}} default: THROW_ERROR("Invalid new_type: ", Tcl_GetString(Tcl_NewIntObj(new_type))); } if (retval == TCL_OK) { Tcl_IncrRefCount(*res = Tcl_GetObjResult(interp)); Tcl_ResetResult(interp); } return retval; } //}}} static int jsonNRObj(ClientData cdata, Tcl_Interp* interp, int objc, Tcl_Obj *const objv[]) //{{{ { int subcommand; static const char *subcommands[] = { "parse", // DEPRECATED "normalize", "extract", "type", "length", "keys", "exists", "get", "set", "unset", "new", // DEPRECATED "fmt", // DEPRECATED "isnull", "template", "template_string", "foreach", "lmap", "amap", "omap", "pretty", "valid", "debug", // "merge", // Create json types "string", "number", "boolean", "object", "array", "decode", // Debugging "free_cache", "nop", "_leak_obj", "_leak_info", "template_actions", (char*)NULL }; enum { M_PARSE, M_NORMALIZE, M_EXTRACT, M_TYPE, M_LENGTH, M_KEYS, M_EXISTS, M_GET, M_SET, M_UNSET, M_NEW, M_FMT, M_ISNULL, M_TEMPLATE, M_TEMPLATE_STRING, M_FOREACH, M_LMAP, M_AMAP, M_OMAP, M_PRETTY, M_VALID, M_DEBUG, // M_MERGE, M_STRING, M_NUMBER, M_BOOLEAN, M_OBJECT, M_ARRAY, M_DECODE, // Debugging M_FREE_CACHE, M_NOP, M_LEAK_OBJ, M_LEAK_INFO, M_TEMPLATE_ACTIONS }; if (objc < 2) { Tcl_WrongNumArgs(interp, 1, objv, "subcommand ?arg ...?"); return TCL_ERROR; } TEST_OK(Tcl_GetIndexFromObj(interp, objv[1], subcommands, "subcommand", TCL_EXACT, &subcommand)); switch (subcommand) { case M_PARSE: return jsonParse(cdata, interp, objc-1, objv+1); case M_NORMALIZE: return jsonNormalize(cdata, interp, objc-1, objv+1); case M_TYPE: return jsonType(cdata, interp, objc-1, objv+1); case M_LENGTH: return jsonLength(cdata, interp, objc-1, objv+1); case M_KEYS: return jsonKeys(cdata, interp, objc-1, objv+1); case M_EXISTS: return jsonExists(cdata, interp, objc-1, objv+1); case M_GET: return jsonGet(cdata, interp, objc-1, objv+1); case M_EXTRACT: return jsonExtract(cdata, interp, objc-1, objv+1); case M_SET: return jsonSet(cdata, interp, objc-1, objv+1); case M_UNSET: return jsonUnset(cdata, interp, objc-1, objv+1); case M_FMT: case M_NEW: return jsonNew(cdata, interp, objc-1, objv+1); case M_STRING: return jsonString(cdata, interp, objc-1, objv+1); case M_NUMBER: return jsonNumber(cdata, interp, objc-1, objv+1); 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_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); case M_TEMPLATE_STRING: return jsonTemplateString(cdata, interp, objc-1, objv+1); case M_FOREACH: return jsonForeach(cdata, interp, objc-1, objv+1); case M_LMAP: return jsonLmap(cdata, interp, objc-1, objv+1); case M_AMAP: return jsonAmap(cdata, interp, objc-1, objv+1); case M_OMAP: return jsonOmap(cdata, interp, objc-1, objv+1); case M_FREE_CACHE: return jsonFreeCache(cdata, interp, objc-1, objv+1); case M_NOP: return jsonNop(cdata, interp, objc-1, objv+1); case M_PRETTY: return jsonPretty(cdata, interp, objc-1, objv+1); case M_VALID: return jsonValid(cdata, interp, objc-1, objv+1); case M_DEBUG: return jsonDebug(cdata, interp, objc-1, objv+1); // case M_MERGE: return jsonMerge(cdata, interp, objc-1, objv+1); case M_TEMPLATE_ACTIONS: return jsonTemplateActions(cdata, interp, objc-1, objv+1); case M_LEAK_OBJ: Tcl_NewObj(); break; case M_LEAK_INFO: { unsigned long 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; s = Tcl_GetStringFromObj(obj, &len); fprintf(stderr, "\tLeaked obj: %p[%d] len %d: \"%s\"\n", obj, obj->refCount, len, len < 256 ? s : "<too long>"); break; } default: // Should be impossible to reach THROW_ERROR("Invalid subcommand"); } return TCL_OK; } //}}} static int jsonObj(ClientData cdata, Tcl_Interp* interp, int objc, Tcl_Obj *const objv[]) //{{{ { return Tcl_NRCallObjProc(interp, jsonNRObj, cdata, objc, objv); } //}}} void free_interp_cx(ClientData cdata, Tcl_Interp* interp) //{{{ { struct interp_cx* l = cdata; int i; l->interp = NULL; release_tclobj(&l->tcl_true); release_tclobj(&l->tcl_false); release_tclobj(&l->tcl_one); release_tclobj(&l->tcl_zero); Tcl_DecrRefCount(l->tcl_empty); release_tclobj(&l->tcl_empty); release_tclobj(&l->json_true); release_tclobj(&l->json_false); release_tclobj(&l->json_null); release_tclobj(&l->json_empty_string); release_tclobj(&l->tcl_empty_dict); release_tclobj(&l->tcl_empty_list); for (i=0; i<2; i++) release_tclobj(&l->force_num_cmd[i]); for (i=0; i<JSON_TYPE_MAX; i++) { release_tclobj(&l->type_int[i]); release_tclobj(&l->type[i]); } for (i=0; i<TEMPLATE_ACTIONS_END; i++) release_tclobj(&l->action[i]); #if DEDUP free_cache(l); Tcl_DeleteHashTable(&l->kc); #endif release_tclobj(&l->apply); release_tclobj(&l->decode_bytes); free(l); l = NULL; } //}}} static int checkmem(ClientData cdata, Tcl_Interp* interp, int objc, Tcl_Obj *const objv[]) //{{{ { int retcode = TCL_OK; FILE* h_before = NULL; FILE* h_after = NULL; char linebuf[1024]; char* line = NULL; Tcl_HashTable seen; Tcl_Obj* res = NULL; #define TEMP_TEMPLATE "/tmp/rl_json_XXXXXX" char temp[sizeof(TEMP_TEMPLATE)]; int fd; #if DEDUP struct interp_cx* l = (struct interp_cx*)cdata; #endif CHECK_ARGS(2, "cmd newactive"); memcpy(temp, TEMP_TEMPLATE, sizeof(TEMP_TEMPLATE)); fd = mkstemp(temp); h_before = fdopen(fd, "r"); #if DEDUP free_cache(l); #endif Tcl_DumpActiveMemory(temp); if (unlink(temp) != 0) { Tcl_SetObjResult(interp, Tcl_ObjPrintf("Error removing before tmp file: %s", Tcl_ErrnoMsg(Tcl_GetErrno()))); retcode = TCL_ERROR; goto finally; } retcode = Tcl_EvalEx(interp, Tcl_GetString(objv[1]), -1, TCL_EVAL_DIRECT); #if DEDUP free_cache(l); #endif memcpy(temp, TEMP_TEMPLATE, sizeof(TEMP_TEMPLATE)); fd = mkstemp(temp); h_after = fdopen(fd, "r"); Tcl_DumpActiveMemory(temp); if (unlink(temp) != 0) { Tcl_SetObjResult(interp, Tcl_ObjPrintf("Error removing after tmp file: %s", Tcl_ErrnoMsg(Tcl_GetErrno()))); retcode = TCL_ERROR; goto finally; } Tcl_InitHashTable(&seen, TCL_STRING_KEYS); while (!feof(h_before)) { int new, len; line = fgets(linebuf, 1024, h_before); if (line == NULL || strstr(line, " @ ./") == NULL) continue; len = strnlen(line, 1024); if (line[len-1] == '\n') len--; Tcl_CreateHashEntry(&seen, line, &new); } fclose(h_before); h_before = NULL; replace_tclobj(&res, Tcl_NewListObj(0, NULL)); while (!feof(h_after)) { int new, len; line = fgets(linebuf, 1024, h_after); if (line == NULL || strstr(line, " @ ./") == NULL) continue; len = strnlen(line, 1024); if (line[len-1] == '\n') len--; Tcl_CreateHashEntry(&seen, line, &new); if (new) { retcode = Tcl_ListObjAppendElement(interp, res, Tcl_NewStringObj(line, len)); if (retcode != TCL_OK) break; } } fclose(h_after); h_after = NULL; if (retcode == TCL_OK) if (Tcl_ObjSetVar2(interp, objv[2], NULL, res, TCL_LEAVE_ERR_MSG) == NULL) retcode = TCL_ERROR; finally: release_tclobj(&res); if (h_before) {fclose(h_before); h_before = NULL;} if (h_after) {fclose(h_after); h_after = NULL;} Tcl_DeleteHashTable(&seen); return retcode; } //}}} #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ DLLEXPORT int Rl_json_Init(Tcl_Interp* interp) //{{{ { int i; struct interp_cx* l = NULL; #ifdef USE_TCL_STUBS if (Tcl_InitStubs(interp, "8.6", 0) == NULL) return TCL_ERROR; #endif // USE_TCL_STUBS TEST_OK(init_types(interp)); l = (struct interp_cx*)malloc(sizeof *l); l->interp = interp; Tcl_IncrRefCount(l->tcl_true = Tcl_NewStringObj("1", 1)); Tcl_IncrRefCount(l->tcl_false = Tcl_NewStringObj("0", 1)); Tcl_IncrRefCount(l->tcl_empty = Tcl_NewStringObj("", 0)); // Ensure the empty string rep is considered "shared" Tcl_IncrRefCount(l->tcl_empty); Tcl_IncrRefCount(l->tcl_one = Tcl_NewIntObj(1)); Tcl_IncrRefCount(l->tcl_zero = Tcl_NewIntObj(0)); Tcl_IncrRefCount(l->json_true = JSON_NewJvalObj(JSON_BOOL, l->tcl_true)); Tcl_IncrRefCount(l->json_false = JSON_NewJvalObj(JSON_BOOL, l->tcl_false)); Tcl_IncrRefCount(l->json_null = JSON_NewJvalObj(JSON_NULL, NULL)); Tcl_IncrRefCount(l->json_empty_string = JSON_NewJvalObj(JSON_STRING, l->tcl_empty)); Tcl_IncrRefCount(l->tcl_empty_dict = Tcl_NewDictObj()); Tcl_IncrRefCount(l->tcl_empty_list = Tcl_NewListObj(0, NULL)); // Hack to ensure a value is a number (could be any of the Tcl number types: double, int, wide, bignum) Tcl_IncrRefCount(l->force_num_cmd[0] = Tcl_NewStringObj("::tcl::mathop::+", -1)); Tcl_IncrRefCount(l->force_num_cmd[1] = Tcl_NewIntObj(0)); l->force_num_cmd[2] = NULL; // Const type name objects for (i=0; i<JSON_TYPE_MAX; i++) { Tcl_IncrRefCount(l->type_int[i] = Tcl_NewStringObj(type_names_int[i], -1)); Tcl_IncrRefCount(l->type[i] = Tcl_NewStringObj(type_names[i], -1)); } // Const template action objects for (i=0; i<TEMPLATE_ACTIONS_END; i++) Tcl_IncrRefCount(l->action[i] = Tcl_NewStringObj(action_opcode_str[i], -1)); #if DEDUP Tcl_InitHashTable(&l->kc, TCL_STRING_KEYS); l->kc_count = 0; memset(&l->freemap, 0xFF, sizeof(l->freemap)); #endif l->typeDict = Tcl_GetObjType("dict"); l->typeInt = Tcl_GetObjType("int"); l->typeDouble = Tcl_GetObjType("double"); l->typeBignum = Tcl_GetObjType("bignum"); if (l->typeDict == NULL) THROW_ERROR("Can't retrieve objType for dict"); if (l->typeInt == NULL) THROW_ERROR("Can't retrieve objType for int"); if (l->typeDouble == NULL) THROW_ERROR("Can't retrieve objType for double"); //if (l->typeBignum == NULL) THROW_ERROR("Can't retrieve objType for bignum"); Tcl_IncrRefCount(l->apply = Tcl_NewStringObj("apply", 5)); Tcl_IncrRefCount(l->decode_bytes = Tcl_NewStringObj( // Tcl lambda to decode raw bytes to a unicode string {{{ "{bytes {encoding auto}} {\n" //" puts \"Decoding using $encoding: [regexp -all -inline .. [binary encode hex $bytes]]\"\n" " set decode_utf16 {{bytes encoding} {\n" //" puts \"Decoding using $encoding: [regexp -all -inline .. [binary encode hex $bytes]]\"\n" " set process_utf16_word {char {\n" " upvar 1 w1 w1 w2 w2\n" "\n" " set t [expr {$char & 0b1111110000000000}]\n" " if {$t == 0b1101100000000000} { # high surrogate\n" " set w1 [expr {$char & 0b0000001111111111}]\n" " } elseif {$t == 0b1101110000000000} { # low surrogate\n" " set w2 [expr {$char & 0b0000001111111111}]\n" " } else {\n" //"puts \"emitting [format %x $char]: ([format %c $char])\"\n" " return [format %c $char]\n" " }\n" "\n" " if {[info exists w1] && [info exists w2]} {\n" " set char [expr {($w1 << 10) | $w2 | 0x10000}]\n" //"puts [format {W1: %04x, W2: %04x, char: %x} $w1 $w2 $char]\n" " unset -nocomplain w1 w2\n" //"puts \"emitting [format %x $char]: ([format %c $char])\"\n" " return [format %c $char]\n" " }\n" "\n" " return\n" " }}\n" "\n" " if {[string range $encoding 0 1] eq {x }} {\n" " set encoding [string range $encoding 2 end]\n" // Hack to allow the test suite to force manual decoding " } elseif {$encoding in [encoding names]} {\n" " return [encoding convertfrom $encoding $bytes]\n" " }\n" //" puts \"Manual $encoding decode\"\n" " binary scan $bytes [expr {$encoding eq {utf-16le} ? {su*} : {Su*}}] chars\n" //" puts \"chars:\n\t[join [lmap e $chars {format %04x $e}] \\n\\t]\"\n" " set res {}\n" " foreach char $chars {\n" " append res [apply $process_utf16_word $char]\n" " }\n" " set res\n" " }}\n" "\n" " set decode_utf32 {{bytes encoding} {\n" //" puts \"Decoding using $encoding: [regexp -all -inline .. [binary encode hex $bytes]]\"\n" " if {[string range $encoding 0 1] eq {x }} {\n" " set encoding [string range $encoding 2 end]\n" // Hack to allow the test suite to force manual decoding " } elseif {$encoding in [encoding names]} {\n" " puts \"$encoding is in \\[encoding names\\], using native\"\n" " return [encoding convertfrom $encoding $bytes]\n" " }\n" //" puts \"Manual $encoding decode\"\n" " binary scan $bytes [expr {$encoding eq {utf-32le} ? {iu*} : {Iu*}}] chars\n" //" puts \"chars:\n\t[join [lmap e $chars {format %04x $e}] \\n\\t]\"\n" " set res {}\n" " foreach char $chars {\n" " append res [format %c $char]\n" " }\n" " set res\n" " }}\n" "\n" " if {$encoding eq {auto}} {\n" " set bom [binary encode hex [string range $bytes 0 3]]\n" " switch -glob -- $bom {\n" " 0000feff { set encoding utf-32be }\n" " fffe0000 { set encoding utf-32le }\n" " feff* { set encoding utf-16be }\n" " fffe* { set encoding utf-16le }\n" "\n" " efbbbf -\n" " default { # No BOM, or UTF-8 BOM\n" " set encoding utf-8\n" " }\n" " }\n" " }\n" "\n" " switch -- $encoding {\n" " utf-8 {\n" " encoding convertfrom utf-8 $bytes\n" " }\n" "\n" " {x utf-16le} -\n" " {x utf-16be} -\n" " utf-16le -\n" " utf-16be { apply $decode_utf16 $bytes $encoding }\n" "\n" " {x utf-32le} -\n" " {x utf-32be} -\n" " utf-32le -\n" " utf-32be { apply $decode_utf32 $bytes $encoding }\n" "\n" " default {\n" " error \"Unsupported encoding \\\"$encoding\\\"\"\n" " }\n" " }\n" "}\n" , -1)); //}}} Tcl_SetAssocData(interp, "rl_json", free_interp_cx, l); { Tcl_Namespace* ns = NULL; #if ENSEMBLE Tcl_Namespace* ns_cmd = NULL; Tcl_Command ens_cmd = NULL; #endif #define NS "::rl_json" ns = Tcl_CreateNamespace(interp, NS, NULL, NULL); TEST_OK(Tcl_Export(interp, ns, "*", 0)); #if ENSEMBLE #define ENS NS "::json::" ns_cmd = Tcl_CreateNamespace(interp, NS "::json", NULL, NULL); ens_cmd = Tcl_CreateEnsemble(interp, NS "::json", ns_cmd, 0); #if 1 { Tcl_Obj* subcommands = Tcl_NewListObj(0, NULL); Tcl_ListObjAppendElement(NULL, subcommands, Tcl_NewStringObj("parse", -1)); Tcl_ListObjAppendElement(NULL, subcommands, Tcl_NewStringObj("normalize", -1)); Tcl_ListObjAppendElement(NULL, subcommands, Tcl_NewStringObj("type", -1)); Tcl_ListObjAppendElement(NULL, subcommands, Tcl_NewStringObj("length", -1)); Tcl_ListObjAppendElement(NULL, subcommands, Tcl_NewStringObj("keys", -1)); Tcl_ListObjAppendElement(NULL, subcommands, Tcl_NewStringObj("exists", -1)); Tcl_ListObjAppendElement(NULL, subcommands, Tcl_NewStringObj("get", -1)); Tcl_ListObjAppendElement(NULL, subcommands, Tcl_NewStringObj("extract", -1)); Tcl_ListObjAppendElement(NULL, subcommands, Tcl_NewStringObj("set", -1)); Tcl_ListObjAppendElement(NULL, subcommands, Tcl_NewStringObj("unset", -1)); Tcl_ListObjAppendElement(NULL, subcommands, Tcl_NewStringObj("fmt", -1)); Tcl_ListObjAppendElement(NULL, subcommands, Tcl_NewStringObj("new", -1)); Tcl_ListObjAppendElement(NULL, subcommands, Tcl_NewStringObj("string", -1)); Tcl_ListObjAppendElement(NULL, subcommands, Tcl_NewStringObj("number", -1)); Tcl_ListObjAppendElement(NULL, subcommands, Tcl_NewStringObj("boolean", -1)); Tcl_ListObjAppendElement(NULL, subcommands, Tcl_NewStringObj("object", -1)); Tcl_ListObjAppendElement(NULL, subcommands, Tcl_NewStringObj("array", -1)); Tcl_ListObjAppendElement(NULL, subcommands, Tcl_NewStringObj("decode", -1)); Tcl_ListObjAppendElement(NULL, subcommands, Tcl_NewStringObj("isnull", -1)); Tcl_ListObjAppendElement(NULL, subcommands, Tcl_NewStringObj("template", -1)); Tcl_ListObjAppendElement(NULL, subcommands, Tcl_NewStringObj("_template", -1)); Tcl_ListObjAppendElement(NULL, subcommands, Tcl_NewStringObj("foreach", -1)); Tcl_ListObjAppendElement(NULL, subcommands, Tcl_NewStringObj("lmap", -1)); Tcl_ListObjAppendElement(NULL, subcommands, Tcl_NewStringObj("amap", -1)); Tcl_ListObjAppendElement(NULL, subcommands, Tcl_NewStringObj("omap", -1)); Tcl_ListObjAppendElement(NULL, subcommands, Tcl_NewStringObj("free_cache", -1)); Tcl_ListObjAppendElement(NULL, subcommands, Tcl_NewStringObj("nop", -1)); Tcl_ListObjAppendElement(NULL, subcommands, Tcl_NewStringObj("pretty", -1)); Tcl_ListObjAppendElement(NULL, subcommands, Tcl_NewStringObj("valid", -1)); Tcl_ListObjAppendElement(NULL, subcommands, Tcl_NewStringObj("debug", -1)); Tcl_ListObjAppendElement(NULL, subcommands, Tcl_NewStringObj("template_actions", -1)); Tcl_SetEnsembleSubcommandList(interp, ens_cmd, subcommands); } #endif TEST_OK(Tcl_Export(interp, ns_cmd, "*", 0)); Tcl_CreateObjCommand(interp, ENS "parse", jsonParse, l, NULL); // Deprecated Tcl_CreateObjCommand(interp, ENS "normalize", jsonNormalize, l, NULL); Tcl_CreateObjCommand(interp, ENS "type", jsonType, l, NULL); Tcl_CreateObjCommand(interp, ENS "length", jsonLength, l, NULL); Tcl_CreateObjCommand(interp, ENS "keys", jsonKeys, l, NULL); Tcl_CreateObjCommand(interp, ENS "exists", jsonExists, l, NULL); Tcl_CreateObjCommand(interp, ENS "get", jsonGet, l, NULL); Tcl_CreateObjCommand(interp, ENS "extract", jsonExtract, l, NULL); Tcl_CreateObjCommand(interp, ENS "set", jsonSet, l, NULL); Tcl_CreateObjCommand(interp, ENS "unset", jsonUnset, l, NULL); Tcl_CreateObjCommand(interp, ENS "fmt", jsonNew, l, NULL); // Deprecated Tcl_CreateObjCommand(interp, ENS "new", jsonNew, l, NULL); // Deprecated Tcl_CreateObjCommand(interp, ENS "string", jsonString, l, NULL); Tcl_CreateObjCommand(interp, ENS "number", jsonNumber, l, NULL); 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 "decode", jsonDecode, l, NULL); Tcl_CreateObjCommand(interp, ENS "isnull", jsonIsNull, l, NULL); Tcl_CreateObjCommand(interp, ENS "template", jsonTemplate, l, NULL); Tcl_CreateObjCommand(interp, ENS "template_string", jsonTemplateString, l, NULL); Tcl_NRCreateCommand(interp, ENS "foreach", jsonForeach, jsonNRForeach, l, NULL); Tcl_NRCreateCommand(interp, ENS "lmap", jsonLmap, jsonNRLmap, l, NULL); Tcl_NRCreateCommand(interp, ENS "amap", jsonAmap, jsonNRAmap, l, NULL); Tcl_NRCreateCommand(interp, ENS "omap", jsonOmap, jsonNROmap, l, NULL); Tcl_CreateObjCommand(interp, ENS "free_cache", jsonFreeCache, l, NULL); Tcl_CreateObjCommand(interp, ENS "nop", jsonNop, l, NULL); Tcl_CreateObjCommand(interp, ENS "pretty", jsonPretty, l, NULL); Tcl_CreateObjCommand(interp, ENS "valid", jsonValid, l, NULL); Tcl_CreateObjCommand(interp, ENS "debug", jsonDebug, l, NULL); Tcl_CreateObjCommand(interp, ENS "template_actions", jsonTemplateActions, l, NULL); //Tcl_CreateObjCommand(interp, ENS "merge", jsonMerge, l, NULL); #else Tcl_NRCreateCommand(interp, "::rl_json::json", jsonObj, jsonNRObj, l, NULL); #endif Tcl_CreateObjCommand(interp, NS "::checkmem", checkmem, l, NULL); } TEST_OK(Tcl_PkgProvide(interp, PACKAGE_NAME, PACKAGE_VERSION)); return TCL_OK; } //}}} DLLEXPORT int Rl_json_SafeInit(Tcl_Interp* interp) //{{{ { // No unsafe features return Rl_json_Init(interp); } //}}} DLLEXPORT int Rl_json_Unload(Tcl_Interp* interp, int flags) //{{{ { Tcl_Namespace* ns; switch (flags) { case TCL_UNLOAD_DETACH_FROM_INTERPRETER: //fprintf(stderr, "rl_json detach from interpreter\n"); Tcl_DeleteAssocData(interp, "rl_json"); ns = Tcl_FindNamespace(interp, "::rl_json", NULL, TCL_GLOBAL_ONLY); if (ns) { Tcl_DeleteNamespace(ns); ns = NULL; } break; case TCL_UNLOAD_DETACH_FROM_PROCESS: //fprintf(stderr, "rl_json detach from process\n"); Tcl_DeleteAssocData(interp, "rl_json"); ns = Tcl_FindNamespace(interp, "::rl_json", NULL, TCL_GLOBAL_ONLY); if (ns) { Tcl_DeleteNamespace(ns); ns = NULL; } break; default: THROW_ERROR("Unhandled flags"); } return TCL_OK; } //}}} DLLEXPORT int Rl_json_SafeUnload(Tcl_Interp* interp, int flags) //{{{ { // No unsafe features return Rl_json_Unload(interp, flags); } //}}} #ifdef __cplusplus } #endif /* __cplusplus */ /* Local Variables: */ /* tab-width: 4 */ /* c-basic-offset: 4 */ /* End: */ // vim: foldmethod=marker foldmarker={{{,}}} ts=4 shiftwidth=4 |
Changes to jni/rl_json/generic/rl_json.decls.
1 2 3 | library rl_json interface rl_json declare 0 generic { | > | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 | library rl_json interface rl_json declare 0 generic { Tcl_Obj* JSON_NewJSONObj(Tcl_Interp* interp, Tcl_Obj* from) } declare 1 generic { int JSON_NewJStringObj(Tcl_Interp* interp, Tcl_Obj* string, Tcl_Obj** new) } declare 2 generic { int JSON_NewJNumberObj(Tcl_Interp* interp, Tcl_Obj* number, Tcl_Obj** new) } declare 3 generic { int JSON_NewJBooleanObj(Tcl_Interp* interp, Tcl_Obj* boolean, Tcl_Obj** new) } declare 4 generic { int JSON_NewJNullObj(Tcl_Interp* interp, Tcl_Obj** new) } declare 5 generic { int JSON_NewJObjectObj(Tcl_Interp* interp, Tcl_Obj** new) } declare 6 generic { int JSON_NewJArrayObj(Tcl_Interp*, int objc, Tcl_Obj* objv[], Tcl_Obj** new) } # type is one of the DYN types, key is the variable name the template is replaced with declare 7 generic { int JSON_NewTemplateObj(Tcl_Interp* interp, enum json_types type, Tcl_Obj* key, Tcl_Obj** new) } declare 8 generic { int JSON_ForceJSON(Tcl_Interp* interp, Tcl_Obj* obj) } declare 9 generic { enum json_types JSON_GetJSONType(Tcl_Obj* obj) } declare 10 generic { int JSON_GetObjFromJStringObj(Tcl_Interp* interp, Tcl_Obj* jstringObj, Tcl_Obj** stringObj) } # Return a native Tcl number type object declare 11 generic { int JSON_GetObjFromJNumberObj(Tcl_Interp* interp, Tcl_Obj* jnumberObj, Tcl_Obj** numberObj) } # Return a native Tcl number type object declare 12 generic { int JSON_GetObjFromJBooleanObj(Tcl_Interp* interp, Tcl_Obj* jbooleanObj, Tcl_Obj** booleanObj) } declare 13 generic { int JSON_JArrayObjAppendElement(Tcl_Interp* interp, Tcl_Obj* arrayObj, Tcl_Obj* elem) } declare 14 generic { int JSON_JArrayObjAppendList(Tcl_Interp* interp, Tcl_Obj* arrayObj, Tcl_Obj* elems /* a JArrayObj or ListObj */ ) } declare 15 generic { int JSON_SetJArrayObj(Tcl_Interp* interp, Tcl_Obj* obj, int objc, Tcl_Obj* objv[]) } declare 16 generic { int JSON_JArrayObjGetElements(Tcl_Interp* interp, Tcl_Obj* arrayObj, int* objc, Tcl_Obj*** objv) } declare 17 generic { int JSON_JArrayObjIndex(Tcl_Interp* interp, Tcl_Obj* arrayObj, int index, Tcl_Obj** elem) } declare 18 generic { int JSON_JArrayObjReplace(Tcl_Interp* interp, Tcl_Obj* arrayObj, int first, int count, int objc, Tcl_Obj* objv[]) } # TODO: JObject interface, similar to DictObj # declare 19 generic { int JSON_Get(Tcl_Interp* interp, Tcl_Obj* obj, Tcl_Obj* path, Tcl_Obj** res) } declare 20 generic { int JSON_Extract(Tcl_Interp* interp, Tcl_Obj* obj, Tcl_Obj* path, Tcl_Obj** res) } declare 21 generic { int JSON_Exists(Tcl_Interp* interp, Tcl_Obj* obj, Tcl_Obj* path, int* exists) } declare 22 generic { int JSON_Set(Tcl_Interp* interp, Tcl_Obj* obj, Tcl_Obj* path, Tcl_Obj* replacement) } declare 23 generic { int JSON_Unset(Tcl_Interp* interp, Tcl_Obj* obj, Tcl_Obj* path) } 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) } declare 26 generic { int JSON_Template(Tcl_Interp* interp, Tcl_Obj* template, Tcl_Obj* dict, Tcl_Obj** res) } declare 27 generic { int JSON_IsNULL(Tcl_Interp* interp, Tcl_Obj* obj, Tcl_Obj* path, int* isnull) } declare 28 generic { int JSON_Type(Tcl_Interp* interp, Tcl_Obj* obj, Tcl_Obj* path, enum json_types* type) } declare 29 generic { int JSON_Length(Tcl_Interp* interp, Tcl_Obj* obj, Tcl_Obj* path, int* length) } declare 30 generic { int JSON_Keys(Tcl_Interp* interp, Tcl_Obj* obj, Tcl_Obj* path, Tcl_Obj** keyslist) } declare 31 generic { int JSON_Decode(Tcl_Interp* interp, Tcl_Obj* bytes, Tcl_Obj* encoding, Tcl_Obj** decodedstring) } declare 32 generic { int JSON_Foreach(Tcl_Interp* interp, Tcl_Obj* iterators, int* body, enum collecting_mode collect, Tcl_Obj** res, ClientData cdata) } declare 33 generic { int JSON_Valid(Tcl_Interp* interp, Tcl_Obj* json, int* valid, enum extensions extensions, struct parse_error* details) } |
Changes to jni/rl_json/generic/rl_json.h.
1 2 3 | #ifndef _JSON_MAIN_H #define _JSON_MAIN_H | < < < < < < < < < < < | | < < < | < | | < > > > > > > > < > > > | < > > > < < < < < > < < < < < < < < < < < < < < < < < | < < < < > > > | < | < | | < < < > | < | > | > > | | < < < < < | < < < < | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | #ifndef _JSON_MAIN_H #define _JSON_MAIN_H #include <tcl.h> #ifdef BUILD_rl_json #undef TCL_STORAGE_CLASS #define TCL_STORAGE_CLASS DLLEXPORT #endif /* BUILD_rl_json */ enum json_types { // Order must be preserved JSON_UNDEF = 0, JSON_OBJECT, JSON_ARRAY, JSON_STRING, JSON_NUMBER, JSON_BOOL, JSON_NULL, /* Dynamic types - placeholders for dynamic values in templates */ JSON_DYN_STRING, // ~S: JSON_DYN_NUMBER, // ~N: JSON_DYN_BOOL, // ~B: JSON_DYN_JSON, // ~J: JSON_DYN_TEMPLATE, // ~T: JSON_DYN_LITERAL, // ~L: literal escape - used to quote literal values that start with the above sequences JSON_TYPE_MAX // Not an actual type - records the number of types }; enum collecting_mode { COLLECT_NONE, COLLECT_LIST, COLLECT_ARRAY, COLLECT_OBJECT }; enum extensions { EXT_NONE = 0, EXT_COMMENTS = (1 << 0) }; struct parse_error { const char* errmsg; const char* doc; size_t char_ofs; // Offset in chars, not bytes }; // Stubs exported API #include "rl_jsonDecls.h" EXTERN CONST char* Rl_jsonInitStubs _ANSI_ARGS_((Tcl_Interp* interp, CONST char* version, int exact)); #ifndef USE_TCL_STUBS # define Rl_jsonInitStubs(interp, version, exact) Tcl_PkgRequire(interp, "rl_json", version, exact) #endif EXTERN int Rl_jsonInit _ANSI_ARGS_((Tcl_Interp* interp)); #endif |
Changes to jni/rl_json/generic/rl_jsonDecls.h.
1 2 3 4 5 6 7 8 9 10 11 12 | /* !BEGIN!: Do not edit below this line. */ #ifdef __cplusplus extern "C" { #endif /* * Exported function declarations: */ /* 0 */ | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > | > > > > > > > > > > > > > > > > > | > > > | > > > > > > > | | > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > | > > > > | > > > > > > | > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 | /* !BEGIN!: Do not edit below this line. */ #ifdef __cplusplus extern "C" { #endif /* * Exported function declarations: */ /* 0 */ EXTERN Tcl_Obj* JSON_NewJSONObj(Tcl_Interp*interp, Tcl_Obj*from); /* 1 */ EXTERN int JSON_NewJStringObj(Tcl_Interp*interp, Tcl_Obj*string, Tcl_Obj**new); /* 2 */ EXTERN int JSON_NewJNumberObj(Tcl_Interp*interp, Tcl_Obj*number, Tcl_Obj**new); /* 3 */ EXTERN int JSON_NewJBooleanObj(Tcl_Interp*interp, Tcl_Obj*boolean, Tcl_Obj**new); /* 4 */ EXTERN int JSON_NewJNullObj(Tcl_Interp*interp, Tcl_Obj**new); /* 5 */ EXTERN int JSON_NewJObjectObj(Tcl_Interp*interp, Tcl_Obj**new); /* Slot 6 is reserved */ /* 7 */ EXTERN int JSON_NewTemplateObj(Tcl_Interp*interp, enum json_types type, Tcl_Obj*key, Tcl_Obj**new); /* 8 */ EXTERN int JSON_ForceJSON(Tcl_Interp*interp, Tcl_Obj*obj); /* 9 */ EXTERN enum json_types JSON_GetJSONType(Tcl_Obj*obj); /* 10 */ EXTERN int JSON_GetObjFromJStringObj(Tcl_Interp*interp, Tcl_Obj*jstringObj, Tcl_Obj**stringObj); /* 11 */ EXTERN int JSON_GetObjFromJNumberObj(Tcl_Interp*interp, Tcl_Obj*jnumberObj, Tcl_Obj**numberObj); /* 12 */ EXTERN int JSON_GetObjFromJBooleanObj(Tcl_Interp*interp, Tcl_Obj*jbooleanObj, Tcl_Obj**booleanObj); /* 13 */ EXTERN int JSON_JArrayObjAppendElement(Tcl_Interp*interp, Tcl_Obj*arrayObj, Tcl_Obj*elem); /* 14 */ EXTERN int JSON_JArrayObjAppendList(Tcl_Interp*interp, Tcl_Obj*arrayObj, Tcl_Obj* elems /* a JArrayObj or ListObj */); /* 15 */ EXTERN int JSON_SetJArrayObj(Tcl_Interp*interp, Tcl_Obj*obj, int objc, Tcl_Obj*objv[]); /* 16 */ EXTERN int JSON_JArrayObjGetElements(Tcl_Interp*interp, Tcl_Obj*arrayObj, int*objc, Tcl_Obj***objv); /* 17 */ EXTERN int JSON_JArrayObjIndex(Tcl_Interp*interp, Tcl_Obj*arrayObj, int index, Tcl_Obj**elem); /* 18 */ EXTERN int JSON_JArrayObjReplace(Tcl_Interp*interp, Tcl_Obj*arrayObj, int first, int count, int objc, Tcl_Obj*objv[]); /* 19 */ EXTERN int JSON_Get(Tcl_Interp*interp, Tcl_Obj*obj, Tcl_Obj*path, Tcl_Obj**res); /* 20 */ EXTERN int JSON_Extract(Tcl_Interp*interp, Tcl_Obj*obj, Tcl_Obj*path, Tcl_Obj**res); /* 21 */ EXTERN int JSON_Exists(Tcl_Interp*interp, Tcl_Obj*obj, Tcl_Obj*path, int*exists); /* 22 */ EXTERN int JSON_Set(Tcl_Interp*interp, Tcl_Obj*obj, Tcl_Obj*path, Tcl_Obj*replacement); /* 23 */ EXTERN int JSON_Unset(Tcl_Interp*interp, Tcl_Obj*obj, Tcl_Obj*path); /* 24 */ 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); /* 26 */ EXTERN int JSON_Template(Tcl_Interp*interp, Tcl_Obj*template, Tcl_Obj*dict, Tcl_Obj**res); /* 27 */ EXTERN int JSON_IsNULL(Tcl_Interp*interp, Tcl_Obj*obj, Tcl_Obj*path, int*isnull); /* 28 */ EXTERN int JSON_Type(Tcl_Interp*interp, Tcl_Obj*obj, Tcl_Obj*path, enum json_types*type); /* 29 */ EXTERN int JSON_Length(Tcl_Interp*interp, Tcl_Obj*obj, Tcl_Obj*path, int*length); /* 30 */ EXTERN int JSON_Keys(Tcl_Interp*interp, Tcl_Obj*obj, Tcl_Obj*path, Tcl_Obj**keyslist); /* 31 */ EXTERN int JSON_Decode(Tcl_Interp*interp, Tcl_Obj*bytes, Tcl_Obj*encoding, Tcl_Obj**decodedstring); /* 32 */ EXTERN int JSON_Foreach(Tcl_Interp*interp, Tcl_Obj*iterators, int*body, enum collecting_mode collect, Tcl_Obj**res, ClientData cdata); /* 33 */ EXTERN int JSON_Valid(Tcl_Interp*interp, Tcl_Obj*json, int*valid, enum extensions extensions, struct parse_error*details); typedef struct Rl_jsonStubs { int magic; void *hooks; Tcl_Obj* (*jSON_NewJSONObj) (Tcl_Interp*interp, Tcl_Obj*from); /* 0 */ int (*jSON_NewJStringObj) (Tcl_Interp*interp, Tcl_Obj*string, Tcl_Obj**new); /* 1 */ int (*jSON_NewJNumberObj) (Tcl_Interp*interp, Tcl_Obj*number, Tcl_Obj**new); /* 2 */ int (*jSON_NewJBooleanObj) (Tcl_Interp*interp, Tcl_Obj*boolean, Tcl_Obj**new); /* 3 */ int (*jSON_NewJNullObj) (Tcl_Interp*interp, Tcl_Obj**new); /* 4 */ int (*jSON_NewJObjectObj) (Tcl_Interp*interp, Tcl_Obj**new); /* 5 */ void (*reserved6)(void); int (*jSON_NewTemplateObj) (Tcl_Interp*interp, enum json_types type, Tcl_Obj*key, Tcl_Obj**new); /* 7 */ int (*jSON_ForceJSON) (Tcl_Interp*interp, Tcl_Obj*obj); /* 8 */ enum json_types (*jSON_GetJSONType) (Tcl_Obj*obj); /* 9 */ int (*jSON_GetObjFromJStringObj) (Tcl_Interp*interp, Tcl_Obj*jstringObj, Tcl_Obj**stringObj); /* 10 */ int (*jSON_GetObjFromJNumberObj) (Tcl_Interp*interp, Tcl_Obj*jnumberObj, Tcl_Obj**numberObj); /* 11 */ int (*jSON_GetObjFromJBooleanObj) (Tcl_Interp*interp, Tcl_Obj*jbooleanObj, Tcl_Obj**booleanObj); /* 12 */ int (*jSON_JArrayObjAppendElement) (Tcl_Interp*interp, Tcl_Obj*arrayObj, Tcl_Obj*elem); /* 13 */ int (*jSON_JArrayObjAppendList) (Tcl_Interp*interp, Tcl_Obj*arrayObj, Tcl_Obj* elems /* a JArrayObj or ListObj */); /* 14 */ int (*jSON_SetJArrayObj) (Tcl_Interp*interp, Tcl_Obj*obj, int objc, Tcl_Obj*objv[]); /* 15 */ int (*jSON_JArrayObjGetElements) (Tcl_Interp*interp, Tcl_Obj*arrayObj, int*objc, Tcl_Obj***objv); /* 16 */ int (*jSON_JArrayObjIndex) (Tcl_Interp*interp, Tcl_Obj*arrayObj, int index, Tcl_Obj**elem); /* 17 */ int (*jSON_JArrayObjReplace) (Tcl_Interp*interp, Tcl_Obj*arrayObj, int first, int count, int objc, Tcl_Obj*objv[]); /* 18 */ int (*jSON_Get) (Tcl_Interp*interp, Tcl_Obj*obj, Tcl_Obj*path, Tcl_Obj**res); /* 19 */ int (*jSON_Extract) (Tcl_Interp*interp, Tcl_Obj*obj, Tcl_Obj*path, Tcl_Obj**res); /* 20 */ int (*jSON_Exists) (Tcl_Interp*interp, Tcl_Obj*obj, Tcl_Obj*path, int*exists); /* 21 */ int (*jSON_Set) (Tcl_Interp*interp, Tcl_Obj*obj, Tcl_Obj*path, Tcl_Obj*replacement); /* 22 */ int (*jSON_Unset) (Tcl_Interp*interp, Tcl_Obj*obj, Tcl_Obj*path); /* 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_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, int*isnull); /* 27 */ int (*jSON_Type) (Tcl_Interp*interp, Tcl_Obj*obj, Tcl_Obj*path, enum json_types*type); /* 28 */ int (*jSON_Length) (Tcl_Interp*interp, Tcl_Obj*obj, Tcl_Obj*path, int*length); /* 29 */ int (*jSON_Keys) (Tcl_Interp*interp, Tcl_Obj*obj, Tcl_Obj*path, Tcl_Obj**keyslist); /* 30 */ int (*jSON_Decode) (Tcl_Interp*interp, Tcl_Obj*bytes, Tcl_Obj*encoding, Tcl_Obj**decodedstring); /* 31 */ int (*jSON_Foreach) (Tcl_Interp*interp, Tcl_Obj*iterators, int*body, enum collecting_mode collect, Tcl_Obj**res, ClientData cdata); /* 32 */ int (*jSON_Valid) (Tcl_Interp*interp, Tcl_Obj*json, int*valid, enum extensions extensions, struct parse_error*details); /* 33 */ } Rl_jsonStubs; extern const Rl_jsonStubs *rl_jsonStubsPtr; #ifdef __cplusplus } #endif #if defined(USE_RL_JSON_STUBS) /* * Inline function declarations: */ #define JSON_NewJSONObj \ (rl_jsonStubsPtr->jSON_NewJSONObj) /* 0 */ #define JSON_NewJStringObj \ (rl_jsonStubsPtr->jSON_NewJStringObj) /* 1 */ #define JSON_NewJNumberObj \ (rl_jsonStubsPtr->jSON_NewJNumberObj) /* 2 */ #define JSON_NewJBooleanObj \ (rl_jsonStubsPtr->jSON_NewJBooleanObj) /* 3 */ #define JSON_NewJNullObj \ (rl_jsonStubsPtr->jSON_NewJNullObj) /* 4 */ #define JSON_NewJObjectObj \ (rl_jsonStubsPtr->jSON_NewJObjectObj) /* 5 */ /* Slot 6 is reserved */ #define JSON_NewTemplateObj \ (rl_jsonStubsPtr->jSON_NewTemplateObj) /* 7 */ #define JSON_ForceJSON \ (rl_jsonStubsPtr->jSON_ForceJSON) /* 8 */ #define JSON_GetJSONType \ (rl_jsonStubsPtr->jSON_GetJSONType) /* 9 */ #define JSON_GetObjFromJStringObj \ (rl_jsonStubsPtr->jSON_GetObjFromJStringObj) /* 10 */ #define JSON_GetObjFromJNumberObj \ (rl_jsonStubsPtr->jSON_GetObjFromJNumberObj) /* 11 */ #define JSON_GetObjFromJBooleanObj \ (rl_jsonStubsPtr->jSON_GetObjFromJBooleanObj) /* 12 */ #define JSON_JArrayObjAppendElement \ (rl_jsonStubsPtr->jSON_JArrayObjAppendElement) /* 13 */ #define JSON_JArrayObjAppendList \ (rl_jsonStubsPtr->jSON_JArrayObjAppendList) /* 14 */ #define JSON_SetJArrayObj \ (rl_jsonStubsPtr->jSON_SetJArrayObj) /* 15 */ #define JSON_JArrayObjGetElements \ (rl_jsonStubsPtr->jSON_JArrayObjGetElements) /* 16 */ #define JSON_JArrayObjIndex \ (rl_jsonStubsPtr->jSON_JArrayObjIndex) /* 17 */ #define JSON_JArrayObjReplace \ (rl_jsonStubsPtr->jSON_JArrayObjReplace) /* 18 */ #define JSON_Get \ (rl_jsonStubsPtr->jSON_Get) /* 19 */ #define JSON_Extract \ (rl_jsonStubsPtr->jSON_Extract) /* 20 */ #define JSON_Exists \ (rl_jsonStubsPtr->jSON_Exists) /* 21 */ #define JSON_Set \ (rl_jsonStubsPtr->jSON_Set) /* 22 */ #define JSON_Unset \ (rl_jsonStubsPtr->jSON_Unset) /* 23 */ #define JSON_Normalize \ (rl_jsonStubsPtr->jSON_Normalize) /* 24 */ #define JSON_Pretty \ (rl_jsonStubsPtr->jSON_Pretty) /* 25 */ #define JSON_Template \ (rl_jsonStubsPtr->jSON_Template) /* 26 */ #define JSON_IsNULL \ (rl_jsonStubsPtr->jSON_IsNULL) /* 27 */ #define JSON_Type \ (rl_jsonStubsPtr->jSON_Type) /* 28 */ #define JSON_Length \ (rl_jsonStubsPtr->jSON_Length) /* 29 */ #define JSON_Keys \ (rl_jsonStubsPtr->jSON_Keys) /* 30 */ #define JSON_Decode \ (rl_jsonStubsPtr->jSON_Decode) /* 31 */ #define JSON_Foreach \ (rl_jsonStubsPtr->jSON_Foreach) /* 32 */ #define JSON_Valid \ (rl_jsonStubsPtr->jSON_Valid) /* 33 */ #endif /* defined(USE_RL_JSON_STUBS) */ /* !END!: Do not edit above this line. */ |
Added jni/rl_json/generic/rl_jsonInt.h.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 | #ifndef _RL_JSONINT #define _RL_JSONINT #include "rl_json.h" #include "tclstuff.h" #include <string.h> #include <errno.h> #include <stdlib.h> #include <math.h> #include <stddef.h> #include <stdint.h> #include <unistd.h> #include <tclTomMath.h> #include "tip445.h" #define CX_STACK_SIZE 6 #ifdef __builtin_expect # define likely(exp) __builtin_expect(!!(exp), 1) # define unlikely(exp) __builtin_expect(!!(exp), 0) #else # define likely(exp) (exp) # define unlikely(exp) (exp) #endif enum parse_mode { PARSE, VALIDATE }; struct parse_context { struct parse_context* last; // Only valid for the first entry struct parse_context* prev; Tcl_Obj* val; Tcl_Obj* hold_key; size_t char_ofs; enum json_types container; int closed; Tcl_ObjType* objtype; struct interp_cx* l; enum parse_mode mode; }; struct foreach_iterator { int data_c; Tcl_Obj** data_v; int data_i; Tcl_Obj* varlist; int var_c; Tcl_Obj** var_v; int is_array; // Dict search related state - when iterating over JSON objects Tcl_DictSearch search; Tcl_Obj* k; Tcl_Obj* v; int done; }; struct foreach_state { unsigned int loop_num; unsigned int max_loops; unsigned int iterators; struct foreach_iterator* it; Tcl_Obj* script; Tcl_Obj* res; int collecting; }; enum serialize_modes { SERIALIZE_NORMAL, // We're updating the string rep of a json value or template SERIALIZE_TEMPLATE // We're interpolating values into a template }; struct serialize_context { Tcl_DString* ds; enum serialize_modes serialize_mode; Tcl_Obj* fromdict; // NULL if no dict supplied struct interp_cx* l; int allow_null; }; struct template_cx { Tcl_Interp* interp; struct interp_cx* l; Tcl_Obj* map; Tcl_Obj* actions; int slots_used; }; struct cx_stack { Tcl_Obj* target; Tcl_Obj* elem; }; enum modifiers { MODIFIER_NONE, MODIFIER_LENGTH, // for arrays and strings: return the length as an int MODIFIER_SIZE, // for objects: return the number of keys as an int MODIFIER_TYPE, // for all types: return the string name as Tcl_Obj MODIFIER_KEYS // for objects: return the keys defined as Tcl_Obj }; enum action_opcode { NOP, ALLOCATE, FETCH_VALUE, DECLARE_LITERAL, STORE_STRING, STORE_NUMBER, STORE_BOOLEAN, STORE_JSON, STORE_TEMPLATE, PUSH_TARGET, POP_TARGET, REPLACE_VAL, REPLACE_ARR, REPLACE_ATOM, REPLACE_KEY, TEMPLATE_ACTIONS_END }; #if DEDUP struct kc_entry { Tcl_Obj *val; unsigned int hits; }; /* Find the best BSF (bit-scan-forward) implementation available: * In order of preference: * - __builtin_ffsll - provided by gcc >= 3.4 and clang >= 5.x * - ffsll - glibc extension, freebsd libc >= 7.1 * - ffs - POSIX, but only on int * TODO: possibly provide _BitScanForward implementation for Visual Studio >= 2005? */ #if defined(HAVE___BUILTIN_FFSLL) || defined(HAVE_FFSLL) # define FFS_TMP_STORAGE /* nothing to declare */ # if defined(HAVE___BUILTIN_FFSLL) # define FFS __builtin_ffsll # else # define FFS ffsll # endif # define FREEMAP_TYPE long long #elif defined(_MSC_VER) && defined(_WIN64) && _MSC_VER >= 1400 # define FFS_TMP_STORAGE unsigned long ix; /* _BitScanForward64 numbers bits starting with 0, ffsll starts with 1 */ # define FFS(x) (_BitScanForward64(&ix, x) ? ix+1 : 0) # define FREEMAP_TYPE long long #else # define FFS_TMP_STORAGE /* nothing to declare */ # define FFS ffs # define FREEMAP_TYPE int #endif #define KC_ENTRIES 384 // Must be an integer multiple of 8*sizeof(FREEMAP_TYPE) #endif struct interp_cx { Tcl_Interp* interp; Tcl_Obj* tcl_true; Tcl_Obj* tcl_false; Tcl_Obj* tcl_empty; Tcl_Obj* tcl_one; Tcl_Obj* tcl_zero; Tcl_Obj* json_true; Tcl_Obj* json_false; Tcl_Obj* json_null; Tcl_Obj* json_empty_string; Tcl_Obj* tcl_empty_dict; Tcl_Obj* tcl_empty_list; Tcl_Obj* action[TEMPLATE_ACTIONS_END]; Tcl_Obj* force_num_cmd[3]; Tcl_Obj* type_int[JSON_TYPE_MAX]; // Tcl_Obj for JSON_STRING, JSON_ARRAY, etc Tcl_Obj* type[JSON_TYPE_MAX]; // Holds the Tcl_Obj values returned for [json type ...] #if DEDUP Tcl_HashTable kc; int kc_count; FREEMAP_TYPE freemap[(KC_ENTRIES / (8*sizeof(FREEMAP_TYPE)))+1]; // long long for ffsll struct kc_entry kc_entries[KC_ENTRIES]; #endif const Tcl_ObjType* typeDict; // Evil hack to identify objects of type dict, used to choose whether to iterate over a list of pairs as a dict or a list, for efficiency const Tcl_ObjType* typeInt; // Evil hack to snoop on the type of a number, so that we don't have to add 0 to a candidate to know if it's a valid number const Tcl_ObjType* typeDouble; const Tcl_ObjType* typeBignum; Tcl_Obj* apply; Tcl_Obj* decode_bytes; }; void append_to_cx(struct parse_context *cx, Tcl_Obj *val); int serialize(Tcl_Interp* interp, struct serialize_context* scx, Tcl_Obj* obj); int init_types(Tcl_Interp* interp); Tcl_Obj* new_stringobj_dedup(struct interp_cx *l, const char *bytes, int length); int lookup_type(Tcl_Interp* interp, Tcl_Obj* typeobj, int* type); int is_template(const char* s, int len); extern Tcl_ObjType* g_objtype_for_type[]; extern const char* type_names_int[]; #ifdef TCL_MEM_DEBUG # undef JSON_NewJvalObj Tcl_Obj* JSON_DbNewJvalObj(enum json_types type, Tcl_Obj* val, const char* file, int line); # define JSON_NewJvalObj(type, val) JSON_DbNewJvalObj(type, val, __FILE__ " (JVAL)", __LINE__) #endif // Taken from tclInt.h: #if !defined(INT2PTR) && !defined(PTR2INT) # if defined(HAVE_INTPTR_T) || defined(intptr_t) # define INT2PTR(p) ((void *)(intptr_t)(p)) # define PTR2INT(p) ((int)(intptr_t)(p)) # else # define INT2PTR(p) ((void *)(p)) # define PTR2INT(p) ((int)(p)) # endif #endif #if !defined(UINT2PTR) && !defined(PTR2UINT) # if defined(HAVE_UINTPTR_T) || defined(uintptr_t) # define UINT2PTR(p) ((void *)(uintptr_t)(p)) # define PTR2UINT(p) ((unsigned int)(uintptr_t)(p)) # else # define UINT2PTR(p) ((void *)(p)) # define PTR2UINT(p) ((unsigned int)(p)) # endif #endif Tcl_Obj* JSON_NewJvalObj(enum json_types type, Tcl_Obj* val); int JSON_SetIntRep(Tcl_Obj* target, enum json_types type, Tcl_Obj* replacement); int JSON_GetIntrepFromObj(Tcl_Interp* interp, Tcl_Obj* obj, enum json_types* type, Tcl_ObjIntRep** ir); int JSON_GetJvalFromObj(Tcl_Interp *interp, Tcl_Obj *obj, enum json_types *type, Tcl_Obj **val); int JSON_IsJSON(Tcl_Obj* obj, enum json_types* type, Tcl_ObjIntRep** ir); int type_is_dynamic(const enum json_types type); int force_json_number(Tcl_Interp* interp, struct interp_cx* l, Tcl_Obj* obj, Tcl_Obj** forced); Tcl_Obj* as_json(Tcl_Interp* interp, Tcl_Obj* from); const char* get_dyn_prefix(enum json_types type); const char* get_type_name(enum json_types type); Tcl_Obj* get_unshared_val(Tcl_ObjIntRep* ir); int apply_template_actions(Tcl_Interp* interp, Tcl_Obj* template, Tcl_Obj* actions, Tcl_Obj* dict, Tcl_Obj** res); 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); int json_pretty(Tcl_Interp* interp, Tcl_Obj* json, Tcl_Obj* indent, Tcl_Obj* pad, Tcl_DString* ds); #define TEMPLATE_TYPE(s, len, out) \ if (s[0] == '~' && (len) >= 3 && s[2] == ':') { \ switch (s[1]) { \ case 'S': out = JSON_DYN_STRING; break; \ case 'N': out = JSON_DYN_NUMBER; break; \ case 'B': out = JSON_DYN_BOOL; break; \ case 'J': out = JSON_DYN_JSON; break; \ case 'T': out = JSON_DYN_TEMPLATE; break; \ case 'L': out = JSON_DYN_LITERAL; break; \ default: out = JSON_STRING; s -= 3; break; \ } \ s += 3; \ } else {out = JSON_STRING;} #include "dedup.h" #endif |
Changes to jni/rl_json/generic/rl_jsonStubInit.c.
1 2 3 4 5 6 7 | #include "rl_json.h" /* !BEGIN!: Do not edit below this line. */ const Rl_jsonStubs rl_jsonStubs = { TCL_STUB_MAGIC, 0, | | | > > > > > > > > > > > > > > > > > > > > | > > > | > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | #include "rl_json.h" /* !BEGIN!: Do not edit below this line. */ const Rl_jsonStubs rl_jsonStubs = { TCL_STUB_MAGIC, 0, JSON_NewJSONObj, /* 0 */ JSON_NewJStringObj, /* 1 */ JSON_NewJNumberObj, /* 2 */ JSON_NewJBooleanObj, /* 3 */ JSON_NewJNullObj, /* 4 */ JSON_NewJObjectObj, /* 5 */ 0, /* 6 */ JSON_NewTemplateObj, /* 7 */ JSON_ForceJSON, /* 8 */ JSON_GetJSONType, /* 9 */ JSON_GetObjFromJStringObj, /* 10 */ JSON_GetObjFromJNumberObj, /* 11 */ JSON_GetObjFromJBooleanObj, /* 12 */ JSON_JArrayObjAppendElement, /* 13 */ JSON_JArrayObjAppendList, /* 14 */ JSON_SetJArrayObj, /* 15 */ JSON_JArrayObjGetElements, /* 16 */ JSON_JArrayObjIndex, /* 17 */ JSON_JArrayObjReplace, /* 18 */ JSON_Get, /* 19 */ JSON_Extract, /* 20 */ JSON_Exists, /* 21 */ JSON_Set, /* 22 */ JSON_Unset, /* 23 */ JSON_Normalize, /* 24 */ JSON_Pretty, /* 25 */ JSON_Template, /* 26 */ JSON_IsNULL, /* 27 */ JSON_Type, /* 28 */ JSON_Length, /* 29 */ JSON_Keys, /* 30 */ JSON_Decode, /* 31 */ JSON_Foreach, /* 32 */ JSON_Valid, /* 33 */ }; /* !END!: Do not edit above this line. */ |
Changes to jni/rl_json/generic/rl_jsonStubLib.c.
|
| < < < < | | < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | #include "rl_json.h" MODULE_SCOPE const Rl_jsonStubs *rl_jsonStubsPtr; const Rl_jsonStubs *Rl_jsonStubsPtr = NULL; #undef rl_jsonInitStubs EXTERN CONST const char* Rl_jsonInitStubs(Tcl_Interp* interp, const char* version, int exact) { const char* packageName = "rl_json"; const char* errMsg = NULL; Rl_jsonStubs* stubsPtr = NULL; const char* actualVersion = tclStubsPtr->tcl_PkgRequireEx(interp, packageName, version, exact, &stubsPtr); if (actualVersion == NULL) { return NULL; |
︙ | ︙ |
Changes to jni/rl_json/generic/tclstuff.h.
︙ | ︙ | |||
11 12 13 14 15 16 17 18 19 20 21 22 23 24 | (ClientData *) NULL, NULL ); #define THROW_ERROR( ... ) \ { \ Tcl_AppendResult(interp, ##__VA_ARGS__, NULL); \ return TCL_ERROR; \ } #define THROW_ERROR_LABEL( label, var, ... ) \ { \ Tcl_AppendResult(interp, ##__VA_ARGS__, NULL); \ var = TCL_ERROR; \ goto label; \ } | > > > > > > | 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | (ClientData *) NULL, NULL ); #define THROW_ERROR( ... ) \ { \ Tcl_AppendResult(interp, ##__VA_ARGS__, NULL); \ return TCL_ERROR; \ } #define THROW_PRINTF( fmtstr, ... ) \ { \ Tcl_SetObjResult(interp, Tcl_ObjPrintf((fmtstr), ##__VA_ARGS__)); \ return TCL_ERROR; \ } #define THROW_ERROR_LABEL( label, var, ... ) \ { \ Tcl_AppendResult(interp, ##__VA_ARGS__, NULL); \ var = TCL_ERROR; \ goto label; \ } |
︙ | ︙ | |||
49 50 51 52 53 54 55 56 57 58 59 60 61 | var = TCL_ERROR; \ goto label; \ } #else #define TEST_OK( cmd ) \ if (cmd != TCL_OK) return TCL_ERROR; #endif #define TEST_OK_LABEL( label, var, cmd ) \ if (cmd != TCL_OK) { \ var = TCL_ERROR; \ goto label; \ } #endif | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 | var = TCL_ERROR; \ goto label; \ } #else #define TEST_OK( cmd ) \ if (cmd != TCL_OK) return TCL_ERROR; #endif #define TEST_OK_LABEL( label, var, cmd ) \ if (cmd != TCL_OK) { \ var = TCL_ERROR; \ goto label; \ } #define TEST_OK_BREAK(var, cmd) if (TCL_OK != (var=(cmd))) break; static inline void release_tclobj(Tcl_Obj** obj) { if (*obj) { Tcl_DecrRefCount(*obj); *obj = NULL; } } #define RELEASE_MACRO(obj) if (obj) {Tcl_DecrRefCount(obj); obj=NULL;} #define REPLACE_MACRO(target, replacement) \ { \ release_tclobj(&target); \ if (replacement) Tcl_IncrRefCount(target = replacement); \ } static inline void replace_tclobj(Tcl_Obj** target, Tcl_Obj* replacement) { if (*target) { Tcl_DecrRefCount(*target); *target = NULL; } *target = replacement; if (*target) Tcl_IncrRefCount(*target); } #include <signal.h> #define DEBUGGER raise(SIGTRAP) #endif |
Added jni/rl_json/generic/tip445.h.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | #ifndef _TIP445_H #define _TIP445_H #if TIP445_SHIM /* Just enough of TIP445 to build rl_json on Tcl 8.6 */ #ifndef Tcl_ObjIntRep typedef union Tcl_ObjIntRep { struct { void* ptr1; void* ptr2; } twoPtrValue; } Tcl_ObjIntRep; #endif #ifndef Tcl_FetchIntRep #define Tcl_FetchIntRep(obj, type) (Tcl_ObjIntRep*)(((obj)->typePtr == (type)) ? &(obj)->internalRep : NULL) #endif #ifndef Tcl_FreeIntRep static inline void Tcl_FreeIntRep(Tcl_Obj* obj) { if (obj->typePtr && obj->typePtr->freeIntRepProc) obj->typePtr->freeIntRepProc(obj); } #endif #ifndef Tcl_StoreIntRep static inline void Tcl_StoreIntRep(Tcl_Obj* objPtr, const Tcl_ObjType* typePtr, const Tcl_ObjIntRep* irPtr) { objPtr->typePtr = typePtr; objPtr->internalRep.twoPtrValue.ptr1 = irPtr->twoPtrValue.ptr1; objPtr->internalRep.twoPtrValue.ptr2 = irPtr->twoPtrValue.ptr2; } #endif #ifndef Tcl_HasStringRep #define Tcl_HasStringRep(obj) ((obj)->bytes != NULL) #endif #endif #endif |
Added jni/rl_json/get_tclconfig.tcl.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | #!/usr/bin/env tclsh package require http try { package require tls } on ok ver { puts "Have tls $ver" http::register https 443 ::tls::socket } on error {} {} cd [file dirname [file normalize [info script]]] set url https://core.tcl-lang.org/tclconfig/tarball/trunk/tclconfig.tar.gz while {[incr tries] <= 5} { set token [http::geturl $url] switch -glob -- [http::ncode $token] { 30* { foreach {k v} [http::meta $token] { if {[string tolower $k] eq "location"} { set url $v puts "Followed [http::ncode $token] redirect to $url" break } } } 5* { puts stderr "Server error [http::ncode $token], delaying 1s and trying again" after 1000 } 2* { file delete -force -- tclconfig set h [open |[list tar xzf -] wb+] try { puts -nonewline $h [http::data $token] chan close $h write read $h close $h } on ok {} { puts "Refreshed tclconfig with the latest version from core.tcl-lang.org" exit 0 } trap CHILDSTATUS {errmsg options} { set status [lindex [dict get $options -errorinfo] 2] puts stderr "tar exited with status $status" exit $status } trap CHILDKILLED {errmsg options} { set sig [lindex [dict get $options -errorinfo] 2] puts stderr "tar killed by signal $sig" exit 1 } finally { if {[info exists h] && $h in [chan names]} { close $h } } } default { puts stderr "Fetching tclconfig failed: [http::ncode $token] [http::error $token] [http::data $token]" parray $token exit 1 } } } puts "Gave up after [expr {$tries-1}] attempts" exit 1 |
Added jni/rl_json/support/urlencode-1.0.tm.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 | package require parse_args namespace eval ::urlencode { namespace export * namespace ensemble create -prefixes no namespace eval _uri { variable uri_charmap # Set up classvar uri_charmap <<< # The reason that this is here is so that the sets, lists and charmaps # are generated only once, making the instantiation of uri objects much # lighter array set uri_common { reserved {; ? : @ & = + $ ,} lowalpha {a b c d e f g h i j k l m n o p q r s t u v w x y z} upalpha {A B C D E F G H I J K L M N O P Q R S T U V W X Y Z} digit {0 1 2 3 4 5 6 7 8 9} mark {- _ . ! ~ * ' ( )} alpha {} alphanum {} unreserved {} } set uri_common(alpha) [concat $uri_common(lowalpha) $uri_common(upalpha)] set uri_common(alphanum) [concat $uri_common(alpha) $uri_common(digit)] set uri_common(unreserved) [concat $uri_common(alphanum) $uri_common(mark)] variable uri_charmap variable uri_charmap_compat variable uri_charmap_path variable uri_charmap_path_compat variable uri_decode_min {} variable uri_decode {} for {set i 0} {$i < 256} {incr i} { set c [binary format c $i] if {$c in $uri_common(unreserved)} { lappend uri_charmap $c } else { lappend uri_charmap [format %%%02x $i] } if {$c in $uri_common(alphanum)} { lappend uri_charmap_compat $c } else { lappend uri_charmap_compat [format %%%02x $i] lappend uri_decode_min [format %%%02x $i] $c [format %%%02X $i] $c } lappend uri_decode [format %%%02x $i] $c [format %%%02X $i] $c } lappend uri_decode + " " lappend uri_decode_min + " " unset uri_common set uri_charmap_path $uri_charmap lset uri_charmap 0x2f / set uri_charmap_path_compat $uri_charmap_compat lset uri_charmap_compat 0x2f / # Set up classvar uri_charmap >>> proc percent_encode {charset map data} { #<<< binary scan [encoding convertto $charset $data] cu* byteslist set out "" foreach byte $byteslist { append out [lindex $map $byte] } set out } #>>> proc percent_decode {charset data} { #<<< variable uri_decode encoding convertfrom $charset [string map $uri_decode $data] } #>>> } proc rfc_urlencode args { #<<< # besides alphanumeric characters, the valid un-encoded characters are: - _ . ! ~ * ' ( ) parse_args::parse_args $args { -charset {-default utf-8} -part {-enum {query path} -default query} str {-required} } switch -- $part { query { set map $::urlencode::_uri::uri_charmap } path { set map $::urlencode::_uri::uri_charmap_path } } ::urlencode::_uri::percent_encode $charset $map $str } #>>> proc rfc_urldecode args { #<<< parse_args::parse_args $args { -charset {-default utf-8} str {-required} } # the encoding convertto here is in case we're passed unicode, not percent encoded utf-8 ::urlencode::_uri::percent_decode $charset [encoding convertto utf-8 $str] } #>>> proc encode_query args { #<<< switch -- [llength $args] { 0 { return } 1 { set params [lindex $args 0] } default { set params $args } } if {[llength $params] == 0} return string cat ? [join [lmap {k v} $params { if {$v eq ""} { rfc_urlencode -- $k $v } else { format %s=%s [rfc_urlencode -- $k] [rfc_urlencode -- $v] } }] &] } #>>> proc decode_query q { #<<< set params {} foreach part [split $q &] { lassign [split $part =] ke ve lappend params [rfc_urldecode -- $ke] [rfc_urldecode -- $ve] } set params } #>>> } # vim: ft=tcl foldmethod=marker foldmarker=<<<,>>> ts=4 shiftwidth=4 |
Changes to jni/rl_json/tests/_parser.test.
︙ | ︙ | |||
924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 | unset -nocomplain j } -result [list {["foo","bar"]} bar] #>>> test parser/structure-4.1 {orphaned hold_key} -body { #<<< list [catch { json normalize {{"structure-4.1"}} } r o] [expr {[dict exists $o -errorcode] ? [dict get $o -errorcode] : ""}] $r } -result [list 1 [list RL JSON PARSE {Expecting : after object key} {{"structure-4.1"}} 16] {Error parsing JSON value: Expecting : after object key at offset 16}] #>>> test parser/structure-4.2 {orphaned hold_key} -body { #<<< list [catch { json normalize {{"structure-4.2":}} } r o] [expr {[dict exists $o -errorcode] ? [dict get $o -errorcode] : ""}] $r } -result [list 1 [list RL JSON PARSE {Illegal character} {{"structure-4.2":}} 17] {Error parsing JSON value: Illegal character at offset 17}] #>>> test parser/structure-4.3 {nested orphaned hold_key} -body { #<<< list [catch { json normalize {{"x":"y","structure-4.3":{"nested-4.3":}}} } r o] [expr {[dict exists $o -errorcode] ? [dict get $o -errorcode] : ""}] $r } -result [list 1 [list RL JSON PARSE {Illegal character} {{"x":"y","structure-4.3":{"nested-4.3":}}} 39] {Error parsing JSON value: Illegal character at offset 39}] #>>> test parser/structure-4.4 {nested orphaned hold_key, nested key parse error} -body { #<<< list [catch { json normalize {{"structure-4.4":{"nested-4.4\x":"bar"}}} } r o] [expr {[dict exists $o -errorcode] ? [dict get $o -errorcode] : ""}] $r } -result [list 1 [list RL JSON PARSE {Illegal character} {{"structure-4.4":{"nested-4.4\x":"bar"}}} 30] {Error parsing JSON value: Illegal character at offset 30}] #>>> test parser/structure-5.4 {Deep nesting, ensure that this test nests deeper than CX_STACK_SIZE} -body { #<<< json get { { "first": { "second": { | > > > > > > > > | 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 | unset -nocomplain j } -result [list {["foo","bar"]} bar] #>>> test parser/structure-4.1 {orphaned hold_key} -body { #<<< list [catch { json normalize {{"structure-4.1"}} } r o] [expr {[dict exists $o -errorcode] ? [dict get $o -errorcode] : ""}] $r } -cleanup { unset -nocomplain r o } -result [list 1 [list RL JSON PARSE {Expecting : after object key} {{"structure-4.1"}} 16] {Error parsing JSON value: Expecting : after object key at offset 16}] #>>> test parser/structure-4.2 {orphaned hold_key} -body { #<<< list [catch { json normalize {{"structure-4.2":}} } r o] [expr {[dict exists $o -errorcode] ? [dict get $o -errorcode] : ""}] $r } -cleanup { unset -nocomplain r o } -result [list 1 [list RL JSON PARSE {Illegal character} {{"structure-4.2":}} 17] {Error parsing JSON value: Illegal character at offset 17}] #>>> test parser/structure-4.3 {nested orphaned hold_key} -body { #<<< list [catch { json normalize {{"x":"y","structure-4.3":{"nested-4.3":}}} } r o] [expr {[dict exists $o -errorcode] ? [dict get $o -errorcode] : ""}] $r } -cleanup { unset -nocomplain r o } -result [list 1 [list RL JSON PARSE {Illegal character} {{"x":"y","structure-4.3":{"nested-4.3":}}} 39] {Error parsing JSON value: Illegal character at offset 39}] #>>> test parser/structure-4.4 {nested orphaned hold_key, nested key parse error} -body { #<<< list [catch { json normalize {{"structure-4.4":{"nested-4.4\x":"bar"}}} } r o] [expr {[dict exists $o -errorcode] ? [dict get $o -errorcode] : ""}] $r } -cleanup { unset -nocomplain r o } -result [list 1 [list RL JSON PARSE {Illegal character} {{"structure-4.4":{"nested-4.4\x":"bar"}}} 30] {Error parsing JSON value: Illegal character at offset 30}] #>>> test parser/structure-5.4 {Deep nesting, ensure that this test nests deeper than CX_STACK_SIZE} -body { #<<< json get { { "first": { "second": { |
︙ | ︙ | |||
976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 | } first second third fourth fifth sixth seventh eighth ninth tenth eleventh end } -result structure-5.4 #>>> test parser/structure-6.1 {Nesting error} -body { #<<< list [catch { json normalize {{"structure-6.1":[}]} } r o] [expr {[dict exists $o -errorcode] ? [dict get $o -errorcode] : ""}] $r } -result [list 1 [list RL JSON PARSE {Illegal character} {{"structure-6.1":[}]} 18] {Error parsing JSON value: Illegal character at offset 18}] #>>> test parser/structure-6.2 {Nesting error} -body { #<<< list [catch { json normalize {{"structure-6.2":[null,}]} } r o] [expr {[dict exists $o -errorcode] ? [dict get $o -errorcode] : ""}] $r } -result [list 1 [list RL JSON PARSE {Illegal character} {{"structure-6.2":[null,}]} 23] {Error parsing JSON value: Illegal character at offset 23}] #>>> test parser/structure-6.3 {Nesting error} -body { #<<< list [catch { json normalize {[{"structure-6.3"]}]} } r o] [expr {[dict exists $o -errorcode] ? [dict get $o -errorcode] : ""}] $r } -result [list 1 [list RL JSON PARSE {Expecting : after object key} {[{"structure-6.3"]}]} 17] {Error parsing JSON value: Expecting : after object key at offset 17}] #>>> test parser/structure-6.4 {Nesting error} -body { #<<< list [catch { json normalize {[{"structure-6.4":]}]} } r o] [expr {[dict exists $o -errorcode] ? [dict get $o -errorcode] : ""}] $r } -result [list 1 [list RL JSON PARSE {Illegal character} {[{"structure-6.4":]}]} 18] {Error parsing JSON value: Illegal character at offset 18}] #>>> test parser/structure-6.5 {Nesting error} -body { #<<< list [catch { json normalize {[{"structure-6.5":1]}]} } r o] [expr {[dict exists $o -errorcode] ? [dict get $o -errorcode] : ""}] $r } -result [list 1 [list RL JSON PARSE "Expecting \} or ," {[{"structure-6.5":1]}]} 19] "Error parsing JSON value: Expecting \} or , at offset 19"] #>>> test parser/structure-6.6 {Nesting error} -body { #<<< list [catch { json normalize {[{"structure-6.6":1,]}]} } r o] [expr {[dict exists $o -errorcode] ? [dict get $o -errorcode] : ""}] $r } -result [list 1 [list RL JSON PARSE {Illegal character} {[{"structure-6.6":1,]}]} 20] {Error parsing JSON value: Illegal character at offset 20}] #>>> test parser/structure-7.1 {Object key not a string: number} -body { #<<< list [catch { json normalize {{7.1:"structure-7.1"}} } r o] [expr {[dict exists $o -errorcode] ? [dict get $o -errorcode] : ""}] $r } -result [list 1 [list RL JSON PARSE {Object key is not a string} {{7.1:"structure-7.1"}} 1] {Error parsing JSON value: Object key is not a string at offset 1}] #>>> test parser/structure-7.2 {Object key not a string: true} -body { #<<< list [catch { json normalize {{true:"structure-7.2"}} } r o] [expr {[dict exists $o -errorcode] ? [dict get $o -errorcode] : ""}] $r } -result [list 1 [list RL JSON PARSE {Object key is not a string} {{true:"structure-7.2"}} 1] {Error parsing JSON value: Object key is not a string at offset 1}] #>>> test parser/structure-7.3 {Object key not a string: false} -body { #<<< list [catch { json normalize {{false:"structure-7.3"}} } r o] [expr {[dict exists $o -errorcode] ? [dict get $o -errorcode] : ""}] $r } -result [list 1 [list RL JSON PARSE {Object key is not a string} {{false:"structure-7.3"}} 1] {Error parsing JSON value: Object key is not a string at offset 1}] #>>> test parser/structure-7.4 {Object key not a string: null} -body { #<<< list [catch { json normalize {{null:"structure-7.4"}} } r o] [expr {[dict exists $o -errorcode] ? [dict get $o -errorcode] : ""}] $r } -result [list 1 [list RL JSON PARSE {Object key is not a string} {{null:"structure-7.4"}} 1] {Error parsing JSON value: Object key is not a string at offset 1}] #>>> test parser/structure-7.5 {Object key not a string: object} -body { #<<< list [catch { json normalize {{{}:"structure-7.5"}} } r o] [expr {[dict exists $o -errorcode] ? [dict get $o -errorcode] : ""}] $r } -result [list 1 [list RL JSON PARSE {Object key is not a string} {{{}:"structure-7.5"}} 1] {Error parsing JSON value: Object key is not a string at offset 1}] #>>> test parser/structure-7.6 {Object key not a string: array} -body { #<<< list [catch { json normalize {{[]:"structure-7.6"}} } r o] [expr {[dict exists $o -errorcode] ? [dict get $o -errorcode] : ""}] $r } -result [list 1 [list RL JSON PARSE {Object key is not a string} {{[]:"structure-7.6"}} 1] {Error parsing JSON value: Object key is not a string at offset 1}] #>>> test parser/structure-8.1 {Object key not a string: dyn_number} -body { #<<< json normalize {{"~N:f":"structure-8.1"}} } -result {{"~N:f":"structure-8.1"}} #>>> test parser/structure-8.2 {Object key not a string: dyn_bool} -body { #<<< | > > > > > > > > > > > > > > > > > > > > > > > > | 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 | } first second third fourth fifth sixth seventh eighth ninth tenth eleventh end } -result structure-5.4 #>>> test parser/structure-6.1 {Nesting error} -body { #<<< list [catch { json normalize {{"structure-6.1":[}]} } r o] [expr {[dict exists $o -errorcode] ? [dict get $o -errorcode] : ""}] $r } -cleanup { unset -nocomplain r o } -result [list 1 [list RL JSON PARSE {Illegal character} {{"structure-6.1":[}]} 18] {Error parsing JSON value: Illegal character at offset 18}] #>>> test parser/structure-6.2 {Nesting error} -body { #<<< list [catch { json normalize {{"structure-6.2":[null,}]} } r o] [expr {[dict exists $o -errorcode] ? [dict get $o -errorcode] : ""}] $r } -cleanup { unset -nocomplain r o } -result [list 1 [list RL JSON PARSE {Illegal character} {{"structure-6.2":[null,}]} 23] {Error parsing JSON value: Illegal character at offset 23}] #>>> test parser/structure-6.3 {Nesting error} -body { #<<< list [catch { json normalize {[{"structure-6.3"]}]} } r o] [expr {[dict exists $o -errorcode] ? [dict get $o -errorcode] : ""}] $r } -cleanup { unset -nocomplain r o } -result [list 1 [list RL JSON PARSE {Expecting : after object key} {[{"structure-6.3"]}]} 17] {Error parsing JSON value: Expecting : after object key at offset 17}] #>>> test parser/structure-6.4 {Nesting error} -body { #<<< list [catch { json normalize {[{"structure-6.4":]}]} } r o] [expr {[dict exists $o -errorcode] ? [dict get $o -errorcode] : ""}] $r } -cleanup { unset -nocomplain r o } -result [list 1 [list RL JSON PARSE {Illegal character} {[{"structure-6.4":]}]} 18] {Error parsing JSON value: Illegal character at offset 18}] #>>> test parser/structure-6.5 {Nesting error} -body { #<<< list [catch { json normalize {[{"structure-6.5":1]}]} } r o] [expr {[dict exists $o -errorcode] ? [dict get $o -errorcode] : ""}] $r } -cleanup { unset -nocomplain r o } -result [list 1 [list RL JSON PARSE "Expecting \} or ," {[{"structure-6.5":1]}]} 19] "Error parsing JSON value: Expecting \} or , at offset 19"] #>>> test parser/structure-6.6 {Nesting error} -body { #<<< list [catch { json normalize {[{"structure-6.6":1,]}]} } r o] [expr {[dict exists $o -errorcode] ? [dict get $o -errorcode] : ""}] $r } -cleanup { unset -nocomplain r o } -result [list 1 [list RL JSON PARSE {Illegal character} {[{"structure-6.6":1,]}]} 20] {Error parsing JSON value: Illegal character at offset 20}] #>>> test parser/structure-7.1 {Object key not a string: number} -body { #<<< list [catch { json normalize {{7.1:"structure-7.1"}} } r o] [expr {[dict exists $o -errorcode] ? [dict get $o -errorcode] : ""}] $r } -cleanup { unset -nocomplain r o } -result [list 1 [list RL JSON PARSE {Object key is not a string} {{7.1:"structure-7.1"}} 1] {Error parsing JSON value: Object key is not a string at offset 1}] #>>> test parser/structure-7.2 {Object key not a string: true} -body { #<<< list [catch { json normalize {{true:"structure-7.2"}} } r o] [expr {[dict exists $o -errorcode] ? [dict get $o -errorcode] : ""}] $r } -cleanup { unset -nocomplain r o } -result [list 1 [list RL JSON PARSE {Object key is not a string} {{true:"structure-7.2"}} 1] {Error parsing JSON value: Object key is not a string at offset 1}] #>>> test parser/structure-7.3 {Object key not a string: false} -body { #<<< list [catch { json normalize {{false:"structure-7.3"}} } r o] [expr {[dict exists $o -errorcode] ? [dict get $o -errorcode] : ""}] $r } -cleanup { unset -nocomplain r o } -result [list 1 [list RL JSON PARSE {Object key is not a string} {{false:"structure-7.3"}} 1] {Error parsing JSON value: Object key is not a string at offset 1}] #>>> test parser/structure-7.4 {Object key not a string: null} -body { #<<< list [catch { json normalize {{null:"structure-7.4"}} } r o] [expr {[dict exists $o -errorcode] ? [dict get $o -errorcode] : ""}] $r } -cleanup { unset -nocomplain r o } -result [list 1 [list RL JSON PARSE {Object key is not a string} {{null:"structure-7.4"}} 1] {Error parsing JSON value: Object key is not a string at offset 1}] #>>> test parser/structure-7.5 {Object key not a string: object} -body { #<<< list [catch { json normalize {{{}:"structure-7.5"}} } r o] [expr {[dict exists $o -errorcode] ? [dict get $o -errorcode] : ""}] $r } -cleanup { unset -nocomplain r o } -result [list 1 [list RL JSON PARSE {Object key is not a string} {{{}:"structure-7.5"}} 1] {Error parsing JSON value: Object key is not a string at offset 1}] #>>> test parser/structure-7.6 {Object key not a string: array} -body { #<<< list [catch { json normalize {{[]:"structure-7.6"}} } r o] [expr {[dict exists $o -errorcode] ? [dict get $o -errorcode] : ""}] $r } -cleanup { unset -nocomplain r o } -result [list 1 [list RL JSON PARSE {Object key is not a string} {{[]:"structure-7.6"}} 1] {Error parsing JSON value: Object key is not a string at offset 1}] #>>> test parser/structure-8.1 {Object key not a string: dyn_number} -body { #<<< json normalize {{"~N:f":"structure-8.1"}} } -result {{"~N:f":"structure-8.1"}} #>>> test parser/structure-8.2 {Object key not a string: dyn_bool} -body { #<<< |
︙ | ︙ | |||
1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 | unset -nocomplain bytes lower upper } #>>> test parser/whitespace-4.1 {Half opened // comment sequence} -body { #<<< list [catch { json normalize " /foo" } r o] [expr {[dict exists $o -errorcode] ? [dict get $o -errorcode] : ""}] $r } -result [list 1 {RL JSON PARSE {Illegal character} { /foo} 2} {Error parsing JSON value: Illegal character at offset 2}] #>>> test parser/whitespace-5.1 {Two comments, no whitespace} -body { #<<< json get {/*foo*//*bar*/123} } -result 123 #>>> test parser/whitespace-6.1 {Two comments, whitespace between} -body { #<<< | > > | 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 | unset -nocomplain bytes lower upper } #>>> test parser/whitespace-4.1 {Half opened // comment sequence} -body { #<<< list [catch { json normalize " /foo" } r o] [expr {[dict exists $o -errorcode] ? [dict get $o -errorcode] : ""}] $r } -cleanup { unset -nocomplain r o } -result [list 1 {RL JSON PARSE {Illegal character} { /foo} 2} {Error parsing JSON value: Illegal character at offset 2}] #>>> test parser/whitespace-5.1 {Two comments, no whitespace} -body { #<<< json get {/*foo*//*bar*/123} } -result 123 #>>> test parser/whitespace-6.1 {Two comments, whitespace between} -body { #<<< |
︙ | ︙ | |||
1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 | test parser/whitespace-9.6 {Commented object key} -body { #<<< json template { { //"foo": {} } } } -result "{}" ::tcltest::cleanupTests return # vim: ft=tcl foldmethod=marker foldmarker=<<<,>>> ts=4 shiftwidth=4 | > | 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 | test parser/whitespace-9.6 {Commented object key} -body { #<<< json template { { //"foo": {} } } } -result "{}" #>>> ::tcltest::cleanupTests return # vim: ft=tcl foldmethod=marker foldmarker=<<<,>>> ts=4 shiftwidth=4 |
Changes to jni/rl_json/tests/all.tcl.
︙ | ︙ | |||
13 14 15 16 17 18 19 20 21 22 23 24 25 26 | } if {[llength [info commands memory]] == 1} { memory init on #memory onexit memdebug #memory validate on #memory trace on } set ::tcltest::testSingleFile false set ::tcltest::testsDirectory [file dir [info script]] # We need to ensure that the testsDirectory is absolute if {[catch {::tcltest::normalizePath ::tcltest::testsDirectory}]} { | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 | } if {[llength [info commands memory]] == 1} { memory init on #memory onexit memdebug #memory validate on #memory trace on memory tag startup proc intersect3 {list1 list2} { set firstonly {} set intersection {} set secondonly {} set list1 [lsort -unique $list1] set list2 [lsort -unique $list2] foreach item $list1 { if {[lsearch -sorted $list2 $item] == -1} { lappend firstonly $item } else { lappend intersection $item } } foreach item $list2 { if {[lsearch -sorted $intersection $item] == -1} { lappend secondonly $item } } list $firstonly $intersection $secondonly } proc _mem_objs {} { set h [file tempfile name] try { memory objs $name seek $h 0 gets $h ;# Throw away the "total objects:" header set res {} while {![eof $h]} { set line [gets $h] lappend res $line } set res } finally { file delete $name close $h } } proc _mem_info {} { set res {} foreach line [split [string trim [memory info]] \n] { if {![regexp {^(.*?)\s+([0-9]+)$} $line - k v]} continue dict set res $k $v } set res } proc _diff_mem_info {before after} { set res {} dict for {k v} $after { dict set res $k [expr {$v - [dict get $before $k]}] } set res } proc _mem_active tag { set h [file tempfile name] try { memory active $name seek $h 0 set res {} while {![eof $h]} { set line [gets $h] if {[string match *$tag $line]} { lappend res $line } } set res } finally { file delete $name close $h } } proc memtest {name args} { if {[llength $::tcltest::match] > 0} { set ok 0 foreach match $::tcltest::match { if {[string match $match $name]} { set ok 1 break } } if {!$ok} return } set tag "test $name" set before [_mem_objs] set before_inf [_mem_info] memory tag $tag try { uplevel 1 [list if 1 [list ::tcltest::test $name {*}$args]] } finally { set after_inf [_mem_info] memory tag "overhead" set diff [_diff_mem_info $before_inf $after_inf] if {[dict get $diff {current packets allocated}] > 80} { set after [_mem_objs] #set new_active [_mem_active $tag] #set new_active [_mem_active ""] #puts stderr "$name: $diff" if {[incr ::count] > 0} { #if {[llength $new_active] > 0} { # puts "$name new active:\n\t[join $new_active \n\t]" #} lassign [intersect3 $before $after] freed common new set new [lmap line $new { if {![string match *unknown* $line]} continue #if {![regexp {/generic/(rl_json.|parser.|tclstuff.)} $line]} continue switch -glob -- $line { *tclIOCmd.c* - *tclLiteral* - *tclExecute* continue } set line }] set freed [lmap line $freed { if {![string match *unknown* $line]} continue #if {![regexp {/generic/(rl_json.|parser.|tclstuff.)} $line]} continue switch -glob -- $line { *tclIOCmd.c* - *tclLiteral* - *tclExecute* continue } if {![string match *unknown* $line]} continue set line }] if {[llength $freed] > 0} { puts stderr "$name Existing objs freed:\n\t[join $freed \n\t]" } if {[llength $new] > 0} { #puts stderr "$name New objs created:\n\t[join $new \n\t]" #key = 0x0x1494cd8, objPtr = 0x0x1494cd8, file = unknown, line = 0 puts stderr "$name New objs created:" foreach line $new { if {[regexp {, objPtr = 0x(0x.*?),} $line - addr]} { ::rl_json::json _leak_info $addr } } } #set common [lmap line $common { # if {![regexp {/generic/(rl_json.|parser.|tclstuff.)} $line]} continue # set line #}] #if {[llength $common] > 0} { # puts stderr "$name New objs created:\n\t[join $common \n\t]" #} } } } } proc memtest2 {name args} { set bodyidx [lsearch $args -body] if {$bodyidx == -1} { puts stderr "Can't find -body param to inject memory monitoring" } else { set body [lindex $args $bodyidx+1] set args [lreplace $args $bodyidx+1 $bodyidx+1 "set _checkmem_result \[::rl_json::checkmem [list $body] _checkmem_newactive\]; if {\[llength \$_checkmem_newactive\] > 0} {error \"Leaked memory:\\n\\t\[join \$_checkmem_newactive\[unset _checkmem_newactive\] \\n\\t\]\"} else {unset _checkmem_newactive}; return -level 0 \$_checkmem_result\[unset _checkmem_result\]"] } tailcall ::tcltest::test $name {*}$args } proc memtest3 {name args} { if {[llength $::tcltest::match] > 0} { set ok 0 foreach match $::tcltest::match { if {[string match $match $name]} { set ok 1 break } } if {!$ok} return } ::rl_json::checkmem { apply {{name args} { ::tcltest::test $name {*}$args }} $name {*}$args } newactive ::tcltest::test "$name-mem" "Memory test for $name" -body { if {[llength $newactive] > 0} { return -level 0 "Leaked memory:\n\t[join $newactive \n\t]" } } -result {} } if 1 { rename test _test #interp alias {} test {} ::memtest #interp alias {} test {} ::memtest2 interp alias {} ::test {} ::memtest3 } } else { proc memtest args {tailcall ::tcltest::test {*}$args} } set ::tcltest::testSingleFile false set ::tcltest::testsDirectory [file dir [info script]] # We need to ensure that the testsDirectory is absolute if {[catch {::tcltest::normalizePath ::tcltest::testsDirectory}]} { |
︙ | ︙ | |||
63 64 65 66 67 68 69 70 71 | puts $chan $msg } } # cleanup puts $chan "\nTests ended at [eval $timeCmd]" ::tcltest::cleanupTests 1 return | > > > > > > > > | 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 | puts $chan $msg } } # cleanup puts $chan "\nTests ended at [eval $timeCmd]" ::tcltest::cleanupTests 1 if {[llength [info commands memory]] == 1} { memory tag shutdown #rl_json::json free_cache unload -nocomplain {} rl_json memory objs tclobjs_remaining } return |
Added jni/rl_json/tests/amap.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 | if {"::tcltest" ni [namespace children]} { package require tcltest namespace import ::tcltest::* } package require rl_json namespace path {::rl_json} test amap-1.0 {Single iterator, no vars, over array} -body { #<<< json amap {} {["a", "b", "other var", null, true, false, {"x": 42}]} {} } -returnCodes error -result "foreach varlist is empty" #>>> test amap-1.1 {Single iterator, single var, over array} -setup { #<<< set i 0 } -body { json amap elem {["a", "b", "other var", null, true, false, {"x": 42}]} { string cat [incr i]($elem)([json get $elem]) } } -cleanup { unset -nocomplain i elem } -result {["1(\"a\")(a)","2(\"b\")(b)","3(\"other var\")(other var)","4(null)()","5(true)(1)","6(false)(0)","7({\"x\":42})(x 42)"]} #>>> test amap-2.1 {Single iterator, multi vars, over array} -setup { #<<< set i 0 } -body { json amap {x y} {["a", "b", "other var", null, true, false, {"x": 42}, 42.1, "last"]} { string cat [incr i] \ ($x)([json get $x]) \ ($y)([json get $y]) } } -cleanup { unset -nocomplain i x y } -result {["1(\"a\")(a)(\"b\")(b)","2(\"other var\")(other var)(null)()","3(true)(1)(false)(0)","4({\"x\":42})(x 42)(42.1)(42.1)","5(\"last\")(last)(null)()"]} #>>> test amap-3.0 {Multiple iterators, no vars, over array} -body { #<<< json amap \ elem {["a", "b", "other var", null, true, false, {"x": 42}]} \ {} {["A", "B", "OTHER VAR", null, true, {"X": 42}]} \ {} } -cleanup { unset -nocomplain elem } -returnCodes error -result "foreach varlist is empty" #>>> test amap-3.1 {Multiple iterators, single var, over array} -setup { #<<< set i 0 } -body { json amap \ elem {["a", "b", "other var", null, true, false, {"x": 42}]} \ elem2 {["A", "B", "OTHER VAR", null, true, {"X": 42}]} \ { string cat [incr i]($elem)([json get $elem])/($elem2)([json get $elem2]) } } -cleanup { unset -nocomplain i elem elem2 } -result {["1(\"a\")(a)/(\"A\")(A)","2(\"b\")(b)/(\"B\")(B)","3(\"other var\")(other var)/(\"OTHER VAR\")(OTHER VAR)","4(null)()/(null)()","5(true)(1)/(true)(1)","6(false)(0)/({\"X\":42})(X 42)","7({\"x\":42})(x 42)/(null)()"]} #>>> test amap-4.1 {Multiple iterators, multi vars, over array} -setup { #<<< set i 0 } -body { json amap \ {x y} {["a", "b", "other var", null, true, false, {"x": 42}, 42.1]} \ {a b} {["A", "B", "OTHER VAR", null, true, {"X": 42}, 42.1]} \ { string cat [incr i] \ ($x)([json get $x]) \ ($y)([json get $y]) \ / \ ($a)([json get $a]) \ ($b)([json get $b]) } } -cleanup { unset -nocomplain i x y a b } -result {["1(\"a\")(a)(\"b\")(b)/(\"A\")(A)(\"B\")(B)","2(\"other var\")(other var)(null)()/(\"OTHER VAR\")(OTHER VAR)(null)()","3(true)(1)(false)(0)/(true)(1)({\"X\":42})(X 42)","4({\"x\":42})(x 42)(42.1)(42.1)/(42.1)(42.1)(null)()"]} #>>> test amap-5.1 {Single iterator, single var, over array, continue} -setup { #<<< set i 0 } -body { json amap elem {["a", "b", "other var", null, true, false, {"x": 42}]} { if {[json get $elem] eq ""} continue string cat [incr i]($elem)([json get $elem]) } } -cleanup { unset -nocomplain i elem } -result {["1(\"a\")(a)","2(\"b\")(b)","3(\"other var\")(other var)","4(true)(1)","5(false)(0)","6({\"x\":42})(x 42)"]} #>>> test amap-5.1.1 {Single iterator, single var, over array, continue on final iteration} -setup { #<<< set i 0 } -body { json amap elem {["a", "b", "other var", null]} { if {[json get $elem] eq ""} continue string cat [incr i]($elem)([json get $elem]) } } -cleanup { unset -nocomplain i elem } -result {["1(\"a\")(a)","2(\"b\")(b)","3(\"other var\")(other var)"]} #>>> test amap-6.1 {Single iterator, single var, over array, break} -setup { #<<< set i 0 } -body { json amap elem {["a", "b", "other var", null, true, false, {"x": 42}]} { if {[json get $elem] eq ""} break string cat [incr i]($elem)([json get $elem]) } } -cleanup { unset -nocomplain i elem } -result {["1(\"a\")(a)","2(\"b\")(b)","3(\"other var\")(other var)"]} #>>> test amap-6.2 {Single iterator, single var, over array, return} -setup { #<<< set i 0 } -body { json amap elem {["a", "b", "other var", null, true, false, {"x": 42}]} { if {[json get $elem] eq ""} {return returned} string cat [incr i]($elem)([json get $elem]) } } -cleanup { unset -nocomplain i elem } -result returned #>>> test amap-7.1 {Single iterator, single var, over null} -setup { #<<< set i 0 } -body { json amap elem null { incr i } } -cleanup { unset -nocomplain i elem } -result {[]} #>>> test amap-7.2 {Single iterator, single var, over empty array} -setup { #<<< set i 0 } -body { json amap elem {[]} { incr i } } -cleanup { unset -nocomplain i elem } -result {[]} #>>> test amap-7.3 {Single iterator, multi vars, over null} -setup { #<<< set i 0 } -body { json amap {x y} null { incr i } } -cleanup { unset -nocomplain i x y } -result {[]} #>>> test amap-8.1 {Multiple iterators, single var, over null} -setup { #<<< set i 0 } -body { json amap \ elem {["a", "b", "other var", null, true, false, {"x": 42}]} \ elem2 {null} \ { string cat [incr i]($elem)([json get $elem])/($elem2)([json get $elem2]) } } -cleanup { unset -nocomplain i elem elem2 } -result {["1(\"a\")(a)/(null)()","2(\"b\")(b)/(null)()","3(\"other var\")(other var)/(null)()","4(null)()/(null)()","5(true)(1)/(null)()","6(false)(0)/(null)()","7({\"x\":42})(x 42)/(null)()"]} #>>> test amap-8.2 {Multiple iterators, single var, over empty array} -setup { #<<< set i 0 set res {} } -body { json amap \ elem {["a", "b", "other var", null, true, false, {"x": 42}]} \ elem2 {[]} \ { string cat [incr i]($elem)([json get $elem])/($elem2)([json get $elem2]) } } -cleanup { unset -nocomplain i elem elem2 } -result {["1(\"a\")(a)/(null)()","2(\"b\")(b)/(null)()","3(\"other var\")(other var)/(null)()","4(null)()/(null)()","5(true)(1)/(null)()","6(false)(0)/(null)()","7({\"x\":42})(x 42)/(null)()"]} #>>> test amap-9.1.0 {Single iterator, no vars, over object} -body { #<<< json amap {} { { "a": "b", "other var": null } } {} } -returnCodes error -result "foreach varlist is empty" #>>> test amap-9.1.1 {Single iterator, single var, over object} -setup { #<<< set i 0 } -body { json amap {k v} {{"a": "b", "other var": null}} { string cat [incr i]:$k/$v } } -cleanup { unset -nocomplain i k v } -result {["1:a/\"b\"","2:other var/null"]} #>>> test amap-9.1.2 {Single iterator, single var, over object} -setup { #<<< set i 0 } -body { json amap {k} {{"a": "b", "other var": null}} { string cat [incr i]:$k/$v } } -cleanup { unset -nocomplain i k v } -returnCodes error -result {When iterating over a JSON object, varlist must be a pair of varnames (key value)} #>>> test amap-9.1.3 {Single iterator, single var, over object} -setup { #<<< set i 0 } -body { json amap {k v x} {{"a": "b", "other var": null}} { string cat [incr i]:$k/$v } } -cleanup { unset -nocomplain i k v x } -returnCodes error -result {When iterating over a JSON object, varlist must be a pair of varnames (key value)} #>>> test amap-9.3.0 {Multiple iterators, no vars, over object} -body { #<<< json amap \ {k v} {{"a": "b", "other var": null}} \ {} {{"A": "B", "OTHER VAR": null}} \ {} } -cleanup { unset -nocomplain k v } -returnCodes error -result "foreach varlist is empty" #>>> test amap-9.3.1 {Multiple iterators, single var, over object} -setup { #<<< set i 0 } -body { json amap \ {k v} {{"a": "b", "other var": null}} \ {k2 v2} {{"A": "B", "OTHER VAR": null}} \ { string cat [incr i]($k/$v)($k/[json get $v])/($k2/$v2)($k2/[json get $v2]) } } -cleanup { unset -nocomplain i k1 k2 v1 v2 } -result {["1(a/\"b\")(a/b)/(A/\"B\")(A/B)","2(other var/null)(other var/)/(OTHER VAR/null)(OTHER VAR/)"]} #>>> test amap-9.5.1 {Single iterator, single var, over object, continue} -setup { #<<< set i 0 } -body { json amap {k v} {{"a": "b", "other var": null,"x": "y"}} { if {[json get $v] eq ""} continue string cat [incr i]($k/$v)([json get $v]) } } -cleanup { unset -nocomplain i k v } -result {["1(a/\"b\")(b)","2(x/\"y\")(y)"]} #>>> test amap-9.5.1.1 {Single iterator, single var, over object, continue on final iteration} -setup { #<<< set i 0 } -body { json amap {k v} {{"a": "b", "X": "Y", "other var": null}} { if {[json get $v] eq ""} continue string cat [incr i]($k/$v)([json get $v]) } } -cleanup { unset -nocomplain i k v } -result {["1(a/\"b\")(b)","2(X/\"Y\")(Y)"]} #>>> test amap-9.6.1 {Single iterator, single var, over object, break} -setup { #<<< set i 0 } -body { json amap {k v} {{"a": "b", "other var": null, "x": "y"}} { if {[json get $v] eq ""} break string cat [incr i]($k/$v)([json get $v]) } } -cleanup { unset -nocomplain i k v } -result {["1(a/\"b\")(b)"]} #>>> test amap-9.6.2 {Single iterator, single var, over object, return} -setup { #<<< set i 0 } -body { json amap {k v} {{"a": "b", "other var": null, "x": "y"}} { if {[json get $v] eq ""} {return returned} string cat [incr i]($k/$v)([json get $v]) } } -cleanup { unset -nocomplain i k v } -result returned #>>> test amap-10.1.1 {iteration over array result is a (native) JSON value: string} -body { #<<< json amap e {["a","bb","ccc"]} { json string ([json get $e]) } } -cleanup { unset -nocomplain e } -result {["(a)","(bb)","(ccc)"]} #>>> test amap-10.1.2 {iteration over array result is a (pure string) JSON value: string} -body { #<<< json amap e {["a","bb","ccc"]} { string trim " \"([json get $e])\"" } } -cleanup { unset -nocomplain e } -result {["(a)","(bb)","(ccc)"]} #>>> test amap-10.2.1 {iteration over array result is a (native) JSON value: number} -body { #<<< json amap e {["a","bb","ccc"]} { json number [string length [json get $e]] } } -cleanup { unset -nocomplain e } -result {[1,2,3]} #>>> test amap-10.2.2 {iteration over array result is a (pure string) JSON value: number} -body { #<<< json amap e {["a","bb","ccc"]} { string trim " [string length [json get $e]]" } } -cleanup { unset -nocomplain e } -result {[1,2,3]} #>>> test amap-10.3.1 {iteration over array result is a (native) JSON value: bool} -body { #<<< json amap e {["a","bb","ccc"]} { json boolean [expr {[string length [json get $e]] % 2 == 0}] } } -cleanup { unset -nocomplain e } -result {[false,true,false]} #>>> test amap-10.3.2 {iteration over array result is a (pure string) JSON value: bool} -body { #<<< json amap e {["a","bb","ccc"]} { string trim " [expr {[string length [json get $e]] % 2 == 0 ? "true":"false"}]" } } -cleanup { unset -nocomplain e } -result {[false,true,false]} #>>> test amap-10.4.1 {iteration over array result is a (native) JSON value: null} -body { #<<< json amap e {["a","bb","ccc"]} { json extract {[null]} 0 } } -cleanup { unset -nocomplain e } -result {[null,null,null]} #>>> test amap-10.4.2 {iteration over array result is a (pure string) JSON value: null} -body { #<<< json amap e {["a","bb","ccc"]} { string trim " null" } } -cleanup { unset -nocomplain e } -result {[null,null,null]} #>>> test amap-10.5.1 {iteration over array result is a (native) JSON value: array} -body { #<<< json amap e {["a","bb","ccc"]} { set r {[]} json set r end+1 $e json set r end+1 [json number [string length [json get $e]]] } } -cleanup { unset -nocomplain e r } -result {[["a",1],["bb",2],["ccc",3]]} #>>> test amap-10.5.2 {iteration over array result is a (pure string) JSON value: array} -body { #<<< json amap e {["a","bb","ccc"]} { set r {[]} json set r end+1 $e json set r end+1 [json number [string length [json get $e]]] string trim " $r" } } -cleanup { unset -nocomplain e r } -result {[["a",1],["bb",2],["ccc",3]]} #>>> test amap-10.6.1 {iteration over array result is a (native) JSON value: object} -body { #<<< json amap e {["a","bb","ccc"]} { set r {{}} json set r k $e json set r l [json number [string length [json get $e]]] } } -cleanup { unset -nocomplain e r } -result {[{"k":"a","l":1},{"k":"bb","l":2},{"k":"ccc","l":3}]} #>>> test amap-10.6.2 {iteration over array result is a (pure string) JSON value: object} -body { #<<< json amap e {["a","bb","ccc"]} { set r {{}} json set r k $e json set r l [json number [string length [json get $e]]] string trim " $r" } } -cleanup { unset -nocomplain e r } -result {[{"k":"a","l":1},{"k":"bb","l":2},{"k":"ccc","l":3}]} #>>> test amap-11.1.1 {iteration over object, iteration result a (native) JSON value: string} -body { #<<< json amap {k v} {{"x":"a","y":"bb","z":"ccc"}} { json string ($k/[json get $v]) } } -cleanup { unset -nocomplain k v } -result {["(x/a)","(y/bb)","(z/ccc)"]} #>>> test amap-11.1.2 {iteration over object, iteration result a (pure string) JSON value: string} -body { #<<< json amap {k v} {{"x":"a","y":"bb","z":"ccc"}} { string trim " ($k/[json get $v])" } } -cleanup { unset -nocomplain e } -result {["(x/a)","(y/bb)","(z/ccc)"]} #>>> test amap-11.2.1 {iteration over object, iteration result a (native) JSON value: number} -body { #<<< json amap {k v} {{"x":"a","y":"bb","z":"ccc"}} { json number [string length [json get $v]] } } -cleanup { unset -nocomplain k v } -result {[1,2,3]} #>>> test amap-11.2.2 {iteration over object, iteration result a (pure string) JSON value: number} -body { #<<< json amap {k v} {{"x":"a","y":"bb","z":"ccc"}} { string trim " [string length [json get $v]]" } } -cleanup { unset -nocomplain k v } -result {[1,2,3]} #>>> test amap-11.3.1 {iteration over object, iteration result a (native) JSON value: bool} -body { #<<< json amap {k v} {{"x":"a","y":"bb","z":"ccc"}} { json boolean [expr {[string length [json get $v]] % 2 == 0}] } } -cleanup { unset -nocomplain k v } -result {[false,true,false]} #>>> test amap-11.3.2 {iteration over object, iteration result a (pure string) JSON value: bool} -body { #<<< json amap {k v} {{"x":"a","y":"bb","z":"ccc"}} { string trim " [expr {[string length [json get $v]] % 2 == 0 ? "true":"false"}]" } } -cleanup { unset -nocomplain k v } -result {[false,true,false]} #>>> test amap-11.4.1 {iteration over object, iteration result a (native) JSON value: null} -body { #<<< json amap {k v} {{"x":"a","y":"bb","z":"ccc"}} { json extract {[null]} 0 } } -cleanup { unset -nocomplain k v } -result {[null,null,null]} #>>> test amap-11.4.2 {iteration over object, iteration result a (pure string) JSON value: null} -body { #<<< json amap {k v} {{"x":"a","y":"bb","z":"ccc"}} { string trim " null" } } -cleanup { unset -nocomplain k v } -result {[null,null,null]} #>>> test amap-11.5.1 {iteration over object, iteration result a (native) JSON value: array} -body { #<<< json amap {k v} {{"x":"a","y":"bb","z":"ccc"}} { set r {[]} json set r end+1 $v json set r end+1 [json number [string length [json get $v]]] } } -cleanup { unset -nocomplain k v r } -result {[["a",1],["bb",2],["ccc",3]]} #>>> test amap-11.5.2 {iteration over object, iteration result a (pure string) JSON value: array} -body { #<<< json amap {k v} {{"x":"a","y":"bb","z":"ccc"}} { set r {[]} json set r end+1 $v json set r end+1 [json number [string length [json get $v]]] string trim " $r" } } -cleanup { unset -nocomplain k v r } -result {[["a",1],["bb",2],["ccc",3]]} #>>> test amap-11.6.1 {iteration over object, iteration result a (native) JSON value: object} -body { #<<< json amap {k v} {{"x":"a","y":"bb","z":"ccc"}} { set r {{}} json set r k $v json set r l [json number [string length [json get $v]]] } } -cleanup { unset -nocomplain k v r } -result {[{"k":"a","l":1},{"k":"bb","l":2},{"k":"ccc","l":3}]} #>>> test amap-11.6.2 {iteration over object, iteration result a (pure string) JSON value: object} -body { #<<< json amap {k v} {{"x":"a","y":"bb","z":"ccc"}} { set r {{}} json set r k $v json set r l [json number [string length [json get $v]]] string trim " $r" } } -cleanup { unset -nocomplain k v r } -result {[{"k":"a","l":1},{"k":"bb","l":2},{"k":"ccc","l":3}]} #>>> test amap-20.5 {too few args} -body { #<<< list [catch {json amap x {[]}} r o] $r [dict get $o -errorcode] } -cleanup { unset -nocomplain x y r o } -result {1 {wrong # args: should be "*amap ?varlist datalist ...? script"} {TCL WRONGARGS}} -match glob #>>> test amap-20.6 {too many args} -body { #<<< list [catch {json amap x {[]} y {}} r o] $r [dict get $o -errorcode] } -cleanup { unset -nocomplain x y r o } -result {1 {wrong # args: should be "*amap ?varlist datalist ...? script"} {TCL WRONGARGS}} -match glob #>>> ::tcltest::cleanupTests return # vim: ft=tcl foldmethod=marker foldmarker=<<<,>>> ts=4 shiftwidth=4 |
Added jni/rl_json/tests/array.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | if {"::tcltest" ni [namespace children]} { package require tcltest namespace import ::tcltest::* } package require rl_json source [file join [file dirname [info script]] helpers.tcl] test array-1.1 {Create an array from type-value pairs} -setup { #<<< set typevalues [lmap v {a 1 c 2 e 3 g} { if {[string is digit $v]} { list number $v } else { list string $v } }]; } -body { json new array {*}$typevalues } -cleanup { unset -nocomplain typevalues } -result {["a",1,"c",2,"e",3,"g"]} #>>> test array-1.2 {Create an array from JSON values} -setup { #<<< set values [lmap v {a 1 c 2 e 3 g} { if {[string is digit $v]} { json new number $v } else { json new string $v } }]; } -body { json new array {*}[lmap v $values { list json $v }]; } -cleanup { unset -nocomplain values v } -result {["a",1,"c",2,"e",3,"g"]} #>>> ::tcltest::cleanupTests return # Local Variables: # mode: tcl # tab-width: 4 # End: # vim: ft=tcl foldmethod=marker foldmarker=<<<,>>> ts=4 shiftwidth=4 |
Added jni/rl_json/tests/boolean.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 | if {"::tcltest" ni [namespace children]} { package require tcltest namespace import ::tcltest::* } package require rl_json test boolean-1.1 {Create a json boolean: 1} -body { #<<< json boolean 1 } -result true #>>> test boolean-1.2 {Create a json boolean: 0} -body { #<<< json boolean 0 } -result false #>>> test boolean-1.3 {Create a json boolean: truthy string: true} -body { #<<< json boolean true } -result true #>>> test boolean-1.4 {Create a json boolean: truthy string: t} -body { #<<< json boolean t } -result true #>>> test boolean-1.5 {Create a json boolean: truthy string: yes} -body { #<<< json boolean yes } -result true #>>> test boolean-1.6 {Create a json boolean: truthy string: y} -body { #<<< json boolean y } -result true #>>> test boolean-1.7 {Create a json boolean: truthy string: on} -body { #<<< json boolean on } -result true #>>> test boolean-1.8 {Create a json boolean: truthy string: o (ambiguous prefix)} -body { #<<< json boolean o } -returnCodes error -result {expected boolean value but got "o"} #>>> test boolean-1.9 {Create a json boolean: truthy number: 42} -body { #<<< json boolean 42 } -result true #>>> test boolean-1.10 {Create a json boolean: truthy number: 42.5} -body { #<<< json boolean 42.5 } -result true #>>> test boolean-1.11 {Create a json boolean: truthy number: 1e6} -body { #<<< json boolean 1e6 } -result true #>>> test boolean-1.12 {Create a json boolean: truthy number: positive bignum} -body { #<<< json boolean [expr {2**1000-1}] } -result true #>>> test boolean-1.13 {Create a json boolean: truthy number: -42} -body { #<<< json boolean -42 } -result true #>>> test boolean-1.14 {Create a json boolean: truthy number: -42.5} -body { #<<< json boolean -42.5 } -result true #>>> test boolean-1.15 {Create a json boolean: truthy number: -1e6} -body { #<<< json boolean -1e6 } -result true #>>> test boolean-1.16 {Create a json boolean: truthy number: negative bignum} -body { #<<< json boolean [expr {-1*(2**1000-1)}] } -result true #>>> test boolean-1.17 {Create a json boolean: json boolean true} -body { #<<< json boolean [json boolean 1] } -result true #>>> test boolean-1.18 {Create a json boolean: json boolean false} -body { #<<< json boolean [json boolean 0] } -result false #>>> test boolean-2.1 {Too few args} -body { #<<< set code [catch { json boolean } r o] list $code $r [dict get $o -errorcode] } -result {1 {wrong # args: should be "*boolean value"} {TCL WRONGARGS}} -match glob #>>> test boolean-2.2 {Too many args} -body { #<<< set code [catch { json boolean foo bar } r o] list $code $r [dict get $o -errorcode] } -result {1 {wrong # args: should be "*boolean value"} {TCL WRONGARGS}} -match glob #>>> test boolean-2.3 {Empty string} -body { #<<< set code [catch { json boolean "" } r o] list $code $r [dict get $o -errorcode] } -result {1 {expected boolean value but got ""} {TCL VALUE NUMBER}} #>>> test boolean-2.4 {Non-truthy string} -body { #<<< set code [catch { json boolean maybe } r o] list $code $r [dict get $o -errorcode] } -result {1 {expected boolean value but got "maybe"} {TCL VALUE NUMBER}} #>>> ::tcltest::cleanupTests return # Local Variables: # mode: tcl # tab-width: 4 # End: # vim: ft=tcl foldmethod=marker foldmarker=<<<,>>> ts=4 shiftwidth=4 |
Added jni/rl_json/tests/decode.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 | if {"::tcltest" ni [namespace children]} { package require tcltest namespace import ::tcltest::* } package require rl_json namespace path {::rl_json} # Helpers <<< proc readbin fn { set h [open $fn rb] try {read $h} finally {close $h} } proc unicode_string {} { # hello, は 🙂 world return "hello, \u306f [format %c 0x1F642] world" } if {"utf-16le" in [encoding names]} { proc string_to_utf16le s { encoding convertto utf-16le $s } } else { proc string_to_utf16le s { set chars {} foreach e [split $s {}] { scan $e %c o if {$o >= 0x010000} { set u [expr {$o - 0x10000}] set w1 [expr {0b1101100000000000 | ($u >> 10)}] set w2 [expr {0b1101110000000000 | ($u & 0b1111111111)}] lappend chars $w1 $w2 } else { lappend chars $o } } binary format su* $chars } } if {"utf-16be" in [encoding names]} { proc string_to_utf16be s { encoding convertto utf-16be $s } } else { proc string_to_utf16be s { set chars {} foreach e [split $s {}] { scan $e %c o if {$o >= 0x010000} { set u [expr {$o - 0x10000}] set w1 [expr {0b1101100000000000 | ($u >> 10)}] set w2 [expr {0b1101110000000000 | ($u & 0b1111111111)}] lappend chars $w1 $w2 } else { lappend chars $o } } binary format Su* $chars } } proc string_to_utf32le s { binary format iu* [lmap e [split $s {}] {scan $e %c o; set o}] } proc string_to_utf32be s { binary format Iu* [lmap e [split $s {}] {scan $e %c o; set o}] } #>>> test decode-0.1 {Too few args} -body { #<<< list [catch {json decode} r o] $r [dict get $o -errorcode] } -cleanup { unset -nocomplain r o } -result {1 {wrong # args: should be "*decode bytes ?encoding?"} {TCL WRONGARGS}} -match glob #>>> test decode-0.2 {Too many args} -body { #<<< list [catch {json decode foo auto bar} r o] $r [dict get $o -errorcode] } -cleanup { unset -nocomplain r o } -result {1 {wrong # args: should be "*decode bytes ?encoding?"} {TCL WRONGARGS}} -match glob #>>> test decode-0.3 {No optional encoding arg} -body { #<<< json decode foo } -result foo #>>> test decode-1.1 {Decode utf-8, no BOM} -body { #<<< json decode [encoding convertto utf-8 [unicode_string]] } -result [unicode_string] #>>> test decode-1.2 {Decode utf-8, BOM} -body { #<<< json decode [binary decode hex {EF BB BF}][encoding convertto utf-8 [unicode_string]] } -result \uFEFF[unicode_string] #>>> test decode-2.1 {Decode utf-16le, no BOM, explicit encoding} -body { #<<< json decode [string_to_utf16le [unicode_string]] utf-16le } -result [unicode_string] #>>> test decode-2.2 {Decode utf-16le, BOM} -body { #<<< json decode [binary decode hex {FF FE}][string_to_utf16le [unicode_string]] } -result \uFEFF[unicode_string] #>>> test decode-3.1 {Decode utf-16be, no BOM, explicit encoding} -body { #<<< json decode [string_to_utf16be [unicode_string]] utf-16be } -result [unicode_string] #>>> test decode-3.2 {Decode utf-16be, BOM} -body { #<<< json decode [binary decode hex {FE FF}][string_to_utf16be [unicode_string]] } -result \uFEFF[unicode_string] #>>> test decode-4.1 {Decode utf-32le, no BOM, explicit encoding} -body { #<<< json decode [string_to_utf32le [unicode_string]] utf-32le } -result [unicode_string] #>>> test decode-4.2 {Decode utf-32le, BOM} -body { #<<< json decode [binary decode hex {FF FE 00 00}][string_to_utf32le [unicode_string]] } -result \uFEFF[unicode_string] #>>> test decode-5.1 {Decode utf-32be, no BOM, explicit encoding} -body { #<<< json decode [string_to_utf32be [unicode_string]] utf-32be } -result [unicode_string] #>>> test decode-5.2 {Decode utf-32be, BOM} -body { #<<< json decode [binary decode hex {00 00 FE FF}][string_to_utf32be [unicode_string]] } -result \uFEFF[unicode_string] #>>> test decode-6.1 {Decode utf-16le, no BOM, explicit encoding, force manual decode} -body { #<<< json decode [string_to_utf16le [unicode_string]] "x utf-16le" } -result [unicode_string] #>>> test decode-6.2 {Decode utf-16le, BOM, force manual decode} -body { #<<< json decode [binary decode hex {FF FE}][string_to_utf16le [unicode_string]] "x utf-16le" } -result \uFEFF[unicode_string] #>>> test decode-7.1 {Decode utf-16be, no BOM, explicit encoding, force manual decode} -body { #<<< json decode [string_to_utf16be [unicode_string]] "x utf-16be" } -result [unicode_string] #>>> test decode-7.2 {Decode utf-16be, BOM, force manual decode} -body { #<<< json decode [binary decode hex {FE FF}][string_to_utf16be [unicode_string]] "x utf-16be" } -result \uFEFF[unicode_string] #>>> test decode-8.1 {Decode utf-32le, no BOM, explicit encoding, force manual decode} -body { #<<< json decode [string_to_utf32le [unicode_string]] utf-32le } -result [unicode_string] #>>> test decode-8.2 {Decode utf-32le, BOM, force manual decode} -body { #<<< json decode [binary decode hex {FF FE 00 00}][string_to_utf32le [unicode_string]] utf-32le } -result \uFEFF[unicode_string] #>>> test decode-9.1 {Decode utf-32be, no BOM, explicit encoding, force manual decode} -body { #<<< json decode [string_to_utf32be [unicode_string]] utf-32be } -result [unicode_string] #>>> test decode-9.2 {Decode utf-32be, BOM, force manual decode} -body { #<<< json decode [binary decode hex {00 00 FE FF}][string_to_utf32be [unicode_string]] utf-32be } -result \uFEFF[unicode_string] #>>> test decode-10.1 {Decode utf-32le, no BOM, explicit encoding, force manual decode} -body { #<<< json decode [string_to_utf32le [unicode_string]] "x utf-32le" } -result [unicode_string] #>>> test decode-10.2 {Decode utf-32le, BOM, force manual decode} -body { #<<< json decode [binary decode hex {FF FE 00 00}][string_to_utf32le [unicode_string]] "x utf-32le" } -result \uFEFF[unicode_string] #>>> test decode-11.1 {Decode utf-32be, no BOM, explicit encoding, force manual decode} -body { #<<< json decode [string_to_utf32be [unicode_string]] "x utf-32be" } -result [unicode_string] #>>> test decode-11.2 {Decode utf-32be, BOM, force manual decode} -body { #<<< json decode [binary decode hex {00 00 FE FF}][string_to_utf32be [unicode_string]] "x utf-32be" } -result \uFEFF[unicode_string] #>>> ::tcltest::cleanupTests return # vim: ft=tcl foldmethod=marker foldmarker=<<<,>>> ts=4 shiftwidth=4 |
Changes to jni/rl_json/tests/exists.test.
︙ | ︙ | |||
107 108 109 110 111 112 113 114 115 116 117 118 119 120 | test exists-7.6 {exists for invalid value - truncated boolean: false} -body { #<<< json exists {fals} foo } -result 0 #>>> test exists-7.7 {exists for invalid value - truncated null} -body { #<<< json exists {nul} foo } -result 0 #>>> # Modifiers - ?type test exists-20.1 {type modifier, string} -body { #<<< json exists { { "foo": "bar", | > > > > > > > > > > > > > > > > > > > > | 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 | test exists-7.6 {exists for invalid value - truncated boolean: false} -body { #<<< json exists {fals} foo } -result 0 #>>> test exists-7.7 {exists for invalid value - truncated null} -body { #<<< json exists {nul} foo } -result 0 #>>> test exists-8.1 {exists on an atomic value - string} -body { #<<< json exists {"foo"} } -result 1 #>>> test exists-8.2 {exists on an atomic value - number} -body { #<<< json exists 123 } -result 1 #>>> test exists-8.3 {exists on an atomic value - bool: true} -body { #<<< json exists true } -result 1 #>>> test exists-8.4 {exists on an atomic value - bool: false} -body { #<<< json exists false } -result 1 #>>> test exists-8.5 {exists on an atomic value - null} -body { #<<< json exists null } -result 0 #>>> # Modifiers - ?type test exists-20.1 {type modifier, string} -body { #<<< json exists { { "foo": "bar", |
︙ | ︙ |
Changes to jni/rl_json/tests/foreach.test.
︙ | ︙ | |||
243 244 245 246 247 248 249 250 251 252 253 254 | {3("other var")(other var)/(null)()} \ 4(null)()/(null)() \ 5(true)(1)/(null)() \ "6(false)(0)/(null)()" \ "7({\"x\":42})(x 42)/(null)()" \ ] #>>> ::tcltest::cleanupTests return # vim: ft=tcl foldmethod=marker foldmarker=<<<,>>> ts=4 shiftwidth=4 | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 | {3("other var")(other var)/(null)()} \ 4(null)()/(null)() \ 5(true)(1)/(null)() \ "6(false)(0)/(null)()" \ "7({\"x\":42})(x 42)/(null)()" \ ] #>>> test foreach-20.1 {over string} -body { # TODO: perhaps allow this, and iterate over characters? <<< json foreach x {"foo"} { lappend res $x } set res } -cleanup { unset -nocomplain x res } -returnCodes error -result {Cannot iterate over JSON type string} #>>> test foreach-20.2 {over number} -body { #<<< json foreach x {123} { lappend res $x } set res } -cleanup { unset -nocomplain x res } -returnCodes error -result {Cannot iterate over JSON type number} #>>> test foreach-20.3 {over true} -body { #<<< json foreach x {true} { lappend res $x } set res } -cleanup { unset -nocomplain x res } -returnCodes error -result {Cannot iterate over JSON type boolean} #>>> test foreach-20.4 {over false} -body { #<<< json foreach x {false} { lappend res $x } set res } -cleanup { unset -nocomplain x res } -returnCodes error -result {Cannot iterate over JSON type boolean} #>>> test foreach-20.5 {too few args} -body { #<<< list [catch {json foreach x {[]}} r o] $r [dict get $o -errorcode] } -cleanup { unset -nocomplain x y r o } -result {1 {wrong # args: should be "*foreach ?varlist datalist ...? script"} {TCL WRONGARGS}} -match glob #>>> test foreach-20.6 {too many args} -body { #<<< list [catch {json foreach x {[]} y {}} r o] $r [dict get $o -errorcode] } -cleanup { unset -nocomplain x y r o } -result {1 {wrong # args: should be "*foreach ?varlist datalist ...? script"} {TCL WRONGARGS}} -match glob #>>> ::tcltest::cleanupTests return # vim: ft=tcl foldmethod=marker foldmarker=<<<,>>> ts=4 shiftwidth=4 |
Changes to jni/rl_json/tests/get.test.
︙ | ︙ | |||
875 876 877 878 879 880 881 882 883 884 885 886 887 | "baz": ["str", 123, 123.4, true, false, null, {"inner": "obj"}], "something": { "?type": "fromdoc" } } } foo blah } -returnCodes error -result {Cannot descend into atomic type "string" with path element 2: "blah"} #>>> ::tcltest::cleanupTests return # vim: ft=tcl foldmethod=marker foldmarker=<<<,>>> ts=4 shiftwidth=4 | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 | "baz": ["str", 123, 123.4, true, false, null, {"inner": "obj"}], "something": { "?type": "fromdoc" } } } foo blah } -returnCodes error -result {Cannot descend into atomic type "string" with path element 2: "blah"} #>>> test get-60.23 {invalid path: subscribed atomic: template invalid} -body { #<<< json get { { "foo": "~X:bar", "baz": ["str", 123, 123.4, true, false, null, {"inner": "obj"}], "something": { "?type": "fromdoc" } } } baz en } -returnCodes error -result {Expected an integer index or end(-integer)?, got en} #>>> test get-60.24 {invalid path: subscribed atomic: template invalid} -body { #<<< json get { { "foo": "~X:bar", "baz": ["str", 123, 123.4, true, false, null, {"inner": "obj"}], "something": { "?type": "fromdoc" } } } baz foo } -returnCodes error -result {Expected an integer index or end(-integer)?, got foo} #>>> test get-60.25 {Get a template point} -body { #<<< json get { { "foo": "~S:bar" } } foo } -result {~S:bar} #>>> ::tcltest::cleanupTests return # vim: ft=tcl foldmethod=marker foldmarker=<<<,>>> ts=4 shiftwidth=4 |
Deleted jni/rl_json/tests/get_typed.test.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Changes to jni/rl_json/tests/helpers.tcl.
︙ | ︙ | |||
8 9 10 11 12 13 14 | append msg ", at path $path" } throw {RL TEST JSON_MISMATCH} $msg } } try { | | | | 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | append msg ", at path $path" } throw {RL TEST JSON_MISMATCH} $msg } } try { json type $j1 } on error errmsg { apply $mismatch "Cannot parse left JSON value:\n$errmsg" } on ok j1_type {} try { json type $j2 } on error errmsg { apply $mismatch "Cannot parse right JSON value:\n$errmsg" } on ok j2_type {} set j1_val [json get $j1] set j2_val [json get $j2] |
︙ | ︙ |
Added jni/rl_json/tests/jsontestsuite.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 | if {"::tcltest" ni [namespace children]} { package require tcltest namespace import ::tcltest::* } package require rl_json namespace path {::rl_json} proc readbin fn { set h [open $fn rb] try {read $h} finally {close $h} } foreach file [glob -nocomplain -type f [file join [file dirname [info script]] JSONTestSuite test_parsing *.json]] { if {![regexp {/([yni])_([^/]+)\.json$} $file - expected name]} { puts stderr "Could not interpret test case filename \"$file\"" continue } set encoding auto # outcomes overrides: comments, etc switch -- $name { object_trailing_comment_slash_open - structure_object_with_comment - object_trailing_comment { # Comments are supported by default, but can be disabled - test both set expected comment } string_utf16BE_no_BOM { # UTF-16 files without a BOM need an out-of-band signal to indicate the encoding set encoding utf-16be } string_utf16LE_no_BOM { # UTF-16 files without a BOM need an out-of-band signal to indicate the encoding set encoding utf-16le } } set cmd [list test jsontestsuite-$name ${expected}_$name -match glob -result * -body] lappend cmd [string map [list %file% [list $file] %encoding% [list $encoding]] { json normalize [json decode [readbin %file%] %encoding%] }] if {$expected eq "n"} { lappend cmd -returnCodes error } {*}$cmd switch -- $expected { y - i { set cmd [list test jsontestsuite_valid-$name ${expected}_$name -result 1 -body] lappend cmd [string map [list %file% [list $file] %encoding% [list $encoding]] { json valid [json decode [readbin %file%] %encoding%] }] {*}$cmd } n { set cmd [list test jsontestsuite_valid-$name ${expected}_$name -result 0 -body] lappend cmd [string map [list %file% [list $file] %encoding% [list $encoding]] { json valid [json decode [readbin %file%] %encoding%] }] {*}$cmd } comment { set cmd [list test jsontestsuite_valid-$name-1 ${expected}_$name -result 0 -body] lappend cmd [string map [list %file% [list $file] %encoding% [list $encoding]] { json valid -extensions {} [json decode [readbin %file%] %encoding%] }] {*}$cmd set cmd [list test jsontestsuite_valid-$name-2 ${expected}_$name -result 1 -body] lappend cmd [string map [list %file% [list $file] %encoding% [list $encoding]] { json valid -extensions {comments} [json decode [readbin %file%] %encoding%] }] {*}$cmd } default { error "Unexpected expected value: \"$expected\"" } } } foreach file [glob -nocomplain -type f [file join [file dirname [info script]] JSONTestSuite test_transform *.json]] { if {![regexp {/([^/]+)\.json$} $file - name]} { puts stderr "Could not interpret test case filename \"$file\"" continue } set encoding auto set result [string trim [json decode [readbin $file]]] # Overrides for our intended behaviours in various situations switch -- $name { object_same_key_different_values { # Duplicate keys in object - later replaces earlier set result {{"a":2}} } object_same_key_unclear_values { set result {{"a":-0}} } object_same_key_same_value { set result {{"a":1}} } string_1_escaped_invalid_codepoint { # Replace each maximal subpart of the ill-formed sequence by a single U+FFFD (option 2 of http://unicode.org/review/pr-121.html) set result "\[\"\uFFFD\"\]" } string_2_escaped_invalid_codepoints { set result "\[\"\uFFFD\uFFFD\"\]" } string_3_escaped_invalid_codepoints { set result "\[\"\uFFFD\uFFFD\uFFFD\"\]" } default {} } set cmd [list test jsontestsuite-$name $name -result $result -body] lappend cmd [string map [list %file% [list $file] %encoding% [list $encoding]] { json normalize [json decode [readbin %file%] %encoding%] }] {*}$cmd } ::tcltest::cleanupTests return # vim: ft=tcl foldmethod=marker foldmarker=<<<,>>> ts=4 shiftwidth=4 |
Added jni/rl_json/tests/keys.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 | if {"::tcltest" ni [namespace children]} { package require tcltest namespace import ::tcltest::* } package require rl_json namespace path {::rl_json} test keys-0.1 {too few args} -body { #<<< set code [catch { json keys } r o] list $code $r [dict get $o -errorcode] } -cleanup { unset -nocomplain code r o } -result {1 {wrong # args: should be "*keys json_val ?path ...?"} {TCL WRONGARGS}} -match glob #>>> test keys-1.1 {type: object, no path} -body { #<<< json keys { { "foo": "bar", "baz": ["str", 123, 123.4, true, false, null, {"inner": "obj"}] } } } -result {foo baz} #>>> test keys-1.2 {type: array, no path} -body { #<<< json keys { [1,2,3] } } -returnCodes error -result {Named JSON value type isn't supported: array} #>>> test keys-1.3 {type: string, no path} -body { #<<< json keys { "foo" } } -returnCodes error -result {Named JSON value type isn't supported: string} #>>> test keys-1.4 {type: number, no path} -body { #<<< set code [catch { json keys { 1234 } } r o] list $code $r [dict get $o -errorcode] } -cleanup { unset -nocomplain code r o } -result {1 {Named JSON value type isn't supported: number} NONE} #>>> test keys-1.5 {type: boolean true, no path} -body { #<<< set code [catch { json keys { true } } r o] list $code $r [dict get $o -errorcode] } -cleanup { unset -nocomplain code r o } -result {1 {Named JSON value type isn't supported: boolean} NONE} #>>> test keys-1.6 {type: boolean false, no path} -body { #<<< set code [catch { json keys { false } } r o] list $code $r [dict get $o -errorcode] } -cleanup { unset -nocomplain code r o } -result {1 {Named JSON value type isn't supported: boolean} NONE} #>>> test keys-1.7 {type: null, no path} -body { #<<< set code [catch { json keys { null } } r o] list $code $r [dict get $o -errorcode] } -cleanup { unset -nocomplain code r o } -result {1 {Named JSON value type isn't supported: null} NONE} #>>> test keys-2.1 {type: object, path} -body { #<<< json keys { { "foo": "bar", "baz": ["str", 123, 123.4, true, false, null, {"inner": "obj", "key2": "foo"}] } } baz end } -result {inner key2} #>>> test keys-2.2 {type: array, path} -body { #<<< json keys { { "foo": "bar", "baz": ["str", 123, 123.4, true, false, null, {"inner": "obj"}] } } baz } -returnCodes error -result {Named JSON value type isn't supported: array} #>>> test keys-2.3 {type: string, path} -body { #<<< json keys { { "foo": "bar", "baz": ["str", 123, 123.4, true, false, null, {"inner": "obj"}] } } foo } -returnCodes error -result {Named JSON value type isn't supported: string} #>>> test keys-2.4 {type: number, path} -body { #<<< set code [catch { json keys { { "foo": "bar", "baz": ["str", 123, 123.4, true, false, null, {"inner": "obj"}] } } baz 2 } r o] list $code $r [dict get $o -errorcode] } -result {1 {Named JSON value type isn't supported: number} NONE} #>>> test keys-2.5 {type: boolean true, path} -body { #<<< set code [catch { json keys { { "foo": "bar", "baz": ["str", 123, 123.4, true, false, null, {"inner": "obj"}] } } baz 3 } r o] list $code $r [dict get $o -errorcode] } -result {1 {Named JSON value type isn't supported: boolean} NONE} #>>> test keys-2.6 {type: boolean false, path} -body { #<<< set code [catch { json keys { { "foo": "bar", "baz": ["str", 123, 123.4, true, false, null, {"inner": "obj"}] } } baz 4 } r o] list $code $r [dict get $o -errorcode] } -result {1 {Named JSON value type isn't supported: boolean} NONE} #>>> test keys-2.7 {type: null, path} -body { #<<< set code [catch { json keys { { "foo": "bar", "baz": ["str", 123, 123.4, true, false, null, {"inner": "obj"}] } } baz 5 } r o] list $code $r [dict get $o -errorcode] } -result {1 {Named JSON value type isn't supported: null} NONE} #>>> test keys-3.1 {type: invalid path} -body { #<<< set code [catch { json keys { { "foo": "bar", "baz": ["str", 123, 123.4, true, false, null, {"inner": "obj"}] } } baz 7 } r o] list $code $r [dict get $o -errorcode] } -result {1 {Named JSON value type isn't supported: null} NONE} #>>> test keys-3.2 {type: invalid path: invalid key} -body { #<<< set code [catch { json keys { { "foo": "bar", "baz": ["str", 123, 123.4, true, false, null, {"inner": "obj"}] } } quux } r o] list $code $r [dict get $o -errorcode] } -result {1 {Path element 2: "quux" not found} NONE} #>>> # Invalid paths test keys-60.1 {invalid path: missing key} -body { #<<< json keys { { "foo": "bar", "baz": ["str", 123, 123.4, true, false, null, {"inner": "obj"}], "something": { "?type": "fromdoc" } } } blah } -returnCodes error -result {Path element 2: "blah" not found} #>>> test keys-60.2 {invalid path: subscribed atomic: string} -body { #<<< json keys { { "foo": "bar", "baz": ["str", 123, 123.4, true, false, null, {"inner": "obj"}], "something": { "?type": "fromdoc" } } } foo blah } -returnCodes error -result {Cannot descend into atomic type "string" with path element 2: "blah"} #>>> test keys-60.3 {invalid path: subscribed atomic: number} -body { #<<< json keys { { "foo": "bar", "baz": ["str", 123, 123.4, true, false, null, {"inner": "obj"}], "something": { "?type": "fromdoc" } } } baz 1 blah } -returnCodes error -result {Cannot descend into atomic type "number" with path element 3: "blah"} #>>> test keys-60.4 {invalid path: subscribed atomic: boolean} -body { #<<< json keys { { "foo": "bar", "baz": ["str", 123, 123.4, true, false, null, {"inner": "obj"}], "something": { "?type": "fromdoc" } } } baz 3 blah } -returnCodes error -result {Cannot descend into atomic type "boolean" with path element 3: "blah"} #>>> test keys-60.5 {invalid path: subscribed atomic: null} -body { #<<< json keys { { "foo": "bar", "baz": ["str", 123, 123.4, true, false, null, {"inner": "obj"}], "something": { "?type": "fromdoc" } } } baz 5 blah } -returnCodes error -result {Cannot descend into atomic type "null" with path element 3: "blah"} #>>> test keys-60.6 {invalid path: invalid array index} -body { #<<< json keys { { "foo": "bar", "baz": ["str", 123, 123.4, true, false, null, {"inner": "obj"}], "something": { "?type": "fromdoc" } } } baz blah } -returnCodes error -result {Expected an integer index or end(-integer)?, got blah} #>>> test keys-60.7 {invalid path: invalid array index} -body { #<<< json keys { { "foo": "bar", "baz": ["str", 123, 123.4, true, false, null, {"inner": "obj"}], "something": { "?type": "fromdoc" } } } baz end-12 } -returnCodes error -result {Named JSON value type isn't supported: null} #>>> test keys-60.8 {invalid path: invalid array index} -body { #<<< json keys { { "foo": "bar", "baz": ["str", 123, 123.4, true, false, null, {"inner": "obj"}], "something": { "?type": "fromdoc" } } } baz 100 } -returnCodes error -result {Named JSON value type isn't supported: null} #>>> test keys-60.9 {invalid path: invalid array index} -body { #<<< json keys { { "foo": "bar", "baz": ["str", 123, 123.4, true, false, null, {"inner": "obj"}], "something": { "?type": "fromdoc" } } } baz -2 } -returnCodes error -result {Named JSON value type isn't supported: null} #>>> test keys-60.10 {invalid path: invalid array index} -body { #<<< json keys { { "foo": "bar", "baz": ["str", 123, 123.4, true, false, null, {"inner": "obj"}], "something": { "?type": "fromdoc" } } } baz end-2foo } -returnCodes error -result {Expected an integer index or end(-integer)?, got end-2foo} #>>> test keys-60.11 {invalid path: invalid array index} -body { #<<< json keys { { "foo": "bar", "baz": ["str", 123, 123.4, true, false, null, {"inner": "obj"}], "something": { "?type": "fromdoc" } } } baz end-foo } -returnCodes error -result {Expected an integer index or end(-integer)?, got end-foo} #>>> test keys-60.12 {invalid path: invalid array index} -body { #<<< json keys { { "foo": "bar", "baz": ["str", 123, 123.4, true, false, null, {"inner": "obj"}], "something": { "?type": "fromdoc" } } } baz end2 } -returnCodes error -result {Expected an integer index or end(-integer)?, got end2} #>>> test keys-60.13 {invalid path: invalid array index} -body { #<<< json keys { { "foo": "bar", "baz": ["str", 123, 123.4, true, false, null, {"inner": "obj"}], "something": { "?type": "fromdoc" } } } baz end+2 } -returnCodes error -result {Expected an integer index or end(-integer)?, got end+2} #>>> test keys-60.14 {invalid path: invalid array index} -body { #<<< json keys { { "foo": "bar", "baz": ["str", 123, 123.4, true, false, null, {"inner": "obj"}], "something": { "?type": "fromdoc" } } } baz "end- 2" } -returnCodes error -result {Expected an integer index or end(-integer)?, got end- 2} #>>> test keys-60.15 {invalid path: invalid array index} -body { #<<< json keys { { "foo": "bar", "baz": ["str", 123, 123.4, true, false, null, {"inner": "obj"}], "something": { "?type": "fromdoc" } } } baz end- } -returnCodes error -result {Expected an integer index or end(-integer)?, got end-} #>>> test keys-60.16 {invalid path: subscribed atomic: template string} -body { #<<< json keys { { "foo": "~S:bar", "baz": ["str", 123, 123.4, true, false, null, {"inner": "obj"}], "something": { "?type": "fromdoc" } } } foo blah } -returnCodes error -result {Cannot descend into atomic type "string" with path element 2: "blah"} #>>> test keys-60.17 {invalid path: subscribed atomic: template number} -body { #<<< json keys { { "foo": "~N:bar", "baz": ["str", 123, 123.4, true, false, null, {"inner": "obj"}], "something": { "?type": "fromdoc" } } } foo blah } -returnCodes error -result {Cannot descend into atomic type "string" with path element 2: "blah"} #>>> test keys-60.18 {invalid path: subscribed atomic: template boolean} -body { #<<< json keys { { "foo": "~B:bar", "baz": ["str", 123, 123.4, true, false, null, {"inner": "obj"}], "something": { "?type": "fromdoc" } } } foo blah } -returnCodes error -result {Cannot descend into atomic type "string" with path element 2: "blah"} #>>> test keys-60.19 {invalid path: subscribed atomic: template json doc} -body { #<<< json keys { { "foo": "~J:bar", "baz": ["str", 123, 123.4, true, false, null, {"inner": "obj"}], "something": { "?type": "fromdoc" } } } foo blah } -returnCodes error -result {Cannot descend into atomic type "string" with path element 2: "blah"} #>>> test keys-60.20 {invalid path: subscribed atomic: template json template} -body { #<<< json keys { { "foo": "~T:bar", "baz": ["str", 123, 123.4, true, false, null, {"inner": "obj"}], "something": { "?type": "fromdoc" } } } foo blah } -returnCodes error -result {Cannot descend into atomic type "string" with path element 2: "blah"} #>>> test keys-60.21 {invalid path: subscribed atomic: template literal} -body { #<<< json keys { { "foo": "~L:bar", "baz": ["str", 123, 123.4, true, false, null, {"inner": "obj"}], "something": { "?type": "fromdoc" } } } foo blah } -returnCodes error -result {Cannot descend into atomic type "string" with path element 2: "blah"} #>>> test keys-60.22 {invalid path: subscribed atomic: template invalid} -body { #<<< json keys { { "foo": "~X:bar", "baz": ["str", 123, 123.4, true, false, null, {"inner": "obj"}], "something": { "?type": "fromdoc" } } } foo blah } -returnCodes error -result {Cannot descend into atomic type "string" with path element 2: "blah"} #>>> test keys-70.1 {Path tail element isn't interpreted as a modifier - no need to escape} -body { #<<< json keys { { "foo": "bar", "baz": ["str", 123, 123.4, true, false, null, {"inner": "obj"}], "something": { "?type": {"fromdoc": "yes", "also this": "too", "another": null} } } } something ?type } -result {fromdoc {also this} another} #>>> ::tcltest::cleanupTests return # vim: ft=tcl foldmethod=marker foldmarker=<<<,>>> ts=4 shiftwidth=4 |
Added jni/rl_json/tests/length.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 | if {"::tcltest" ni [namespace children]} { package require tcltest namespace import ::tcltest::* } package require rl_json namespace path {::rl_json} test length-0.1 {too few args} -body { #<<< set code [catch { json length } r o] list $code $r [dict get $o -errorcode] } -cleanup { unset -nocomplain code r o } -result {1 {Wrong # of arguments. Must be "length json_val ?path ...?"} NONE} #>>> test length-1.1 {type: object, no path} -body { #<<< json length { { "foo": "bar", "baz": ["str", 123, 123.4, true, false, null, {"inner": "obj"}] } } } -result 2 #>>> test length-1.2 {type: array, no path} -body { #<<< json length { [1,2,3] } } -result 3 #>>> test length-1.3 {type: string, no path} -body { #<<< json length { "foo" } } -result 3 #>>> test length-1.3.1.1 {type: template string, no path} -body { #<<< json length { "~S:foo" } } -result 6 #>>> test length-1.3.1.2 {type: template number, no path} -body { #<<< json length { "~N:foo" } } -result 6 #>>> test length-1.3.1.3 {type: template boolean, no path} -body { #<<< json length { "~B:foo" } } -result 6 #>>> test length-1.3.1.4 {type: template json value, no path} -body { #<<< json length { "~J:foo" } } -result 6 #>>> test length-1.3.1.5 {type: nested template value, no path} -body { #<<< json length { "~T:foo" } } -result 6 #>>> test length-1.3.1.6 {type: template literal, no path} -body { #<<< json length { "~L:~S:foo" } } -result 9 #>>> test length-1.4 {type: number, no path} -body { #<<< set code [catch { json length { 1234 } } r o] list $code $r [dict get $o -errorcode] } -cleanup { unset -nocomplain code r o } -result {1 {Named JSON value type isn't supported: number} NONE} #>>> test length-1.5 {type: boolean true, no path} -body { #<<< set code [catch { json length { true } } r o] list $code $r [dict get $o -errorcode] } -cleanup { unset -nocomplain code r o } -result {1 {Named JSON value type isn't supported: boolean} NONE} #>>> test length-1.6 {type: boolean false, no path} -body { #<<< set code [catch { json length { false } } r o] list $code $r [dict get $o -errorcode] } -cleanup { unset -nocomplain code r o } -result {1 {Named JSON value type isn't supported: boolean} NONE} #>>> test length-1.7 {type: null, no path} -body { #<<< set code [catch { json length { null } } r o] list $code $r [dict get $o -errorcode] } -cleanup { unset -nocomplain code r o } -result {1 {Named JSON value type isn't supported: null} NONE} #>>> test length-2.1 {type: object, path} -body { #<<< json length { { "foo": "bar", "baz": ["str", 123, 123.4, true, false, null, {"inner": "obj"}] } } baz end } -result 1 #>>> test length-2.2 {type: array, path} -body { #<<< json length { { "foo": "bar", "baz": ["str", 123, 123.4, true, false, null, {"inner": "obj"}] } } baz } -result 7 #>>> test length-2.3 {type: string, path} -body { #<<< json length { { "foo": "bar", "baz": ["str", 123, 123.4, true, false, null, {"inner": "obj"}] } } foo } -result 3 #>>> test length-2.4 {type: number, path} -body { #<<< set code [catch { json length { { "foo": "bar", "baz": ["str", 123, 123.4, true, false, null, {"inner": "obj"}] } } baz 2 } r o] list $code $r [dict get $o -errorcode] } -result {1 {Named JSON value type isn't supported: number} NONE} #>>> test length-2.5 {type: boolean true, path} -body { #<<< set code [catch { json length { { "foo": "bar", "baz": ["str", 123, 123.4, true, false, null, {"inner": "obj"}] } } baz 3 } r o] list $code $r [dict get $o -errorcode] } -result {1 {Named JSON value type isn't supported: boolean} NONE} #>>> test length-2.6 {type: boolean false, path} -body { #<<< set code [catch { json length { { "foo": "bar", "baz": ["str", 123, 123.4, true, false, null, {"inner": "obj"}] } } baz 4 } r o] list $code $r [dict get $o -errorcode] } -result {1 {Named JSON value type isn't supported: boolean} NONE} #>>> test length-2.7 {type: null, path} -body { #<<< set code [catch { json length { { "foo": "bar", "baz": ["str", 123, 123.4, true, false, null, {"inner": "obj"}] } } baz 5 } r o] list $code $r [dict get $o -errorcode] } -result {1 {Named JSON value type isn't supported: null} NONE} #>>> test length-3.1 {type: invalid path} -body { #<<< set code [catch { json length { { "foo": "bar", "baz": ["str", 123, 123.4, true, false, null, {"inner": "obj"}] } } baz 7 } r o] list $code $r [dict get $o -errorcode] } -result {1 {Named JSON value type isn't supported: null} NONE} #>>> test length-3.2 {type: invalid path: invalid key} -body { #<<< set code [catch { json length { { "foo": "bar", "baz": ["str", 123, 123.4, true, false, null, {"inner": "obj"}] } } quux } r o] list $code $r [dict get $o -errorcode] } -result {1 {Path element 2: "quux" not found} NONE} #>>> # Invalid paths test length-60.1 {invalid path: missing key} -body { #<<< json length { { "foo": "bar", "baz": ["str", 123, 123.4, true, false, null, {"inner": "obj"}], "something": { "?type": "fromdoc" } } } blah } -returnCodes error -result {Path element 2: "blah" not found} #>>> test length-60.2 {invalid path: subscribed atomic: string} -body { #<<< json length { { "foo": "bar", "baz": ["str", 123, 123.4, true, false, null, {"inner": "obj"}], "something": { "?type": "fromdoc" } } } foo blah } -returnCodes error -result {Cannot descend into atomic type "string" with path element 2: "blah"} #>>> test length-60.3 {invalid path: subscribed atomic: number} -body { #<<< json length { { "foo": "bar", "baz": ["str", 123, 123.4, true, false, null, {"inner": "obj"}], "something": { "?type": "fromdoc" } } } baz 1 blah } -returnCodes error -result {Cannot descend into atomic type "number" with path element 3: "blah"} #>>> test length-60.4 {invalid path: subscribed atomic: boolean} -body { #<<< json length { { "foo": "bar", "baz": ["str", 123, 123.4, true, false, null, {"inner": "obj"}], "something": { "?type": "fromdoc" } } } baz 3 blah } -returnCodes error -result {Cannot descend into atomic type "boolean" with path element 3: "blah"} #>>> test length-60.5 {invalid path: subscribed atomic: null} -body { #<<< json length { { "foo": "bar", "baz": ["str", 123, 123.4, true, false, null, {"inner": "obj"}], "something": { "?type": "fromdoc" } } } baz 5 blah } -returnCodes error -result {Cannot descend into atomic type "null" with path element 3: "blah"} #>>> test length-60.6 {invalid path: invalid array index} -body { #<<< json length { { "foo": "bar", "baz": ["str", 123, 123.4, true, false, null, {"inner": "obj"}], "something": { "?type": "fromdoc" } } } baz blah } -returnCodes error -result {Expected an integer index or end(-integer)?, got blah} #>>> test length-60.7 {invalid path: invalid array index} -body { #<<< json length { { "foo": "bar", "baz": ["str", 123, 123.4, true, false, null, {"inner": "obj"}], "something": { "?type": "fromdoc" } } } baz end-12 } -returnCodes error -result {Named JSON value type isn't supported: null} #>>> test length-60.8 {invalid path: invalid array index} -body { #<<< json length { { "foo": "bar", "baz": ["str", 123, 123.4, true, false, null, {"inner": "obj"}], "something": { "?type": "fromdoc" } } } baz 100 } -returnCodes error -result {Named JSON value type isn't supported: null} #>>> test length-60.9 {invalid path: invalid array index} -body { #<<< json length { { "foo": "bar", "baz": ["str", 123, 123.4, true, false, null, {"inner": "obj"}], "something": { "?type": "fromdoc" } } } baz -2 } -returnCodes error -result {Named JSON value type isn't supported: null} #>>> test length-60.10 {invalid path: invalid array index} -body { #<<< json length { { "foo": "bar", "baz": ["str", 123, 123.4, true, false, null, {"inner": "obj"}], "something": { "?type": "fromdoc" } } } baz end-2foo } -returnCodes error -result {Expected an integer index or end(-integer)?, got end-2foo} #>>> test length-60.11 {invalid path: invalid array index} -body { #<<< json length { { "foo": "bar", "baz": ["str", 123, 123.4, true, false, null, {"inner": "obj"}], "something": { "?type": "fromdoc" } } } baz end-foo } -returnCodes error -result {Expected an integer index or end(-integer)?, got end-foo} #>>> test length-60.12 {invalid path: invalid array index} -body { #<<< json length { { "foo": "bar", "baz": ["str", 123, 123.4, true, false, null, {"inner": "obj"}], "something": { "?type": "fromdoc" } } } baz end2 } -returnCodes error -result {Expected an integer index or end(-integer)?, got end2} #>>> test length-60.13 {invalid path: invalid array index} -body { #<<< json length { { "foo": "bar", "baz": ["str", 123, 123.4, true, false, null, {"inner": "obj"}], "something": { "?type": "fromdoc" } } } baz end+2 } -returnCodes error -result {Expected an integer index or end(-integer)?, got end+2} #>>> test length-60.14 {invalid path: invalid array index} -body { #<<< json length { { "foo": "bar", "baz": ["str", 123, 123.4, true, false, null, {"inner": "obj"}], "something": { "?type": "fromdoc" } } } baz "end- 2" } -returnCodes error -result {Expected an integer index or end(-integer)?, got end- 2} #>>> test length-60.15 {invalid path: invalid array index} -body { #<<< json length { { "foo": "bar", "baz": ["str", 123, 123.4, true, false, null, {"inner": "obj"}], "something": { "?type": "fromdoc" } } } baz end- } -returnCodes error -result {Expected an integer index or end(-integer)?, got end-} #>>> test length-60.16 {invalid path: subscribed atomic: template string} -body { #<<< json length { { "foo": "~S:bar", "baz": ["str", 123, 123.4, true, false, null, {"inner": "obj"}], "something": { "?type": "fromdoc" } } } foo blah } -returnCodes error -result {Cannot descend into atomic type "string" with path element 2: "blah"} #>>> test length-60.17 {invalid path: subscribed atomic: template number} -body { #<<< json length { { "foo": "~N:bar", "baz": ["str", 123, 123.4, true, false, null, {"inner": "obj"}], "something": { "?type": "fromdoc" } } } foo blah } -returnCodes error -result {Cannot descend into atomic type "string" with path element 2: "blah"} #>>> test length-60.18 {invalid path: subscribed atomic: template boolean} -body { #<<< json length { { "foo": "~B:bar", "baz": ["str", 123, 123.4, true, false, null, {"inner": "obj"}], "something": { "?type": "fromdoc" } } } foo blah } -returnCodes error -result {Cannot descend into atomic type "string" with path element 2: "blah"} #>>> test length-60.19 {invalid path: subscribed atomic: template json doc} -body { #<<< json length { { "foo": "~J:bar", "baz": ["str", 123, 123.4, true, false, null, {"inner": "obj"}], "something": { "?type": "fromdoc" } } } foo blah } -returnCodes error -result {Cannot descend into atomic type "string" with path element 2: "blah"} #>>> test length-60.20 {invalid path: subscribed atomic: template json template} -body { #<<< json length { { "foo": "~T:bar", "baz": ["str", 123, 123.4, true, false, null, {"inner": "obj"}], "something": { "?type": "fromdoc" } } } foo blah } -returnCodes error -result {Cannot descend into atomic type "string" with path element 2: "blah"} #>>> test length-60.21 {invalid path: subscribed atomic: template literal} -body { #<<< json length { { "foo": "~L:bar", "baz": ["str", 123, 123.4, true, false, null, {"inner": "obj"}], "something": { "?type": "fromdoc" } } } foo blah } -returnCodes error -result {Cannot descend into atomic type "string" with path element 2: "blah"} #>>> test length-60.22 {invalid path: subscribed atomic: template invalid} -body { #<<< json length { { "foo": "~X:bar", "baz": ["str", 123, 123.4, true, false, null, {"inner": "obj"}], "something": { "?type": "fromdoc" } } } foo blah } -returnCodes error -result {Cannot descend into atomic type "string" with path element 2: "blah"} #>>> test length-70.1 {Path tail element isn't interpreted as a modifier - no need to escape} -body { #<<< json length { { "foo": "bar", "baz": ["str", 123, 123.4, true, false, null, {"inner": "obj"}], "something": { "?type": "fromdoc" } } } something ?type } -result 7 #>>> ::tcltest::cleanupTests return # vim: ft=tcl foldmethod=marker foldmarker=<<<,>>> ts=4 shiftwidth=4 |
Changes to jni/rl_json/tests/lmap.test.
1 2 3 4 5 6 7 8 9 | if {"::tcltest" ni [namespace children]} { package require tcltest namespace import ::tcltest::* } package require rl_json namespace path {::rl_json} test lmap-1.0 {Single iterator, no vars, over array} -body { #<<< | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | if {"::tcltest" ni [namespace children]} { package require tcltest namespace import ::tcltest::* } package require rl_json namespace path {::rl_json} test lmap-1.0 {Single iterator, no vars, over array} -body { #<<< json lmap {} {["a", "b", "other var", null, true, false, {"x": 42}]} {} } -returnCodes error -result "foreach varlist is empty" #>>> test lmap-1.1 {Single iterator, single var, over array} -setup { #<<< set i 0 } -body { json lmap elem {["a", "b", "other var", null, true, false, {"x": 42}]} { string cat [incr i]($elem)([json get $elem]) |
︙ | ︙ | |||
113 114 115 116 117 118 119 | "2(\"b\")(b)" \ "3(\"other var\")(other var)" \ "4(true)(1)" \ "5(false)(0)" \ "6({\"x\":42})(x 42)" \ ] #>>> | | | 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 | "2(\"b\")(b)" \ "3(\"other var\")(other var)" \ "4(true)(1)" \ "5(false)(0)" \ "6({\"x\":42})(x 42)" \ ] #>>> test lmap-5.1.1 {Single iterator, single var, over array, continue on final iteration} -setup { #<<< set i 0 } -body { json lmap elem {["a", "b", "other var", null]} { if {[json get $elem] eq ""} continue string cat [incr i]($elem)([json get $elem]) } } -cleanup { |
︙ | ︙ | |||
222 223 224 225 226 227 228 229 230 231 232 233 | {3("other var")(other var)/(null)()} \ 4(null)()/(null)() \ 5(true)(1)/(null)() \ "6(false)(0)/(null)()" \ "7({\"x\":42})(x 42)/(null)()" \ ] #>>> ::tcltest::cleanupTests return # vim: ft=tcl foldmethod=marker foldmarker=<<<,>>> ts=4 shiftwidth=4 | > > > > > > > > > > > > > | 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 | {3("other var")(other var)/(null)()} \ 4(null)()/(null)() \ 5(true)(1)/(null)() \ "6(false)(0)/(null)()" \ "7({\"x\":42})(x 42)/(null)()" \ ] #>>> test lmap-20.5 {too few args} -body { #<<< list [catch {json lmap x {[]}} r o] $r [dict get $o -errorcode] } -cleanup { unset -nocomplain x y r o } -result {1 {wrong # args: should be "*lmap ?varlist datalist ...? script"} {TCL WRONGARGS}} -match glob #>>> test lmap-20.6 {too many args} -body { #<<< list [catch {json lmap x {[]} y {}} r o] $r [dict get $o -errorcode] } -cleanup { unset -nocomplain x y r o } -result {1 {wrong # args: should be "*lmap ?varlist datalist ...? script"} {TCL WRONGARGS}} -match glob #>>> ::tcltest::cleanupTests return # vim: ft=tcl foldmethod=marker foldmarker=<<<,>>> ts=4 shiftwidth=4 |
Changes to jni/rl_json/tests/memory.test.
1 2 3 4 5 6 7 8 | if {"::tcltest" ni [namespace children]} { package require tcltest namespace import ::tcltest::* } package require rl_json namespace path {::rl_json} | > > > > > > > > > | | | | | | | | | | | | | > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | if {"::tcltest" ni [namespace children]} { package require tcltest namespace import ::tcltest::* } package require rl_json namespace path {::rl_json} if {$tcl_platform(platform) eq "windows" && ![catch {package require twapi}]} { proc my_rss {} { # Not clear what would correspond to VMRss on Linux. # -workingset is too volatile. -pagefilebytes seems # a little better but even that has variations # (positive and negative) lindex [twapi::get_process_info -pagefilebytes] 1 } } else { proc my_rss {} { # Linux only! Need alternatives for Mac, Win <<< set h [open /proc/self/statm r] try { read $h } on ok statm { expr { [lindex $statm 1] * 4 } } finally { close $h } } #>>> } test memory-1.1 {memory leak with static object} -setup { #<<< proc test_memory-1.1 it { for {set i 0} {$i < $it} {incr i} { set doc "{}" json set doc foo null } |
︙ | ︙ | |||
188 189 190 191 192 193 194 195 196 197 198 199 | } -body { test_memory-2.1 expr {[my_rss] - $before} } -cleanup { unset -nocomplain before rename test_memory-2.1 {} } -result 0 ::tcltest::cleanupTests return # vim: ft=tcl foldmethod=marker foldmarker=<<<,>>> ts=4 shiftwidth=4 | > > > > > > > > > > > > > > > > > > > > > > > > > > > | 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 | } -body { test_memory-2.1 expr {[my_rss] - $before} } -cleanup { unset -nocomplain before rename test_memory-2.1 {} } -result 0 #>>> test memory-2.2 {deeply nested array error} -setup { #<<< proc test_memory-2.2 {} { json normalize [string repeat "\[" 10000] } # Warm caches, overhead, etc catch {test_memory-2.2} set before [my_rss] } -body { set code [catch {test_memory-2.2} r o] list [expr {[my_rss] - $before}] $code $r } -cleanup { unset -nocomplain before code r o rename test_memory-2.2 {} } -match glob -result {0 1 {Error parsing JSON value: Unterminated array at offset *}} #>>> if {[llength [info commands ::memtest2]]} { memtest2 memory-3.1 {Deliberate memory leak} -constraints {} -body { #<<< json _leak_obj } -returnCodes error -match regexp -result {^Leaked memory:\n\t0x[0-9a-f]+\s+-\s+0x[0-9a-f]+\s+[0-9]+\s+@\s+\./generic/rl_json.c\s+[0-9]+\s+memory-3.1$} #>>> } test memory-3.2 {Deliberate memory leak} -constraints {} -body { #<<< json _leak_obj } -result {} #>>> ::tcltest::cleanupTests return # vim: ft=tcl foldmethod=marker foldmarker=<<<,>>> ts=4 shiftwidth=4 |
Added jni/rl_json/tests/misc.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | if {"::tcltest" ni [namespace children]} { package require tcltest namespace import ::tcltest::* } package require rl_json namespace path {::rl_json} test misc-1.1 {too few args} -body { #<<< list [catch {json} r o] $r [dict get $o -errorcode] } -cleanup { unset -nocomplain r o } -result {1 {wrong # args: should be "*subcommand ?arg ...?"} {TCL WRONGARGS}} -match glob #>>> test misc-1.2 {invalid subcommand} -body { #<<< list [catch {json invalid_subcommand} r o] [dict get $o -errorcode] } -cleanup { unset -nocomplain r o } -result {^1 {(?:TCL LOOKUP SUBCOMMAND invalid_subcommand|TCL LOOKUP INDEX subcommand invalid_subcommand)}$} -match regexp #>>> test misc-2.1 {isnull, no path, true} -body { #<<< json isnull null } -result 1 #>>> test misc-2.2 {isnull, no path, false} -body { #<<< json isnull 123 } -result 0 #>>> test misc-2.3 {isnull, path, true} -body { #<<< json isnull {["a",null,"c"]} 1 } -result 1 #>>> test misc-2.4 {isnull, path, false} -body { #<<< json isnull {["a",null,"c"]} 2 } -result 0 #>>> test misc-2.5 {isnull, path, out of array bounds} -body { #<<< json isnull {["a",null,"c"]} 3 } -result 1 #>>> test misc-2.6 {isnull, path, out of array bounds} -body { #<<< json isnull {["a",null,"c"]} -1 } -result 1 #>>> test misc-3.1 {interp free} -body { #<<< set slave [interp create] $slave eval {load {} Rl_json; rl_json::json get {["hello","slave"]}} } -cleanup { interp delete $slave unset -nocomplain slave } -result {hello slave} #>>> test misc-3.2 {interp free, safe interp} -body { #<<< set slave [interp create -safe] $slave invokehidden load {} Rl_json $slave eval {rl_json::json get {["hello","slave"]}} } -cleanup { interp delete $slave unset -nocomplain slave } -result {hello slave} #>>> ::tcltest::cleanupTests return # vim: ft=tcl foldmethod=marker foldmarker=<<<,>>> ts=4 shiftwidth=4 |
Changes to jni/rl_json/tests/new.test.
1 2 3 4 5 6 7 8 9 10 11 12 | if {"::tcltest" ni [namespace children]} { package require tcltest namespace import ::tcltest::* } package require rl_json source [file join [file dirname [info script]] helpers.tcl] test new-1.1 {Create an array from type-value pairs} -setup { #<<< set typevalues [lmap v {a 1 c 2 e 3 g} { if {[string is digit $v]} { | | | | > > > > > > > > > > > > > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 | if {"::tcltest" ni [namespace children]} { package require tcltest namespace import ::tcltest::* } package require rl_json source [file join [file dirname [info script]] helpers.tcl] test new-1.1 {Create an array from type-value pairs} -setup { #<<< set typevalues [lmap v {a 1 c 2 e 3 g} { if {[string is digit $v]} { list number $v } else { list string $v } }] } -body { json new array {*}$typevalues } -cleanup { unset -nocomplain typevalues } -result {["a",1,"c",2,"e",3,"g"]} #>>> test new-1.1.1 {Create an array from type-value pairs, syntax error in elem (too many args)} -setup { #<<< set typevalues [lmap v {a 1 c 2 e 3 g} { if {[string is digit $v]} { list number $v x } else { list string $v } }] } -body { json new array {*}$typevalues } -cleanup { unset -nocomplain typevalues } -returnCodes error -result {wrong # args: should be "number value"} #>>> test new-1.2 {Create an array from JSON values} -setup { #<<< set values [lmap v {a 1 c 2 e 3 g} { if {[string is digit $v]} { json new number $v; } else { json new string $v; } }]; } -body { json new array {*}[lmap v $values { list json $v; }]; } -cleanup { unset -nocomplain values v; } -result {["a",1,"c",2,"e",3,"g"]} #>>> test new-2.1 {json string, plain string} -body { #<<< json string new-2.1 } -result {"new-2.1"} #>>> test new-2.2 {json string, too few args} -body { #<<< set code [catch {json string} r o] list $code $r [dict get $o -errorcode] } -cleanup { unset -nocomplain code r o } -result [list 1 {wrong # args: should be "*string value"} {TCL WRONGARGS}] -match glob #>>> test new-2.3 {json string, too many args} -body { #<<< set code [catch {json string foo bar} r o] list $code $r [dict get $o -errorcode] } -cleanup { unset -nocomplain code r o } -result [list 1 {wrong # args: should be "*string value"} {TCL WRONGARGS}] -match glob #>>> test new-2.4 {json string, template string} -body { #<<< json string ~S:foo } -result {"~S:foo"} #>>> test new-3.1 {json number, int} -body { #<<< json number 1234 } -result 1234 #>>> test new-3.6 {json number, float} -body { #<<< json number 1234.4 } -result 1234.4 #>>> test new-4.1.1 {json boolean, truthy int} -body { #<<< json boolean 1234 } -result true #>>> test new-4.1.2 {json boolean, truthy string: true} -body { #<<< json boolean true } -result true #>>> test new-4.1.3 {json boolean, truthy string: tr} -body { #<<< json boolean tr } -result true #>>> test new-4.1.4 {json boolean, truthy string: yes} -body { #<<< json boolean yes } -result true #>>> test new-4.1.5 {json boolean, truthy string: on} -body { #<<< json boolean on } -result true #>>> test new-4.1.6 {json boolean, truthy int: -1} -body { #<<< json boolean -1 } -result true #>>> test new-4.1.7 {json boolean, truthy int} -body { #<<< json boolean 0 } -result false #>>> test new-4.1.8 {json boolean, truthy string: false} -body { #<<< json boolean false } -result false #>>> test new-4.1.9 {json boolean, truthy string: fal} -body { #<<< json boolean fal } -result false #>>> test new-4.1.10 {json boolean, truthy string: no} -body { #<<< json boolean no } -result false #>>> test new-4.1.11 {json boolean, truthy string: off} -body { #<<< json boolean off } -result false #>>> test new-4.2 {json boolean, too few args} -body { #<<< set code [catch {json boolean} r o] list $code $r [dict get $o -errorcode] } -cleanup { unset -nocomplain code r o } -result [list 1 {wrong # args: should be "*boolean value"} {TCL WRONGARGS}] -match glob #>>> test new-4.3 {json boolean, too many args} -body { #<<< set code [catch {json boolean foo bar} r o] list $code $r [dict get $o -errorcode] } -cleanup { unset -nocomplain code r o } -result [list 1 {wrong # args: should be "*boolean value"} {TCL WRONGARGS}] -match glob #>>> test new-4.4 {json boolean, invalid string} -body { #<<< set code [catch {json boolean foo} r o] list $code $r [dict get $o -errorcode] } -cleanup { unset -nocomplain code r o } -result [list 1 {expected boolean value but got "foo"} {TCL VALUE NUMBER}] #>>> test new-4.5 {json boolean, empty string} -body { #<<< set code [catch {json boolean ""} r o] list $code $r [dict get $o -errorcode] } -cleanup { unset -nocomplain code r o } -result [list 1 {expected boolean value but got ""} {TCL VALUE NUMBER}] #>>> test new-4.6 {json boolean, float} -body { #<<< json boolean 1234.4 } -result true #>>> test new-4.7 {json boolean, scientific notation} -body { #<<< json boolean 1e6 } -result true #>>> test new-5.1.1 {Create an object - args} -body { #<<< json new object a {number 1} c {num 2} e {num 3} g {num 4} } -result {{"a":1,"c":2,"e":3,"g":4}} #>>> test new-5.1.2 {Create an object - single arg} -body { #<<< json new object {a {number 1} c {num 2} e {num 3} g {num 4}} } -result {{"a":1,"c":2,"e":3,"g":4}} #>>> test new-6.1 {Create a string} -body { #<<< json new string "hello, world" } -result {"hello, world"} #>>> test new-6.2 {Create a template: string} -body { #<<< json new string "~S:foo" } -result {"~S:foo"} #>>> test new-6.3 {Create a template: number} -body { #<<< json new string "~N:foo" } -result {"~N:foo"} #>>> test new-6.4 {Create a template: boolean} -body { #<<< json new string "~B:foo" } -result {"~B:foo"} #>>> test new-6.5 {Create a template: json} -body { #<<< json new string "~J:foo" } -result {"~J:foo"} #>>> test new-6.6 {Create a template: template} -body { #<<< json new string "~T:foo" } -result {"~T:foo"} #>>> test new-6.7 {Create a template: literal} -body { #<<< json new string "~L:~S:foo" } -result {"~L:~S:foo"} #>>> test new-6.7 {Create a template: undef} -body { #<<< json new string "~X:foo" } -result {"~X:foo"} #>>> test new-6.8 {Create new bool: true} -body { #<<< json new true } -result true #>>> test new-6.9 {Create new bool: false} -body { #<<< json new false } -result false #>>> test new-6.10.1 {Create new bool: value} -body { #<<< json new bool } -returnCodes error -result {Wrong # of arguments. Must be "boolean val"} #>>> test new-6.10.2 {Create new bool: value} -body { #<<< json new bool true false } -returnCodes error -result {Wrong # of arguments. Must be "boolean val"} #>>> test new-6.10.3 {Create new bool: value} -body { #<<< json new bool true } -result true #>>> test new-6.10.4 {Create new bool: value} -body { #<<< json new bool tr } -result true #>>> test new-6.10.5 {Create new bool: value} -body { #<<< json new bool false } -result false #>>> test new-6.10.6 {Create new bool: value} -body { #<<< json new bool no } -result false #>>> test new-6.10.7 {Create new bool: value} -body { #<<< json new bool 0 } -result false #>>> test new-6.10.8 {Create new bool: value} -body { #<<< json new bool -42 } -result true #>>> test new-6.11 {Create new null} -body { #<<< json new null } -result null #>>> ::tcltest::cleanupTests return # Local Variables: # mode: tcl # tab-width: 4 # End: # vim: ft=tcl foldmethod=marker foldmarker=<<<,>>> ts=4 shiftwidth=4 |
Added jni/rl_json/tests/number.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 | if {"::tcltest" ni [namespace children]} { package require tcltest namespace import ::tcltest::* } package require rl_json test number-1.1.1 {Create a json number: 1 (was native number} -body { #<<< set n 1 expr {$n+0} list [json number $n] $n } -cleanup { unset -nocomplain n } -result [list 1 1] #>>> test number-1.1.2 {Create a json number: 123 (was string} -body { #<<< set n [string trim " 1"] list [json number $n] $n } -cleanup { unset -nocomplain n } -result [list 1 1] #>>> test number-1.2 {Create a json number: 0} -body { #<<< json number 0 } -result 0 #>>> test number-1.3 {Create a json number: real} -body { #<<< json number 42.1 } -result 42.1 #>>> test number-1.4.1 {Create a json number: scientific notation, native double} -body { #<<< set n 1e6 expr {$n+0} list [json number $n] $n } -result {1e6 1e6} #>>> test number-1.4.2 {Create a json number: scientific notation, string} -body { #<<< set n [string trim " 1e6"] list [json number $n] $n } -result {1000000.0 1e6} #>>> test number-1.5 {Create a max 32 bit signed int} -body { #<<< json number [expr {2**31-1}] } -result 2147483647 #>>> test number-1.6 {Create a min 32 bit signed int} -body { #<<< json number [expr {-1*(2**31)}] } -result -2147483648 #>>> test number-1.7 {Create a max 32 bit unsigned int} -body { #<<< json number [expr {2**32-1}] } -result 4294967295 #>>> test number-1.8 {Create a max 64 bit signed int} -body { #<<< json number [expr {2**63-1}] } -result 9223372036854775807 #>>> test number-1.9 {Create a min 64 bit signed int} -body { #<<< json number [expr {-1*(2**63)}] } -result -9223372036854775808 #>>> test number-1.10 {Create a max 64 bit unsigned int} -body { #<<< json number [expr {2**64-1}] } -result 18446744073709551615 #>>> test number-1.11 {Create an positive bignum} -body { #<<< json number [expr {2**1000-1}] } -result 10715086071862673209484250490600018105614048117055336074437503883703510511249361224931983788156958581275946729175531468251871452856923140435984577574698574803934567774824230985421074605062371141877954182153046474983581941267398767559165543946077062914571196477686542167660429831652624386837205668069375 #>>> test number-1.12 {Create an negative bignum} -body { #<<< json number [expr {-1*(2**1000-1)}] } -result -10715086071862673209484250490600018105614048117055336074437503883703510511249361224931983788156958581275946729175531468251871452856923140435984577574698574803934567774824230985421074605062371141877954182153046474983581941267398767559165543946077062914571196477686542167660429831652624386837205668069375 #>>> test number-1.13 {string} -body { #<<< json number [string cat 4 2] } -result 42 #>>> test number-1.14.1 {positive octal} -body { #<<< set n 077 expr {$n+0} ;# Convert to number type list [json number $n] $n } -cleanup { unset -nocomplain n } -result {63 077} #>>> test number-1.14.2 {negative octal} -body { #<<< set n -077 expr {$n+0} ;# Convert to number type list [json number $n] $n } -cleanup { unset -nocomplain n } -result {-63 -077} #>>> test number-1.15.1 {positive hex} -body { #<<< set n 0xA0 expr {$n+0} ;# Convert to number type list [json number $n] $n } -cleanup { unset -nocomplain n } -result {160 0xA0} #>>> test number-1.15.2 {negative hex} -body { #<<< set n -0xA0 expr {$n+0} ;# Convert to number type list [json number $n] $n } -cleanup { unset -nocomplain n } -result {-160 -0xA0} #>>> test number-1.16.1 {positive double, valid leading 0} -body { #<<< set n 0.5e3 expr {$n+0} ;# Convert to number type list [json number $n] $n } -cleanup { unset -nocomplain n } -result {0.5e3 0.5e3} #>>> test number-1.16.2 {negative double, valid leading 0} -body { #<<< set n -0.5e3 expr {$n+0} ;# Convert to number type list [json number $n] $n } -cleanup { unset -nocomplain n } -result {-0.5e3 -0.5e3} #>>> test number-1.17.1 {trailing whitespace: ' '} -body { #<<< set n {42 } expr {$n+0} ;# Convert to number type list [json number $n] $n } -cleanup { unset -nocomplain n } -result [list 42 {42 }] #>>> test number-1.17.2 {trailing whitespace: '\n'} -body { #<<< set n 42\n expr {$n+0} ;# Convert to number type list [json number $n] $n } -cleanup { unset -nocomplain n } -result [list 42 42\n] #>>> test number-1.17.3 {trailing whitespace: '\t'} -body { #<<< set n 42\t expr {$n+0} ;# Convert to number type list [json number $n] $n } -cleanup { unset -nocomplain n } -result [list 42 42\t] #>>> test number-1.17.4 {trailing whitespace: '\v'} -body { #<<< set n 42\v expr {$n+0} ;# Convert to number type list [json number $n] $n } -cleanup { unset -nocomplain n } -result [list 42 42\v] #>>> test number-1.17.5 {trailing whitespace: '\r'} -body { #<<< set n 42\r expr {$n+0} ;# Convert to number type list [json number $n] $n } -cleanup { unset -nocomplain n } -result [list 42 42\r] #>>> test number-1.17.6 {trailing whitespace: '\f'} -body { #<<< set n 42\f expr {$n+0} ;# Convert to number type list [json number $n] $n } -cleanup { unset -nocomplain n } -result [list 42 42\f] #>>> test number-1.18.1 {leading whitespace: ' '} -body { #<<< set n { 42} expr {$n+0} ;# Convert to number type list [json number $n] $n } -cleanup { unset -nocomplain n } -result [list 42 { 42}] #>>> test number-1.18.2 {leading whitespace: '\n'} -body { #<<< set n \n42 expr {$n+0} ;# Convert to number type list [json number $n] $n } -cleanup { unset -nocomplain n } -result [list 42 \n42] #>>> test number-1.18.3 {leading whitespace: '\t'} -body { #<<< set n \t42 expr {$n+0} ;# Convert to number type list [json number $n] $n } -cleanup { unset -nocomplain n } -result [list 42 \t42] #>>> test number-1.18.4 {leading whitespace: '\v'} -body { #<<< set n \v42 expr {$n+0} ;# Convert to number type list [json number $n] $n } -cleanup { unset -nocomplain n } -result [list 42 \v42] #>>> test number-1.18.5 {leading whitespace: '\r'} -body { #<<< set n \r42 expr {$n+0} ;# Convert to number type list [json number $n] $n } -cleanup { unset -nocomplain n } -result [list 42 \r42] #>>> test number-1.18.6 {leading whitespace: '\f'} -body { #<<< set n \f42 expr {$n+0} ;# Convert to number type list [json number $n] $n } -cleanup { unset -nocomplain n } -result [list 42 \f42] #>>> test number-2.1 {Too few args} -body { #<<< set code [catch { json number } r o] list $code $r [dict get $o -errorcode] } -result {1 {wrong # args: should be "*number value"} {TCL WRONGARGS}} -match glob #>>> test number-2.2 {Too many args} -body { #<<< set code [catch { json number foo bar } r o] list $code $r [dict get $o -errorcode] } -result {1 {wrong # args: should be "*number value"} {TCL WRONGARGS}} -match glob #>>> test number-2.3 {json number, not a number} -body { #<<< set code [catch {json number foo} r o] list $code $r [dict get $o -errorcode] } -cleanup { unset -nocomplain code r o } -result [list 1 {can't use non-numeric string as operand of "+"} {ARITH DOMAIN {non-numeric string}}] #>>> test number-2.4 {json number, not a number: empty string} -body { #<<< set code [catch {json number ""} r o] list $code $r [dict get $o -errorcode] } -cleanup { unset -nocomplain code r o } -result [list 1 {can't use empty string as operand of "+"} {ARITH DOMAIN {empty string}}] #>>> ::tcltest::cleanupTests return # Local Variables: # mode: tcl # tab-width: 4 # End: # vim: ft=tcl foldmethod=marker foldmarker=<<<,>>> ts=4 shiftwidth=4 |
Added jni/rl_json/tests/object.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | if {"::tcltest" ni [namespace children]} { package require tcltest namespace import ::tcltest::* } package require rl_json source [file join [file dirname [info script]] helpers.tcl] test object-1.1 {Create an object - args} -body { #<<< json object a {number 1} c {num 2} e {num 3} g {num 4} } -result {{"a":1,"c":2,"e":3,"g":4}} #>>> test object-1.2 {Create an object - single arg} -body { #<<< json object {a {number 1} c {num 2} e {num 3} g {num 4}} } -result {{"a":1,"c":2,"e":3,"g":4}} #>>> ::tcltest::cleanupTests return # Local Variables: # mode: tcl # tab-width: 4 # End: # vim: ft=tcl foldmethod=marker foldmarker=<<<,>>> ts=4 shiftwidth=4 |
Added jni/rl_json/tests/omap.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 | if {"::tcltest" ni [namespace children]} { package require tcltest namespace import ::tcltest::* } package require rl_json namespace path {::rl_json} test omap-1.0 {Single iterator, no vars, over array} -body { #<<< json omap {} {["a", "b", "other var", null, true, false, {"x": 42}]} {} } -returnCodes error -result "foreach varlist is empty" #>>> test omap-1.1 {Single iterator, single var, over array} -setup { #<<< set i 0 } -body { json omap elem {["a", "b", "other var", null, true, false, {"x": 42}]} { list [incr i] ($elem)([json get $elem]) } } -cleanup { unset -nocomplain i elem } -result {{"1":"(\"a\")(a)","2":"(\"b\")(b)","3":"(\"other var\")(other var)","4":"(null)()","5":"(true)(1)","6":"(false)(0)","7":"({\"x\":42})(x 42)"}} #>>> test omap-2.1 {Single iterator, multi vars, over array} -setup { #<<< set i 0 } -body { json omap {x y} {["a", "b", "other var", null, true, false, {"x": 42}, 42.1, "last"]} { list [incr i] "($x)([json get $x])($y)([json get $y])" } } -cleanup { unset -nocomplain i x y } -result {{"1":"(\"a\")(a)(\"b\")(b)","2":"(\"other var\")(other var)(null)()","3":"(true)(1)(false)(0)","4":"({\"x\":42})(x 42)(42.1)(42.1)","5":"(\"last\")(last)(null)()"}} #>>> test omap-3.0 {Multiple iterators, no vars, over array} -body { #<<< json omap \ elem {["a", "b", "other var", null, true, false, {"x": 42}]} \ {} {["A", "B", "OTHER VAR", null, true, {"X": 42}]} \ {} } -cleanup { unset -nocomplain elem } -returnCodes error -result "foreach varlist is empty" #>>> test omap-3.1 {Multiple iterators, single var, over array} -setup { #<<< set i 0 } -body { json omap \ elem {["a", "b", "other var", null, true, false, {"x": 42}]} \ elem2 {["A", "B", "OTHER VAR", null, true, {"X": 42}]} \ { list [incr i] ($elem)([json get $elem])/($elem2)([json get $elem2]) } } -cleanup { unset -nocomplain i elem elem2 } -result {{"1":"(\"a\")(a)/(\"A\")(A)","2":"(\"b\")(b)/(\"B\")(B)","3":"(\"other var\")(other var)/(\"OTHER VAR\")(OTHER VAR)","4":"(null)()/(null)()","5":"(true)(1)/(true)(1)","6":"(false)(0)/({\"X\":42})(X 42)","7":"({\"x\":42})(x 42)/(null)()"}} #>>> test omap-4.1 {Multiple iterators, multi vars, over array} -setup { #<<< set i 0 } -body { json omap \ {x y} {["a", "b", "other var", null, true, false, {"x": 42}, 42.1]} \ {a b} {["A", "B", "OTHER VAR", null, true, {"X": 42}, 42.1]} \ { list [incr i] "($x)([json get $x])($y)([json get $y])/($a)([json get $a])($b)([json get $b])" } } -cleanup { unset -nocomplain i x y a b } -result {{"1":"(\"a\")(a)(\"b\")(b)/(\"A\")(A)(\"B\")(B)","2":"(\"other var\")(other var)(null)()/(\"OTHER VAR\")(OTHER VAR)(null)()","3":"(true)(1)(false)(0)/(true)(1)({\"X\":42})(X 42)","4":"({\"x\":42})(x 42)(42.1)(42.1)/(42.1)(42.1)(null)()"}} #>>> test omap-5.1 {Single iterator, single var, over array, continue} -setup { #<<< set i 0 } -body { json omap elem {["a", "b", "other var", null, true, false, {"x": 42}]} { if {[json get $elem] eq ""} continue list [incr i] ($elem)([json get $elem]) } } -cleanup { unset -nocomplain i elem } -result {{"1":"(\"a\")(a)","2":"(\"b\")(b)","3":"(\"other var\")(other var)","4":"(true)(1)","5":"(false)(0)","6":"({\"x\":42})(x 42)"}} #>>> test omap-5.1.1 {Single iterator, single var, over array, continue on final iteration} -setup { #<<< set i 0 } -body { json omap elem {["a", "b", "other var", null]} { if {[json get $elem] eq ""} continue list [incr i] ($elem)([json get $elem]) } } -cleanup { unset -nocomplain i elem } -result {{"1":"(\"a\")(a)","2":"(\"b\")(b)","3":"(\"other var\")(other var)"}} #>>> test omap-6.1 {Single iterator, single var, over array, break} -setup { #<<< set i 0 } -body { json omap elem {["a", "b", "other var", null, true, false, {"x": 42}]} { if {[json get $elem] eq ""} break list [incr i] ($elem)([json get $elem]) } } -cleanup { unset -nocomplain i elem } -result {{"1":"(\"a\")(a)","2":"(\"b\")(b)","3":"(\"other var\")(other var)"}} #>>> test omap-6.2 {Single iterator, single var, over array, return} -setup { #<<< set i 0 } -body { json omap elem {["a", "b", "other var", null, true, false, {"x": 42}]} { if {[json get $elem] eq ""} {return returned} list [incr i] ($elem)([json get $elem]) } } -cleanup { unset -nocomplain i elem } -result returned #>>> test omap-7.1 {Single iterator, single var, over null} -setup { #<<< set i 0 } -body { json omap elem null { incr i } } -cleanup { unset -nocomplain i elem } -result {{}} #>>> test omap-7.2 {Single iterator, single var, over empty array} -setup { #<<< set i 0 } -body { json omap elem {[]} { incr i } } -cleanup { unset -nocomplain i elem } -result {{}} #>>> test omap-7.3 {Single iterator, multi vars, over null} -setup { #<<< set i 0 } -body { json omap {x y} null { incr i } } -cleanup { unset -nocomplain i x y } -result {{}} #>>> test omap-8.1 {Multiple iterators, single var, over null} -setup { #<<< set i 0 } -body { json omap \ elem {["a", "b", "other var", null, true, false, {"x": 42}]} \ elem2 {null} \ { list [incr i] ($elem)([json get $elem])/($elem2)([json get $elem2]) } } -cleanup { unset -nocomplain i elem elem2 } -result {{"1":"(\"a\")(a)/(null)()","2":"(\"b\")(b)/(null)()","3":"(\"other var\")(other var)/(null)()","4":"(null)()/(null)()","5":"(true)(1)/(null)()","6":"(false)(0)/(null)()","7":"({\"x\":42})(x 42)/(null)()"}} #>>> test omap-8.2 {Multiple iterators, single var, over empty array} -setup { #<<< set i 0 set res {} } -body { json omap \ elem {["a", "b", "other var", null, true, false, {"x": 42}]} \ elem2 {[]} \ { list [incr i] ($elem)([json get $elem])/($elem2)([json get $elem2]) } } -cleanup { unset -nocomplain i elem elem2 } -result {{"1":"(\"a\")(a)/(null)()","2":"(\"b\")(b)/(null)()","3":"(\"other var\")(other var)/(null)()","4":"(null)()/(null)()","5":"(true)(1)/(null)()","6":"(false)(0)/(null)()","7":"({\"x\":42})(x 42)/(null)()"}} #>>> test omap-9.1.0 {Single iterator, no vars, over object} -body { #<<< json omap {} { { "a": "b", "other var": null } } {} } -returnCodes error -result "foreach varlist is empty" #>>> test omap-9.1.1 {Single iterator, single var, over object} -setup { #<<< set i 0 } -body { json omap {k v} {{"a": "b", "other var": null}} { list [incr i] $k/$v } } -cleanup { unset -nocomplain i k v } -result {{"1":"a/\"b\"","2":"other var/null"}} #>>> test omap-9.3.0 {Multiple iterators, no vars, over object} -body { #<<< json omap \ {k v} {{"a": "b", "other var": null}} \ {} {{"A": "B", "OTHER VAR": null}} \ {} } -cleanup { unset -nocomplain k v } -returnCodes error -result "foreach varlist is empty" #>>> test omap-9.3.1 {Multiple iterators, single var, over object} -setup { #<<< set i 0 } -body { json omap \ {k v} {{"a": "b", "other var": null}} \ {k2 v2} {{"A": "B", "OTHER VAR": null}} \ { list [incr i] ($k/$v)($k/[json get $v])/($k2/$v2)($k2/[json get $v2]) } } -cleanup { unset -nocomplain i k1 k2 v1 v2 } -result {{"1":"(a/\"b\")(a/b)/(A/\"B\")(A/B)","2":"(other var/null)(other var/)/(OTHER VAR/null)(OTHER VAR/)"}} #>>> test omap-9.5.1 {Single iterator, single var, over object, continue} -setup { #<<< set i 0 } -body { json omap {k v} {{"a": "b", "other var": null,"x": "y"}} { if {[json get $v] eq ""} continue list [incr i] ($k/$v)([json get $v]) } } -cleanup { unset -nocomplain i k v } -result {{"1":"(a/\"b\")(b)","2":"(x/\"y\")(y)"}} #>>> test omap-9.5.1.1 {Single iterator, single var, over object, continue on final iteration} -setup { #<<< set i 0 } -body { json omap {k v} {{"a": "b", "X": "Y", "other var": null}} { if {[json get $v] eq ""} continue list [incr i] ($k/$v)([json get $v]) } } -cleanup { unset -nocomplain i k v } -result {{"1":"(a/\"b\")(b)","2":"(X/\"Y\")(Y)"}} #>>> test omap-9.6.1 {Single iterator, single var, over object, break} -setup { #<<< set i 0 } -body { json omap {k v} {{"a": "b", "other var": null, "x": "y"}} { if {[json get $v] eq ""} break list [incr i] ($k/$v)([json get $v]) } } -cleanup { unset -nocomplain i k v } -result {{"1":"(a/\"b\")(b)"}} #>>> test omap-9.6.2 {Single iterator, single var, over object, return} -setup { #<<< set i 0 } -body { json omap {k v} {{"a": "b", "other var": null, "x": "y"}} { if {[json get $v] eq ""} {return returned} list [incr i] ($k/$v)([json get $v]) } } -cleanup { unset -nocomplain i k v } -result returned #>>> test omap-10.1.1 {iteration over array result is a (native) JSON value: string} -body { #<<< set i 0 json omap e {["a","bb","ccc"]} { list [incr i] [json string ([json get $e])] } } -cleanup { unset -nocomplain e i } -result {{"1":"(a)","2":"(bb)","3":"(ccc)"}} #>>> test omap-10.1.2 {iteration over array result is a (pure string) JSON value: string} -body { #<<< set i 0 json omap e {["a","bb","ccc"]} { list [incr i] [string trim " \"([json get $e])\""] } } -cleanup { unset -nocomplain e i } -result {{"1":"(a)","2":"(bb)","3":"(ccc)"}} #>>> test omap-10.2.1 {iteration over array result is a (native) JSON value: number} -body { #<<< set i 0 json omap e {["a","bb","ccc"]} { list [incr i] [json number [string length [json get $e]]] } } -cleanup { unset -nocomplain e i } -result {{"1":1,"2":2,"3":3}} #>>> test omap-10.2.2 {iteration over array result is a (pure string) JSON value: number} -body { #<<< set i 0 json omap e {["a","bb","ccc"]} { list [incr i] [string trim " [string length [json get $e]]"] } } -cleanup { unset -nocomplain e i } -result {{"1":1,"2":2,"3":3}} #>>> test omap-10.3.1 {iteration over array result is a (native) JSON value: bool} -body { #<<< set i 0 json omap e {["a","bb","ccc"]} { list [incr i] [json boolean [expr {[string length [json get $e]] % 2 == 0}]] } } -cleanup { unset -nocomplain e i } -result {{"1":false,"2":true,"3":false}} #>>> test omap-10.3.2 {iteration over array result is a (pure string) JSON value: bool} -body { #<<< set i 0 json omap e {["a","bb","ccc"]} { list [incr i] [string trim " [expr {[string length [json get $e]] % 2 == 0 ? "true":"false"}]"] } } -cleanup { unset -nocomplain e i } -result {{"1":false,"2":true,"3":false}} #>>> test omap-10.4.1 {iteration over array result is a (native) JSON value: null} -body { #<<< set i 0 json omap e {["a","bb","ccc"]} { list [incr i] [json extract {[null]} 0] } } -cleanup { unset -nocomplain e i } -result {{"1":null,"2":null,"3":null}} #>>> test omap-10.4.2 {iteration over array result is a (pure string) JSON value: null} -body { #<<< set i 0 json omap e {["a","bb","ccc"]} { list [incr i] [string trim " null"] } } -cleanup { unset -nocomplain e i } -result {{"1":null,"2":null,"3":null}} #>>> test omap-10.5.1 {iteration over array result is a (native) JSON value: array} -body { #<<< set i 0 json omap e {["a","bb","ccc"]} { set r {[]} json set r end+1 $e json set r end+1 [json number [string length [json get $e]]] list [incr i] $r } } -cleanup { unset -nocomplain e r i } -result {{"1":["a",1],"2":["bb",2],"3":["ccc",3]}} #>>> test omap-10.5.2 {iteration over array result is a (pure string) JSON value: array} -body { #<<< set i 0 json omap e {["a","bb","ccc"]} { set r {[]} json set r end+1 $e json set r end+1 [json number [string length [json get $e]]] list [incr i] [string trim " $r"] } } -cleanup { unset -nocomplain e r i } -result {{"1":["a",1],"2":["bb",2],"3":["ccc",3]}} #>>> test omap-10.6.1 {iteration over array result is a (native) JSON value: object} -body { #<<< set i 0 json omap e {["a","bb","ccc"]} { set r {{}} json set r k $e json set r l [json number [string length [json get $e]]] list [incr i] $r } } -cleanup { unset -nocomplain e r i } -result {{"1":{"k":"a","l":1},"2":{"k":"bb","l":2},"3":{"k":"ccc","l":3}}} #>>> test omap-10.6.2 {iteration over array result is a (pure string) JSON value: object} -body { #<<< set i 0 json omap e {["a","bb","ccc"]} { set r {{}} json set r k $e json set r l [json number [string length [json get $e]]] list [incr i] [string trim " $r"] } } -cleanup { unset -nocomplain e r i } -result {{"1":{"k":"a","l":1},"2":{"k":"bb","l":2},"3":{"k":"ccc","l":3}}} #>>> test omap-11.1.1 {iteration over object, iteration result a (native) JSON value: string} -body { #<<< set i 0 json omap {k v} {{"x":"a","y":"bb","z":"ccc"}} { list [incr i] [json string ($k/[json get $v])] } } -cleanup { unset -nocomplain k v i } -result {{"1":"(x/a)","2":"(y/bb)","3":"(z/ccc)"}} #>>> test omap-11.1.2 {iteration over object, iteration result a (pure string) JSON value: string} -body { #<<< set i 0 json omap {k v} {{"x":"a","y":"bb","z":"ccc"}} { list [incr i] [string trim " ($k/[json get $v])"] } } -cleanup { unset -nocomplain e i } -result {{"1":"(x/a)","2":"(y/bb)","3":"(z/ccc)"}} #>>> test omap-11.2.1 {iteration over object, iteration result a (native) JSON value: number} -body { #<<< set i 0 json omap {k v} {{"x":"a","y":"bb","z":"ccc"}} { list [incr i] [json number [string length [json get $v]]] } } -cleanup { unset -nocomplain k v i } -result {{"1":1,"2":2,"3":3}} #>>> test omap-11.2.2 {iteration over object, iteration result a (pure string) JSON value: number} -body { #<<< set i 0 json omap {k v} {{"x":"a","y":"bb","z":"ccc"}} { list [incr i] [string trim " [string length [json get $v]]"] } } -cleanup { unset -nocomplain k v i } -result {{"1":1,"2":2,"3":3}} #>>> test omap-11.3.1 {iteration over object, iteration result a (native) JSON value: bool} -body { #<<< set i 0 json omap {k v} {{"x":"a","y":"bb","z":"ccc"}} { list [incr i] [json boolean [expr {[string length [json get $v]] % 2 == 0}]] } } -cleanup { unset -nocomplain k v i } -result {{"1":false,"2":true,"3":false}} #>>> test omap-11.3.2 {iteration over object, iteration result a (pure string) JSON value: bool} -body { #<<< set i 0 json omap {k v} {{"x":"a","y":"bb","z":"ccc"}} { list [incr i] [string trim " [expr {[string length [json get $v]] % 2 == 0 ? "true":"false"}]"] } } -cleanup { unset -nocomplain k v i } -result {{"1":false,"2":true,"3":false}} #>>> test omap-11.4.1 {iteration over object, iteration result a (native) JSON value: null} -body { #<<< set i 0 json omap {k v} {{"x":"a","y":"bb","z":"ccc"}} { list [incr i] [json extract {[null]} 0] } } -cleanup { unset -nocomplain k v i } -result {{"1":null,"2":null,"3":null}} #>>> test omap-11.4.2 {iteration over object, iteration result a (pure string) JSON value: null} -body { #<<< set i 0 json omap {k v} {{"x":"a","y":"bb","z":"ccc"}} { list [incr i] [string trim " null"] } } -cleanup { unset -nocomplain k v i } -result {{"1":null,"2":null,"3":null}} #>>> test omap-11.5.1 {iteration over object, iteration result a (native) JSON value: array} -body { #<<< set i 0 json omap {k v} {{"x":"a","y":"bb","z":"ccc"}} { set r {[]} json set r end+1 $v json set r end+1 [json number [string length [json get $v]]] list [incr i] $r } } -cleanup { unset -nocomplain k v r i } -result {{"1":["a",1],"2":["bb",2],"3":["ccc",3]}} #>>> test omap-11.5.2 {iteration over object, iteration result a (pure string) JSON value: array} -body { #<<< set i 0 json omap {k v} {{"x":"a","y":"bb","z":"ccc"}} { set r {[]} json set r end+1 $v json set r end+1 [json number [string length [json get $v]]] list [incr i] [string trim " $r"] } } -cleanup { unset -nocomplain k v r i } -result {{"1":["a",1],"2":["bb",2],"3":["ccc",3]}} #>>> test omap-11.6.1 {iteration over object, iteration result a (native) JSON value: object} -body { #<<< set i 0 json omap {k v} {{"x":"a","y":"bb","z":"ccc"}} { set r {{}} json set r k $v json set r l [json number [string length [json get $v]]] list [incr i] $r } } -cleanup { unset -nocomplain k v r i } -result {{"1":{"k":"a","l":1},"2":{"k":"bb","l":2},"3":{"k":"ccc","l":3}}} #>>> test omap-11.6.2 {iteration over object, iteration result a (pure string) JSON value: object} -body { #<<< set i 0 json omap {k v} {{"x":"a","y":"bb","z":"ccc"}} { set r {{}} json set r k $v json set r l [json number [string length [json get $v]]] list [incr i] [string trim " $r"] } } -cleanup { unset -nocomplain k v r i } -result {{"1":{"k":"a","l":1},"2":{"k":"bb","l":2},"3":{"k":"ccc","l":3}}} #>>> test omap-12.1 {iteration result is a list, 0 elements} -body { #<<< json omap e {["a","bb","ccc"]} { list } } -cleanup { unset -nocomplain e } -result {{}} #>>> test omap-12.2 {iteration result is a list, 2 elements} -body { #<<< json omap e {["a","bb","ccc"]} { list [json get $e] [string length [json get $e]] } } -cleanup { unset -nocomplain e } -result {{"a":1,"bb":2,"ccc":3}} #>>> test omap-12.3 {iteration result is a list, 4 elements} -body { #<<< json omap e {["a","bb","ccc"]} { list \ [json get $e] [string length [json get $e]] \ [json get $e]2 [expr {[string length [json get $e]]+10}] } } -cleanup { unset -nocomplain e } -result {{"a":1,"a2":11,"bb":2,"bb2":12,"ccc":3,"ccc2":13}} #>>> test omap-12.4 {iteration result is a list, 3 elements} -body { #<<< set code [catch { json omap e {["a","bb","ccc"]} { list \ [json get $e] [string length [json get $e]] \ [json get $e]2 } } r o] list $code $r [dict get $o -errorcode] } -cleanup { unset -nocomplain e code r o } -result {1 {Iteration result must be a list with an even number of elements} NONE} #>>> test omap-13.1 {iteration result is a dict, 0 entry} -body { #<<< json omap e {["a","bb","ccc"]} { dict create } } -cleanup { unset -nocomplain e } -result {{}} #>>> test omap-13.2 {iteration result is a dict, 1 entry} -body { #<<< json omap e {["a","bb","ccc"]} { set d {} dict set d [json get $e] [string length [json get $e]] set d } } -cleanup { unset -nocomplain e d } -result {{"a":1,"bb":2,"ccc":3}} #>>> test omap-13.3 {iteration result is a dict, 2 entry} -body { #<<< json omap e {["a","bb","ccc"]} { set d {} dict set d [json get $e] [string length [json get $e]] dict set d [json get $e]2 [expr {[string length [json get $e]]+10}] set d } } -cleanup { unset -nocomplain e d } -result {{"a":1,"a2":11,"bb":2,"bb2":12,"ccc":3,"ccc2":13}} #>>> test omap-20.5 {too few args} -body { #<<< list [catch {json omap x {[]}} r o] $r [dict get $o -errorcode] } -cleanup { unset -nocomplain x y r o } -result {1 {wrong # args: should be "*omap ?varlist datalist ...? script"} {TCL WRONGARGS}} -match glob #>>> test omap-20.6 {too many args} -body { #<<< list [catch {json omap x {[]} y {}} r o] $r [dict get $o -errorcode] } -cleanup { unset -nocomplain x y r o } -result {1 {wrong # args: should be "*omap ?varlist datalist ...? script"} {TCL WRONGARGS}} -match glob #>>> ::tcltest::cleanupTests return # vim: ft=tcl foldmethod=marker foldmarker=<<<,>>> ts=4 shiftwidth=4 |
Changes to jni/rl_json/tests/pretty.test.
1 2 3 4 5 6 7 8 9 10 | if {"::tcltest" ni [namespace children]} { package require tcltest namespace import ::tcltest::* } package require rl_json package require parse_args namespace path {::rl_json ::parse_args} test pretty-1.1 {Basic pretty-print} -body { #<<< | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 | if {"::tcltest" ni [namespace children]} { package require tcltest namespace import ::tcltest::* } package require rl_json package require parse_args namespace path {::rl_json ::parse_args} test pretty-1.1 {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"}]}} } -result {{ "foo": null, "empty": {}, "emptyarr": [], "hello, world": "bar", "This is a much longer key": [ "str", 123, 123.4, true, false, null, { "inner": "obj" } ] }} #>>> test pretty-1.1 {Basic pretty-print, different indent} -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"}]}} " " } -result {{ "foo": null, "empty": {}, "emptyarr": [], "hello, world": "bar", "This is a much longer key": [ "str", 123, 123.4, true, false, null, { "inner": "obj" } ] }} #>>> test pretty-2.1 {too few args} -body { #<<< json pretty } -returnCodes error -result {Wrong # of arguments. Must be "pretty json_val ?indent?"} #>>> test pretty-2.2 {too many args} -body { #<<< json pretty {"foo"} 8 bar } -returnCodes error -result {Wrong # of arguments. Must be "pretty json_val ?indent?"} #>>> 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 ]+\){ "foo": \(0x[0-9a-fA-F]+\[[0-9]+\]+/NULL\)null, "empty": \(0x[0-9a-fA-F]+\[[0-9]+\]+/0x[0-9a-fA-F]+\[[0-9]+\]+ [a-z ]+\){}, "emptyarr": \(0x[0-9a-fA-F]+\[[0-9]+\]+/0x[0-9a-fA-F]+\[[0-9]+\]+ [a-z ]+\)\[\], "hello, world": \(0x[0-9a-fA-F]+\[[0-9]+\]+/0x[0-9a-fA-F]+\[[0-9]+\]+ [a-z ]+\)"bar", "This is a much longer key": \(0x[0-9a-fA-F]+\[[0-9]+\]+/0x[0-9a-fA-F]+\[[0-9]+\]+ [a-z ]+\)\[ \(0x[0-9a-fA-F]+\[[0-9]+\]+/0x[0-9a-fA-F]+\[[0-9]+\]+ [a-z ]+\)"str", \(0x[0-9a-fA-F]+\[[0-9]+\]+/0x[0-9a-fA-F]+\[[0-9]+\]+ [a-z ]+\)123, \(0x[0-9a-fA-F]+\[[0-9]+\]+/0x[0-9a-fA-F]+\[[0-9]+\]+ [a-z ]+\)123.4, \(0x[0-9a-fA-F]+\[[0-9]+\]+/0x[0-9a-fA-F]+\[[0-9]+\]+ [a-z ]+\)true, \(0x[0-9a-fA-F]+\[[0-9]+\]+/0x[0-9a-fA-F]+\[[0-9]+\]+ [a-z ]+\)false, \(0x[0-9a-fA-F]+\[[0-9]+\]+/NULL\)null, \(0x[0-9a-fA-F]+\[[0-9]+\]+/0x[0-9a-fA-F]+\[[0-9]+\]+ [a-z ]+\){ "inner": \(0x[0-9a-fA-F]+\[[0-9]+\]+/0x[0-9a-fA-F]+\[[0-9]+\]+ [a-z ]+\)"obj" } \] }$} #>>> ::tcltest::cleanupTests return # vim: ft=tcl foldmethod=marker foldmarker=<<<,>>> ts=4 shiftwidth=4 |
Changes to jni/rl_json/tests/set.test.
︙ | ︙ | |||
377 378 379 380 381 382 383 384 385 386 387 388 | } -body { json set json foo end+3 {"updated"} set json } -cleanup { unset -nocomplain json } -result {{"foo":["a","b","c",null,null,"updated"]}} #>>> ::tcltest::cleanupTests return # vim: ft=tcl foldmethod=marker foldmarker=<<<,>>> ts=4 shiftwidth=4 | > > > > > > > > > > > > > > > > > > > > > > > > > > > | 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 | } -body { json set json foo end+3 {"updated"} set json } -cleanup { unset -nocomplain json } -result {{"foo":["a","b","c",null,null,"updated"]}} #>>> test set-7.1 {Be lenient about the value - if it can't be interpreted as a JSON native type, convert to a JSON string} -body { #<<< set j {{}} foreach {k v} { num 42 bool true null null string string arr { [1,2,3]} notarr { [1,2,3} } { json set j $k $v } set j } -cleanup { unset -nocomplain j k v } -result [json normalize { { "num": 42, "bool": true, "null": null, "string": "string", "arr": [1,2,3], "notarr": " [1,2,3" } }] #>>> ::tcltest::cleanupTests return # vim: ft=tcl foldmethod=marker foldmarker=<<<,>>> ts=4 shiftwidth=4 |
Added jni/rl_json/tests/string.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 | if {"::tcltest" ni [namespace children]} { package require tcltest namespace import ::tcltest::* } package require rl_json test string-1.1 {Create a json string} -body { #<<< json string foo } -result {"foo"} #>>> test string-1.2 {Create a json string, template string} -body { #<<< json string ~S:foo } -result {"~S:foo"} #>>> test string-1.3 {Create a json string, template number} -body { #<<< json string "~N:foo" } -result {"~N:foo"} #>>> test string-1.4 {Create a json string, template boolean} -body { #<<< json string "~B:foo" } -result {"~B:foo"} #>>> test string-1.5 {Create a json string, template json value} -body { #<<< json string "~J:foo" } -result {"~J:foo"} #>>> test string-1.6 {Create a json string, nested template value} -body { #<<< json string "~T:foo" } -result {"~T:foo"} #>>> test string-1.7 {Create a json string, template literal} -body { #<<< json string "~L:~S:foo" } -result {"~L:~S:foo"} #>>> test string-1.8 {Create a json string, undef} -body { #<<< json string "~X:foo" } -result {"~X:foo"} #>>> test string-1.9.1 {Create a json string, template string, with unicode} -body { #<<< json string "~S:foo\x00bar" } -result {"~S:foo\u0000bar"} #>>> test string-1.9.2 {Create a json string, template number, with unicode} -body { #<<< json string "~N:foo\x00bar" } -result {"~N:foo\u0000bar"} #>>> test string-1.9.3 {Create a json string, template boolean, with unicode} -body { #<<< json string "~B:foo\x00bar" } -result {"~B:foo\u0000bar"} #>>> test string-1.9.4 {Create a json string, template json, with unicode} -body { #<<< json string "~J:foo\x00bar" } -result {"~J:foo\u0000bar"} #>>> test string-1.9.5 {Create a json string, template template, with unicode} -body { #<<< json string "~T:foo\x00bar" } -result {"~T:foo\u0000bar"} #>>> test string-1.9.6 {Create a json string, template string, with unicode} -body { #<<< json string "~L:~S:foo\x00bar" } -result {"~L:~S:foo\u0000bar"} #>>> test string-2.1 {Too few args} -body { #<<< set code [catch { json string } r o] list $code $r [dict get $o -errorcode] } -result {1 {wrong # args: should be "*string value"} {TCL WRONGARGS}} -match glob #>>> test string-2.2 {Too many args} -body { #<<< set code [catch { json string foo bar } r o] list $code $r [dict get $o -errorcode] } -result {1 {wrong # args: should be "*string value"} {TCL WRONGARGS}} -match glob #>>> ::tcltest::cleanupTests return # Local Variables: # mode: tcl # tab-width: 4 # End: # vim: ft=tcl foldmethod=marker foldmarker=<<<,>>> ts=4 shiftwidth=4 |
Changes to jni/rl_json/tests/template.test.
︙ | ︙ | |||
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | x str y 123 on yes off 0 subdoc {{"inner": "~S:bar"}} subdoc2 {{"inner2": "~S:bar"}} subdoc3 null } } -body { json template { { "foo": "~S:bar", "baz": [ "~S:a(x)", "~N:a(y)", 123.4, "~B:a(on)", "~B:a(off)", "~S:a(not_defined)", "~L:~S:not a subst", "~J:a(subdoc)", "~T:a(subdoc2)", "~J:a(subdoc3)" ] } } } -cleanup { unset -nocomplain bar a | > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > | | 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 | x str y 123 on yes off 0 subdoc {{"inner": "~S:bar"}} subdoc2 {{"inner2": "~S:bar"}} subdoc3 null empty "" } } -body { json template { { "foo": "~S:bar", "baz": [ "~S:a(empty)", "~S:a(x)", "~N:a(y)", 123.4, "~B:a(on)", "~B:a(off)", "~S:a(not_defined)", "~L:~S:not a subst", "~J:a(subdoc)", "~T:a(subdoc2)", "~J:a(subdoc3)" ] } } } -cleanup { unset -nocomplain bar a } -result {{"foo":"Bar","baz":["","str",123,123.4,true,false,null,"~S:not a subst",{"inner":"~S:bar"},{"inner2":"Bar"},null]}} #>>> test template-1.1.1 {Action list for 1.1} -body { #<<< string cat \n [join [lmap {k a b} [json template_actions { { "x": "y", "foo": "~S:bar", "unchanged": ["a","b","c"], "~S:x": [ "~S:a(empty)", "~S:a(x)", "~N:a(y)", 123.4, "~B:a(on)", "~B:a(off)", "~S:a(not_defined)", "~L:~S:not a subst", "~J:a(subdoc)", "~T:a(subdoc2)", "~J:a(subdoc3)" ] } }] { format {%20s %20s %20s} $k $a $b }] \n] \n } -cleanup { unset -nocomplain k a b } -result { ALLOCATE 2 13 FETCH_VALUE bar STORE_STRING 1 FETCH_VALUE a(empty) STORE_STRING 2 FETCH_VALUE a(x) STORE_STRING 3 FETCH_VALUE a(y) STORE_NUMBER 4 FETCH_VALUE a(on) STORE_BOOLEAN 5 FETCH_VALUE a(off) STORE_BOOLEAN 6 FETCH_VALUE a(not_defined) STORE_STRING 7 DECLARE_LITERAL "~S:not a subst" STORE_JSON 8 FETCH_VALUE a(subdoc) STORE_JSON 9 FETCH_VALUE a(subdoc2) STORE_TEMPLATE 10 FETCH_VALUE a(subdoc3) STORE_JSON 11 FETCH_VALUE x STORE_STRING 12 PUSH_TARGET { "x": "y", "foo": "~S:bar", "unchanged": ["a","b","c"], "~S:x": [ "~S:a(empty)", "~S:a(x)", "~N:a(y)", 123.4, "~B:a(on)", "~B:a(off)", "~S:a(not_defined)", "~L:~S:not a subst", "~J:a(subdoc)", "~T:a(subdoc2)", "~J:a(subdoc3)" ] } REPLACE_VAL foo 1 PUSH_TARGET ["~S:a(empty)","~S:a(x)","~N:a(y)",123.4,"~B:a(on)","~B:a(off)","~S:a(not_defined)","~L:~S:not a subst","~J:a(subdoc)","~T:a(subdoc2)","~J:a(subdoc3)"] REPLACE_ARR 0 2 REPLACE_ARR 1 3 REPLACE_ARR 2 4 REPLACE_ARR 4 5 REPLACE_ARR 5 6 REPLACE_ARR 6 7 REPLACE_ARR 7 8 REPLACE_ARR 8 9 REPLACE_ARR 9 10 REPLACE_ARR 10 11 POP_TARGET REPLACE_VAL ~S:x 0 REPLACE_KEY ~S:x 12 POP_TARGET REPLACE_ATOM 0 } #>>> test template-1.2 {Produce a JSON doc with interpolated values, using dict} -body { #<<< json template { { "foo": "~S:bar", "baz": [ "~S:empty", "~S:x", "~N:y", 123.4, "~B:on", "~B:off", "~S:not_defined", "~L:~S:not a subst", "~J:subdoc", "~T:subdoc2", "~J:subdoc3", "~S:baz", "~S:ts", "~S:tn", "~S:tb", "~S:tj", "~S:tt", "~S:tl", "~S:tx", "~N:nonesuch", "~B:nonesuch", "~J:nonesuch" ] } } { foo "Foo" bar "Ba\"r" baz "Baz" x str y 123 on yes off 0 subdoc {{"inner" : "~S:foo"}} subdoc2 {{"inner2" : "~S:foo"}} subdoc3 null empty {} ts ~S:foo tn ~N:foo tb ~B:foo tj ~J:foo tt ~T:foo tl ~L:~S:foo tx ~X:foo } } -result {{"foo":"Ba\"r","baz":["","str",123,123.4,true,false,null,"~S:not a subst",{"inner":"~S:foo"},{"inner2":"Foo"},null,"Baz","~S:foo","~N:foo","~B:foo","~J:foo","~T:foo","~L:~S:foo","~X:foo",null,null,null]}} #>>> test template-1.3 {Produce a JSON doc with interpolated values, subst object keys, using dict} -body { #<<< json template { { "~S:foo": "~S:bar", "baz": [ "~S:x", |
︙ | ︙ | |||
98 99 100 101 102 103 104 | y 123 on yes off 0 subdoc {{"inner" : "~S:foo"}} subdoc2 {{"inner2" : "~S:foo"}} subdoc3 null } | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 | y 123 on yes off 0 subdoc {{"inner" : "~S:foo"}} subdoc2 {{"inner2" : "~S:foo"}} subdoc3 null } } -result {{"baz":["str",123,123.4,true,false,null,"~S:not a subst",{"inner":"~S:foo"},{"inner2":"Foo"},null,"Baz"],"Foo":"Ba\"r"}} #>>> test template-1.3.1 {Action list for 1.3} -body { #<<< string cat \n [join [lmap {k a b} [json template_actions { { "~S:foo": "~S:bar", "baz": [ "~S:x", "~N:y", 123.4, "~B:on", "~B:off", "~S:not_defined", "~L:~S:not a subst", "~J:subdoc", "~T:subdoc2", "~J:subdoc3", "~S:baz" ] } }] { format {%20s %20s %20s} $k $a $b }] \n] \n } -cleanup { unset -nocomplain k a b } -result { ALLOCATE 2 13 FETCH_VALUE bar STORE_STRING 1 FETCH_VALUE foo STORE_STRING 2 FETCH_VALUE x STORE_STRING 3 FETCH_VALUE y STORE_NUMBER 4 FETCH_VALUE on STORE_BOOLEAN 5 FETCH_VALUE off STORE_BOOLEAN 6 FETCH_VALUE not_defined STORE_STRING 7 DECLARE_LITERAL "~S:not a subst" STORE_JSON 8 FETCH_VALUE subdoc STORE_JSON 9 FETCH_VALUE subdoc2 STORE_TEMPLATE 10 FETCH_VALUE subdoc3 STORE_JSON 11 FETCH_VALUE baz STORE_STRING 12 PUSH_TARGET { "~S:foo": "~S:bar", "baz": [ "~S:x", "~N:y", 123.4, "~B:on", "~B:off", "~S:not_defined", "~L:~S:not a subst", "~J:subdoc", "~T:subdoc2", "~J:subdoc3", "~S:baz" ] } REPLACE_VAL ~S:foo 1 REPLACE_KEY ~S:foo 2 PUSH_TARGET ["~S:x","~N:y",123.4,"~B:on","~B:off","~S:not_defined","~L:~S:not a subst","~J:subdoc","~T:subdoc2","~J:subdoc3","~S:baz"] REPLACE_ARR 0 3 REPLACE_ARR 1 4 REPLACE_ARR 3 5 REPLACE_ARR 4 6 REPLACE_ARR 5 7 REPLACE_ARR 6 8 REPLACE_ARR 7 9 REPLACE_ARR 8 10 REPLACE_ARR 9 11 REPLACE_ARR 10 12 POP_TARGET REPLACE_VAL baz 0 POP_TARGET REPLACE_ATOM 0 } #>>> test template-1.4 {Invalid interpolated type key fallthrough} -body { #<<< json template { { "foo": "~S:foo", "bar": "~A:bar" } |
︙ | ︙ | |||
121 122 123 124 125 126 127 128 129 130 131 132 133 134 | "Bar": "baz" } } { num "" } } -returnCodes error -result {Error substituting value from "num" into template, not a number: ""} #>>> test template-3.1 {Non-string key subst: number} -body { #<<< list [catch { json template { {"~N:v":"bar"} } {v 1} } r o] [expr {[dict exists $o -errorcode] ? [dict get $o -errorcode] : ""}] $r } -cleanup { unset -nocomplain r o } -result [list 1 NONE {Only strings allowed as object keys}] | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 | "Bar": "baz" } } { num "" } } -returnCodes error -result {Error substituting value from "num" into template, not a number: ""} #>>> test template-2.2.1 {Atomic string} -body { #<<< set template {"~S:num"} list [json template $template {num " 1"}] [json template_actions $template] } -cleanup { unset -nocomplain template } -result {{" 1"} {ALLOCATE 0 2 FETCH_VALUE num {} STORE_STRING {} 1 REPLACE_ATOM {} 1}} #>>> test template-2.2.2 {Atomic number} -body { #<<< set template {"~N:num"} list [json template $template {num " 1"}] [json template_actions $template] } -cleanup { unset -nocomplain template } -result {1 {ALLOCATE 0 2 FETCH_VALUE num {} STORE_NUMBER {} 1 REPLACE_ATOM {} 1}} #>>> test template-2.2.3 {Atomic boolean} -body { #<<< set template {"~B:num"} list [json template $template {num " 1"}] [json template_actions $template] } -cleanup { unset -nocomplain template } -result {true {ALLOCATE 0 2 FETCH_VALUE num {} STORE_BOOLEAN {} 1 REPLACE_ATOM {} 1}} #>>> test template-2.2.4.1 {Atomic null: string} -body { #<<< set template {"~S:num"} list [json template $template {numx " 1"}] [json template_actions $template] } -cleanup { unset -nocomplain template } -result {null {ALLOCATE 0 2 FETCH_VALUE num {} STORE_STRING {} 1 REPLACE_ATOM {} 1}} #>>> test template-2.2.4.2 {Atomic null: number} -body { #<<< set template {"~N:num"} list [json template $template {numx " 1"}] [json template_actions $template] } -cleanup { unset -nocomplain template } -result {null {ALLOCATE 0 2 FETCH_VALUE num {} STORE_NUMBER {} 1 REPLACE_ATOM {} 1}} #>>> test template-2.2.4.3 {Atomic null: boolean} -body { #<<< set template {"~B:num"} list [json template $template {numx " 1"}] [json template_actions $template] } -cleanup { unset -nocomplain template } -result {null {ALLOCATE 0 2 FETCH_VALUE num {} STORE_BOOLEAN {} 1 REPLACE_ATOM {} 1}} #>>> test template-2.2.4.4 {Atomic null: json} -body { #<<< set template {"~J:num"} list [json template $template {numx " 1"}] [json template_actions $template] } -cleanup { unset -nocomplain template } -result {null {ALLOCATE 0 2 FETCH_VALUE num {} STORE_JSON {} 1 REPLACE_ATOM {} 1}} #>>> test template-2.2.4.5 {Atomic null: template} -body { #<<< set template {"~T:num"} list [json template $template {numx " 1"}] [json template_actions $template] } -cleanup { unset -nocomplain template } -result {null {ALLOCATE 0 2 FETCH_VALUE num {} STORE_TEMPLATE {} 1 REPLACE_ATOM {} 1}} #>>> test template-2.2.4.6 {Atomic: literal} -body { #<<< set template {"~L:num"} list [json template $template {numx " 1"}] [json template_actions $template] } -cleanup { unset -nocomplain template } -result {{"num"} {ALLOCATE 0 2 DECLARE_LITERAL num {} STORE_STRING {} 1 REPLACE_ATOM {} 1}} #>>> test template-3.1 {Non-string key subst: number} -body { #<<< list [catch { json template { {"~N:v":"bar"} } {v 1} } r o] [expr {[dict exists $o -errorcode] ? [dict get $o -errorcode] : ""}] $r } -cleanup { unset -nocomplain r o } -result [list 1 NONE {Only strings allowed as object keys}] |
︙ | ︙ | |||
145 146 147 148 149 150 151 | list [catch { json template { {"~B:v":"bar"} } {v 0} } r o] [expr {[dict exists $o -errorcode] ? [dict get $o -errorcode] : ""}] $r } -cleanup { unset -nocomplain r o } -result [list 1 NONE {Only strings allowed as object keys}] #>>> | | | | > > > > > > > > > > > > > > > > > > > > > > > > | 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 | list [catch { json template { {"~B:v":"bar"} } {v 0} } r o] [expr {[dict exists $o -errorcode] ? [dict get $o -errorcode] : ""}] $r } -cleanup { unset -nocomplain r o } -result [list 1 NONE {Only strings allowed as object keys}] #>>> test template-3.4 {Non-string key subst: null} -body { #<<< list [catch { json template { {"~S:v":"bar"} } {} } r o] [expr {[dict exists $o -errorcode] ? [dict get $o -errorcode] : ""}] $r } -cleanup { unset -nocomplain r o } -result [list 1 NONE {Only strings allowed as object keys, got: null for key "~S:v"}] #>>> test template-3.5 {Non-string key subst: literal} -body { #<<< json template { {"~L:~N:v":"bar"} } {} } -result {{"~N:v":"bar"}} #>>> test template-3.5.1 {Non-string key subst: literal} -body { #<<< json template_actions { {"~L:~N:v":"bar"} } } -result {ALLOCATE 1 2 DECLARE_LITERAL {"~N:v"} {} STORE_JSON {} 1 PUSH_TARGET { {"~L:~N:v":"bar"} } {} REPLACE_KEY ~L:~N:v 1 POP_TARGET {} {} REPLACE_ATOM {} 0} #>>> test template-3.5.2 {Non-string key subst: literal, shorter than pref} -body { #<<< json template_actions { {"~L:v":"bar"} } } -result {ALLOCATE 1 2 DECLARE_LITERAL v {} STORE_STRING {} 1 PUSH_TARGET { {"~L:v":"bar"} } {} REPLACE_KEY ~L:v 1 POP_TARGET {} {} REPLACE_ATOM {} 0} #>>> test template-3.5.3 {Non-string key subst: literal, empty} -body { #<<< json template_actions { {"~L:":"bar"} } } -result {ALLOCATE 1 2 DECLARE_LITERAL {} {} STORE_STRING {} 1 PUSH_TARGET { {"~L:":"bar"} } {} REPLACE_KEY ~L: 1 POP_TARGET {} {} REPLACE_ATOM {} 0} #>>> test template-3.5.4 {Non-string key subst: literal, empty, no ~} -body { #<<< json template_actions { {"~L:xS:":"bar"} } } -result {ALLOCATE 1 2 DECLARE_LITERAL xS: {} STORE_STRING {} 1 PUSH_TARGET { {"~L:xS:":"bar"} } {} REPLACE_KEY ~L:xS: 1 POP_TARGET {} {} REPLACE_ATOM {} 0} #>>> test template-3.5.5 {Non-string key subst: literal, empty, no :} -body { #<<< json template_actions { {"~L:~S/":"bar"} } } -result {ALLOCATE 1 2 DECLARE_LITERAL ~S/ {} STORE_STRING {} 1 PUSH_TARGET { {"~L:~S/":"bar"} } {} REPLACE_KEY ~L:~S/ 1 POP_TARGET {} {} REPLACE_ATOM {} 0} #>>> test template-3.5.6 {Non-string key subst: literal, empty, not a valid letter} -body { #<<< json template_actions { {"~L:~X:":"bar"} } } -result {ALLOCATE 1 2 DECLARE_LITERAL ~X: {} STORE_STRING {} 1 PUSH_TARGET { {"~L:~X:":"bar"} } {} REPLACE_KEY ~L:~X: 1 POP_TARGET {} {} REPLACE_ATOM {} 0} #>>> try { # Check string quoting set leading "fooは" set trailing "barほ" for {set i 0} {$i <= 0x1F} {incr i} { lappend range [format 0x%02X $i] } |
︙ | ︙ | |||
218 219 220 221 222 223 224 225 226 227 228 229 | unset -nocomplain str } -result "\"$leading$expect$trailing\"" #>>> } } finally { unset -nocomplain c char expect range i special } ::tcltest::cleanupTests return # vim: ft=tcl foldmethod=marker foldmarker=<<<,>>> ts=4 shiftwidth=4 | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 | unset -nocomplain str } -result "\"$leading$expect$trailing\"" #>>> } } finally { unset -nocomplain c char expect range i special } test template-5.1 {Not quite a template} -body { #<<< json template { { "foo": "~X:bar", "~s:bar": null } } } -result {{"foo":"~X:bar","~s:bar":null}} #>>> test template-5.1.1 {Actions for 5.1} -body { #<<< json template_actions { { "foo": "~X:bar", "~s:ar": null } } } -result {} #>>> test template-6.1 {too few args} -body { #<<< json template } -returnCodes error -result {wrong # args: should be "*template json_template ?source_dict?"} -match glob #>>> test template-6.2 {too many args} -body { #<<< json template {"~S:foo"} bar baz } -returnCodes error -result {wrong # args: should be "*template json_template ?source_dict?"} -match glob #>>> test template-7.1 {Action list: optimize fetches} -body { #<<< } -body { string cat \n [join [lmap {k a b} [json template_actions { { "foo": "~S:bar", "~S:bar": [ "~N:bar", "~B:bar", "~J:bar", "~L:~S:not a subst" ] } }] { format {%20s %20s %20s} $k $a $b }] \n] \n } -cleanup { unset -nocomplain k a b } -result { ALLOCATE 2 6 FETCH_VALUE bar STORE_STRING 1 STORE_NUMBER 2 STORE_BOOLEAN 3 STORE_JSON 4 DECLARE_LITERAL "~S:not a subst" STORE_JSON 5 PUSH_TARGET { "foo": "~S:bar", "~S:bar": [ "~N:bar", "~B:bar", "~J:bar", "~L:~S:not a subst" ] } REPLACE_VAL foo 1 PUSH_TARGET ["~N:bar","~B:bar","~J:bar","~L:~S:not a subst"] REPLACE_ARR 0 2 REPLACE_ARR 1 3 REPLACE_ARR 2 4 REPLACE_ARR 3 5 POP_TARGET REPLACE_VAL ~S:bar 0 REPLACE_KEY ~S:bar 1 POP_TARGET REPLACE_ATOM 0 } #>>> ::tcltest::cleanupTests return # vim: ft=tcl foldmethod=marker foldmarker=<<<,>>> ts=4 shiftwidth=4 |
Added jni/rl_json/tests/template_string.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 | if {"::tcltest" ni [namespace children]} { package require tcltest namespace import ::tcltest::* } package require rl_json namespace path {::rl_json} test template_string-1.1 {Produce a JSON doc with interpolated values, no dict} -setup { #<<< set bar "Bar" unset -nocomplain a array set a { x str y 123 on yes off 0 subdoc {{"inner": "~S:bar"}} subdoc2 {{"inner2": "~S:bar"}} subdoc3 null } } -body { json template_string { { "foo": "~S:bar", "baz": [ "~S:a(x)", "~N:a(y)", 123.4, "~B:a(on)", "~B:a(off)", "~S:a(not_defined)", "~L:~S:not a subst", "~J:a(subdoc)", "~T:a(subdoc2)", "~J:a(subdoc3)" ] } } } -cleanup { unset -nocomplain bar a } -result {{"foo":"Bar","baz":["str",123,123.4,true,false,null,"~S:not a subst",{"inner":"~S:bar"},{"inner2":"Bar"},null]}} #>>> test template_string-1.2 {Produce a JSON doc with interpolated values, using dict} -body { #<<< json template_string { { "foo": "~S:bar", "baz": [ "~S:x", "~N:y", 123.4, "~B:on", "~B:off", "~S:not_defined", "~L:~S:not a subst", "~J:subdoc", "~T:subdoc2", "~J:subdoc3", "~S:baz" ] } } { foo "Foo" bar "Ba\"r" baz "Baz" x str y 123 on yes off 0 subdoc {{"inner" : "~S:foo"}} subdoc2 {{"inner2" : "~S:foo"}} subdoc3 null } } -result {{"foo":"Ba\"r","baz":["str",123,123.4,true,false,null,"~S:not a subst",{"inner":"~S:foo"},{"inner2":"Foo"},null,"Baz"]}} #>>> test template_string-1.3 {Produce a JSON doc with interpolated values, subst object keys, using dict} -body { #<<< json template_string { { "~S:foo": "~S:bar", "baz": [ "~S:x", "~N:y", 123.4, "~B:on", "~B:off", "~S:not_defined", "~L:~S:not a subst", "~J:subdoc", "~T:subdoc2", "~J:subdoc3", "~S:baz" ] } } { foo "Foo" bar "Ba\"r" baz "Baz" x str y 123 on yes off 0 subdoc {{"inner" : "~S:foo"}} subdoc2 {{"inner2" : "~S:foo"}} subdoc3 null } } -result {{"Foo":"Ba\"r","baz":["str",123,123.4,true,false,null,"~S:not a subst",{"inner":"~S:foo"},{"inner2":"Foo"},null,"Baz"]}} #>>> test template_string-1.4 {Invalid interpolated type key fallthrough} -body { #<<< json template_string { { "foo": "~S:foo", "bar": "~A:bar" } } { } } -result {{"foo":null,"bar":"~A:bar"}} #>>> test template_string-2.1 {Test interpolated numeric validation} -body { #<<< json template_string { { "Foo": "~N:num", "Bar": "baz" } } { num "" } } -returnCodes error -result {Error substituting value from "num" into template, not a number: ""} #>>> test template_string-3.1 {Non-string key subst: number} -body { #<<< list [catch { json template_string { {"~N:v":"bar"} } {v 1} } r o] [expr {[dict exists $o -errorcode] ? [dict get $o -errorcode] : ""}] $r } -cleanup { unset -nocomplain r o } -result [list 1 NONE {Only strings allowed as object keys, got ~N:v}] #>>> test template_string-3.2 {Non-string key subst: true} -body { #<<< list [catch { json template_string { {"~B:v":"bar"} } {v 1} } r o] [expr {[dict exists $o -errorcode] ? [dict get $o -errorcode] : ""}] $r } -cleanup { unset -nocomplain r o } -result [list 1 NONE {Only strings allowed as object keys, got ~B:v}] #>>> test template_string-3.3 {Non-string key subst: false} -body { #<<< list [catch { json template_string { {"~B:v":"bar"} } {v 0} } r o] [expr {[dict exists $o -errorcode] ? [dict get $o -errorcode] : ""}] $r } -cleanup { unset -nocomplain r o } -result [list 1 NONE {Only strings allowed as object keys, got ~B:v}] #>>> test template_string-3.4 {Non-string key subst: null} -body { #<<< list [catch { json template_string { {"~S:v":"bar"} } {} } r o] [expr {[dict exists $o -errorcode] ? [dict get $o -errorcode] : ""}] $r } -cleanup { unset -nocomplain r o } -result [list 1 NONE {Only strings allowed as object keys}] #>>> test template_string-3.5 {Non-string key subst: literal} -body { #<<< json template_string { {"~L:~N:v":"bar"} } {} } -result {{"~N:v":"bar"}} #>>> try { # Check string quoting set leading "fooは" set trailing "barほ" for {set i 0} {$i <= 0x1F} {incr i} { lappend range [format 0x%02X $i] } lappend range 0x22 0x5C 0x2F set special { 0x08 {\b} 0x09 {\t} 0x0A {\n} 0x0C {\f} 0x0D {\r} 0x22 {\"} 0x5C {\\} 0x2F {/} } foreach c $range { set char [format %c $c] if {[dict exists $special $c]} { set expect [dict get $special $c] } else { set expect [format {\u%04X} $c] } test template_string-4.$c.1 "String quoting: $c -> $expect, no leading, no trailing" -setup { #<<< set str $char } -body { json template_string {"~S:str"} } -cleanup { unset -nocomplain str } -result "\"$expect\"" #>>> test template_string-4.$c.2 "String quoting: $c -> $expect, leading, no trailing" -setup { #<<< set str "$leading$char" } -body { json template_string {"~S:str"} } -cleanup { unset -nocomplain str } -result "\"$leading$expect\"" #>>> test template_string-4.$c.3 "String quoting: $c -> $expect, no leading, trailing" -setup { #<<< set str "$char$trailing" } -body { json template_string {"~S:str"} } -cleanup { unset -nocomplain str } -result "\"$expect$trailing\"" #>>> test template_string-4.$c.4 "String quoting: $c -> $expect, leading, trailing" -setup { #<<< set str "$leading$char$trailing" } -body { json template_string {"~S:str"} } -cleanup { unset -nocomplain str } -result "\"$leading$expect$trailing\"" #>>> } } finally { unset -nocomplain c char expect range i special } test template_string-5.1 {Not quite a template} -body { #<<< json template_string { { "foo": "~X:bar", "~s:bar": null } } } -result {{"foo":"~X:bar","~s:bar":null}} #>>> test template_string-6.1 {too few args} -body { #<<< json template_string } -returnCodes error -result {wrong # args: should be "*template_string json_template ?source_dict?"} -match glob #>>> test template_string-6.2 {too many args} -body { #<<< json template_string {"~S:foo"} bar baz } -returnCodes error -result {wrong # args: should be "*template_string json_template ?source_dict?"} -match glob #>>> ::tcltest::cleanupTests return # vim: ft=tcl foldmethod=marker foldmarker=<<<,>>> ts=4 shiftwidth=4 |
Changes to jni/rl_json/tests/type.test.
︙ | ︙ | |||
110 111 112 113 114 115 116 | { "foo": "bar", "baz": ["str", 123, 123.4, true, false, null, {"inner": "obj"}] } } baz 5 } -result null #>>> | | | > > > > > > > > > > > > | 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 | { "foo": "bar", "baz": ["str", 123, 123.4, true, false, null, {"inner": "obj"}] } } baz 5 } -result null #>>> test type-3.1 {type: invalid path: out of array bounds} -body { #<<< json type { { "foo": "bar", "baz": ["str", 123, 123.4, true, false, null, {"inner": "obj"}] } } baz 7 } -result null #>>> test type-3.2 {type: invalid path: invalid key} -body { #<<< set code [catch { json type { { "foo": "bar", "baz": ["str", 123, 123.4, true, false, null, {"inner": "obj"}] } } quux } r o] list $code $r [dict get $o -errorcode] } -result {1 {Path element 2: "quux" not found} NONE} #>>> # Invalid paths test type-60.1 {invalid path: missing key} -body { #<<< json type { { "foo": "bar", |
︙ | ︙ |
Changes to jni/rl_json/tests/unset.test.
︙ | ︙ | |||
81 82 83 84 85 86 87 | set json { { "foo": "Foo", "baz": "Baz" } } } -body { | | | | 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 | set json { { "foo": "Foo", "baz": "Baz" } } } -body { list [catch {json unset json bar Bar} r o] $r [dict get $o -errorcode] } -cleanup { unset -nocomplain json r o } -result {1 {Path element "bar" doesn't exist} {RL JSON BAD_PATH bar}} #>>> test unset-3.1 {Throw error for dereferencing atomic objects in path} -setup { #<<< set json { { "foo": "Foo", "baz": "Baz" } |
︙ | ︙ | |||
209 210 211 212 213 214 215 | } -body { json unset json foo end+3 set json } -cleanup { unset -nocomplain json } -result {{"foo":["a","b","c"]}} #>>> | > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 | } -body { json unset json foo end+3 set json } -cleanup { unset -nocomplain json } -result {{"foo":["a","b","c"]}} #>>> test unset-7.1 {Unset an element in an array, end-relative} -setup { #<<< set json { { "foo": ["a", ["1","2","3"], "c"] } } } -body { json unset json foo end-1 1 set json } -cleanup { unset -nocomplain json } -result {{"foo":["a",["1","3"],"c"]}} #>>> test unset-7.2 {Unset an element in an array, end-relative, invalid} -setup { #<<< set json { { "foo": ["a", ["1","2","3"], "c"] } } } -body { json unset json foo end/1 1 set json } -cleanup { unset -nocomplain json } -returnCodes error -result {Expected an integer index or end(+/-integer)?, got end/1} #>>> test unset-7.3 {Unset an element in an array, end-relative, invalid} -setup { #<<< set json { { "foo": ["a", ["1","2","3"], "c"] } } } -body { json unset json foo en 1 set json } -cleanup { unset -nocomplain json } -returnCodes error -result {Expected an integer index or end(+/-integer)?, got en} #>>> test unset-7.4 {Unset an element in an array, end-relative, invalid} -setup { #<<< set json { { "foo": ["a", ["1","2","3"], "c"] } } } -body { json unset json foo end-1x 1 set json } -cleanup { unset -nocomplain json } -returnCodes error -result {Expected an integer index or end(+/-integer)?, got end-1x} #>>> test unset-7.5 {Unset an element in an array, invalid} -setup { #<<< set json { { "foo": ["a", ["1","2","3"], "c"] } } } -body { json unset json foo -1 1 set json } -cleanup { unset -nocomplain json } -returnCodes error -result {Path element "foo -1" doesn't exist} #>>> test unset-7.6 {Unset an element in an array, invalid} -setup { #<<< set json { { "foo": ["a", ["1","2","3"], "c"] } } } -body { json unset json foo 4 1 set json } -cleanup { unset -nocomplain json } -returnCodes error -result {Path element "foo 4" doesn't exist} #>>> test unset-7.7 {Unset an element in an array, invalid} -setup { #<<< set json { { "foo": ["~S:a", ["1","2","3"], "c"] } } } -body { json unset json foo 0 1 set json } -cleanup { unset -nocomplain json } -returnCodes error -result {Attempt to index into atomic type string at path "foo 0 1"} #>>> test unset-7.8 {Unset an element in an array, invalid} -setup { #<<< set json { { "foo": ["~S:a", ["1","2","3"], "c"] } } } -body { json unset json foo 1 end/1 set json } -cleanup { unset -nocomplain json } -returnCodes error -result {Expected an integer index or end(+/-integer)?, got end/1} #>>> test unset-7.9 {Unset an element in an array, invalid} -setup { #<<< set json { { "foo": ["~S:a", ["1","2","3"], "c"] } } } -body { json unset json foo 1 en set json } -cleanup { unset -nocomplain json } -returnCodes error -result {Expected an integer index or end(+/-integer)?, got en} #>>> test unset-7.10 {Unset an element in an array, invalid} -setup { #<<< set json { { "foo": ["~S:a", ["1","2","3"], "c"] } } } -body { json unset json foo 1 end-1x set json } -cleanup { unset -nocomplain json } -returnCodes error -result {Expected an integer index or end(+/-integer)?, got end-1x} #>>> ::tcltest::cleanupTests return # vim: ft=tcl foldmethod=marker foldmarker=<<<,>>> ts=4 shiftwidth=4 |
Added jni/rl_json/tests/valid.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 | if {"::tcltest" ni [namespace children]} { package require tcltest namespace import ::tcltest::* } package require rl_json namespace path {::rl_json} test valid-0.1 {Too few args} -body { #<<< list [catch {json valid} r o] $r [dict get $o -errorcode] } -cleanup { unset -nocomplain r o } -result {1 {wrong # args: should be "valid ?-extensions extensionslist -details detailsvar? json_val"} {TCL WRONGARGS}} #>>> test valid-0.2 {Too few args: missing value for -extensions} -body { #<<< list [catch {json valid -extensions true} r o] $r [dict get $o -errorcode] } -cleanup { unset -nocomplain r o } -result {1 {wrong # args: should be "valid -extensions extensionslist json_val"} {TCL WRONGARGS}} #>>> test valid-0.3 {Too few args: missing value for -details} -body { #<<< list [catch {json valid -details true} r o] $r [dict get $o -errorcode] } -cleanup { unset -nocomplain r o } -result {1 {wrong # args: should be "valid -details detailsvar json_val"} {TCL WRONGARGS}} #>>> test valid-0.4 {Too few args: missing json_val folling -extensions} -body { #<<< list [catch {json valid -extensions {}} r o] $r [dict get $o -errorcode] } -cleanup { unset -nocomplain r o } -result {1 {wrong # args: should be "valid -extensions extensionslist json_val"} {TCL WRONGARGS}} #>>> test valid-0.5 {Too few args: missing json_val folling -details} -body { #<<< list [catch {json valid -details d} r o] $r [dict get $o -errorcode] } -cleanup { unset -nocomplain r o d } -result {1 {wrong # args: should be "valid -details detailsvar json_val"} {TCL WRONGARGS}} #>>> test valid-0.6 {Too few args: missing json_val folling -extensions -details} -body { #<<< list [catch {json valid -extensions {} -details d} r o] $r [dict get $o -errorcode] } -cleanup { unset -nocomplain r o d } -result {1 {wrong # args: should be "valid -extensions {} -details detailsvar json_val"} {TCL WRONGARGS}} #>>> test valid-0.7 {Too few args: missing json_val folling -details -extensions} -body { #<<< list [catch {json valid -details d -extensions {}} r o] $r [dict get $o -errorcode] } -cleanup { unset -nocomplain r o d } -result {1 {wrong # args: should be "valid -details d -extensions extensionslist json_val"} {TCL WRONGARGS}} #>>> test valid-0.8 {Too many args: no options} -body { #<<< list [catch {json valid true false} r o] $r [dict get $o -errorcode] } -cleanup { unset -nocomplain r o d } -result {1 {bad option "true": must be -extensions or -details} {TCL LOOKUP INDEX option true}} #>>> test valid-0.9 {Too many args: with -extensions} -body { #<<< list [catch {json valid -extensions {} true false} r o] $r [dict get $o -errorcode] } -cleanup { unset -nocomplain r o d } -result {1 {bad option "true": must be -extensions or -details} {TCL LOOKUP INDEX option true}} #>>> test valid-0.10 {Too many args: with -details} -body { #<<< list [catch {json valid -details d true false} r o] $r [dict get $o -errorcode] } -cleanup { unset -nocomplain r o d } -result {1 {bad option "true": must be -extensions or -details} {TCL LOOKUP INDEX option true}} #>>> test valid-0.11 {Too many args: with -details and -extensions} -body { #<<< list [catch {json valid -details d -extensions {} true false} r o] $r [dict get $o -errorcode] } -cleanup { unset -nocomplain r o d } -result {1 {bad option "true": must be -extensions or -details} {TCL LOOKUP INDEX option true}} #>>> test valid-1.0.1 {extensions: invalid extension} -body { #<<< list [catch {json valid -extensions {nonesuch} true} r o] $r [dict get $o -errorcode] } -cleanup { unset -nocomplain r o d } -result {1 {bad extension "nonesuch": must be comments} {TCL LOOKUP INDEX extension nonesuch}} #>>> test valid-1.1.1 {extensions: comments included} -body { #<<< json valid -extensions comments {true // Allowed?} } -result 1 #>>> test valid-1.1.2 {extensions: comments excluded} -body { #<<< json valid -extensions {} {true // Allowed?} } -result 0 #>>> test valid-1.1.3 {extensions: comments default} -body { #<<< json valid {true // Allowed?} } -result 1 #>>> test valid-1.1.4 {extensions: comments default with -details} -body { #<<< json valid -details d {true // Allowed?} } -cleanup { unset -nocomplain d } -result 1 #>>> test valid-1.2.0.1 {details: bad detailsvar} -body { #<<< set d "already a scalar" list [catch {json valid -details d(foo) "true \x1f"} r o] $r [dict get $o -errorcode] } -cleanup { unset -nocomplain d } -result {1 {can't set "d(foo)": variable isn't array} {TCL LOOKUP VARNAME d}} #>>> test valid-1.2.0.2 {details: bad detailsvar} -body { #<<< set d(foo) "already an array" list [catch {json valid -details d "true \x1f"} r o] $r [dict get $o -errorcode] } -cleanup { unset -nocomplain d } -result {1 {can't set "d": variable is array} {TCL WRITE VARNAME}} #>>> test valid-1.2.1 {details: scalar detailsvar} -body { #<<< unset -nocomplain d list [json valid -details d "true \x1f"] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Trailing garbage after value} doc "true \x1f" char_ofs 5]] #>>> test valid-1.2.1 {details: array detailsvar} -body { #<<< unset -nocomplain d list [json valid -details d(foo) "true \x1f"] $d(foo) } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Trailing garbage after value} doc "true \x1f" char_ofs 5]] #>>> test valid-1.2.2 {details: don't set on valid} -body { #<<< unset -nocomplain d list [json valid -details d true] [info exists d] } -cleanup { unset -nocomplain d } -result {1 0} #>>> test valid-1.2.3 {no details} -body { #<<< list [json valid "true \x1f"] } -result 0 #>>> test valid-2.1 {Truncated string} -body { #<<< list [json valid -details d "\"foo"] $d } -cleanup { unset -nocomplain d } -result {0 {errmsg {Document truncated} doc {"foo} char_ofs 4}} #>>> test valid-2.2 {Empty document} -body { #<<< list [json valid -details d ""] $d } -cleanup { unset -nocomplain d } -result {0 {errmsg {No JSON value found} doc {} char_ofs 0}} #>>> test valid-2.3 {Empty value} -body { #<<< list [json valid -details d "\{\"foo\":"] $d } -cleanup { unset -nocomplain d } -result {0 {errmsg {Document truncated} doc \{\"foo\": char_ofs 7}} #>>> test valid/backslash-1.1 {\u in string value, no leading, no trailing} -body { #<<< json valid {"\u306f"} } -result 1 #>>> test valid/backslash-1.2 {\u in string value, leading, no trailing} -body { #<<< json valid {"( \u306f"} } -result 1 #>>> test valid/backslash-1.3 {\u in string value, no leading, trailing} -body { #<<< json valid {"\u306f )"} } -result 1 #>>> test valid/backslash-1.4 {\u in string value, leading, trailing} -body { #<<< json valid {"( \u306f )"} } -result 1 #>>> test valid/backslash-2.1 {\u in string value, too few digits, no trailing} -body { #<<< list [json valid -details d {"\u"}] $d } -cleanup { unset -nocomplain d } -result {0 {errmsg {Unicode sequence too short} doc {"\u"} char_ofs 3}} #>>> test valid/backslash-2.2 {\u in string value, too few digits, no trailing} -body { #<<< list [json valid -details d {"\u3"}] $d } -cleanup { unset -nocomplain d } -result {0 {errmsg {Unicode sequence too short} doc {"\u3"} char_ofs 4}} #>>> test valid/backslash-2.3 {\u in string value, too few digits, no trailing} -body { #<<< list [json valid -details d {"\u30"}] $d } -cleanup { unset -nocomplain d } -result {0 {errmsg {Unicode sequence too short} doc {"\u30"} char_ofs 5}} #>>> test valid/backslash-2.4 {\u in string value, too few digits, no trailing} -body { #<<< list [json valid -details d {"\u306"}] $d } -cleanup { unset -nocomplain d } -result {0 {errmsg {Unicode sequence too short} doc {"\u306"} char_ofs 6}} #>>> test valid/backslash-2.5 {\u in string value, too few digits, truncated} -body { #<<< list [json valid -details d "\"\\u"] $d } -cleanup { unset -nocomplain d } -result {0 {errmsg {Unicode sequence too short} doc {"\u} char_ofs 3}} #>>> test valid/backslash-2.6 {\u in string value, too few digits, truncated} -body { #<<< list [json valid -details d "\"\\u3"] $d } -cleanup { unset -nocomplain d } -result {0 {errmsg {Unicode sequence too short} doc {"\u3} char_ofs 4}} #>>> test valid/backslash-2.7 {\u in string value, too few digits, truncated} -body { #<<< list [json valid -details d "\"\\u30"] $d } -cleanup { unset -nocomplain d } -result {0 {errmsg {Unicode sequence too short} doc {"\u30} char_ofs 5}} #>>> test valid/backslash-2.8 {\u in string value, too few digits, truncated} -body { #<<< list [json valid -details d "\"\\u306"] $d } -cleanup { unset -nocomplain d } -result {0 {errmsg {Unicode sequence too short} doc {"\u306} char_ofs 6}} #>>> test valid/backslash-3.1 {\u in string value, too few digits, trailing < '0'} -body { #<<< list [json valid -details d {"\u/xxxx"}] $d } -cleanup { unset -nocomplain d } -result {0 {errmsg {Unicode sequence too short} doc {"\u/xxxx"} char_ofs 3}} #>>> test valid/backslash-3.2 {\u in string value, too few digits, trailing < '0'} -body { #<<< list [json valid -details d {"\u3/xxxx"}] $d } -cleanup { unset -nocomplain d } -result {0 {errmsg {Unicode sequence too short} doc {"\u3/xxxx"} char_ofs 4}} #>>> test valid/backslash-3.3 {\u in string value, too few digits, trailing < '0'} -body { #<<< list [json valid -details d {"\u30/xxxx"}] $d } -cleanup { unset -nocomplain d } -result {0 {errmsg {Unicode sequence too short} doc {"\u30/xxxx"} char_ofs 5}} #>>> test valid/backslash-3.4 {\u in string value, too few digits, trailing < '0'} -body { #<<< list [json valid -details d {"\u306/xxxx"}] $d } -cleanup { unset -nocomplain d } -result {0 {errmsg {Unicode sequence too short} doc {"\u306/xxxx"} char_ofs 6}} #>>> test valid/backslash-4.1 {\u in string value, too few digits, trailing < 'A'} -body { #<<< list [json valid -details d {"\u@xxxx"}] $d } -cleanup { unset -nocomplain d } -result {0 {errmsg {Unicode sequence too short} doc {"\u@xxxx"} char_ofs 3}} #>>> test valid/backslash-4.2 {\u in string value, too few digits, trailing < 'A'} -body { #<<< list [json valid -details d {"\u3@xxxx"}] $d } -cleanup { unset -nocomplain d } -result {0 {errmsg {Unicode sequence too short} doc {"\u3@xxxx"} char_ofs 4}} #>>> test valid/backslash-4.3 {\u in string value, too few digits, trailing < 'A'} -body { #<<< list [json valid -details d {"\u30@xxxx"}] $d } -cleanup { unset -nocomplain d } -result {0 {errmsg {Unicode sequence too short} doc {"\u30@xxxx"} char_ofs 5}} #>>> test valid/backslash-4.4 {\u in string value, too few digits, trailing < 'A'} -body { #<<< list [json valid -details d {"\u306@xxxx"}] $d } -cleanup { unset -nocomplain d } -result {0 {errmsg {Unicode sequence too short} doc {"\u306@xxxx"} char_ofs 6}} #>>> test valid/backslash-5.1 {\u in string value, too few digits, trailing < 'a'} -body { #<<< list [json valid -details d {"\u`xxxx"}] $d } -cleanup { unset -nocomplain d } -result {0 {errmsg {Unicode sequence too short} doc {"\u`xxxx"} char_ofs 3}} #>>> test valid/backslash-5.2 {\u in string value, too few digits, trailing < 'a'} -body { #<<< list [json valid -details d {"\u3`xxxx"}] $d } -cleanup { unset -nocomplain d } -result {0 {errmsg {Unicode sequence too short} doc {"\u3`xxxx"} char_ofs 4}} #>>> test valid/backslash-5.3 {\u in string value, too few digits, trailing < 'a'} -body { #<<< list [json valid -details d {"\u30`xxxx"}] $d } -cleanup { unset -nocomplain d } -result {0 {errmsg {Unicode sequence too short} doc {"\u30`xxxx"} char_ofs 5}} #>>> test valid/backslash-5.4 {\u in string value, too few digits, trailing < 'a'} -body { #<<< list [json valid -details d {"\u306`xxxx"}] $d } -cleanup { unset -nocomplain d } -result {0 {errmsg {Unicode sequence too short} doc {"\u306`xxxx"} char_ofs 6}} #>>> test valid/backslash-6.1 {\u in string value, too few digits, trailing > '9'} -body { #<<< list [json valid -details d {"\u:xxxx"}] $d } -cleanup { unset -nocomplain d } -result {0 {errmsg {Unicode sequence too short} doc {"\u:xxxx"} char_ofs 3}} #>>> test valid/backslash-6.2 {\u in string value, too few digits, trailing > '9'} -body { #<<< list [json valid -details d {"\u3:xxxx"}] $d } -cleanup { unset -nocomplain d } -result {0 {errmsg {Unicode sequence too short} doc {"\u3:xxxx"} char_ofs 4}} #>>> test valid/backslash-6.3 {\u in string value, too few digits, trailing > '9'} -body { #<<< list [json valid -details d {"\u30:xxxx"}] $d } -cleanup { unset -nocomplain d } -result {0 {errmsg {Unicode sequence too short} doc {"\u30:xxxx"} char_ofs 5}} #>>> test valid/backslash-6.4 {\u in string value, too few digits, trailing > '9'} -body { #<<< list [json valid -details d {"\u306:xxxx"}] $d } -cleanup { unset -nocomplain d } -result {0 {errmsg {Unicode sequence too short} doc {"\u306:xxxx"} char_ofs 6}} #>>> test valid/backslash-6.1 {\u in string value, too few digits, trailing > 'F'} -body { #<<< list [json valid -details d {"\uGxxxx"}] $d } -cleanup { unset -nocomplain d } -result {0 {errmsg {Unicode sequence too short} doc {"\uGxxxx"} char_ofs 3}} #>>> test valid/backslash-6.2 {\u in string value, too few digits, trailing > 'F'} -body { #<<< list [json valid -details d {"\u3Gxxxx"}] $d } -cleanup { unset -nocomplain d } -result {0 {errmsg {Unicode sequence too short} doc {"\u3Gxxxx"} char_ofs 4}} #>>> test valid/backslash-6.3 {\u in string value, too few digits, trailing > 'F'} -body { #<<< list [json valid -details d {"\u30Gxxxx"}] $d } -cleanup { unset -nocomplain d } -result {0 {errmsg {Unicode sequence too short} doc {"\u30Gxxxx"} char_ofs 5}} #>>> test valid/backslash-6.4 {\u in string value, too few digits, trailing > 'F'} -body { #<<< list [json valid -details d {"\u306Gxxxx"}] $d } -cleanup { unset -nocomplain d } -result {0 {errmsg {Unicode sequence too short} doc {"\u306Gxxxx"} char_ofs 6}} #>>> test valid/backslash-6.1 {\u in string value, too few digits, trailing > 'f'} -body { #<<< list [json valid -details d {"\ugxxxx"}] $d } -cleanup { unset -nocomplain d } -result {0 {errmsg {Unicode sequence too short} doc {"\ugxxxx"} char_ofs 3}} #>>> test valid/backslash-6.2 {\u in string value, too few digits, trailing > 'f'} -body { #<<< list [json valid -details d {"\u3gxxxx"}] $d } -cleanup { unset -nocomplain d } -result {0 {errmsg {Unicode sequence too short} doc {"\u3gxxxx"} char_ofs 4}} #>>> test valid/backslash-6.3 {\u in string value, too few digits, trailing > 'f'} -body { #<<< list [json valid -details d {"\u30gxxxx"}] $d } -cleanup { unset -nocomplain d } -result {0 {errmsg {Unicode sequence too short} doc {"\u30gxxxx"} char_ofs 5}} #>>> test valid/backslash-6.4 {\u in string value, too few digits, trailing > 'f'} -body { #<<< list [json valid -details d {"\u306gxxxx"}] $d } -cleanup { unset -nocomplain d } -result {0 {errmsg {Unicode sequence too short} doc {"\u306gxxxx"} char_ofs 6}} #>>> test valid/backslash-7.1 {\u in string value, too many digits} -body { #<<< json valid {"\u306F3"} } -result 1 #>>> test valid/backslash-8.1 {\u in string value, reject leading sign} -body { #<<< list [json valid -details d {"\u-306F"}] $d } -cleanup { unset -nocomplain d } -result {0 {errmsg {Unicode sequence too short} doc {"\u-306F"} char_ofs 3}} #>>> test valid/backslash-8.2 {\u in string value, reject leading sign} -body { #<<< list [json valid -details d {"\u+306F"}] $d } -cleanup { unset -nocomplain d } -result {0 {errmsg {Unicode sequence too short} doc {"\u+306F"} char_ofs 3}} #>>> test valid/backslash-10.1 {\u in string value, valid hex bounds} -body { #<<< json valid {"\u0009"} } -result 1 #>>> test valid/backslash-10.2 {\u in string value, valid hex bounds} -body { #<<< json valid {"\uAfaF"} } -result 1 #>>> test valid/backslash-10.3 {\u in string value, valid hex bounds} -body { #<<< json valid {"\u0000"} } -result 1 #>>> try { # Test every ASCII escape char other than u (tested above) <<< set valid { 34 \u0022 92 \u005c 47 \u002f 98 \u0008 102 \u000c 110 \u000a 114 \u000d 116 \u0009 } for {set c 0} {$c < 0x80} {incr c} { if {[format %c $c] eq "u"} continue if {[dict exists $valid $c]} { test valid/backslash-20.$c.1 "test every non-u backquote: [format 0x%02x $c], no trailing" -body { #<<< json valid [format {"\%c"} $c] } -result 1 #>>> test valid/backslash-20.$c.2 "test every non-u backquote: [format 0x%02x $c], trailing" -body { #<<< json valid [format {"\%cx"} $c] } -result 1 #>>> test valid/backslash-20.$c.3 "test every non-u backquote: [format 0x%02x $c], truncated" -body { #<<< list [json valid -details d [format "\"\\%c" $c]] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Document truncated} doc [format "\"\\%c" $c] char_ofs 3]] #>>> } else { test valid/backslash-20.$c.1 "test every non-u backquote: [format 0x%02x $c], no trailing" -body { #<<< list [json valid -details d [format {"\%c"} $c]] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Illegal character} doc [format "\"\\%c\"" $c] char_ofs 2]] #>>> test valid/backslash-20.$c.2 "test every non-u backquote: [format 0x%02x $c], trailing" -body { #<<< list [json valid -details d [format {"\%cx"} $c]] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Illegal character} doc [format "\"\\%cx\"" $c] char_ofs 2]] #>>> test valid/backslash-20.$c.3 "test every non-u backquote: [format 0x%02x $c], truncated" -body { #<<< list [json valid -details d [format "\"\\%c" $c]] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Illegal character} doc [format "\"\\%c" $c] char_ofs 2]] #>>> } } } finally { unset -nocomplain c valid } #>>> test valid/backslash-30.1 {single backslash, closing quote} -body { #<<< list [json valid -details d "\"\\\""] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Document truncated} doc "\"\\\"" char_ofs 3]] #>>> test valid/backslash-30.2 {single backslash, truncated} -body { #<<< list [json valid -details d "\"\\"] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Document truncated} doc "\"\\" char_ofs 2]] #>>> test valid/controlchar-1.1 {Control char after valid chars, no trailing} -body { #<<< list [json valid -details d "\"foo\x1F\""] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Illegal character} doc "\"foo\x1F\"" char_ofs 4]] #>>> test valid/controlchar-1.2 {Control char after valid chars, trailing} -body { #<<< list [json valid -details d "\"foo\x1Fx\""] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Illegal character} doc "\"foo\x1Fx\"" char_ofs 4]] #>>> test valid/controlchar-1.3 {Control char after valid chars, truncated} -body { #<<< list [json valid -details d "\"foo\x1F"] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Illegal character} doc "\"foo\x1F" char_ofs 4]] #>>> test valid/controlchar-2.1 {Control char at start, no trailing} -body { #<<< list [json valid -details d "\"\x1F\""] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Illegal character} doc "\"\x1F\"" char_ofs 1]] #>>> test valid/controlchar-2.2 {Control char at start, trailing} -body { #<<< list [json valid -details d "\"\x1Fx\""] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Illegal character} doc "\"\x1Fx\"" char_ofs 1]] #>>> test valid/controlchar-2.3 {Control char at start, truncated} -body { #<<< list [json valid -details d "\"\x1F"] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Illegal character} doc "\"\x1F" char_ofs 1]] #>>> try { # Test all control chars except \0 (Tcl will always supply this as 0xC0 0x80) (RFC4627 excludes 0x7f) <<< for {set c 1} {$c <= 0x1F} {incr c} { test valid/controlchar-3.$c [format {Test control char 0x%02x} $c] -body { #<<< list [json valid -details d [format {"%c"} $c]] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Illegal character} doc [format {"%c"} $c] char_ofs 1]] #>>> } } finally { unset -nocomplain c } #>>> try { # Test all ASCII non-escape chars <<< set str "" for {set c 0x20} {$c < 0x80} {incr c} { if {$c in {34 92 98 102 110 114 116}} continue append str [format %c $c] } test valid/controlchar-4.1 {Test all ASCII non-escape chars} -body { json valid "\"$str\"" } -result 1 } finally { unset -nocomplain c str } #>>> test valid/numbers-1.1 {Bare number value - integer} -body { #<<< json valid 42 } -result 1 #>>> test valid/numbers-1.2 {Bare number value - negative integer} -body { #<<< json valid -42 } -result 1 #>>> test valid/numbers-1.3 {Bare number value - integer, postive sign} -body { #<<< list [json valid -details d +42] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Illegal character} doc +42 char_ofs 0]] #>>> test valid/numbers-1.4 {Bare number value - medium integer} -body { #<<< json valid 1234567890 } -result 1 #>>> test valid/numbers-1.5 {Ranges in integer part} -body { #<<< json valid 909090 } -result 1 #>>> test valid/numbers-1.6 {Ranges in fractional part} -body { #<<< json valid 1.909090 } -result 1 #>>> test valid/numbers-1.7 {Ranges in exponent part} -body { #<<< json valid 1e909090 } -result 1 #>>> test valid/numbers-2.1 {Invalid char - here because this is handled in the numeric case} -body { #<<< list [json valid -details d x] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Illegal character} doc x char_ofs 0]] #>>> test valid/numbers-2.2 {Document truncated after minus sign} -body { #<<< list [json valid -details d -] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Document truncated} doc - char_ofs 1]] #>>> test valid/numbers-2.3 {Invalid char after minus sign} -body { #<<< list [json valid -details d -x] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Illegal character} doc -x char_ofs 1]] #>>> test valid/numbers-3.1 {No integer part} -body { #<<< list [json valid -details d .1] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Illegal character} doc .1 char_ofs 0]] #>>> test valid/numbers-4.1 {Decimal point without decimal part} -body { #<<< list [json valid -details d 12.] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Document truncated} doc 12. char_ofs 3]] #>>> test valid/numbers-4.2 {Decimal point without decimal part} -body { #<<< list [json valid -details d 12.] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Document truncated} doc 12. char_ofs 3]] #>>> test valid/numbers-4.3 {Decimal point with decimal part} -body { #<<< json valid 12.34 } -result 1 #>>> test valid/numbers-5.1 {Upper case exponent symbol, no exponent part, truncated} -body { #<<< list [json valid -details d 12E] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Document truncated} doc 12E char_ofs 3]] #>>> test valid/numbers-5.2 {Lower case exponent symbol, no exponent part, truncated} -body { #<<< list [json valid -details d 12e] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Document truncated} doc 12e char_ofs 3]] #>>> test valid/numbers-5.3 {Lower case exponent symbol, no exponent part} -body { #<<< list [json valid -details d {[12e,4]}] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Illegal character} doc {[12e,4]} char_ofs 4]] #>>> test valid/numbers-6.1 {Upper case exponent symbol, simple integer exponent part} -body { #<<< json valid 12E3 } -result 1 #>>> test valid/numbers-6.2 {Lower case exponent symbol, simple integer exponent part} -body { #<<< json valid 12e3 } -result 1 #>>> test valid/numbers-7.1 {Upper case exponent symbol, negative integer exponent part} -body { #<<< json valid 12E-3 } -result 1 #>>> test valid/numbers-7.2 {Lower case exponent symbol, negative integer exponent part} -body { #<<< json valid 12e-3 } -result 1 #>>> test valid/numbers-8.1 {Upper case exponent symbol, positive integer exponent part} -body { #<<< json valid 12E+3 } -result 1 #>>> test valid/numbers-8.2 {Lower case exponent symbol, positive integer exponent part} -body { #<<< json valid 12e+3 } -result 1 #>>> test valid/numbers-9.1 {Lower case exponent symbol, many digit exponent} -body { #<<< json valid 12e+321 } -result 1 #>>> test valid/keywords-1.1 {true, incomplete} -body { #<<< list [json valid -details d t] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Illegal character} doc t char_ofs 0]] #>>> test valid/keywords-1.2 {true, incomplete} -body { #<<< list [json valid -details d tr] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Illegal character} doc tr char_ofs 0]] #>>> test valid/keywords-1.3 {true, incomplete} -body { #<<< list [json valid -details d tru] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Illegal character} doc tru char_ofs 0]] #>>> test valid/keywords-1.4 {true, complete} -body { #<<< json valid true } -result 1 #>>> test valid/keywords-1.5 {true, trailing garbage} -body { #<<< list [json valid -details d truely] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Trailing garbage after value} doc truely char_ofs 4]] #>>> test valid/keywords-1.6 {true, trailing whitespace} -body { #<<< json valid "true " } -result 1 #>>> test valid/keywords-1.7 {true, leading whitespace} -body { #<<< json valid " true" } -result 1 #>>> test valid/keywords-2.1 {false, incomplete} -body { #<<< list [json valid -details d f] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Illegal character} doc f char_ofs 0]] #>>> test valid/keywords-2.2 {false, incomplete} -body { #<<< list [json valid -details d fa] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Illegal character} doc fa char_ofs 0]] #>>> test valid/keywords-2.3 {false, incomplete} -body { #<<< list [json valid -details d fal] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Illegal character} doc fal char_ofs 0]] #>>> test valid/keywords-2.4 {false, incomplete} -body { #<<< list [json valid -details d fals] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Illegal character} doc fals char_ofs 0]] #>>> test valid/keywords-2.5 {false, complete} -body { #<<< json valid false } -result 1 #>>> test valid/keywords-2.6 {false, trailing garbage} -body { #<<< list [json valid -details d falsely] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Trailing garbage after value} doc falsely char_ofs 5]] #>>> test valid/keywords-2.6 {false, trailing whitespace} -body { #<<< json valid "false " } -result 1 #>>> test valid/keywords-2.7 {false, leading whitespace} -body { #<<< json valid " false" } -result 1 #>>> test valid/keywords-3.1 {null, incomplete} -body { #<<< list [json valid -details d n] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Illegal character} doc n char_ofs 0]] #>>> test valid/keywords-3.2 {null, incomplete} -body { #<<< list [json valid -details d nu] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Illegal character} doc nu char_ofs 0]] #>>> test valid/keywords-3.3 {null, incomplete} -body { #<<< list [json valid -details d nul] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Illegal character} doc nul char_ofs 0]] #>>> test valid/keywords-3.4 {null, complete} -body { #<<< set j [json normalize null] list $j [json valid $j] } -cleanup { unset -nocomplain j } -result [list null 1] #>>> test valid/keywords-3.5 {null, trailing garbage} -body { #<<< list [json valid -details d nullo] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Trailing garbage after value} doc nullo char_ofs 4]] #>>> test valid/keywords-3.6 {null, trailing whitespace} -body { #<<< json valid "null " } -result 1 #>>> test valid/keywords-3.7 {null, leading whitespace} -body { #<<< json valid " null" } -result 1 #>>> test valid/structure-1.1 {start of object} -body { #<<< set j [json normalize "{}"] list $j [json valid $j] } -cleanup { unset -nocomplain j } -result [list "{}" 1] #>>> test valid/structure-1.2 {start of array} -body { #<<< set j [json normalize {[]}] list $j [json valid $j] } -cleanup { unset -nocomplain j } -result [list {[]} 1] #>>> test valid/structure-1.3 {nested empty structures} -body { #<<< set j [json normalize {[{}]}] list $j [json valid $j] } -cleanup { unset -nocomplain j } -result [list {[{}]} 1] #>>> test valid/structure-1.4 {nested empty structures with whitespace} -body { #<<< set j [json normalize { [ { } ] } ] list $j [json valid $j] } -cleanup { unset -nocomplain j } -result [list {[{}]} 1] #>>> test valid/structure-1.5 {nested empty structures with error in whitespace} -body { #<<< list [json valid -details d " \[ { /*\x1f*/ } \] "] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Illegal character} doc " \[ { /*\x1f*/ } \] " char_ofs 7]] #>>> test valid/structure-1.6 {empty array with error in whitespace} -body { #<<< list [json valid -details d " \[ /*\x1f*/ \] "] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Illegal character} doc " \[ /*\x1f*/ \] " char_ofs 5]] #>>> test valid/structure-1.7 {No comma} -body { #<<< list [json valid -details d {["foo" "bar"]}] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Expecting ] or ,} doc {["foo" "bar"]} char_ofs 7]] #>>> test valid/structure-1.8 {Error in whitespace after comma} -body { #<<< list [json valid -details d "\[\"foo\",/*\x1f*/\"bar\"\]"] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Illegal character} doc "\[\"foo\",/*\x1f*/\"bar\"\]" char_ofs 9]] #>>> test valid/structure-1.9 {Unterminated object} -body { #<<< list [json valid -details d "\{\"foo\":1.9,"] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Unterminated object} doc "\{\"foo\":1.9," char_ofs 0]] #>>> test valid/structure-1.10 {Unterminated array} -body { #<<< list [json valid -details d "\[1.10,"] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Unterminated array} doc "\[1.10," char_ofs 0]] #>>> test valid/structure-2.1 {object, single key packed} -body { #<<< set j [json normalize {{"foo":"bar"}}] list $j [json valid $j] } -cleanup { unset -nocomplain j } -result [list {{"foo":"bar"}} 1] #>>> test valid/structure-2.2 {object, single key whitespace} -body { #<<< set j [json normalize { { "foo" : "bar" } }] list $j [json valid $j] } -cleanup { unset -nocomplain j } -result [list {{"foo":"bar"}} 1] #>>> test valid/structure-2.3 {object, multi key packed} -body { #<<< set j [json normalize {{"foo":"bar","foo2":"bar2"}}] list $j [json valid $j] } -cleanup { unset -nocomplain j } -result [list {{"foo":"bar","foo2":"bar2"}} 1] #>>> test valid/structure-2.4 {object, multi key whitespace} -body { #<<< set j [json normalize { { "foo" : "bar" , "foo2": "bar2" } }] list $j [json valid $j ] } -cleanup { unset -nocomplain j } -result [list {{"foo":"bar","foo2":"bar2"}} 1] #>>> test valid/structure-3.1 {array, single element packed} -body { #<<< set j [json normalize {["foo"]}] list $j [json valid $j] } -cleanup { unset -nocomplain j } -result [list {["foo"]} 1] #>>> test valid/structure-3.2 {array, single element whitespace} -body { #<<< set j [json normalize { [ "foo" ] }] list $j [json valid $j] } -cleanup { unset -nocomplain j } -result [list {["foo"]} 1] #>>> test valid/structure-3.3 {array, multi element packed} -body { #<<< set j [json normalize {["foo","bar"]}] list $j [json valid $j] } -cleanup { unset -nocomplain j } -result [list {["foo","bar"]} 1] #>>> test valid/structure-3.4 {array, multi element whitespace} -body { #<<< set j [json normalize { [ "foo" , "bar" ] }] list $j [json valid $j] } -cleanup { unset -nocomplain j } -result [list {["foo","bar"]} 1] #>>> test valid/structure-4.1 {orphaned hold_key} -body { #<<< list [json valid -details d {{"structure-4.1"}}] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Expecting : after object key} doc {{"structure-4.1"}} char_ofs 16]] #>>> test valid/structure-4.2 {orphaned hold_key} -body { #<<< list [json valid -details d {{"structure-4.2":}}] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Illegal character} doc {{"structure-4.2":}} char_ofs 17]] #>>> test valid/structure-4.3 {nested orphaned hold_key} -body { #<<< list [json valid -details d {{"x":"y","structure-4.3":{"nested-4.3":}}}] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Illegal character} doc {{"x":"y","structure-4.3":{"nested-4.3":}}} char_ofs 39]] #>>> test valid/structure-4.4 {nested orphaned hold_key, nested key parse error} -body { #<<< list [json valid -details d {{"structure-4.4":{"nested-4.4\x":"bar"}}}] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Illegal character} doc {{"structure-4.4":{"nested-4.4\x":"bar"}}} char_ofs 30]] #>>> test valid/structure-5.4 {Deep nesting, ensure that this test nests deeper than CX_STACK_SIZE} -body { #<<< json valid { { "first": { "second": { "third": { "fourth": { "fifth": { "sixth": { "seventh": { "eighth": { "ninth": { "tenth": { "eleventh": [1, 2, "structure-5.4"] } } } } } } } } } } } } } -result 1 #>>> test valid/structure-6.1 {Nesting error} -body { #<<< list [json valid -details d {{"structure-6.1":[}]}] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Illegal character} doc {{"structure-6.1":[}]} char_ofs 18]] #>>> test valid/structure-6.2 {Nesting error} -body { #<<< list [json valid -details d {{"structure-6.2":[null,}]}] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Illegal character} doc {{"structure-6.2":[null,}]} char_ofs 23]] #>>> test valid/structure-6.3 {Nesting error} -body { #<<< list [json valid -details d {[{"structure-6.3"]}]}] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Expecting : after object key} doc {[{"structure-6.3"]}]} char_ofs 17]] #>>> test valid/structure-6.4 {Nesting error} -body { #<<< list [json valid -details d {[{"structure-6.4":]}]}] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Illegal character} doc {[{"structure-6.4":]}]} char_ofs 18]] #>>> test valid/structure-6.5 {Nesting error} -body { #<<< list [json valid -details d {[{"structure-6.5":1]}]}] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg "Expecting \} or ," doc {[{"structure-6.5":1]}]} char_ofs 19]] #>>> test valid/structure-6.6 {Nesting error} -body { #<<< list [json valid -details d {[{"structure-6.6":1,]}]}] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Illegal character} doc {[{"structure-6.6":1,]}]} char_ofs 20]] #>>> test valid/structure-7.1 {Object key not a string: number} -body { #<<< list [json valid -details d {{7.1:"structure-7.1"}}] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Object key is not a string} doc {{7.1:"structure-7.1"}} char_ofs 1]] #>>> test valid/structure-7.2 {Object key not a string: true} -body { #<<< list [json valid -details d {{true:"structure-7.2"}}] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Object key is not a string} doc {{true:"structure-7.2"}} char_ofs 1]] #>>> test valid/structure-7.3 {Object key not a string: false} -body { #<<< list [json valid -details d {{false:"structure-7.3"}}] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Object key is not a string} doc {{false:"structure-7.3"}} char_ofs 1]] #>>> test valid/structure-7.4 {Object key not a string: null} -body { #<<< list [json valid -details d {{null:"structure-7.4"}}] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Object key is not a string} doc {{null:"structure-7.4"}} char_ofs 1]] #>>> test valid/structure-7.5 {Object key not a string: object} -body { #<<< list [json valid -details d {{{}:"structure-7.5"}}] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Object key is not a string} doc {{{}:"structure-7.5"}} char_ofs 1]] #>>> test valid/structure-7.6 {Object key not a string: array} -body { #<<< list [json valid -details d {{[]:"structure-7.6"}}] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Object key is not a string} doc {{[]:"structure-7.6"}} char_ofs 1]] #>>> test valid/structure-8.1 {Object key not a string: dyn_number} -body { #<<< json valid {{"~N:f":"structure-8.1"}} } -result 1 #>>> test valid/structure-8.2 {Object key not a string: dyn_bool} -body { #<<< json valid {{"~B:f":"structure-8.2"}} } -result 1 #>>> test valid/structure-8.5 {Object key not a string: dyn_json} -body { #<<< json valid {{"~J:f":"structure-8.5"}} } -result 1 #>>> test valid/structure-8.6 {Object key not a string: dyn_template} -body { #<<< json valid {{"~T:f":"structure-8.6"}} } -result 1 #>>> test valid/structure-8.7 {Object key not a string: dyn_literal} -body { #<<< json valid {{"~L:~S:f":"structure-8.7"}} } -result 1 #>>> test valid/structure-8.8 {Object key not a string: dyn_string} -body { #<<< json valid {{"~S:f":"structure-8.8"}} } -result 1 #>>> test valid/utf-1.1 {String containing multibyte utf-8 encoding} -body { #<<< json valid {"helloは"} } -result 1 #>>> test valid/utf-1.2 {Length calculation for 1, 2 and 3 byte UTF-8 sequences} -body { #<<< json valid {"h¿は"} } -result 1 #>>> try { # Test the UTF-8 encoding length edge cases (limited to 3 byte sequences (the BMP) by Tcl support) <<< # 0x20 is used as the lower bound for the 1 byte case because 0x1F and # below are control characters and not legal in JSON as unescaped chars foreach {bytes lower upper} { 1 0x20 0x7f 2 0x80 0x7ff 3 0x800 0xffff } { test valid/utf-2.$bytes.1 "lower bound of $bytes byte chars" -body { #<<< json valid [format {"%1$c%1$c"} $lower] } -result 1 #>>> test valid/utf-2.$bytes.2 "upper bound of $bytes byte chars" -body { #<<< json valid [format {"%1$c%1$c"} $upper] } -result 1 #>>> test valid/utf-2.$bytes.3 "error reporting char_ofs for lower bound of $bytes byte chars" -body { #<<< list [json valid -details d [format {"%1$c%1$c%2$c"} $lower 0x1f]] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Illegal character} doc [format {"%1$c%1$c%2$c"} $lower 0x1f] char_ofs 3]] #>>> test valid/utf-2.$bytes.4 "error reporting char_ofs for upper bound of $bytes byte chars" -body { #<<< list [json valid -details d [format {"%1$c%1$c%2$c"} $upper 0x1f]] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Illegal character} doc [format {"%1$c%1$c%2$c"} $upper 0x1f] char_ofs 3]] #>>> } } finally { unset -nocomplain bytes lower upper } #>>> test valid/whitespace-1.1 {String containing multibyte utf-8 encoding} -body { #<<< json valid { /*hello€*/ "helloは" //The rest is a comment ほ} } -result 1 #>>> test valid/whitespace-1.1.1 {String containing multibyte utf-8 encoding, comments disabled} -body { #<<< list [json valid -extensions {} -details d { /*hello€*/ "helloは" //The rest is a comment ほ}] $d } -cleanup { unset -nocomplain d } -result {0 {errmsg {Illegal character} doc { /*hello€*/ "helloは" //The rest is a comment ほ} char_ofs 1}} #>>> test valid/whitespace-1.2 {All whitespace chars and utf-8 char lengths in comments, no whitespace between comments and string} -body { #<<< json valid "\n\t\r /*h¿は*/\"foo\"//h¿は\n" } -result 1 #>>> test valid/whitespace-1.2.1 {All whitespace chars and utf-8 char lengths in comments, no whitespace between comments and string} -body { #<<< list [json valid -extensions {} -details d "\n\t\r /*h¿は*/\"foo\"//h¿は\n"] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Illegal character} doc "\n\t\r /*h¿は*/\"foo\"//h¿は\n" char_ofs 4]] #>>> test valid/whitespace-1.3 {All whitespace chars and utf-8 char lengths in comments, leading whitespace between comments and string} -body { #<<< json valid "\n\t\r /*h¿は*/\r\"foo\"//h¿は\n" } -result 1 #>>> test valid/whitespace-1.3.1 {All whitespace chars and utf-8 char lengths in comments, leading whitespace between comments and string} -body { #<<< list [json valid -extensions {} -details d "\n\t\r /*h¿は*/\r\"foo\"//h¿は\n"] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Illegal character} doc "\n\t\r /*h¿は*/\r\"foo\"//h¿は\n" char_ofs 4]] #>>> test valid/whitespace-1.4 {All whitespace chars and utf-8 char lengths in comments, trailing whitespace between comments and string} -body { #<<< json valid "\n\t\r /*h¿は*/\"foo\"\r//h¿は\n" } -result 1 #>>> test valid/whitespace-1.4.1 {All whitespace chars and utf-8 char lengths in comments, trailing whitespace between comments and string} -body { #<<< list [json valid -extensions {} -details d "\n\t\r /*h¿は*/\"foo\"\r//h¿は\n"] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Illegal character} doc "\n\t\r /*h¿は*/\"foo\"\r//h¿は\n" char_ofs 4]] #>>> test valid/whitespace-1.5 {All whitespace chars and utf-8 char lengths in comments, leading and trailing whitespace between comments and string} -body { #<<< json valid "\n\t\r /*h¿は*/\t\"foo\"\r//h¿は\n" } -result 1 #>>> test valid/whitespace-1.5.1 {All whitespace chars and utf-8 char lengths in comments, leading and trailing whitespace between comments and string} -body { #<<< list [json valid -extensions {} -details d "\n\t\r /*h¿は*/\t\"foo\"\r//h¿は\n"] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Illegal character} doc "\n\t\r /*h¿は*/\t\"foo\"\r//h¿は\n" char_ofs 4]] #>>> test valid/whitespace-1.6 {char offset counting for skipped chars in comments} -body { #<<< list [json valid -details d "\n\t\r /*h¿は*/\"foo\"//h¿は\nbar"] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Trailing garbage after value} doc "\n\t\r /*h¿は*/\"foo\"//h¿は\nbar" char_ofs 22]] #>>> test valid/whitespace-1.6.1 {char offset counting for skipped chars in comments} -body { #<<< list [json valid -extensions {} -details d "\n\t\r /*h¿は*/\"foo\"//h¿は\nbar"] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Illegal character} doc "\n\t\r /*h¿は*/\"foo\"//h¿は\nbar" char_ofs 4]] #>>> try { # Test the UTF-8 encoding length edge cases (limited to 3 byte sequences (the BMP) by Tcl support) <<< # 0x20 is used as the lower bound for the 1 byte case because 0x1F and # below are control characters and not legal in JSON as unescaped chars foreach {bytes lower upper} { 1 0x20 0x7f 2 0x80 0x7ff 3 0x800 0xffff } { test valid/whitespace-2.$bytes.1 "lower bound of $bytes byte chars in // comment" -body { #<<< json valid [format "//%1\$c%1\$c\nfalse" $lower] } -result 1 #>>> test valid/whitespace-2.$bytes.1.1 "lower bound of $bytes byte chars in // comment" -body { #<<< list [json valid -extensions {} -details d [format "//%1\$c%1\$c\nfalse" $lower]] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Illegal character} doc [format "//%1\$c%1\$c\nfalse" $lower] char_ofs 0]] #>>> test valid/whitespace-2.$bytes.2 "upper bound of $bytes byte chars in // comment" -body { #<<< json valid [format "//%1\$c%1\$c\nfalse" $upper] } -result 1 #>>> test valid/whitespace-2.$bytes.2.1 "upper bound of $bytes byte chars in // comment" -body { #<<< list [json valid -extensions {} -details d [format "//%1\$c%1\$c\nfalse" $upper]] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Illegal character} doc [format "//%1\$c%1\$c\nfalse" $upper] char_ofs 0]] #>>> test valid/whitespace-2.$bytes.3 "error reporting char_ofs for lower bound of $bytes byte chars in // comment" -body { #<<< list [json valid -details d [format "//%1\$c%1\$c%2\$c\nfalse" $lower 0x1f]] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Illegal character} doc [format "//%1\$c%1\$c%2\$c\nfalse" $lower 0x1f] char_ofs 4]] #>>> test valid/whitespace-2.$bytes.3.1 "error reporting char_ofs for lower bound of $bytes byte chars in // comment" -body { #<<< list [json valid -extensions {} -details d [format "//%1\$c%1\$c%2\$c\nfalse" $lower 0x1f]] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Illegal character} doc [format "//%1\$c%1\$c%2\$c\nfalse" $lower 0x1f] char_ofs 0]] #>>> test valid/whitespace-2.$bytes.4 "error reporting char_ofs for upper bound of $bytes byte chars in // comment" -body { #<<< list [json valid -details d [format "//%1\$c%1\$c%2\$c\nfalse" $upper 0x1f]] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Illegal character} doc [format "//%1\$c%1\$c%2\$c\nfalse" $upper 0x1f] char_ofs 4]] #>>> test valid/whitespace-2.$bytes.4.1 "error reporting char_ofs for upper bound of $bytes byte chars in // comment" -body { #<<< list [json valid -extensions {} -details d [format "//%1\$c%1\$c%2\$c\nfalse" $upper 0x1f]] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Illegal character} doc [format "//%1\$c%1\$c%2\$c\nfalse" $upper 0x1f] char_ofs 0]] #>>> test valid/whitespace-3.$bytes.1 "lower bound of $bytes byte chars in /* */ comment" -body { #<<< json valid [format "/*%1\$c%1\$c*/false" $lower] } -result 1 #>>> test valid/whitespace-3.$bytes.1.1 "lower bound of $bytes byte chars in /* */ comment" -body { #<<< list [json valid -extensions {} -details d [format "/*%1\$c%1\$c*/false" $lower]] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Illegal character} doc [format "/*%1\$c%1\$c*/false" $lower] char_ofs 0]] #>>> test valid/whitespace-3.$bytes.2 "upper bound of $bytes byte chars in /* */ comment" -body { #<<< json valid [format "/*%1\$c%1\$c*/false" $upper] } -result 1 #>>> test valid/whitespace-3.$bytes.2.1 "upper bound of $bytes byte chars in /* */ comment" -body { #<<< list [json valid -extensions {} -details d [format "/*%1\$c%1\$c*/false" $upper]] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Illegal character} doc [format "/*%1\$c%1\$c*/false" $upper] char_ofs 0]] #>>> test valid/whitespace-3.$bytes.3 "error reporting char_ofs for lower bound of $bytes byte chars in /* */ comment" -body { #<<< list [json valid -details d [format "/*%1\$c%1\$c%2\$c*/false" $lower 0x1f]] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Illegal character} doc [format "/*%1\$c%1\$c%2\$c*/false" $lower 0x1f] char_ofs 4]] #>>> test valid/whitespace-3.$bytes.3.1 "error reporting char_ofs for lower bound of $bytes byte chars in /* */ comment" -body { #<<< list [json valid -extensions {} -details d [format "/*%1\$c%1\$c%2\$c*/false" $lower 0x1f]] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Illegal character} doc [format "/*%1\$c%1\$c%2\$c*/false" $lower 0x1f] char_ofs 0]] #>>> test valid/whitespace-3.$bytes.4 "error reporting char_ofs for upper bound of $bytes byte chars in /* */ comment" -body { #<<< list [json valid -details d [format "/*%1\$c%1\$c%2\$c*/false" $upper 0x1f]] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Illegal character} doc [format "/*%1\$c%1\$c%2\$c*/false" $upper 0x1f] char_ofs 4]] #>>> test valid/whitespace-3.$bytes.4.1 "error reporting char_ofs for upper bound of $bytes byte chars in /* */ comment" -body { #<<< list [json valid -extensions {} -details d [format "/*%1\$c%1\$c%2\$c*/false" $upper 0x1f]] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Illegal character} doc [format "/*%1\$c%1\$c%2\$c*/false" $upper 0x1f] char_ofs 0]] #>>> } } finally { unset -nocomplain bytes lower upper } #>>> test valid/whitespace-4.1 {Half opened // comment sequence} -body { #<<< list [json valid -details d " /foo"] $d } -cleanup { unset -nocomplain d } -result [list 0 {errmsg {Illegal character} doc { /foo} char_ofs 2}] #>>> test valid/whitespace-5.1 {Two comments, no whitespace} -body { #<<< json valid {/*foo*//*bar*/123} } -result 1 #>>> test valid/whitespace-5.1.1 {Two comments, no whitespace} -body { #<<< list [json valid -extensions {} -details d {/*foo*//*bar*/123}] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Illegal character} doc {/*foo*//*bar*/123} char_ofs 0]] #>>> test valid/whitespace-6.1 {Two comments, whitespace between} -body { #<<< json valid {/*foo*/ /*bar*/123} } -result 1 #>>> test valid/whitespace-6.1.1 {Two comments, whitespace between} -body { #<<< list [json valid -extensions {} -details d {/*foo*/ /*bar*/123}] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Illegal character} doc {/*foo*/ /*bar*/123} char_ofs 0]] #>>> test valid/whitespace-7.1 {Two // comments} -body { #<<< json valid "321//foo\n//bar" } -result 1 #>>> test valid/whitespace-7.1.1 {Two // comments} -body { #<<< list [json valid -extensions {} -details d "321//foo\n//bar"] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Trailing garbage after value} doc "321//foo\n//bar" char_ofs 3]] #>>> test valid/whitespace-8.1 {// comment, no newline} -body { #<<< json valid "8.1//bar" } -result 1 #>>> test valid/whitespace-8.1.1 {// comment, no newline} -body { #<<< list [json valid -extensions {} -details d "8.1//bar"] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Trailing garbage after value} doc "8.1//bar" char_ofs 3]] #>>> test valid/whitespace-8.2 {// comment, no newline, no value} -body { #<<< list [json valid -details d {//bar}] $d } -cleanup { unset -nocomplain d } -result {0 {errmsg {No JSON value found} doc //bar char_ofs 5}} #>>> test valid/whitespace-8.2.1 {// comment, no newline, no value} -body { #<<< list [json valid -extensions {} -details d {//bar}] $d } -cleanup { unset -nocomplain d } -result {0 {errmsg {Illegal character} doc //bar char_ofs 0}} #>>> test valid/whitespace-9.1 {Unterminated comment} -body { #<<< list [json valid -details d {9.1/* はhello}] $d } -cleanup { unset -nocomplain d } -result {0 {errmsg {Unterminated comment} doc {9.1/* はhello} char_ofs 3}} #>>> test valid/whitespace-9.1.1 {Unterminated comment} -body { #<<< list [json valid -extensions {} -details d {9.1/* はhello}] $d } -cleanup { unset -nocomplain d } -result {0 {errmsg {Trailing garbage after value} doc {9.1/* はhello} char_ofs 3}} #>>> test valid/whitespace-9.2 {Unterminated comment} -body { #<<< list [json valid -details d {9.2/* はhello*}] $d } -cleanup { unset -nocomplain d } -result {0 {errmsg {Unterminated comment} doc {9.2/* はhello*} char_ofs 3}} #>>> test valid/whitespace-9.2.1 {Unterminated comment} -body { #<<< list [json valid -extensions {} -details d {9.2/* はhello*}] $d } -cleanup { unset -nocomplain d } -result {0 {errmsg {Trailing garbage after value} doc {9.2/* はhello*} char_ofs 3}} #>>> test valid/whitespace-9.3 {Unterminated comment} -body { #<<< list [json valid -details d {9.3/* はhello*x}] $d } -cleanup { unset -nocomplain d } -result {0 {errmsg {Unterminated comment} doc {9.3/* はhello*x} char_ofs 3}} #>>> test valid/whitespace-9.3.1 {Unterminated comment} -body { #<<< list [json valid -extensions {} -details d {9.3/* はhello*x}] $d } -cleanup { unset -nocomplain d } -result {0 {errmsg {Trailing garbage after value} doc {9.3/* はhello*x} char_ofs 3}} #>>> test valid/whitespace-9.4 {Comment terminated at EOF} -body { #<<< json valid {9.4/* はhello*/} } -result 1 #>>> test valid/whitespace-9.4.1 {Comment terminated at EOF} -body { #<<< list [json valid -extensions {} -details d {9.4/* はhello*/}] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Trailing garbage after value} doc {9.4/* はhello*/} char_ofs 3]] #>>> test valid/whitespace-9.5 {Multiline comment} -body { #<<< json valid {9.5/* はhello "foo" */} } -result 1 #>>> test valid/whitespace-9.5.1 {Multiline comment} -body { #<<< list [json valid -extensions {} -details d {9.5/* はhello "foo" */}] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Trailing garbage after value} doc {9.5/* はhello "foo" */} char_ofs 3]] #>>> test valid/whitespace-9.6 {Commented object key} -body { #<<< json valid { { //"foo": {} } } } -result 1 #>>> test valid/whitespace-9.6.1 {Commented object key} -body { #<<< list [json valid -extensions {} -details d { { //"foo": {} } }] $d } -cleanup { unset -nocomplain d } -result [list 0 [list errmsg {Illegal character} doc { { //"foo": {} } } char_ofs 8]] #>>> ::tcltest::cleanupTests return # vim: ft=tcl foldmethod=marker foldmarker=<<<,>>> ts=4 shiftwidth=4 |
Added jni/rl_json/win/makefile.vc.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | #------------------------------------------------------------- -*- makefile -*- # # Makefile for building rl_json # # Basic build and install # nmake /s /nologo /f makefile.vc INSTALLDIR=c:\path\to\tcl # nmake /s /nologo /f makefile.vc INSTALLDIR=c:\path\to\tcl install # # For other build options (debug, static etc.) # See TIP 477 (https://core.tcl.tk/tips/doc/trunk/tip/477.md) for # detailed documentation. # #------------------------------------------------------------------------------ # The name of the package PROJECT = rl_json !include "rules-ext.vc" PRJ_OBJS = \ $(TMP_DIR)\rl_json.obj \ $(TMP_DIR)\rl_jsonStubInit.obj \ $(TMP_DIR)\json_types.obj \ $(TMP_DIR)\dedup.obj \ $(TMP_DIR)\api.obj \ $(TMP_DIR)\parser.obj PRJ_STUBOBJS = $(TMP_DIR)\rl_jsonStubLib.obj PRJ_DEFINES = -D_CRT_SECURE_NO_WARNINGS !include "$(_RULESDIR)\targets.vc" pkgindex: default-pkgindex-tea install: default-install-docs-html |
Added jni/rl_json/win/nmakehlp.c.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 | /* * ---------------------------------------------------------------------------- * nmakehlp.c -- * * This is used to fix limitations within nmake and the environment. * * Copyright (c) 2002 by David Gravereaux. * Copyright (c) 2006 by Pat Thoyts * * See the file "license.terms" for information on usage and redistribution of * this file, and for a DISCLAIMER OF ALL WARRANTIES. * ---------------------------------------------------------------------------- */ #define _CRT_SECURE_NO_DEPRECATE #include <windows.h> #pragma comment (lib, "user32.lib") #pragma comment (lib, "kernel32.lib") #include <stdio.h> #include <math.h> /* * This library is required for x64 builds with _some_ versions of MSVC */ #if defined(_M_IA64) || defined(_M_AMD64) #if _MSC_VER >= 1400 && _MSC_VER < 1500 #pragma comment(lib, "bufferoverflowU") #endif #endif /* ISO hack for dumb VC++ */ #ifdef _MSC_VER #define snprintf _snprintf #endif /* protos */ static int CheckForCompilerFeature(const char *option); static int CheckForLinkerFeature(const char **options, int count); static int IsIn(const char *string, const char *substring); static int SubstituteFile(const char *substs, const char *filename); static int QualifyPath(const char *path); static int LocateDependency(const char *keyfile); static const char *GetVersionFromFile(const char *filename, const char *match, int numdots); static DWORD WINAPI ReadFromPipe(LPVOID args); /* globals */ #define CHUNK 25 #define STATICBUFFERSIZE 1000 typedef struct { HANDLE pipe; char buffer[STATICBUFFERSIZE]; } pipeinfo; pipeinfo Out = {INVALID_HANDLE_VALUE, '\0'}; pipeinfo Err = {INVALID_HANDLE_VALUE, '\0'}; /* * exitcodes: 0 == no, 1 == yes, 2 == error */ int main( int argc, char *argv[]) { char msg[300]; DWORD dwWritten; int chars; const char *s; /* * Make sure children (cl.exe and link.exe) are kept quiet. */ SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX); /* * Make sure the compiler and linker aren't effected by the outside world. */ SetEnvironmentVariable("CL", ""); SetEnvironmentVariable("LINK", ""); if (argc > 1 && *argv[1] == '-') { switch (*(argv[1]+1)) { case 'c': if (argc != 3) { chars = snprintf(msg, sizeof(msg) - 1, "usage: %s -c <compiler option>\n" "Tests for whether cl.exe supports an option\n" "exitcodes: 0 == no, 1 == yes, 2 == error\n", argv[0]); WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars, &dwWritten, NULL); return 2; } return CheckForCompilerFeature(argv[2]); case 'l': if (argc < 3) { chars = snprintf(msg, sizeof(msg) - 1, "usage: %s -l <linker option> ?<mandatory option> ...?\n" "Tests for whether link.exe supports an option\n" "exitcodes: 0 == no, 1 == yes, 2 == error\n", argv[0]); WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars, &dwWritten, NULL); return 2; } return CheckForLinkerFeature(&argv[2], argc-2); case 'f': if (argc == 2) { chars = snprintf(msg, sizeof(msg) - 1, "usage: %s -f <string> <substring>\n" "Find a substring within another\n" "exitcodes: 0 == no, 1 == yes, 2 == error\n", argv[0]); WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars, &dwWritten, NULL); return 2; } else if (argc == 3) { /* * If the string is blank, there is no match. */ return 0; } else { return IsIn(argv[2], argv[3]); } case 's': if (argc == 2) { chars = snprintf(msg, sizeof(msg) - 1, "usage: %s -s <substitutions file> <file>\n" "Perform a set of string map type substutitions on a file\n" "exitcodes: 0\n", argv[0]); WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars, &dwWritten, NULL); return 2; } return SubstituteFile(argv[2], argv[3]); case 'V': if (argc != 4) { chars = snprintf(msg, sizeof(msg) - 1, "usage: %s -V filename matchstring\n" "Extract a version from a file:\n" "eg: pkgIndex.tcl \"package ifneeded http\"", argv[0]); WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars, &dwWritten, NULL); return 0; } s = GetVersionFromFile(argv[2], argv[3], *(argv[1]+2) - '0'); if (s && *s) { printf("%s\n", s); return 0; } else return 1; /* Version not found. Return non-0 exit code */ case 'Q': if (argc != 3) { chars = snprintf(msg, sizeof(msg) - 1, "usage: %s -Q path\n" "Emit the fully qualified path\n" "exitcodes: 0 == no, 1 == yes, 2 == error\n", argv[0]); WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars, &dwWritten, NULL); return 2; } return QualifyPath(argv[2]); case 'L': if (argc != 3) { chars = snprintf(msg, sizeof(msg) - 1, "usage: %s -L keypath\n" "Emit the fully qualified path of directory containing keypath\n" "exitcodes: 0 == success, 1 == not found, 2 == error\n", argv[0]); WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars, &dwWritten, NULL); return 2; } return LocateDependency(argv[2]); } } chars = snprintf(msg, sizeof(msg) - 1, "usage: %s -c|-f|-l|-Q|-s|-V ...\n" "This is a little helper app to equalize shell differences between WinNT and\n" "Win9x and get nmake.exe to accomplish its job.\n", argv[0]); WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars, &dwWritten, NULL); return 2; } static int CheckForCompilerFeature( const char *option) { STARTUPINFO si; PROCESS_INFORMATION pi; SECURITY_ATTRIBUTES sa; DWORD threadID; char msg[300]; BOOL ok; HANDLE hProcess, h, pipeThreads[2]; char cmdline[100]; hProcess = GetCurrentProcess(); ZeroMemory(&pi, sizeof(PROCESS_INFORMATION)); ZeroMemory(&si, sizeof(STARTUPINFO)); si.cb = sizeof(STARTUPINFO); si.dwFlags = STARTF_USESTDHANDLES; si.hStdInput = INVALID_HANDLE_VALUE; ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES)); sa.nLength = sizeof(SECURITY_ATTRIBUTES); sa.lpSecurityDescriptor = NULL; sa.bInheritHandle = FALSE; /* * Create a non-inheritible pipe. */ CreatePipe(&Out.pipe, &h, &sa, 0); /* * Dupe the write side, make it inheritible, and close the original. */ DuplicateHandle(hProcess, h, hProcess, &si.hStdOutput, 0, TRUE, DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE); /* * Same as above, but for the error side. */ CreatePipe(&Err.pipe, &h, &sa, 0); DuplicateHandle(hProcess, h, hProcess, &si.hStdError, 0, TRUE, DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE); /* * Base command line. */ lstrcpy(cmdline, "cl.exe -nologo -c -TC -Zs -X -Fp.\\_junk.pch "); /* * Append our option for testing */ lstrcat(cmdline, option); /* * Filename to compile, which exists, but is nothing and empty. */ lstrcat(cmdline, " .\\nul"); ok = CreateProcess( NULL, /* Module name. */ cmdline, /* Command line. */ NULL, /* Process handle not inheritable. */ NULL, /* Thread handle not inheritable. */ TRUE, /* yes, inherit handles. */ DETACHED_PROCESS, /* No console for you. */ NULL, /* Use parent's environment block. */ NULL, /* Use parent's starting directory. */ &si, /* Pointer to STARTUPINFO structure. */ &pi); /* Pointer to PROCESS_INFORMATION structure. */ if (!ok) { DWORD err = GetLastError(); int chars = snprintf(msg, sizeof(msg) - 1, "Tried to launch: \"%s\", but got error [%u]: ", cmdline, err); FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS| FORMAT_MESSAGE_MAX_WIDTH_MASK, 0L, err, 0, (LPVOID)&msg[chars], (300-chars), 0); WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, lstrlen(msg), &err,NULL); return 2; } /* * Close our references to the write handles that have now been inherited. */ CloseHandle(si.hStdOutput); CloseHandle(si.hStdError); WaitForInputIdle(pi.hProcess, 5000); CloseHandle(pi.hThread); /* * Start the pipe reader threads. */ pipeThreads[0] = CreateThread(NULL, 0, ReadFromPipe, &Out, 0, &threadID); pipeThreads[1] = CreateThread(NULL, 0, ReadFromPipe, &Err, 0, &threadID); /* * Block waiting for the process to end. */ WaitForSingleObject(pi.hProcess, INFINITE); CloseHandle(pi.hProcess); /* * Wait for our pipe to get done reading, should it be a little slow. */ WaitForMultipleObjects(2, pipeThreads, TRUE, 500); CloseHandle(pipeThreads[0]); CloseHandle(pipeThreads[1]); /* * Look for the commandline warning code in both streams. * - in MSVC 6 & 7 we get D4002, in MSVC 8 we get D9002. */ return !(strstr(Out.buffer, "D4002") != NULL || strstr(Err.buffer, "D4002") != NULL || strstr(Out.buffer, "D9002") != NULL || strstr(Err.buffer, "D9002") != NULL || strstr(Out.buffer, "D2021") != NULL || strstr(Err.buffer, "D2021") != NULL); } static int CheckForLinkerFeature( const char **options, int count) { STARTUPINFO si; PROCESS_INFORMATION pi; SECURITY_ATTRIBUTES sa; DWORD threadID; char msg[300]; BOOL ok; HANDLE hProcess, h, pipeThreads[2]; int i; char cmdline[255]; hProcess = GetCurrentProcess(); ZeroMemory(&pi, sizeof(PROCESS_INFORMATION)); ZeroMemory(&si, sizeof(STARTUPINFO)); si.cb = sizeof(STARTUPINFO); si.dwFlags = STARTF_USESTDHANDLES; si.hStdInput = INVALID_HANDLE_VALUE; ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES)); sa.nLength = sizeof(SECURITY_ATTRIBUTES); sa.lpSecurityDescriptor = NULL; sa.bInheritHandle = TRUE; /* * Create a non-inheritible pipe. */ CreatePipe(&Out.pipe, &h, &sa, 0); /* * Dupe the write side, make it inheritible, and close the original. */ DuplicateHandle(hProcess, h, hProcess, &si.hStdOutput, 0, TRUE, DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE); /* * Same as above, but for the error side. */ CreatePipe(&Err.pipe, &h, &sa, 0); DuplicateHandle(hProcess, h, hProcess, &si.hStdError, 0, TRUE, DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE); /* * Base command line. */ lstrcpy(cmdline, "link.exe -nologo "); /* * Append our option for testing. */ for (i = 0; i < count; i++) { lstrcat(cmdline, " \""); lstrcat(cmdline, options[i]); lstrcat(cmdline, "\""); } ok = CreateProcess( NULL, /* Module name. */ cmdline, /* Command line. */ NULL, /* Process handle not inheritable. */ NULL, /* Thread handle not inheritable. */ TRUE, /* yes, inherit handles. */ DETACHED_PROCESS, /* No console for you. */ NULL, /* Use parent's environment block. */ NULL, /* Use parent's starting directory. */ &si, /* Pointer to STARTUPINFO structure. */ &pi); /* Pointer to PROCESS_INFORMATION structure. */ if (!ok) { DWORD err = GetLastError(); int chars = snprintf(msg, sizeof(msg) - 1, "Tried to launch: \"%s\", but got error [%u]: ", cmdline, err); FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS| FORMAT_MESSAGE_MAX_WIDTH_MASK, 0L, err, 0, (LPVOID)&msg[chars], (300-chars), 0); WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, lstrlen(msg), &err,NULL); return 2; } /* * Close our references to the write handles that have now been inherited. */ CloseHandle(si.hStdOutput); CloseHandle(si.hStdError); WaitForInputIdle(pi.hProcess, 5000); CloseHandle(pi.hThread); /* * Start the pipe reader threads. */ pipeThreads[0] = CreateThread(NULL, 0, ReadFromPipe, &Out, 0, &threadID); pipeThreads[1] = CreateThread(NULL, 0, ReadFromPipe, &Err, 0, &threadID); /* * Block waiting for the process to end. */ WaitForSingleObject(pi.hProcess, INFINITE); CloseHandle(pi.hProcess); /* * Wait for our pipe to get done reading, should it be a little slow. */ WaitForMultipleObjects(2, pipeThreads, TRUE, 500); CloseHandle(pipeThreads[0]); CloseHandle(pipeThreads[1]); /* * Look for the commandline warning code in the stderr stream. */ return !(strstr(Out.buffer, "LNK1117") != NULL || strstr(Err.buffer, "LNK1117") != NULL || strstr(Out.buffer, "LNK4044") != NULL || strstr(Err.buffer, "LNK4044") != NULL || strstr(Out.buffer, "LNK4224") != NULL || strstr(Err.buffer, "LNK4224") != NULL); } static DWORD WINAPI ReadFromPipe( LPVOID args) { pipeinfo *pi = (pipeinfo *) args; char *lastBuf = pi->buffer; DWORD dwRead; BOOL ok; again: if (lastBuf - pi->buffer + CHUNK > STATICBUFFERSIZE) { CloseHandle(pi->pipe); return (DWORD)-1; } ok = ReadFile(pi->pipe, lastBuf, CHUNK, &dwRead, 0L); if (!ok || dwRead == 0) { CloseHandle(pi->pipe); return 0; } lastBuf += dwRead; goto again; return 0; /* makes the compiler happy */ } static int IsIn( const char *string, const char *substring) { return (strstr(string, substring) != NULL); } /* * GetVersionFromFile -- * Looks for a match string in a file and then returns the version * following the match where a version is anything acceptable to * package provide or package ifneeded. */ static const char * GetVersionFromFile( const char *filename, const char *match, int numdots) { size_t cbBuffer = 100; static char szBuffer[100]; char *szResult = NULL; FILE *fp = fopen(filename, "rt"); if (fp != NULL) { /* * Read data until we see our match string. */ while (fgets(szBuffer, cbBuffer, fp) != NULL) { LPSTR p, q; p = strstr(szBuffer, match); if (p != NULL) { /* * Skip to first digit after the match. */ p += strlen(match); while (*p && !isdigit(*p)) { ++p; } /* * Find ending whitespace. */ q = p; while (*q && (strchr("0123456789.ab", *q)) && ((!strchr(".ab", *q) && (!strchr("ab", q[-1])) || --numdots))) { ++q; } memcpy(szBuffer, p, q - p); szBuffer[q-p] = 0; szResult = szBuffer; break; } } fclose(fp); } return szResult; } /* * List helpers for the SubstituteFile function */ typedef struct list_item_t { struct list_item_t *nextPtr; char * key; char * value; } list_item_t; /* insert a list item into the list (list may be null) */ static list_item_t * list_insert(list_item_t **listPtrPtr, const char *key, const char *value) { list_item_t *itemPtr = malloc(sizeof(list_item_t)); if (itemPtr) { itemPtr->key = strdup(key); itemPtr->value = strdup(value); itemPtr->nextPtr = NULL; while(*listPtrPtr) { listPtrPtr = &(*listPtrPtr)->nextPtr; } *listPtrPtr = itemPtr; } return itemPtr; } static void list_free(list_item_t **listPtrPtr) { list_item_t *tmpPtr, *listPtr = *listPtrPtr; while (listPtr) { tmpPtr = listPtr; listPtr = listPtr->nextPtr; free(tmpPtr->key); free(tmpPtr->value); free(tmpPtr); } } /* * SubstituteFile -- * As windows doesn't provide anything useful like sed and it's unreliable * to use the tclsh you are building against (consider x-platform builds - * eg compiling AMD64 target from IX86) we provide a simple substitution * option here to handle autoconf style substitutions. * The substitution file is whitespace and line delimited. The file should * consist of lines matching the regular expression: * \s*\S+\s+\S*$ * * Usage is something like: * nmakehlp -S << $** > $@ * @PACKAGE_NAME@ $(PACKAGE_NAME) * @PACKAGE_VERSION@ $(PACKAGE_VERSION) * << */ static int SubstituteFile( const char *substitutions, const char *filename) { size_t cbBuffer = 1024; static char szBuffer[1024], szCopy[1024]; char *szResult = NULL; list_item_t *substPtr = NULL; FILE *fp, *sp; fp = fopen(filename, "rt"); if (fp != NULL) { /* * Build a list of substutitions from the first filename */ sp = fopen(substitutions, "rt"); if (sp != NULL) { while (fgets(szBuffer, cbBuffer, sp) != NULL) { unsigned char *ks, *ke, *vs, *ve; ks = (unsigned char*)szBuffer; while (ks && *ks && isspace(*ks)) ++ks; ke = ks; while (ke && *ke && !isspace(*ke)) ++ke; vs = ke; while (vs && *vs && isspace(*vs)) ++vs; ve = vs; while (ve && *ve && !(*ve == '\r' || *ve == '\n')) ++ve; *ke = 0, *ve = 0; list_insert(&substPtr, (char*)ks, (char*)vs); } fclose(sp); } /* debug: dump the list */ #ifdef _DEBUG { int n = 0; list_item_t *p = NULL; for (p = substPtr; p != NULL; p = p->nextPtr, ++n) { fprintf(stderr, "% 3d '%s' => '%s'\n", n, p->key, p->value); } } #endif /* * Run the substitutions over each line of the input */ while (fgets(szBuffer, cbBuffer, fp) != NULL) { list_item_t *p = NULL; for (p = substPtr; p != NULL; p = p->nextPtr) { char *m = strstr(szBuffer, p->key); if (m) { char *cp, *op, *sp; cp = szCopy; op = szBuffer; while (op != m) *cp++ = *op++; sp = p->value; while (sp && *sp) *cp++ = *sp++; op += strlen(p->key); while (*op) *cp++ = *op++; *cp = 0; memcpy(szBuffer, szCopy, sizeof(szCopy)); } } printf(szBuffer); } list_free(&substPtr); } fclose(fp); return 0; } BOOL FileExists(LPCTSTR szPath) { #ifndef INVALID_FILE_ATTRIBUTES #define INVALID_FILE_ATTRIBUTES ((DWORD)-1) #endif DWORD pathAttr = GetFileAttributes(szPath); return (pathAttr != INVALID_FILE_ATTRIBUTES && !(pathAttr & FILE_ATTRIBUTE_DIRECTORY)); } /* * QualifyPath -- * * This composes the current working directory with a provided path * and returns the fully qualified and normalized path. * Mostly needed to setup paths for testing. */ static int QualifyPath( const char *szPath) { char szCwd[MAX_PATH + 1]; GetFullPathName(szPath, sizeof(szCwd)-1, szCwd, NULL); printf("%s\n", szCwd); return 0; } /* * Implements LocateDependency for a single directory. See that command * for an explanation. * Returns 0 if found after printing the directory. * Returns 1 if not found but no errors. * Returns 2 on any kind of error * Basically, these are used as exit codes for the process. */ static int LocateDependencyHelper(const char *dir, const char *keypath) { HANDLE hSearch; char path[MAX_PATH+1]; int dirlen, keylen, ret; WIN32_FIND_DATA finfo; if (dir == NULL || keypath == NULL) return 2; /* Have no real error reporting mechanism into nmake */ dirlen = strlen(dir); if ((dirlen + 3) > sizeof(path)) return 2; strncpy(path, dir, dirlen); strncpy(path+dirlen, "\\*", 3); /* Including terminating \0 */ keylen = strlen(keypath); #if 0 /* This function is not available in Visual C++ 6 */ /* * Use numerics 0 -> FindExInfoStandard, * 1 -> FindExSearchLimitToDirectories, * as these are not defined in Visual C++ 6 */ hSearch = FindFirstFileEx(path, 0, &finfo, 1, NULL, 0); #else hSearch = FindFirstFile(path, &finfo); #endif if (hSearch == INVALID_HANDLE_VALUE) return 1; /* Not found */ /* Loop through all subdirs checking if the keypath is under there */ ret = 1; /* Assume not found */ do { int sublen; /* * We need to check it is a directory despite the * FindExSearchLimitToDirectories in the above call. See SDK docs */ if ((finfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) continue; sublen = strlen(finfo.cFileName); if ((dirlen+1+sublen+1+keylen+1) > sizeof(path)) continue; /* Path does not fit, assume not matched */ strncpy(path+dirlen+1, finfo.cFileName, sublen); path[dirlen+1+sublen] = '\\'; strncpy(path+dirlen+1+sublen+1, keypath, keylen+1); if (FileExists(path)) { /* Found a match, print to stdout */ path[dirlen+1+sublen] = '\0'; QualifyPath(path); ret = 0; break; } } while (FindNextFile(hSearch, &finfo)); FindClose(hSearch); return ret; } /* * LocateDependency -- * * Locates a dependency for a package. * keypath - a relative path within the package directory * that is used to confirm it is the correct directory. * The search path for the package directory is currently only * the parent and grandparent of the current working directory. * If found, the command prints * name_DIRPATH=<full path of located directory> * and returns 0. If not found, does not print anything and returns 1. */ static int LocateDependency(const char *keypath) { int i, ret; static char *paths[] = {"..", "..\\..", "..\\..\\.."}; for (i = 0; i < (sizeof(paths)/sizeof(paths[0])); ++i) { ret = LocateDependencyHelper(paths[i], keypath); if (ret == 0) return ret; } return ret; } /* * Local variables: * mode: c * c-basic-offset: 4 * fill-column: 78 * indent-tabs-mode: t * tab-width: 8 * End: */ |
Added jni/rl_json/win/rules-ext.vc.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 | # This file should only be included in makefiles for Tcl extensions, # NOT in the makefile for Tcl itself. !ifndef _RULES_EXT_VC # We need to run from the directory the parent makefile is located in. # nmake does not tell us what makefile was used to invoke it so parent # makefile has to set the MAKEFILEVC macro or we just make a guess and # warn if we think that is not the case. !if "$(MAKEFILEVC)" == "" !if exist("$(PROJECT).vc") MAKEFILEVC = $(PROJECT).vc !elseif exist("makefile.vc") MAKEFILEVC = makefile.vc !endif !endif # "$(MAKEFILEVC)" == "" !if !exist("$(MAKEFILEVC)") MSG = ^ You must run nmake from the directory containing the project makefile.^ If you are doing that and getting this message, set the MAKEFILEVC^ macro to the name of the project makefile. !message WARNING: $(MSG) !endif !if "$(PROJECT)" == "tcl" !error The rules-ext.vc file is not intended for Tcl itself. !endif # We extract version numbers using the nmakehlp program. For now use # the local copy of nmakehlp. Once we locate Tcl, we will use that # one if it is newer. !if [$(CC) -nologo "nmakehlp.c" -link -subsystem:console > nul] !endif # First locate the Tcl directory that we are working with. !if "$(TCLDIR)" != "" _RULESDIR = $(TCLDIR:/=\) !else # If an installation path is specified, that is also the Tcl directory. # Also Tk never builds against an installed Tcl, it needs Tcl sources !if defined(INSTALLDIR) && "$(PROJECT)" != "tk" _RULESDIR=$(INSTALLDIR:/=\) !else # Locate Tcl sources !if [echo _RULESDIR = \> nmakehlp.out] \ || [nmakehlp -L generic\tcl.h >> nmakehlp.out] _RULESDIR = ..\..\tcl !else !include nmakehlp.out !endif !endif # defined(INSTALLDIR).... !endif # ifndef TCLDIR # Now look for the targets.vc file under the Tcl root. Note we check this # file and not rules.vc because the latter also exists on older systems. !if exist("$(_RULESDIR)\lib\nmake\targets.vc") # Building against installed Tcl _RULESDIR = $(_RULESDIR)\lib\nmake !elseif exist("$(_RULESDIR)\win\targets.vc") # Building against Tcl sources _RULESDIR = $(_RULESDIR)\win !else # If we have not located Tcl's targets file, most likely we are compiling # against an older version of Tcl and so must use our own support files. _RULESDIR = . !endif !if "$(_RULESDIR)" != "." # Potentially using Tcl's support files. If this extension has its own # nmake support files, need to compare the versions and pick newer. !if exist("rules.vc") # The extension has its own copy !if [echo TCL_RULES_MAJOR = \> versions.vc] \ && [nmakehlp -V "$(_RULESDIR)\rules.vc" RULES_VERSION_MAJOR >> versions.vc] !endif !if [echo TCL_RULES_MINOR = \>> versions.vc] \ && [nmakehlp -V "$(_RULESDIR)\rules.vc" RULES_VERSION_MINOR >> versions.vc] !endif !if [echo OUR_RULES_MAJOR = \>> versions.vc] \ && [nmakehlp -V "rules.vc" RULES_VERSION_MAJOR >> versions.vc] !endif !if [echo OUR_RULES_MINOR = \>> versions.vc] \ && [nmakehlp -V "rules.vc" RULES_VERSION_MINOR >> versions.vc] !endif !include versions.vc # We have a newer version of the support files, use them !if ($(TCL_RULES_MAJOR) != $(OUR_RULES_MAJOR)) || ($(TCL_RULES_MINOR) < $(OUR_RULES_MINOR)) _RULESDIR = . !endif !endif # if exist("rules.vc") !endif # if $(_RULESDIR) != "." # Let rules.vc know what copy of nmakehlp.c to use. NMAKEHLPC = $(_RULESDIR)\nmakehlp.c # Get rid of our internal defines before calling rules.vc !undef TCL_RULES_MAJOR !undef TCL_RULES_MINOR !undef OUR_RULES_MAJOR !undef OUR_RULES_MINOR !if exist("$(_RULESDIR)\rules.vc") !message *** Using $(_RULESDIR)\rules.vc !include "$(_RULESDIR)\rules.vc" !else !error *** Could not locate rules.vc in $(_RULESDIR) !endif !endif # _RULES_EXT_VC |