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
|
/*
Copyright (C) 2022 The University of Notre Dame
This software is distributed under the GNU General Public License.
See the file COPYING for details.
*/
#ifndef JX_H
#define JX_H
/** @file jx.h JSON Expressions (JX) library.
This module implements extended JSON expressions as a C library.
We use our own custom library to avoid external dependencies and
to implement various extensions to strict JSON.
For example, the following bit of code:
<pre>
struct jx * obj = jx_object(
jx_pair(
jx_string("hello"),
jx_string("world"),
0)
);
jx_print_file(jx,stdout);
jx_delete(jx);
</pre>
Will create the following output:
<pre>
{ "hello" : "world" }
</pre>
@see jx_parse.h
@see jx_print.h
*/
#include <stdint.h>
#include <inttypes.h>
/** JX atomic type. */
typedef enum {
JX_NULL = 0, /**< null value */
JX_BOOLEAN, /**< true or false */
JX_INTEGER, /**< integer value */
JX_DOUBLE, /**< floating point value */
JX_STRING, /**< string value */
JX_SYMBOL, /**< variable identifier */
JX_ARRAY, /**< array containing values */
JX_OBJECT, /**< object containing key-value pairs */
JX_OPERATOR, /**< operator on multiple values. */
JX_ERROR, /**< indicates failed evaluation */
} jx_type_t;
typedef int64_t jx_int_t;
#define PRIiJX PRIi64
struct jx_comprehension {
unsigned line;
char *variable; /**< variable for comprehension */
struct jx *elements; /**< items for list comprehension */
struct jx *condition; /**< condition for filtering list comprehension */
struct jx_comprehension *next;
};
/** JX item linked-list used by @ref JX_ARRAY and @ref jx.items */
struct jx_item {
unsigned line;
struct jx *value; /**< value of this item */
struct jx_comprehension *comp;
struct jx_item *next; /**< pointer to next item */
};
/** JX key-value pairs used by @ref JX_OBJECT and @ref jx.pairs */
struct jx_pair {
unsigned line;
struct jx *key; /**< key of this pair */
struct jx *value; /**< value of this pair */
struct jx_comprehension *comp;
struct jx_pair *next; /**< pointer to next pair */
};
typedef enum {
JX_OP_EQ,
JX_OP_NE,
JX_OP_LE,
JX_OP_LT,
JX_OP_GE,
JX_OP_GT,
JX_OP_ADD,
JX_OP_SUB,
JX_OP_MUL,
JX_OP_DIV,
JX_OP_MOD,
JX_OP_AND,
JX_OP_OR,
JX_OP_NOT,
JX_OP_LOOKUP,
JX_OP_CALL,
JX_OP_SLICE,
JX_OP_DOT,
JX_OP_INVALID,
} jx_operator_t;
struct jx_operator {
jx_operator_t type;
unsigned line;
struct jx *left;
struct jx *right;
};
/** JX value representing any expression type. */
struct jx {
jx_type_t type; /**< type of this value */
unsigned line; /**< line where this value was defined */
union {
int boolean_value; /**< value of @ref JX_BOOLEAN */
jx_int_t integer_value; /**< value of @ref JX_INTEGER */
double double_value; /**< value of @ref JX_DOUBLE */
char * string_value; /**< value of @ref JX_STRING */
char * symbol_name; /**< value of @ref JX_SYMBOL */
struct jx_item *items; /**< value of @ref JX_ARRAY */
struct jx_pair *pairs; /**< value of @ref JX_OBJECT */
struct jx_operator oper; /**< value of @ref JX_OPERATOR */
struct jx *err; /**< error value of @ref JX_ERROR */
} u;
};
/** Create a JX null value. @return A JX expression. */
struct jx * jx_null();
/** Create a JX boolean value. @param boolean_value A C boolean value. @return A JX boolean value.*/
struct jx * jx_boolean( int boolean_value );
/** Create a JX integer value. @param integer_value A C integer. @return a JX integer value. */
struct jx * jx_integer( jx_int_t integer_value );
/** Create a JX floating point value. @param double_value A C double precision floating point. @return a JX double value. */
struct jx * jx_double( double double_value );
/** Create a JX string value. @param string_value A C string, which will be duplicated via strdup(). @return A JX string value. */
struct jx * jx_string( const char *string_value );
/** Create a JX string value without copying (uncommon). @param string_value A C string, which will be *not* be duplicated, but will be freed at object deletion. @return A JX string value. */
struct jx * jx_string_nocopy( char *string_value );
/** Create a JX string value using prinf style formatting. @param fmt A printf-style format string, followed by matching arguments. @return A JX string value. */
struct jx * jx_format( const char *fmt, ... );
/** Create a JX symbol.
Note that symbols are an extension to the JSON standard. A symbol is a reference to an external variable, which can be resolved by using @ref jx_eval
@param symbol_name A C string.
@return A JX expression.
*/
struct jx * jx_symbol( const char *symbol_name );
/** Create a JX_ERROR.
@param err The associated data for the error. This should be a string description of the error.
@return A JX error value.
*/
struct jx * jx_error( struct jx *err );
/** Create a JX array. @param items A linked list of @ref jx_item values. @return A JX array. */
struct jx * jx_array( struct jx_item *items );
/** Create a JX array with inline items. @param value One or more items of the array must be given, terminated with a null value. @return A JX array. */
struct jx * jx_arrayv( struct jx *value, ... );
/** Create a JX object. @param pairs A linked list of @ref jx_pair key-value pairs. @return a JX object. */
struct jx * jx_object( struct jx_pair *pairs );
/** Create a JX object. Arguments are alternating string key -- *jx values. Must be termianted with a null value.
* This is syntactic sugar for jx_object(jx_pair(jx_string(k), v, jx_pair(jx_string(k), v, jx_pair( etc.))));
*
* struct jx *j = jx_objectv("A", jx_integer(42), // key and value of A,
* "B", jx_objectv("C", jx_string("xyz") // key and value of B
* NULL)); // terminating null value.
* @param key Name of the first property
* @param value Value for the first property
* @param ... Alternating key-value pairs
* @return a JX object*/
struct jx * jx_objectv( const char *key, struct jx *value, ... );
/** Create a JX binary expression, @param oper The kind of operator. @param left The left side of the expression. @param right The right side of the expression. */
struct jx * jx_operator( jx_operator_t oper, struct jx *left, struct jx *right );
/** Create a JX key-value pair. @param key The key. @param value The value. @param next The next item in the linked list. @return A key-value pair.*/
struct jx_pair * jx_pair( struct jx *key, struct jx *value, struct jx_pair *next );
/** Create a JX array item. @param value The value of this item. @param next The next item in the linked list. @return An array item. */
struct jx_item * jx_item( struct jx *value, struct jx_item *next );
/** Create a JX comprehension.
* @param variable The variable name to bind.
* @param elements The elements to bind.
* @param condition The boolean filter to evaluate.
* @param next Nested comprehension(s).
* @returns A JX comprehension.
*/
struct jx_comprehension *jx_comprehension(const char *variable, struct jx *elements, struct jx *condition, struct jx_comprehension *next);
/** Test an expression's type. @param j An expression. @param type The desired type. @return True if the expression type matches, false otherwise. */
int jx_istype( struct jx *j, jx_type_t type );
/** Test for an atomic value. @param j An expression. @return True if the expression is an atomic integer, float, string or boolean. */
int jx_isatomic( struct jx *j );
/** Test an expression for the boolean value TRUE. @param j An expression to test. @return True if the expression is boolean and true. */
int jx_istrue( struct jx *j );
/** Test an expression for the boolean value FALSE @param j An expression to test. @return True if the expression is boolean and false. */
int jx_isfalse( struct jx *j );
int jx_comprehension_equals(struct jx_comprehension *j, struct jx_comprehension *k);
int jx_item_equals(struct jx_item *j, struct jx_item *k);
int jx_pair_equals(struct jx_pair *j, struct jx_pair *k);
/** Test two expressions for equality. @param j A constant expression. @param k A constant expression. @return True if equal, false if not.
*/
int jx_equals( struct jx *j, struct jx *k );
/** Get the length of an array. Returns -1 if array is null or not an array. @param array The array to check. */
int jx_array_length( struct jx *array );
struct jx_comprehension *jx_comprehension_copy(struct jx_comprehension *c);
struct jx_item *jx_item_copy(struct jx_item *i);
struct jx_pair *jx_pair_copy(struct jx_pair *p);
/** Duplicate an expression. @param j An expression. @return A copy of the expression, which must be deleted by @ref jx_delete
*/
struct jx * jx_copy( struct jx *j );
/** Delete an expression recursively. @param j An expression to delete. */
void jx_delete( struct jx *j );
/** Delete a key-value pair. @param p The key-value pair to delete. */
void jx_pair_delete( struct jx_pair *p );
/** Delete an array item. @param i The array item to delete. */
void jx_item_delete( struct jx_item *i );
/** Delete a comprehension. @param comp The comprehension to delete. */
void jx_comprehension_delete(struct jx_comprehension *comp);
/** Remove a key-value pair from an object. @param object The object. @param key The key. @return The corresponding value, or null if it is not present. */
struct jx * jx_remove( struct jx *object, struct jx *key );
/** Insert a key-value pair into an object. @param object The object. @param key The key. @param value The value. @return True on success, false on failure. Failure can only occur if the object is not a @ref JX_OBJECT. */
int jx_insert( struct jx *object, struct jx *key, struct jx *value );
/** Insert a key-value pair into an object, unless the value is an empty collection, in which case delete the key and value. @param object The target object. @param key The key. @param value The value. @return 1 on success, -1 on empty value, 0 on failure. Failure can only occur if the object is not a @ref JX_OBJECT. */
int jx_insert_unless_empty( struct jx *object, struct jx *key, struct jx *value );
/** Insert a boolean value into an object @param object The object @param key The key represented as a C string @param value The boolean value. */
void jx_insert_boolean( struct jx *object, const char *key, int value );
/** Insert an integer value into an object @param object The object @param key The key represented as a C string @param value The integer value. */
void jx_insert_integer( struct jx *object, const char *key, jx_int_t value );
/** Insert a double value into an object @param object The object @param key The key represented as a C string @param value The double value. */
void jx_insert_double( struct jx *object, const char *key, double value );
/** Insert a string value into an object @param object The object @param key The key represented as a C string @param value The C string value. */
void jx_insert_string( struct jx *object, const char *key, const char *value );
/** Search for a arbitrary item in an object. The key is an ordinary string value. @param object The object in which to search. @param key The string key to match. @return The value of the matching pair, or null if none is found. */
struct jx * jx_lookup( struct jx *object, const char *key );
/* Like @ref jx_lookup, but found is set to 1 when the key is found. Useful for when value is false. */
struct jx * jx_lookup_guard( struct jx *j, const char *key, int *found );
/** Search for a string item in an object. The key is an ordinary string value. @param object The object in which to search. @param key The string key to match. @return The C string value of the matching object, or null if it is not found, or is not a string. */
const char * jx_lookup_string( struct jx *object, const char *key );
/** Search for a string item in an object. Behaves the same as jx_lookup_string, but returns a duplicated copy. The result must be deallocated with free(). @param object The object in which to search. @param key The string key to match. @return The C string value of the matching object, or null if it is not found, or is not a string. */
char * jx_lookup_string_dup( struct jx *object, const char *key );
/** Search for an integer item in an object. The key is an ordinary string value. @param object The object in which to search. @param key The string key to match. @return The integer value of the matching object, or zero if it is not found, or is not an integer. */
jx_int_t jx_lookup_integer( struct jx *object, const char *key );
/** Search for a boolean item in an object. The key is an ordinary string value. @param object The object in which to search. @param key The string key to match. @return One if the value of the matching object is true, or zero if it is false, not found, or not a boolean. */
int jx_lookup_boolean( struct jx *object, const char *key );
/** Search for a double item in an object. The key is an ordinary string value. @param object The object in which to search. @param key The string key to match. @return The double value of the matching object, or null if it is not found, or is not a double. */
double jx_lookup_double( struct jx *object, const char *key );
/** Insert an item at the beginning of an array. @param array The array to modify. @param value The value to insert. */
void jx_array_insert( struct jx *array, struct jx *value );
/** Append an item at the end of an array. @param array The array to modify. @param value The value to append. */
void jx_array_append( struct jx *array, struct jx *value );
/** Get the nth item in an array. @param array The array to search. @param nth The index of the desired value. @return The nth element, or NULL if the index is out of bounds. */
struct jx * jx_array_index( struct jx *array, int nth );
/** Concatenate the given arrays into a single array. The passed arrays are consumed. @param array An array to concatenate. The list of arrays must be terminated by NULL. */
struct jx *jx_array_concat( struct jx *array, ...);
/** Remove and return the first element in the array.
* @param array The JX_ARRAY to update.
* @returns The first value in array, or NULL if array is empty or not a JX_ARRAY. */
struct jx *jx_array_shift(struct jx *array);
/** Determine if an expression is constant. Traverses the expression recursively, and returns true if it consists only of constant values, arrays, and objects. @param j The expression to evaluate. @return True if constant. */
int jx_is_constant( struct jx *j );
/** Export a jx object into the current environment using setenv(). @param j A JX_OBJECT. */
void jx_export( struct jx *j );
/** Iterate over the values in an array.
* The iteration state is stored by the caller in an opaque pointer variable. When starting iteration,
* the caller MUST pass the address of a pointer initialized to NULL as i. It is undefined behavior
* to pass a non-NULL iterator variable not set by a previous call. Subsequent calls should
* use the same variable to continue iteration. After the initial call, the value of j is ignored.
*
* struct jx *item;
* for (void *i = NULL; (item = jx_iterate_array(j, &i));) {
* printf("array item: ");
* jx_print_stream(item, stdout);
* printf("\n");
* }
*
* @param j The JX_ARRAY to iterate over.
* @param i A variable to store the iteration state.
* @return A pointer to each item in the array, and NULL when iteration is finished.
*/
struct jx * jx_iterate_array(struct jx *j, void **i);
/** Iterate over the values in an object.
* The iteration state is stored by the caller in an opaque pointer variable. When starting iteration,
* the caller MUST pass the address of a pointer initialized to NULL as i. It is undefined behavior
* to pass a non-NULL iterator variable not set by a previous call. Subsequent calls should
* use the same variable to continue iteration. After the initial call, the value of j is ignored.
*
* struct jx *item;
* void *i = NULL;
* while ((item = jx_iterate_values(j, &i))) {
* printf("object value: ");
* jx_print_stream(item, stdout);
* printf("\n");
* }
*
* @param j The JX_OBJECT to iterate over.
* @param i A variable to store the iteration state.
* @return A pointer to each value in the object, and NULL when iteration is finished.
*/
struct jx * jx_iterate_values(struct jx *j, void **i);
/** Iterate over the keys in an object.
* The iteration state is stored by the caller in an opaque pointer variable. When starting iteration,
* the caller MUST pass the address of a pointer initialized to NULL as i. It is undefined behavior
* to pass a non-NULL iterator variable not set by a previous call. Subsequent calls should
* use the same variable to continue iteration. After the initial call, the value of j is ignored.
*
* struct jx *item;
* void *i = NULL;
* while ((item = jx_iterate_keys(j, &i))) {
* printf("object key: ");
* jx_print_stream(item, stdout);
* printf("\n");
* }
*
* @param j The JX_OBJECT to iterate over.
* @param i A variable to store the iteration state.
* @return A pointer to each key in the object, and NULL when iteration is finished.
*/
const char *jx_iterate_keys(struct jx *j, void **i);
/* Get the current key while iterating over an object.
* The iteration variable must have been passed to jx_iterate_keys
* or jx_iterate_values. This directly fetches the current key rather than
* doing a lookup from the beginning, so it takes constant time and
* can handle repeated keys.
*/
const char *jx_get_key(void **i);
/* Get the current value while iterating over an object.
* The iteration variable must have been passed to jx_iterate_keys
* or jx_iterate_values. This directly fetches the current value rather than
* doing a lookup from the beginning, so it takes constant time and
* can handle repeated keys.
*/
struct jx *jx_get_value(void **i);
/** Merge an arbitrary number of JX_OBJECTs into a single new one. The constituent objects are not consumed. Objects are merged in the order given, i.e. a key can replace an identical key in a preceding object. The last argument must be NULL to mark the end of the list. @return A merged JX_OBJECT that must be deleted with jx_delete. */
struct jx *jx_merge(struct jx *j, ...);
#endif
/*vim: set noexpandtab tabstop=8: */
|