#if !defined(S2_AMALGAMATION_BUILD) # define S2_AMALGAMATION_BUILD #endif #if !defined(_WIN32) # if ! defined(_XOPEN_SOURCE) /** on Linux, required for usleep(). */ # define _XOPEN_SOURCE 500 # endif # ifndef _XOPEN_SOURCE_EXTENDED # define _XOPEN_SOURCE_EXTENDED # endif # ifndef _BSD_SOURCE # define _BSD_SOURCE # endif #endif /* start of file ../cwal_amalgamation.h */ /* start of file include/wh/cwal/cwal_config.h */ #if !defined(WANDERINGHORSE_NET_CWAL_CONFIG_H_INCLUDED) #define WANDERINGHORSE_NET_CWAL_CONFIG_H_INCLUDED 1 #if defined(__cplusplus) && !defined(__STDC_FORMAT_MACROS) /* inttypes.h needs this for the PRI* and SCN* macros in C++ mode. */ # define __STDC_FORMAT_MACROS #endif #include /* PRIuXX macros and fixed-size integers. Actually C99, but i can live with that for fixed-size int types and standard printf/scanf specifiers. */ /*#define CWAL_VOID_PTR_IS_BIG 1*/ #if defined(CWAL_SIZE_T_BITS) # error "CWAL_SIZE_T_BITS must not be defined before including this file! Edit this file instead!" #endif /** @def CWAL_ENABLE_JSON_PARSER If CWAL_ENABLE_JSON_PARSER is set to 0 then the cwal_json_parse() family of functions get disabled (they still exist, but will return errors). It has no effect on the JSON output routines. The reason for this is only one of licensing: the (3rd-party) parser uses a BSD-style license with a "do no evil" clause, and that clause prohibits the code from being used in certain package repositories. Note that setting this macro might not be enough to please package maintainers - they may also require physical removal of the code blocked off by this macro. It can all be found in cwal_json.c. */ #if !defined(CWAL_ENABLE_JSON_PARSER) #define CWAL_ENABLE_JSON_PARSER 1 #endif /** @def CWAL_DISABLE_FLOATING_POINT "The plan is" to allow compiling cwal with no support for doubles, but that is not yet in place. */ #if !defined(CWAL_DISABLE_FLOATING_POINT) #define CWAL_DISABLE_FLOATING_POINT 0 #endif /** @def CWAL_VOID_PTR_IS_BIG ONLY define this to a true value if you know that (sizeof(cwal_int_t) <= sizeof(void*)) If that is the case, cwal does not need to dynamically allocate integers. ALL CLIENTS must use the same value for this macro. This value is (or was?) also used to optimize/modify/make-more-portable a couple other internal bits. FIXME: figure out exactly how this plays together with CWAL_INT_T_BITS and document what combinations are portable across c89/c99 and 32/64 bits. */ #if !defined(CWAL_VOID_PTR_IS_BIG) /* Largely taken from http://predef.sourceforge.net/prearch.html See also: http://poshlib.hookatooka.com/poshlib/trac.cgi/browser/posh.h */ # if defined(_WIN64) || defined(__LP64__)/*gcc*/ \ || defined(_M_X64) || defined(__amd64__) || defined(__amd64) \ || defined(__x86_64__) || defined(__x86_64) \ || defined(__ia64__) || defined(__ia64) || defined(_IA64) || defined(__IA64__) \ || defined(_M_IA64) \ || defined(__sparc_v9__) || defined(__sparcv9) || defined(_ADDR64) \ || defined(__64BIT__) # define CWAL_VOID_PTR_IS_BIG 1 # else # define CWAL_VOID_PTR_IS_BIG 0 # endif #endif /** @def CWAL_SIZE_T_BITS CWAL_SIZE_T_BITS defines the number of bits used by cwal's primary cwal_size_t. This is used so that cwal can guaranty and enforce certain number ranges. This value must be one of (16,32,64), though values lower than 32 may or may not work well with any given component of the library (e.g. string interning can use relatively much compared to the rest of the lib). */ /** @def CWAL_INT_T_BITS CWAL_INT_T_BITS is the cwal_int_t counterpart of CWAL_SIZE_T_BITS. cwal_int_t is independent of cwal_size_t, and may have a different size. */ #if 0 /* For testing purposes, or if a client wants to override this globally... */ #define CWAL_SIZE_T_BITS 16 #endif #if !defined(CWAL_SIZE_T_BITS) /* CWAL_SIZE_T_BITS "really should" not be higher than the pointer size (in bits), since it would be impossible to allocate anywhere near that number of items and this value is largely used as a length- and reference counter. It is NOT anticipated that cwal will be used in any environments where an unsigned 32-bit limit could ever be reached for the things it uses cwal_size_t for (discounting artifical/malicious inflation of reference counts and such). A value of 16 is perfectly reasonable for small use cases. It doesn't save _much_ memory, but it does save some. */ # if CWAL_VOID_PTR_IS_BIG # define CWAL_SIZE_T_BITS 64 # else # define CWAL_SIZE_T_BITS 32 # endif #endif #if !defined(CWAL_INT_T_BITS) /* The ONLY reason we fall back to 32 bits here is because C89 lacks a portable printf format string for the equivalent of PRIi64 :/. Other than that 64-bits will (should!) work find on 32-bit platforms as long as CWAL_VOID_PTR_IS_BIG is false. In C99 mode 64-bit int compiles fine on 32-bit (because the result of PRIi64 is well-defined there). */ # if CWAL_VOID_PTR_IS_BIG || (defined(__STDC_VERSION__) && (__STDC_VERSION__>=199901L)) # define CWAL_INT_T_BITS CWAL_SIZE_T_BITS # else # define CWAL_INT_T_BITS CWAL_SIZE_T_BITS # endif #endif /** @def CWAL_SIZE_T_PFMT Is is a printf-style specifier, minus the '%' prefix, for use with cwal_size_t arguments. It can be used like this: @code cwal_size_t x = 42; printf("The value of x is %"CWAL_SIZE_T_PFMT".", x ); @endcode Using this constant ensures that the printf-style commands work when cwal_size_t is of varying sizes. @see CWAL_SIZE_T_SFMT */ /** @def CWAL_SIZE_T_SFMT CWAL_SIZE_T_SFMT is the scanf counterpart of CWAL_SIZE_T_PFMT. @see CWAL_SIZE_T_PFMT @see CWAL_SIZE_T_SFMT */ /** @def CWAL_SIZE_T_PFMTX CWAL_SIZE_T_PFMTX is the hexidecimal counterpart of CWAL_SIZE_T_PFMT. @see CWAL_SIZE_T_PFMT */ /** @def CWAL_SIZE_T_SFMTX CWAL_SIZE_T_SFMTX is the hexidecimal counterpart to CWAL_SIZE_T_SFMT. @see CWAL_SIZE_T_PFMT @see CWAL_SIZE_T_SFMT */ /** @def CWAL_INT_T_PFMT CWAL_INT_T_PFMT is the cwal_int_t counterpart of CWAL_SIZE_T_PFMT. @see CWAL_SIZE_T_PFMT @see CWAL_INT_T_SFMT */ /** @def CWAL_INT_T_SFMT CWAL_INT_T_SFMT is the scanf counterpart of CWAL_INT_T_PFMT. @see CWAL_INT_T_PFMT */ /** @def CWAL_INT_T_PFMTX CWAL_INT_T_PFMTX is the hexidecimal counterpart of CWAL_INT_T_PFMT. @see CWAL_INT_T_SFMT */ /** @def CWAL_INT_T_SFMTX CWAL_INT_T_SFMTX is the hexidecimal counterpart to CWAL_INT_T_SFMT. @see CWAL_INT_T_PFMT @see CWAL_INT_T_SFMT */ /** @def CWAL_INT_T_MIN CWAL_INT_T_MIN is the minimum value of the data type cwal_int_t. @see CWAL_INT_T_MAX */ /** @def CWAL_INT_T_MAX CWAL_INT_T_MAX is the maximum value of the data type cwal_int_t. @see CWAL_INT_T_MAX */ /** typedef some_unsigned_int_type_which_is_CWAL_SIZE_T_BITS_long cwal_size_t cwal_size_t is a configurable unsigned integer type specifying the ranges used by this library. Its exact type depends on the value of CWAL_SIZE_T_BITS: it will be uintXX_t, where XX is the value of CWAL_SIZE_T_BITS (16, 32, or 64). We use a fixed-size numeric type, instead of relying on a standard type with an unspecified size (e.g. size_t) to help avoid nasty surprises when porting to machines with different size_t sizes. For cwal's intended purposes uint16_t is "almost certainly" fine, but those who are concerned about 64kb limitations on certain contexts might want to set this to uint32_t. */ /** @typedef some_signed_integer cwal_int_t This is the type of integer value used by the library. */ /* Set up CWAL_SIZE_T... */ #if CWAL_SIZE_T_BITS == 16 # define CWAL_SIZE_T_PFMT PRIu16 # define CWAL_SIZE_T_PFMTX PRIx16 # define CWAL_SIZE_T_SFMT SCNu16 # define CWAL_SIZE_T_SFMTX SCNx16 # define CWAL_SIZE_T_MAX 65535U typedef uint16_t cwal_size_t; #elif CWAL_SIZE_T_BITS == 32 # define CWAL_SIZE_T_PFMT PRIu32 # define CWAL_SIZE_T_PFMTX PRIx32 # define CWAL_SIZE_T_SFMT SCNu32 # define CWAL_SIZE_T_SFMTX SCNx32 # define CWAL_SIZE_T_MAX 4294967295U typedef uint32_t cwal_size_t; #elif CWAL_SIZE_T_BITS == 64 # define CWAL_SIZE_T_PFMT PRIu64 # define CWAL_SIZE_T_PFMTX PRIx64 # define CWAL_SIZE_T_SFMT SCNu64 # define CWAL_SIZE_T_SFMTX SCNx64 # define CWAL_SIZE_T_MAX 18446744073709551615U typedef uint64_t cwal_size_t; #else # error "CWAL_SIZE_T_BITS must be one of: 16, 32, 64" #endif /* Set up CWAL_INT_... */ #if CWAL_INT_T_BITS == 16 # define CWAL_INT_T_PFMT PRIi16 # define CWAL_INT_T_PFMTX PRIx16 # define CWAL_INT_T_SFMT SCNi16 # define CWAL_INT_T_SFMTX SCNx16 # define CWAL_INT_T_MAX 32767 typedef int16_t cwal_int_t; #elif CWAL_INT_T_BITS == 32 # define CWAL_INT_T_PFMT PRIi32 # define CWAL_INT_T_PFMTX PRIx32 # define CWAL_INT_T_SFMT SCNi32 # define CWAL_INT_T_SFMTX SCNx32 # define CWAL_INT_T_MAX 2147483647 typedef int32_t cwal_int_t; #elif CWAL_INT_T_BITS == 64 # define CWAL_INT_T_PFMT PRIi64 # define CWAL_INT_T_PFMTX PRIx64 # define CWAL_INT_T_SFMT SCNi64 # define CWAL_INT_T_SFMTX SCNx64 # define CWAL_INT_T_MAX 9223372036854775807 typedef int64_t cwal_int_t; #else # error "CWAL_INT_T_BITS must be one of: 16, 32, 64" #endif #define CWAL_INT_T_MIN ((-CWAL_INT_T_MAX)-1) /* Reminder: the definition ((-CWAL_INT_T_MAX)-1) was gleaned from the clang headers, but AFAIK C does not actually define what happens for under/overflow for _signed_ types. Trying to use the literal value in 64-bit mode gives me a compile error on gcc ("constant value is only signed in C99", or some such). */ /** @typedef some_unsigned_int_type cwal_hash_t Hash value type used by the library. It must be an unsigned integer type. */ #if 16 == CWAL_INT_T_BITS typedef uint16_t cwal_hash_t; #elif 32 == CWAL_INT_T_BITS typedef uint32_t cwal_hash_t; #elif 64 == CWAL_INT_T_BITS typedef uint64_t cwal_hash_t; #endif /** @typedef double_or_long_double cwal_double_t This is the type of double value used by the library. It is only lightly tested with long double, and when using long double the memory requirements for such values goes up (of course). Note that by default cwal uses C-API defaults for numeric precision. To use a custom precision throughout the library, one needs to define the macros CWAL_DOUBLE_T_SFMT and/or CWAL_DOUBLE_T_PFMT macros to include their desired precision, and must build BOTH cwal AND the client using these same values. For example: @code #define CWAL_DOUBLE_T_PFMT ".8Lf" #define HAVE_LONG_DOUBLE @endcode */ #if CWAL_DISABLE_FLOATING_POINT /* No doubles support: use integers instead so we don't have to block out portions of the API. */ typedef cwal_int_t cwal_double_t; # define CWAL_DOUBLE_T_SFMT CWAL_INT_T_SFMT # define CWAL_DOUBLE_T_PFMT CWAL_INT_T_PFMT #else # if defined(HAVE_LONG_DOUBLE) typedef long double cwal_double_t; # ifndef CWAL_DOUBLE_T_SFMT # define CWAL_DOUBLE_T_SFMT "Lf" # endif # ifndef CWAL_DOUBLE_T_PFMT # define CWAL_DOUBLE_T_PFMT "Lf" # endif # else typedef double cwal_double_t; # ifndef CWAL_DOUBLE_T_SFMT # define CWAL_DOUBLE_T_SFMT "f" #endif # ifndef CWAL_DOUBLE_T_PFMT # define CWAL_DOUBLE_T_PFMT "f" #endif # endif #endif #if !defined(CWAL_ENABLE_TRACE) /** Setting CWAL_ENABLE_TRACE to a true value enables cwal-internal tracing. Profiling shows it to be very expensive, and its level of detail is too great for most users to be able to do anything with, so it is recommended that it be left off unless needed. */ # define CWAL_ENABLE_TRACE 0 #endif #endif /* WANDERINGHORSE_NET_CWAL_CONFIG_H_INCLUDED */ /* end of file include/wh/cwal/cwal_config.h */ /* start of file include/wh/cwal/cwal.h */ #if !defined(WANDERINGHORSE_NET_CWAL_H_INCLUDED) #define WANDERINGHORSE_NET_CWAL_H_INCLUDED 1 #if defined(HAVE_CONFIG_H) || defined(HAVE_AUTOCONFIG_H) # include "config.h" #endif #include /* va_list */ #include /* FILE decl */ /** @page page_cwal cwal API cwal (pronounced "sea wall") is the Scriping Engine Without A Language (sewal, shortened to cwal) is an object-oriented C API providing _part_ of a scripting engine, namely the engine and not the scripting. The intention is that custom scripting languages/mini-languages can be written on top of this basis, which takes care of the core type system, de/allocation of values, tracking references, and other low-level work which is largely independent of any specific language syntax (but is better suited to some more than others). Its design does impose some conventions/requirements on host languages, but nothing too onerous, i hope. Alternately, it can be used as sort of garbage collection system for clients willing to use its type system. Another potential use might be a data-binding mechanism between, e.g., database drivers and client code, acting as a type-normalization layer between the two (so the client code can be written without much knowledge of the underlying driver(s)). cwal is still very much beta and may yet undergo any number of design changes at this point. That said, as of mid-2014 we have a good deal of add-on code, in the form of th1ish and s2, which we would like to keep running, so massive changes are unlikely. Project home page: http://fossil.wanderinghorse.net/repos/cwal Author: Stephan Beal (http://www.wanderinghorse.net/home/stephan/) License: Dual Public Domain/MIT The full license text is in the main header file (cwal.h or cwal_amalgamation.h, depending on the distribution used): search the file for the word LICENSE. Examples of how to use the library are scattered throughout the API documentation, in the test.c file in the source repo, in the wiki on the project home page, and (in a big way) in the th1ish/ and s2/ subdirectories of the main source tree. Main properties of cwal: - cwal does NOT provide a scripting language. It is an engine which "could" be used as a basis for one. It can also be used as simple form of garbage collector for client apps willing to live with its scoping mechanism. (In the mean time we have two an example/proof-of-concept languages built on cwal.) - Provides a type system similar (not identical) to that of ECMAScript. Values are opaque handles with some polymorphic behaviours depending on their logical type. Provides support for binding client-specified "native" values, such that they can participate in the normal lifetime tracking and destruction process and can be converted from their Value handles with 100% type-safety. - Uses a reference-counting/scope-ownership hybrid for value sharing and garbage collection. Its destruction mechanism behves sanely (so far!) when faced with cycles provided the rules of the API are followed (they're pretty easy). - Destruction is deterministic and happens more or less like it does in C++, with the addition that refcounting can be used to extend the lifetime of a value outside of the scope where it is created. While refcounting in conjunction with container can make destruction order somewhat difficult to predict, it is still deterministic in that values with lower refcounts are destroyed before those with higher refcounts. The destruction order of non-containers is largely irrelevant because their cleanup is opaque to the client and those types cannot form cycles. - The wrapping of client-provided native types uses a type-safety mechanism to ensure that clients are getting the type of pointer they expect when fetching the (void*) from its cwal value type counterpart. Such types participate as first-class Values and may contain properties (meaning they can participate in graphs). - Provides optional automatic "internalizing" of new string values, and all strings which share the same length and byte content are automatically shared via the reference-counting mechanism. When the final reference is released the string is un-internalized automatically. This causes a couple corner cases internally but can drastrically reduce allocations in scripts which make heavy use of identifier strings. - Highly optimized to reduce calls to malloc() and free(), and optionally makes use of memory recycling. The recycling limits can be set on a per-data-type basis. Practice has shown recycling to be tremendously effective at reducing malloc calls by over 90% in typical script code (a 98% reduction is not uncommon!). - Optionally provides tracing (via callbacks) so that clients can see what it is doing on the inside. - Clean code is a design goal, as is having relatively good documentation. */ /** @page page_cwal_gc cwal Garbage Collection This section describes how cwal manages the memory of Values created by the client. cwal's main concern, above all, is memory management. This includes at least the following aspects: - Allocation and deallocation of raw memory. This is delegated to cwal_engine_vtab so that clients may swap it out. cwal internally manages a number of optional recycling bins, broken down by data type, where it stores freed Values (and other internals) for re-use. Once the recycle bins have been populated a bit, it has to allocate memory far less often (how often depends on usage and recycler configuration). - Tracking the lifetimes of components which clients cannot reasonably track themselves. This refers specifically to Values, since their inter-relationships (via hierachies and key/value properties) can easily lead to unpredictable patterns which would be unrealistically burdensome for client code to try to properly manage. cwal's garbage collection mechanism has two basic components: reference counts and scopes. cwal uses convential refcount semantics. The reference count of a Value is increased when code "expresses an interest" in the Value, e.g. it is inserted into a container or an explicit call to cwal_value_ref() is called. Reference counts are decreased as Values are removed from containers (meaning Objects and Arrays) or cwal_value_unref() is called. When the refcount goes to 0, the Value is cleaned up (recursively for containers). Scopes act as a "root" for a collection of values, such that cleaning up a root will clean up all values under that root (regardless of their reference count). Reference counting is straightforward and easy to implement but is conventionally problematic when it comes to managing graphs of Values (i.e. cyclic data structures). When a cwal scopes is closed, it destructs any values it owns by iteratively dereferencing them. This process removes the values from the scope in such a way that graphs get weeded out one incrementally (one level at a time) and (more or less) cleanly. Consider this pseudo-script-code: var obj = new Object(); obj.set(obj, obj); // (key, value) (Admitedly unusual, but cwal's value system supports this.) We now have an object with 3 references: 1 held by the identifier mapping, one held by the key, and one held by the value. (Possibly others, depending on the scripting implementation.) Now we do: unset obj To remove the identifier (and its reference). That leaves us with a refcount of 2 and no handle back to the object. The object must be kept alive because there are references (the key/value refering to the object itself). That value will remain an orphan until its owning scope is cleaned, at which point the scope's cleaning process will weed out the cycles and finalize the object when the final reference is removed. There are of course wrinkles in that equation. For example, the finalization process for an object must recurisvely climb down properties, and in the above case doing so will trigger finalization of an object while it is traversing itself. cwal's cleanup mechanism temporarily delays freeing of Container Values' memory during scope cleanup, queuing them up for later destruction. The end effect is that Values get cleaned up but the memory remains valid until scope cleanup has finished. This makes it safe (if psychologically a bit unsettling) to traverse "destroyed" objects during finalization (it is a harmless no-op). Once scope cleanup is complete, all queued-up destroyed values are then flushed to the deallocator (or the recycling bin, if it has space). Summary: in cwal it is possible to orphan graphs in such a way that the client has no reference to them but circular references prevent cleanup. Such values will be freed when their parent scope is cleaned. There is also a "sweep" mechanism to trigger cleanups but it will not work on a value for which a reference has already been obtained Speaking of parent scopes... cwal complements the reference counting with the concept of "owning scope." Every value belongs to exactly one scope, and this scope is always the highest-level scope which has ever referenced the value. When a value is created, it belongs to the scope active at the time. Values can be rescoped, however, moving up in the stack (into an older scope, never a newer scope). When a value is inserted into a container, the value is scoped into the container's owning scope. When the container is added to another, the container is recursively re-scoped (if necessary) to match the scope of its parent container. This ensures that values from a higher-level (older) scope are always valid through the lifetime of lower-level (newer) scopes. It also allows a scope to pass a value to its parent scope. When a value is re-scoped, it is removed from its owning scope's management list and added to the new scope's list. These list are linked lists made up of the cwal_value::left and cwal_value::right members, and use O(1) algos for the list management (except for cleanup, which is effectively linear). Because of the graph-orphaning problem, it is recommended that client code make heavy use of cwal's scoping API to keep the lifetimes of such values to a minimum. cwal_scope_sweep() may be used periodically to free up temporaries (values which have no active references), but client code must be careful not to accidentally clean up values which will become return results (they must get a reference to the value, making it a non-temporary or they must up-scope it before sweeping). */ #if defined(__cplusplus) extern "C" { #endif /* Forward declarations. Most of these types are opaque to client code. */ typedef struct cwal_scope cwal_scope; typedef struct cwal_engine_vtab cwal_engine_vtab; typedef struct cwal_engine cwal_engine; typedef struct cwal_value cwal_value; typedef struct cwal_array cwal_array; typedef struct cwal_object cwal_object; typedef struct cwal_string cwal_string; typedef struct cwal_kvp cwal_kvp; typedef struct cwal_native cwal_native; typedef struct cwal_buffer cwal_buffer; typedef struct cwal_exception cwal_exception; typedef struct cwal_hash cwal_hash; typedef struct cwal_weak_ref cwal_weak_ref; typedef struct cwal_callback_hook cwal_callback_hook; typedef struct cwal_callback_args cwal_callback_args; /** A callback type for pre-call callback hooks. If a hook is installed via cwal_callback_hook_set(), its "pre" callback is called before the callback is called. The first parameter is the one which will be passed to the callback and post-callback hook if this hook succeeds. The state parameter (2nd argument) is the one passed to cwal_callback_hook_set(). Its interpretation is implementation-defined. The callback must return 0 on success. On error, the non-0 result code is returned from the cwal API which triggered the hook (cwal_function_call() and friends). On success, the callback is called and then (regardless of callback success!), the "post" callback is called. The intention of the callback hook mechanism is to give script engines a places to do pre-call setup such as installing local variables (e.g. similar to JavaScript's "this", "arguments", and "arguments.callee"). It can also be used for instrumentation purposes (logging) or to implement multi-casting of client-side pre-call hook mechanism to multiple listeners/interceptors. Note that the hook mechanism is "global" - it affects all cwal_function_call_in_scope() calls, which means all script-triggered callbacks and any other place a cwal_function_call() (or similar) is used. It _is_ possible (using cwal_function_state_get()) for a client to determine whether the callback exists in script code or native code, which means that hooks can act dependently of that if they need to. e.g. there is generally no need to inject "this" and "arguments" symbols into native-level callbacks, whereas it is generally useful to do so for script-side callbacks. Native callbacks have access to the same information via the argv object, so they don't need scope-level copies of those values. @see cwal_callback_hook_post_f() @see cwal_callback_hook @see cwal_callback_hook_set() */ typedef int (*cwal_callback_hook_pre_f)(cwal_callback_args const * argv, void * state); /** The state parameter (2nd argument) is the one passed to cwal_callback_hook_set(). Its interpretation is implementation-defined. The fRc (3rd) parameter is the return code of the callback, in case it interests the hook (e.g. error logging). If this value is not 0 then the rv parameter is always NULL. The rv (4rd) parameter is the result value of the function. The engine will re-scope rv brief milliseconds after this hook returns. rv MAY be NULL, indicating that the callback did not set a value (which typically equates to the undefined value (cwal_value_undefined()) downstream or may be because an exception was thrown or a non-exception error code was returned). If the "pre" hook is called and returns 0, the API guarantees that the post-hook is called. Conversely, if the pre hook returns non-0, the post hook is never called. @see cwal_callback_hook_pre_f() @see cwal_callback_hook @see cwal_callback_hook_set() */ typedef int (*cwal_callback_hook_post_f)(cwal_callback_args const * argv, void * state, int fRc, cwal_value * rv); /** Holds state information for a set of cwal_engine callback hooks. @see cwal_callback_hook_pre_f() @see cwal_callback_hook_post_f() @see cwal_callback_hook_set() */ struct cwal_callback_hook { /** Implementation-dependent state pointer which gets passed as the 2nd argument to this->pre() and this->post(). */ void * state; /** The pre-callback hook. May be NULL. */ cwal_callback_hook_pre_f pre; /** The post-callback hook. May be NULL. */ cwal_callback_hook_post_f post; }; /** An initialized-with-defaults instance of cwal_callback_hook, intended for const-copy intialization. */ #define cwal_callback_hook_empty_m {NULL,NULL,NULL} /** An initialized-with-defaults instance of cwal_callback_hook, intended for copy intialization. */ extern const cwal_callback_hook cwal_callback_hook_empty; /** The set of result codes used by most cwal API routines. Not all of these are errors, per se, and may have context-dependent interpretations. Client code MUST NOT rely on any of these entries having a particular value EXCEPT for CWAL_RC_OK, which is guaranteed to be 0. All other entries are guaranteed to NOT be 0, but their exact values may change from time to time. The values are guaranteed to stay within a standard enum range of signed integer bits, but no other guarantees are made (e.g. whether the values are positive or negative, 2 digits or 5, are unspecified and may change). */ enum cwal_rc { /** The canonical "not an error" code. */ CWAL_RC_OK = 0, /** Generic "don't have anything better" error code. */ CWAL_RC_ERROR = 1, /** Out-of-memory. */ CWAL_RC_OOM = 2, /** Signifies that the cwal engine may be in an unspecific state and must not be used further. */ CWAL_RC_FATAL = 3, /** Signals that the returning operation wants one of its callers to implement "continue" semantics. */ CWAL_RC_CONTINUE = 101, /** Signals that the returning operation wants one of its callers to implement "break" semantics. */ CWAL_RC_BREAK = 102, /** Signals that the returning operation wants one of its callers to implement "return" semantics. */ CWAL_RC_RETURN = 103, /** Indicates that the interpreter should stop running the current script immediately. */ CWAL_RC_EXIT = 104, /** Indicates that the interpreter "threw an exception", which "should" be reflected by passing a cwal_exception value down the call stack via one of the cwal_exception_set() family of functions. Callers "should" treat this return value as fatal, immediately passing it back to their callers (if possible), until a call is reached in the stack which handles this return type (e.g. the conventional "catch" handler), at which point the propagation should stop. */ CWAL_RC_EXCEPTION = 105, /** Indicates that the interpreter triggered an assertion. Whether these are handled as outright fatal errors or exceptions (or some other mechanism) is up to the interpreter. */ CWAL_RC_ASSERT = 106, /** Indicates that some argument value is incorrect or a precondition is not met. */ CWAL_RC_MISUSE = 201, /** Indicates that a resource being searched for was not found. */ CWAL_RC_NOT_FOUND = 301, /** Indicates that a resource being searched for or replaced already exists (whether or not this is an error is context-dependent). */ CWAL_RC_ALREADY_EXISTS = 302, /** A more specific form of CWAL_RC_MISUSE, this indicates that some value (argument or data a routine depends on) is "out of range." */ CWAL_RC_RANGE = 303, /** Indicates that some value is not of the required type (or family of types). */ CWAL_RC_TYPE = 304, /** Indicates an unsupported/not-yet-implemented operation was requested. */ CWAL_RC_UNSUPPORTED = 305, /** Indicates that access to some resource was denied, e.g. a set operation on a variable flagged as read-only. */ CWAL_RC_ACCESS = 306, /** Indicates that visitation of a value would step into a cycle collision. Whether or not this is an error is context-dependent. */ CWAL_RC_CYCLES_DETECTED = 401, /** Used by value cleanup to help ensure that values with cycles do not get freed multiple times. Returned only by cwal_value_unref() and friends and only when a value encounters a reference to itself somewhere in the destruction process. In that context this value is a flag, not an error, but it is also used in assert()s to ensure that pre- and post-conditions involving cycle traversal during destruction are held. */ CWAL_RC_DESTRUCTION_RUNNING = 402, /** Returned by cwal_value_unref() when it really finalizes a value. */ CWAL_RC_FINALIZED = 403, /** Returned by cwal_value_unref() when it really the value it is given still has active references after unref returns. */ CWAL_RC_HAS_REFERENCES = 404, /** Reserved for future use. */ CWAL_RC_INTERRUPTED = 501, /** Reserved for future use. */ CWAL_RC_CANCELLED = 502, /** Indicates an i/o error of some sort. */ CWAL_RC_IO = 601, /** Intended for use by routines which normally assert() a particular condition but do not do so when built in non-debug mode. */ CWAL_RC_CANNOT_HAPPEN = 666, CWAL_RC_JSON_INVALID_CHAR = 700, CWAL_RC_JSON_INVALID_KEYWORD, CWAL_RC_JSON_INVALID_ESCAPE_SEQUENCE, CWAL_RC_JSON_INVALID_UNICODE_SEQUENCE, CWAL_RC_JSON_INVALID_NUMBER, CWAL_RC_JSON_NESTING_DEPTH_REACHED, CWAL_RC_JSON_UNBALANCED_COLLECTION, CWAL_RC_JSON_EXPECTED_KEY, CWAL_RC_JSON_EXPECTED_COLON, /** The CWAL_SCR_xxx family of values are intended for use by concrete scripting implementations based on cwal. */ CWAL_SCR_readme = 2000, /** Indicates that the provided token "could not be consumed" by the given handler, but that there is otherwise no known error. */ CWAL_SCR_CANNOT_CONSUME, /** Indicates that an invalid operation was performed on a value, or that the type(s) required for a given operation are incorrect. */ CWAL_SCR_INVALID_OP, /** Special case of CWAL_RC_NOT_FOUND, indicates that an identifier string could not be found in the scope path. */ CWAL_SCR_UNKNOWN_IDENTIFIER, /** Indicates a (failed) attempt to call() a non-Function value. */ CWAL_SCR_CALL_OF_NON_FUNCTION, /** More concrete case of CWAL_SCR_SYNTAX. */ CWAL_SCR_MISMATCHED_BRACE, /** More concrete case of CWAL_SCR_SYNTAX. */ CWAL_SCR_MISSING_SEPARATOR, /** More concrete case of CWAL_SCR_SYNTAX. */ CWAL_SCR_UNEXPECTED_TOKEN, /** Indicates division or modulus by 0 would have been attempted. */ CWAL_SCR_DIV_BY_ZERO, /** Indicates a generic syntax error. */ CWAL_SCR_SYNTAX, /** Indicates that an unexpected EOF was encountered (e.g. while reading a string literal). */ CWAL_SCR_UNEXPECTED_EOF, /** Indicates EOF was encountered. Whether or not this is an error is context-dependent, and CWAL_SCR_UNEXPECTED_EOF is intended for the error case. */ CWAL_SCR_EOF, /** More concrete case of CWAL_RC_RANGE. */ CWAL_SCR_TOO_MANY_ARGUMENTS, /** More concrete case of CWAL_SCR_SYNTAX. */ CWAL_SCR_EXPECTING_IDENTIFIER, /** The evaluation result code starting point for adding client-specific RC values for use in evaluation engines. */ CWAL_RC_CLIENT_BEGIN = 3000 }; /** Convenience typedef. */ typedef enum cwal_rc cwal_rc; enum cwal_e_options { /** Max number of arguments cwal_function_callf() and (variadic) friends. Remember that each one takes up sizeof(cwal_value*) in stack space. */ CWAL_OPT_MAX_FUNC_CALL_ARGS = 32 }; /** A collection of values which control what tracing messages get emitted by a cwal_engine. By an unfortunate fluke of mis-design, entries which are themselves not group masks (groups are named xxxx_MASK) cannot be effecitvely mixed together via bitmasking. The end effect is that only the MASK, NONE, or ALL entries can be usefully/predictibly applied. i'll see about fixing that. */ enum cwal_trace_flags { CWAL_TRACE_NONE = 0, CWAL_TRACE_GROUP_MASK = 0x7F000000, CWAL_TRACE_MEM_MASK = 0x01000000, CWAL_TRACE_MEM_MALLOC = CWAL_TRACE_MEM_MASK | (1 << 0), CWAL_TRACE_MEM_REALLOC = CWAL_TRACE_MEM_MASK | (1 << 1), CWAL_TRACE_MEM_FREE = CWAL_TRACE_MEM_MASK | (1 << 2), CWAL_TRACE_MEM_TO_RECYCLER = CWAL_TRACE_MEM_MASK | (1 << 3), CWAL_TRACE_MEM_FROM_RECYCLER = CWAL_TRACE_MEM_MASK | (1 << 4), CWAL_TRACE_MEM_TO_GC_QUEUE = CWAL_TRACE_MEM_MASK | (1 << 5), CWAL_TRACE_VALUE_MASK = 0x02000000, CWAL_TRACE_VALUE_CREATED = CWAL_TRACE_VALUE_MASK | (1 << 0), CWAL_TRACE_VALUE_SCOPED = CWAL_TRACE_VALUE_MASK | (1 << 1), CWAL_TRACE_VALUE_UNSCOPED = CWAL_TRACE_VALUE_MASK | (1 << 2), CWAL_TRACE_VALUE_CLEAN_START = CWAL_TRACE_VALUE_MASK | (1 << 3), CWAL_TRACE_VALUE_CLEAN_END = CWAL_TRACE_VALUE_MASK | (1 << 4), CWAL_TRACE_VALUE_CYCLE = CWAL_TRACE_VALUE_MASK | (1 << 5), CWAL_TRACE_VALUE_INTERNED = CWAL_TRACE_VALUE_MASK | (1 << 6), CWAL_TRACE_VALUE_UNINTERNED = CWAL_TRACE_VALUE_MASK | (1 << 7), CWAL_TRACE_VALUE_VISIT_START = CWAL_TRACE_VALUE_MASK | (1 << 8), CWAL_TRACE_VALUE_VISIT_END = CWAL_TRACE_VALUE_MASK | (1 << 9), CWAL_TRACE_VALUE_REFCOUNT = CWAL_TRACE_VALUE_MASK | (1 << 10), CWAL_TRACE_SCOPE_MASK = 0X04000000, CWAL_TRACE_SCOPE_PUSHED = CWAL_TRACE_SCOPE_MASK | (1 << 0), CWAL_TRACE_SCOPE_CLEAN_START = CWAL_TRACE_SCOPE_MASK | (1 << 1), CWAL_TRACE_SCOPE_CLEAN_END = CWAL_TRACE_SCOPE_MASK | (1 << 2), CWAL_TRACE_SCOPE_SWEEP_START = CWAL_TRACE_SCOPE_MASK | (1 << 3), CWAL_TRACE_SCOPE_SWEEP_END = CWAL_TRACE_SCOPE_MASK | (1 << 4), CWAL_TRACE_ENGINE_MASK =0X08000000, CWAL_TRACE_ENGINE_STARTUP = CWAL_TRACE_ENGINE_MASK | (1 << 1), CWAL_TRACE_ENGINE_SHUTDOWN_START = CWAL_TRACE_ENGINE_MASK | (1 << 2), CWAL_TRACE_ENGINE_SHUTDOWN_END = CWAL_TRACE_ENGINE_MASK | (1 << 3), CWAL_TRACE_FYI_MASK = 0x10000000, CWAL_TRACE_MESSAGE = CWAL_TRACE_FYI_MASK | (1<<1), CWAL_TRACE_ERROR_MASK = 0x20000000, CWAL_TRACE_ERROR = CWAL_TRACE_ERROR_MASK | (1<<0), /** Contains all cwal_trace_flags values except CWAL_TRACE_NONE. */ CWAL_TRACE_ALL = 0x7FFFFFFF/*1..31*/ }; /** Convenience typedef. */ typedef enum cwal_trace_flags cwal_trace_flags; #if CWAL_ENABLE_TRACE typedef struct cwal_trace_state cwal_trace_state; struct cwal_trace_state { cwal_trace_flags event; int32_t mask; cwal_rc code_NYI; cwal_engine const * e; cwal_value const * value; cwal_scope const * scope; void const * memory; cwal_size_t memorySize; char const * msg; cwal_size_t msgLen; char const * cFile; char const * cFunc; int cLine; }; #else typedef char cwal_trace_state; #endif #if CWAL_ENABLE_TRACE # define cwal_trace_state_empty_m { \ CWAL_TRACE_NONE/*event*/, \ 0/*mask*/, CWAL_RC_OK/*code*/,\ 0/*engine*/,0/*scope*/,0/*value*/,\ 0/*mem*/,0/*memorySize*/,0/*msg*/,0/*msgLen*/, \ 0/*cFile*/,0/*cLine*/,\ } #else # define cwal_trace_state_empty_m 0 #endif extern const cwal_trace_state cwal_trace_state_empty; /** Converts the given cwal_rc value to "some string", or returns an unspecified string if rc is not a cwal_rc value. The returned bytes are always the same for a given value, and static, and are thus guaranteed to survive at least until main() returns or exit() is called. */ char const * cwal_rc_cstr(int rc); /** Type IDs used by cwal. They correspond roughly to JavaScript/JSON-compatible types, plus some extensions. These are primarily in the public API to allow O(1) client-side dispatching based on cwal_value types, as opposed to using O(N) if/else if/else. */ enum cwal_type_id { /** GCC likes to make enums unsigned at times, which breaks strict comparison of integers with enums. Soooo... */ CWAL_TYPE_FORCE_SIGNED_ENUM = -1, /** The special "undefined" value constant. Its value must be 0 for internal reasons. */ CWAL_TYPE_UNDEF = 0, /** The special "null" value constant. */ CWAL_TYPE_NULL = 1, /** The bool value type. */ CWAL_TYPE_BOOL = 2, /** The integer value type, represented in this library by cwal_int_t. */ CWAL_TYPE_INTEGER = 3, /** The double value type, represented in this library by cwal_double_t. */ CWAL_TYPE_DOUBLE = 4, /** The immutable string type. This library stores strings as immutable UTF8. */ CWAL_TYPE_STRING = 5, /** The "Array" type. */ CWAL_TYPE_ARRAY = 6, /** The "Object" type. */ CWAL_TYPE_OBJECT = 7, /** The "Function" type. */ CWAL_TYPE_FUNCTION = 8, /** A handle to a generic "error" or "exception" type. */ CWAL_TYPE_EXCEPTION = 9, /** A handle to a client-defined "native" handle. */ CWAL_TYPE_NATIVE = 10, /** The "buffer" type, representing a generic memory buffer. Note that CWAL_TYPE_BUFFER is not a Container Type for historical reasons, as the cwal_buffer class existed long before the Object-level support did. It seems that there would be little benefit refactoring for that at this point, as cwal_buffer is primarily used in C code, and relatively seldomly in script code (though th1ish uses buffers instead of strings in many cases). Alternately, we could split the buffer into C-side (cwal_buffer) and script-side (cwal_buffer_val), but that also hasn't seemed worth the effort yet. */ CWAL_TYPE_BUFFER = 11, /** Represents a hashtable type (cwal_hash), which is similar to OBJECT but guarantees a faster property store. */ CWAL_TYPE_HASH = 12, /** A pseudo-type-id used internaly, and does not see use in the public API (it might at some future point). */ CWAL_TYPE_SCOPE = 13, /** KVP (Key/Value Pair) is a pseudo-type-id used internally, and does not see use in the public API. */ CWAL_TYPE_KVP = 14, /** Used _almost_ only internally. The only public API use for this entry is with cwal_engine_recycle_max() and friends. */ CWAL_TYPE_WEAK_REF = 15, /** Used only internally during the initialization of "external strings." After initializations these take the type CWAL_TYPE_STRING. The only public API use for this entry is with cwal_engine_recycle_max() and friends. */ CWAL_TYPE_XSTRING = 16, /** Used only internally during the initialization of "z-strings." After initializations these take the type CWAL_TYPE_STRING. The only public API use for this entry is with cwal_engine_recycle_max() and friends. */ CWAL_TYPE_ZSTRING = 17, /** Must be the last entry in this enum. */ CWAL_TYPE_end }; /** Convenience typedef. */ typedef enum cwal_type_id cwal_type_id; /** Convenience typedef. */ typedef struct cwal_function cwal_function; /** A type holding arguments generated from "script" code which call()s a Function value. */ struct cwal_callback_args{ /** The engine object making the call. */ cwal_engine * engine; /** The scope in which the function is called. */ cwal_scope * scope; /** The "this" value for this call. */ cwal_value * self; /** The function being called. */ cwal_function * callee; /** State associated with the function by native client code. This is set via cwal_new_function() or equivalent. */ void * state; /** A client-provided "tag" which can be used to determine if this->state is of the type the client expects. This value is provided to the new-function APIs. */ void const * stateTypeID; /** Number of arguments. */ uint16_t argc; /** Array of arguments argc items long. */ cwal_value * const * argv; }; #define cwal_callback_args_empty_m \ {0/*engine*/,0/*scope*/,0/*self*/, \ 0/*callee*/,0/*state*/,NULL/*stateTypeID*/, \ 0/*argc*/,0/*argv*/\ } extern const cwal_callback_args cwal_callback_args_empty; /** Callback function interface for cwal "script" functions. args contains various state information related to the call. The callback returns a value to the framework by assigning *rv to it (assigning it to NULL is equivalent to assigning it to cwal_value_undefined()). Implementations can rely on rv being non-NULL but must not rely on any previous contents of *rv. Callbacks must return 0 on success, CWAL_RC_EXCEPTION if they set the cwal exception state, or (preferably) one of the other relevant CWAL_RC values on error. ACHTUNG: it is critical that implementations return CWAL_RC_xxx values, as the framework relies on several specific values to report information to the framework and to scripting engines built on it. e.g. CWAL_RC_RETURN, CWAL_RC_OOM, CWAL_RC_BREAK, and CWAL_RC_EXCEPTION are often treated specially. If clients return non-cwal result codes from this function, cwal may get confused and downstream behaviour is undefined. */ typedef int (*cwal_callback_f)( cwal_callback_args const * args, cwal_value ** rv ); /** Framework-wide interface for finalizer functions for memory managed by a cwal_engine instance. Generally speaking it must semantically behave like free(3), but if the implementor knows what he's doing these can also be used for "cleanup" (as opposed to free()ing). */ typedef void (*cwal_finalizer_f)( cwal_engine * e, void * m ); /** A cwal_finalizer_f() implementation which requires that s be a (FILE*). If s is not NULL and not one of (stdin, stdout, stderr) then this routine fclose()s it. This implementation ignores the e parameter. If s is NULL this is a harmless no-op. Results are undefined if s is not NULL and is not a valid opened (FILE*). */ void cwal_finalizer_f_fclose( cwal_engine * e, void * s ); /** Generic list type. It is up to the APIs using this type to manage the entry count member and use cwal_list_reserve() to manage the "alloced" member. @see cwal_list_reserve() @see cwal_list_append() */ struct cwal_list { /** Array of entries. It contains this->alloced entries, this->count of which are "valid" (in use). */ void ** list; /** Number of "used" entries in the list. */ cwal_size_t count; /** Number of slots allocated in this->list. Use cwal_list_reserve() to modify this. Doing so might move the this->list pointer but the values it points to will stay stable. */ cwal_size_t alloced; }; typedef struct cwal_list cwal_list; /** Empty-initialized cwal_list object. */ #define cwal_list_empty_m { NULL, 0, 0 } /** Empty-initialized cwal_list object. */ extern const cwal_list cwal_list_empty; /** A helper class for holding arbitrary state with an optional associated finalizer. The interpretation of the state and the finalizer's requirements are context-specific. */ struct cwal_state { /** The raw data. Its interpretation is of course very context-specific. The typeID field can be used to "tag" this value with type info so that clients can ensure that they do not mis-cast this pointer. */ void * data; /** An arbitrary "tag" value which clients can use to indicate that this->data is of a specific type. In practice this is normally set to the address of some internal structure or value which is not exposed via public APIs. */ void const * typeID; /** Cleanup function for this->data. It may be NULL if this->data has no cleanup requirements or is owned by someone else. */ cwal_finalizer_f finalize; }; /** Convenience typedef. */ typedef struct cwal_state cwal_state; /** Empty-initialized cwal_state object. */ #define cwal_state_empty_m { NULL, NULL, NULL } /** Empty-initialized cwal_state object. */ extern const cwal_state cwal_state_empty; /** Generic output interface intended to be used via cwal_engine_vtab via cwal_output(). Script-side code which generates "console-style" output intended for the user should use cwal_output() to put it there. An implementation of this interface is then responsible for sending the output somewhere. Must return 0 on success or an error code (preferably from cwal_rc) on error. Because an output mechanism can modify the output, there is not direct 1-to-1 mapping of input and output lengths, and thus it returns neither of those. state is the state value of the output container (part of the cwal_engine_vtab interface). */ typedef int (*cwal_output_f)( void * state, void const * src, cwal_size_t n ); /** Library-wide interface for allocating, reallocating, freeing memory. It must semantically behave like realloc(3) with the minor clarification that the free() operation (size==0) it must return NULL instead of "some value suitable for passing to free()." The state argument (typically) comes from the state member of the cwal_engine_vtab which holds one of these functions. The (mem,size) parameters are as for realloc(3). In summary: If (mem==NULL) then it must semantically behave like malloc(3). If (size==0) then it must sematically behave like free(3). If (mem!=NULL) and (size!=0) then it must semantically behave like realloc(3). Values may not be shared across multiple cwal_engine instances unless all instances use the same underlying allocator. */ typedef void * (*cwal_engine_realloc_f)( void * state, void * mem, cwal_size_t size ); typedef void (*cwal_engine_tracer_f)( void * state, cwal_trace_state const * event ); struct cwal_engine_tracer{ cwal_engine_tracer_f trace; void (*close)( void * state ); void * state; }; typedef struct cwal_engine_tracer cwal_engine_tracer; #define cwal_engine_tracer_empty_m { 0, 0, 0 } extern const cwal_engine_tracer cwal_engine_tracer_empty; extern const cwal_engine_tracer cwal_engine_tracer_FILE; void cwal_engine_tracer_f_FILE( void * filePtr, cwal_trace_state const * event ); void cwal_engine_tracer_close_FILE( void * filePtr ); /** Part of the cwal_engine_vtab interface, this defines the API for a memory allocator used by the cwal_engine API. */ struct cwal_allocator{ /** The memory management function. cwal_engine uses this exclusively for all de/re/allocations. */ cwal_engine_realloc_f realloc; /** State for the allocator. Its requirements/interpretation depend on the concrete realloc implementation. */ cwal_state state; }; /** Convenience typedef. */ typedef struct cwal_allocator cwal_allocator; /** Empty-initialized cwal_allocator object. */ #define cwal_allocator_empty_m { 0, cwal_state_empty_m } /** Empty-initialized cwal_allocator object. */ extern const cwal_allocator cwal_allocator_empty; /** cwal_allocator object configured to use realloc(3). */ extern const cwal_allocator cwal_allocator_std; /** Part of the cwal_engine_vtab interface, this defines a generic interface for outputing "stuff" (presumably script-generated text). The intention is that script-side output should all go through a common channel, to provide the client an easy to to intercept/redirect it, or to add layers like output buffer stacks (this particular output interface originates from such an implementation in TH1). */ struct cwal_outputer{ cwal_output_f output; /** Intended to flush the output channel, if needed. If not needed, this member may be NULL, in which case it is ignored, or it may simply return 0. It is passed this.state.data as its argument. */ int (*flush)( void * state ); cwal_state state; }; typedef struct cwal_outputer cwal_outputer; /** Empty-initialized cwal_outputer object. */ #define cwal_outputer_empty_m { 0, NULL, cwal_state_empty_m } /** Empty-initialized cwal_outputer object. */ extern const cwal_outputer cwal_outputer_empty; /** cwal_outputer object set up to use cwal_output_f_FILE to stdout by default. After copying this value, set it the copy's state.data to a (FILE*) to redirect it. If its file handle needs to be closed during cleanup, the state.finalize member should be set to a function which will close the file handle (e.g. cwal_finalizer_f_fclose()). */ extern const cwal_outputer cwal_outputer_FILE; /** Typedef for a predicate function which tells a cwal_engine whether or not a given string is "internable" or not. Clients may provide an implementation of this via cwal_engine_vtab::internable. If interning is enabled, when a new string is created, this function will be called and passed: - The state pointer set in cwal_engine_vtab::internable::state. - The string which is about to be created as a cwal_string. - The length of that string. This function is only called for non-empty strings. Thus len is always greater than 0, str is never NULL, and never starts with a NUL byte. Client implementations need not concern themselves with NULL str or a len of 0. Once a given series of bytes have been interned, this function will not be called again for that same series of bytes as long as there is at least one live interned reference to an equivalent string. */ typedef char (*cwal_cstr_internable_predicate_f)( void * state, char const * str, cwal_size_t len ); /** The default "is this string internable?" predicate. The state parameter is ignored. The default impl uses only a basic length cutoff point to determine "internalizableness." @see cwal_cstr_internable_predicate_f() */ char cwal_cstr_internable_predicate_f_default( void * state, char const * str, cwal_size_t len ); /** The "virtual table" of cwal_engine instances, providing the functionality which clients can override with their own implementations. Multiple cwal_engine instances may share if vtab instance if and only if: - The state member (if used) may legally share the same values across engines AND state::finalize() does not destroy state::state (if it does, each engine will try to clean it up). - The vtab does not make values from one engine visible to another. This will lead to Undefined Behaviour. - The app is single-threaded OR... - all access to cwal_engine_vtab is otherwise serialized via a client-side mutex OR... - the vtab instance is otherwise "immune" the threading effects (e.g. because its underlying APIs do the locking). Mutex locking is not a feature planned for the cwal API. */ struct cwal_engine_vtab { /* Potential TODOs: void (*shutdown)( cwal_engine_vtab * self ); shutdown() would be called when an engine is cleaned up (after it has finished cleaning up), instead of state.finalize(), and would be repsonsible for cleaning up allocator.state and outputer.state, if needed. */ /** The memory allocator. All memory allocated in the context of a given cwal_engine is (re)allocated/freed through it's vtab's allocator member. */ cwal_allocator allocator; /** The handler which receives all data passed to cwal_output(). */ cwal_outputer outputer; /** Handles cwal_engine tracing events (if tracing is enabled). */ cwal_engine_tracer tracer; /** A place to store client-defined state. The engine places no value on this, other than to (optionally) clean it up when the engine is finalized. The finalize() method in the state member is called when an engine using this object shuts down. Because it happens right after the engine is destroyed, it is passed a NULL engine argument. Thus it is called like: vtab->state.finalize( NULL, vtab->state.data ); This of course means that a single vtab cannot differentiate between multiple engines for the shutdown phase, and we might have to add reference counting to the vtab in order to account for this (currently it would need to be somewhere in state.data). */ cwal_state state; /** A place to add client-side hooks into engine post-initialization, and possibly for other events at some point (if we can find a use for it). */ struct { /** May be used to add post-init code to cwal_engine instances. If this member is not 0 then it is called right after cwal_engine_init() is finished, before it returns. If this function returns non-0 then initialization fails, the engine is cleaned up, and the return value is passed back to the caller of cwal_engine_init(). Note that the vtab parameter is guaranteed to be the vtab which initialized e. e->vtab==vtab is guaranteed to be true, but client code "should really" use the passed-in vtab pointer instead of relying on the private/internal e->vtab member (its name/placement may change). This is only called one time per initialization of an engine, so the client may (if needed) clean up the init_state member (i.e. vtab->hook->init_state). */ int (*on_init)( cwal_engine * e, cwal_engine_vtab * vtab ); /** Arbitrary state passed to on_init(). */ void * init_state; } hook; /** Holds state for determining whether a given string is internable or not. */ struct { /** The is-internable predicate. If NULL, interning is disabled regardless of any other considerations (e.g. the CWAL_FEATURE_INTERN_STRINGS flag). @see cwal_cstr_internable_predicate_f() */ cwal_cstr_internable_predicate_f is_internable; /** State to be passed as the first argument to is_internable(). */ void * state; } interning; }; /** Empty-initialized cwal_engine_vtab object. */ #define cwal_engine_vtab_empty_m { \ cwal_allocator_empty_m, \ cwal_outputer_empty_m, \ cwal_engine_tracer_empty_m,\ cwal_state_empty_m, \ {/*hook*/ 0/*on_init()*/,0/*init_state*/}, \ {/*interning*/ cwal_cstr_internable_predicate_f_default, NULL} \ } /** Empty-initialized cwal_engine_vtab object. */ extern const cwal_engine_vtab cwal_engine_vtab_empty; /** A cwal_realloc_f() implementation which uses the standard C memory allocators. */ void * cwal_realloc_f_std( void * state, void * m, cwal_size_t n ); /** A cwal_output_f() implementation which requires state to be a valid (FILE*) opened in write mode. It sends all output to that file and returns n on success. If state is NULL then this routine uses stdout. */ int cwal_output_f_FILE( void * state, void const * src, cwal_size_t n ); /** A cwal_outputer::flush() implementation whichr equires that f be a (FILE*). For symmetry with cwal_output_f_FILE, if !f then stdout is assumed. */ int cwal_output_flush_f_FILE( void * f ); /** A state type for use with cwal_output_f_buffer(). @see cwal_output_f_buffer() */ struct cwal_output_buffer_state { cwal_engine * e; cwal_buffer * b; }; /** Convenience typedef. */ typedef struct cwal_output_buffer_state cwal_output_buffer_state; /** Empty-initialized cwal_output_buffer_state instance. */ extern const cwal_output_buffer_state cwal_output_buffer_state_empty; /** A cwal_output_f() implementation which requires state to be a valid (cwal_output_buffer_state*), that state->e points to a valid (cwal_engine*), and that state->b points to a valid (cwal_buffer*). It sends all output to state->b, expanding the buffer as necessary, and returns 0 on success. Results are undefined if state is not a cwal_output_buffer_state. */ int cwal_output_f_buffer( void * state, void const * src, cwal_size_t n ); /** A cwal_finalizer_f() which requires that m be a (cwal_output_buffer_state*). This function calls cwal_buffer_reserve(e, state->buffer, 0) to free up the buffer's memory, then zeroes out state's contents. In theory this can be used together with cwal_output_f_buffer() and cwal_outputer to provide buffering of all cwal_output()-generated output, but there's a chicken-egg scenario there, in that the outputer "should" be set up before the engine is intialized. In this case it has to be modified after the engine is intialized because the engine is part of the outputer's state. */ void cwal_output_buffer_finalizer( cwal_engine * e, void * state ); /** A cwal_engine_vtab instance which can be bitwise copied to inialize a "basic" vtab instance for use with cwal_engine_init(). It uses cwal_allocator_std and cwal_outputer_FILE for its memory and output operations. */ extern const cwal_engine_vtab cwal_engine_vtab_basic; /** Allocates n bytes of memory in the context of e. The returned memory is "associated with" (but strictly owned by) e and is owned (or shared with) the caller, who must eventually pass it to cwal_free() or cwal_realloc(), passing the same engine instance as used for the allocation. It is NEVER legal to share malloc/free/realloc memory across engine instances, even if they use the same allocator, because doing so can lead to "missing" entries in one engine or the other and mis-traversal of graphs during cleanup. */ void * cwal_malloc( cwal_engine * e, cwal_size_t n ); /** Frees memory allocated via cwal_malloc() or cwal_realloc(). */ void cwal_free( cwal_engine * e, void * m ); /** Works as described for cwal_realloc_f(). See cwal_malloc() for important notes. */ void * cwal_realloc( cwal_engine * e, void * m, cwal_size_t n ); /** Convenience typedef. */ typedef struct cwal_exception_info cwal_exception_info; /** NOT YET USED. Holds error state information for a cwal_engine instance. */ struct cwal_exception_info { /** Current error code. */ cwal_rc code; /** Length (in bytes) of cMsg. */ cwal_size_t msgLen; /** Pointer to string memory not owned by the engine but which must be guaranteed to live "long enough." If zMsg is set then this must point to zMsg's. This is primarily a malloc optimization, to allow us to point to strings we know are static without having to strdup() them or risk accidentally free()ing them. */ char const * cMsg; /** Dynamically-allocated memory which is owned by the containing engine and might be freed/invalidated on the next call into the engine API. */ char * zMsg; /** Error value associated with the error. This would presumably be some sort of language-specific error type, or maybe a cwal_string form of cMsg. */ cwal_value * value; /* TODO?: stack trace info, if tracing is on. */ }; /** Empty-initialized cwal_exception_info object. */ #define cwal_exception_info_empty_m { \ CWAL_RC_OK /*code*/, \ 0U /*msgLen*/, \ 0 /*cMsg*/, \ 0 /*zMsg*/, \ 0 /*value*/ \ } /** Empty-initialized cwal_exception_info object. */ extern const cwal_exception_info cwal_exception_info_empty; /** @internal Internal part of the cwal_ptr_table construct. Each cwal_ptr_table is made up of 0 or more cwal_ptr_page instances. Each slot in a page is analog to a hash code, and hash code collisions are resolved by creating a new page (as opposed to linking the individual colliding items into a list as a hashtable would do). */ struct cwal_ptr_page { /** List of pointers, with a length specified by the containing cwal_ptr_table::hashSize. */ void ** list; /** Number of live entries in this page. */ uint16_t entryCount; /** Link to the next entry in a linked list. */ struct cwal_ptr_page * next; }; /** Convenience typedef. */ typedef struct cwal_ptr_page cwal_ptr_page; /** @internal A "key-only" hashtable, the intention being, being able to quickly answer the question "do we know about this pointer already?" It is used for tracking interned strings and weak references. It was originally conceived to help track cycles during traversal, but it is not used for that purpose. */ struct cwal_ptr_table{ /** The number of (void*) entries in each page. */ uint16_t hashSize; /** A "span" value for our strange hash function. Ideally this value should be the least common sizeof() shared by all values in the table, and it degrades somewhat when using mixed-size values (which most cwal_values actually are, internally, as a side-effect of malloc() reduction optimizations). For tables where the sizeof() is the same for all members this type should provide near-ideal access speed and a fair memory cost if hashSize can be predicted (which it most likely cannot). */ uint16_t step; /** Where we keep track of pages in the table. */ struct { /** First page in the list. */ cwal_ptr_page * head; /** Last page in the list. We keep this pointer only to speed up a small handful of operations. */ cwal_ptr_page * tail; } pg; /** Internal allocation marker. */ void const * allocStamp; }; typedef struct cwal_ptr_table cwal_ptr_table; /** Empty-initialized cwal_ptr_table, for use in in-struct initialization. */ #define cwal_ptr_table_empty_m { \ 0/*hashSize*/, \ 0/*step*/, \ {/*pg*/ NULL/*head*/, NULL/*tail*/},\ NULL/*allocStamp*/ \ } /** Empty-initialized cwal_ptr_table, for use in copy initialization. */ extern const cwal_ptr_table cwal_ptr_table_empty; /** Holds the state for a cwal scope. Scopes provide one layer of the cwal memory model, and are modeled more or less off of their C++ counterparts. All allocation of new values in a cwal_engine context happen within an active scope, and the engine tracks a stack of scopes which behave more or less as scopes do in C++. When a scope is popped from the stack it is cleaned up and unreferences any values it currently owns (those allocated by it and not since taken over by another scope). Unreferencing might or might not destroy the values, depending on factors such as reference counts from cycles in the value graph or from other scopes. If values remain after cleaning up, it cleans up again and again until all values are gone (this is how it resolves cycles). When values are manipulated the engine (tries to) keep(s) them in the lowest-level (oldest) scope from which they are ever referenced. This ensures that the values can survive destruction of their originating scope, while also ensuring that a destructing scope can clean up values which have _not_ been taken over by another scope. This "can" (under specific usage patterns) potentially lead to some values being "orphaned" in a lower-level scope for an undue amount of time (unused but still owned by the scope), and the cwal_scope_sweep() API is intended to help alleviate that problem. */ struct cwal_scope { /** The engine which created and manages this scope. */ cwal_engine * e; /** Parent scope. */ cwal_scope * parent; /** Stores this object's key/value properties (its local variables). */ cwal_object * props; /** Internal memory allocation stamp. */ void const * allocStamp; /** Values allocated while this scope is the top of the stack are all placed here and unref'd when the scope is cleaned up. We split it into two lists to simplify and improve the performance of certain operations (while slightly complicating others ;). Maintenance reminder: we MUST clean up the containers completely (including cycles) BEFORE we clean the PODs or else we can end up either leaking values or (depending on whether we clean PODs 1x or Nx) end up pulling values out from under containers which still reference them. This is largely because interned strings. Without that the scope would only need to unref non-container values exactly 1 time. We COULD possibly optimize that by handling interned values in another list... TODO: refactor this into an array of lists, like cwal_engine::recycler. We can then refine it easily by adding extra lists for specific types or groups of types. */ struct { /** Head of the "PODs" (Plain old Data) list. This includes all non-containers. */ cwal_value * headPod; /** Head of the "Objects" list. This includes all container types. */ cwal_value * headObj; /** Holds items which just came into being and have a refcount of 0. This potentially gives us a faster/safer/easier sweep() operation. */ cwal_value * r0; /** Container values marked with the flag CWAL_F_IS_VACUUM_SAFE are managed in this list and treated basically like named vars for purposes of vacuuming. The intention is to provide a place where clients can put non-script-visible values which are safe from sweep/vacuum operations, but otherwise have normal lifetimes. Making a value vacuum-proof does not make it sweep-proof. */ cwal_value * headSafe; } mine; /** The depth level this scope was created at. This is used in figuring out whether a value needs to be migrated to a lower-numbered (a.k.a. "higher") scope for memory management reasons. Scope numbers start at 1, with 0 being reserved for "invalid scope.". */ cwal_size_t level; /** Internal flags. */ uint32_t flags; }; /** Empty-initialized cwal_scope object. */ #define cwal_scope_empty_m { \ NULL/*engine*/, \ NULL/*parent*/, \ NULL/*props*/, \ NULL/*allocStamp*/, \ {/*mine*/ 0/*headPod*/,0/*headObj*/,0/*r0*/, 0/*headSafe*/}, \ 0U/*level*/, \ 0U/*flags*/\ } /** Empty-initialized cwal_scope_api object. */ extern const cwal_scope cwal_scope_empty; /** Used to store "recyclable memory" - that which has been finalized but not yet free()d. */ struct cwal_recycler { /** Client-interpreted "ID" for this instance. It is used internally for sanity checking. */ int id; /** Current length of this->list. */ cwal_size_t count; /** Preferred maximum length for this list. Algorithms which insert in this->list should honor this value and reject insertion if it would be exceeded. */ cwal_size_t maxLength; /** Underlying list. The exact type of entry is context-dependent (e.g. cwal_value or cwal_kvp pointers). */ void * list; }; /** Convenience typedef. */ typedef struct cwal_recycler cwal_recycler; /** Default-initialized cwal_recycler object. */ #define cwal_recycler_empty_m {-1/*id*/, 0U/*count*/, 128U/*maxLength*/, NULL/*list*/} /** Default-initialized cwal_recycler object. */ extern const cwal_recycler cwal_recycler_empty; /** A generic buffer class. They can be used like this: @code cwal_buffer b = cwal_buffer_empty; int rc = cwal_buffer_reserve( &buf, 100 ); if( 0 != rc ) { ... allocation error ... } ... use buf.mem ... ... then free it up ... cwal_buffer_reserve( &buf, 0 ); @endcode To take over ownership of a buffer's memory: @code void * mem = b.mem; // mem is b.capacity bytes long, but only b.used // bytes of it has been "used" by the API. b = cwal_buffer_empty; @endcode The memory now belongs to the caller and must eventually be free()d. */ struct cwal_buffer { /** The number of bytes allocated for this object. Use cwal_buffer_reserve() to change its value. */ cwal_size_t capacity; /** The number of bytes "used" by this object. It is not needed for all use cases, and management of this value (if needed) is up to the client. The cwal_buffer public API does not use this member. The intention is that this can be used to track the length of strings which are allocated via cwal_buffer, since they need an explicit length and/or null terminator. */ cwal_size_t used; /** The memory allocated for and owned by this buffer. Use cwal_buffer_reserve() to change its size or free it. To take over ownership, do: @code void * myptr = buf.mem; buf = cwal_buffer_empty; @endcode (You might also need to store buf.used and buf.capacity, depending on what you want to do with the memory.) When doing so, the memory must eventually be passed to free() to deallocate it. */ unsigned char * mem; }; /** A typedef used by cwal_engine_type_name_proxy() to allow clients to hook their own type names into cwal_value_type_name(). Its semantics are as follows: v is a valid, non-NULL value. If the implementation can map that value to a type name it must return that type name string and set *len (if len is not NULL) to the length of that string. The returned bytes must be guaranteed to be static/permanent in nature (they may be dynamically allocated but must outlive any values associated with the name). If it cannot map a name to the value then it must return NULL, in which case cwal_value_type_name() will fall back to its default implementation. Example implementation: @code static char const * my_type_name_proxy( cwal_value const * v, cwal_size_t * len ){ cwal_value const * tn = cwal_prop_get(v, "__typename", 10); return tn ? cwal_value_get_cstr(tn, len) : NULL; } @endcode @see cwal_engine_type_name_proxy() */ typedef char const * (*cwal_value_type_name_proxy_f)( cwal_value const * v, cwal_size_t * len ); /** The core manager type for the cwal API. Each "engine" instance manages a stack of scopes and (indirectly) the memory associated with Values created during the life of a Scope. */ struct cwal_engine { cwal_engine_vtab * vtab; /** Internal memory allocation marker. */ void const * allocStamp; /** A handle to the top scope. Used mainly for internal convenience and sanity checking of the scope stack handling. */ cwal_scope * top; /** Scope stack. Manipulated via cwal_scope_push() and cwal_scope_pop(). */ cwal_scope * current; /** Holds any current pending exception, similarly to how the rv member is managed. */ cwal_value * exception; /** A slot for a single propagating value which will automatically be pushed up the stack. Intended for keywords which propagate via error reporting, so that they have a place to keep their result (if any). */ cwal_value * propagating; /** Where clients may store their customized base prototypes for each cwal_type_id. The indexes in this array correspond directly to cwal_type_id values, but (A) that is an implementation detail, and (B) some slots are not used (but we use this structure as a convenience to save cycles in type-to-index conversions). */ cwal_array * prototypes; /** A place to store values which are being destroyed during the traversal of cycles. Used for delayed freeing of cwal_value memory during destruction runs. See gcInitiator for more details. TODO: this currently flushes in reverse insertion order, but it should arguably flush in insertion order. Because those values have already been freed, this happens outside of any "lifetime" and has no side-effects vis-a-vis finalization. i.e. the order is actually irrelevant, technically speaking, but i "suspect" that a reverse order would be more efficient for the (de)allocator this thing feeds. */ cwal_value * gcList; /** When a scope is cleaned, if deferred freeing is not active then this pointer is set to some opaque value known only by the currently-being-freed scope before it starts cleaning up. As long as this is set, freeing and recycling of containers is deferred until cleanup returns to the being-freed scope, at which point this value is cleared and the gc list is flushed, all of its entries being submitted for recycling (or freeing, if recycling is disabled or full). This mechanism acts as a safety net when traversing cycles where one of the traversed values was freed along the way. The lowest-level scope from which destruction is initiated (normally also the bottom-most scope, but i would like to consider having scopes as first-class values) is the "fence" for this operation because destruction theoretically cannot happen for values in higher scopes during cleanup of a lower scope. i.e. when destructing scopes from anywhere but the top of the stack the initial scope in the destruction loop is the one which will queue up any to-be-freed containers for recycling, and it will flush the gc list. Because values form linked lists, we use those to form the chain of deferred destructions, so this operation costs us no additional memory (it just delays deallocation/recycling a bit) and is O(1). Note that types which cannot participate in graphs are not queued - they are (normally) immediately recycled or cwal_free()d when their refcount drops to 0 (or is reduced when it is already 0, as is the case for "temporary" values which never get a reference). */ void const * gcInitiator; /** Internal flags. See the API-internal CWAL_FLAGS enum for the gory details. */ uint32_t flags; /** List of list managers for (cwal_value*) of types which we can recycle. We can recycle memory for these types: integer, double, array, object, native, buffer, function, exception, x-string/z-string (in the same list), cwal_kvp, cwal_scope The lists are in an order determined by the internal function cwal_recycler_index(). Other types cannot be recycled as efficiently (e.g. cwal_string use a separate mechanism) or because they are never allocated (null, bool, undefined and any built-in shared value instances). Reminder: the default recycle bin sizes do not reflect any allocation size, simply the number of objects we don't free immediately, so there is little harm is setting them relatively high (e.g. 100 or 1000). Because cwal_value and cwal_kvp objects form a linked list, a given recycle bin may grow arbitrarly large without requiring extra memory to do so (we just link the values in each recycle bin, and those values have already been allocated). This all happens in O(1) time by simply making each new entry the head of the list (and removing them in that order as well). Maintenance reminder: if this list changes, update this array's length, cwal_recycler_index(), cwal_engine_recycle_max() (because of its special-case UNDEF/STRING type handling), AND cwal_engine_destroy(). */ cwal_recycler recycler[11]; /** reString is a special-case recycler for cwal_string values. String values are recycled based on their size. i.e. we won't recycle a 36-byte string's memory to serve a 10-byte string allocation request. As an optimization, we (optionally) pad string allocations to a multiple of some small number of bytes (e.g. 4 or 8), as this lets us recycle more efficiently (up to 36% more string recycling in some quick tests). See the API-internal CwalConsts::StringPadSize for details. */ cwal_recycler reString; /** Special-case recycler for cwal_weak_ref values. */ cwal_recycler reWeak; /** When an Array is destroyed we stuff its list memory (if any) here for re-use. These are currently handled separately than other recyclers but it might make sense to eventually (A) transform cwal_list into a linked list itself and (B) dynamically allocate these, so that we can handle them like other recycling pools (e.g. with runtime-configurable limits). Someday. Some rainy day. */ struct { /** When an Array is freed, if this list has space then its list contents are placed here for re-use by the next array which needs one. This is a primitive, inflexible approach to re-use, but it does indeed save us allocations. The underlying memory is "raw", so we don't have a nicer way to structure it without allocating another structure to track it. We "could" expand this to generically recycling blobs using lists of cwal_buffer to do so, but so far that seems like overkill. */ cwal_list lists[10]; /** The position in the list of the most-recently-saved entry. Cursor starts at -1 and can go up to ((sizeof(lists)/sizeof(lists[0]))-1). */ int cursor; } reList; /** Used just like this->reList, but for cwal_buffer memory, with slightly different heuristics regarding how we recycle it. TODO: consolidate reList and this type into one list. We can use cwal_buffer for storage of the raw memory in both cases (or a new, hypothetical, cwal_memchunk class). */ struct { /** The cwal_buffer counterpart of reList::lists. */ cwal_buffer buffers[5]; /** Used like reList::cursor, but for the this->buffers array. */ int cursor; } reBuf; /** A place for client code to associated a data pointer and finalizer with the engine. It is cleaned up early in the engine finalization process. */ cwal_state client; /** A value-to-type-name proxy, manipulated via cwal_engine_type_name_proxy(). */ cwal_value_type_name_proxy_f type_name_proxy; /** Where we store internalized strings. Maintenance note: this is-a cwal_ptr_table but uses its own hashing/searching/insertion/removal API. Do NOT use the equivalent cwal_ptr_table ops on this instance. See the internal cwal_interned_search(), cwal_interned_insert(), and cwal_interned_remove() for details. */ cwal_ptr_table interned; /** Memory for which we have a weak reference is annotated by simply inserting its address into this table. When the memory is cleaned up, if it has an entry here, we invalidate any cwal_weak_refs which point to it. */ cwal_ptr_table weakp; /** cwal_weak_ref instances are stored here, grouped by underlying memory type to speed up the invalidate-ref operation. Weak refs to buit-in constants are handled specially (to avoid allocating new instances and because they can never be invalidated). Some slots of this array (those of constant types, e.g. null/undefined/bool) are unused, but we keep their slots in this array because it greatly simplifies our usage of this array. The (void*) memory pointed to by weak references is held in weakr[CWAL_TYPE_WEAK_REF], since that slot is otherwise unused. We "could" use the NULL/BOOL/UNDEF slots for similar purposes. */ cwal_weak_ref * weakr[CWAL_TYPE_end]; /** The top-most scope. This is an optimization to avoid an allocation. */ cwal_scope topScope; /** If built with CWAL_ENABLE_TRACE to 0 then this is a no-op dummy placeholder, else it holds information regarding engine tracing. This state continually gets overwritten if tracing is active, and sent to the client via this->api->tracer. */ cwal_trace_state trace; /** Buffer for internal string conversions and whatnot. This buffer is volatile and its contents may be re-allocated or modified by any calls into the public API. Internal APIs need to be careful not to stomp the buffer out from under higher-scope public APIs and internal calls. */ cwal_buffer buffer; /** A buffer used by cwal_prop_sort(), to avoid having to allocate on each call. */ cwal_buffer sortBuf; /** Where Function-type call() hooks are stored. */ cwal_callback_hook cbHook; /** A place for storing metrics. */ struct { /** Each time a request is made to allocate a Value, the value type's entry in this array is increments. Does not apply to certain optimized-away situations like empty strings, bools/null/undef, and the constant numeric values. */ cwal_size_t requested[CWAL_TYPE_end]; /** Each time we have to reach into the allocator to allocate an engine resource, its type's entry in this array is incremented. This values will always be less than or equal to the same offered in the 'requested' member. */ cwal_size_t allocated[CWAL_TYPE_end]; /** The number of allocated bytes for each type is totaled here. We can't simply use (allocated*sizeof) for some types (e.g. strings, arrays, buffers, and hashtables), and this value requires some fiddling with in certain areas to ensure it gets all memory for some types (namely arrays and buffers, whose sizes change with time). In any case, it is only a close approximation because reallocs play havoc with our counting in some cases. */ cwal_size_t bytes[CWAL_TYPE_end]; } metrics; }; /** @def cwal_engine_empty_m Empty-initialized cwal_engine object. */ #define cwal_engine_empty_m { \ NULL /* api */, \ NULL /* allocStamp */, \ NULL /* top */, \ NULL /* current */, \ NULL /* exception */, \ NULL /* propagating */,\ NULL /* prototypes */, \ NULL /* gcList */,\ NULL /* gcInitiator */, \ 0U /* flags */, \ {/* recycler */ \ { CWAL_TYPE_INTEGER, 0, 20, NULL }, \ { CWAL_TYPE_DOUBLE, 0, 20, NULL }, \ { CWAL_TYPE_STRING/*x/z-strings, actually*/, 0, 10, NULL }, \ { CWAL_TYPE_OBJECT, 0, 20, NULL }, \ { CWAL_TYPE_ARRAY, 0, 20, NULL }, \ { CWAL_TYPE_NATIVE, 0, 10, NULL }, \ { CWAL_TYPE_BUFFER, 0, 10, NULL }, \ { CWAL_TYPE_FUNCTION, 0, 10, NULL }, \ { CWAL_TYPE_EXCEPTION, 0, 2, NULL \ /* We don't expect many exceptions, but if we */ \ /* don't recycle at least a couple, it's not */ \ /* worth having this member at all! */ \ }, \ { CWAL_TYPE_KVP, 0, 50, NULL }, \ { CWAL_TYPE_SCOPE, 0, 5, NULL } \ }, \ {/*reString*/ CWAL_TYPE_STRING, 0, 40, NULL }, \ {/*reWeak*/ CWAL_TYPE_WEAK_REF, 0, 10, NULL }, \ {/*reList*/ \ { /*lists*/ \ cwal_list_empty_m, cwal_list_empty_m, cwal_list_empty_m, \ cwal_list_empty_m, cwal_list_empty_m, cwal_list_empty_m, \ cwal_list_empty_m, cwal_list_empty_m, cwal_list_empty_m, \ cwal_list_empty_m \ }, -1 /*cursor*/ \ }, \ { /*reBuf*/ \ {/*buffers*/ \ cwal_buffer_empty_m,cwal_buffer_empty_m,cwal_buffer_empty_m,\ cwal_buffer_empty_m,cwal_buffer_empty_m \ }, \ -1 /*cursor*/ \ }, \ cwal_state_empty_m /* client */, \ NULL/*type_name_proxy*/, \ cwal_ptr_table_empty_m/*interned*/, \ cwal_ptr_table_empty_m/*weakp*/,\ {/*weakr*/NULL,NULL,NULL,NULL,NULL, \ NULL,NULL,NULL,NULL,NULL,\ NULL,NULL,NULL},\ cwal_scope_empty_m/*topScope*/,\ cwal_trace_state_empty_m/*trace*/, \ cwal_buffer_empty_m/*buffer*/, \ cwal_buffer_empty_m/*sortBbuf*/, \ cwal_callback_hook_empty_m/*cbHook*/, \ {/*metrics*/\ /*requested[]*/{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, \ /*allocated[]*/{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, \ /*bytes[]*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} \ }\ } /** Empty-initialized cwal_engine object. */ extern const cwal_engine cwal_engine_empty; /** Initializes a cwal_engine instance. vtab must not be NULL and must be populated in accordance with the cwal_engine_vtab documentation. e must either be a pointer to a NULL-initialized pointer or a pointer to a pre-allocated instance (possibly from the stack or embedded in another struct) with a clean state (e.g. by copying cwal_engine_empty or cwal_engine_empty_m over it). vtab MUST outlive e, and in practice it can normally be static. On success 0 is returned and *e points to the engine instance. On success e is initialized with a top scope, so clients need not use cwal_scope_push() before using the cwal_new_xxx() family of factory functions to allocate values. On error non-0 is returned and *e is NOT cleaned up so that any error state in the object can be collected by the client before freeing it. If *e is NULL on returning and argument validation succeeds, then allocation of the instance failed and CWAL_RC_OOM will be returned. If vtab.on_init() returns non-0, this function will return that code, but vtab.on_init() is only called if the priliminary initialization succeeds (it can only fail on allocation errors). In short: the caller must, regardless of whether or not this function succeeds, if *e is not NULL, eventually pass *e to cwal_engine_destroy(). If this function allocated it, that function will free it. Errors include: CWAL_RC_MISUSE: one of the arguments is NULL CWAL_RC_OOM: memory allocation failed Any other error code should be considered a bug in this code. Potential TODO: require the client to push the first scope, giving him a time slot between initialization and the first scope to set configuration options which might affect the first scope (currently we have none built in, and the vtab's on_init() hook can be used to make changes for the time being). */ int cwal_engine_init( cwal_engine ** e, cwal_engine_vtab * vtab ); /** Frees all resources owned by e. If e was allocated dyanamically by cwal_engine_init() (or equivalent) then this function deallocates it, otherwise e is a left in an empty state after this call (suitable for re-using with cwal_engine_init()). */ int cwal_engine_destroy( cwal_engine * e ); /** Installs f as a proxy for cwal_value_type_name(). See cwal_value_type_name_proxy_f for full details. f may be NULL but e may not. e may only have one type name proxy installed at a time, and this replaces any existing one. Returns the existing proxy (possibly NULL), so that the client may swap it in and out, but in practice the client sets this up only once in the initialization phase. @see cwal_value_type_name() @see cwal_value_type_name2() */ cwal_value_type_name_proxy_f cwal_engine_type_name_proxy( cwal_engine * e, cwal_value_type_name_proxy_f f ); /** Pushes a new scope onto e's stack, making it the current scope. If s is not NULL and (*s) is NULL then *s is assigned to a newly-allocated/recycled scope on success. If s is not NULL and (*s) is not NULL when this function is called then it must be a cleanly-initialized scope value, and this function will use it instead of allocating/recycling one. For example: @code cwal_scope sub_ = cwal_scope_empty; cwal_scope * sub = &sub_; int rc = cwal_scope_push(e, &sub); if(rc) { ...error... do NOT pop the scope ...} else { ...do your work, then... cwal_scope_pop(sub); } @endcode Using scopes this way can be considered a malloc-count optimization over simply passing NULL as the 's' parameter. Initializing scopes this way does not change how they are popped: cwal_scope_pop() is still the correct way to clean up the scope. Client who want to ensure that downstream code has not corrupted the stack can check if cwal_scope_current()==theirScopePointer before popping the stack (and should fail loudly if they do not match). Returns 0 on success. On error: CWAL_RC_MISUSE: e is NULL CWAL_RC_OOM: memory allocation error. This error can only happen if the user passes a NULL or a _pointer to NULL_ as the second parameter, otherwise this function allocates no memory and cannot fail if e and s are valid. When passed a client-supplied scope, this function has no error conditions as long as e is valid. */ int cwal_scope_push( cwal_engine * e, cwal_scope ** s ); /** Pops the current scope from the stack. Returns 0 on success. Returns CWAL_RC_MISUSE if !e and CWAL_RC_RANGE if e has no current scope (use cwal_scope_push() first)). Except as described above, this "really shouldn't" ever fail, and a failure is likely either the result of a cwal-internal bug or serious offenses against the memory management rules. */ int cwal_scope_pop( cwal_engine * e ); /** "Might partially clean up" the given scope, as follows... For each value owned by scope which has a reference count of _exactly_ 0, it is unref'd. Values with a refcount of 0 are considered "probationary" values, subject to summary cleanup. Once a value has ever had a reference added to it, it moves out of probationary status and cannot be affected by sweep operations unless they are once again re-probated (which can happen in one of several special cases). Thus this could be called after (or periodically during) loops which create lots of anonymous/throw-away values. It is only safe to call this if the client has explicitly referenced all values he is still holding a pointer to (and expects that pointer to be valid after this call). See cwal_new_VALUE() for a description of how references are acquired. Returns the number of unref's triggered by the sweep, or 0 if any arguments are invalid. Note that it does not count recursively-removed values in the return code because those cleanups happen at a different level. Performance is effectively O(N+M) when no cycles are introduced, where N=total number of probationary values and M is the cleaning costs of those values cleaned. Cyles theoretically cannot happen in a probationary object because that would necessarily cause a refcount increase of the value partaking in the cycle, which would move the value out of probationary state. Note that if two calls are made to this function without having allocated/transfered new values from/to s, the second (and subsequent) will be no-ops because only probationary values are affected in any way. There are certain abstract operation chains where calling this will almost certainly be fatal to the app. For example, consider this pseudocode: @code myFunction( 1+2, 7*8, somethingWhichSweeps ); @endcode the sweep activated in the 3rd argument could (depending on how the arguments are collected) destroy the temporaries before they get passed on to the function. Earlier in the docs we mentioned a special corner case where a value can re-enter probationary state. This happens when moving values up scopes while containers in lower (newer) scopes holding references to them get cleaned up. Example code taken from th1ish: @code assert 17 === false || scope { var obj = object {a:17} obj.a // implicit return value } @endcode Normally obj.a would be cleaned up by obj at scope's end, but the 'scope' operator supports implicit returns and thus needs to pass it unmolested up the scope chain. It does not modify the ref-count, but moves obj.a up a scope before popping the scope. obj's cleanup then recognizes that obj.a has an older scope than obj does, so it reduces the refcount but does not destroy the value. In this case, that moves (former) obj.a into the parent scope with a refcount of 0, moving it back into probationary state. @see cwal_engine_sweep() @see cwal_engine_vacuum() */ cwal_size_t cwal_scope_sweep( cwal_scope * s ); /** Returns the cwal_engine which owns s, or NULL if s is NULL. */ cwal_engine * cwal_scope_engine(cwal_scope const * s); /** Calls cwal_scope_sweep() on e's current scope, returning the result of that call. Returns 0 if !e or if e has no current scope. @see cwal_scope_sweep() @see cwal_engine_vacuum() */ cwal_size_t cwal_engine_sweep( cwal_engine * e ); /** If allScopes is false, this behaves like cwal_engine_sweep(), otherwise it sweeps each scope, as per cwal_scope_sweep(), starting at the current scope and working upwards in the stack. Returns the total of all resulting cwal_scope_sweep() calls. This is inherently a dangerous operation as it can sweep up values in higher scopes which are being used by the current scope if those values do not have a reference somewhere. Potential culprits here include: - Temporaries created while evaluating function arguments, which then get passed (without an explicit ref) to a function which triggers the recursive sweep. If the arguments get a reference, they are not problematic here. - Propagating result values, because they are not tracked directly by cwal, are not exempt from sweep-up. Initially, cwal kept track of a single result value, but it turned out (in th1ish) to be much easier to do from client code (the script interpreter). Maybe we can revisit that design decision someday. - Propagating exceptions are not immune to sweep-up, either. */ cwal_size_t cwal_engine_sweep2( cwal_engine * e, char allScopes ); /** DO NOT USE! This is an experiment. This function cleans up all values owned by the current scope by determining whether or not they refer to, or are referred to by, scope-level properties (variables). The mechanism is relatively simple: 1) Push a new scope onto the stack with the same parent as e's current scope, but fake its level to (s->level-1) so that it looks like an older scope. We'll call the current scope s1 and this new scope s2. 2) upscope s1's object properties and any values in s1 marked as vacuum-proof into s2. Because s2 looks like an older scope, this will transfer any values in s1 which are variables, vacuum-proof, or reachable via either of those, leaving any orphaned values in s1 but not in s2. 3) clean all values remaining in s1. 4) re-set s2's parent to be s1 and fake s2's level to (s1->level+1) so that s2 looks like a newer scope. 5) upscope (again) the object properties and vacuum-proofed values, this time from s2 to s1. Because of step 4, s1 now looks like a higher/older scope to the copy process, which will move the variables, and values referenced by them, back into s1. Note that we cannot simply move the value lists from s2 to s1 because we need to ensure that the value->scope pointers all point to where they need to, and the underlying engine does that for us if we just copy the values back again. 6) Clean up scope s2, as if it had been popped from the stack. The end result is that after this call (on success), only variabes, and values reachable via variable references, will be in the scope, all other (presumably script-unreachable) values having been cleaned up. This operation requires no allocation, just traversal of values to tag them with their new scope. It is, computationally, speaking, difficult to predict the performance. For current th1ish uses it is quite fast enough to run very often (after every expression evaluation), but very complex graphs will slow it down a bit. For most purposes (no, or only few, simple graphs) it can be considered linear on the number of values owned by the scope. ACHTUNG: this invalidates any and all of the following pointers: - Values owned by this scope but which are not reachable from either scope-level variables or a vacuum-proof value. Returns 0 on success. On success, if sweepCount is not 0 then it is set to the number of values removed from the scope by this operaiton. If sweepCount is 0, this operation is a few ticks faster because it does not have to do any extra counting. Any error other than argument validation (CWAL_RC_MISUSE) indicates either an allocation problem or unexpected bits were found while fiddling around, either of which must be treated as fatal to the current scope. In this case, the current scope will be cleaned up in its entirety (but not popped from the scope stack) because we simply have no other sane recovery strategy where all known values have some reasonable lifetime. Since a refactoring which avoids any allocation, the only possible errors are invalid arguments or corruption detected during the operation (indeed, possibly caused by it ;). To make specific Values immune to vacuuming, use cwal_value_make_vacuum_proof(). Design note: sweepCount is a signed int because initial tests in th1ish have added values to the scope (possibly internals not visible from script code), leading to an overall negative sweep count. i believe this to be either a th1ish-side usage error or bug, however: sweepCount should always be 0 or positive on success, and this code assert()s that that is so. @see cwal_value_make_vacuum_proof() @see cwal_engine_sweep() */ int cwal_engine_vacuum( cwal_engine * e, int * sweepCount ); /** Sets the given pointers as client state in e. What that means is: - e applies no meaning to the state but will, at cleanup time, call dtor() (early on in the engine shutdown process) to clean up the state if dtor is not 0. - There can be only one piece of client state installed at a time, and this function fails with CWAL_RC_ACCESS if state is already set (to avoid having to answer questions about its lifetime). - cwal_engine_client_state_get() can be used, passed the same (e, typeId) values used here, to fetch the pointer later on. Calls to that function with other (e, typeId) combinations will return 0. The typeId can be an arbitrary pointer value, but must outlive e. It is typically the address of some static const value associated with state's concrete data type. Returns 0 on success. Errors include: - CWAL_RC_MISUSE if e or state are 0 (typeId and dtor may be 0) - CWAL_RC_ACCESS if state has already been sete on e. */ int cwal_engine_client_state_set( cwal_engine * e, void * state, void const * typeId, cwal_finalizer_f dtor); /** If cwal_engine_client_state_set() was passed e and typeId at some point then this returns the state pointer, otherwise it returns 0. */ void * cwal_engine_client_state_get( cwal_engine * e, void const * typeId ); /** On success, *s is assigned to the current scope and 0 is returned. On error *s is not modified and one of the following are returned: CWAL_RC_MISUSE: one of the arguments is NULL. CWAL_RC_RANGE: e currently has no scope. */ int cwal_scope_current( cwal_engine * e, cwal_scope ** s ); /** Simplified form for cwal_scope_current() which returns the current scope, or 0 if !e or if there are no scopes. */ cwal_scope * cwal_scope_current_get( cwal_engine * e ); /** Interpreter-level flags for "variables." Maintenance reminder: the Container base type (cwal_obase) and cwal_kvp only have 16 bits for flags. That said, padding is normally applied which we could conceivably use by adding another 2 flag bytes. */ enum cwal_var_flags { CWAL_VAR_F_NONE = 0, /** Indicates that the variable should be "const." cwal_var_set() refuses to set a variable which has this flag. */ CWAL_VAR_F_CONST = 0x0001, /** Indicates that property iteration operations on types capable of holding key/value pairs should not expose properties with this flag. e.g. cwal_props_visit_kvp() and friends. */ CWAL_VAR_F_HIDDEN = 0x0002, /** NOT YET IMPLEMENTED. May never be. Tells the engine to do case-INsensitive property comparisons on this Container (cwal_obase) value. This would only be possible for on String keys and would require UTF-8-aware comparisons. Not likely to happen soon. */ CWAL_VAR_F_CASE_INSENSITIVE = 0x0004, /** Indicates that any existing flags of the property should be kept as-is. For newly-created properties this is applied as if it were CWAL_VAR_F_NONE. */ CWAL_VAR_F_PRESERVE = 0xFFFF }; /** Returns s's property storage object, instantiating it if necessary. If s is NULL, or on allocation error, it returns NULL. The returned Value will be of "some container type," currently Object (as Hashes turned out to be too expensive). */ cwal_value * cwal_scope_properties( cwal_scope * s ); /** Returns the parent scope of s, NULL if !s or s has no parent. */ cwal_scope * cwal_scope_parent( cwal_scope * s ); /** Returns the top scope in s's stack, NULL if !s. */ cwal_scope * cwal_scope_top( cwal_scope * s ); /** Searches s and optionally its parents for the given key. If maxDepth is greater than 0 then only up to that many scope levels is searched. If maxDepth is less than 0 then any number of parent levels can be searched. A maxDepth of 0 means to search only s. If foundIn is not NULL, it is assigned the scope in which the property is found. Returns NULL if !s, !k, or no entry is found, else returns the searched-for value. */ cwal_value * cwal_scope_search_v( cwal_scope * s, int maxDepth, cwal_value const * k, cwal_scope ** foundIn ); /** Similar to cwal_prop_get_kvp_v(), but searches through a scope (optionally recursively). upToDepth is interpreted as described for cwal_scope_search_v(). If a match is found, the underlying key-value pair is returned and foundIn (if not 0) is assigned to the scope in which the match was found. Returns 0 if no mach is found, !s, or !key. ACHTUNG: the returned object is owned by an object which is owned either by the scope the key is found in or an older one, and it may be invalidated on any modification of that object (i.e. any changing of properties in that scope). */ cwal_kvp * cwal_scope_search_kvp_v( cwal_scope * s, int upToDepth, cwal_value const * key, cwal_scope ** foundIn ); /** The C-string counterpart of cwal_scope_search_kvp_v(). If keyLen is 0 and *key then cwal_strlen() is used to determine key's length. */ cwal_kvp * cwal_scope_search_kvp( cwal_scope * s, int upToDepth, char const * key, cwal_size_t keyLen, cwal_scope ** foundIn ); /** Functionally equivalent to cwal_scope_search_v(), except that if (key && !keyLen) then the equivalent of equivalent is used to determine key's length. Returns as described for cwal_scope_search_v(), and also returns NULL if (!key). */ cwal_value * cwal_scope_search( cwal_scope * s, int maxDepth, char const * key, cwal_size_t keyLen, cwal_scope ** foundIn ); /** Sets a property in s or one of its parent scopes. If upToDepth is 0 then the property will be set in s, else s and up to upToDepth parents will be searched for the key (e.g. a value of 1 means to check this scope and its parent, but no higher). If upToDepth is negative it means "arbitrarily high up in the stack." If it is found then it is set in the scope it was found in, else it is set in s. To unset a key, pass a val of NULL. Returns 0 on success. */ int cwal_scope_chain_set_v( cwal_scope * s, int upToDepth, cwal_value * key, cwal_value * val ); /** The C-string form of cwal_scope_chain_set_v(), with the addition than if k is not NULL and keyLen is 0 then the equivalent of strlen(k) is used to get its length. */ int cwal_scope_chain_set( cwal_scope * s, int upToDepth, char const * k, cwal_size_t keyLen, cwal_value * v ); /** "Declares" a variable in the given scope. Declaring is almost identical to setting (cwal_var_set_s() and friends) but fails with CWAL_RC_ALREADY_EXISTS if the given entry is already declared (or set) in s. In addition, if v==NULL then cwal_value_undefined() is used as the default. If s is NULL then e's current scope is used. If e is 0 then s's engine is used. If both are NULL, CWAL_RC_MISUSE is returned. Returns CWAL_RC_MISUSE if key is NULL or empty (or otherwise starts with a NUL byte). */ int cwal_var_decl_s( cwal_engine * e, cwal_scope * s, cwal_string * key, cwal_value * v, uint16_t flags ); /** Equivalent to cwal_var_decl_s() except that it takes its key in raw cwal_value form. */ int cwal_var_decl_v( cwal_engine * e, cwal_scope * s, cwal_value * key, cwal_value * v, uint16_t flags ); /** Functionally identical to cwal_var_decl_s(), but takes a C-style string (key) which must be keyLen bytes long. If keyLen is 0 and *key is not 0 then the equivalent of strlen(key) is used to figure out the length. */ int cwal_var_decl( cwal_engine * e, cwal_scope * s, char const * key, cwal_size_t keyLen, cwal_value * v, uint16_t flags ); /** If c contains a key matching the given one then this function returns any flags declared for that property (e.g. via cwal_var_decl()). If no entry is found, or on error, a negative value is returned. 0 indicates that a value was found but it has no flags. TODO: add a convenience version which returns the value and flags in one go, to avoid duplicate lookups in cases using this. */ int cwal_prop_flags_v( cwal_value *c, cwal_value * key ); /** Sets the given variable name to the given value in the given scope. Note that a NULL value means to _unset_ the value, effectively removing the object from the scope's list of variables (its may have live references elsewhere). If s is NULL then e's current scope is used. If e is 0 then s's engine is used. If both are NULL, CWAL_RC_MISUSE is returned. Returns 0 on success, CWAL_RC_MISUSE if e or key are NULL, CWAL_RC_NOT_FOUND if (v==NULL) and the key is not found, and may propagate CWAL_RC_OOM or other lower-level errors. If the given value is not found in this object, but is found in a parent scope, it is set in that parent scope, not s. */ int cwal_var_set_s( cwal_engine * e, cwal_scope * s, cwal_string * key, cwal_value * v ); /** Equivalent to cwal_var_set_s() except that it takes its key in plain cwal_value form. */ int cwal_var_set_v( cwal_engine * e, cwal_scope * s, cwal_value * key, cwal_value * v ); /** Functionally identical to cwal_var_set_s(), but takes a C-style string (key) which must be keyLen bytes long. If keyLen is 0 then the equivalent of strlen(key) is used to figure out the length. */ int cwal_var_set( cwal_engine * e, cwal_scope * s, char const * n, cwal_size_t nLen, cwal_value * v ); /** Equivalent to cwal_var_set(s,key,keyLen,NULL). Returns CWAL_RC_NOT_FOUND if the given var name is not found. */ int cwal_var_unset( cwal_engine * e, cwal_scope * s, char const * key, cwal_size_t keyLen ); /** Equivalent to cwal_var_set_s(s,key,NULL). Returns CWAL_RC_NOT_FOUND if the given var name is not found. */ int cwal_var_unset_s( cwal_engine * e, cwal_scope * s, cwal_string * key ); /** Equivalent to cwal_var_set_s() except that it takes its key in cwal_value form. */ int cwal_var_unset_v( cwal_engine * e, cwal_scope * s, cwal_value * key ); /** Searches for a declared variable with the given given name in the given scope. On success 0 is returns and: v (if not NULL) is set to the value, containingScope (if not NULL) is set to the scope where *v was found. If searchParents is true (non-0) and the given key is not found in s, s's parent scopes are searched. Thus containingScope may (on success) point to some scope other than s, but only if searchParents is true. On error non-0 is returned and neither v nor containingScope are modified. The typical error return codes would be CWAL_RC_MISUSE (invalid arguments) or CWAL_RC_NOT_FOUND. Anything else is likely a "real" error. If this routine finds a variable in a parent scope and that variable is const, it will add that variable to the initially-searched scope to speed up future searches. It cannot do this for non-const values because doing so would "sorely misinteract" with the expected values of variables which get assigned to across scope levels (e.g. in a loop body). */ int cwal_var_get_s( cwal_engine * e, cwal_scope * s, cwal_string * key, cwal_value ** v, char searchParents, cwal_scope ** containingScope ); /** Equivalent to cwal_var_get_s() but takes its key argument as a C-style string, which must be at least keyLen bytes long. */ int cwal_var_get( cwal_engine * e, cwal_scope * s, char const * key, cwal_size_t keyLen, cwal_value ** v, char searchParents, cwal_scope ** containingScope ); /** Equivalent to cwal_var_get_s() except that the key is provided in cwal_value form. */ int cwal_var_get_v( cwal_engine * e, cwal_scope * s, cwal_value * key, cwal_value ** v, char searchParents, cwal_scope ** containingScope ); /** cwal_value_unref() is THE function clients must use for destroying values allocated via this framework. It decrements the reference count of a cwal_value, cleaning up if needed. Whether or not clients must (or should) call this function depends on how the values are used. Newly-created values have a reference count of 0. This reference count is increased when the value is added to a container (as a key _or_ value) or the client calls cwal_value_ref(). If a client calls cwal_value_ref() then he is obligated to call this function OR allow the scope to clean up the value when the scope is popped. If a client creates a new value, does not explicitly reference, but adds it to a container (implicitly referencing it) then he must _NOT_ call cwal_value_unref() (doing so will leave a dangling pointer in the container). Caveat: unref'ing a STRING value without having explicitly referenced it (cwal_value_ref()) can potentially be dangerous if string interning is enabled and more than one (shared) reference to that string is alive. This does not apply to other value types. It is never _necessary_ to call this function: if it is not called, then the value's owning scope will clean it up whenever the scope is cleaned up OR (for newly-allocated values with no refcount) during a sweep or vacuum operation (see cwal_engine_sweep() and cwal_engine_vacuum()). The safest guideline for client usage, unless they really know what they're doing and how to use/abuse this properly: - DO NOT EVER call this function UNLESS one of the following applies: A) You have called cwal_value_ref() on that value. B) You created the value using cwal_new_TYPE() (or one of the various convenience variants) AND it is NOT a CWAL_TYPE_STRING value (x-strings and z-strings ARE safe here). All other uses, unless the caller is intricately familiar with cwal's memory management, "might" be dangerous. String interning (if enabled) leaves open a corner case where it is not safe to call this on a string (CWAL_TYPE_STRING) value unless the caller has explicitly cwal_value_ref()'d the string or otherwise has very, very (almost inconceivably VERY) much certainty about its ownership. Note that string interning does not apply to x-strings and z-strings, so they are "safe" in this context. This does not mean string interning is unsafe in general (in normal uses cases it works just fine), just that it opens up a corner case involving shared strings which does not apply to other types. Clients MUST treat this function as if it destroys v; it has semantically the same role as free(3) and v must not be used by the client after this function is called. Likewise, the return values may, for essentially all purposes, be ignored by the client, but this function returns a value to describe what it actually does, the semantics of which are somewhat different from the rest of the framework (i.e. non-0 is not necessarily an error): CWAL_RC_MISUSE if !e or !v. CWAL_RC_OK if this function does nothing but that's not an error (e.g. if passed a handle to one of the built-in constant values). CWAL_RC_DESTRUCTION_RUNNING if v is currently being destroyed. This result should ONLY be returned while destructing a graph in which v has cycles. Client code should never see this unless they are doing manual cleanup of container values in the destructors of their own custom cwal_native implementations. CWAL_RC_HAS_REFERENCES if v was not destroyed because it still has pending references. CWAL_RC_FINALIZED if this function actually finalizes the value (refcount drops to (or was) zero). CWAL_RC_DESTRUCTION_RUNNING: only if v's final destruction is already running, and only applies to "container" types. This "should" never appear when called from client code. It indicates the discovery of a cycle involving v during cleanup of v's children during destruction. v might be modified in that case but its refcount remains valid vis-a-vis e's internal state (i.e. treat it as if this call has freed v, though we know that in reality v is currently being cleaned up). Implementation notes: - This function might recycle v's memory for the next allocation of the same value type. Some types are not recycled or recycled differently (namely strings because their allocation mechanism limits how well we can recycle them). The interning mechanism, however, (if enabled) ensures that we don't need to alloc/free strings in many common use patterns. - Note that some built-in/constant values do not actually participate in reference-counting (see cwal_value_is_builtin()) but, for reasons of consistency, should be treated as if they do, and should be passed to this function just like any other values (it is a harmless no-op). - And now that you actually read all of that, here's the real kicker: in practice (meaning "in th1ish"), it is very rarely necessary to ever use this function because the majority of values are either temporaries (swept up or cleaned up by the scope) or assigned to containers/variables, giving them an owner/a reference. As a general pattern, this library's architect tends to use this function primarily in error handling to explicitly free up locally-created values, but he admits that that's pedantic overkill because the current scope will clean them up anyway (and th1ish uses lots of scopes to keep lifetimes short). @see cwal_value_ref() */ int cwal_value_unref( cwal_value * v ); /** EXPERIMENTAL! A very close relative of cwal_value_unref(), this variant behaves slightly differently: - If v is NULL or has no refcount, it is left untouched. Builtin values are implicitly covered by this condition. - If v has a positive refcount, its refcount is reduced by 1. If the refcount is still above 0, there are no further side-effects. If the refcount drops to 0, v is _reprobated_ in its current owning scope. That means that it gets moved into the list of values which which can be swept up by cwal_engine_sweep() and friends. The intended use of this function is to give script engines a way to "let go" of a value with a clean conscience, without outright destroying it or having to be unduly uncertain about whether it will survive the currently-evaluating expression. This allows them to take advantage of scope lifetimes and sweep intervals (both of which they control) to more closely manage the lifetimes of values which are potentially temporary but will possibly be used as result values. */ void cwal_value_unhand( cwal_value * v ); /** Increments v's reference count by 1. Returns 0 on success. The only error condition is if v is NULL, v has no scope (indicating an internal error or memory mis-use/corruption), or if incrementing would overflow past compile-time boundaries (some number approaching the high-end of cwal_size_t's limit). Note that some built-in/constant values do not actually participate in reference-counting but, for reasons of consistency, should be treated as if they do, and may be passed to this function just like any other values. (See cwal_value_is_builtin() for the list of shared/constant values.) Claiming a reference point never requires a new allocation. Calling this function obligates the client to eventually either call cwal_value_unref() to release the reference OR be content to wait until the value's owning scope (eventually) cleans up (at which point this value is freed regardless of its reference count). @see cwal_value_unref() */ int cwal_value_ref( cwal_value * v ); /** Sets v as the (single) specially-propagating result value for e. This is only to be used by keywords which toss a value up the call stack, and use non-0 result codes to do so, but are not necessarily errors. e.g. return, break, exit. If v is not 0, a reference is added to v, held by e. If e has a prior propagating value, it gets unhanded after referencing v (see cwal_value_unhand()). If v is 0, any propagating value is unhanded before being removed from the propagating value slot. Returns v. @see cwal_propagating_get() @see cwal_propagating_take() */ cwal_value * cwal_propagating_set( cwal_engine * e, cwal_value * v ); /** Returns the currently specially-propagating value from e, if any. Ownership of the value is not modified by this call, and e still holds a reference to it (if the value is is not 0). The intention is that this value will be set for an as-yet unhandled RETURN or BREAK statements, as well as for an EXIT or FATAL (which necessarily can't be handled until the app level, or it loses its functionality). @see cwal_propagating_take() @see cwal_propagating_set() */ cwal_value * cwal_propagating_get( cwal_engine * e ); /** Effectively the same as calling cwal_propagating_get() followed by cwal_propagating_set(se,0). Returns the result of the first call. Note that the returned value may very well be a temporary awaiting a reference before the next sweep-up. @see cwal_propagating_get() @see cwal_propagating_set() */ cwal_value * cwal_propagating_take( cwal_engine * e ); /** This sets the given value to be e's one and only "exception" value. This value is given special treatment in terms of lifetime - it wanders up the call stack as scopes are popped, until the client calls this again with x==NULL. x may be NULL, in which case any pending exception is cleared and its handle gets "unhanded" (see cwal_value_unhand()) and CWAL_RC_OK is returned. Otherwise... If x is not NULL then this function returns CWAL_RC_EXCEPTION on success(!!!) or a different non-0 cwal_rc value on error. The justification for this is so that it can be called in as the return expression for a callback which is throwing the exception. The exception value gets a reference held by the engine, and any pending exception is cwal_value_unhand()ed. If x is NULL then 0 indicates success. Interpretation of the exception value is client-dependent, and cwal's only special handling of it is ensuring that it survives the ride up a popping scope stack. While the exception value may be of any cwal Value type, the cwal_exception class is specifically intended for this purpose. @see cwal_exception_get() */ int cwal_exception_set( cwal_engine * e, cwal_value * x ); /** Convenience form of cwal_exception_set() which uses cwal_new_stringfv() to create an exception message string for the new cwal_exception value (which can be fetched using cwal_exception_get()). See cwal_printf() for the supported formatting options. code is the exception's error code. fmt and following arguments are the formatted error message. If !*fmt then this call creates an exception value with no message part. */ int cwal_exception_setfv(cwal_engine * e, int code, char const * fmt, va_list args); /** Identical to cwal_exception_setfv() but takes its arguments in ellipsis form. */ int cwal_exception_setf(cwal_engine * e, int code, char const * fmt, ...); /** If e has a current exception value, it is returned to the caller and (possibly) transfered into the calling scope (for lifetime/ownership purposes). If not, NULL is returned. Note that the lifetime of the exception value is managed internally by the engine to ensure that it survives as scopes are popped. If the client wants to stop this from happening for a given exception value, he should use cwal_exception_set() to set the current exception state to 0. That will keep the (previous) current exception rooted in its current scope, from which it will wander only if it is later referenced by an older scope. */ cwal_value * cwal_exception_get( cwal_engine * e ); /** NOT IMPLEMENTED. Frees any message-related memory owned by err (or shared with it, in the case of err->value). Returns 0 on success, or CWAL_RC_MISUSE if either paramter is 0. After calling this, err contains an empty state and must eventually be deallocated using whatever mechanism complements its allocation (e.g. do nothing more for stack-allocated objects or those embedded in another struct). */ int cwal_exception_info_clear( cwal_engine * e, cwal_exception_info * err ); /* NOT IMPLEMENTED. */ int cwal_engine_err_set( cwal_engine * e, cwal_exception_info * err ); /** Returns e's error state. This pointer stays valid for the life of e, but its contents may change during (almost) any calls into this API which take e as a parameter, and thus clients must take care not to hold copies of the info object's internal pointers. They are owned by e and can be cleaned up using cwal_exception_info_clear() (but the API does this as needed, so clients normally need not deal with it). */ cwal_exception_info * cwal_engine_err_get( cwal_engine * e ); /** Sends (src,n) through the engine-specified output mechanism (specified via its vtab). See cwal_output_f() for the semantics. Returns 0 on success: CWAL_RC_MISUSE: e or src are 0. Any other error code is propagated from the output routine. This function is a no-op if n==0. TODO? consider making (0==src, 0==n) a heuristic for signaling a desire to flush the output. */ int cwal_output( cwal_engine * e, void const * src, cwal_size_t n ); /** If e's vtab is set up to be able to flush its output channel, this calls that function and returns its result. Returns CWAL_RC_MISUSE if !e or e is not initialized. Returns 0 on success or if there is nothing to do. */ int cwal_output_flush( cwal_engine * e ); /** printf()-like variant of cwal_output(). See cwal_printf.h for the format specifiers (they're pretty much standard, plus some extensions inherited from sqlite). */ int cwal_outputf( cwal_engine * e, char const * fmt, ... ); /** va_list variant of cwal_outputf(). */ int cwal_outputfv( cwal_engine * e, char const * fmt, va_list args ); /** The cwal_new_VALUE() function does not really exist - it is here for documentation purposes to consolidate the common documentation for the large family of cwal_new_xxx() routines. These routines typically come in some variation of these three forms: 1) cwal_value * cwal_new_SOMETHING(); 2) cwal_value * cwal_new_SOMETHING(cwal_engine*); 3) cwal_SOMETHING * cwal_new_SOMETHING(cwal_engine*, ...); The first form is only for types which do not allocate memory, meaning types with a known set of constant values (boolean, undefined, null) and a small set of constant values for other types (empty string and numeric 0). The second form is only for types which need no initialization parameters, e.g. Objects and Arrays. The third form is used by types which require a concrete native value for their initialization. The values held by such types are immutable, and cannot be changed for the lifetime of the cwal_value handle. Ownership of the new returned value is initially held by the scope which is active during creationg. A newly-created value as a reference count of 0 (not 1, though it was in versions prior to 20130522). A value with a refcount of 0 is considered a "probationary" value, and has a special status in the scope-sweep operations. In short, a sweep operation will free up _all_ values with refcount 0 in the scope. If clients need to ensure a specific lifetime, they must provide the value with a reference. This can happen in one of several ways: - Insert the value into a container. e.g. set it as an Object key or value, or insert it into an Array. - Call cwal_value_ref() to increase the refcount by 1. If a value is ever referenced from a higher (older) scope, it is automatically moved into that scope for ownership/cleanup purposes. This ensures that values live as long as the last scope which references them, or until they are otherwise cleaned up. Semantically speaking this function Returns NULL on allocation error, but the non-allocating factories never actually allocate (and so cannot fail). Nonetheless, all values returned by variations of this function must be treated as if it were an allocated value (this consistency is encouraged to avoid clients special-casing code due to a cwal-internal implementation detail). General rules for the cwal_new_XXX() family of functions (all of which point the reader to this function) are: - Those which (might) allocate memory take a cwal_engine value as their first argument. Non-allocating factories SHOULD be treated as if they allocate, e.g. by assuming that they participate in the normal reference-counting and de/allocation mechanism (which they don't, actually). Note that cwal_new_string() and friends can return a re-used pointer for an interned string, but each call increases the reference count by 1, and each must be followed by a call to cwal_value_unref() or (for the last point) allowing the scope or a parent container to clean it up. - To re-iterate: certain built-in/constant values neither allocate nor participate in reference-counting/scope-tracking, but that is an internal implementation detail, and clients should treat all values as equivalent for memory-management purposes except for noted for specific APIs. - All newly-allocated values initially have a reference count of 0. Client code may call cwal_value_unref() to immediately unreference a value, possibly cleaning it up (depending on other references to the value). Clients may cal cwal_value_ref() to claim a reference point, and adding values to containers also manipulates their reference count. */ void cwal_new_VALUE(cwal_engine * e, ...); /** Creates a new cwal_value from the given boolean value. @see cwal_new_VALUE() */ cwal_value * cwal_new_bool( char v ); /** Semantically the same as cwal_new_bool(), but for doubles. @see cwal_new_VALUE() */ cwal_value * cwal_new_double( cwal_engine * e, cwal_double_t v ); /** Semantically the same as cwal_new_bool(), but for integers. @see cwal_new_VALUE() */ cwal_value * cwal_new_integer( cwal_engine * e, cwal_int_t v ); /** If v is within the range CWAL_INT_T_MIN and CWAL_INT_T_MAX then this function behaves like cwal_new_integer(), else it behaves like cwal_new_double(). */ cwal_value * cwal_new_number( cwal_engine * e, cwal_double_t v ); /** Returns the special "null" value. See cwal_new_VALUE() for notes regarding the returned value's memory. @see cwal_new_VALUE() */ cwal_value * cwal_value_null(); /** Returns the special "undefined" value. See cwal_new_VALUE() for notes regarding the returned value's memory. @see cwal_new_VALUE() */ cwal_value * cwal_value_undefined(); /** Equivalent to cwal_new_bool(0). @see cwal_new_VALUE() */ cwal_value * cwal_value_false(); /** Equivalent to cwal_new_bool(1). @see cwal_new_VALUE() */ cwal_value * cwal_value_true(); /** Converts the given value to a boolean, using JavaScript semantics depending on the concrete type of val: undef or null: false boolean: same integer, double: 0 or 0.0 == false, else true object, array: true string: length-0 string is false, else true. Returns 0 on success and assigns *v (if v is not NULL) to either 0 or 1. On error (val is NULL) then v is not modified. */ int cwal_value_fetch_bool( cwal_value const * val, char * v ); /** Simplified form of cwal_value_fetch_bool(). Returns 0 if val is NULL. */ char cwal_value_get_bool( cwal_value const * val ); /** Similar to cwal_value_fetch_bool(), but fetches an integer value. The conversion, if any, depends on the concrete type of val: NULL, null, undefined: *v is set to 0 and 0 is returned. string, object, array: *v is set to 0 and CWAL_RC_TYPE is returned. The error may normally be safely ignored, but it is provided for those wanted to know whether a direct conversion was possible. integer: *v is set to the int value and 0 is returned. double: *v is set to the value truncated to int and 0 is returned. */ int cwal_value_fetch_integer( cwal_value const * val, cwal_int_t * v ); /** Simplified form of cwal_value_fetch_integer(). Returns 0 if val is NULL. */ cwal_int_t cwal_value_get_integer( cwal_value const * val ); /** The same conversions and return values as cwal_value_fetch_integer(), except that the roles of int/double are swapped. */ int cwal_value_fetch_double( cwal_value const * val, cwal_double_t * v ); /** Simplified form of cwal_value_fetch_double(). Returns 0.0 if val is NULL. */ cwal_double_t cwal_value_get_double( cwal_value const * val ); /** Equivalent to cwal_string_value( cwal_new_string(e,str,len) ). @see cwal_new_string() @see cwal_new_VALUE() */ cwal_value * cwal_new_string_value(cwal_engine * e, char const * str, cwal_size_t len); /** Returns a pointer to the NULL-terminated string bytes of str. The bytes are owned by string and will be invalided when it is cleaned up. If str is NULL then NULL is returned. If the string has a length of 0 then "" is returned. @see cwal_string_length_bytes() @see cwal_value_get_string() */ char const * cwal_string_cstr(cwal_string const *v); /** Semantically the same as cwal_new_bool(), but for strings. This creates a JSON value which copies the first n bytes of str. The string will automatically be NUL-terminated. If (!str || !*str), this function still returns non-NULL value representing that empty string. (The empty string is a library-internal constant, shared across all invocations.) If (str && *str && (0==len)) then strlen() (or equivalent) is used to determine the length (in bytes, not characters) of str. This is provided only as a convenience, and clients "should be" in the habit of passing the size. Returns NULL on allocation error. See cwal_new_VALUE() for important information about the returned memory. For what it's worth: practice has shown the this function taking cwal_size_t causes more headaches in client-side code than it should. This function _should_ take an unsigned type and use strlen() to figure out the length of len is negative. In practice (in other projects), that is much simpler to use in client code. i regret the original decision of using 0 to mean use strlen(), but changing it now would be a huge hassle. Possibly worth it. Maybe something to try on a rainy day (it will cause a lot of downstream breakage in th1ish and its plugins). */ cwal_string * cwal_new_string(cwal_engine * e, char const * str, cwal_size_t len); /** printf-like form of cwal_new_string(). See cwal_printf() for the formatting specifiers. */ cwal_string * cwal_new_stringf(cwal_engine * e, char const * fmt, ...); /** printf-like form of cwal_new_string(). See cwal_printfv() for the formatting specifiers. */ cwal_string * cwal_new_stringfv(cwal_engine * e, char const * fmt, va_list args); /** Experimental! Not yet known to work perfectly. Creates a new handle for an "x-string" (as in "external"). This is different from cwal_new_string() in that it does not copy str's bytes. The client must guaranty that len bytes of str are valid for at least as long as the returned value is used. i.e. this is safe to use on static strings or on buffers which the client can guaranty outlive the returned string. If len is 0 and *str is not NUL then the equivalent of strlen(str) is used to determine the length. If !str or !*str then this function behaves identically to cwal_new_string() passed that same string. Returns NULL on error. The returned string cannot be differentiated from a non-external string using the public API, with the minor exception that calling cwal_string_cstr() on the returned string will (for non-zero length strings) return the same C-string pointer passed to this function (as opposed to an internal one). Be aware that... - X-strings might not be NUL-terminated, so routines which blindly display strings until the NUL might be surprised by the results of doing so with cwal_string_cstr(anXString). - Strings shorter than sizeof(char *) are not going to get any memory benefits compared to non-X-strings. Use normal strings for those. - X-strings do not partake in string internalization, because doing so would potentially invalidate lifetime guarantees. Their "empty shells" (all but the external string pointer) participate in recycling. - X-strings DO partake in the length-0-string optimization, so cwal_new_string(e,"",0) and cwal_new_xstring(e,"",0) will return the same value (but that's an implementation detail clients should not make code-level decisions based on). */ cwal_string * cwal_new_xstring(cwal_engine * e, char const * str, cwal_size_t len); /** Equivalent to passing the return value of cwal_new_xstring(e,str,len) to cwal_string_value(). */ cwal_value * cwal_new_xstring_value(cwal_engine * e, char const * str, cwal_size_t len); /** A "z-string" is closely related to an "x-string" (see cwal_new_xstring()) in that the caller allocates the string, but (different from x-strings), the caller gives its memory over to a new cwal_string value. This can avoid extra copies in some cases. The caller transfers ownership of str to this function, regardless of success or failure, and should treat it as if it were freed, using the cwal_string APIs to access it further. Note that transfer of str is only legal if str was allocated by the same underlying allocator as the rest of the library (i.e. cwal_free(str) must be legal or Undefined Behaviour may ensue). On success the ownership of str is transfered to the returned cwal_string value and it will be freed (via cwal_free()) when the cwal_string is freed or (possibly) by this function. Thus it is critical that clients treat the str memory as invalid after calling this, and (to repeat) only use the cwal_string APIs to get its string value. To simplify usage, if allocation of the new cwal_string fails, this function _still_ takes over ownership of the given string memory and frees it before returning NULL from this call. (Design note: if we did not do this, error checking would become more complicated and the caller would have to decide to add extra checks or leak.) ACHTUNG: - z-strings do not participate in string interning, but their "empty shells" (all but the client-supplied string bytes) participate in recycling. - str MUST have been allocated using the same allocator as cwal_malloc(e,...) uses or results are undefined. e.g. memory from a cwal_buffer would be safe but memory which can from strdup(), malloc(), or similar "might" not be. - str's contents MUST NOT be modified after calling this. Doing so can lead to very unpredictable behaviour in code using the string (e.g. hashing of keys will break). The underlying laws of physics cwal is based on assume that string bytes are always immutable. - If str is NULL or (!len && !*str) then this behaves identically to cwal_new_string() and cwal_new_xstring() (returning a shared empty string instance). If passed an empty string, it frees it immediately and uses the shared instance in its place. The term "z-string" refers to a coding convention seen in some source tree (not this one) where pointers to strings for which the client owns the memory are named with a "z" prefix, e.g. zMyString. */ cwal_string * cwal_new_zstring(cwal_engine * e, char * str, cwal_size_t len); /** Equivalent to passing the result value of cwal_new_string(e,str,len) to cwal_string_value(). */ cwal_value * cwal_new_zstring_value(cwal_engine * e, char * str, cwal_size_t len); /** Creates a new cwal_string value by concatenating two string values. Returns NULL if either argument is NULL or if allocation of the new string fails. */ cwal_value * cwal_string_concat( cwal_string const * s1, cwal_string const * s2 ); /** An enum holding bitmasks for toggleable cwal_engine features. See cwal_engine_feature_flags(). */ enum cwal_engine_features { /** For internal use. All feature flags must have all their bits in this range. */ CWAL_FEATURE_MASK = 0xFF00, /** Used in cwal_engine::flag to specify that auto-interning should be enabled. Reminder to self: these must currently reside in the high byte. Need to check/consolidate how the internal flags (low byte) are being used. */ CWAL_FEATURE_INTERN_STRINGS = 0x0100, /** Used in cwal_engine::flags to specify that the engine should zero out string memory before freeing it. */ CWAL_FEATURE_ZERO_STRINGS_AT_CLEANUP = 0x0200 }; /** Sets the current set of feature flags and returns the old flags. Pass a negative flags value to have it return the current flags without setting them. flags is interpreted as a bitmask of cwal_engine_features values. If !e or tracing is disabled at built-time, returns -1. Example: @code // Get current flags: uint32_t const flags = cwal_engine_feature_flags(e,-1); // Disable string-interning: cwal_engine_feature_flags(e, flags & ~CWAL_FEATURE_INTERN_STRINGS ); @endcode Calling this might have side effects other than setting the flags. Namely: If CWAL_FEATURE_INTERN_STRINGS is disabled by this call (and was enabled before it) then the memory used for tracking interned strings is released. The strings are left intact and unaffected, but future strings with those same values will be created anew instead of sharing the interned values. Note that interning may be enabled or disabled at any time without any adverse effects vis-a-vis string ownership, reference counting, etc. */ uint32_t cwal_engine_feature_flags( cwal_engine * e, int32_t flags); /** Returns the Value associated with s, or NULL if !s. @see cwal_new_VALUE() */ cwal_value * cwal_string_value(cwal_string const * s); /** Returns the length of str in bytes, or 0 if !str. This is an O(1) operation. */ cwal_size_t cwal_string_length_bytes( cwal_string const * str ); /** Returns the length of the first n bytes of str in UTF8 characters, or 0 if !str. Results are undefined if str is not legal UTF8. This is an O(N) operation. */ cwal_size_t cwal_cstr_length_utf8( char const * str, cwal_size_t n ); /** Functionally equivalent to strlen(3) except that if !str it returns 0 instead of crashing, and it returns cwal_size_t, which very well may not be the same size as size_t. */ cwal_size_t cwal_strlen( char const * str ); /** Equivalent to: cwal_cstr_length_utf8(cwal_string_cstr(str),cwal_string_length_bytes(str)) Returns 0 if !str. */ cwal_size_t cwal_string_length_utf8( cwal_string const * str ); /** Returns the upper-cased form of the given utf8 character, or ch if it doesn't know what to do. The mappings cover all the one-to-one mappings defined by Unicode: http://www.unicode.org/faq/casemap_charprop.html ftp://ftp.unicode.org/Public/3.0-Update/UnicodeData-3.0.0.html ftp://ftp.unicode.org/Public/UCD/latest/ucd/UnicodeData.txt None of the "special cases" are covered. @see cwal_utf8_char_tolower() */ int cwal_utf8_char_toupper( int ch ); /** Returns the lower-cased form of the given utf8 character, or ch if it doesn't know what to do. @see cwal_utf8_char_toupper() */ int cwal_utf8_char_tolower( int ch ); /** Reads a single UTF-8 character from an input string. Return the unicode value. zBegin is the start of the string. zTerm points to the logical EOF (one-after-the-end). It writes a pointer to the next unread byte back into *pzNext. When looping, that value should be the next position passed to this function (see the example below). Notes On Invalid UTF-8: - This routine never allows a 7-bit character (0x00 through 0x7f) to be encoded as a multi-byte character. Any multi-byte character that attempts to encode a value between 0x00 and 0x7f is rendered as 0xfffd. - This routine never allows a UTF16 surrogate value to be encoded. If a multi-byte character attempts to encode a value between 0xd800 and 0xe000 then it is rendered as 0xfffd. - Bytes in the range of 0x80 through 0xbf which occur as the first byte of a character are interpreted as single-byte characters and rendered as themselves even though they are technically invalid characters. - This routine accepts an infinite number of different UTF8 encodings for unicode values 0x80 and greater. It does not change over-length encodings to 0xfffd as some systems recommend. Credits: the implementation and most of the docs were stolen from the public domain sqlite3 source tree. Example usage: @code char unsigned const * pos = inputString; char unsigned const * end = pos + inputStringLength; int ch; while( (pos < end) && (ch=cwal_utf8_read_char(pos, end, &pos)) ){ do something with ch... } @endcode */ int cwal_utf8_read_char( const unsigned char *zBegin, const unsigned char *zTerm, const unsigned char **pzNext); /** Given UTF8 character value c, this calculates its length, in bytes, writes that many bytes to output, and returns that length. If the calculated size is api is NULL, or v holds the special undefined value. */ char cwal_value_is_undef( cwal_value const * v ); /** Returns true if v contains a null value. */ char cwal_value_is_null( cwal_value const * v ); /** Returns true if v contains a bool value. */ char cwal_value_is_bool( cwal_value const * v ); /** Returns true if v contains an integer value. */ char cwal_value_is_integer( cwal_value const * v ); /** Returns true if v contains a double value. */ char cwal_value_is_double( cwal_value const * v ); /** Returns true if v contains a number (double, integer, bool) value. */ char cwal_value_is_number( cwal_value const * v ); /** Returns true if v contains a string value. */ char cwal_value_is_string( cwal_value const * v ); /** Returns true if v contains an array value. */ char cwal_value_is_array( cwal_value const * v ); /** Returns true if v contains an object value. */ char cwal_value_is_object( cwal_value const * v ); /** Returns true if v contains a native value. */ char cwal_value_is_native( cwal_value const * v ); /** Returns true if v contains a buffer value. */ char cwal_value_is_buffer( cwal_value const * v ); /** A special-purpose function which upscopes v into s if v is in a lower scope. If v is in a lower scope then this function has no side-effects. This is necessary when clients create values which they need to survive the current scope. In such cases they should take a handle to their virtual top scope and up-scope any values they need to last that long into there. Returns 0 on success, CWAL_RC_MISUSE if any argument is 0, and is believed to be infalible (hah!) as long as the arguments are legal. This is a no-op if cwal_value_is_builtin(v). */ int cwal_value_rescope( cwal_scope * s, cwal_value * v ); /** Given a pointer, returns true (non-0) if m refers to one of the built-in/constant/shared (cwal_value*) instances, else returns 0. Is tolerant of NULL (returns 0). This determination is O(1) or 2x O(1) (only pointer range comparisons), depending on whether we have to check both sets of builtins. If this returns true, m MUST NOT EVER be cwal_free()d because it refers to stack memory! It MAY be (harmlessly) cwal_value_ref()'d and cwal_value_unref()'d, which are no-ops for built-ins. */ char cwal_value_is_builtin( void const * m ); /** Creates a new value which refers to a client-provided "native" object of an arbitrary type. N is the native object to bind. dtor is the optional finalizer function to call when the new value is finalized. typeID is an arbitrary pointer which is used later to verify that a given cwal_native refers to a native of a specific type. A stack-allocated native pointer is only legal if it can be _guaranteed_ to out-live the wrapping value handle. This function returns NULL if (e, N, typeID) are NULL or on allocation error.On success it returns a new value, initially owned by the current scope. Clients can fetch the native value later using cwal_native_get(). The typeID passed here can be passed to cwal_native_get() to allow cwal to confirm that the caller is getting the type of pointer he wants. In practice the typeID is a pointer to an app/library-wide value of any type. Its contents are irrelevant, only its _address_ is relevant. While it might seem intutive to use a string as the type ID, compilers may (or may not) combine identical string constants into a single string instance, which may or may not foul up such usage. If one needs/wants to use a string, set it in 1 place, e.g. via a file-scope variable, and expose its address to any client code which needs it (as opposed to them each inlining the string, which might or might not work at runtime, depending on whether the strings get compacted into a single instance). When the returned value is finalized (at a time determined by the cwal engine), if dtor is not NULL then dtor(e,N) is called to free the value after any Object-level properties are destructed. If N was allocated using cwal_malloc() or cwal_realloc() and it has no special cleanup requirements then cwal_free can be passed as the dtor value. Finalizer functions "might currently be prohibited" from performing "certain operations" with the cwal API during cleanup, but which ones those are (or should be) are not yet known. Note that it is perfectly legal to use a static value for N, assuming the finalizer function (if any) does not actually try to free() it. In the case of a static, the value could be used as its own typeID (but since the client has that pointer, and it's static, there doesn't seem to be much use for having a cwal_native for that case!). See cwal_new_VALUE() for more details about the return value. @see cwal_new_VALUE() @see cwal_new_native() @see cwal_value_get_native() */ cwal_value * cwal_new_native_value( cwal_engine * e, void * N, cwal_finalizer_f dtor, void const * typeID ); /** Equvalent to passing the return value of cwal_new_native_value() to cwal_value_get_native(). */ cwal_native * cwal_new_native( cwal_engine * e, void * n, cwal_finalizer_f dtor, void const * typeID ); /** A special-purpose component for binding cwal_natives, necessary when native bindings hold Value pointers whose lifetimes are not managed via Object-level properties of the native. This callback is passed the following arguments: s: the scope to potentially be rescoped to. v: the cwal_value part of the Native n: the cwal_native part of the Native. Rescopers must do the following: a) For each "unmanaged" Value, call cwal_value_rescope(s, theValue). d) It must not rescope the Native passed to it, as that value is in the process of rescoping when this is called. Implementations must return 0 on success and any error is tantamount to an assertion. See cwal_native_set_rescoper(). */ typedef int (*cwal_native_rescoper_f)(cwal_scope * s, cwal_value * v, cwal_native * n); /** A special-case function which is necessary when client-side natives manage Values which are not visible to the native's Object parent (i.e. they are not tracked as properties). When creating such natives, after calling cwal_new_native() or cwal_new_native_value(), call this function and pass it your rescoper implementation. This function returns 0 unless nv is NULL, in which case it returns CWAL_RC_MISUSE. */ int cwal_native_set_rescoper( cwal_native * nv, cwal_native_rescoper_f rescoper ); /** Returns the cwal_value form of n, or 0 if !n. */ cwal_value * cwal_native_value( cwal_native const * n ); /** If val is of type CWAL_TYPE_NATIVE then this function assigns *n (if n is not NULL) to its cwal_native handle and returns 0, else it returns CWAL_RC_TYPE and does not modify *n. */ int cwal_value_fetch_native( cwal_value const * val, cwal_native ** n); /** If v is of type CWAL_TYPE_NATIVE then this function returns its native handle, else it returns 0. This is a simplified form of cwal_value_fetch_native(). */ cwal_native * cwal_value_get_native( cwal_value const * v ); /** Fetches the a raw "native" value (void pointer) associated with n. If (0==typeID) or n's type ID is the same as typeID then *dest (if dest is not NULL) is assigned to n's raw native value and 0 is returned, else... CWAL_RC_TYPE: typeID does not match. CWAL_RC_MISUSE: n is NULL. Note that clients SHOULD pass a value for typeID to ensure that they are getting back the type of value they expect. The API recognizes, however, that the type ID might not be available or might be irrelevant to a particular piece of code, and therefore allows (but only grudgingly) typeID to be NULL to signify that the client knows WTF he is doing and is getting a non-type-checked pointer back (via *dest). */ int cwal_native_fetch( cwal_native const * n, void const * typeID, void ** dest); /** Convenience form of cwal_native_fetch() which returns NULL if n's type ID does not match typeID. */ void * cwal_native_get( cwal_native const * n, void const * typeID); /** Clears the underlying native part of n, such that future calls to cwal_native_get() will return NULL. If callFinalizer is true (non-0) then the native's finalizer, if not NULL, is called, otherwise we assume the caller knows more about the lifetime of the value than we do and the finalizer is not called. As a general rule, clients should pass a true value for the second parameter. */ void cwal_native_clear( cwal_native * n, char callFinalizer ); /** Creates a new "buffer" value. startingSize is the amount of memory to reserve in the buffer by default (0 means not to reserve any, of course). If reservation of the buffer fails then this function returns NULL. See cwal_new_VALUE() for details on the ownership. See the cwal_buffer API for how to use buffers. */ cwal_value * cwal_new_buffer_value(cwal_engine *e, cwal_size_t startingSize); /** Equvalent to passing the return value of cwal_new_buffer_value() to cwal_value_get_buffer(). */ cwal_buffer * cwal_new_buffer(cwal_engine *e, cwal_size_t startingSize); /** Equivalent to cwal_value_unref( e, cwal_buffer_value(v) ). */ int cwal_buffer_unref(cwal_engine *e, cwal_buffer *v); /** This convenience routine takes b's buffered memory and transfers it to a new Z-string value (see cwal_new_zstring()). b may either have been created using cwal_new_buffer() or be a "non-value" buffer which the client happens to be using. The new string will have a string byte length of b->used. If !b or !e, or on allocation error, NULL is returned. If b has no memory, the empty string value is returned. The returned value is owned by e and (unless it is the empty string) will initially be owned by e's current scope. If NULL is returned, b's memory is not modified, otherwise after calling this b will be an empty buffer (but its lifetime is otherwise unaffected). For metrics-counting purposes, b's the memory is moved over into the z-string-byte counter. */ cwal_string * cwal_buffer_to_zstring(cwal_engine * e, cwal_buffer * b); /** Equivalent to cwal_string_value(cwal_buffer_to_zstring(e,b)). @see cwal_buffer_to_zstring() */ cwal_value * cwal_buffer_to_zstring_value(cwal_engine * e, cwal_buffer * b); /** Equivalent to cwal_value_fetch_object() and friends, but for buffer values. */ int cwal_value_fetch_buffer( cwal_value const * val, cwal_buffer ** ar); /** If value is-a Buffer then this returns the cwal_buffer form of the value, else it returns 0. */ cwal_buffer * cwal_value_get_buffer( cwal_value const * v ); /** Returns the cwal_value handle associated with the given buffer, or NULL if !s. WARNING OH MY GOD SUCH AN IMPORTANT WARNING: NEVER EVER EVER pass a cwal_buffer which was NOT created via cwal_new_buffer_value() to this function!!! It WILL cause invalid memory access if passed e.g. a cwal_buffer which was allocated on the stack (or by ANY means other than the functions listed above) and might (depending on the state of the random memory we're reading) cause the client to get invalid memory back (as opposed to NULL). */ cwal_value * cwal_buffer_value(cwal_buffer const * s); /** Creates a new "exception" value. See cwal_new_VALUE() for details on the ownership of the return value. code is a client-interpreted error code. (Clients are free to use the cwal_rc values.) msg is an optional (may be NULL) value which stores some form of error message (of an arbitrary value type). Exception values may hold key/value pairs, so they may be "enriched" with client-specific information like a stack trace or source line/column information. On success the returned Exception value will contain the properties "code" and "message", reflecting the values passed here. @see cwal_new_exception(). @see cwal_new_exceptionf() @see cwal_new_exceptionfv() */ cwal_value * cwal_new_exception_value(cwal_engine *e, int code, cwal_value * msg ); /** Equivalent to passing the return value of cwal_new_exception_value() to cwal_value_get_exception(). */ cwal_exception * cwal_new_exception(cwal_engine *e, int code, cwal_value * msg ); /** A printf-like form of cwal_new_exception() which uses cwal_new_stringf() to create a formatted message to pass to cwal_new_exception(). Returns the new Exception value on success, NULL on allocation error or if either e is NULL. A format string of NULL or "" are treated equivalently as NULL. @see cwal_new_exceptionf() @see cwal_new_exception() */ cwal_exception * cwal_new_exceptionfv(cwal_engine * e, int code, char const * fmt, va_list args ); /** Identical to cwal_new_exceptionv() but takes its arguments in ellipsis form. */ cwal_exception * cwal_new_exceptionf(cwal_engine * e, int code, char const * fmt, ... ); /** Returns true if v is-a Exception, else false. */ char cwal_value_is_exception(cwal_value const *v); /** Equivalent to cwal_value_unref( e, cwal_exception_value(v) ). */ int cwal_exception_unref(cwal_engine *e, cwal_exception *v); /** Equivalent to cwal_value_fetch_object() and friends, but for error values. */ int cwal_value_fetch_exception( cwal_value const * val, cwal_exception ** ar); /** If value is-a Exception then this returns the cwal_exception form of the value, else it returns 0. */ cwal_exception * cwal_value_get_exception( cwal_value const * v ); /** Returns the cwal_value handle associated with the given error value, or NULL if !r. */ cwal_value * cwal_exception_value(cwal_exception const * r); /** Returns r's current result code, or some unspecified non-0 value if !r. */ int cwal_exception_code_get( cwal_exception const * r ); /** Sets r's result code. Returns 0 on success, CWAL_RC_MISUSE if !r. */ int cwal_exception_code_set( cwal_exception * r, int code ); /** Returns the "message" part of the given error value, NULL if !r or r has no message part. The returned value is owned by/shared with r via reference counting, and it must not be unref'd by the client unless he explicitly references himself. */ cwal_value * cwal_exception_message_get( cwal_exception const * r ); /** Sets the given msg value to be r's "message" component. Interpretation of the message is up to the client. Returns 0 on success, CWAL_RC_MISUSE if either e or r are NULL. msg may be NULL. This function adds a reference to msg and removes a reference from its previous message (if any). */ int cwal_exception_message_set( cwal_engine * e, cwal_exception * r, cwal_value * msg ); /** Creates a new value wrapping a function. e is the owning engine, callback is the native function to wrap (it may not be NULL). state is optional state for the callback and may (assuming client application conditions allow for it) be NULL. The stateTypeID parameter is not directly used by the framework but can be used when the callback is called (via cwal_function_call() and friends) to determine whether the state parameter passed into the function is of a type expected by the client (which avoids mis-casting pointers when script code criss-crosses methods between object instances and classes). See cwal_args_callee_state() for more details. See cwal_new_VALUE() for details regarding ownership and lifetime of the returned value. Returns NULL if preconditions are not met (e and callback may not be NULL) or on allocation error. When the callback is called via cwal_function_call() and friends, state->data will be available via the cwal_callback_args instance passed to the callback. When the returned value is destroyed, if stateDtor is not NULL then stateDtor(state) is called at destruction time to clean up the state value. */ cwal_value * cwal_new_function_value( cwal_engine * e, cwal_callback_f callback, void * state, cwal_finalizer_f stateDtor, void const * stateTypeID ); /** Equvalent to passing the return value of cwal_new_function_value() to cwal_value_get_function(). */ cwal_function * cwal_new_function( cwal_engine * e, cwal_callback_f, void * state, cwal_finalizer_f stateDtor, void const * stateTypeID ); /** Returns true if v is-a Function, else false. */ char cwal_value_is_function(cwal_value const *v); /** If v is-a Function then this returns that Function handle, else it returns 0. */ cwal_function * cwal_value_get_function(cwal_value const *v); /** Returns the Value handle part of f, or 0 if !f. */ cwal_value * cwal_function_value(cwal_function const *f); /** Equivalent to cwal_value_unref(cwal_function_value(f)). */ int cwal_function_unref(cwal_function *f); /** Calls the given function, passing the the given arguments and other state via its single cwal_callback_args parameter. The given scope is used as the execution context for purposes of ownership of new values. self may technically be 0, but f may require it to be of a specific type. Its intention is to be interpreted as the "this" object/value for the call (the semantics of which are client-dependent). argv must point at at least argc values. Both argv and argc may be 0. Returns the result from f, or CWAL_RC_OOM if allocating a new scope fails. Callback implementors should keep in mind that returning a value other than 0 (CWAL_RC_OK) will "usually" (but not always) be interpreted as an error condition (exceptions include using the return value to implement continue/break/return/exception semantics). The exact details depend on the client's use of cwal. If resultVal is not NULL then on success *resultVal holds the value-level result from the function call. It is strictly illegal to pop the current scope from within (or via) the f->callback(). Subscopes may of course be pushed (and must be popped before returning to this function, or an assertion may be triggered!). If s is not the interpreter's current scope, this function artificially changes the current scope, which comes with a _potential_ caveat: during the life of the f->callback() call, up until the next scope is pushed (if that happens), s is the current scope for all intents and purposes. But that's the point of this routine. If callback hooks have been installed via cwal_callback_hook_set() then they are triggered in this function as described in the cwal_callback_hook documentation. The "pre" callback is only triggered after it is certain that f will be called (i.e., after basic argument validation). If the pre-callback returns non-0 then neither f nor the post-callback are triggered. If the pre-callback returns 0 then both f and the post-callback are guaranteed to be called. f will be made vaccuum- and sweep-proof during the life of the call, if it was not already so. */ int cwal_function_call_in_scope( cwal_scope * s, cwal_function * f, cwal_value * self, cwal_value ** resultVal, uint16_t argc, cwal_value * const * argv ); /** Convenience form of cwal_function_call_in_scope() which pushes a new scope onto the stack before calling that function. Note that the value-level result of the function call might be owned by the pushed scope or a subscope, and therefore be cleaned up when the function call returns. If resultValue is not NULL then the result value of the call() is moved into the scope which was active before the call, such that it is guaranteed to survive when the scope created for the call() is closed. If resultValue is null, scope ownership of the call()'s result is not modified, and it "may" be cleaned up as soon as the scope expires. */ int cwal_function_call( cwal_function * f, cwal_value * self, cwal_value ** resultVal, uint16_t argc, cwal_value * const * argv ); /** A form of cwal_function_call() which takes its arguments in the form of a cwal_array (which may be NULL). If s is NULL then this acts as a proxy for cwal_function_call(), otherwise it behaves like cwal_function_call_in_scope(), using s as the call scope. Returns 0 on success, non-0 on error. Results are undefined if args is NULL. ACHTUNG: any empty entries in the array will be passed to the callback as literal NULLs, and experience has shown that most callbacks do not generally expect any literal NULLs (because script code cannot generate them). So... be careful with that. */ int cwal_function_call_array( cwal_scope * s, cwal_function * f, cwal_value * self, cwal_value ** rv, cwal_array * args); /** Works like cwal_function_call() but has very specific requirements on the variadic arguments: the list must contain 0 or more (cwal_value*) arguments and MUST ALWAYS be terminated by a 0/NULL value (NOT a cwal_value, e.g. cwal_value_null(), but a literal 0). This function places some "reasonable upper limit" on the number of arguments to avoid having to allocate non-stack space for them (CWAL_OPT_MAX_FUNC_CALL_ARGS). It returns CWAL_RC_RANGE if that limit is exceeded. TODO? A variant of this which allows one to pass a string describing the list of arguments, e.g. %v for (cwal_value*), %s for c-string, %d for cwal_int_t, %f for cwal_double_t, %b for bool, etc. */ int cwal_function_callv( cwal_function * f, cwal_value * self, cwal_value ** resultVal, va_list args ); /** Equivalent to cwal_function_callv() but takes its arguments in ellipsis form. BE SURE to read the docs for that function regarding the arguments! */ int cwal_function_callf( cwal_function * f, cwal_value * self, cwal_value ** resultValue, ... ); /** The cwal_function_call_in_scope() counterpart of cwal_function_callv(). See those functions for more details. */ int cwal_function_call_in_scopef( cwal_scope * s, cwal_function * f, cwal_value * self, cwal_value ** resultValue, ... ); /** If args is not NULL and args->stateTypeID==stateTypeID then returns args->state, else returns NULL. If args is not NULL, it returns the same as cwal_function_state_get(args->callee, stateTypeID). */ void * cwal_args_state( cwal_callback_args const * args, void const * stateTypeID ); /** If f is not NULL and f was created with the same stateTypeID as provided in the 2nd argument, then f's native state is returned, else NULL is returned. Example usage: @code // From within a cwal_callback_f implementation... MyType * my = (MyType *)cwal_function_state_get(args->callee, MyTypeID); if(!my) { ...args->callee was not created with MyTypeID... } @endcode @see cwal_args_callee_state() */ void * cwal_function_state_get( cwal_function * f, void const * stateTypeID ); /** @deprecated This function is an accidental duplicate of cwal_args_state()'s functionality - use that one, as it's got a shorter name (less to type). */ void * cwal_args_callee_state( cwal_callback_args const * args, void const * stateTypeID ); /** Creates a new hash table object. These tables can store arbitrary cwal_value keys and values and have amortized O(1) search, insertion, and removal performance. hashSize is the number of elements in the hash (it cannot be changed after creation). The number is a hint and may be modified before use (e.g. rounded to a nearby prime number). Returns the new hash table on success, NULL on error. It is an error if hashSize is 0. */ cwal_hash * cwal_new_hash( cwal_engine * e, cwal_size_t hashSize ); /** Equivalent to: cwal_hash_value(cwal_new_hash(e,hashSize)) */ cwal_value * cwal_new_hash_value( cwal_engine * e, cwal_size_t hashSize); /** Searches the given hashtable for a key, returning it if found, NULL if not found. @see cwal_hash_search() */ cwal_value * cwal_hash_search_v( cwal_hash * h, cwal_value * key ); /** Like cwal_hash_search_v() but will only ever match true string keys, not non-string keys which might otherwise compare (via cwal_value_compare()) to equivalent. Returns NULL if !h or !key. If keyLen is 0 and *key is not then the equivalent of strlen(key) is used to find its length. @see cwal_hash_search_v() */ cwal_value * cwal_hash_search( cwal_hash * h, char const * key, cwal_size_t keyLen ); /** Returns true (non-0) if v is of the concrete type cwal_hash. */ char cwal_value_is_hash( cwal_value const * v ); /** If cwal_value_is_hash(v) then this returns the value's cwal_hash representation, else it returns NULL. */ cwal_hash * cwal_value_get_hash( cwal_value * v ); /** Returns the cwal_value part of a cwal_hash value, or NULL if !h. */ cwal_value * cwal_hash_value( cwal_hash * h ); /** Removes all entries from the hashtable. If freeProps is true then non-hash properties (those belonging to the object base type) are also cleared. After calling this, cwal_hash_entry_count() will be 0 until new entries are added. */ void cwal_hash_clear( cwal_hash * ar, char freeProps ); /** Inserts a value into the given hash. Returns 0 on success. If the given key already exists then insertion fails and CWAL_RC_ALREADY_EXISTS is returned unless allowOverwrite is true (in which case the entry is replaced). On error the key and value acquire no new references. This function returns CWAL_RC_ACCESS if called while h is being iterated over (e.g. via cwal_hash_visit_kvp() and friends), as modifying the hash during iteration could potentially lead the memory corruption as the iteration. */ int cwal_hash_insert_v( cwal_hash * h, cwal_value * key, cwal_value * v, char allowOverwrite ); /** Like cwal_hash_insert_v() but takes its key in the form of the first keyLen bytes of the given key. If keyLen is 0 and *key is not then the equivalent of strlen(key) is used to find its length. This routine allocates a new String value for the key (just in case there was any doubt about that). */ int cwal_hash_insert( cwal_hash * h, char const * key, cwal_size_t keyLen, cwal_value * v, char allowOverwrite ); /** Removes the given key from the given hashtable, potentially freeing the value (and possibly even the passed-in key, depening on ownership conditions). Returns 0 on success, CWAL_RC_MISUSE if either argument is NULL, or CWAL_RC_NOT_FOUND if the entry is not found. This function returns CWAL_RC_ACCESS if called while h is being iterated over (e.g. via cwal_hash_visit_kvp() and friends), as modifying the hash during iteration could potentially lead the memory-related problems. */ int cwal_hash_remove( cwal_hash * h, cwal_value * key ); /** Returns the number of entries in the given hash, or 0 if !h. This is an O(1) operation. */ cwal_size_t cwal_hash_entry_count(cwal_hash *h); /** Returns the table size of h, or 0 if !h. */ cwal_size_t cwal_hash_size( cwal_hash * h ); /** Similar to cwal_props_visit_kvp() except that it operates on the hash table entries of h. See cwal_props_visit_kvp() for the semantics of the visitor and its return value. */ int cwal_hash_visit_kvp( cwal_hash * h, cwal_kvp_visitor_f f, void * state ); /** Equivalent to cwal_props_visit_keys() except that it operates on the hash table entries of h, passing each key in the hashtable to f (in an indeterminate order). */ int cwal_hash_visit_keys( cwal_hash * h, cwal_value_visitor_f f, void * state ); /** Equivalent to cwal_props_visit_keys() except that it operates on the hash table entries of h, passing each value in the table to f (in an indeterminate order). */ int cwal_hash_visit_values( cwal_hash * h, cwal_value_visitor_f f, void * state ); /** Converts v to a string representation and copies it to dest. dest must be valid memory at least *nDest bytes long. On success (*nDest is long enough to hold the number) then *nDest is set to the size of the string and dest is updated with its contents. Returns CWAL_RC_OK on success, else: CWAL_RC_MISUSE: dest or nDest are NULL. CWAL_RC_RANGE: *nDest is not enough to hold the resulting string. dest is not modified in this case, but *nDest is updated to contain the size which would be needed to write the full value. For normal use cases, a memory length of 30 or less is more than sufficient. */ int cwal_int_to_cstr( cwal_int_t v, char * dest, cwal_size_t * nDest ); /** Functionally identical to cwal_int_to_cstr() but works on a double value. For normal use cases, a memory length of 128 or less is more than sufficient. The largest result i've ever witnessed was about 80 bytes long. */ int cwal_double_to_cstr( cwal_double_t v, char * dest, cwal_size_t * nDest ); /** Tries to interpret slen bytes of cstr as an integer value, optionally prefixed by a '+' or '-' character. On success 0 is returned and *dest (if dest is not NULL) will contain the parsed value. On error one of the following is returned: - CWAL_RC_MISUSE if !slen, !cstr, or !*cstr. - CWAL_RC_TYPE if cstr contains any non-numeric characters. - CWAL_RC_RANGE if the numeric string is too large for cwal_int_t. Potential TODOs: hex with leading 0x or 0X, and octal with leading 0o. */ int cwal_cstr_to_int( char const * cstr, cwal_size_t slen, cwal_int_t * dest ); /** Equivalent to cwal_cstr_to_int() but takes a cwal_string value. Returns CWAL_RC_MISUSE if !s, else returns as for cwal_cstr_to_int(). */ int cwal_string_to_int( cwal_string const * s, cwal_int_t * dest ); /** Behaves as for cwal_cstr_to_int(), but parses an integer or literal double (in decimal form) with an optional leading sign. */ int cwal_cstr_to_double( char const * cstr, cwal_size_t slen, cwal_double_t * dest ); /** The cwal_string counterpart of cwal_cstr_to_double(). */ int cwal_string_to_double( cwal_string const * s, cwal_double_t * dest ); /** Compares the two given strings using memcmp() semantics with these exceptions: if either of len1 or len2 are 0 then the longer of the two strings compares de facto (without a string comparison) to greater than the other. If both are 0 they are compared as equal. len1 and len2 MUST point to their respective number of bytes of live memory. If they are 0 their corresponding string is not touched. i.e. s1 may be NULL only if len1 is 0, and likewise for (s2,len2). */ int cwal_compare_cstr( char const * s1, cwal_size_t len1, char const * s2, cwal_size_t len2 ); /** A cwal_compare_cstr() proxy which compares the given cwal_string to the given c-style string. */ int cwal_compare_str_cstr( cwal_string const * s1, char const * s2, cwal_size_t len2 ); /** Configures e to recycle, at most, n elements for the given type. If the recycle list already contains more than that then any extra elements in it are freed by this call. Set it to 0 to disable recycling for the given type. typeID must be one of: CWAL_TYPE_INTEGER, CWAL_TYPE_DOUBLE (see notes below!), CWAL_TYPE_OBJECT, CWAL_TYPE_ARRAY, CWAL_TYPE_NATIVE, CWAL_TYPE_BUFFER, CWAL_TYPE_KVP, CWAL_TYPE_WEAK_REF, CWAL_TYPE_STRING (but see below regarding strings). Or, as a special case, CWAL_TYPE_UNDEF means to apply this change to all of the above-listed types. Also note that any built-in constant values are never allocated, and so are not recycled via this mechanism. Returns 0 on succes, CWAL_RC_MISUSE if !e, and CWAL_RC_TYPE if typeID is not refer to one of the recyclable types. Notes: On platforms where sizeof(cwal_double_t)==sizeof(cwal_int_t), the CWAL_TYPE_INTEGER and CWAL_TYPE_DOUBLE recycling bins are pooled together. For this reason, clients "should," when setting up their pool sizes, set CWAL_TYPE_DOUBLE first, and then CWAL_TYPE_INT. On affected platforms, the effect of this ordering will be to use the latter value (which, in practice, is typically higher for integers, as those get used more often). CWAL_TYPE_KVP is an internal type with no cwal_value representation. Each key/value pair in an Object requires one instance of cwal_kvp, and clients can control that recycling level here. CWAL_TYPE_WEAK_REF is an internal type with no cwal_value representation. We do, however, recycle them, if they are configured for it. CWAL_TYPE_XSTRING and CWAL_TYPE_ZSTRING are equivalent here, as those types use the same recycling bin. CWAL_TYPE_STRING recycling is comparatively limited because a string's size plays a factor in its reusability. When choosing strings from the recycling pool, only strings with the same approximate length will be considered. This means it is possible, depending on usage, to fill up the recycle pool with strings of sizes we won't ever recycle. Internally, the library pads new string sizes up to some common boundary because doing so saves memory (somewhat ironically) by improving recylability of strings from exact-fit-only to a close-fit. */ int cwal_engine_recycle_max( cwal_engine * e, cwal_type_id type, cwal_size_t n ); /** For the given cwal value type ID, this function returns the maximum number of values of that type which e is configured to keep in its recycle bin. Returns 0 if !e or recycling is disabled or not allowed for the given type. Example: @code cwal_size_t const x = cwal_engine_recyle_max_get(e, CWAL_TYPE_OBJECT); @endcode */ cwal_size_t cwal_engine_recycle_max_get( cwal_engine * e, cwal_type_id type ); /** Runs the type-specific equivalence comparison operation for lhs and rhs, using memcmp() semantics: returns 0 if lhs and rhs are equivalent, less than 0 if lhs is "less than" rhs, and greater than 0 if lhs is "greater than" rhs. Note that many types do not have any sort of sensible orderings. This API attempts to do something close to ECMAScript, but it does not exactly match that. Note that this function does not guaranty return values of exactly -1, 0, or 1, but may return any (perhaps varying) negative resp. positive values. TODO: find the appropriate place to document the cross-type comparisons and weird cases like undefined/null. Notes: - CWAL_TYPE_NULL and CWAL_TYPE_UNDEF compare equivalently to any falsy value. (This was not true before 20140614, but no known current code was broken by that change.) */ int cwal_value_compare( cwal_value const * lhs, cwal_value const * rhs ); #if 0 /* th1 has something like this... */ int cwal_engine_call_scoped( cwal_engine * e, int (*callback)(cwal_engine *e, void * state1, void * state2) ); #endif /** A generic interface for callback functions which act as a streaming input source for... well, for whatever. The arguments are: - state: implementation-specific state needed by the function. - n: when called, *n will be the number of bytes the function should read and copy to dest. The function MUST NOT copy more than *n bytes to dest. Before returning, *n must be set to the number of bytes actually copied to dest. If that number is smaller than the original *n value, the input is assumed to be completed (thus this is not useful with non-blocking readers). - dest: the destination memory to copy the data to. Must return 0 on success, non-0 on error (preferably a value from cwal_rc). There may be specific limitations imposed upon implementations or extra effort required by clients. e.g. a text input parser may need to take care to accommodate that this routine might fetch a partial character from a UTF multi-byte character. */ typedef int (*cwal_input_f)( void * state, void * dest, cwal_size_t * n ); /** A cwal_input_f() implementation which requires the state argument to be a readable (FILE*) handle. */ int cwal_input_FILE( void * state, void * dest, cwal_size_t * n ); /** An empty-initialized cwal_buffer object. ALWAYS initialize embedded-in-struct cwal_buffers by copying this object! */ #define cwal_buffer_empty_m {0/*capacity*/,0/*used*/,NULL/*mem*/} /** An empty-initialized cwal_buffer object. ALWAYS initialize stack-allocated cwal_buffers by copying this object! */ extern const cwal_buffer cwal_buffer_empty; /** Reserves the given amount of memory for the given buffer object. If n is 0 then buf->mem is freed and its state is set to NULL/0 values. If buf->capacity is less than or equal to n then 0 is returned and buf is not modified. If n is larger than buf->capacity then buf->mem is (re)allocated and buf->capacity contains the new length. Newly-allocated bytes are filled with zeroes. On success 0 is returned. On error non-0 is returned and buf is not modified. buf->mem is owned by buf and must eventually be freed by passing an n value of 0 to this function. buf->used is never modified by this function unless n is 0, in which case it is reset. Example: @code cwal_buffer buf = cwal_buffer_empty; // VERY IMPORTANT: copy initialization! int rc = cwal_buffer_reserve( e, &buf, 1234 ); ... cwal_buffer_reserve( e, &buf, 0 ); // frees the memory @endcode */ int cwal_buffer_reserve( cwal_engine * e, cwal_buffer * buf, cwal_size_t n ); /** Fills all bytes of the given buffer with the given character. Returns the number of bytes set (buf->capacity), or 0 if !buf or buf has no memory allocated to it. */ cwal_size_t cwal_buffer_fill( cwal_buffer * buf, unsigned char c ); /** Uses a cwal_input_f() function to buffer input into a cwal_buffer. dest must be a non-NULL, initialized (though possibly empty) cwal_buffer object. Its contents, if any, will be overwritten by this function, and any memory it holds might be re-used. The src function is called, and passed the state parameter, to fetch the input. If it returns non-0, this function returns that error code. src() is called, possibly repeatedly, until it reports that there is no more data. Whether or not this function succeeds, dest still owns any memory pointed to by dest->mem, and the client must eventually free it by calling cwal_buffer_reserve(dest,0). dest->mem might (and possibly will) be (re)allocated by this function, so any pointers to it held from before this call might be invalidated by this call. On error non-0 is returned and dest has almost certainly been modified but its state must be considered incomplete. Errors include: - dest or src are NULL (CWAL_RC_MISUSE) - Allocation error (CWAL_RC_OOM) - src() returns an error code Whether or not the state parameter may be NULL depends on the src implementation requirements. On success dest will contain the contents read from the input source. dest->used will be the length of the read-in data, and dest->mem will point to the memory. dest->mem is automatically NUL-terminated if this function succeeds, but dest->used does not count that terminator. On error the state of dest->mem must be considered incomplete, and is not guaranteed to be NUL-terminated. Example usage: @code cwal_buffer buf = cwal_buffer_empty; int rc = cwal_buffer_fill_from( engine, &buf, cwal_input_FILE, stdin ); if( rc ){ fprintf(stderr,"Error %d (%s) while filling buffer.\n", rc, cwal_rc_cstr(rc)); cwal_buffer_reserve( &buf, 0 ); return ...; } ... use the buf->mem ... ... clean up the buffer ... cwal_buffer_reserve( &buf, 0 ); @endcode To take over ownership of the buffer's memory, do: @code void * mem = buf.mem; buf = cwal_buffer_empty; @endcode In which case the memory must eventually be passed to free() to free it. */ int cwal_buffer_fill_from( cwal_engine * e, cwal_buffer * dest, cwal_input_f src, void * state ); /** A cwal_buffer_fill_from() proxy which overwrite's dest->mem with the contents of the given FILE handler (which must be opened for read access). Returns 0 on success, after which dest->mem contains dest->used bytes of content from the input source. On error dest may be partially filled. */ int cwal_buffer_fill_from_FILE( cwal_engine * e, cwal_buffer * dest, FILE * src ); /** Wrapper for cwal_buffer_fill_from_FILE() which gets its input from the given file name. As a special case it interprets the name "-" as stdin. */ int cwal_buffer_fill_from_filename( cwal_engine * e, cwal_buffer * dest, char const * filename ); /** Works just like cwal_buffer_fill_from_filename() except that it takes a required length for the filename. This routine uses an internal buffer to copy (on the stack) the given name and NUL-terminate it at the nameLen'th byte. This is intended to help protect against potentially non-NUL-terminated input strings, e.g. from X- or Z-strings. Returns 0 on success, CWAL_RC_MISUSE if any pointer argument is 0, and CWAL_RC_RANGE if nameLen is larger than the internal name buffer (of "some reasonable size"). */ int cwal_buffer_fill_from_filename2( cwal_engine * e, cwal_buffer * dest, char const * filename, cwal_size_t nameLen); /** Sets the "used" size of b to 0 and NULs the first byte of b->mem if b->capacity is greater than 0. DOES NOT deallocate any memory. Returns 0 on success and the only error case is if !b (CWAL_RC_MISUSE). @see cwal_buffer_reserve() */ int cwal_buffer_reset( cwal_buffer * b ); /** Similar to cwal_buffer_reserve() except that... - It does not free all memory when n==0. Instead it essentially makes the memory a length-0, NUL-terminated string. - It will try to shrink (realloc) buf's memory if (ncapacity). - It sets buf->capacity to (n+1) and buf->used to n. This routine allocates one extra byte to ensure that buf is always NUL-terminated. - On success it always NUL-terminates the buffer at offset buf->used. Returns 0 on success, CWAL_RC_MISUSE if !buf, CWAL_RC_OOM if (re)allocation fails. @see cwal_buffer_reserve() @see cwal_buffer_clear() */ int cwal_buffer_resize( cwal_engine * e, cwal_buffer * buf, cwal_size_t n ); /** Convenience equivalent to cwal_buffer_reserve(e, b, 0). */ int cwal_buffer_clear( cwal_engine * e, cwal_buffer * b ); /** Appends the first n bytes of data to b->mem at position b->used, expanding b if necessary. Returns 0 on success. If !data then CWAL_RC_MISUSE is returned. This function NUL-terminates b on success. */ int cwal_buffer_append( cwal_engine * e, cwal_buffer * b, void const * data, cwal_size_t n ); /** Appends printf-style formatted bytes to b using cwal_printf(). Returns 0 on success. Always NUL-terminates the buffer on success, but that NUL byte does not count against b->used's length. */ int cwal_buffer_printf( cwal_engine * e, cwal_buffer * b, char const * fmt, ... ); /** Equivalent to cwal_buffer_printf() but takes a va_list instead of ellipsis. */ int cwal_buffer_printfv( cwal_engine * e, cwal_buffer * b, char const * fmt, va_list ); /** A string formatting function similar to Java's java.lang.String.format(), with similar formatting rules. It uses a formatting string to describe how to convert its arguments to a formatted string, and appends the output to a cwal_buffer instance. Overview of formatting rules: A format specifier has this syntax: %N$[flags][[-]width][.precision][type] "%%" is interpreted as a single "%" character, not a format specifier. N = the 1-based argument (argv) index. It is 1-based because that is how java.lang.String.format() does it. The argv value at that index is expected to be of the type(s) specified by the format specifier, or convertible to that type. How the width and precision are handled varies by type. TODO: document the various behaviours and ensure semantic compatibility (or close) with java.lang.String.format(). [type] must one of the following: - b: treat the argument as a boolean, evaluate to "true" or "false". Width and precision are ignored. (TODO: treat width/precision as padding/truncation, as for strings.) - B: "blobifies" the argument (which must be a Buffer or String), encoding it as a series of hex values, two hex characters per byte of length. The precision specifies the maximum number of byte pairs to output (so the formatted length will be twice the precision). - d, o, x, X: means interpret the result as an integer in decimal, octal, hex (lower-case), or hex (upper-case), respectively. If a width is specified and starts with a '0' then '0' (instead of ' ') is used for left-side padding if the number is shorter than the specified width. Precision is ignored(?). - f: double value. Width and precision work like cwal_outputf() and friends. - J: runs the value through cwal_json_output() to convert it to a JSON string. The width can be used to specify indentation. Positive values indent by that many spaces per level, negative values indicate that many hard tabs per level. The precision is ignored. - N, U: interpret the value as "null" or "undefined", respectively. Width and precision are ignored. - p: evaluates to a string in the form TYPE_NAME@ADDRESS, using the hex notation form of the value's address. Width and precision are ignored. - q: expects a string or NULL value. Replaces single-quote characters with two single-quote characters and interpets NULL values as "(NULL)" (without the quotes). - Q: like 'q' but surrounds string ouput with single quotes and interprets NULL values as "NULL" (without the quotes). - s: string or buffer value. The precision determines the maximum length. The width determines the minimum length. If the string is shorter (in bytes!) than the absolute value of the width then a positive width will left-pad the string with spaces and a negative width will left-pad the string with spaces. FIXME: USE UTF8 CHARS for precision and width! - y: evaluates to cwal_value_type_name(argv[theIndex]). Width and precision are ignored. The flags field may currently only be a '+', which forces numeric conversions to include a sign character. This sign character does not count against the width/precision. Anything which is not a format specifier is appended as-is to tgt. Note that tgt is appended to by this function, so when re-using a buffer one may either need to set tgt->used=0 before calling this or the caller should copy tgt->used before calling this function and treating (tgt->mem + preCallUsed) as the start of the output and (tgt->used - preCallUsed) as its length. Note that this function might reallocate tgt->mem, so any pointers to it may be invalidated. Returns 0 on success. On error it returns non-0 and may replace the contents of tgt->mem with an error string. It will do this for all cases exception invalid arguments being passed to this function (CWAL_RC_MISUSE) or an allocation error (CWAL_RC_OOM). For all others, on error it writes an error message (NUL-terminated) to (tgt->mem + (tgt->used when this function was called)). TODO: refactor this to take a cwal_output_f() instead of a buffer then reimplement this function on top of that one. */ int cwal_buffer_format( cwal_engine * e, cwal_buffer * tgt, char const * fmt, cwal_size_t fmtLen, uint16_t argc, cwal_value * const * const argv); /** Client-configurable options for the cwal_json_output() family of functions. */ struct cwal_json_output_opt{ /** Specifies how to indent (or not) output. The values are: (0) == no extra indentation. (-N) == -N TAB character for each level. (N) == N SPACES for each level. TODO: replace or supplement this with a ((char const *), length) pair. */ int indent; /** Maximum object/array depth to traverse. Traversing deeply can be indicative of cycles in the containers, and this value is used to figure out when to abort the traversal. */ unsigned short maxDepth; /** If true, a newline will be added to the end of the generated output, else not. */ char addNewline; /** If true, a space will be added after the colon operator in objects' key/value pairs. */ char addSpaceAfterColon; /** If true, a space will be appended after commas in array/object lists, else no space will be appended. */ char addSpaceAfterComma; /** If set to 1 then objects/arrays containing only a single value will not indent an extra level for that value (but will indent on subsequent levels if that value contains multiple values). */ char indentSingleMemberValues; /** The JSON format allows, but does not require, JSON generators to backslash-escape forward slashes. This option enables/disables that feature. According to JSON's inventor, Douglas Crockford: It is allowed, not required. It is allowed so that JSON can be safely embedded in HTML, which can freak out when seeing strings containing " (from an email on 2011-04-08) The default value is 0 (because it's just damned ugly). */ char escapeForwardSlashes; /** If true, cyclic structures will not cause an error, but will instead be replaced by a symbolic (but useless) placeholder string indicating which value cycled. Useful primarily for debugging, and not for generating useful JSON output. */ char cyclesAsStrings; /** If true, Function values will be output as objects, otherwise they will trigger a CWAL_RC_TYPE error. */ char functionsAsObjects; }; typedef struct cwal_json_output_opt cwal_json_output_opt; /** Empty-initialized cwal_json_output_opt object. */ #define cwal_json_output_opt_empty_m { 0/*indent*/, \ 15/*maxDepth*/, \ 0/*addNewline*/, \ 1/*addSpaceAfterColon*/, \ 1/*addSpaceAfterComma*/, \ 0/*indentSingleMemberValues*/, \ 0/*escapeForwardSlashes*/, \ 1/*cyclesAsStrings*/, \ 1/*functionsAsObjects*/\ } /** Empty-initialized cwal_json_output_opt object. */ extern const cwal_json_output_opt cwal_json_output_opt_empty; /** Outputs the given NON-GRAPH value in JSON format (insofar as possible) via the given output function. The state argument is passed as the first argument to f(). If f() returns non-0, output stops and returns that value to the caller. Note that f() will be called very often, so it should be relatively efficient. If fmt is NULL some default is used. This function is intended for emitting Objects and Arrays, but it can also do the immutable types (just don't try to hand them off to a downstream client as a valid JSON object). Note that conversion to JSON is fundamentally a const operation, and the value is not visibly modified, but in order for this code to catch cycles it must mark containers it visits. (It unmarks each one as it finishes traversing it.) Returns 0 on success. Returns CWAL_RC_CYCLES_DETECTED if cycles are detected while traversing src. Returns CWAL_RC_RANGE if the maximum output depth level (as specified in the fmt argument or its default) is exceeded. ACHTUNG: this implementation assumes that all cwal_string values are UTF8 and may fail in mysterious ways with other encodings. */ int cwal_json_output( cwal_value * src, cwal_output_f f, void * state, cwal_json_output_opt const * fmt ); /** A wrapper around cwal_json_output() which sends the output via cwal_output(). */ int cwal_json_output_engine( cwal_engine * e, cwal_value * src, cwal_json_output_opt const * fmt ); /** Wrapper around cwal_json_output() which sends its output to the given file handle, which must be opened in write/append mode. If fmt is NULL some default is used. Minor achtung: if fmt is NULL, this function uses a different default than cwal_json_output() does, and it forces the addNewline option to be set. If you don't want that, pass in a non-NULL fmt object. */ int cwal_json_output_FILE( cwal_value * src, FILE * dest, cwal_json_output_opt const * fmt ); /** Convenience wrapper around cwal_json_output_FILE(). This function does NOT create directories in the given filename/path, and will fail if given a name which refers to a non-existing directory. The file name "-" is interpreted as stdout. */ int cwal_json_output_filename( cwal_value * src, char const * dest, cwal_json_output_opt const * fmt ); /** Wrapper around cwal_json_output() which sends its output to the given buffer, which must be opened in write/append mode. If fmt is NULL some default is used. */ int cwal_json_output_buffer( cwal_engine * e, cwal_value * src, cwal_buffer * dest, cwal_json_output_opt const * fmt ); /** A class for holding JSON parser information. It is primarily intended for finding the nature and position of a parse error. */ struct cwal_json_parse_info { /** 1-based line number. */ cwal_size_t line; /** 0-based column number. */ cwal_size_t col; /** Length, in bytes, parsed. On error this will be "very close to" the error position. */ cwal_size_t length; /** Error code of the parse run (0 for no error). */ int errorCode; }; typedef struct cwal_json_parse_info cwal_json_parse_info; /** Empty-initialized cwal_json_parse_info object. */ #define cwal_json_parse_info_empty_m {\ 1/*line*/, \ 0/*col*/, \ 0/*length*/, \ 0/*errorCode*/ \ } /** Empty-initialized cwal_json_parse_info object. Should be copied by clients when they initialize an instance of this type. */ extern const cwal_json_parse_info cwal_json_parse_info_empty; /** Parses input from src as a top-level JSON Object/Array value. The state parameter has no meaning for this function but is passed on to src(), so state must be compatible with the given src implementation. The pInfo parameter may be NULL. If it is not then its state is updated with parsing information, namely the error location. It is modified on success and for any parser-level error, but its contents on success are not likely to be useful. Likewise, its contents are not useful for errors triggered due to invalid arguments or during initial setup of the parser. The caller should initialize pInfo by copying cwal_json_parse_info_empty over it. After this returns, if pInfo->errorCode is not 0, then the failure was either during parsing or an allocation failed during parsing. On success, 0 is returned and *tgt is assigned to the root object/array of the tree (it is initially owned by the currently active scope). On success *tgt is guaranteed to be either of type Object or Array (i.e. _one_ of cwal_value_get_object() or cwal_value_get_array() will return non-NULL). On error non-0 is returned and *tgt is not modified. pInfo will, if not NULL, contain the location of the parse error (if any). ACHTUNG: if the build-time configuration option CWAL_ENABLE_JSON_PARSER is set to 0 then the whole family of cwal_json_parse() functions returns CWAL_RC_UNSUPPORTED when called, but they will do so after doing any normal argument validation, so those codes are still valid in such builds. */ int cwal_json_parse( cwal_engine * e, cwal_input_f src, void * state, cwal_value ** tgt, cwal_json_parse_info * pInfo ); /** Convenience form of cwal_json_parse() which reads its contents from the given opened/readable file handle. */ int cwal_json_parse_FILE( cwal_engine * e, FILE * src, cwal_value ** tgt, cwal_json_parse_info * pInfo ); /** Convenience form of cwal_json_parse() which reads its contents from the given file name. */ int cwal_json_parse_filename( cwal_engine * e, char const * src, cwal_value ** tgt, cwal_json_parse_info * pInfo ); /** Convenience form of cwal_json_parse() which reads its contents from (at most) the first len bytes of the given string. */ int cwal_json_parse_cstr( cwal_engine * e, char const * src, cwal_size_t len, cwal_value ** tgt, cwal_json_parse_info * pInfo ); /** Sets the current trace mask and returns the old mask. mask is interpreted as a bitmask of cwal_trace_flags values. If mask == -1 then it returns the current mask without setting it, otherwise it sets the trace mask to the given value and returns the previous value. If !e or tracing is disabled at built-time, returns -1. */ int32_t cwal_engine_trace_flags( cwal_engine * e, int32_t mask ); /** Experimental. Sets v's prototype value. Both v and prototype must be container types (those compatible with cwal_prop_set() and friends), and prototype may be NULL. If prototype is already v's prototype then this is a harmless no-op. If v is not a container type, CWAL_RC_TYPE is returned. If (v==prototype), CWAL_RC_MISUSE is returned. Returns CWAL_RC_CYCLES_DETECTED if v appears anywhere in the given prototype's prototype chain, with the special allowance of prototype already being v's prototype (see above). If v already has a different prototype, it is un-ref'd during replacement. On success, v adds a reference to the prototype object. */ int cwal_value_prototype_set( cwal_value * v, cwal_value * prototpe ); /** If v is a type capable of having a prototype, its prototype (possibly NULL) is returned, otherwise it is equivalent to cwal_value_prototype_base_get(e,cwal_value_type_id(v)) is returned. Reminder to self: the engine argument is only required so that this can integrate with cwal_prototype_base_get(). */ cwal_value * cwal_value_prototype_get( cwal_engine * e, cwal_value const * v ); /** Maps the given client-specified prototype value to be the prototype for new values of type t. This adds a reference to proto and moves it to e's top-most scope so that it will live as long as e has scopes. Returns 0 on success, CWAL_RC_MISUSE if !e, and CWAL_RC_OOM if insertion of the prototype mapping could not allocate memory. All instances of the given type created after this is called will, if they are container types (meaning, by extension, capable of having a prototype) have proto assigned as their prototype as part of their construction process. Note that cwal does not assign prototypes by default - this is reserved solely for client-side use. Results are of course undefined if t is not a valid type ID (e.g. cast from an out-of-range integer). Potential uses: - Mapping common functions, e.g. toString() implementations, for types which cannot normally have prototypes (meaning non-container types). - A central place to plug in client-defined prototypes, such that new instances will inherit their prototypes (having had this feature would have saved th1ish a bit of code). @see cwal_prototype_base_get() */ int cwal_prototype_base_set( cwal_engine * e, cwal_type_id t, cwal_value * proto ); /** Returns a prototype value set via cwal_prototype_base_set(), or NULL if !e or no entry has been set by the client. */ cwal_value * cwal_prototype_base_get( cwal_engine * e, cwal_type_id t ); /** Returns true (non-0) if v==proto or v has proto in its prototype chain. Returns 0 if any argument is NULL. Reminder to self: the engine argument is only necessary so that this can integrate with cwal_prototype_base_get(). */ char cwal_value_derives_from( cwal_engine * e, cwal_value const * v, cwal_value const * proto ); /** @deprecated This function currently always (intentionally) fails an assertion in debug builds. Don't use this - it turns out blindly up-scoping values, especially return values of indeterminate origin, is not what we want to do more often than not. Normally we need to usw cwal_value_rescope() to ensure the proper rescoping (or not). Reparents v into it's current scope's parent scope. Returns CWAL_RC_MISUSE if !v or if v has no associated cwal_engine, CWAL_RC_RANGE if v is already in a top-level scope. This is a no-op for built-in constant values (which do not participate in lifetime tracking). */ int cwal_value_upscope( cwal_value * v ); /** Returns a handle to v's originating cwal_engine, or NULL if !v. */ cwal_engine * cwal_value_engine( cwal_value const * v ); /** Returns the current owning scope of v, or NULL if !v. */ cwal_scope * cwal_value_scope( cwal_value const * v ); /** Possibly reallocates self->list, changing its size. This function ensures that self->list has at least n entries. If n is 0 then the list is deallocated (but the self object is not), BUT THIS DOES NOT DO ANY TYPE-SPECIFIC CLEANUP of the items. If n is less than or equal to self->alloced then there are no side effects. If n is greater than self->alloced, self->list is reallocated and self->alloced is adjusted to be at least n (it might be bigger - this function may pre-allocate a larger value). Passing an n of 0 when self->alloced is 0 is a no-op. Newly-allocated slots will be initialized with NUL bytes. Returns the total number of items allocated for self->list. On success, the value will be equal to or greater than n (in the special case of n==0, 0 is returned). Thus a return value smaller than n is an error. Note that if n is 0 or self is NULL then 0 is returned. The return value should be used like this: @code cwal_size_t const n = number of bytes to allocate; if( n > cwal_list_reserve( myList, n ) ) { ... error ... } // Or the other way around: if( cwal_list_reserve( myList, n ) < n ) { ... error ... } @endcode */ cwal_size_t cwal_list_reserve( cwal_engine * e, cwal_list * self, cwal_size_t n ); /** Appends a bitwise copy of cp to self->list, expanding the list as necessary and adjusting self->count. Ownership of cp is unchanged by this call. cp may not be NULL. Returns 0 on success, CWAL_RC_MISUSE if any argument is NULL, or CWAL_RC_OOM on allocation error. */ int cwal_list_append( cwal_engine * e, cwal_list * self, void * cp ); /** @typedef typedef int (*cwal_list_visitor_f)(void * p, void * visitorState ) Generic visitor interface for cwal_list lists. Used by cwal_list_visit(). p is the pointer held by that list entry and visitorState is the 4th argument passed to cwal_list_visit(). Implementations must return 0 on success. Any other value causes looping to stop and that value to be returned, but interpration of the value is up to the caller (it might or might not be an error, depending on the context). Note that client code may use custom values, and is not restricted to CWAL_RC_xxx values. */ typedef int (*cwal_list_visitor_f)(void * obj, void * visitorState ); /** For each item in self->list, visitor(item,visitorState) is called. The item is owned by self. The visitor function MUST NOT free the item, but may manipulate its contents if application rules do not specify otherwise. If order is 0 or greater then the list is traversed from start to finish, else it is traverse from end to begin. Returns 0 on success, non-0 on error. If visitor() returns non-0 then looping stops and that code is returned. */ int cwal_list_visit( cwal_list * self, char order, cwal_list_visitor_f visitor, void * visitorState ); /** Works similarly to the visit operation without the _p suffix except that the pointer the visitor function gets is a (**) pointing back to the entry within this list. That means that callers can assign the entry in the list to another value during the traversal process (e.g. set it to 0). If shiftIfNulled is true then if the callback sets the list's value to 0 then it is removed from the list and self->count is adjusted (self->alloced is not changed). */ int cwal_list_visit_p( cwal_list * self, char order, char shiftIfNulled, cwal_list_visitor_f visitor, void * visitorState ); /** Parses command-line-style arguments into a cwal object tree. argc and argv are expected to be values from main() (or similar, possibly adjusted to remove argv[0]). It expects arguments to be in any of these forms, and any number of leading dashes are treated identically: --key : Treats key as a boolean with a true value. --key=VAL : Treats VAL as either a double, integer, or string. --key= : Treats key as a JSON null (not literal NULL) value. All such properties are accumulated in the (*tgt).flags Object. Arguments not starting with a dash are treated as "non-flags" and are accumulated in the (*tgt).nonFlags array. Each key/value pair is inserted into an object. If a given key appears more than once then only the final entry is actually stored. tgt must be either a pointer to NULL or a pointer to a client-provided Object. If (NULL==*tgt) then this function allocates a new object and on success it stores the new object in *tgt (it is owned by the caller). If (NULL!=*tgt) then it is assumed to be a properly allocated object. DO NOT pass a pointer to an unitialized pointer, as that will fool this function into thinking it is a valid object and Undefined Behaviour will ensue. If count is not NULL then the number of arugments parsed by this function are assigned to it. On error, count will be the number of options successfully parsed before the error was encountered. On success: - 0 is returned. - If (*tgt==NULL) then *tgt is assigned to a newly-allocated object, owned by the caller. Note that even if no arguments are parsed, the object is still created. On error: - non-0 is returned - If (*tgt==NULL) then it is not modified. - If (*tgt!=NULL) (i.e., the caller provides his own object) then it might contain partial results. */ int cwal_parse_argv_flags( cwal_engine * e, int argc, char const * const * argv, cwal_value ** tgt ); /** A helper function intended for use in implementing utilities like cwal_parse_argv_flags(). This function tries to evaluate arg as follows: - If it looks like a number, return a numeric value. - If it is "true" or "false", return the equivalent boolean value. - If it is NULL or "null", return the special null value. - Else treat it like a string. Returns NULL only on allocation error or if !e. */ cwal_value * cwal_value_from_arg(cwal_engine * e, char const *arg); /** Creates a new weak reference for the given value. The return value can be passed to cwal_weak_ref_value() to find out if the value referenced by the cwal_weak_ref is still valid. Returns NULL if !v or on allocation error. If recycling is enabled for the CWAL_TYPE_WEAK_REF type then this will re-use recyclable memory if any is available. Results are strictly undefined if v is not valid at the time this is called (e.g. if it has already been destroyed and is a dangling pointer). The caller must eventually pass the returned instance to cwal_weak_ref_free() to clean it up. Note that cwal_weak_refs are not owned by scopes, like values are, so they will not be pulled out from under the client if a weak ref survives past the cwal_scope under which it is created. Minor achtung: weak refs are themselves reference-counted, and all weak refs to the same value (assuming it really _is_ the same value when all weak refs are created) will be the same weak ref instance. However, UNLIKE VALUES, they start life with a refcount of 1 instead of 0 (a currently-necessary side-effect of the sharing). That, however, is an implementation detail which clients must not rely on. i.e. the must pass each returned value from this function to cwal_weak_ref_free(), even though this function may return the same value multiple times. If v is one of the built-in values then this function returns a shared cwal_weak_ref instance, but this is an optimization and implementation detail, and clients should not rely on it. The above refcounting and sharing is mentioned here primarily in case someone happens to notice this function returning duplicate pointers and thinks its a bug. It's not a bug, it just means that v is one of the special built-in constants or a multiply-weak-ref'd value. For built-ins, the weak reference will never become invalidated because the built-in values are neither allocated nor freed (and thus valid for the life of the program). @see cwal_weak_ref_free() @see cwal_weak_ref_value() @see cwal_weak_ref_custom_new() */ cwal_weak_ref * cwal_weak_ref_new( cwal_value * v ); /** If r was created by cwal_weak_ref_new() and r's value is still alive then this function returns it, else it returns NULL. Will return NULL after the referenced value has been destroyed via the normal value lifetime processes. Returns NULL if !r. @see cwal_weak_ref_new() @see cwal_weak_ref_free() */ cwal_value * cwal_weak_ref_value( cwal_weak_ref * r ); /** Frees (or recycles) the memory associated with a weak reference created by cwal_weak_ref_new() or cwal_weak_ref_custom_new(). If the client fails to do so, the reference will effectively leak until the engine is cleaned up, at which point it will reap the memory of all dangling weak references (at which point it becomes illegal for the client to try to do so because both the cwal_engine and the weak reference are invalid!). cwal_engine_recycle_max() can be used to configure the size of the weak reference recycling pool by passing CWAL_TYPE_WEAK_REF as its second parameter. @see cwal_weak_ref_new() */ void cwal_weak_ref_free( cwal_engine * e, cwal_weak_ref * r ); /** Creates a weak reference which "monitors" p. A call to cwal_weak_ref_custom_invalidate(e,p) will "invalidate" any weak references pointing to, such that cwal_weak_ref_custom_check() and cwal_weak_ref_custom_ptr() for references to that memory will return NULL. Note that this function recycles cwal_weak_ref instances for any given value of p, meaning that this function may return the same instance multiple times when passed the same parameters. However, it reference counts them and each instance should still be treated as unique and passed to cwal_weak_ref_free() when the client is done with it. Clients must at some point call cwal_weak_ref_custom_invalidate() to remove any entries they "map" via weak references. Ideally they should do this in the moment before their native memory is being finalized or otherwise unassociated with script-space. If clients do not do so then weak references to that memory will (incorrectly) still think it is alive because cwal still holds a copy of that pointer. @see cwal_weak_ref_custom_invalidate() @see cwal_weak_ref_custom_check() @see cwal_weak_ref_custom_ptr() */ cwal_weak_ref * cwal_weak_ref_custom_new( cwal_engine * e, void * p ); /** "Invalidates" p, in that future calls to cwal_weak_ref_custom_check(e,p) or cwal_weak_ref_custom_ptr() will return NULL. Returns 0 (false) if it does not find p in e's weak ref mapping or non-0 (true) if it does (and thereby invalidates existing weak refs to it). @see cwal_weak_ref_custom_new() */ char cwal_weak_ref_custom_invalidate( cwal_engine * e, void * p ); /** Searches e to see if p is being monitored by weak references created via cwal_weak_ref_custom_new(e,p). If one is found then then p is returned, else NULL is returned. Note that a call to cwal_weak_ref_custom_invalidate() "erases" monitored pointers, and if p has been passed to it then this function will return NULL. This is essentially an O(1) operation (a hashtable lookup). */ void * cwal_weak_ref_custom_check( cwal_engine * e, void * p ); /** If r was created by cwal_weak_ref_custom_new() and has not been invalidated then this function returns r's native memory pointer (of a type known only to whoever created r, if at all). Otherwise it returns NULL. This is faster than cwal_weak_ref_custom_check() (O(1) vs. a slower O(1)). */ void * cwal_weak_ref_custom_ptr( cwal_weak_ref * r ); /** Returns true (non-0) if p has been registered as weakly-referenced memory with e, else false (0). Note that p is intended to be a client-side native memory address or cwal_value pointer, and NOT one of the concrete higher-level types like cwal_object, nor a cwal_weak_ref instance. p "should" be a const pointer, but some internals disallow that (we don't do anything non-consty with it, though). In script bindings, however, const pointers are fairly rarely because bound data are rarely const. */ char cwal_is_weak_referenced( cwal_engine * e, void * p ); /** Tokenizes an input string on a given separator. Inputs are: - (inp) = is a pointer to the pointer to the start of the input. - (separator) = the separator character - (end) = a pointer to NULL. i.e. (*end == NULL) This function scans *inp for the given separator char or a NULL char. Successive separators at the start of *inp are skipped. The effect is that, when this function is called in a loop, all neighboring separators are ignored. e.g. the string "aa.bb...cc" will tokenize to the list (aa,bb,cc) if the separator is '.' and to (aa.,...cc) if the separator is 'b'. Returns 0 (false) if it finds no token, else non-0 (true). Output: - (*inp) will be set to the first character of the next token. - (*end) will point to the one-past-the-end point of the token. If (*inp == *end) then the end of the string has been reached without finding a token. Post-conditions: - (*end == *inp) if no token is found. - (*end > *inp) if a token is found. It is intolerant of NULL values for (inp, end), and will assert() in debug builds if passed NULL as either parameter. When looping, one must be sure to re-set the inp and end parameters on each iterator. For example: @code char const * head = "/a/b/c"; char const * tail = NULL; while( cwal_strtok( &inp, '/', &tail ) ) { ... head = tail; tail = NULL; } @endcode If the loop calls 'continue', it must be careful to ensure that the parameters are re-set, to avoid an endless loop. This can be simplified with a goto: @code while( cwal_strtok( &head, '/', &tail ) ) { if( some condition ) { ... do something ... goto next_iter; } else { ... } next_iter; head = tail; tail = NULL; } @endcode or a for loop: @code for( ; cwal_strtok(&head, '/', &tail); head = tail, tail = NULL){ ... } @endcode */ char cwal_strtok( char const ** inp, char separator, char const ** end ); /** Returns the first Function in v's prototype chain, including v. */ cwal_function * cwal_value_function_part( cwal_engine * e, cwal_value * v ); /** Returns the first Array in v's prototype chain, including v. */ cwal_array * cwal_value_array_part( cwal_engine * e, cwal_value * v ); /** Returns the first Hash in v's prototype chain, including v. */ cwal_hash * cwal_value_hash_part( cwal_engine * e, cwal_value * v ); /** Returns the first Buffer in v's prototype chain, including v. */ cwal_buffer * cwal_value_buffer_part( cwal_engine * e, cwal_value * v ); /** Returns the first Exception in v's prototype chain, including v. */ cwal_value * cwal_value_exception_part( cwal_engine * e, cwal_value * v ); /** Returns the first String in v's prototype chain, including v. */ cwal_string * cwal_value_string_part( cwal_engine * e, cwal_value * v ); /** Returns the first Native in v's prototype chain, including v. */ cwal_native * cwal_value_native_part( cwal_engine * e, cwal_value * v ); /** Installs or removes a callback hook. If h is not NULL, its contents are bitwise copied into space owned by e, replacing any existing callback hook. If h is NULL, any installed callback hook is cleared (with no notification to the hooks!). @see cwal_callback_hook */ int cwal_callback_hook_set(cwal_engine * e, cwal_callback_hook const * h ); /** Dumps e's internalized strings table to e's output channel. If showEntries is true it lists all entries. If includeStrings is not 0 then strings of that length or less are also output (longer ones are not shown). If includeStrings is 0 then the strings are not output. Note that the strings are listed in an unspecified order (actually orded by (hash page number/hash code), ascending, but that's an implementation detail). */ void cwal_dump_interned_strings_table( cwal_engine * e, char showEntries, cwal_size_t includeStrings ); /** Dumps some allocation-related metrics to e's output channel. Intended only for optimization and debugging purposes. */ void cwal_dump_allocation_metrics( cwal_engine * e ); /** Marks v, which must be a container type (for which cwal_props_can() returns true), as being exempted (or not) from vacuum operations, but otherwise does not affect its lifetimes. Values marked as being exemted, and any values they reference, will be treated as named variables for purposes of cwal_engine_vacuum() (that is, a vacuum will not destroy them). If the 2nd argument is true, the value is marked as vacuum-proof, otherwise it is unmarked, making it _potentially_ (based on it's exactly place in the universe) subject to subsequent vacuuming. Returns 0 on success (v is a container), CWAL_RC_MISUSE if v is 0, and CWAL_RC_TYPE if v is not a container. The intent of this function is only to make internal Values which are not accessible via script code and which need to stay alive. Such values require a reference (see cwal_value_ref()) and to be vacuum-proofed via this function. As of this writing, in the whole cwal/th1ish constellation, only two values are marked as vacuum-proof: (A) cwal's internal list of prototypes (only the list, not the prototypes) and (B) a piece of th1sh's internals where it stashes its own non-script visible values. Any values reachable via a vacuum-proof container are safe from vacuuming, thanks to side-effects of cwal's lifetime management. @see cwal_engine_vacuum() */ int cwal_value_make_vacuum_proof( cwal_value * v, char yes ); /** Returns true if v has explicitly been made vacuum-proof using cwal_value_make_vacuum_proof() OR if it is a built-in constant value, else false. A value which is not explicitly vacuum-proof may still be implicitly vacuum-proofed via a container which creates a path leading to the value. */ char cwal_value_is_vacuum_proof( cwal_value const * v ); /* LICENSE This software's source code, including accompanying documentation and demonstration applications, are licensed under the following conditions... Certain files are imported from external projects and have their own licensing terms. Namely, the JSON_parser.* files. See their files for their official licenses, but the summary is "do what you want [with them] but leave the license text and copyright in place." The author (Stephan G. Beal [http://wanderinghorse.net/home/stephan/]) explicitly disclaims copyright in all jurisdictions which recognize such a disclaimer. In such jurisdictions, this software is released into the Public Domain. In jurisdictions which do not recognize Public Domain property (e.g. Germany as of 2011), this software is Copyright (c) 2011-2014 by Stephan G. Beal, and is released under the terms of the MIT License (see below). In jurisdictions which recognize Public Domain property, the user of this software may choose to accept it either as 1) Public Domain, 2) under the conditions of the MIT License (see below), or 3) under the terms of dual Public Domain/MIT License conditions described here, as they choose. The MIT License is about as close to Public Domain as a license can get, and is described in clear, concise terms at: http://en.wikipedia.org/wiki/MIT_License The full text of the MIT License follows: -- Copyright (c) 2011-2014 Stephan G. Beal (http://wanderinghorse.net/home/stephan/) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --END OF MIT LICENSE-- */ #if defined(__cplusplus) } /*extern "C"*/ #endif #endif /* WANDERINGHORSE_NET_CWAL_H_INCLUDED */ /* end of file include/wh/cwal/cwal.h */ /* start of file include/wh/cwal/cwal_printf.h */ #ifndef WANDERINGHORSE_NET_WHPRINTF_H_INCLUDED #define WANDERINGHORSE_NET_WHPRINTF_H_INCLUDED 1 #ifdef _MSC_VER #define _CRT_NONSTDC_NO_DEPRECATE #endif #include #include /* FILE handle */ #ifdef __cplusplus extern "C" { #endif /** @page cwal_printf_page_main cwal_printf printf-like API This API contains a printf-like implementation which supports aribtrary data destinations. Authors: many, probably. This code supposedly goes back to the early 1980's. Current maintainer: Stephan Beal (http://wanderinghorse.net/home/stephan) License: Public Domain. The primary functions of interest are cwal_printfv() and cwal_printf(), which works similarly to printf() except that they take a callback function which they use to send the generated output to arbitrary destinations. e.g. one can supply a callback to output formatted text to a UI widget or a C++ stream object. */ /** @typedef long (*cwal_printf_appender)( void * arg, char const * data, long n ) The cwal_printf_appender typedef is used to provide cwal_printfv() with a flexible output routine, so that it can be easily send its output to arbitrary targets. The policies which implementations need to follow are: - arg is an implementation-specific pointer (may be 0) which is passed to vappendf. cwal_printfv() doesn't know what this argument is but passes it to its cwal_printf_appender. Typically it will be an object or resource handle to which string data is pushed or output. - The 'data' parameter is the data to append. If it contains embedded nulls, this function will stop at the first one. Thus it is not binary-safe. - n is the number of bytes to read from data. If n<0 then strlen(data) should be used. - Returns, on success, the number of bytes appended (may be 0). - Returns, on error, an implementation-specified negative number. Returning a negative error code will cause cwal_printfv() to stop the processing of that string. Note that 0 is a success value (some printf format specifiers do not add anything to the output). */ typedef long (*cwal_printf_appender)( void * arg, char const * data, long n ); /** This function works similarly to classical printf implementations, but instead of outputing somewhere specific, it uses a callback function to push its output somewhere. This allows it to be used for arbitrary external representations. It can be used, for example, to output to an external string, a UI widget, or file handle (it can also emulate printf by outputing to stdout this way). INPUTS: pfAppend : The is a cwal_printf_appender function which is responsible for accumulating the output. If pfAppend returns a negative integer then processing stops immediately. pfAppendArg : is ignored by this function but passed as the first argument to pfAppend. pfAppend will presumably use it as a data store for accumulating its string. fmt : This is the format string, as in the usual printf(). ap : This is a pointer to a list of arguments. Same as in vprintf() and friends. OUTPUTS: The return value is the total number of characters sent to the function "func", or a negative number on a pre-output error. If this function returns an integer greater than 1 it is in general impossible to know if all of the elements were output. As such failure can only happen if the callback function returns an error, and this type of error is very rare in a printf-like context, this is not considered to be a significant problem. (The same is true for any classical printf implementations, as far as i'm aware.) CURRENT (documented) PRINTF EXTENSIONS: %%z works like %%s, but takes a non-const (char *) and vappendf deletes the string (using free()) after appending it to the output. %%h (HTML) works like %s but converts certain characters (like '<' and '&' to their HTML escaped equivalents. %%t (URL encode) works like %%s but converts certain characters into a representation suitable for use in an HTTP URL. (e.g. ' ' gets converted to %%20) %%T (URL decode) does the opposite of %t - it decodes URL-encoded strings. %%r requires an int and renders it in "ordinal form". That is, the number 1 converts to "1st" and 398 converts to "398th". %%q quotes a string as required for SQL. That is, '\'' characters get doubled. %%Q as %%q, but includes the outer '\'' characters and null pointers replaced by SQL NULL. (The %%q and %%Q specifiers are options inherited from this printf implementation's sqlite3 genes.) These extensions may be disabled by setting certain macros when compiling vappendf.c (see that file for details). */ long cwal_printfv( cwal_printf_appender pfAppend, /* Accumulate results here */ void * pfAppendArg, /* Passed as first arg to pfAppend. */ const char *fmt, /* Format string */ va_list ap /* arguments */ ); /** Identical to cwal_printfv() but takes a (...) ellipses list instead of a va_list. */ long cwal_printf(cwal_printf_appender pfAppend, void * pfAppendArg, const char *fmt, ... ); /** Emulates fprintf() using cwal_printfv(). */ long cwal_printf_FILE( FILE * fp, char const * fmt, ... ); /** va_list variant of cwal_printf_FILE(). */ long cwal_printfv_FILE( FILE * fp, char const * fmt, va_list args ); /** Works like cwal_printfv(), but appends all output to a dynamically-allocated string, expanding the string as necessary to collect all formatted data. The returned null-terminated string is owned by the caller and it must be cleaned up using free(). If !fmt or if the expanded string evaluates to empty, null is returned, not a 0-byte string. */ char * cwal_printfv_cstr( char const * fmt, va_list vargs ); /** Equivalent to cwal_printfv_cstr(), but takes elipsis arguments instead of a va_list. */ char * cwal_printf_cstr( char const * fmt, ... ); #ifdef __cplusplus } /* extern "C" */ #endif #endif /* WANDERINGHORSE_NET_WHPRINTF_H_INCLUDED */ /* end of file include/wh/cwal/cwal_printf.h */ /* end of file ../cwal_amalgamation.h */ /* start of file s2_t10n.h */ /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=2 et sw=2 tw=80: */ #ifndef NET_WANDERINGHORSE_CWAL_S2_T10N_H_INCLUDED_ #define NET_WANDERINGHORSE_CWAL_S2_T10N_H_INCLUDED_ /** An experiment in stack machines... None of this code is to be considered in any way useful until this comment is removed. It's all an experiment. */ #ifdef __cplusplus extern "C" { #endif typedef struct s2_ptoker s2_ptoker; typedef struct s2_ptoken s2_ptoken; /** s2 token type and operator IDs. Values under 128 can be translated literally to their equivalent char value. Values over 127 are symbolic, not necessarily mapping to a single byte. @see s2_ttype_cstr() */ enum s2_token_types { /** Used as the token type by s2_ptoker_next_token() when a tokenization-level error is encountered. */ S2_T_TokErr = -2, /** The generic EOF marker. Used by s2_ptoker_next_token() when the end of the tokenizer's input range is reached. Note that this token is also used for "virtual" EOF and does NOT necessarily map to a NUL byte in the input. e.g. when sub-parsing part of a larger expression, the subexpression will get a subset of the parent range to parse, and its virtual EOF will be part of its parent parser's input range. */ S2_T_EOF = -1, /** S2_T_INVALID is guaranteed by the API to be the entry in this enum with the value 0, whereas the concrete values for other non-ASCII-range tokens is unspecified except that they are guaranteed to be non-0. */ S2_T_INVALID = 0, /** Generic end-of-expression token. */ S2_T_EOX = 200, S2_T_Tab = 9, S2_T_NL = 10, S2_T_VTab = 11, S2_T_FF = 12, S2_T_CR = 13, /** Generic EOL token, for \r, \n, and \r\n. Whether or not newlines end an expression is (or should be) context-dependent, and may depend on what token(s) lie(s) before it in the parsing process. */ S2_T_EOL = 213, /** Generic token for runs of s2_is_blank() characters. */ S2_T_Space = 32 /* ' ' */, /** Generic token for runs of s2_is_blank() characters. */ S2_T_Blank = 132, S2_T_Whitespace = 232, S2_T_UTFBOM = 332 /* UTF byte-order marker (0xEF 0xBB 0xBF) */, S2_T_OpNot = 33 /* ! */, S2_T_OpHash = 35 /* # */, S2_T_Shebang = 135 /* #!... */, S2_T_OpModulo = 37 /* % */, S2_T_OpModuloAssign = 237 /* %= */, S2_T_OpModuloAssign3 = 337 /* X.Y %= Z*/, S2_T_OpAndBitwise = 38 /* & */, S2_T_OpAnd = 238 /* && */, S2_T_OpAndAssign = 338 /* &= */, S2_T_OpAndAssign3 = 438 /* X.Y &= Z */, S2_T_ParenOpen = 40 /* ( */, S2_T_ParenGroup = 140 /* a (...) group contained as string content in the token's value */, S2_T_ParenClose = 41 /* ) */, S2_T_OpMultiply = 42 /* * */, S2_T_OpMultiplyAssign = 242 /* *= */, S2_T_OpMultiplyAssign3 = 342 /* X.Y*=Z */, S2_T_OpPlus = 43 /* + */, S2_T_OpPlusUnary = 243 /* + */, S2_T_OpPlusAssign = 343 /* += */, S2_T_OpPlusAssign3 = 443 /* X.Y+=Z */, S2_T_OpIncr = 543 /* ++ */, S2_T_OpIncrPre = 643 /* ++ */, S2_T_OpIncrPost = 843 /* ++ */, S2_T_Comma = 44 /* , */, S2_T_RHSEval = 144 /* internal-use-only pseudo-operator */, S2_T_OpMinus = 45 /* - */, S2_T_OpMinusUnary = 245 /* - */, S2_T_OpMinusAssign = 345 /* -= */, S2_T_OpMinusAssign3 = 445 /* X.Y-=y */, S2_T_OpDecr = 545 /* -- */, S2_T_OpDecrPre = 645 /* -- */, S2_T_OpDecrPost = 745 /* -- */, S2_T_OpDot = 46 /* . */, S2_T_OpArrow = 146 /* -> */, S2_T_OpDivide = 47 /* / */, S2_T_OpDivideAssign = 147 /* /= */, S2_T_OpDivideAssign3 = 247 /* X.Y/=Z */, S2_T_Colon = 58 /* : */, S2_T_Colon2 = 258 /* :: */, S2_T_Semicolon = 59 /* ; */, S2_T_CmpLT = 60 /* < */, S2_T_CmpLE = 260 /* <= */, S2_T_OpShiftLeft = 360 /* << */, S2_T_OpShiftLeftAssign = 460 /* <<= */, S2_T_OpShiftLeftAssign3 = 560 /* X.Y<<=Z */, S2_T_HeredocStart = 660 /* <<< */, S2_T_OpAssign = 61 /* = */, S2_T_OpAssign3 = 161 /* = */, S2_T_CmpEq = 261 /* == */, S2_T_CmpNotEq = 361 /* != */, S2_T_CmpEqStrict = 461 /* === */, S2_T_CmpNotEqStrict = 561 /* !== */, S2_T_OpInherits = 661 /* inherits */, S2_T_OpContains = 761 /* =~ */, S2_T_OpNotContains = 861 /* !~ */, S2_T_CmpGT = 62 /* > */, S2_T_CmpGE = 262 /* >= */, S2_T_OpShiftRight = 362 /* >> */, S2_T_OpShiftRightAssign = 462 /* >>= */, S2_T_OpShiftRightAssign3 = 562 /* X.Y>>=Z */, S2_T_Question = 63 /* ? */, S2_T_BraceOpen = 91 /* [ */, S2_T_BraceGroup = 191 /* [ */, S2_T_Backslash = 92 /* \\ */, S2_T_BraceClose = 93 /* ] */, S2_T_OpXOr = 94 /* ^ */, S2_T_OpXOrAssign = 294 /* ^= */, S2_T_OpXOrAssign3 = 394 /* X.Y^=Z */, S2_T_SquigglyOpen = 123 /* { */, S2_T_SquigglyString = 223 /* a th1ish-style {string} */, S2_T_OpOrBitwise = 124 /* | */, S2_T_OpOr = 224 /* || */, S2_T_OpOr3 = 324 /* ||| */, S2_T_OpOrAssign = 424 /* |= */, S2_T_OpOrAssign3 = 524 /* X.Y|=Z */, S2_T_SquigglyClose = 125 /* } */, S2_T_OpNegateBitwise = 126 /* ~ */, S2_T_Literal__ = 1000, S2_T_LiteralInt, S2_T_LiteralIntDec, S2_T_LiteralIntHex, S2_T_LiteralIntOct, S2_T_LiteralDouble, S2_T_LiteralStringDQ, S2_T_LiteralStringSQ, S2_T_LiteralString /* for "untranslated" strings */, S2_T_PropertyKey /* special case of LiteralString */, S2_T_Identifier, S2_T_ValueTypes__ = 2000, S2_T_Value, S2_T_Undefined, S2_T_Null, S2_T_False, S2_T_True, S2_T_Object, S2_T_Array, S2_T_Function, S2_T_Keyword__ = 3000, S2_T_KeywordAffirm, S2_T_KeywordAssert, S2_T_KeywordBREAKPOINT, S2_T_KeywordBreak, S2_T_KeywordCOLUMN, S2_T_KeywordCatch, S2_T_KeywordConst, S2_T_KeywordContinue, S2_T_KeywordDo, S2_T_KeywordEval, S2_T_KeywordExit, S2_T_KeywordFILE, S2_T_KeywordFILEDIR, S2_T_KeywordFalse, S2_T_KeywordFatal, S2_T_KeywordFor, S2_T_KeywordFunction, S2_T_KeywordIf, S2_T_KeywordLINE, S2_T_KeywordNameof, S2_T_KeywordNull, S2_T_KeywordProc, S2_T_KeywordRefcount, S2_T_KeywordReturn, S2_T_KeywordSRCPOS, S2_T_KeywordScope, S2_T_KeywordThrow, S2_T_KeywordTrue, S2_T_KeywordTypename, S2_T_KeywordUndefined, S2_T_KeywordUnset, S2_T_KeywordVar, S2_T_KeywordWhile, S2_T_Comment__ = 4000, S2_T_CommentC, S2_T_CommentCpp, S2_T_Mark__ = 5000, S2_T_MarkVariadicStart, S2_T_Misc__ = 6000, /** A pseudo-token used internally to translate empty [] blocks to a PHP-style array-append operation. The parser current only allows this op in the context of an assignment */ S2_T_ArrayAppend, S2_T_Foo, S2_T_comma_kludge_ }; #if 0 typedef struct s2_byte_range s2_byte_range; /** Holds a pair of pointers indicating a range to an abstract string data source. */ struct s2_byte_range { /** The starting position of source. */ char const * begin; /** One-past-the-end position. */ char const * end; }; #define s2_byte_range_empty_m {0,0} extern const s2_byte_range s2_byte_range_empty; #endif /** A "parser token" - tokens used by the s2 tokenization and (to some extent) evaluation process. */ struct s2_ptoken{ /** A s2_token_types values. */ int ttype; /** The starting point of the token, relative to its containing script. Invalid tokens have a NULL begin value. */ char const * begin; /** The one-after-the-end point for the token. When tokenizing iteratively, each next token starts at the end position of the previous token. */ char const * end; /** Some token types "trim" their bytes to some subset of [begin, end). For such token types, the range [adjBegin, adjEnd) should be used for fetching their "inner" bytes, while [begin, end) will hold the full token bytes. Currently the types for which s2_next_token() does this include: S2_T_SquigglyOpen, S2_T_HeredocStart (both of whichi it converts to S2_T_SquigglyString), S2_T_BraceOpen, S2_T_ParenOpen. */ char const * adjBegin; /** The one-after-the-end counterpart of adjBegin. */ char const * adjEnd; }; /** Empty-initialized s2_ptoken structure, intended for const-copy initialization. */ #define s2_ptoken_empty_m {S2_T_INVALID,0,0,0,0} /** Empty-initialized s2_ptoken structure, intended for copy initialization. */ extern const s2_ptoken s2_ptoken_empty; /** The s2_ptoker class is just that - a simple basis for a tokenizer, largely syntax- and language-independent. Its origins go back many years and several projects. This tokenizer requires that all input be available in advance of tokenization, but it can be used to tokenizer (almost) arbitrarily small (token-sized) chunks of input at a time. @see s2_ptoker_init() @see s2_ptoker_next_token() @see s2_ptoker_lookahead() @see s2_ptoker_putback() */ struct s2_ptoker { /** Starting position of input. The full input range is [begin, end). */ char const * begin; /** One-past-the-end position of the input (i.e. the position where the NUL byte normally is). */ char const * end; /** Error string (static memory) from tokenization errors. Set by s2_ptoker_next_token(). */ char const * errMsg; /** A hint for the error position. Must be 0 or between [begin,end). Used for calculating line/column positions for error reporting. */ char const * errPos; /** Used for error reporting. May be a file name or a descriptive name like "eval script". */ char const * name; /** The length of the name, in bytes. */ cwal_size_t nameLen; /** Used for adjusting errPos when calculating line/col info for sub-parsing errors. */ s2_ptoker const * parent; /** Used for capturing line/column offset info for "distant child" tokenizers, which "know" they derive from another but have access to it (it may be long gone). */ int lineOffset; /** Column counterpart of lineOffset. */ int colOffset; /** 1-based current tokenization line number. This is maintained by s2_ptoker_next_token(), updated as it crosses newline boundaries. */ int currentLine; /** 0-based current tokenization line number. This is maintained by s2_ptoker_next_token(), updated as it crosses newline boundaries. */ int currentCol; /** The current token. Its state is set up thusly: Initially, token.begin must be this->begin and token.end must be 0. That state is used by s2_ptoker_next_token() to recognize the initial token state and DRTR. During tokenization, this object's state is updated to reflect the by range from [this->begin, this->end) matching a token (or an error position, in the case of a tokenization error). */ s2_ptoken token; /** s2_ptoker_next_token() copies this->token to this object before attempting any tokenization. s2_ptoker_putback() copies pbToken over this->token and clears pbToken. */ s2_ptoken pbToken; /** This token is only used for its [begin, end) range. _Some_ APIs set this to a range encompasing all input which they consume. e.g. it can be used to record the whole result of multiple s2_ptoker_next_token() calls by setting capture.begin to the start of the first token and capture.end the end of the last token captured. */ s2_ptoken capture; }; /** Empty-initialized s2_ptoker object. */ #define s2_ptoker_empty_m { \ 0/*begin*/,0/*end*/, \ 0/*errMsg*/,0/*errPos*/, \ 0/*name*/, \ 0/*nameLen*/, \ 0/*parent*/, \ 0/*lineOffset*/,0/*colOffset*/,\ 1/*currentLine*/,0/*currentCol*/,\ s2_ptoken_empty_m/*token*/, \ s2_ptoken_empty_m/*pbToken*/, \ s2_ptoken_empty_m/*capture*/ \ } /** Empty-initialized s2_ptoker object. */ extern const s2_ptoker s2_ptoker_empty; /** Must be passed a s2_ptoker and its input source. If len is negative then the equivalent of strlen() is used to calculate its length. Returns 0 on success, CWAL_RC_MISUSE if !t or !src. Use s2_ptoker_next_token() to fetch the next token, s2_ptoker_lookahead() to "peek" at the next token, and s2_ptoker_putback() to put a just-fetched token back. */ int s2_ptoker_init( s2_ptoker * t, char const * src, cwal_int_t len ); /** Initializes t as a sub-tokenizer of parent, using parent->token as t's input range. Returns CWAL_RC_RANGE if parent->token does not have a valid byte range. On success, [t->begin, t->end) point to the sub-tokenization range and t->parent points to parent. Results are undefined if either argument is NULL or points to uninitialized memory. */ int s2_ptoker_sub_from_token( s2_ptoker * t, s2_ptoker const * parent ); /* s2_ptoker const * s2_ptoker_root( s2_ptoker const * t ); */ /** Returns the top-most object from t->parent, or t if !t->parent. */ s2_ptoker const * s2_ptoker_top_parent( s2_ptoker const * t ); /** Returns either t->name or the first name from the t->parent chain. Returns 0 if no name is found. If len is not 0 then if this function returns non-0, len is set to that name's length. */ char const * s2_ptoker_name_first( s2_ptoker const * t, cwal_size_t * len ); /** Returns the top-most name from t and its parent chain. Returns 0 if no name is found. */ char const * s2_ptoker_name_top( s2_ptoker const * t ); char const * s2_ptoker_err_pos_first( s2_ptoker const * t ); /** Fetches the next token from t. t must have been successfully intialized using s2_ptoker_init(). This function is intended to be called repeatedly until it either returns 0 (success) AND has (t->ttype==S2_T_EOF) or until it returns non-0 (error). On each iteration, clients should collection the token information they need before calling this again (which changes t's state). Note that this is a lower-level function thatn s2_next_token(). That one builds off of this one. On success 0 is returned and t is updated as follows: t->tokenBegin points to the start of the token. t->tokenEnd points to the one-past-the-end character of the token, so the length of the token is (t->tokenEnd - t->tokenBegin). t->ttype will be set to one of the S2_T_xxx constants. At the end of input, t->ttype will be S2_T_EOF and the token length with be 0 (t->begin==t->end). On error non-0 is returned, t->ttype will be S2_T_TokErr, and t->errMsg will contain a basic description of the error. On success t->errMsg will be 0, so clients may use that to check for errors instead checking the result code or token type S2_T_TokErr. The bytes in t->errMsg are guaranteed to be static. On error t->tokenBegin will point to the starting position of the erroneous or unrecognized token. The underlying tokenizer is fairly grammar-agnostic but tokizes many constructs as they exist in C-like languages, e.g. ++ is a single token (as opposed to two + tokens), and >>= is also a single token. This function saves the pre-call current token state to a putback token, and the token can be "put back" by calling s2_ptoker_putback(). Use s2_ptoker_lookahead() to "peek" at the next token while keeping the token iterator in place. Any tokenization error is assumed to be unrecoverable, and it is not normally useful to call this again without re-initializing the tokenizer. */ int s2_ptoker_next_token( s2_ptoker * t ); /** For a given s2_token_types values, this returns a unique string representation of its type ID. The returned bytes are static. Returns 0 for an unknown value. The main purpose of this function is actually so that we can let gcc warn us if any values in s2_token_types collide with one another. Implementing it caught two collisions :). */ char const * s2_ttype_cstr( int ttype ); /** Similar to s2_ptoker_next_token(), but it skips over any tokens for which s2_ttype_is_junk() returns true. On returning, st->token holds the last-tokenized position. After this call, the putback token will be the previous token read before this call. i.e. the intervening junk tokens are not placed into the putback token. */ int s2_ptoker_next_token_skip_junk( s2_ptoker * st ); /** If st->token holds no token information, false is returned and this function has no side-effects, otherwise st->token is replaced by the put-back token, the put-back token is cleared, and true is returned. */ char s2_ptoker_putback( s2_ptoker * st ); /** Uses s2_ptoker_next_token() to fetch the next token, sets *tgt to the state of that token, resets the tokenizer position to its pre-call state, and returns the result of the s2_ptoker_next_token() call. After calling this, both the current token position and the putback token will be as they were before this function was called, but st->errMsg might contain error details if non-0 is returned. Pedantic note: the _contents_ of st->token and st->pbToken will change during the life of this call, but they will be reverted before it returned. The point being: don't rely on pointers held within those two members being stable between before and after this call, and always reference the addresses directly from the current state of st->token. @see s2_ptoker_lookahead_skip_junk() */ int s2_ptoker_lookahead( s2_ptoker * st, s2_ptoken * tgt ); /** A function signature for predicates which tell the caller whether a s2_token_types value meets (or does not meet) a certain condition. Implementations must return ttype if ttype meets their predicate condition(s), else false (0). Note that S2_T_INVALID is guaranteed by the API to be 0. */ typedef int (*s2_ttype_predicate_f)( int ttype ); /** Similar to s2_ptoker_lookahead(), but it skips over any leading tokens for which pred() returns true. On success *tgt contains the content of the token which either failed the predicate or is an EOF token. The client can force st to that tokenization position by passing it to s2_ptoker_token_set(). Before this function returns, st->token and st->pbToken are restored to their pre-call state. */ int s2_ptoker_lookahead_skip( s2_ptoker * st, s2_ptoken * tgt, s2_ttype_predicate_f pred ); /** Works like s2_ptoker_lookahead_skip(), but inverts the meaning of the predicate: it stops at the first token for which pred() returns true. */ int s2_ptoker_lookahead_until( s2_ptoker * st, s2_ptoken * tgt, s2_ttype_predicate_f pred ); /** Sets st->pbToken to st->token, then st->token to *t. */ void s2_ptoker_token_set( s2_ptoker * st, s2_ptoken const * t ); /** Returns st->token.ttype if st's current token represents an EOF. */ int s2_ptoker_is_eof( s2_ptoker const * st ); /** Returns st->token.ttype if st's current token represents an end-of-expression. */ int s2_ptoker_is_eox( s2_ptoker const * st ); /** Returns t->ttype if t's contents refer to a "true" {squigglyString}, as opposed to a heredoc in disguise. */ int s2_ptoken_is_true_squiggly( s2_ptoken const * t ); /** Returns ttype if ttype is an end-of-line token. */ int s2_ttype_is_eol( int ttype ); /** Returns ttype if ttype represents a "space" token (in any of its various incarnations). */ int s2_ttype_is_space( int ttype ); /** Returns ttype if the given token type is considered a "junk" token (with no syntactical meaning). Junk includes the following token types: ASCII 32d (SPACE), ASCII 13d (CR), ASCII 9d (TAB), S2_T_Blank, S2_T_CommentC, S2_T_CommentCpp, S2_T_Whitespace, S2_T_Shebang, S2_T_UTFBOM Note that S2_T_NL/S2_T_EOL (newline/EOL) is not considered junk here, as it is an expression separator in some contexts and skippable in others. Potential TODO: treat S2_T_CommentCpp as an EOL because this type of token implies one. */ int s2_ttype_is_junk( int ttype ); /** Returns ttype if ttype is a basic assignment op: S2_T_OpAssign, S2_T_ArrayAppend (internally treated as assignment). */ int s2_ttype_is_assignment( int ttype ); /** Returns ttype if ttype refers to one of the "combo assignment" operators, e.g. +=, -=, *=, etc. */ int s2_ttype_is_assignment_combo( int ttype ); /** Returns ttype if op is 0 or represents an operator which may legally directly proceed a unary operator. */ int s2_ttype_may_precede_unary( int ttype ); /** Returns ttype if ttype represents a symbol that marks the end of an expression: S2_T_EOF, S2_T_EOX, S2_T_Semicolon, S2_T_EOL, S2_T_CR, S2_T_NL Note that S2_T_Comma is NOT in that list. It arguably "should" be, but doing so breaks the current tokenizing/evaluation order in some contexts. */ int s2_ttype_is_eox( int ttype ); /** Returns ttype if ttype is one of: S2_T_Semicolon, S2_T_EOF else returns 0. */ int s2_ttype_is_hard_eox( int ttype ); /** Returns ttype if ttype represents an EOF (or virtual EOF) token. */ int s2_ttype_is_eof( int ttype ); /** Returns ttype if ttype presents a "group" type: S2_T_ParenGroup, S2_T_BraceGroup */ int s2_ttype_is_group( int ttype ); int s2_ttype_is_keyword( int ttype ); /** Returns ttype if it respends a token whose value can be converted to a cwal_value with ease. */ int s2_ttype_is_pod( int ttype ); /** Returns ttype if it represents an operator which is (in principal) capable of short-circuiting part of its arguments: S2_T_OpOr, S2_T_OpAnd, S2_T_Question */ int s2_ttype_short_circuits( int ttype ); /** Returns ttype if ttype is one of: S2_T_OpIncr, S2_T_OpDecr, S2_T_OpIncrPre, S2_T_OpIncrPost, S2_T_OpDecrPre, S2_T_OpDecrPost else returns 0. */ int s2_ttype_is_identifier_prefix( int ttype ); /** If pos is in the range [src,end) then this function calculates the line (1-based) and column (0-based) of pos within [src,end) and sets line/col to those values if those pointers are not NULL. If pos is out of range CWAL_RC_RANGE is returned and this function has no side-effects. Returns 0 on success. Note that s2 globally follows emacs line/column conventions: lines are 1-based and columns are 0-based. */ int s2_count_lines( char const * src, char const * end_, char const * pos_, int *line, int *col ); /** Wrapper around s2_count_lines(), which uses [pt->begin, pt->end) as the source range. */ int s2_ptoker_count_lines( s2_ptoker const * pt, char const * pos, int * line, int * col ); /** Collects info from pt which is useful in error reporting. pos is expected to be a position within [pt->begin,pt->end). Its line/column position is calculated as for s2_count_lines() (so *line will be 0 if pos is out of range). If pos is 0 then pt->errPos is used. If name is not NULL, *name is set to the value returned from s2_ptoker_name_top(). Any of the (name, line, col) parameters may be 0. */ void s2_ptoker_err_info( s2_ptoker const * pt, char const ** name, char const * pos, int * line, int * col ); /** Unescapes the raw source stream defined by [begin,end) and copies it to dest (_appending_ to any existing content in dest). Returns 0 on success. On success, dest->used is set to the length of the unescaped content plus its old length, not counting the trailing NUL (but the buffer is NUL-terminated). This is safe to use on an empty string (begin==end), in which case the first byte of the result will be the trailing NUL byte. Because this appends its results to dest, the caller may (depending on how he is using the buffer) need to remember the value of dest->used before this is called, as that will mark the point at which this function starts appending data. */ int s2_unescape_string( cwal_engine * e, char const * begin, char const * end, cwal_buffer * dest ); /** Assumes that zPos is the start of an identifier and reads until the next non-identifier character. zMaxPos must be the logical EOF for zPos. On returning, *zIdEnd is set to the one-after-the-end position of the read identifier (which will be (*zIdEnd-pos) bytes long). Expects the input to be valid ASCII/UTF8, else results are undefined. s2 treats ANY UTF8 character outside the ASCII range as an identifier character. */ void s2_read_identifier( char const * zPos, char const * zMaxPos, char const ** zIdEnd ); /** Returns true if ch is one of: ' ', \t, \v, \f */ char s2_is_blank( int ch ); /** Returns true if s2_is_blank(ch) is true of if ch is one of: \n, \r */ char s2_is_space( int ch ); /** Returns true if ch is-a digit character (0..9). */ char s2_is_digit( int ch ); /** Returns true if ch is-a hexidecimal digit character (0..9, a..F, A..F). */ char s2_is_xdigit( int ch ); /** Returns true if ch is an ASCII alphabetic character (a..z, A..Z). */ char s2_is_alpha( int ch ); /** Returns true if s2_is_alpha(ch) or s2_is_digit(ch). */ char s2_is_alnum( int ch ); /** Checks whether tok's range contains only "junk" tokens or not. If tok's range contains only noise tokens, 0 is returned, otherwise the token type ID of the first non-noise token is returned. Note that it also returns 0 if there is a tokenization error. */ int s2_ptoken_has_content( s2_ptoken const * tok ); /** If t is any of the following types then this sets *rc (if rc is not NULL) to its integer representation and returns true: S2_T_LiteralIntOct, S2_T_LiteralIntDec, S2_T_LiteralIntHex. Returns false (0) if all conditions are not met. ACHTUNG: this does not handle a leading sign. @see s2_ptoken_parse_double() @see s2_cstr_parse_double() @see s2_cstr_parse_int() */ char s2_ptoken_parse_int( s2_ptoken const * t, cwal_int_t * rc ); /** The double counterpart of s2_ptoken_parse_int(). @see s2_ptoken_parse_int() @see s2_cstr_parse_double() @see s2_cstr_parse_int() */ char s2_ptoken_parse_double( s2_ptoken const * t, cwal_double_t * rc ); /** Works like s2_ptoken_parse_int() except that: - It uses all bytes in the range [str, str+slen). - It accepts a leading sign character, regardless of the integer notation (decimal, hex, octal). It ignores spaces around the sign character. @see s2_ptoken_parse_double() @see s2_ptoken_parse_int() @see s2_cstr_parse_double() */ char s2_cstr_parse_int( char const * str, cwal_int_t slen, cwal_int_t * result ); /** The double counterpart of s2_cstr_parse_int(). @see s2_ptoken_parse_double() @see s2_ptoken_parse_int() @see s2_cstr_parse_int() */ char s2_cstr_parse_double( char const * str, cwal_int_t slen, cwal_double_t * result ); /** Expects a filename-like string in the first slen bytes of str, with directory components separated by either '/' or '\\' (it uses the first of those it finds, in that order, as the separator). If any separator is found, a pointer to the last instance of it in str is returned, otherwise 0 is returned. */ char const * s2_last_path_sep(char const * str, cwal_size_t slen ); #ifdef __cplusplus }/*extern "C"*/ #endif #endif /* include guard */ /* end of file s2_t10n.h */ /* start of file s2.h */ /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=2 et sw=2 tw=80: */ /* License: same as cwal. See cwal.h resp. cwal_amalgamation.h for details. */ #ifndef NET_WANDERINGHORSE_CWAL_S2_H_INCLUDED_ #define NET_WANDERINGHORSE_CWAL_S2_H_INCLUDED_ #if defined(_WIN32) # define S2_OS_WINDOWS #else # define S2_OS_UNIX #endif #if defined(S2_OS_UNIX) # if !defined(_XOPEN_SOURCE) /** on Linux, required for usleep(). */ # define _XOPEN_SOURCE 700 # endif # ifndef _XOPEN_SOURCE_EXTENDED # define _XOPEN_SOURCE_EXTENDED # endif # ifndef _BSD_SOURCE # define _BSD_SOURCE # endif # if !defined(_POSIX_C_SOURCE) # define _POSIX_C_SOURCE 199309L # endif #endif /** s2 is a light-weight yet flexible scripting language based on the cwal (Scripting Engine Without A Language) library. cwal provides the abstract Value types, memory lifetime management, and garbage collection systems, and s2 provides a scripting language on top of that. A breif over of properties of s2 which are of most interest to potential clients: - Library licensing: dual Public Domain/MIT, with one optional BSD-licensed part (the JSON input parser) which can be compiled out if that license is too restrictive. - A lightweight, portable[1] C API for binding client-defined functionality to script-space, making it scriptable and trackable by the script's garbage collector. Tests so far put s2 on par with lua in terms of memory usage. - s2's primary distributable is two C files: one header and one implementation file, called the "amalgamation build," intended for direct drop-in use in arbitrary client source trees. The canonical build tree is intended primarily for development of cwal/s2, not for development of client applications. The amalgamation includes the cwal amalgamation, so it need not be acquired separately. - Has an expression-oriented syntax with a distinct JavaScript flavor, but distinctly different scoping/lifetime rules. - cwal's garbage collector guarantees that client-specific finalizers (for client-specified types and, optionally, for function bindings) get called, provided it is used properly, though it cannot always guaranty the order of destruction. - cwal and s2 are developed with the compiler set to pedantic warning/error levels (and them some), and the s2 test suite includes valgrind-based testing and reporting. New code rarely hits the trunk without having run the whole test suite through valgrind. - Extending s2: the default s2 shell (s2sh) provides not only a copy/paste bootstrap for creating client applications, but can be extended by clients via script code or C (linking their features in directly in or loading them via DLLs) without modifying its sources, as described in the manual linked to below. [1] = Portable means: C89 with a weak dependency on the C99-specified inttypes.h and stdint.h for their fixed-size integers and portable printf/scanf format specifiers. In addition to these API docs, s2 as a whole is described in gross amounts of detail in this public Google Doc: https://docs.google.com/document/d/1hvMoHSIz94dob6fCU6SLxle_s7YL6CrA_4aU12OWwWY/view Which currently (late August, 2014) weighs in at just over 90 pages, covering both the scripting language itself (in detail) and how to use it in C (mainly via overviews and links to working code). */ #include /* struct tm */ #ifdef __cplusplus extern "C" { #endif typedef struct s2_engine s2_engine; typedef struct s2_scope s2_scope; typedef struct s2_op s2_op; typedef struct s2_stoken s2_stoken; typedef struct s2_stoken_stack s2_stoken_stack; typedef struct s2_error s2_error; typedef struct s2_estack s2_estack; typedef struct s2_sweep_guard s2_sweep_guard; typedef struct s2_func_state s2_func_state; enum s2_rc_t { S2_RC_placeholder = CWAL_RC_CLIENT_BEGIN, /** Used internally by routines which visit lists of keys/values to provide a mechanism for aborting traversal early without triggering an error. */ S2_RC_END_EACH_ITERATION, /** To be used only by the 'toss' keyword, if it ever gets added. */ S2_RC_TOSS }; /** Works like cwal_rc_cstr() but also accommodates the s2_rc_t values. */ char const * s2_rc_cstr(int rc); /** Enum specifying the precedences of operators in s2. Derived from: http://n.ethz.ch/~werdemic/download/week3/C++%20Precedence.html */ enum s2_precedents { S2_PR__start = 1, /** Parens precedence is actually irrelevant here, as we parse parens groups as atomic values instead of operators. */ S2_PR_ParensOpen, S2_PR_Comma, /** Internal pseudo-operator for RHS evaluation in some case. No longer used - can be removed. */ S2_PR_RHSEval, /** = += -= *= /= <<= >>= %= &= ^= |= */ S2_PR_Assignment__, /** The = operator. */ S2_PR_OpAssign =S2_PR_Assignment__, /** PHP-style array-append. Essentially works like (array DOT index = ...), where the index is the array's current length. Only usable in assignment contexts. */ S2_PR_ArrayAppend = S2_PR_Assignment__, /* S2_PR_Conditional__, */ /** In JavaScript ternary if has a higher precedence than assignment, but we're going to go with the C/C++ precedence here. */ S2_PR_TernaryIf = S2_PR_Assignment__, S2_PR_Logical__, S2_PR_LogicalOr = S2_PR_Logical__ /* || */, S2_PR_LogicalOr3 = S2_PR_Logical__ /* ||| */, S2_PR_LogicalAnd = S2_PR_Logical__ + 1 /* && */, S2_PR_Bitwise__, S2_PR_BitwiseOr = S2_PR_Bitwise__, S2_PR_BitwiseXor = S2_PR_Bitwise__ + 1, S2_PR_BitwiseAnd = S2_PR_Bitwise__ + 2, S2_PR_Equality__, S2_PR_CmpEq = S2_PR_Equality__, S2_PR_CmpEqStrict = S2_PR_Equality__, S2_PR_CmpNotEq = S2_PR_Equality__, S2_PR_CmpNotEqStrict = S2_PR_Equality__, S2_PR_Relational__, S2_PR_CmpLT = S2_PR_Relational__, S2_PR_CmpGT = S2_PR_Relational__, S2_PR_CmpLE = S2_PR_Relational__, S2_PR_CmpGE = S2_PR_Relational__, S2_PR_OpInherits = S2_PR_Relational__, /** '=~'. Should this have Equality precedence? */ S2_PR_Contains = S2_PR_Relational__, /** '!~'. Should this have Equality precedence? */ S2_PR_NotContains = S2_PR_Relational__, /* TODO? <== and >== for strict-type LE resp GE ==< resp ==> for strict LT/GT */ S2_PR_Bitshift__, S2_PR_ShiftLeft = S2_PR_Bitshift__, S2_PR_ShiftRight = S2_PR_Bitshift__, S2_PR_Additive__, S2_PR_Plus = S2_PR_Additive__, S2_PR_Minus = S2_PR_Additive__, S2_PR_Multiplicative__, S2_PR_Multiply = S2_PR_Multiplicative__, S2_PR_Divide = S2_PR_Multiplicative__, S2_PR_Modulo = S2_PR_Multiplicative__, S2_PR_Unary__, S2_PR_PlusUnary = S2_PR_Unary__, S2_PR_MinusUnary = S2_PR_Unary__, S2_PR_LogicalNot = S2_PR_Unary__, S2_PR_BitwiseNegate = S2_PR_Unary__, S2_PR_Keyword = S2_PR_Unary__, S2_PR_IncrDecr = S2_PR_Unary__, /* C++: S2_PR_Unary__ ==> sizeof, new, delete, & (addr of), * (deref), (typeCast), */ S2_PR_Primary__, S2_PR_OpIncr = S2_PR_Primary__, S2_PR_OpDecr = S2_PR_Primary__, S2_PR_FuncCall = S2_PR_Primary__, S2_PR_Subscript = S2_PR_Primary__, S2_PR_Braces = S2_PR_Primary__, S2_PR_DotDeref = S2_PR_Primary__, S2_PR_OpArrow = S2_PR_DotDeref, /* C++: S2_PR_Primary__ ==> typeid(), xxx_cast */ S2_PR_Specials__, S2_PR_ParensClose, S2_PR_NamespaceCpp /* :: */, S2_PR_end__ }; #undef PARENS_LOW_PRIO /** Represents a combination value/operator for an s2_engine. Each token represents one operand or operator for an s2 evaluation stack. They get allocated often, but recycled by their associated s2_engine, so allocations after the first few stack-pops are O(1) and cost no new memory. Token instances must not be in use more than once concurrently, e.g. a token may not be in more than one stack at a time, nor may it be in the same stack multiple times. */ struct s2_stoken{ /** A s2_token_types value. */ int ttype; /** Certain token types have a value associated with them. The tokenization process will create these, but will not add a reference to them (because doing so complicates lifetimes, in particular for result values which need up-scoping). This means the client must be careful when using them, to ensure that they get a ref if one is needed, and to either clean them up or leave them to the GC if they don't want them. */ cwal_value * value; /** Used for creating chains (e.g. a stack). */ s2_stoken * next; /** To eventually be used to optionally point to source location metadata which could be used in improving error reporting from the stack layer up through the evaluation layer. */ char const * srcPos; }; /** Empty-initialized s2_stoken structure, intended for const-copy initialization. */ #define s2_stoken_empty_m { \ S2_T_INVALID/*ttype*/, \ 0/*value*/, \ 0/*next*/, \ 0/*srcPos*/ \ } /** Empty-initialized s2_stoken structure, intended for copy initialization. */ extern const s2_stoken s2_stoken_empty; typedef struct s2_strace_entry s2_strace_entry; /** Under construction. */ struct s2_strace_entry { s2_ptoker const * pr; char const * pos; s2_strace_entry * up; s2_strace_entry * down; }; /** Empty-initilized s2_strace_entry object. */ #define s2_strace_entry_empty_m { \ 0/*pr*/, 0/*pos*/, 0/*up*/, 0/*down*/ \ } /** Empty-initilized s2_strace_entry object. */ extern const s2_strace_entry s2_strace_entry_empty; /** Callback function for operator implementations. The framework will pass the operator, the underlying engine, the number of operatand it was given (argc). Preconditions: - se's token stack will contain (argc) tokens intended for this operator. - The top of se operator stack will not be this operator (or not this invocation of it). - rv will not be NULL, but may point to a NULL pointer. Implementation requirements: - Implementations MUST pop EXACTLY (argc) tokens from se's token stack before returning or calling into any API which might modify se's token stack further. - A result value, if any, must be assigned to *rv. If the operation has no result, assign it to 0. The API will never pass a NULL rv to this routine.. If the operation creates a scope and receives *rv from that scope then it must be sure to upscope *rv before returning, to ensure that *rv is still valid after the scope is popped. As a rule, the result value is ignored/discarded if non-0 (error) is returned. - Must return 0 on success, and a non-0 CWAL_RC_xxx code on error. - If se->skipLevel is greater than 0, then the operator must do as little work as possible (e.g. no allocations or calculations), pop all arguments from the stack, assign *rv to some no-meaning value (cwal_value_undefined() is good), and return 0. This is used for implementing quasi-short-circuit logic, in that we allow the operators to run, but skip-mode indicates that we really are only interested in getting past the operator and its arguments, without having side-effects like creating new values. Certain result codes will be treated specially: CWAL_RC_OOM triggers a fatal OOM error. CWAL_RC_EXCEPTION means the function triggered an exception and set the engine's exception state. CWAL_RC_EXIT or CWAL_RC_FATAL trigger and end of the current evaluation. CWAL_RC_RETURN ... tbd. */ typedef int (*s2_op_f)( s2_op const * self, s2_engine * se, int argc, cwal_value **rv ); /** Represents a stack machine operation. Each operation in s2 equates to a shared/const instance of this class. */ struct s2_op { /** Generic symbolic representation, not necessarily what appears in script code. Need not be unique, either. Primarily intended for debugging. */ char const * sym; /** Operator type ID. Must be one of the s2_token_types values. */ int id; /** Number of expected operands. Negative value means any number, and the syntax will have to determine where to stop looking for operands. */ int arity; /** Associativity: <0 = left 0 = non-associative >0 = right */ int assoc; /** Precedence. */ int prec; /** <0 = has no operands or sits on the left of its operand(s). Arity must be <=0. 0 = sits between its 2 operands. arity must == 2. >0 = sits on the right of its 1 operand. arity must == 1. */ int placement; /** The operator's implementation function. */ s2_op_f call; /** An experiment in inferring compound comparison operators from simpler operators, e.g. infering '<' and '==' from '<=' and using them if the latter op is not available but the former two are. inferLeft is the LHS op id, inferRight is the RHS id. */ int inferLeft; int inferRight; }; /** @internal Empty-initialized s2_op structure, intended for const-copy initialization. Only used internally. */ #define s2_op_empty_m { \ 0/*sym*/, 0/*id*/, 0/*arity*/, \ 0/*assoc*/, 0/*prec*/, \ 0/*placement*/, 0/*call()*/, \ 0/*inferLeft*/, 0/*inferRight*/\ } #if 0 /** @internal Empty-initialized s2_op structure, intended for copy initialization. */ extern const s2_op s2_op_empty; #endif /** Holds a stack of s2_stokens. */ struct s2_stoken_stack { /** The top of the stack. Maintained via s2_stoken_stack_push(), s2_stoken_stack_pop(), and friends. */ s2_stoken * top; /** Number of items in the stack. */ int size; }; /** Empty-initialized s2_stoken_stack structure, intended for const-copy initialization. */ #define s2_stoken_stack_empty_m {0,0} /** Empty-initialized s2_stoken_stack structure, intended for copy initialization. */ extern const s2_stoken_stack s2_stoken_stack_empty; /** An "evaluation stack," a thin wrapper over two s2_stoken_stacks, intended to simplify swapping the stacks in and out of an s2_engine while parsing subscrips. */ struct s2_estack{ /** The value stack. */ s2_stoken_stack vals; /** The operator stack. */ s2_stoken_stack ops; }; /** Empty-initialized s2_estack structure, intended for const-copy initialization. */ #define s2_estack_empty_m {s2_stoken_stack_empty_m, s2_stoken_stack_empty_m} /** Empty-initialized s2_estack structure, intended for copy initialization. */ extern const s2_estack s2_estack_empty; /** A generic error code/message combination. Intended for reporting non-exception errors, possibly propagating them on their way to becoming exceptions. */ struct s2_error { /** Error code, preferably a CWAL_RC_xxx value. */ int code; /** Line-number of error, if relevant. 1-based, so use 0 as a sentry value. */ int line; /** Column position of error (if relevant). 0-based. */ int col; /** The error message content. */ cwal_buffer msg; /** Holds a script name associated with this error (if any). We use a buffer instead of a string because it might be re-set fairly often, and we can re-use the memory. */ cwal_buffer script; }; /** Empty-initialized s2_error structure, intended for const-copy initialization. */ #define s2_error_empty_m {0, 0, 0, cwal_buffer_empty_m, cwal_buffer_empty_m} /** Empty-initialized s2_error structure, intended for copy initialization. */ extern const s2_error s2_error_empty; /** @internal An internal helper type for swapping an s2_engine's sweep-guard state in and out at certain points. The docs for this struct assume only one context: that this is used embedded in an s2_engine struct. */ struct s2_sweep_guard { /** If greater than 0, s2_engine_sweep() will neither sweep nor vacuum. Reminder to self: we will never be able to use recursive sweep unless we set this at the scope level, as opposed to the s2_engine level. For that we would need our own Scope abstraction, e.g. s2_scope, which contains a cwal_scope, inside of s2_scope_push() and s2_scope_pop(). And then we'd need to hope that it doesn't misinteract with cwal-level code or addons which use cwal_scope_push/pop() directly. Hmmm. */ int sweep; /** If greater than 0, s2_engine_sweep() will not vacuum, but will fall back to sweep mode (if not disabled) instead. This HAS to be >0 if the client makes use of any non-script-visible values which are not otherwise vacuum-proofed and may be needed at a time when script code may trigger s2_engine_sweep() (see cwal_value_make_vacuum_proof()). */ int vacuum; }; #define s2_sweep_guard_empty_m {0,0} extern const s2_sweep_guard s2_sweep_guard_empty; /** An abstraction layer over cwal_scope so that we can store more per-scope metadata. */ struct s2_scope { cwal_scope scope; s2_sweep_guard sguard; s2_scope * parent; }; #define s2_scope_empty_m {cwal_scope_empty_m, s2_sweep_guard_empty_m, 0} extern const s2_scope s2_scope_empty; /** This class encapsulates a basic stack engine which uses the cwal_value type as its generic operand type. Each s2_engine must be initialized with a cwal_engine, which the s2_engine owns and uses for all memory management, as well as the core Value Type system. @see s2_engine_alloc() @see s2_engine_init() @see s2_engine_init2() @see s2_engine_finalize() */ struct s2_engine{ cwal_engine * e; void const * allocStamp; /** The stacks of operators and values. */ s2_estack st; /** A general-purpose buffer for internal (re)use. */ cwal_buffer buffer; /** For holding non-exception error state. */ s2_error err; /** When an engine is intialized, s2 pops cwal's implicit top scope from ths stack and pushes its own, using this instance to hold it. */ s2_scope topScope; /** The currently pushed s2 scope (s2_scope_push()). */ s2_scope * currentScope; /** If greater than 0, "skip-mode" must be honored by all evaluation code. Skip-mode basically means "consume as much as you normally would, but have (if possible) no side-effects while doing so." That allows us to consume tokens with or without actually evaluating the results as we go (the engine pretends to evaluate, but uses the 'undefined' value for everything, so it doesn't actually allocate any values). This is the basis of short-circuit evaluation. */ int skipLevel; /** Every this-many calls to s2_engine_sweep() should either sweep or vacuum. When 0, sweeping is disabled (generally not a good idea). The lowest-level evaluation routine disables sweeping during evaluation of an expression to keep the lifetimes of temporaries safe. s2_eval_ptoker(), a high-level eval routine, sweeps up after every expression, but only 1-in-skipInterval sweep-ups has an effect. Reminder to self: something to try: if s2_eval_expr_impl() uses an array to control lifetimes of temps (like s2_eval_ptoker() does, except that eval needs to keep more than one value at a time), we would almost not need sweeping at all, except to cleanup those pesky ignored return values, orphaned cycles, and such. That might also make a recursive sweep safe (recursive vacuum is theoretically not safe). A sweepInterval of 1 is very aggressive, but is recommended during testing/development because it triggers problems related to value lifetime mismanagement more quickly, in particular if vacuumInterval is also 1. Very basic tests on a large script with a milliseconds-precision and massif's instruction counter show only a relatively small impact, both in speed (no appreciable change) and total CPU instructions (under 0.5%), when comparing sweepInterval of 1 vs 7. The former used 90kb RAM and 215M CPU instructions, and the latter 92kb RAM and 214M instructions. i.e. there is (so far) no compelling argument for increasing this above 1. */ int sweepInterval; /** Every this-many sweep attempts will be replaced by a vaccuum instead if this->sguard->vacuum is 0. There is no single optimal value. 1 is aggressive (always vacuuming instead of sweeping, potentially very costly). In generic tests in th1ish, 3-5 seemed to be a good compromise, and then only 1 in 10 or 15 vacuum runs was cleaning up more than sweeping was, because vacuuming only does its real magic when there are orphaned cyclic structures laying around (which doesn't happen often). In scripts with short-lived scopes, a value of 0 here is fine because scope cleanup will also get those orphans, provided they're not propagated up out of the scope (via explicit propagation or containment in a higher-scoped container). */ int vacuumInterval; /** Keeps track of when to sweep - incremented once per call to s2_engine_sweep(). */ int sweepTick; /** Total number of s2_engine_sweep() calls which led to a sweep or vacuum. */ int sweepTotal; /** Not yet used to control when a "break" or "continue" is legal. */ int localLoopLevel; /** Some sort of container used by s2_stash_get() and s2_stash_set(). This is where we keep internally-allocated Values which must not be garbage-collected. This value is made vacuum-proof. */ cwal_value * stash; /** Internal holder for script function source strings. For keys we use Integer values and the values are the strings. This mechanism provides script functions with a lifetime-safe (and vacuum-safe) copy of their source code strings. It moves those strings into the top scope, but the function state holds the key for the string, and removes that key when the function is finalized, so the strings have essentially normal lifetimes and won't be abandoned in the global scope. There is a corner case there involving Function.sourceCode()'s returned string, where a source string could potentially live longer than it should. */ cwal_hash * funcStash; /** Internal hash ID incrementer for s2_func_state. We need an unsigned type for pedantic safety in the case of an overflow. */ cwal_size_t funcId; /** Not yet properly/completely used, might not even work how i'd like. Intended to allow us to retain more information on the way through pseudo-dot-operators, so that they can behave "just like" the dot operator. But... it doesn't look like it's going to be that straightforward. */ int dotOpId; /** The dot operator sets this to the current "this" value (its LHS). */ cwal_value * dotOpLhs; /** Used to differentiate between prop access wrt to types which should resp. should bind as 'this' in calls to props resolved as functions. Namely: obj.() // binds 'this' array.() // does not hash # () // does not But all three set dotOpLhs and dotOpKey. */ cwal_value * dotOpSelf; /** Set by the dot operator, to its RHS (key) part. Used by assignment and/or the unset op to get access to the property. */ cwal_value * dotOpKey; /** Used to communicate the "argv" value between the interpreter parts which call functions and the pre-call hook called by cwal. This is/will be used to avoid having to create an array in some cases. */ cwal_array * callArgV; /** Gets set by the stack layer when an operator (A) triggers an error and (B) has its srcPos set. Used to communicate operator-triggered error location information back to the parser layer. */ char const * opErrPos; /** Used for collecting error location info during the parse/eval phase of the current script, for places where we don't have a direct local handle to the script. */ s2_ptoker const * currentScript; /** An experiment. */ s2_func_state const * currentScriptFunc; struct { /** Stored as an optimization for sizing the target array when collecting stack traces. */ cwal_size_t count; /** Head of the current stack trace. */ s2_strace_entry * head; /** tail of the current stack trace. */ s2_strace_entry * tail; } strace; /** Holds the current sweep-mode state. Intended as a simplification for some internal code. This is a pointer to currentScope->sguard, managed by s2_scope_push() and s2_scope_pop(). */ s2_sweep_guard * sguard; struct { /** Recycle bin for stokens. */ s2_stoken_stack stok; /** The max number of items to keep in the recycler stack. */ int maxSTokens; /** Recycle bin for script-function state. */ struct { s2_func_state * head; int count; int max; } scriptFuncs; } recycler; /** Does not yet hold cwal_outputer instances for use by the s2_ob_push() family of functions. */ cwal_list ob; /** Holds DLL/module handles so that the interpreter can close them when it cleans up. */ cwal_list modules; struct { /** If greater than 0 then some debug/tracing output is generated. Use higher levels for more output. */ int traceStack; /** >0 means to trace PASSED assertions to cwal_output(). >1 means to also trace FAILED assertions. */ int traceAssertions; /** If true, sweeping keeps metrics (==performance hit) and outputs them to stdout. Only for debugging, of course. */ int traceSweeps; /** To avoid that certain constellations miss an "interrupt" request, we need a flag for this outside of the err object. */ int interrupted; } flags; struct { /** Total number of s2_stoken_alloc() calls for this s2_engine instance. */ unsigned int tokenRequests; /** Total number of calls into cwal_malloc() to allocate an s2_stoken. */ unsigned int tokenAllocs; /** Number of tokens currently allocated but not yet freed nor recycled. */ unsigned int liveTokenCount; /** Maximum number of s2_stokens alive throughout the life of this object. */ unsigned int peakLiveTokenCount; /** Number of script-side assert()ions which have been run. */ unsigned int assertionCount; /** Current sub-expression (e.g. parens/brace group) parsing leve. */ int subexpDepth; /** The highest-ever sub-expression depth. */ int peakSubexpDepth; /** Maximum number of cwal_scope levels deep concurrently. */ int maxScopeDepth; /** Number of script-side functions created. */ unsigned int funcStateRequests; /** Number of script-side functions for which we had to allocate an s2_func_state instance. */ unsigned int funcStateAllocs; /** Total memory allocated for the internal state for script-side functions, NOT including their sourceInfo bits, as those are already recorded in the cwal metrics. */ unsigned int funcStateMemory; /** The number of calls to s2_next_token(). */ unsigned int nextTokenCalls; } metrics; }; /** Empty-initialized s2_engine structure, intended for const-copy initialization. */ #define s2_engine_empty_m { \ 0/*e*/, \ 0/*allocStamp*/, \ s2_estack_empty_m/*st*/, \ cwal_buffer_empty_m/*buffer*/, \ s2_error_empty_m/*err*/, \ s2_scope_empty_m/*topScope*/,\ 0/*currentScope*/,\ 0/*skipLevel*/, \ 1/*sweepInterval*/, \ 5/*vacuumInterval*/, \ 0/*sweepTick*/, \ 0/*sweepTotal*/, \ 0/*localLoopLevel*/, \ 0/*stash*/, \ 0/*funcStash*/,\ 0/*funcId*/,\ 0/*dotOpId*/, \ 0/*dotOpLhs*/, \ 0/*dotOpSelf*/, \ 0/*dotOpKey*/, \ 0/*callArgV*/, \ 0/*opErrPos*/, \ 0/*currentScript*/, \ 0/*currentScriptFunc*/, \ {/*strace*/0/*count*/, 0/*head*/,0/*tail*/}, \ 0/*sguard*/, \ {/*recycler*/ \ s2_stoken_stack_empty_m/*stok*/, \ 50 /*maxSTokens*/, \ {/*scriptFuncs*/ 0/*head*/, 0/*count*/, 10 /*max*/ } \ }, \ cwal_list_empty_m/*ob*/, \ cwal_list_empty_m/*modules*/,\ {/*flags*/ \ 0/*traceStack*/, \ 0/*traceAssertions*/, \ 0/*traceSweeps*/, \ 0/*interrupted*/ \ }, \ {/*metrics*/ \ 0/*tokenRequests*/, \ 0/*tokenAllocs*/, \ 0/*liveTokenCount*/, \ 0/*peakLiveTokenCount*/, \ 0/*assertionCount*/, \ 0/*subexpDepth*/, \ 0/*peakSubexpDepth*/, \ 0/*funcStateRequests*/, \ 0/*funcStateAllocs*/, \ 0/*funcStateMemory*/ \ } \ } /** Empty-initialized s2_engine structure, intended for copy initialization. */ extern const s2_engine s2_engine_empty; /** If ttype (a s2_token_types value) represents a known Operator then that operator's shared/static/const instance is returned, otherwise NULL is returned. */ s2_op const * s2_ttype_op( int ttype ); /** Equivalent to s2_stoken_op(t->type). */ s2_op const * s2_stoken_op( s2_stoken const * t ); int s2_op_is_math( s2_op const * op ); int s2_op_is_expr_border( s2_op const * op ); int s2_op_is_unary_prefix( s2_op const * op ); /** Equivalent to s2_ttype_short_circuits(op ? op->id : 0). */ int s2_op_short_circuits( s2_op const * op ); /** Allocates a new token using se's cwal-level allocator. The value must eventually be cleaned up using s2_stoken_free(). Returns 0 on allocation error (e.g. if !se or !se->e). */ s2_stoken * s2_stoken_alloc( s2_engine * se ); /** Convenience form of s2_stoken_alloc() which sets a token's type and value. type may be any value and v may be NULL. */ s2_stoken * s2_stoken_alloc2( s2_engine * se, int type, cwal_value * v ); /** "frees" the given token. If allowRecycle is false, or if se's token recycling feature is disabled or has reached its capacity, then t is immediate freed using se's allocator, otherwise t will be placed into a recycling bin for later re-use via s2_stoken_alloc(). */ void s2_stoken_free( s2_engine * se, s2_stoken * t, char allowRecycle ); /** Pushes t to the given stack and transfers ownership of t to ts. */ void s2_stoken_stack_push( s2_stoken_stack * ts, s2_stoken * t ); /** If ts has any tokens, the top-most one is removed and that token is returned. Ownership of the returned value is transfered to the caller. Returns 0 on error (!ts or ts is empty). */ s2_stoken * s2_stoken_stack_pop( s2_stoken_stack * ts ); /** Frees all entries owned by ts by popping each one and passing it to s2_stoken_free(se, theToken, allowRecycle). See s2_stoken_free() for the semantics of the se and allowRecycle parameters. */ void s2_stoken_stack_clear( s2_engine * se, s2_stoken_stack * ts, char allowRecycle ); /** Works like s2_stoken_stack_clear(), but operates on both stacks owned by st. */ void s2_estack_clear( s2_engine * e, s2_estack * st, char allowRecycle ); /** Swaps the contents of lhs and rhs (neither may be NULL). */ void s2_estack_swap( s2_estack * lhs, s2_estack * rhs ); /** Initializes se and transfers ownership of e to it. se must be a cleanly-initialized s2_engine instance, allocated via s2_engine_alloc() or stack-allocated and copy-initialized from s2_engine_empty or s2_engine_empty_m (same contents, slightly different usage contexts). e must be a freshly-cwal_engine_init() instance and the client MUST NOT install any new functionality to it before this call because: in order to gain control of all the scopes, s2 must destroy all of e's scopes (it pushes a top scope upon initialization) before continuing with its own initialization. Returns 0 on success, a CWAL_RC_xxx code on error, the only potential ones at this phase of the setup being blatant misuse (passing in a NULL pointer) an allocation error. After returning: A) Ownership of se is not modified. B) If this function fails because either of the arguments is 0, ownership of e is not modified, otherwise ownership of e is transferred to se regardless of success or failure (because there is no "detach and recover" strategy for any errors after the first couple allocations). C) The caller must pass eventually se to s2_engine_finalize() to clean it up, regardless of success or error. */ int s2_engine_init( s2_engine * se, cwal_engine * e ); /** Allocates a new s2_engine instance using e's allocator. Returns 0 on error. */ s2_engine * s2_engine_alloc( cwal_engine * e ); /** Frees up all resources owned by se. If se was allocated using s2_engine_alloc() then se is also freed, otherwise it is assumed to have been allocated by the caller (possibly on the stack) and is cleaned up but not freed. */ void s2_engine_finalize( s2_engine * se ); /** Pushes the given token to se's value stack and transfers its ownership to se. The only error condition is if either argument is 0 or points to invalid memory. */ void s2_engine_push_token( s2_engine * se, s2_stoken * t ); /** The operator-stack counterpart of s2_engine_push_token(). */ void s2_engine_push_op( s2_engine * se, s2_stoken * t ); /** Creates a new token with the given type, appends it to se, and returns it. Return 0 on allocation error. */ s2_stoken * s2_engine_push_ttype( s2_engine * se, int i ); /** Pushes a new token of type S2_T_Value to se, and assigns v to that token's value. It does not change the reference count or ownership of v. Returns 0 on allocation error or if se or v are 0. On success it returns the token pushed onto the stack. */ s2_stoken * s2_engine_push_val( s2_engine * se, cwal_value * v ); /** Creates a new cwal_value value of type integer and pushes it onto se as described for s2_engine_push_val(). Returns 0 on allocation error, or the pushed token on success. */ s2_stoken * s2_engine_push_int( s2_engine * se, cwal_int_t i ); /** Pushes a new token onto VALUE stack, with the given type and the given value (which may be NULL but likely should not be). */ s2_stoken * s2_engine_push_tv( s2_engine * se, int ttype, cwal_value * v ); /** If se's stack contains any tokens, the top-most token is removed. If returnEntry is false then the token is placed in se's recycle bin and 0 is returned. If returnEntry is true, the token is returned to the caller, as is ownership of that token. If se has no stack entries, 0 is returned. */ s2_stoken * s2_engine_pop_token( s2_engine * se, char returnEntry ); /** Similar to s2_engine_pop_token(), this destroys the top-most token and returns its value (if any), transfering ownership (or stewardship) of that value to the caller. */ cwal_value * s2_engine_pop_value( s2_engine * se ); /** The operator-stack counterpart of s2_engine_pop_token(). */ s2_stoken * s2_engine_pop_op( s2_engine * se, char returnEntry ); /** Returns the top-most token in se's token stack, without modifying the stack. Returns 0 if se's token stack is empty. */ s2_stoken * s2_engine_peek_token( s2_engine * se ); /** Equivalent to s2_engine_peek_token(se)->value, but returns 0 (instead of segfaulting) if the value stack is empty. Does not modify ownership of the returned value. */ cwal_value * s2_engine_peek_value( s2_engine * se ); /** Swaps the contents of se->st with st. Neither argument may be NULL. */ void s2_engine_stack_swap( s2_engine * se, s2_estack * st ); /** Clears all entries from se->st, recycling them if possible, freeing them if not. */ void s2_engine_reset_stack( s2_engine * se ); /** Might or might not cwal_engine_sweep() or cwal_engine_vacuum() on se->e, depending on the state of se's various counters and guards. In any case, this function will increase a counter unless sweeping is disabled completely. */ void s2_engine_sweep( s2_engine * se ); /** Runs cwal_engine_vacuum() on se->e. */ void s2_engine_vacuum( s2_engine * se ); /** The op-stack counterpart of s2_engine_peek_token(). */ s2_stoken * s2_engine_peek_op( s2_engine * se ); /** Pushes t onto either the token stack or (if it represents an operator) the operator stack. @see s2_ttype_op() @see s2_stoken_op() */ void s2_engine_push( s2_engine * se, s2_stoken * t ); /** Processes the top-most operator on the stack and pushes the result value back onto the stack. A small number of "marker" operators do not generate a result, and do not push a result onto the stack, but leave the value stack as they found it. It is not yet clear whether or not this API needs to provide a way for clients to know about that. Currently those operators are handled directly by the higher-level parser code or are handled as part of another operation (e.g. the S2_T_MarkVariadicStart token marks the end of N-ary call ops). Returns 0 on success, non-0 on error. On error, the stack state is not well-defined unless CWAL_RC_TYPE is returned (indicating that the top token is not an operator). */ int s2_process_top( s2_engine * se ); /** A lower-level form of s2_process_top(), this variant does not modify the operator stack. It is assumed that op is one which was just popped from it by the caller, or "would have" become the top of the stack but the push is being elided as an optimization. It is up to the caller to ensure that se's stack is set up appropriately for the given up before calling this. Returns 0 on success, a non-0 CWAL_RC_xxx value on error. */ int s2_process_op( s2_engine * se, s2_op const * op ); /** Equivalent to s2_process_op(se, s2_ttype_op(ttype)), except that it returns CWAL_RC_TYPE if ttype is not an operator. */ int s2_process_op_type( s2_engine * se, int ttype ); /** Clears any pending tokens from se's stack(s). */ void s2_engine_reset_stack( s2_engine * se ); /** Sets err's state to the given code/string combination, using cwal_buffer_printf() formatting. If fmt is 0 or !*fmt then any existing error message is reset. As a special case, if code==CWAL_RC_OOM, it behaves as if fmt is 0 to avoid allocating any new memory. The se argument is required for its allocator - this function does not directly modify se's state, only err's. This is intended for propagating errors from the low-level APIs up to the script-level APIs for potential conversion to an exception. On success it returns the 3nd argument (NOT 0!). On error it returns some other non-0 code. */ int s2_error_setv( s2_engine * se, s2_error * err, int code, char const * fmt, va_list ); /** Elipses counterpart of s2_error_setv(). */ int s2_error_set( s2_engine * se, s2_error * err, int code, char const * fmt, ... ); /** Resets any any state in se, but keeps any memory in place for re-use. */ void s2_error_reset( s2_error * err ); /** Frees all memory owned by err, but does not free err. The se argument is required for its underlying allocator. */ void s2_error_clear( s2_engine * se, s2_error * err ); /** If err->code is not 0, *msg and *msgLen (if they are not NULL) are assigned to the message string and its length, respectively. It is legal for the returned *msg value to be NULL, which simply indicates that no error string was provided when the error state was set. Returns 0 if se has no error state is set. */ int s2_error_get( s2_error const * err, char const ** msg, cwal_size_t * msgLen ); /** Takes err's error string and uses it to create a new Exception value. If !err, se->err is used. On success, it returns a new Exception with the err->code error code and a "message" property containing err's error string. If err->msg is empty, then a generic message is created based on err->code. If scriptName is not 0 and (*scriptName) then the exception gets a "script" string property with that value. If (line>0) then the exception gets line/column properties holding the line/col values. Returns 0 on error (OOM). This does not set se's exception state, but it does clear err's error state (so that we can give the string directly to cwal instead of copying it). */ cwal_value * s2_error_exception( s2_engine * se, s2_error * err, char const * scriptName, int line, int col ); /** Converts err (or se->err, if err is NULL) to an Exception value using s2_error_exception() (see that func for important details) then set's se's exception state to that exception. Like cwal_exception_set(), this function returns CWAL_RC_EXCEPTION on success or some other non-0 code if creation of the exception fails. If line<=0 then err->line and err->col are used in place of the given line/column parameters. If script is 0 and err->script is populated, that value is used instead. */ int s2_throw_err( s2_engine * se, s2_error * err, char const * script, int line, int col ); /** Convenience function for cwal_callback_f() implementations, which throws an exception in args->engine. Returns as for cwal_exception_set() and friends (i.e. CWAL_RC_EXCEPTION on success!). */ int s2_cb_throw( cwal_callback_args const * args, int code, char const * fmt, ... ); /** Sets se->e's exception state and returns CWAL_RC_EXCEPTION. This is a convenience form of cwal_exception_setf(). */ int s2_throw( s2_engine * se, int code, char const * fmt, ... ); /** Sets se->err's state, as per s2_error_setv(). This does not set or modify the _exception_ state. */ int s2_engine_err_setv( s2_engine * se, int code, char const * fmt, va_list ); /** Elipses counterpart of s2_engine_err_setv(). */ int s2_engine_err_set( s2_engine * se, int code, char const * fmt, ... ); /** If se has error state, that error code is returned, else if an exception is pending, CWAL_RC_EXCEPTION is returned, else 0 is returned. */ int s2_engine_err_has( s2_engine const * se ); /** Resets se->err's state, as per s2_error_reset(), plus clears any interruption flag. */ void s2_engine_err_reset( s2_engine * se ); /** Clears se->err's state, as per s2_error_clear(), plus clears any interruption flag. */ void s2_engine_err_clear( s2_engine * se ); /** Returns se's error state, as per s2_error_get(), with one special case: if se has been "interrupted" via s2_interrupt(), CWAL_RC_INTERRUPTED is returned, possibly without a message (*msg and *msgLen will be be set to NULL if the interruption does not include a message). */ int s2_engine_err_get( s2_engine const * se, char const ** msg, cwal_size_t * msgLen ); /** A utility function implementing unary and binary addition and subtraction of numbers (in the form of cwal_values). This routine does not take overloading into account. To do binary operations: - Both lhs and rhs must be valid values. They will be converted to a number if needed. lhs is the left-hand-side argument and rhs is the right-hand-side. - Set doAdd to true to perform addition, 0 to perform subtraction. For unary operations: - As for binary ops, but lhs must be NULL. The operation is applied solely to rhs. The result value is assigned to *rv, and rv may not be NULL. Returns 0 on success, non-0 on misuse or allocation error. If lhs (binary) or rhs (unary) is a double value then the result (except for cases listed below) will be a double, otherwise it will be an integer. This routine makes some optimizations to avoid allocating memory when it knows it does not have to. These optimizations are internal details, but may change the expected type of the result, and so are listed here: - If !lhs and doAdd is true (i.e. unary addition), then the result is rhs. - Binary ops: if either argument is 0 resp. 0.0 then the result is the other argument. i.e. A+0===A and 0+A===A. Returns 0 on success, non-0 on error. Errors can come in the form of CWAL_RC_OOM or, for overloaded operators, any sort of error a script-side function can cause. On non-OOM error, se's error state will be update or an exception may be propagated. */ int s2_values_addsub( s2_engine * se, char doAdd, cwal_value * lhs, cwal_value * rhs, cwal_value **rv ); /** The multiply/divide/modulo counterpart of s2_values_addsub(), this routine applies one of those three operations to its (lhs, rhs) arguments and assigns the result to *rv. This routine does not take overloading into account. If mode is negative, the division operation is applied, If mode is 0, modulo is applies. If mode is greater than 0 then multiplication is applied. Returns 0 on success, a non-0 CWAL_RC_xxx value on error. Returns CWAL_SCR_DIV_BY_ZERO for either division or modulo by 0. On error, se's error state will be update or (for overloaded operators) an exception may be propagated. This routine applies several optimizations which might change the expected result type: Modulo: - Always has an integer result except on modulo-by-0. Multiplication: - (0 * 1) === 0 - (0.0 * 1) === 0.0 - (N * 1) === N - If either the lhs or rhs is a double then the result will be a double unless a more specific optimization applies. */ int s2_values_multdivmod( s2_engine * se, int mode, cwal_value * lhs, cwal_value * rhs, cwal_value **rv ); /** Performs bitwise and bitshift operations on one or two values. This routine does not take overloading into account. This function has two modes: Unary: op must be S2_T_OpNegateBitwise and lhs must be NULL. The bitwise negation operation is applied to rhs and the result is stored in *rv. Binary: op must be one of the many S2_T_OpXXXAssign, S2_T_OpXXXAssign3, S2_T_Op{AndBitwise,OrBitwise,XOr,ShiftLeft,ShiftRight}. The binary operation is applied to lhs and rhs and the result is stored in *rv. The resulting value is always of type CWAL_TYPE_INTEGER, but it may be optimized away to the lhs or rhs instance (as opposed to creating a new one). Returns 0 on success. On error *rv is not modified. */ int s2_values_bitwiseshift( s2_engine * se, int op, cwal_value * lhs, cwal_value * rhs, cwal_value ** rv ); /** @internal Internal debugging tool which dumps out info about v (may be NULL), with an optional descriptive message. Expects func and line to be the __FUNCTION__ resp. __LINE__ macros. Use the s2_dump_val() macro to simplify that. Reminder: v cannot be const b/c some types go through JSON output, which requires non-const so that it can catch cycles. */ void s2_dump_value( cwal_value * v, char const * msg, char const * func, int line ); /** @internal Equivalent to s2_dump_value(V, MSG, __FUNCTION__, __LINE__). */ #define s2_dump_val(V, MSG) s2_dump_value((V), (MSG), __FUNCTION__, __LINE__) /** Prints out a cwal_printf()-style message to stderr and exit()s the application. Does not return. Intended for use in place of assert() in test code. The library does not use this internally. */ void s2_fatal( int code, char const * fmt, ... ); /** A cwal_callback_f() implementation performing basic output-to-console. All arguments are output with a single space between them and a newline at the end. Output goes to cwal_output(), so it might not go to the console. */ int s2_cb_print( cwal_callback_args const * args, cwal_value **rv ); /** A cwal_callback_f() implementation which sends a flush request to args->engine's configured output channel. Returns 0 on success, throws on error. */ int s2_cb_flush( cwal_callback_args const * args, cwal_value **rv ); /** Works just like s2_cb_print() except that it does not add extra whitespace around its arguments, nor a newline at the end of the line. */ int s2_cb_write( cwal_callback_args const * args, cwal_value **rv ); /** A cwal_callback_f() which Implements a simple script file execution method. Script usage: thisFunc(filename) Runs the script using s2_eval_filename() and assigns the result of the last expression to *rv (or the undefined value if the script evaluates to NULL (which can happen for a number of reason)). Requires that s2_engine_from_args() returns non-0 (i.e., that args->engine was initialized along with a s2_engine instance). */ int s2_cb_import_script(cwal_callback_args const * args, cwal_value ** rv); /** Returns the prototype object for just about everything. That instance gets stashed away in se. Ownership of the returned pointer is unchanged. The caller MUST NOT unreference it (cwal_value_unref() or cwal_value_unhand()) unless he explicitly obtains a reference. */ cwal_value * s2_prototype_object( s2_engine * se ); cwal_value * s2_prototype_function( s2_engine * se ); cwal_value * s2_prototype_array( s2_engine * se ); cwal_value * s2_prototype_exception( s2_engine * se ); cwal_value * s2_prototype_hash( s2_engine * se ); cwal_value * s2_prototype_string( s2_engine * se ); cwal_value * s2_prototype_double( s2_engine * se ); cwal_value * s2_prototype_integer( s2_engine * se ); cwal_value * s2_prototype_buffer( s2_engine * se ); /** Adds a persistent value to the interpreter. These are stored, for lifetime purposes, under the top-most scope with one reference to it, and they are not visible to script code. They will be made vacuum-proof so long as they are in the stash. This is where clients might store, e.g., references to their custom native-side prototype objects (optionally, they may set them as normal variables). key must be NUL-terminated. Returns 0 on success. */ int s2_stash_set( s2_engine * se, char const * key, cwal_value * v ); /** Fetches a value set with s2_stash_set(). Returns NULL if not found, if !ie, or if (!key || !*key). key must be NUL-terminated. */ cwal_value * s2_stash_get( s2_engine * se, char const * key ); /** Wrapper around cwal_var_decl_v(). Notable error codes: CWAL_RC_ALREADY_EXISTS = key already exists */ int s2_var_decl_v( s2_engine * se, cwal_value * key, cwal_value * v, uint16_t flags ); /** The C-string counterpart of s2_var_decl(). */ int s2_var_decl( s2_engine * se, char const * name, cwal_size_t nameLen, cwal_value * v, uint16_t flags ); /** Searches for a named variable in the given interpreter engine. scopeDepth is as described for cwal_scope_search_v() (normally 0 or -1 would be correct). If keyLen is 0 and key is not NULL then the equivalent of strlen(key) is used to determine its length. Returns the found value on success, NULL if no entry is found or if any arguments are invalid (!ie, !key). */ cwal_value * s2_var_get( s2_engine * se, int scopeDepth, char const * key, cwal_size_t keyLen ); /** Functionally identical to s2_var_get(), but takes its key as a cwal_value. */ cwal_value * s2_var_get_v( s2_engine * se, int scopeDepth, cwal_value * key ); /** If self is not NULL then this performs a lookup in the current scope for a variable with the given key. If self is not NULL then it performs a property search on self. On success the result is stored in *rv and 0 is returned. See s2_set_v() for how Arrays are (sometimes) handled differently. If no entry is found, 0 is returned, but *rv is set to 0. @see s2_set_v() */ int s2_get_v( s2_engine * se, cwal_value * self, cwal_value * key, cwal_value ** rv ); /** C-string variant of th1ish_get_v(). */ int s2_get( s2_engine * se, cwal_value * self, char const * key, cwal_size_t keyLen, cwal_value ** rv ); /** Sets a named variable in the given interpreter engine. scopeDepth is as described for cwal_scope_chain_set_v() (normally 0 would be correct). Use a value of NULL to unset an entry. Returns 0 on success, CWAL_RC_MISUSE if !ie or !key, and potentially other internal/low-level error codes, e.g. CWAL_RC_OOM if allocation of space for the property fails. */ int s2_var_set( s2_engine * se, int scopeDepth, char const * key, cwal_size_t keyLen, cwal_value * v ); /** Functionally identical to s2_var_set(), but takes its key as a cwal_value. */ int s2_var_set_v( s2_engine * se, int scopeDepth, cwal_value * key, cwal_value * v ); /** If self is NULL then this behaves as a proxy for s2_var_set_v(), otherwise... If self is-a Array and key is-a Integer then cwal_array_set() is used to set the property, else cwal_prop_set_v(self,key,v) is used. Returns the result of the underlying setter call. If !v then this is an "unset" operation. In that case, if !self and key is not found in the scope chain, CWAL_RC_NOT_FOUND is returned, but it can normally be ignored as a non-error. @see s2_get_v() */ int s2_set_v( s2_engine * se, cwal_value * self, cwal_value * key, cwal_value * v ); /** C-string variant of th1ish_set_v(). */ int s2_set( s2_engine * se, cwal_value * self, char const * key, cwal_size_t keyLen, cwal_value * v ); /** Installs a core set of Value prototypes into se. Returns 0 on success. */ int s2_install_core_prototypes(s2_engine * se); /** @internal Returns true if v is of a type which may preceed a dot operator, else false. */ char s2_value_may_preceed_dot(s2_engine * se, cwal_value * v); enum s2_next_token_flags { /** Specifies that s2_next_token() should treat EOL tokens as significant (non-junk) when looking for the next token. */ S2_NEXT_NO_SKIP_EOL = 0x01 }; /** A form for s2_ptoker_next() which is specific to s2_engine evaluation, performing various token skipping and post-processing. It searches for the next non-junk token, then perhaps post-processes it before returning. If tgt is NULL then the token is consumed as normal and st->token contains its state after returning. If tgt is not-NULL then the this works like a lookahead: the consumed token is copied to *tgt and st's token/putback state are restored to their pre-call state (regardless of success or failure). The flags parameter must be 0 or a bitmask of s2_next_token_flags values. Note that by default newlines are treated as junk tokens and ignored. Returns 0 on success. On error the input must be considered unparsable/unrecoverable unless st is a sub-parser of a compound token in a larger parser (e.g. a (list) or {script} or [braces]), in which case parsing may continue outside of the subparser (because we know, at that point, that the group tokenizes as an atomic token). This function does not throw exceptions in se, but instead updates se's error state on error. */ int s2_next_token( s2_engine * se, s2_ptoker * st, int flags, s2_ptoken * tgt ); /** Uses s2_next_token(se,pr,nextFlags,...) to peek at the next token in pr. If the next token has the type ttype, this function returns ttype. If consumeOnMatch is true AND the next token matches, pr->token is set to the consumed token. */ int s2_ptoker_next_is_ttype( s2_engine * se, s2_ptoker * pr, int nextFlags, int ttype, char consumeOnMatch ); /** Creates a value for a token. This basically just takes the t->src and makes a string from it, but has different handling for certain token types. On success *rv contains the new string. Returns 0 on success, CWAL_RC_OOM on OOM, else it should not fail. */ int s2_ptoken_create_value( s2_engine * se, s2_ptoken const * t, cwal_value ** rv ); /** Sets se's error state, as for s2_engine_err_set(), and returns that call's result value. If fmt is NULL or !*fm, st->errMsg is used (if set) as the message. If st->errPos is set to a position within [st->begin,st->end) then line/column information, relative to st->begin, is appended to the message. Sometimes it is necessary for callers to set or tweak st->errPos explicitly before calling this. Returns the error code, not 0, on success! Returns some other non-0 code on error. */ int s2_err_ptoker( s2_engine * se, s2_ptoker const * st, int code, char const * fmt, ... ); /** Similar to s2_err_ptoker(), but clears se's error state and setting se's Exception state. */ int s2_throw_ptoker( s2_engine * se, s2_ptoker const * pr, int code, char const * fmt, ... ); /** Throws se's error state (which must be non-OK), using pr (if not NULL, else se->currentScript) as the source for error location information. It tries to determine the position of the error based on se's and pr's state at the time this is called. It is particularly intended to be called from code which: a) is calling non-throwing, error-producing code (e.g. op-stack processing). b) wants to convert those errors to script-side exceptions. */ int s2_throw_err_ptoker( s2_engine * se, s2_ptoker const * pr ); /** Throws a value as an exception in se. If pr is not NULL, it is used for collecting error location information, which is set as properties of the exception. If v is-a exception, it is used as the exception and errCode is ignored, otherwise v is used as the "message" property of the new exception, and errCode its "code" property. */ int s2_throw_value( s2_engine * se, s2_ptoker const * pr, int errCode, cwal_value *v ); /** @internal Post-processor for converting a single "opener" token into a larger token containing the whole braced group, writing the resulting token to the given output token pointer. Requires st->token to be a S2_T_SquigglyOpen, S2_T_BraceOpen, or S2_T_ParenOpen token. It "slurps" the content part, from the starting brace up to (and include) the ending brace. It uses s2_next_token() to parse the group, meaning that it must contain tokenizable code. We cannot simply do a byte-scan through it because doing so would exclude any constructs (e.g. strings) which might themselves contain tokens which would look like closing tokens here. If !out, then st->token is used as the output destination. If out is not NULL then after returning, st->token and st->pbToken will be restored to their pre-call state (regardless of error or success). Returns 0 on success. On error it will set se's error state. On success: - tgt->ttype is set to one of: S2_T_SquigglyString, S2_T_ParenGroup, S2_T_BraceGroup. - [out->begin, out->end) will point to the whole token, including its leading and trailing opener/closer tokens. That range will have a length of at least 2 (one each for the opening and closing token). [out->adjBegin, out->adjEnd) will point to the range encompassing the body of the open/close block, stripped of any leading or trailing whitespaces. That range may be empty. */ int s2_slurp_braces( s2_engine *se, s2_ptoker * st, s2_ptoken * out ); /** @internal Post-processor for S2_T_HeredocStart tokens. se is the interpreter, st is the current tokenizer state. tgt is where the results are written. If tgt is NULL, st->token is used. Requires that st->token be a S2_T_HeredocStart token. This function scans st for the opening and closing heredoc tokens. On error a non-0 CWAL_RC_xxx value is returned and se's error state will be updated. On error st->token is in an unspecified state. On success tgt->ttype is set to S2_T_SquigglyString and [out->begin, out->end) will point to the whole token proper, including its leading and trailing opener/closer tokens. That range will have a length of at least 2 (one each for the opening and closing token). [out->adjBegin, out->adjEnd) will point to the range encompassing the body of the open/close block, stripped of any leading or trailing spaces, as specified below. That range may be empty. Returns 0 on success and sets se's error state on error. Syntax rules: Assuming that the S2_T_HeredocStart token is '<<<', heredocs can be constructed as follows: <<token between calls. If rv is not NULL then on success the result of the expression is written to *rv. Lifetime/ownership of the expression is not modified from its original source (which is indeterminate at this level), and the client must take a reference if needed. If rv is not NULL and the expression does not generate a value (e.g. an empty expression or EOF), *rv is set to 0. On any sort of error, *rv is not modified. In this case, any temporary values created during expression parsing are left to cwal's GC to clean up. Sweeping is disabled while an expression is pending, to avoid that any values used in the expression can live as long as they need to without requiring explicit references everywhere. If flags contains S2_EVAL_SKIP then this function behaves slightly differently: it parses the expression and runs it through the normal evaluation channels, but it does so with "as few side-effects as possible," meaning it can be used to skip over expressions without (in effect) evaluating them. When running in "skip mode," all operations are aware that they should perform no real work (e.g. not allocating any new values), and should instead simply consume all their inputs without doing anything significant. This allows us to pseudo-evaluate an expression to find out if it could be evaluated. In skip mode, if rv is not NULL then any result value written to *rv "should" (by library convention) be 0 (for an empty expression) or cwal_value_undefined(). Achtung: if se->skipLevel is positive then this function always behaves as if S2_EVAL_SKIP is set, whether it is or not. All this flag does is temporarily increments se->skipLevel. Returns 0 on success or any number of CWAL_RC_xxx values on error. On error, se's error state is updated and/or a cwal-level exception is thrown (depending on exactly what type of error and when/where it happened). On success st->token holds the token which caused expression tokenization to terminate. On non-error, if st->token is not at its EOF, evaluation may continue by calling this again. When running in consuming mode (i.e. not using S2_EVAL_NO_CONSUME), then this routine sets st's putback token to the pre-call st->token (i.e. the start of the expression). That means that a s2_ptoker_putback() will put back the whole expression. Upon returning (regardless of success or error), st.capture will point to the range of bytes captured (or partially captured before an error) by this expression. If st->token is an EOF of end-of-expression token after this is called, *rv might still be non-0, indicating the expression ended at an EOF/EOX. This is unfortunate (leads to more work after calling this), but true. Before evaluation, se's current eval stack is moved aside, and it is restored before returning. This means that calls to this function have no relationship with one another vis-a-vis the stack machine. Any such relationships must be built up in downstream code, e.g. by calling this twice and using their combined result values. This property allows the engine to recover from syntax errors at the expression boundary level without the stack manipulation code getting out of hand. It also means that subexpressions cannot corrupt the stack parts used by the parent (or LHS) expression(s). Garbage collection: this routine cannot safely sweep up while it is running (and disables sweep mode for the duration of the expression, in case a subexpression triggers a sweep). s2_eval_ptoker() and friends can, though. Any temporaries created in the current scope by this routine may be swept up after it is called, provided the client has references in place wherever he needs them (namely, on *rv). If the S2_EVAL_PUSH_SCOPE flag is used, sweepup is not necessary because only *rv will survive past the pushed scope. That said, pushing a scope for a single expression is just a tad bit of overkill, and not really recommended. */ int s2_eval_expr( s2_engine * se, s2_ptoker * st, int flags, cwal_value ** rv); /** Evaluates all expressions (iteratively) in the s2 script code wrapped by pt. If rv is NULL then any result value from the parsed expressions is ignored. If rv is not NULL then the final result of the script is stored in *rv. Its ownership is unspecified - it might be a new temporary awaiting a reference (or to be discarded) or it might be a long-lived value which made its way back from the global scope. We just can't know at this point. What the means is: if the caller needs to use *rv, he needs to do so immediately (before the next sweep-up in the current scope or the current scope ending). He may obtain a reference in "any of the usual ways." Returns 0 on success, a CWAL_RC_xxx value on error. On error, se's error state and/or se->e's exception state will be set (depending on what caused the error). Garbage collection: while iterating over expressions, this routine briefly holds a reference to the pending result value, and sweeps up temporaries using s2_engine_sweep() (meaning that it may or may not periodically clean up temporaries). The temporary reference to *rv is released (without destroying *rv) before this function returns, meaning that *rv may have been returned to a probationary (temporary) state by this call. If the caller needs to ensure its safety vis-a-vis sweepup, he must obtain a reference to it. If clients are holding temporaries in the current scope, they need to push a cwal scope before running this, and pop that scope afterwards, upscoping *rv to the previous if necessary (see cwal_value_rescope()). Nuances: - See s2_eval_expr() for lots more details about the parsing and evaluation process. - If pr->name is set them it is used as the name of the script, otherwise a name will be derived from pr->parent (if set), recursively. - *rv (if not 0) may be assigned to 0 even if the script succeeds. This means either an empty script or a series of semicolons tokens have removed the result (as explained above). - A value followed by an end-of-expression (EOX: semicolon, end-of-line, or (in some cases) end-of-file) results to that value. A second "hard EOX" (i.e. a semicolon) will set the result to NULL. For purposes of counting the first EOX, the EOL token is considered an EOX, but multiple EOLs are not treated as multiple EOX. Examples: "3;" === Integer 3, but "3;;" === C-level NULL. Adding newlines (which are normally EOX, but are treated specially here) between (or after) the final value and the semicolons does not change the result. - If the expression triggers a CWAL_RC_RETURN result AND pt->parent is NULL then the "return" result is treated as a legal value, and any pending/propagating result is passed on to the caller via *rv. If pt->parent is not NULL then CWAL_RC_RETURN needs to be propagated up, and is returned as an error (it is treated as one until it hits and handler which accepts "return" results). */ int s2_eval_ptoker( s2_engine * se, s2_ptoker * pt, cwal_value **rv ); /** Functionally equivalent to using s2_eval_ptoker() with a s2_ptoker initialized to use the source range [src,src+srcLen). The name parameter may be 0 - it is used when generating error location information. If newScope is true, a new scope is used, otherwise the code is eval'd in the current scope, which can have unwanted side effects if the evaluation sweeps or (more particularly) vacuums up while the calling code has non-vacuum-safe values laying around (which it might, without know it). If newScope is true, rv is not NULL, and evaluation is successful, *rv gets rescoped (if needed) into the calling scope before returning. */ int s2_eval_cstr( s2_engine * se, char newScope, char const * name, char const * src, int srcLen, cwal_value **rv ); /** Like s2_eval_cstr(), but evaluates the contents of a file. fname is the name of the file. fnlen is the length of the filename, which must be non-0, but cwal_strlen() is used if fnlen is negative. If pushScope is true then the contents are run in a new scope, otherwise they are run in the current scope (quite possibly not what you want, but go ahead if you want). If rv is not NULL then the result value of the evaluation (if any) is assigned to *rv. If *rv is not 0 upon returning then *rv will (if needed) have been moved into the calling cwal scope when this returns. If the script triggers a CWAL_RC_RETURN code then this function treats that as a success result, sets *rv (if rv is not NULL) to the 'return' value, and stops automatic propagation of that value. Most non-exception errors get converted to exceptions, so as to not be fatal to the importing script. Returns 0 on success. On error it may return a number of things: - CWAL_RC_OOM indicates an allocation failure. - CWAL_RC_EXCEPTION indicates that the file's script contents threw or propagated an exception, which is available via cwal_exception_get(). - CWAL_RC_FATAL: means 'fatal' was called (which implicitly triggers an exception). Its pending exception can be found in cwal_exception_get(). - CWAL_RC_EXIT: means the 'exit' keyword was called. Its result value can be found in cwal_propagating_get(). - Most other non-0 codes cause se's error state to be updated with more information (see s2_engine_err_get()). */ int s2_eval_filename( s2_engine * se, char pushScope, char const * fname, cwal_int_t fnlen, cwal_value ** rv ); /** Appends the given value to the given buffer in string form. Returns 0 on success. Objects and Arrays are buffered in JSON form, and this function will fail if traversing them discovers cycles. */ int s2_value_to_buffer( cwal_engine *e, cwal_buffer * buf, cwal_value * arg ); /** A cwal_callback_f() impl which uses s2_value_to_buffer() to convert args->self to a string. The other arguments are ignored. */ int s2_cb_value_to_string( cwal_callback_args const * args, cwal_value **rv ); /** Creates a new Function value bound to the given callback and binds se as the Function's state. The callback can call s2_args_state() to get the interpreter instance. Returns 0 on any error. */ cwal_value * s2_new_function2( s2_engine *se, cwal_callback_f callback); /** If args comes from a Function call where the function has an s2_engine bound to it as state, it returns that pointer, else returns 0. */ s2_engine * s2_args_state( cwal_callback_args const * args ); /** @internal Internal: stores the given prototype value in se's stash using the given name as a suffix for some larger unique key reserved for the various base prototypes. Only to be used by the various s2_prototype_xxx() functions. */ int s2_prototype_stash( s2_engine * se, char const * typeName, cwal_value * proto ); /** If the given name string was used to stash a prototype with s2_prototype_stash(), this function returns that value, else it returns 0. Ownership of the returned value is not modified. */ cwal_value * s2_prototype_stashed( s2_engine * se, char const * typeName ); /** This variant of cwal_scope_push() must be used instead of cwal_scope_push() so that se can perform any scoping-related management. tgt must be a pointer to a cleanly-initialized s2_scope object, preferably one from the stack, copy-initialized from s2_scope_empty or (depending on the context) s2_scope_empty_m. If this function returns 0, the caller is obligted to eventually call s2_scope_pop() to remove the scope from the virtual stack. @see s2_scope_pop() @see s2_scope_current() */ int s2_scope_push( s2_engine * se, s2_scope * tgt ); /** This variant of cwal_scope_pop() must be used instead of cwal_scope_pop() so that se can do its thing. Results are undefined if there has not been a preceeding successful call to s2_scope_push(). Returns as per cwal_scope_pop(). @see s2_scope_push() @see s2_scope_current() */ int s2_scope_pop( s2_engine * se ); /** Returns se's current cwal-level scope, or 0 if no scope is active. @see s2_scope_push() @see s2_scope_pop() */ s2_scope * s2_scope_current( s2_engine * se ); /** Returns se's underlying cwal_engine instance (used by much of the lower-level scripting engine API). It is owned by se. */ cwal_engine * s2_engine_engine(s2_engine * se); /** cwal_callback_f() implementation which acts as a proxy for cwal_value_compare(). If passed two values it passes those to cwal_value_compare(), else it passes args->self and args->argv[0] to it. Throws on usage error, else returns the result of the comparison via *rv. */ int s2_cb_value_compare( cwal_callback_args const * args, cwal_value **rv ); /** A cwal_callback_f() which passes its first argument through cwal_json_output() to produce JSON output. Assigns the resulting string value to *rv. If args->argc is greater than 1 then cwal_json_output_opt. */ int s2_cb_arg_to_json_token( cwal_callback_args const * args, cwal_value **rv ); /** Behaves more or less like the access(2) C function (_access() on Windows builds). Returns true if the given filename is readable (writeable if checkForWriteAccess is true), else false. */ char s2_file_is_accessible( char const * fn, char checkForWriteAccess ); /** cwal_callback_f() impl binding s2_file_is_accessible() in scriptable form: fileIsAccessible(string filename [, bool checkWriteMode=false]) */ int s2_cb_file_accessible( cwal_callback_args const * args, cwal_value **rv ); /** cwal_callback_f() impl binding realpath(3). Script usage: string realpath(path) Throws on error. */ int s2_cb_realpath( cwal_callback_args const * args, cwal_value **rv ); /** @internal If an exception is pending AND it has not yet been decorated with script location information, that is done here. If pr is not 0 then it is used for the location information, else se->currentScript is used. If se->currentScript is also 0, or no error location is recorded somewhere in se->opErrPos or the script chain then this function has no side effects. Returns 0 for a number of not-strictly-error conditions: - !pr && !se->currentScript - if there is no pending exception, or if that exception value may not have properties (it is not a container type) - OR if the pending exception already contains location information. */ int s2_exception_add_script_props( s2_engine * se, s2_ptoker const * pr ); /** @internal Like s2_add_script_props2(), but calculates the script name, line, and column as follows: - The name comes from s2_ptoker_name_top() - The script code position (presumably an error location) comes from s2_ptoker_err_pos_first(). */ int s2_add_script_props( s2_engine * se, cwal_value * v, s2_ptoker const * pr ); /** @internal If v is a container, this adds the properties "script", "line", and "column" to v, using the values passed in. If scriptName is NULL or empty, it is elided. Likewise, the line/column properties are only set if (line>0). If se has stack trace info, it is also injected into v. Potential TODO: separate this routine into a couple different bits. The current behaviour assumes that v is (or will be in a moment) an Exception. @see s2_count_lines() */ int s2_add_script_props2( s2_engine * se, cwal_value * v, char const * scriptName, int line, int col); /** A cwal_callback_f() impl which passes args->self through cwal_json_output() to produce JSON output. Assigns the resulting string value to *rv. If args->argc is not 0 then args->argv[0] is used to specify the indentation, as per cwal_json_output_opt. Script usage depends on whether or not args->self is-a (or inherits) Buffer. If not, then the function's usage looks like: string t = self.toJSONToken([indentation=0]) and returns the JSON-ified from of self. If self is-a Buffer, it looks like: self.toJSONToken(Value v [, indentation=0]) It appends the JSON form of v to self and returns itself. */ int s2_cb_this_to_json_token( cwal_callback_args const * args, cwal_value **rv ); /** A cwal_callback_f() implementation which parses JSON string input. Script usage: @code var json = '{"a":"hi!"}'; var obj = thisFunction(json) @endcode */ int s2_cb_json_parse_string( cwal_callback_args const * args, cwal_value **rv ); /** The file-based counterpart of s2_cb_json_parse_string(). It works identically except that it takes a filename as input instead of a JSON string. */ int s2_cb_json_parse_string( cwal_callback_args const * args, cwal_value **rv ); /** cwal_callback_f() impl which works like getenv(3). Expects one string argument (throws if it does not get one) and returns either a string or the undefined value. */ int s2_cb_getenv( cwal_callback_args const * args, cwal_value **rv ); /** Installs JSON functionality into the given value or (if (key && *key) a property of that value (with the given name). trget which must be a container type. The functions installed: which itself contains these functions: Object parse(String|Buffer jsonString) Object parseFile(String filename) Returns CWAL_RC_TYPE if target is not a container-capable type. Returns 0 on success. */ int s2_install_json( s2_engine * se, cwal_value * target, char const * key); /** If e was created in conjunction with an s2_engine and bound as its client state using &s2_engine_empty as the type ID, this function returns it, else it returns 0. That binding happens during s2_engine_init(), so it will be set if e was successfully processed via that routine. */ s2_engine * s2_engine_from_state( cwal_engine * e ); /** Equivalent to s2_engine_from_state(args->engine). This is intended for use in cwal_callback_f() implementations, for the case that they need the underlying s2_engine (most don't). */ s2_engine * s2_engine_from_args( cwal_callback_args const * args ); /** @internal Internal state for Object.eachProperty() and similar functions. */ struct s2_kvp_each_state { /** Interpreter engine for the visitor. */ cwal_engine * e; /** The 'this' for the visitor. */ cwal_value * self; /** The function to call for each iteration of the visit. */ cwal_function * callback; }; typedef struct s2_kvp_each_state s2_kvp_each_state; /** @internal Internal cwal_value_visitor_f() implementation which requires state to be a (cwal_array*). This function appends v to that array and returns the result. */ int s2_value_visit_append_to_array( cwal_value * v, void * state ); /** @internal State for script-side functions. Each script function gets one of these attached to it. Potential TODOs: - Currently we attach an Object named sourceInfo to the state. Instead, we could make this type a cwal_native binding and store it (instead of a proxy object) directly in the Function? */ struct s2_func_state { /** The underlying interpreter. Potential TODO: i think we can always get this pointer from other sources, so we can potentially optimize this member out. */ s2_engine * se; /** A hashtable key for the source code. The sources are stored in se->funcStash, so they get upscoped to the top scope, but each function removes its source code from the hash. */ cwal_value * keySrc; /** A hashtable key for the script's name. This key points to a string in se->funcStash, and is shared by all functions which have the same script name. Unfortunately, those names must currently stay in the hash for the life of the interpreter, but it is not thought that that poses any real problem other than (potentially) unused script names being kept in in the hash. The problem is that we don't know when it's safe to remove the entry (don't know when the last function-held ref to it is gone, and string interning can confuse the matter). */ cwal_value * keyScriptName; /** The line (1-based) of the function declaration. Used for adjusting error location information. */ int line; /** The column (0-based) of the function declaration. Used for adjusting error location information. */ int col; /* Internal flags */ int flags; /** For the recycling subsystem: this is the next item in the recycling list. */ s2_func_state * next; }; /** Empty-initialized s2_func_state instance. Used as a type ID in a few places. */ extern const s2_func_state s2_func_state_empty; /** The "Path Finder" class is a utility for searching the filesystem for files matching a set of common prefixes and/or suffixes (i.e. directories and file extensions). @see s2_pf_value() @see s2_value_pf() @see s2_pf_dir_add() @see s2_pf_ext_add() @see s2_pf_search() */ typedef struct s2_pf s2_pf; /** Creates a new PathFinder instance. PathFinders are bound to cwal as cwal_native instances and are initially owned by the currently active scope. @see s2_pf_value() @see s2_value_pf() @see s2_pf_dir_add() @see s2_pf_ext_add() @see s2_pf_search() */ s2_pf * s2_pf_new(s2_engine * se); /** Returns the underlying cwal_value which acts as pf's "this". */ cwal_value * s2_pf_value(s2_pf * pf); /** If v was created via s2_pf_new() then this function returns its s2_pf counterpart, else it returns NULL. */ s2_pf * s2_value_pf(cwal_value * v); /** Adds a directory to pf's search path. dir must be at least dirLen bytes and may be an empty but may not be NULL. Returns 0 on success. @see s2_pf_dir_add_v() */ int s2_pf_dir_add( s2_pf * pf, char const * dir, cwal_size_t dirLen); /** Adds a file suffix (extension) to pf's search path. ext must be at least extLen bytes and may be an empty but may not be NULL. Returns 0 on success. @see s2_pf_ext_add_v() */ int s2_pf_ext_add( s2_pf * pf, char const * ext, cwal_size_t extLen); /** Variant of s2_pf_dir_add() which takes its directory part in the form of a cwal_value. Returns 0 on success. */ int s2_pf_dir_add_v( s2_pf * pf, cwal_value * v ); /** Variant of s2_pf_ext_add() which takes its directory part in the form of a cwal_value. Returns 0 on success. */ int s2_pf_ext_add_v( s2_pf * pf, cwal_value * v ); /** Replaces pf's directory list with the given one. Returns 0 on success. */ int s2_pf_dirs_set( s2_pf * pf, cwal_array * ar ); /** Replaces pf's extension/suffix list with the given one. Returns 0 on success. */ int s2_pf_exts_set( s2_pf * pf, cwal_array * ar ); /** Searches for a file whose name can be constructed by some combination of pf's directory/suffix list and the given base name. Returns NULL if !pf, !base, !*base, !baseLen, or on allocation error (it uses a buffer to hold its path combinations). On success it returns a pointer to the path under which it found the item and rcLen (if not NULL) will be set to the length of the returned string. The bytes of the returned string are only valid until the next operation on pf, so copy them if you need them. By default the host platform's customary path separator is used to separate directory/file parts ('\\' on Windows and '/' everywhere else). To change this, set the "separator" property to a string value (even an empty one, in which case the directory paths added to pf should have the trailing separator added to them in order for searching to work). */ char const * s2_pf_search( s2_pf * pf, char const * base, cwal_size_t baseLen, cwal_size_t * rcLen ); /** Returns pf's list of directories, creating it if needed. Only returns NULL if !pf or on allocation error. In script space this value is available via the "prefix" property. */ cwal_array * s2_pf_dirs(s2_pf *pf); /** Returns pf's list of extensions/suffixes, creating it if needed. Only returns NULL if !pf or on allocation error. In script space this value is available via the "suffix" property. */ cwal_array * s2_pf_exts(s2_pf *pf); /** A cwal_callback_f() implementing a constructor of PathFinder (s2_pf) instances. On success, assigns the new instance to *rv. Requires that s2_engine_from_args() returns non-NULL. Script usage: var pf = ThisFunction() it optionally takes up to two array arguments for the directory/extension lists, respectively. */ int s2_cb_pf_new( cwal_callback_args const * args, cwal_value **rv ); /** Installs an Object named PathFinder (the s2_prototype_pf() object) into the given value (which must be a container type). Returns 0 on success, CWAL_RC_MISUSE if !se or !ns, CWAL_RC_TYPE if ns is not a container, and CWAL_RC_OOM if allocating any component fails. */ int s2_install_pf( s2_engine * se, cwal_value * ns ); /** Returns the prototype object for PathFinder instances. That instance gets stashed away in se. Ownership of the returned pointer is unchanged. The caller MUST NOT unreference it (cwal_value_unref() or cwal_value_unhand()) unless he explicitly obtains a reference. */ cwal_value * s2_prototype_pf(s2_engine *se); /** Callback signature for th1ish module import routines. See s2_module_load() for the semantics. */ typedef int (*s2_module_init_f)( s2_engine * se, cwal_value * injectPoint ); /** Holds information for mapping a s2_module_init_f to a name. Its purpose is to get installed by the S2_MODULE_xxx family of macros and referenced later via a module-loading mechanism. */ struct s2_loadable_module{ char const * name; s2_module_init_f init; }; /** Convenience typedef. */ typedef struct s2_loadable_module s2_loadable_module; /** An experiment in loadable modules (DLLs). If compiled without S2_ENABLE_MODULES then this function always returns CWAL_RC_UNSUPPORTED and has no side-effects. Tries to open a DLL named fname using the system's DLL loader. If none is found, CWAL_RC_NOT_FOUND is returned. If one is found, it looks for a symbol in the DLL if symName is not NULL or empty then the symbol "s2_module_symName" is sought, else "s2_module". If no such symbol is found then CWAL_RC_NOT_FOUND (again) is returned, else the symbol is assumed to be a (s2_loadable_module*) and its init() function is called, passed (se, injectPoint), and its result is returned to the caller of this function. The injectPoint parameter must be a container type, and the module "should" inject all of its new functionality into that namespace object. If errMsg is not NULL and opening of the DLL fails AND the underlying DLL opener can give us an error string, then *errMsg is assigned to that error string. If *errMsg comes back non-NULL then the client knows that the problem was in the opening of the DLL, either because it could not be found, symbol resolution problems while opening, or similar. The errMsg string is guaranteed to live in static memory. Returns 0 on success, any number of non-zero CWAL_RC_xxx values on error. Script-side its usage should look like: var imports = {}; loadModule("filename", "symbolName", imports); // or: loadModule("filename", imports); On success it returns the object parameter passed to this function. Note that the API provides no mechanism for unloading DLLs because it is not generically possible to know if it is safe to do so. Closing a DLL whose resources (e.g. a native class definition for a client-bound type) are still in use leads, of course, to undefined results. The caveat, however, is that because dlopen() and friends allocate memory when we open DLLs, and we don't close them, valgrind reports this (rightfully) as a leak. It is not so much a leak as it is a required safety net. That said, the interpreter will close all DLLs it opened (or believes it opened) when it is finalized. That, however, opens up another potential problem: interpreters will close a DLL one time for each time they opened it. How the underlying (system-level) module API deals with that is up to that API. The dlopen()-based and lt_dlopen()-based implementations are safe in that regard (at least on Linux, according to their man pages and a peek at their sources). @see s2_cb_module_load() */ int s2_module_load( s2_engine * se, char const * fname, char const * symName, cwal_value * injectPoint, char const ** errMsg ); /** @def S2_MODULE_DECL Declares an extern (s2_loadable_module*) symbol called s2_module_##NAME##. Use S2_MODULE_IMPL to create the matching implementation code. This macro should be used in the C file for a loadable module. It may be compined in a file with a single S2_MODULE_IMPL1() declaration with the same name, such that the module can be loaded both with and without the explicit symbol name. @see S2_MODULE_IMPL */ #define S2_MODULE_DECL(NAME) \ extern const s2_loadable_module * s2_module_##NAME /** @def S2_MODULE_IMPL Intended to be used to implement module declarations declared using a match call to S2_MODULE_DECL(NAME). Implements a static s2_loadable_module object named s2_module_##NAME##_impl and a non-static (s2_loadable_module*) named s2_module_##NAME which points to s2_module_##NAME##_impl. (That symbol is the one declared by S2_MODULE_DECL.) INIT_F must be a s2_module_init_f() function pointer. This macro should be used in the C file for a loadable module. It may be combined in a file with a single S2_MODULE_IMPL1() declaration with the same name, such that the module can be loaded both with and without the explicit symbol name. Example usage, in a module's header file, if any: @code S2_MODULE_DECL(cpdo); @endcode (The declaration is not strictly necessary - the declaration is more of a matter of documentation.) And in the C file: @code S2_MODULE_IMPL(cpdo,cpdoish_install_to_interp); @endcode If it will be the only module packed in the DLL, one can also add this: @code S2_MODULE_IMPL1(cpdo,cpdoish_install_to_interp); @endcode Which simplifies client-side module loading by allowing them to leave out the module name, but only works if modules are compiled one per DLL (as opposed to being packaged together in one DLL). @see S2_MODULE_DECL @see S2_MODULE_IMPL1 */ #define S2_MODULE_IMPL(NAME,INIT_F) \ static const s2_loadable_module \ s2_module_##NAME##_impl = { #NAME, INIT_F }; \ const s2_loadable_module * s2_module_##NAME = &s2_module_##NAME##_impl /** @def S2_MODULE_IMPL1 Implements a static s2_loadable_module symbol called s2_module_impl and a non-static (s2_loadable_module*) named s2_module which points to s2_module_impl INIT_F must be a s2_module_init_f. This macro should be used in the C file for a loadable module whose DLL includes only that module. */ #define S2_MODULE_IMPL1(NAME,INIT_F) \ static const s2_loadable_module \ s2_module_impl = { #NAME, INIT_F }; \ const s2_loadable_module * s2_module = &s2_module_impl /** cwal_callback_f() impl which wraps s2_module_load(). Script-side usage: var imports = {}; loadModule("filename", "symbolName", imports); // Or: loadModule("filename", imports); On success it returns the object parameter passed to this function. The first form looks for a module entry named "s2_module_SYMBOL_NAME", whereas the second form looks for "s2_module". If such a symbol is found it is assumed to be a (s2_loadable_module*) and its init() function is called if found. On success this function assigns *rv to the 'imports' parameter passed to this function. On error it throws. Achtung: this binding requires that s2_engine_from_args() return non-0. */ int s2_cb_module_load( cwal_callback_args const * args, cwal_value **rv ); /** Pushes one level of output buffer into se's output buffer stack. Buffering works similarly to PHP's ob_start() (and friends) support. While buffer is active, all output send to cwal_engine_output() and friends is redirected to a buffer. The various s2_ob_xxx() functions can be used to: - fetch or discard the contents - pop the buffer from the stack (discarding its contents) When the interpreter is shut down it automatically removes any pushed buffers, but clients should call s2_ob_pop() once for each time they call s2_ob_push() Returns 0 on success, CWAL_RC_MISUSE if !se, CWAL_RC_RANGE if there has been no corresponding call to s2_ob_push(). @see s2_ob_pop() @see s2_ob_get() @see s2_ob_take() @see s2_ob_clear() @see s2_ob_level() @see s2_ob_flush() */ int s2_ob_push( s2_engine * se ); /** Removes the current level of output buffer from ie. Returns 0 on success, CWAL_RC_MISUSE if !ie, CWAL_RC_RANGE if there has been no corresponding call to s2_ob_push(). */ int s2_ob_pop( s2_engine * se ); /** Returns the current buffering level, or 0 if !ie or ie is not in buffering mode. @see s2_ob_push() @see s2_ob_pop() */ cwal_size_t s2_ob_level( s2_engine * se ); /** Gets a pointer to the raw buffer owned by the current level of output buffer, assigning it to *tgt. The buffer is owned by the OB layer and its contents may be modified on any API routines which end up calling cwal_engine_output() or the other s2_ob_xxx() APIs. The caller is intended to copy/use the buffer's contents immediately, and not hold on to it past the current operation. Returns 0 on success, CWAL_RC_MISUSE if !ie or !tgt, CWAL_RC_RANGE if there has been no corresponding call to s2_ob_push(). */ int s2_ob_get( s2_engine * se, cwal_buffer ** tgt ); /** Like s2_ob_get(), but moves the contents of the current buffer layer into tgt, clearing the OB buffer but leaving it on the buffer stack for later use. Returns 0 on success, CWAL_RC_MISUSE if !ie or !tgt, CWAL_RC_RANGE if there has been no corresponding call to s2_ob_push(). tgt must be empty-initialized or the caller must call cwal_buffer_reserve(..., tgt, 0) before calling this or memory may leak. On success ownership of the memory in tgt->mem is transfered to the caller. If tgt was created via cwal_new_buffer() or cwal_new_buffer_value() then tgt and tgt->mem are owned by ie->e. */ int s2_ob_take( s2_engine * se, cwal_buffer * tgt ); /** Clears the contents of the current buffering layer. If releaseBufferMem is true (non-0) then the buffer memory is deallocated, otherwise it is just reset for later use by the OB layer. If it is deallocated, it will be re-allocated later if more output is buffered. Returns 0 on success, CWAL_RC_MISUSE if !ie, CWAL_RC_RANGE if there has been no corresponding call to s2_ob_push(). */ int s2_ob_clear( s2_engine * se, char releaseBufferMem ); /** Pushes the current contents of the output buffer layer to the next output destination in the stack and the current level is cleared of contents (but stays on the stack). If the next outputer is a buffer then the current buffer is appended to it, otherwise it is sent to the originally configured output destination. Returns 0 on success, CWAL_RC_MISUSE if !ie, CWAL_RC_RANGE if there has been no corresponding call to s2_ob_push(), and potentially some other error if flushing to the lower-level implementation fails. @see s2_ob_push() @see s2_ob_pop() */ int s2_ob_flush( s2_engine * se ); /** cwal_callback_f() impl wrapping s2_ob_push(). Requires that args->state be a (th1ish_interp*). Returns argv->self. */ int s2_cb_ob_push( cwal_callback_args const * args, cwal_value **rv ); /** cwal_callback_f() impl wrapping s2_ob_poop(). Requires that args->state be a (th1ish_interp*). Script signature: @code mixed pop([int takePolicy=0]) @endcode If passed no args or a 0/falsy value, it discards any buffered output. If passed numeric greater than 0 then it returns (via *rv) the content as a Buffer. If passed numeric negative then it returns the contents as a String. */ int s2_cb_ob_pop( cwal_callback_args const * args, cwal_value **rv ); /** cwal_callback_f() impl wrapping s2_ob_get(). Requires that args->state be a (th1ish_interp*). Assigns *rv to the string contents of the buffer layer. */ int s2_cb_ob_get( cwal_callback_args const * args, cwal_value **rv ); /** cwal_callback_f() impl wrapping s2_ob_clear(). Requires that args->state be a (th1ish_interp*). Returns argv->self. */ int s2_cb_ob_clear( cwal_callback_args const * args, cwal_value **rv ); /** cwal_callback_f() impl wrapping s2_ob_take(). Requires that args->state be a (th1ish_interp*). Assigns *rv to the string contents of the buffer layer. Design note: the returned string is actually a z-string to avoid having to make another copy of the data. */ int s2_cb_ob_take_string( cwal_callback_args const * args, cwal_value **rv ); /** Functionally identical to s2_cb_ob_take_string() except that it returns (via *rv) a cwal_buffer value (owned by args->engine). */ int s2_cb_ob_take_buffer( cwal_callback_args const * args, cwal_value **rv ); /** cwal_callback_f() impl wrapping s2_ob_flush(). Requires that args->state be a (th1ish_interp*). Returns argv->self. */ int s2_cb_ob_flush( cwal_callback_args const * args, cwal_value **rv ); /** Installs the following functions into tgt (which must be a property container type), all of which correspond to a similarly named s2_ob_XXX() resp. s2_cb_ob_XXX() function: push(), pop(), getString(), takeString(), takeBuffer(), clear(), flush() Returns 0 on success. On error tgt might have been partially populated. Returns CWAL_RC_MISUSE if !ie or !tgt, CWAL_RC_TYPE if tgt is not a container type. */ int s2_install_ob( s2_engine * se, cwal_value * tgt ); /** Variant of s2_install_ob() which installs the OB functionallity into a new object with the given name, and places that object in tgt. Returns 0 on success. */ int s2_install_ob_2( s2_engine * se, cwal_value * tgt, char const * name ); /** Installs various io/filesystem-related APIs. If name is not NULL and not empty, then the APIs get installed in a new object named tgt[name], else the functions are installed directly in tgt (which must be a container type). Returns 0 on success. Functions installed by this: print(), output(), flush(), realpath() (platform-dependent) */ int s2_install_io( s2_engine * se, cwal_value * tgt, char const * name ); /** A cwal_callback_f() which expects its first argument to be an integer. It tries to sleep for at least that many seconds and returns the number of seconds left to sleep if it is interrupted (as per sleep(3)). @see s2_install_time() */ int s2_cb_sleep(cwal_callback_args const * args, cwal_value ** rv); /** A cwal_callback_f() which expects its first argument to be an integer. It sleeps for that many milliseconds. It throws an exception if usleep(3) fails. It returns the undefined value. @see s2_install_time() */ int s2_cb_mssleep(cwal_callback_args const * args, cwal_value ** rv); /** Installs sleep() and mssleep() functions. If name is not 0 and *name is not 0 then a new object with that name is installed to tgt, and the functions are stored there, otherwise the functions are installed directly into tgt. Returns 0 on success. @s22 s2_cb_sleep() @s22 s2_cb_mssleep() */ int s2_install_time( s2_engine * se, cwal_value * tgt, char const * name ); /** Flags for use with s2_tmpl_opt::flags. */ enum s2_tmpl_flags_e { /** Indicates that the output function header definition which checks for and optionally defines the function TMPLOUT (used by the processed template to output its content) should be elided (i.e. not output). */ S2_TMPL_ELIDE_TMPLOUT = 0x01 }; typedef struct s2_tmpl_opt s2_tmpl_opt; /** Holds options for the s2_tmpl_to_code() function. Clients must initialize them by copying either s2_tmpl_opt_empty or (for const contexts) s2_tmpl_opt_empty_m. */ struct s2_tmpl_opt { /** 0 (for no flags) or a bitmask of values from the s2_tmpl_flags_e enum. */ int flags; /** If this is not 0 then: (A) the flag S2_TMPL_ELIDE_TMPLOUT is implied (B) this specifies the script function name which will be called when the processed template is eval'd, to emit its output. If 0 then "TMPLOUT" is used and (A) does not apply. */ char const * outputSymbolPublic; /** In the processed output, the outputSymbolPublic name is only used in the header and aliased to a shorter symbol (so that the output will, for non-trivial cases, be shorter). This member specifies the name it uses. If it is 0 then some unspecified (but short) default is used. It is recommended that clients (if they use this) use weird non-ASCII UTF8 character combinations to avoid any potential symbol collisions. If this is not NULL then the symbol is assumed to be defined by other script code, and is _not_ declared in the output. If the symbol is not defined by prior script code then eval'ing the processed script will fail with an undefined symbol error. */ char const * outputSymbolInternal; /** Similar to outputSymbolInternal, this specifies the name of the processed-template-internal heredoc delimiter. By default (if this is 0) some cryptic combination of non-ASCII UTF8 character is used. */ char const * heredocId; /** The opening tag for "code" blocks. Default is "". If set, then tagCodeOpen must also be set. Must differ from all other tag open/close entries. */ char const * tagCodeClose; /** The opening tag for "value" blocks. Default is "<%". If set, then tagValueClose must also be set. Must differ from all other tag open/close entries. */ char const * tagValueOpen; /** The opening tag for "value" blocks. Default is "%>". If set, then tagValueOpen must also be set. Must differ from all other tag open/close entries. */ char const * tagValueClose; }; /** An initialized-with-defaults instance of s2_tmpl_opt, intended for const-copy initialization. */ #define s2_tmpl_opt_empty_m {0,0,0,0,0,0,0,0} /** An initialized-with-defaults instance of s2_tmpl_opt, intended for copy initialization. */ extern const s2_tmpl_opt s2_tmpl_opt_empty; /** Implements a very basic text template processing mechanism for th1ish. The e arg must be a valid cwal_engine instance. src must be the template source code to process. It is treated as nearly-opaque text input which may contain markup tags (described below) to embed either code blocks or values into the output. dest is where all output is appended (the buffer is not reset by this function). The opt parameter may be 0 (for default options) or an object which configures certain parts of the template processing, as described in the s2_tmpl_opt docs. Returns 0 on success, non-0 on error. Error codes include: - CWAL_RC_MISUSE if !e, !src, or !dest. - CWAL_RC_RANGE if any of the open/close tags specified in the opt parameter are invalid (empty strings or validate rules described in the s2_tmpl_opt docs). - CWAL_RC_OOM on allocation errors. - CWAL_RC_EXCEPTION if it is reporting an error via a cwal exception. On code generation errors it throws an exception in the context of e, containing information about the nature and location (in the original source) of the problem. That said, it may not catch many conceivable malformed content cases and in such cases may generate malformed (as in not eval'able) code. Template processing... (Note that while these docs use fixed tag names, the exact tags can be configured via the opt parameter.) The output starts with a document prefix which sets up output of the text parts of the page. All non-code parts of src are filtered to be output wrapped in individual HEREDOCs embedded in the output script. All code parts of src are handled as follows: '' tag. This ends any current HEREDOC and passes through the code as-is to dest. It does not generate any output in the processed document unless the embedded code generates it. '<%' (without the quotes) starts a "value block," which is processed a little bit differently. The contents between that and the next '%>' tag are simply passed to the configured output routine (see below). An example input document should clear this up: @code Hi, world! x = <%x%>, y = <% y %>, x+y=<% x + y %> @endcode The generated code is an s2 script which, when run (via eval, scope, catch...), outputs a processed document. All non-script parts get wrapped in HEREDOCs for output. The generated code "should" evaluated in a scope of its own, but it can be run in the current scope if desired. The code relies on an output function being defined (resolvable in the evalution scope). That function, if not specified via the opt parameter, is called TMPLOUT. No name is specified and the symbol TMPLOUT is undefined (when the processed template is eval'd), it uses s2.io.output as its default output function. The function must accept any number of Value type parameters and output them "in its conventional string form" (whatever that is). It must not perform any formatting such as spaces between the entries or newlines afterwards. It may define formatting conventions for values passed to it (e.g. it may feel free to reformat doubles to a common representation). The generator outputs some weird/cryptic UTF8 symbols as heredoc markers. It's conceivable, though very unlikely, that these could collide with symbols in the document for heredoc processing purposes. Whitespace handling: - If the script starts with and blocks retains whitespace to the left of the openener and right of the closer, so {abc<%x%>def} will form a single output token (provided 'x' evaluates to such), where {abc <%x%> def} will generate three. Inside the blocks, all whitespace is retained. Inside <% %> blocks, the contents are treated as if they were inside a normal HEREDOC, so their leading/trailing spaces are stripped BUT they are not significant - the _result_ of evaluating the <% %> content gets output when executed, not the content itself. TODOs: - a variant which takes a cwal_output_f() instead of a buffer. */ int s2_tmpl_to_code( cwal_engine * e, cwal_buffer const * src, cwal_buffer * dest, s2_tmpl_opt const * opt ); /** A cwal_callback_f() binding for s2_tmpl_to_code(). It expects one string/buffer argument containing tmplish code and it returns a new Buffer value containing the processed code. Throws on error. Script usage: var compiled = thisFunction(templateSource [, optObject]) If optObject is-a Object then the following properties may influence template processing: - valueOpen and valueClose specify the open/close tags for Value Blocks. - codeOpen and codeClose specify the open/close tags for Code Blocks. */ int s2_cb_tmpl_to_code( cwal_callback_args const * args, cwal_value ** rv ); /** A cwal_callback_f() impl binding the C-standard time(3). */ int s2_cb_time( cwal_callback_args const * args, cwal_value **rv ); /** A strftime() implementation. dest must be valid memory at least destLen bytes long. The result will be written there. fmt must contain the format string. See the file strftime.th1ish (or strftime.c, if you're more into C) for the complete list of format specifiers and their descriptions. timeptr must be the time the caller wants to format. Returns 0 if any arguments are NULL. On success it returns the number of bytes written to dest, not counting the terminating NUL byte (which it also writes). It returns 0 on any error, and the client may need to distinguish between real errors and (destLen==0 or !*fmt), both of which could also look like errors. TODOs: - Refactor this to take a callback or a cwal_buffer, so that we can format arbitrarily long output. - Refactor it to return an integer error code. (i didn't write this implementation - it is derived from public domain sources dating back to the early 1990's.) */ cwal_size_t s2_strftime(char *dest, cwal_size_t destLen, const char *format, const struct tm *timeptr); /** A cwal_callback_f() which wraps s2_strftime(). Script usage: var tm = time(); var str = strftime("%Y-%m-%d %H:%M:%S", tm); The default time value is the current time. Note that this implementation has a limit on the length of the result string (because s2_strftime() works that way), and throws if that length is violated. */ int s2_cb_strftime( cwal_callback_args const * args, cwal_value **rv ); /** Tries to convert an errno value to an equivalent CWAL_RC_xxx value. First argument must be the current errno value. Returns an equivalent, or dflt is no sematic equivalent is known. */ int s2_errno_to_cwal_rc(int errNo, int dflt); /** A cwal_callback_f() impl which returns true if all arguments are functions or derive from a function. i.e. if they are "callable" in s2. */ int s2_cb_is_callable(cwal_callback_args const * args, cwal_value **rv); /** A cwal_callback_f() impl when behaves like rand(3). It will call srand(3), with some pseudo-random seed, the first time it is called. */ int s2_cb_rand_int( cwal_callback_args const * args, cwal_value **rv ); /** Experimental! Intended to be called by app-level code and be passed its shared/global s2_engine instance. This function uses global state and is NOT thread-safe in any way, shape, or form. It stores a copy of se. If se is not NULL then this installs a SIGINT handler which behaves like s2_interrupt() (but uses a different message string). If se is NULL then it removes any previous binding (it does not remove its SIGINT handler, but the handler becomes a no-op if no engine is set to be interrupted). */ void s2_set_interrupt_handlable( s2_engine * se ); /** This sets se's error state to CWAL_RC_INTERRUPTED, a flag it checks for at various points during evaluation and which causes the interpretter to behave essentially as if 'exit' had been used (but without a result value). Calling this is not a guaranty that the engine will stop processing its current script - there are corner cases where the flag can get "lost" during evaluation. On success, returns CWAL_RC_INTERRUPTED. On error (an allocation error generating a message string), it will return another non-0 code (likely CWAL_RC_OOM). This is not strictly thread safe, but "should" be okay to call from a separate thread (e.g. a UI) in most cases, though (depending on timing) it might not have an effect. Known potential race conditions include, but are not necessarily limited to: - se is clearing its error state (which will clear the is-interrupted flag). It resets its error state internally for "non-error errors" which propagate a ways, like "return" and "throw", but those keywords "should" catch and propagate this condition, trumping their own. The lowest-level eval handler checks at every sensible opportunity. - se is cleaning up (inside s2_engine_finalize()), in which case accessing it might (depending on the timing)lead to an illegal memory access (if dynamically allocated) or a useless but harmless[1] access if it's stack-allocated. [1]=so long as the memory itself is still legal to access (e.g. app-level/static). - Third-party bindings may clear se's error state (which includes this flag) indescriminately, without being aware of this condition. There are likely others. */ int s2_interrupt( s2_engine * se ); /** @internal Internal, experimental, and incomplete. */ int s2_check_interrupted( s2_engine * se, int localRc ); /** @internal Removes the specially-propopagating value (if any) from its special propagation mode, effectively transferring to the caller (with all the usual caveats about refcounts, unknowable ownership, etc.) See cwal_propagating_take() for details. In s2, the following keywords use specially-propagating values: return, break, continue, exit. Exceptions use a separate propagation slot dedicated to the currently-thrown exception. */ cwal_value * s2_propagating_take( s2_engine * se ); /** @internal s2 proxy for cwal_propagating_get() for details. */ cwal_value * s2_propagating_get( s2_engine * se ); #ifdef __cplusplus }/*extern "C"*/ #endif #endif /* include guard */ /* end of file s2.h */