├── .gitignore ├── .package ├── tests ├── eval.c ├── run.sh ├── tests.h └── test_xv.c ├── LICENSE ├── README.md ├── xv.h └── xv.c /.gitignore: -------------------------------------------------------------------------------- 1 | *.dSYM 2 | *.o 3 | a.out 4 | .vscode 5 | ryu.c 6 | ryu.h 7 | json.c 8 | json.h 9 | eval 10 | -------------------------------------------------------------------------------- /.package: -------------------------------------------------------------------------------- 1 | file xv.c 2 | file xv.h 3 | 4 | import github.com/tidwall/json.c v1.1.7 5 | import github.com/tidwall/ryu v0.2.0 6 | -------------------------------------------------------------------------------- /tests/eval.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "../xv.h" 6 | 7 | int main(int argc, char **argv) { 8 | if (argc == 1) { 9 | fprintf(stderr, "Usage: %s expression\n", argv[0]); 10 | fprintf(stderr, "Example: %s '10 * 51 + 13'\n", argv[0]); 11 | exit(1); 12 | } 13 | 14 | struct xv value = xv_eval(argv[1], NULL); 15 | const char *result = xv_string(value); 16 | assert(result); 17 | printf("%s\n", result); 18 | xv_cleanup(); 19 | return 0; 20 | } 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2023 Josh Baker 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /tests/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # ./run.sh [] 4 | 5 | set -e 6 | cd $(dirname "${BASH_SOURCE[0]}") 7 | 8 | finish() { 9 | rm -f *.out 10 | rm -f *.test 11 | rm -f *.profraw 12 | rm -fr *.test.dSYM 13 | rm -f *.profdata 14 | echo ; 15 | } 16 | trap finish EXIT 17 | 18 | # Use address sanitizer if possible 19 | if [[ "$1" != bench* ]]; then 20 | CFLAGS="-O0 -g3 -Wall -Wextra $CFLAGS" 21 | if [[ "$CC" == "" && "`which clang`" != "" ]]; then 22 | CC=clang 23 | CFLAGS="$CFLAGS -fno-omit-frame-pointer" 24 | CFLAGS="$CFLAGS -fprofile-instr-generate" 25 | CFLAGS="$CFLAGS -fsanitize=address" 26 | CFLAGS="$CFLAGS -fcoverage-mapping" 27 | CFLAGS="$CFLAGS -fsanitize=undefined" 28 | CFLAGS="$CFLAGS -fno-inline" 29 | CFLAGS="$CFLAGS -pedantic" 30 | WITHCOV="0" 31 | if [[ "$(which llvm-profdata)" == "" ]]; then 32 | if [[ "$(which xcrun)" != "" ]]; then 33 | WITHCOV="1" 34 | XCRUN=xcrun 35 | fi 36 | else 37 | WITHCOV="1" 38 | fi 39 | fi 40 | if [[ "$WITHCOV" == "1" ]]; then 41 | CFLAGS="$CFLAGS -DCOVER_EXCLUDE_UNREACHABLE" 42 | fi 43 | CFLAGS=${CFLAGS:-"-O0 -g3 -Wall -Wextra"} 44 | else 45 | CFLAGS=${CFLAGS:-"-O3"} 46 | fi 47 | CC=${CC:-cc} 48 | echo "CC: $CC" 49 | echo "CFLAGS: $CFLAGS" 50 | $CC --version 51 | 52 | if [[ "$1" == bench* ]]; then 53 | echo "benchmarks are not included" 54 | # echo "BENCHMARKING..." 55 | # echo $CC $CFLAGS ../xv.c ../json.c bench.c -lm 56 | # $CC $CFLAGS ../xv.c ../json.c bench.c -lm 57 | # ./a.out $@ 58 | else 59 | # echo "For benchmarks: 'run.sh bench'" 60 | echo "TESTING..." 61 | for f in *; do 62 | if [[ "$f" != test_*.c ]]; then continue; fi 63 | if [[ "$1" == test_* ]]; then 64 | p=$1 65 | if [[ "$1" == test_*_* ]]; then 66 | # fast track matching prefix with two underscores 67 | suf=${1:5} 68 | rest=${suf#*_} 69 | idx=$((${#suf}-${#rest}-${#_}+4)) 70 | p=${1:0:idx} 71 | fi 72 | if [[ "$f" != $p* ]]; then continue; fi 73 | fi 74 | # echo $CC $CFLAGS ../json.c $f 75 | $CC $CFLAGS -o $f.test ../xv.c ../json.c ../ryu.c -lm $f 76 | if [[ "$WITHCOV" == "1" ]]; then 77 | MallocNanoZone=0 LLVM_PROFILE_FILE="$f.profraw" ./$f.test $@ 78 | elif [[ "$CC" == "clang" ]]; then 79 | MallocNanoZone=0 ./$f.test $@ 80 | else 81 | ./$f.test $@ 82 | fi 83 | done 84 | echo "OK" 85 | 86 | if [[ "$WITHCOV" == "1" ]]; then 87 | $XCRUN llvm-profdata merge *.profraw -o test.profdata 88 | $XCRUN llvm-cov report *.test ../xv.c -ignore-filename-regex=.test. \ 89 | -show-functions=true \ 90 | -instr-profile=test.profdata > /tmp/test.cov.sum.txt 91 | echo coverage: $(cat /tmp/test.cov.sum.txt | grep TOTAL | awk '{ print $7; }') lines 92 | $XCRUN llvm-cov show *.test ../xv.c -ignore-filename-regex=.test. \ 93 | -show-regions=true \ 94 | -show-line-counts-or-regions=true \ 95 | -instr-profile=test.profdata -format=html > /tmp/test.cov.html 96 | echo "details: file:///tmp/test.cov.html" 97 | echo "summary: file:///tmp/test.cov.sum.txt" 98 | elif [[ "$WITHCOV" == "0" ]]; then 99 | echo "code coverage not a available" 100 | echo "install llvm-profdata and use clang for coverage" 101 | fi 102 | 103 | fi 104 | -------------------------------------------------------------------------------- /tests/tests.h: -------------------------------------------------------------------------------- 1 | #ifndef TESTS_H 2 | #define TESTS_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "../xv.h" 11 | 12 | static bool nameless_tests = false; 13 | 14 | static long nallocs = 0; 15 | static long ntotalsize = 0; 16 | 17 | static bool rand_alloc_fail = false; 18 | // 1 in 10 chance malloc or realloc will fail. 19 | static int rand_alloc_fail_odds = 10; 20 | static bool sysalloc = false; 21 | 22 | static void *xmalloc(size_t size) { 23 | if (sysalloc) { 24 | return malloc(size); 25 | } 26 | if (rand_alloc_fail && rand()%rand_alloc_fail_odds == 0) { 27 | return NULL; 28 | } 29 | void *ptr = malloc(sizeof(size_t)+size); 30 | assert(ptr); 31 | nallocs++; 32 | *((size_t*)ptr) = size; 33 | ntotalsize += (long)*((size_t*)ptr); 34 | return (char*)ptr+sizeof(size_t); 35 | } 36 | 37 | static void *xrealloc(void *ptr, size_t size) { 38 | if (sysalloc) { 39 | return realloc(ptr, size); 40 | } 41 | if (!ptr) return xmalloc(size); 42 | if (rand_alloc_fail && rand()%rand_alloc_fail_odds == 0) { 43 | return NULL; 44 | } 45 | ptr = ((char*)ptr) - sizeof(size_t); 46 | ntotalsize -= (long)*((size_t*)ptr); 47 | ptr = realloc(ptr, sizeof(size_t)+size); 48 | assert(ptr); 49 | *((size_t*)ptr) = size; 50 | ntotalsize += (long)*((size_t*)ptr); 51 | return (char*)ptr+sizeof(size_t); 52 | } 53 | 54 | static void xfree(void *ptr) { 55 | if (sysalloc) { 56 | free(ptr); 57 | return; 58 | } 59 | if (!ptr) return; 60 | ptr = ((char*)ptr) - sizeof(size_t); 61 | ntotalsize -= (long)*((size_t*)ptr); 62 | nallocs--; 63 | free(ptr); 64 | } 65 | 66 | double now(void) { 67 | struct timespec now; 68 | clock_gettime(CLOCK_MONOTONIC, &now); 69 | return (now.tv_sec*1e9 + now.tv_nsec) / 1e9; 70 | } 71 | 72 | void seedrand(void) { 73 | uint64_t seed = 0; 74 | FILE *urandom = fopen("/dev/urandom", "r"); 75 | assert(urandom); 76 | assert(fread(&seed, sizeof(uint64_t), 1, urandom)); 77 | fclose(urandom); 78 | srand(seed); 79 | } 80 | 81 | #define cleanup() { \ 82 | xv_cleanup(); \ 83 | assert(nallocs == 0); \ 84 | assert(ntotalsize == 0); \ 85 | struct xv_memstats memstats = xv_memstats(); \ 86 | assert(memstats.heap_size == 0); \ 87 | assert(memstats.heap_allocs == 0); \ 88 | assert(memstats.thread_total_size > 0); \ 89 | assert(memstats.thread_allocs == 0); \ 90 | assert(memstats.thread_size == 0); \ 91 | } 92 | 93 | #define do_test(name) { \ 94 | if (argc < 2 || strstr(#name, argv[1])) { \ 95 | if (!nameless_tests) printf("%s\n", #name); \ 96 | xv_set_allocator(xmalloc, xrealloc, xfree); \ 97 | seedrand(); \ 98 | name(); \ 99 | cleanup(); \ 100 | } \ 101 | } 102 | 103 | #define do_chaos_test(name) { \ 104 | if (argc < 2 || strstr(#name, argv[1])) { \ 105 | if (!nameless_tests) printf("%s\n", #name); \ 106 | rand_alloc_fail = true; \ 107 | xv_set_allocator(xmalloc, xrealloc, xfree); \ 108 | seedrand(); \ 109 | name(); \ 110 | cleanup(); \ 111 | rand_alloc_fail = false; \ 112 | } \ 113 | } 114 | 115 | #define do_sysalloc_test(name) { \ 116 | if (argc < 2 || strstr(#name, argv[1])) { \ 117 | if (!nameless_tests) printf("%s\n", #name); \ 118 | sysalloc = true; \ 119 | xv_set_allocator(xmalloc, xrealloc, xfree); \ 120 | seedrand(); \ 121 | name(); \ 122 | cleanup(); \ 123 | sysalloc = false; \ 124 | } \ 125 | } 126 | 127 | #endif // TESTS_H 128 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # xv 2 | 3 | An expression evaluator for C. 4 | 5 | ## Features 6 | 7 | - **Friendly.** Provides Javascript [syntax](#syntax). 8 | - **Fast.** Evaluates expressions using an optimized [recursive descent parser](https://en.wikipedia.org/wiki/Recursive_descent_parser). 9 | - **Extensible.** Allows for [adding custom](#customize) functions and variables. Includes [JSON](#json) support. 10 | - **Stateless.** [No variable assignments](#stateless) or statements. 11 | - **Safe.** [Thread-safe](#memory-and-safety). No allocations for most expressions. 100% [test](#tests) coverage. 12 | 13 | ## Install 14 | 15 | Clone this respository and then use [pkg.sh](https://github.com/tidwall/pkg.sh) 16 | to import dependencies from the [.package](.package) file. 17 | 18 | ``` 19 | $ git clone https://github.com/tidwall/xv 20 | $ cd xv/ 21 | $ pkg.sh import 22 | ``` 23 | 24 | ## Example 25 | 26 | ```C 27 | #include "xv.h" 28 | 29 | struct xv value = xv_eval("1 + 2 * (10 * 20)", NULL); 30 | printf("%d\n", xv_int64(value)); 31 | // Output: 401 32 | ``` 33 | 34 | There's also an example command line tool in the `tests` directory that can be built 35 | using: 36 | 37 | ```sh 38 | $ cc -o eval tests/eval.c *.c 39 | $ ./eval '2 * (15 + 13) - 4' 40 | # Output: 52 41 | ``` 42 | 43 | ## Syntax 44 | 45 | This implemetation uses a subset of Javascript that includes some of the most 46 | common operators, such as: 47 | `+` `-` `*` `/` `%` `!` `<` `<=` `>` `>=` `==` `===` `!=` `!===` `?:` `??` `,` `[]` `()` `&` `|` `^`. 48 | 49 | By design, xv expressions should work in most Javascript consoles, like Node or the Chrome developer console. 50 | 51 | For example, the following expression evaluates to the same result in xv and Node. 52 | 53 | ```js 54 | (1 + 2 == 3 ? 'yes' : 'no') + ' this works' 55 | ``` 56 | 57 | ### Stateless 58 | 59 | XV is not intended to be a complete programming language. 60 | It's mainly designed to work with simple expressions and read-only user data. 61 | Mutating data and creating variables are not encouraged. 62 | 63 | Thus the `;` and `=` operators are intentionally omitted. 64 | 65 | ## Customize 66 | 67 | The runtime environment can be customized by adding new functions or variables. 68 | 69 | For example: 70 | 71 | ```C 72 | #include 73 | #include "xv.h" 74 | 75 | struct xv get_ref(struct xv this, struct xv ident, void *udata) { 76 | if (xv_is_global(this)) { 77 | if (xv_string_compare(ident, "lastName") == 0) { 78 | return xv_new_string("Anderson"); 79 | } 80 | if (xv_string_compare(ident, "age") == 0) { 81 | return xv_new_int64(51); 82 | } 83 | } 84 | return xv_new_undefined(); 85 | } 86 | 87 | int main() { 88 | struct xv_env env = { .ref = get_ref }; 89 | struct xv result = xv_eval("lastName == 'Anderson' && age > 45", &env); 90 | printf("%s\n", xv_bool(result) ? "YES" : "NO"); 91 | return 0; 92 | }; 93 | 94 | // Output: YES 95 | ``` 96 | 97 | ### JSON 98 | 99 | There's also built-in support for reading data from JSON documents. 100 | 101 | ```C 102 | #include 103 | #include "xv.h" 104 | 105 | struct xv get_ref(struct xv this, struct xv ident, void *udata) { 106 | if (xv_is_global(this)) { 107 | if (xv_string_compare(ident, "json") == 0) { 108 | return xv_new_json(udata); 109 | } 110 | } 111 | return xv_new_undefined(); 112 | } 113 | 114 | int main() { 115 | char *json = "{\"lastName\": \"Anderson\", \"age\": 51}"; 116 | struct xv_env env = { .ref = get_ref, .udata = json }; 117 | struct xv result = xv_eval("json.lastName == 'Anderson' && json.age > 45", &env); 118 | printf("%s\n", xv_bool(result) ? "YES" : "NO"); 119 | return 0; 120 | }; 121 | 122 | // Output: YES 123 | ``` 124 | 125 | ### Custom functions 126 | 127 | Here's an example of adding a function to the runtime enviroment. 128 | 129 | ```C 130 | #include 131 | #include 132 | #include "xv.h" 133 | 134 | #define EARTH 6371e3 135 | #define RADIANS (M_PI / 180) 136 | #define DEGREES (180 / M_PI) 137 | 138 | // geo_distance is the haversine formula for calculating the distance of two 139 | // points on earth. 140 | struct xv geo_distance(struct xv this, struct xv args, void *udata) { 141 | double lat1 = xv_double(xv_array_at(args, 0)); 142 | double lon1 = xv_double(xv_array_at(args, 1)); 143 | double lat2 = xv_double(xv_array_at(args, 2)); 144 | double lon2 = xv_double(xv_array_at(args, 3)); 145 | double φ1 = lat1 * RADIANS; 146 | double λ1 = lon1 * RADIANS; 147 | double φ2 = lat2 * RADIANS; 148 | double λ2 = lon2 * RADIANS; 149 | double Δφ = φ2 - φ1; 150 | double Δλ = λ2 - λ1; 151 | double sΔφ2 = sin(Δφ / 2); 152 | double sΔλ2 = sin(Δλ / 2); 153 | double haver = sΔφ2*sΔφ2 + cos(φ1)*cos(φ2)*sΔλ2*sΔλ2; 154 | double meters = EARTH * 2 * asin(sqrt(haver)); 155 | return xv_new_double(meters); 156 | } 157 | 158 | struct xv get_ref(struct xv this, struct xv ident, void *udata) { 159 | if (xv_is_global(this)) { 160 | if (xv_string_compare(ident, "geo_distance") == 0) { 161 | return xv_new_function(geo_distance); 162 | } 163 | } 164 | return xv_new_undefined(); 165 | } 166 | 167 | int main() { 168 | struct xv_env env = { .ref = get_ref }; 169 | // Distance from Paris, France to Tempe, Arizona 170 | struct xv result = xv_eval("geo_distance(48.8647, 2.3490, 33.4255, -111.9412)", &env); 171 | printf("%.0f km\n", xv_double(result)/1000); 172 | return 0; 173 | }; 174 | 175 | // Output: 8796 km 176 | ``` 177 | 178 | ## Memory and safety 179 | 180 | The xv library is designed to be thread-safe. It uses thread local variables 181 | for managing runtime state and includes a fast internal bump allocator for 182 | expressions that require allocating memory. 183 | 184 | ### xv_cleanup 185 | 186 | There are some cases where the `xv_eval` or `xv_new_*` functions will need to 187 | allocate memory, usually when concatenating strings. 188 | Therefore it's always a good idea to call `xv_cleanup` when you are done. 189 | This will reset the thread-local environment and free any allocated memory. 190 | 191 | For example: 192 | 193 | ```C 194 | struct xv value = xv_eval("'hello' + ' ' + 'world'", NULL); 195 | char *str = xv_string(value); 196 | xv_cleanup(); 197 | 198 | printf("%s\n", str); // prints "hello world" 199 | free(str); 200 | 201 | // Avoid using the returned 'value' variable after calling 202 | // xv_cleanup, otherwise you risk causing undefined behavior. 203 | ``` 204 | 205 | ### Tests 206 | 207 | This project includes a test suite can be run from the command line with: 208 | 209 | ```sh 210 | $ tests/run.sh 211 | ``` 212 | 213 | If [clang](https://clang.llvm.org) is installed then various sanitizers are used such as [AddressSanitizer](https://clang.llvm.org/docs/AddressSanitizer.html) and [UndefinedBehaviorSanitizer](https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html). 214 | And clang will also check code coverage, which this library should always be at 100%. 215 | -------------------------------------------------------------------------------- /xv.h: -------------------------------------------------------------------------------- 1 | #ifndef XV_H 2 | #define XV_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | // struct xv is an expression value. 9 | // 10 | // This is typically created as a result from the xv_eval function, but can 11 | // also be created using the xv_new_*() functions. 12 | struct xv { uint64_t priv[2]; }; 13 | 14 | // enum xv_type is returned from the xv_type() function. 15 | enum xv_type { 16 | XV_UNDEFINED, XV_NULL, XV_STRING, XV_NUMBER, 17 | XV_BOOLEAN, XV_FUNCTION, XV_OBJECT, 18 | }; 19 | 20 | // struct xv_env is a custom environment that is provided to xv_eval. 21 | struct xv_env { 22 | // no_case tells xv_eval to perform case-insensitive comparisons. 23 | bool no_case; 24 | // udata is custom user data. 25 | void *udata; 26 | // ref is a callback that returns a reference value for unknown 27 | // identifiers, properties, and functions. 28 | struct xv (*ref)(struct xv this, struct xv ident, void *udata); 29 | }; 30 | 31 | // xv_eval evaluate an expression and returns the resulting value. 32 | // 33 | // If the xv_eval resulted in an error, such as a syntax error or if the 34 | // system ran out of memory, then the reuslt can be checked with xv_is_err(). 35 | struct xv xv_eval(const char *expr, struct xv_env *env); 36 | struct xv xv_evaln(const char *expr, size_t len, struct xv_env *env); 37 | 38 | // xv_cleanup resets the environment and frees any allocated memory 39 | // that may have occured during an xv_eval. 40 | // 41 | // This should be called when you are totally done with the value that resulted 42 | // from the xv_eval call. Thus each xv_eval should have one xv_cleanup. 43 | // 44 | // DO NOT use the resulting 'struct xv' value after calling xv_cleanup, 45 | // otherwise you risk undefined behavior. 46 | // 47 | // Regarding thread-safety. 48 | // The xv_eval & xv_cleanup are thread-safe functions that use thread-local 49 | // variables and allocations. 50 | void xv_cleanup(void); 51 | 52 | // xv_string_copy copies a value as a string representation into the 53 | // provided C string buffer. 54 | // 55 | // Returns the number of characters, not including the null-terminator, needed 56 | // to store the string representation into the C string buffer. 57 | // If the returned length is greater than nbytes-1, then only a parital copy 58 | // occurred, for example: 59 | // 60 | // char buf[64]; 61 | // size_t len = xv_string_copy(value, str, sizeof(str)); 62 | // if (len > sizeof(str)-1) { 63 | // // ... copy did not complete ... 64 | // } 65 | // 66 | size_t xv_string_copy(struct xv value, char *dst, size_t n); 67 | 68 | // xv_string returns the string representation of the value. 69 | // 70 | // This operation will allocate new memory and it's the caller's responsibility 71 | // to free the string at a later point. 72 | // 73 | // Alternatively the xv_string_copy may be used to avoid allocations. 74 | // 75 | // Returns NULL if system is out of memory. 76 | char *xv_string(struct xv value); 77 | 78 | // xv_string_length returns the length of the string representation of the 79 | // value. 80 | size_t xv_string_length(struct xv value); 81 | 82 | // xv_double returns the double representation of the value. 83 | double xv_double(struct xv value); 84 | 85 | // xv_int64 returns the int64_t representation of the value. 86 | int64_t xv_int64(struct xv value); 87 | 88 | // xv_uint64 returns the uint64_t representation of the value. 89 | uint64_t xv_uint64(struct xv value); 90 | 91 | // xv_object returns the user defined object, or NULL if none. 92 | const void *xv_object(struct xv value); 93 | 94 | // xv_object_tag returns the user defined tag for value, or zero if none. 95 | uint32_t xv_object_tag(struct xv value); 96 | 97 | // xv_bool returns the C bool representation of the value. 98 | bool xv_bool(struct xv value); 99 | 100 | // xv_type return the primitive type of the value. 101 | enum xv_type xv_type(struct xv value); 102 | 103 | // xv_is_undefined returns true if the value is undefined' 104 | bool xv_is_undefined(struct xv value); 105 | 106 | // xv_is_global return true if the value is the global variable. 107 | // 108 | // This is mainly used in the ref callback. When the 'this' value is global 109 | // then the 'ident' should be a global variable, otherwise the 'ident' should 110 | // be a property of 'this'. 111 | bool xv_is_global(struct xv value); 112 | 113 | // xv_is_error return true if the value is an error. 114 | bool xv_is_error(struct xv value); 115 | 116 | // xv_is_oom returns true if the value is an error because xv_eval failed due 117 | // to the system being out of memory. 118 | bool xv_is_oom(struct xv value); 119 | 120 | // xv_array_length returns the number of items in an array value, or zero if 121 | // the value is not an array or there are no items. 122 | size_t xv_array_length(struct xv value); 123 | 124 | // xv_array_at returns the item at index for an array value, or undefined if 125 | // the value is not an array or the index is out of range. 126 | struct xv xv_array_at(struct xv value, size_t index); 127 | 128 | // xv_string_compare compares the value to a C string. 129 | // 130 | // This function performs a binary comparison of the characters. It compares 131 | // each character, one-by-one, of both strings. If they are equal to each 132 | // other, it continues until the characters differ or until the end of the 133 | // value or a terminating null-character in the C string is reached. 134 | // Returns < 0, 0, > 0 for less-than, equal-to, greater-than. 135 | int xv_string_compare(struct xv value, const char *str); 136 | int xv_string_comparen(struct xv value, const char *str, size_t len); 137 | 138 | int xv_string_equal(struct xv value, const char *str); 139 | int xv_string_equaln(struct xv value, const char *str, size_t len); 140 | 141 | 142 | // value construction 143 | struct xv xv_new_string(const char *str); 144 | struct xv xv_new_stringn(const char *str, size_t len); 145 | struct xv xv_new_object(const void *ptr, uint32_t tag); 146 | struct xv xv_new_json(const char *json); 147 | struct xv xv_new_jsonn(const char *json, size_t len); 148 | struct xv xv_new_double(double d); 149 | struct xv xv_new_int64(int64_t i); 150 | struct xv xv_new_uint64(uint64_t u); 151 | struct xv xv_new_boolean(bool t); 152 | struct xv xv_new_undefined(void); 153 | struct xv xv_new_null(void); 154 | struct xv xv_new_error(const char *msg); 155 | struct xv xv_new_array(const struct xv *const *values, size_t nvalues); 156 | struct xv xv_new_function(struct xv (*func)( 157 | struct xv this, const struct xv args, void *udata)); 158 | 159 | // struct xv_memstats is returned by xv_memstats 160 | struct xv_memstats { 161 | size_t thread_total_size; // total size of the thread-local memory space 162 | size_t thread_size; // number of used thread-local memory space bytes 163 | size_t thread_allocs; // number of thread-local memory space allocations 164 | size_t heap_allocs; // number of thread-local heap allocations 165 | size_t heap_size; // number of used thread-local heap bytes 166 | }; 167 | 168 | // xv_memstats returns various memory statistics due to an xv_eval call. 169 | // 170 | // These stats are reset by calling xv_cleanup. 171 | struct xv_memstats xv_memstats(void); 172 | 173 | // xv_set_allocator allows for configuring a custom allocator for 174 | // all xv library operations. This function, if needed, should be called 175 | // only once at program start up and prior to calling any xv_*() functions. 176 | void xv_set_allocator( 177 | void *(*malloc)(size_t), 178 | void *(*realloc)(void*, size_t), 179 | void (*free)(void*)); 180 | 181 | #endif // XV_H 182 | -------------------------------------------------------------------------------- /tests/test_xv.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "tests.h" 3 | 4 | struct xv numobj(struct xv value, struct xv args, 5 | void *udata) 6 | { 7 | (void)value, (void) udata; 8 | double d = xv_double(xv_array_at(args, 0)); 9 | if (d == -80808080) { 10 | return xv_new_error("OperatorError: bad news"); 11 | } 12 | return xv_new_double(d); 13 | } 14 | 15 | struct xv i64(struct xv value, struct xv args, 16 | void *udata) 17 | { 18 | (void)value, (void) udata; 19 | char str[512]; 20 | xv_string_copy(xv_array_at(args, 0), str, sizeof(str)); 21 | return xv_new_int64(strtoll(str, NULL, 10)); 22 | } 23 | 24 | struct xv u64(struct xv value, struct xv args, 25 | void *udata) 26 | { 27 | (void)value, (void) udata; 28 | char str[512]; 29 | xv_string_copy(xv_array_at(args, 0), str, sizeof(str)); 30 | return xv_new_uint64(strtoull(str, NULL, 10)); 31 | } 32 | 33 | struct xv cust(struct xv value, struct xv args, 34 | void *udata) 35 | { 36 | (void)value, (void) udata; 37 | char str[512]; 38 | xv_string_copy(xv_array_at(args, 0), str, sizeof(str)); 39 | return xv_new_double((double)atoi(str)); 40 | } 41 | 42 | struct xv myfn1(struct xv value, struct xv args, 43 | void *udata) 44 | { 45 | (void)value, (void) udata; 46 | if (xv_string_compare(xv_array_at(args, 0), "9999") == 0) { 47 | return xv_new_error("fantastic"); 48 | } 49 | return value; 50 | } 51 | 52 | struct xv myfn2(struct xv value, struct xv args, 53 | void *udata) 54 | { 55 | (void)value, (void) udata; 56 | double sum = 0; 57 | for (size_t i = 0; i < xv_array_length(args); i++) { 58 | sum += xv_double(xv_array_at(args, i)); 59 | } 60 | return xv_new_double(sum); 61 | } 62 | 63 | struct xv eref(struct xv this, struct xv ident, 64 | void *udata) 65 | { 66 | (void)udata; 67 | if (xv_is_global(this)) { 68 | // global 69 | if (xv_string_compare(ident, "numobj") == 0) { 70 | return xv_new_function(numobj); 71 | } 72 | if (xv_string_compare(ident, "i64") == 0) { 73 | return xv_new_function(i64); 74 | } 75 | if (xv_string_compare(ident, "u64") == 0) { 76 | return xv_new_function(u64); 77 | } 78 | if (xv_string_compare(ident, "custom_err") == 0) { 79 | return xv_new_error("ReferenceError: hiya"); 80 | } 81 | if (xv_string_compare(ident, "cust") == 0) { 82 | return xv_new_function(cust); 83 | } 84 | if (xv_string_compare(ident, "howdy") == 0) { 85 | return xv_new_string("hiya"); 86 | } 87 | if (xv_string_compare(ident, "user1") == 0) { 88 | return xv_new_object(NULL, 99); 89 | } 90 | if (xv_string_compare(ident, "json") == 0) { 91 | return xv_new_json("{" 92 | "\"name\": {\"first\": \"Janet\", \"last\": \"Anderson\"}, " 93 | "\"age\": 37," 94 | "\"empty\": []," 95 | "\"one\": [15]," 96 | "\"enc\": \"Big\\nBot\"," 97 | "\"data\": [1,true,false,null,{\"a\":1}]" 98 | "}"); 99 | } 100 | if (xv_string_compare(ident, "badj") == 0) { 101 | return xv_new_json("\""); 102 | } 103 | if (xv_string_compare(ident, "noj") == 0) { 104 | return xv_new_json(""); 105 | } 106 | if (xv_string_compare(ident, "bigjson") == 0) { 107 | return xv_new_json("{\"a\":123456789012345678901234567890}"); 108 | } 109 | 110 | } else { 111 | if (xv_string_compare(ident, "myfn1") == 0) { 112 | return xv_new_function(myfn1); 113 | } 114 | if (xv_string_compare(ident, "myfn2") == 0) { 115 | return xv_new_function(myfn2); 116 | } 117 | if (xv_object_tag(this) == 99) { 118 | if (xv_string_compare(ident, "name") == 0) { 119 | return xv_new_string("andy"); 120 | } 121 | if (xv_string_compare(ident, "age") == 0) { 122 | return xv_new_double(51.0); 123 | } 124 | if (xv_string_compare(ident, "err") == 0) { 125 | return xv_new_error("oh no"); 126 | } 127 | } 128 | } 129 | return xv_new_undefined(); 130 | } 131 | 132 | static bool nocase = false; 133 | 134 | #define eval(input, expect) { \ 135 | int failed = 0; \ 136 | int trys = 0; \ 137 | while (1) { \ 138 | struct xv_env env = { \ 139 | .ref = eref, \ 140 | .no_case = nocase, \ 141 | }; \ 142 | struct xv value = xv_eval((input), &env); \ 143 | char *got = xv_string(value); \ 144 | if (!got) { \ 145 | failed++; \ 146 | continue; \ 147 | } \ 148 | if (xv_is_oom(value)) { \ 149 | if (got) xfree(got); \ 150 | failed++; \ 151 | continue; \ 152 | } \ 153 | if (strcmp(got, (expect)) != 0) { \ 154 | fprintf(stderr, "line %d: expected '%s', got '%s\n", __LINE__, (expect), (got)); \ 155 | exit(1); \ 156 | } \ 157 | if (got) xfree(got); \ 158 | if (rand_alloc_fail && trys < 1000) { \ 159 | trys++; \ 160 | continue; \ 161 | } \ 162 | break; \ 163 | } \ 164 | (void)failed; \ 165 | } 166 | 167 | void test_xv_various(void) { 168 | eval(".1", "0.1"); 169 | eval(".1e-1", "0.01"); 170 | eval(".1e-1 + 5", "5.01"); 171 | eval("0.1", "0.1"); 172 | eval("1u64", "1"); 173 | eval("1.0u64", "SyntaxError"); 174 | eval("-1i64", "-1"); 175 | eval("-1.0u64", "SyntaxError"); 176 | eval("", "undefined"); 177 | eval(" ", "undefined"); 178 | eval("()", "SyntaxError"); 179 | eval("\"\\\"", "SyntaxError"); 180 | eval("1", "1"); 181 | eval("-1", "-1"); 182 | eval("- 1", "-1"); 183 | eval(" - 1", "-1"); 184 | eval(" - -1", "1"); 185 | eval("- - 1", "1"); 186 | eval("- - - -1", "1"); 187 | eval("- - - -1 - 2", "-1"); 188 | eval("+1", "1"); 189 | eval("+ 1", "1"); 190 | eval(" + 1", "1"); 191 | eval(" + +1", "1"); 192 | eval(" + +-1", "-1"); 193 | eval(" + +-+ +- -1", "-1"); 194 | eval("-+-+-+-1 - 2", "-1"); 195 | eval("(", "SyntaxError"); 196 | eval("(1", "SyntaxError"); 197 | eval("(1)", "1"); 198 | eval("( 1 )", "1"); 199 | eval("--1", "SyntaxError"); 200 | eval("1--", "SyntaxError"); 201 | eval("1++", "SyntaxError"); 202 | eval("++1", "SyntaxError"); 203 | eval("-+1", "-1"); 204 | eval("\"hello\"", "hello"); 205 | eval("\"hel\\nlo\"", "hel\nlo"); 206 | eval("\"hi\"+1", "hi1"); 207 | eval("\"hi\"-1", "NaN"); 208 | eval("1+1-0.5", "1.5"); 209 | eval("2*4", "8"); 210 | eval("(2*4", "SyntaxError"); 211 | eval("\"2*4", "SyntaxError"); 212 | eval("1 > 2", "false"); 213 | eval("1 > 2 || 3 > 2", "true"); 214 | eval("2 > 3", "false"); 215 | eval("3 > 2 || (2 > 3 && 1 < 2)", "true"); 216 | eval("(1 < 2 && 3 > 2) + 10", "11"); 217 | eval("999 + 777 * (888 + (0.5 + 1.5)) * (0.5 + true)", "1038294"); 218 | eval("999 + 777 * (888 / 0.456) / true", "1514104.2631578946"); // need ryu 219 | eval("999 + 777 * (888 / 0.456) / 0", "Infinity"); 220 | eval("999 + 777 * (888 / 0.456) / 0", "Infinity"); 221 | eval("1.0e1", "10"); 222 | eval("1.0E1", "10"); 223 | eval("1.0e+1", "10"); 224 | eval("1.0E+1", "10"); 225 | eval("1.0e-1", "0.1"); 226 | eval("1.0E-1", "0.1"); 227 | eval("-1.0E-1", "-0.1"); 228 | eval(("\"he\\\"llo\""), ("he\"llo")); 229 | eval(("\"he\\\'llo\""), ("he\'llo")); 230 | eval(("\"he\\\"\\b\\fllo\""), ("he\"\b\fllo")); 231 | eval(("(\"hello\\\\\\t\\/\\r\\n\\t\\\\\\\"world\")"), ("hello\\\t/\r\n\t\\\"world")); 232 | eval("\"hello", "SyntaxError"); 233 | eval("1 | 2", "3"); 234 | eval("1 & 2", "0"); 235 | eval("5 & 4", "4"); 236 | eval("5 ^ 4", "1"); 237 | eval("500 ^", "SyntaxError"); 238 | eval("500 &", "SyntaxError"); 239 | eval("500 |", "SyntaxError"); 240 | eval("500 ^ 700", "840"); 241 | eval("500u64 ^ 700u64", "840"); 242 | eval("500i64 ^ 700i64", "840"); 243 | eval("numobj(500) ^ numobj(700)", "840"); 244 | eval("'500' ^ '700'", "840"); 245 | eval("500 & 700", "180"); 246 | eval("500u64 & 700u64", "180"); 247 | eval("numobj(500) & numobj(700)", "180"); 248 | eval("500i64 & 700i64", "180"); 249 | eval("'500' & '700'", "180"); 250 | eval("500 | 700", "1020"); 251 | eval("500u64 | 700u64", "1020"); 252 | eval("500i64 | 700i64", "1020"); 253 | eval("numobj(500) | numobj(700)", "1020"); 254 | eval("'500' | '700'", "1020"); 255 | eval("500 | -700", "-524"); 256 | eval("-500 & -700", "-1020"); 257 | eval("500 ^ -700", "-848"); 258 | eval("(%$#) | 500 | (%$#)", "SyntaxError"); 259 | eval("(%$#) & -500 & (%$#)", "SyntaxError"); 260 | eval("(%$#) ^ 500 ^ (%$#)", "SyntaxError"); 261 | eval("(%$# | 500 | (%$#", "SyntaxError"); 262 | eval("(%$# & -500 & (%$#", "SyntaxError"); 263 | eval("(%$# ^ 500 ^ (%$#", "SyntaxError"); 264 | eval("(400) | (500) ^ (%$#) & (%$#", "SyntaxError"); 265 | eval("(%$#) & (-500 & (%$#", "SyntaxError"); 266 | eval("(%$#) ^ (500 ^ (%$#", "SyntaxError"); 267 | eval(("numobj(-80808080) & numobj(-80808080)"), ("OperatorError: bad news")); 268 | eval(("numobj(-80808080) | numobj(-80808080)"), ("OperatorError: bad news")); 269 | eval(("numobj(-80808080) ^ numobj(-80808080)"), ("OperatorError: bad news")); 270 | eval(("(1 && 2}"), ("SyntaxError")); 271 | eval(("1 != 2"), ("true")); 272 | eval(("1 ! 2"), ("SyntaxError")); 273 | eval(("1 >= 2"), ("false")); 274 | eval(("1 == 2"), ("false")); 275 | eval(("1 = 2"), ("SyntaxError")); 276 | eval(("1 == "), ("SyntaxError")); 277 | eval((" == 1"), ("SyntaxError")); 278 | eval(("\"Example emoji, KO: \\ud83d\\udd13, \\ud83c\\udfc3 OK: \\u2764\\ufe0f \""), ("Example emoji, KO: 🔓, 🏃 OK: ❤️ ")); 279 | eval(("\"Example emoji, KO: \\u{d83d}\\udd13, \\ud83c\\udfc3 OK: \\u2764\\ufe0f \""), ("Example emoji, KO: 🔓, 🏃 OK: ❤️ ")); 280 | eval(("\"Example emoji, KO: \\u{d83d}\\u{dd13}, \\ud83c\\udfc3 OK: \\u2764\\ufe0f \""), ("Example emoji, KO: 🔓, 🏃 OK: ❤️ ")); 281 | eval(("\"Example emoji, KO: \\u{d83d}\\u{dd13}, \\u{d83c}\\udfc3 OK: \\u2764\\ufe0f \""), ("Example emoji, KO: 🔓, 🏃 OK: ❤️ ")); 282 | eval(("\"Example emoji, KO: \\u{d83d}\\u{dd13}, \\u{d83c}\\u{dfc3} OK: \\u2764\\ufe0f \""), ("Example emoji, KO: 🔓, 🏃 OK: ❤️ ")); 283 | eval(("\"Example emoji, KO: \\u{d83d}\\u{dd13}, \\u{d83c}\\u{dfc3} OK: \\u{2764}\\ufe0f \""), ("Example emoji, KO: 🔓, 🏃 OK: ❤️ ")); 284 | eval(("\"Example emoji, KO: \\u{d83d}\\u{dd13}, \\u{d83c}\\u{dfc3} OK: \\u{2764}\\u{fe0f} \""), ("Example emoji, KO: 🔓, 🏃 OK: ❤️ ")); 285 | eval(("\"KO: \\xffsd\""), ("KO: ÿsd")); 286 | eval(("\"KO: \\ud8\""), ("SyntaxError")); 287 | eval(("\"KO: \\zd8\""), ("KO: zd8")); 288 | eval(("\"\\1\\0\""), ("SyntaxError")); 289 | eval(("\"1\\0abc\""), ("1\0abc")); 290 | eval(("\"KO: \0\""), ("SyntaxError")); 291 | eval(("false == true"), ("false")); 292 | eval(("false + true"), ("1")); 293 | eval(("false - true"), ("-1")); 294 | eval(("NaN + 1"), ("NaN")); 295 | eval(("NaN * 1"), ("NaN")); 296 | eval(("0.24ab31 - 1"), ("SyntaxError")); 297 | eval(("0 + {1}"), ("SyntaxError")); 298 | eval(("0 + [1]"), ("01")); 299 | eval(("hello + 2"), ("ReferenceError: Can't find variable: 'hello'")); 300 | eval(("i64(\"-9223372036854775808\")"), ("-9223372036854775808")); 301 | eval(("-9223372036854775808i64"), ("-9223372036854775808")); 302 | eval(("i64(\"9223372036854775807\")"), ("9223372036854775807")); 303 | eval(("9223372036854775807i64"), ("9223372036854775807")); 304 | eval(("i64(\"-9223372036854775808\")"), ("-9223372036854775808")); 305 | eval(("u64(\"18446744073709551615\") - u64(\"18446744073709551614\")"), ("1")); 306 | eval(("18446744073709551615u64 - 18446744073709551614u64"), ("1")); 307 | eval(("u64(\"18446744073709551614\") + u64(\"1\")"), ("18446744073709551615")); 308 | eval(("i64(\"-9223372036854775808\") + i64(\"1\")"), ("-9223372036854775807")); 309 | eval(("i64(\"9223372036854775807\") - i64(\"1\")"), ("9223372036854775806")); 310 | eval(("i64(\"9223372036854775807\") - 1"), ("9223372036854776000")); 311 | eval(("u64(\"9223372036854775807\") - 1"), ("9223372036854776000")); 312 | eval(("u64(1) > 0"), ("true")); 313 | eval(("u64(1) >= 0"), ("true")); 314 | eval(("u64(0) >= 0"), ("true")); 315 | eval(("i64(0) >= 0"), ("true")); 316 | eval(("i64(0) >= 0"), ("true")); 317 | eval(("i64(-1) >= 0"), ("false")); 318 | eval(("i64(-1) >= i64(0)"), ("false")); 319 | eval(("u64(1) >= u64(0)"), ("true")); 320 | eval(("u64(1) > u64(0)"), ("true")); 321 | eval(("\"1\" >= \"2\" "), ("false")); 322 | eval(("\"2\" >= \"2\" "), ("true")); 323 | eval(("\"2\" >= \"10\" "), ("true")); 324 | eval(("\"1\" > \"2\" "), ("false")); 325 | eval(("\"2\" > \"2\" "), ("false")); 326 | eval(("\"2\" > \"10\" "), ("true")); 327 | eval(("i64(2) > i64(10)"), ("false")); 328 | eval(("i64(2) == i64(10)"), ("false")); 329 | eval(("i64(10) == i64(10)"), ("true")); 330 | eval(("u64(10) == u64(10)"), ("true")); 331 | eval(("u64(2) == u64(10)"), ("false")); 332 | eval(("\"2\" == \"2\""), ("true")); 333 | eval(("\"2\" == \"3\""), ("false")); 334 | eval(("\"2\" != \"2\""), ("false")); 335 | eval(("\"2\" != \"3\""), ("true")); 336 | eval(("i64(2) != i64(10)"), ("true")); 337 | eval(("i64(2) != i64(2)"), ("false")); 338 | eval(("u64(2) != u64(10)"), ("true")); 339 | eval(("u64(2) != u64(2)"), ("false")); 340 | eval(("true != false"), ("true")); 341 | eval(("true != true"), ("false")); 342 | eval(("true < false"), ("false")); 343 | eval(("false < true"), ("true")); 344 | eval(("true <= false"), ("false")); 345 | eval(("false <= true"), ("true")); 346 | eval(("\"2\" * \"4\""), ("8")); 347 | eval(("\"2\" + \"4\""), ("24")); 348 | eval(("i64(2) * i64(4)"), ("8")); 349 | eval(("u64(2) * u64(4)"), ("8")); 350 | eval(("i64(8) / i64(2)"), ("4")); 351 | eval(("u64(8) / u64(2)"), ("4")); 352 | eval(("2 <= 4"), ("true")); 353 | eval(("4 <= 2"), ("false")); 354 | eval(("i64(2) <= i64(4)"), ("true")); 355 | eval(("i64(4) <= i64(2)"), ("false")); 356 | eval(("u64(2) <= u64(4)"), ("true")); 357 | eval(("u64(4) <= u64(2)"), ("false")); 358 | eval(("\"2\" < \"2\""), ("false")); 359 | eval(("\"2\" < \"3\""), ("true")); 360 | eval(("\"10\" < \"2\""), ("true")); 361 | eval(("i64(2) < i64(2)"), ("false")); 362 | eval(("i64(2) < i64(3)"), ("true")); 363 | eval(("u64(2) < u64(2)"), ("false")); 364 | eval(("u64(2) < u64(3)"), ("true")); 365 | eval(("\"2\" <= \"1\""), ("false")); 366 | eval(("\"2\" <= \"2\""), ("true")); 367 | eval(("\"2\" <= \"3\""), ("true")); 368 | eval(("\"10\" <= \"2\""), ("true")); 369 | eval(("true && false"), ("false")); 370 | eval(("true || false"), ("true")); 371 | eval(("\"1\" || false"), ("true")); 372 | eval(("1 || false"), ("true")); 373 | eval(("0 || false"), ("false")); 374 | eval(("0 || false"), ("false")); 375 | eval(("100 + blank_err"), ("ReferenceError: Can't find variable: 'blank_err'")); 376 | eval(("100 + custom_err"), ("ReferenceError: hiya")); 377 | eval(("\"a \\u\\\"567\""), ("SyntaxError")); 378 | eval(("(hello) + (jello"), ("ReferenceError: Can't find variable: 'hello'")); 379 | eval(("(1) + (jello"), ("SyntaxError")); 380 | eval(("(1) && "), ("SyntaxError")); 381 | eval((" && (1)"), ("SyntaxError")); 382 | eval(("1 < (}2) < (1)"), ("SyntaxError")); 383 | eval(("1 + - 2"), ("-1")); 384 | eval(("1 +"), ("SyntaxError")); 385 | eval(("-1 + 2"), ("1")); 386 | eval(("/1"), ("SyntaxError")); 387 | eval(("10 % 2"), ("0")); 388 | eval(("10 % 3"), ("1")); 389 | eval(("i64(10) % i64(3)"), ("1")); 390 | eval(("u64(10) % u64(3)"), ("1")); 391 | eval(("\"10\" % \"3\""), ("1")); 392 | eval(("(1 || (2 > 5)) && (4 < 5 || 5 < 4)"), ("true")); 393 | eval(("true == !!true"), ("true")); 394 | eval(("true == !!true == !false"), ("true")); 395 | eval(("true == ! ! true == !false"), ("true")); 396 | eval(("true == ! ! true == ! ( 1 == 2 ) "), ("true")); 397 | eval(("cust(123)"), ("123")); 398 | eval(("cust(1) + cust(4)"), ("5")); 399 | eval(("cust(1) - cust(4)"), ("-3")); 400 | eval(("cust(2) * cust(4)"), ("8")); 401 | eval(("cust(2) / cust(4)"), ("0.5")); 402 | eval(("cust(10) % cust(3)"), ("1")); 403 | eval(("cust(10) < cust(3)"), ("false")); 404 | eval(("cust(10) <= cust(3)"), ("false")); 405 | eval(("cust(10) > cust(3)"), ("true")); 406 | eval(("cust(10) >= cust(3)"), ("true")); 407 | eval(("cust(10) == cust(3)"), ("false")); 408 | eval(("cust(10) != cust(3)"), ("true")); 409 | eval(("cust(10) && cust(0)"), ("false")); 410 | eval(("cust(10) || cust(3)"), ("true")); 411 | eval(("cust(10) || cust(3)"), ("true")); 412 | // eval(("-cust(999)"), ("OperatorError: not this time")); // custom operator 413 | // eval(("cust(-90909090) + cust(-90909090)"), ("undefined")); // custom operator 414 | // eval(("cust(-80808080) + cust(-80808080)"), ("OperatorError: bad news")); // custom operator 415 | eval(("0x1"), ("1")); 416 | eval(("0xZ"), ("SyntaxError")); 417 | eval(("Infinity"), ("Infinity")); 418 | eval(("-Infinity"), ("-Infinity")); 419 | eval(("0xFFFFFFFF"), ("4294967295")); 420 | eval(("0xFFFFFFFF+1"), ("4294967296")); 421 | eval(("0xFFFFFFFFFFFFFFFF"), ("18446744073709552000")); 422 | eval(("0xFFFFFFFFFFFFFFFF+1"), ("18446744073709552000")); 423 | eval(("true ? 1 : 2"), ("1")); 424 | eval(("false ? 1 : 2"), ("2")); 425 | eval(("false ? 1 : true ? 2 : 3"), ("2")); 426 | eval(("false ? 1 : false ? 2 : 3"), ("3")); 427 | eval(("5*2-10 ? 1 : (3*3-9 < 1 || 6+6-12 ? 8 : false) ? 2 : 3"), ("2")); 428 | eval(("(false ? 1 : 2"), ("SyntaxError")); 429 | eval(("(false) ? (0xTT) : (0xTT)"), ("SyntaxError")); 430 | eval(("(true) ? (0xTT) : (0xTT)"), ("SyntaxError")); 431 | eval(("(true) ? (0xTT) : (0xTT"), ("SyntaxError")); 432 | eval(("(true) ? (0xTT) 123"), ("SyntaxError")); 433 | eval(("(0xTT) ? (0xTT) : 123"), ("SyntaxError")); 434 | eval(("1e+10 > 0 ? \"big\" : \"small\""), ("big")); 435 | eval(("undefined"), ("undefined")); 436 | eval(("true ? () : ()"), ("SyntaxError")); 437 | eval(("undefined + 10"), ("NaN")); 438 | eval(("null"), ("null")); 439 | eval(("null + 10"), ("10")); 440 | eval(("undefined + undefined"), ("NaN")); 441 | eval(("null + null"), ("0")); 442 | eval(("null + undefined"), ("NaN")); 443 | eval(("!undefined"), ("true")); 444 | eval(("!!undefined"), ("false")); 445 | eval(("!null"), ("true")); 446 | eval(("!!null"), ("false")); 447 | eval(("null??1"), ("1")); 448 | eval(("null??0"), ("0")); 449 | eval(("undefined??1+1"), ("2")); 450 | eval(("undefined??0+1"), ("1")); 451 | eval(("false??1+1"), ("false")); 452 | eval(("true??1+1"), ("true")); 453 | eval(("false??1+1"), ("false")); 454 | eval(("true??1+1"), ("true")); 455 | eval(("(false??1)+1"), ("1")); 456 | eval(("(true??1)+1"), ("2")); 457 | eval(("(cust(1)??cust(2))+1"), ("2")); 458 | eval(("'hello \\'\\\"\\\"\\a\\xFF\\p world'"), ("hello '\"\"aÿp world")); 459 | eval(("\"\\u{A}\""), ("\n")); 460 | eval(("\'\\xFG'"), ("SyntaxError")); 461 | eval(("\"\\u{21}\""), ("!")); 462 | eval(("\"\\u{AFFF}\""), ("꿿")); 463 | eval(("\"\\u{1f516}\""), ("🔖")); 464 | eval(("\"\\v\""), ("\v")); 465 | eval(("\"\\0\""), ("\0")); 466 | eval(("\"\\u{YY}\""), ("SyntaxError")); 467 | eval(("\"\\u{FF\""), ("SyntaxError")); 468 | eval(("1,2,3,4"), ("4")); 469 | eval(("1=,2,3,4"), ("SyntaxError")); 470 | eval(("1(,2,3,4"), ("SyntaxError")); 471 | eval(("1,2,3,(4+)"), ("SyntaxError")); 472 | eval(("6<7 , 2>5 , 5"), ("5")); 473 | eval(("hello ?. world"), ("ReferenceError: Can't find variable: 'hello'")); 474 | eval(("this?.that(\"1\",\"2\")"), ("ReferenceError: Can't find variable: 'this'")); 475 | eval((" != 100"), ("SyntaxError")); 476 | eval((" >= 100"), ("SyntaxError")); 477 | eval((" (1) != (\"\\'1"), ("SyntaxError")); 478 | eval(("1 != 2 > 1 != 1"), ("true")); 479 | eval(("1 != 2 < 1 != 1"), ("false")); 480 | eval(("1 != 1 < 2 != 1"), ("true")); 481 | eval(("u64+\"hello\""), ("[Function]hello")); 482 | eval(("0.123123i64"), ("SyntaxError")); 483 | eval(("howdy.myfn1().myfn2(\"1\",2,\"3\") == 6"), ("true")); 484 | eval(("howdy.myfn1.there"), ("undefined")); 485 | eval(("howdy.myfn3.there"), ("TypeError: Cannot read properties of undefined (reading 'there')")); 486 | eval(("howdy.myfn3?.there"), ("undefined")); 487 | eval(("howdy.myfn1#e"), ("SyntaxError")); 488 | eval(("howdy.myfn1.#e"), ("SyntaxError")); 489 | eval(("#howdy.myfn1.#e"), ("SyntaxError")); 490 | eval(("howdy[\"do\"]"), ("undefined")); 491 | eval(("howdy[9i8203]"), ("SyntaxError")); 492 | eval(("howdy[\"did\"]"), ("undefined")); 493 | eval(("howdy.myfn1(9999)"), ("fantastic")); 494 | eval(("((0i64)%0i64)"), ("NaN")); 495 | eval(("((0i64)/0i64)"), ("NaN")); 496 | eval(("((0u64)%0u64)"), ("NaN")); 497 | eval(("((0u64)/0u64)"), ("NaN")); 498 | eval(("64"), ("64")); 499 | eval(("u64"), ("[Function]")); 500 | eval(("i64"), ("[Function]")); 501 | eval(("1 == \"1\""), ("true")); 502 | eval(("1 === \"1\""), ("false")); 503 | eval(("1 !== \"1\""), ("true")); 504 | eval(("\"1\" === \"1\""), ("true")); 505 | eval(("\"1\" === \"2\""), ("false")); 506 | eval(("\"1\" !== \"2\""), ("true")); 507 | eval(("false !== true"), ("true")); 508 | eval(("false !== ! true"), ("false")); 509 | // eval(("cust1 < cust2"), ("OperatorError: too bad 1")); 510 | // eval(("cust1 <= cust2"), ("OperatorError: too bad 1")); 511 | // eval(("cust1 == cust2"), ("OperatorError: too bad 1")); 512 | // eval(("cust1 > cust2"), ("OperatorError: too bad 2")); 513 | // eval(("cust1 >= cust2"), ("OperatorError: too bad 2")); 514 | // eval(("cust3 < cust2"), ("false")); 515 | // eval(("cust3 <= cust2"), ("OperatorError: too bad 2")); 516 | // eval(("cust2 > cust3"), ("false")); 517 | // eval(("cust2 >= cust3"), ("OperatorError: too bad 2")); 518 | // eval(("cust2 == cust3"), ("OperatorError: too bad 2")); 519 | // eval(("cust3 == cust2"), ("OperatorError: too bad 2")); 520 | // eval(("cust3 !== cust2"), ("OperatorError: too bad 2")); 521 | // eval(("cust3 != cust2"), ("OperatorError: too bad 2")); 522 | eval(("true.hello == undefined"), ("true")); 523 | eval(("true.hello == '11'"), ("false")); 524 | eval(("true.hello == null"), ("false")); 525 | eval(("null == null"), ("true")); 526 | eval(("[1,2,(3,4,'a','b'),3,1==2,3.5+4.5]"), ("1,2,b,3,false,8")); 527 | eval(("11*1"), ("11")); 528 | eval(("11*2"), ("22")); 529 | eval(("[11]*2"), ("22")); 530 | eval(("[11,22]*2"), ("NaN")); 531 | eval(("[]*2"), ("0")); 532 | eval(("[]+2"), ("2")); 533 | eval(("[]-2"), ("-2")); 534 | eval(("howdy()"), ("TypeError: howdy is not a function")); 535 | eval(("user1.name"), ("andy")); 536 | eval(("user1.age"), ("51")); 537 | eval(("json.name.first"), ("Janet")); 538 | eval(("json.name.last"), ("Anderson")); 539 | eval(("json.name"), ("{\"first\": \"Janet\", \"last\": \"Anderson\"}")); 540 | eval(("json.empty * 2"), ("0")); 541 | eval(("json.empty * 2"), ("0")); 542 | eval(("json.one * 2"), ("30")); 543 | eval(("json.data * 2"), ("NaN")); 544 | eval(("json.name * 2"), ("NaN")); 545 | eval(("user1 * 2"), ("NaN")); 546 | // "\"data\": [1,true,false,null,{\"a\":1}]" 547 | eval(("json.data[1] == true"), ("true")); 548 | eval(("json.data[2] == false"), ("true")); 549 | eval(("json.data[3] == null"), ("true")); 550 | eval(("json.data[0]"), ("1")); 551 | eval(("json.data.0"), ("SyntaxError")); 552 | eval(("json.data[-1]"), ("undefined")); 553 | eval(("(json.data[0]+4)*10"), ("50")); 554 | eval(("json.data[4].a"), ("1")); 555 | eval(("json.data[4].b"), ("undefined")); 556 | eval(("json.enc"), ("Big\nBot")); 557 | eval(("badj"), ("")); 558 | eval(("noj"), ("ReferenceError: Can't find variable: 'noj'")); 559 | eval(("11i64 | 22i64"), ("31")); 560 | eval(("11i64 | 22"), ("31")); 561 | eval(("11i64 | '22'"), ("31")); 562 | eval(("11i64 | 22u64"), ("31")); 563 | eval(("11i64 | null"), ("11")); 564 | eval(("11i64 | undefined"), ("11")); 565 | eval(("10i64 | true"), ("11")); 566 | eval(("11u64 | 22u64"), ("31")); 567 | eval(("11u64 | 22"), ("31")); 568 | eval(("11u64 | '22'"), ("31")); 569 | eval(("11u64 | 22i64"), ("31")); 570 | eval(("11u64 | null"), ("11")); 571 | eval(("11u64 | undefined"), ("11")); 572 | eval(("10u64 | true"), ("11")); 573 | eval(("10u64 || 0"), ("true")); 574 | eval(("10u64 || 0u64"), ("true")); 575 | eval(("10u64 || 0i64"), ("true")); 576 | eval(("10i64 || 0i64"), ("true")); 577 | eval(("'1' || '0'"), ("true")); 578 | eval(("'1' ? '2' : '3'"), ("2")); 579 | eval(("[1] ? '2' : '3'"), ("2")); 580 | eval(("[] ? '2' : '3'"), ("2")); 581 | eval(("[0] ? '2' : '3'"), ("2")); 582 | eval(("user1"), ("[Object]")); 583 | eval(("'hello' + 'world' + '99999999999999999'"), ("helloworld99999999999999999")); 584 | eval(("8888888899999999999999999 + 8888888899999999999999999"), ("1.77777778e+25")); 585 | eval(("8888888899999999999999999 + '8888888899999999999999999'"), ("8.8888889e+248888888899999999999999999")); 586 | eval((" 'hello' "), ("hello")); 587 | eval(("\t\n\r\v 'hello' "), ("hello")); 588 | eval(("\t\n\r\v\1 'hello' "), ("SyntaxError")); 589 | eval(("1 ? 2 ? 3 : 2 : 1"), ("3")); 590 | eval(("'11' < '1'"), ("false")); 591 | eval(("'11' < '11'"), ("false")) 592 | 593 | // case sensitivity toggling 594 | nocase = false; 595 | eval(("'hi' < 'HI'"), ("false")); 596 | eval(("'HI' < 'hi'"), ("true")); 597 | eval(("'HI' < 'HI'"), ("false")); 598 | eval(("'HI' < 'HII'"), ("true")); 599 | eval(("'HII' < 'HI'"), ("false")); 600 | nocase = true; 601 | eval(("'hi' < 'HI'"), ("false")); 602 | eval(("'HI' < 'hi'"), ("false")); 603 | eval(("'HI' < 'hii'"), ("true")); 604 | eval(("'hj' < 'HI'"), ("false")); 605 | eval(("'hi' < 'HJ'"), ("true")); 606 | nocase = false; 607 | 608 | eval(("'1' | (bad)"), ("ReferenceError: Can't find variable: 'bad'")); 609 | eval(("('\n') || '1'"), ("SyntaxError")); 610 | eval(("'1' | "), ("SyntaxError")); 611 | eval(("'1' | \t | 3"), ("SyntaxError")); 612 | eval(("'1' | (123) | (123 "), ("SyntaxError")); 613 | eval(("undefined.numobj"), ("TypeError: Cannot read properties of undefined (reading 'numobj')")); 614 | eval(("json.data[0+1,0+2]"), ("false")); 615 | eval(("json.data[0+1,0+]"), ("SyntaxError")); 616 | eval(("json.data[0"), ("SyntaxError")); 617 | eval(("json.data['123']"), ("undefined")); 618 | eval(("user1['e'+'rr']"), ("oh no")); 619 | eval(("user1(1"), ("SyntaxError")); 620 | eval(("numobj(1+'123',)"), ("SyntaxError")); 621 | eval(("123?"), ("SyntaxError")); 622 | eval(("json?.data[0]"), ("1")); 623 | eval(("json?.data[0]?"), ("SyntaxError")); 624 | eval(("json?.data[0]?."), ("SyntaxError")); 625 | eval((" & 1 & 1 "), ("SyntaxError")); 626 | eval((" | 1 | 1 "), ("SyntaxError")); 627 | eval(("1 + [2] + 3"), ("123")); 628 | eval(("1 * [2] * 3"), ("6")); 629 | eval(("1 * [{}] * 3"), ("SyntaxError")); 630 | 631 | // bad strings escape sequences 632 | eval(("'\\n'"), ("\n")); 633 | eval(("'"), ("SyntaxError")); 634 | eval(("'\\"), ("SyntaxError")); 635 | eval(("'\\\\"), ("SyntaxError")); 636 | eval(("'\\u"), ("SyntaxError")); 637 | eval(("'\\u'"), ("SyntaxError")); 638 | eval(("'\\u{"), ("SyntaxError")); 639 | eval(("'\\u{1"), ("SyntaxError")); 640 | eval(("'\\u{}"), ("SyntaxError")); 641 | eval(("'\\u{}'"), ("SyntaxError")); 642 | 643 | // weird codepoints 644 | eval(("'\\ufffd'"), ("�")); 645 | // The following input is an invalid pair. 646 | // Node and safari report differing results. 647 | // But, this is the correct result afaik. 648 | eval(("'\\ud801\\ufffd'"), ("�")); 649 | eval(("'\\ufffd'"), ("�")); 650 | eval(("'\\ud800'"), ("�")); 651 | eval(("'\\ud801'"), ("�")); 652 | 653 | 654 | // unsupported keywords 655 | eval(("new == true"), ("SyntaxError: Unsupported keyword 'new'")); 656 | eval(("typeof == true"), ("SyntaxError: Unsupported keyword 'typeof'")); 657 | eval(("void == true"), ("SyntaxError: Unsupported keyword 'void'")); 658 | eval(("await == true"), ("SyntaxError: Unsupported keyword 'await'")); 659 | eval(("function == true"), ("SyntaxError: Unsupported keyword 'function'")); 660 | eval(("in == true"), ("SyntaxError: Unsupported keyword 'in'")); 661 | eval(("instanceof == true"), ("SyntaxError: Unsupported keyword 'instanceof'")); 662 | eval(("yield == true"), ("SyntaxError: Unsupported keyword 'yield'")); 663 | 664 | eval(("'hello'?"), ("SyntaxError")); 665 | eval(("json?^data[0]"), ("SyntaxError")); 666 | 667 | eval(("howdy.myfn2(1,2,3) == 6"), ("true")); 668 | eval(("howdy.v1"), ("undefined")); 669 | eval(("howdy.v1.v2"), ("TypeError: Cannot read properties of undefined (reading 'v2')")); 670 | eval(("howdy.v1?.v2"), ("undefined")); 671 | eval(("howdy? 0) break; 681 | eval(("'hello' + 'world'"), ("helloworld")); 682 | } 683 | eval(("bigjson + bigjson"), ("{\"a\":123456789012345678901234567890}{\"a\":123456789012345678901234567890}")); 684 | 685 | eval(("'100' / '2'"), ("50")); 686 | eval(("-'100' + 2"), ("-98")); 687 | eval(("-'100' + -'2'"), ("-102")); 688 | eval(("-'100' + -'\\42'"), ("SyntaxError")); 689 | eval(("-'\\4100' + -'\\42'"), ("SyntaxError")); 690 | // eval(("'" "\xFF" "\xFF" "\xFF" "100'"), ("���100")); 691 | // eval(("'\\u{fffd}100'"), ("�100")); 692 | // eval(("'\\u{0x10ffff}100'"), ("�100")); 693 | // eval(("'\\u{0x11ffff}100'"), ("�100")); 694 | 695 | 696 | struct xv result = xv_eval("bad == 1", NULL); 697 | assert(xv_is_error(result)); 698 | } 699 | 700 | void test_xv_various_sysalloc(void) { 701 | test_xv_various(); 702 | } 703 | 704 | void test_xv_various_chaos(void) { 705 | test_xv_various(); 706 | } 707 | 708 | void test_xv_values(void) { 709 | assert(xv_is_undefined(xv_new_undefined())); 710 | assert(!xv_is_undefined(xv_new_null())); 711 | assert(xv_array_length(xv_new_array(NULL, 1)) == 1); 712 | assert(xv_array_length(xv_new_array(NULL, 0)) == 0); 713 | assert(xv_array_length(xv_new_undefined()) == 0); 714 | assert(xv_object_tag(xv_new_object(NULL, 99)) == 99); 715 | assert(xv_object_tag(xv_new_undefined()) == 0); 716 | assert(strcmp(xv_object(xv_new_object("hello", 99)), "hello") == 0); 717 | assert(xv_object(xv_new_undefined()) == NULL); 718 | assert(xv_bool(xv_new_boolean(true)) == true); 719 | assert(xv_bool(xv_new_boolean(false)) == false); 720 | assert(xv_bool(xv_new_undefined()) == false); 721 | assert(xv_bool(xv_new_double(0)) == false); 722 | assert(xv_bool(xv_new_double(1)) == true); 723 | assert(xv_string_compare(xv_new_string("hello"), "hello") == 0); 724 | assert(xv_string_compare(xv_new_string("hello"), "jello") < 0); 725 | assert(xv_string_compare(xv_new_string("jello"), "hello") > 0); 726 | assert(xv_string_compare(xv_new_string("jello"), NULL) > 0); 727 | assert(xv_string_compare(xv_new_string(NULL), "hello") < 0); 728 | assert(xv_string_compare(xv_new_string(NULL), NULL) == 0); 729 | assert(xv_string_compare(xv_new_json("{}"), "{}") == 0); 730 | assert(xv_string_compare(xv_new_json(NULL), "undefined") == 0); 731 | assert(xv_string_compare(xv_new_json("\"hello\""), "hello") == 0); 732 | assert(xv_string_equal(xv_new_string("hello"), "hello")); 733 | assert(!xv_string_equal(xv_new_string(NULL), "hello")); 734 | assert(xv_string_equal(xv_new_string(NULL), NULL)); 735 | assert(!xv_string_equal(xv_new_string("hello"), NULL)); 736 | assert(xv_string_equal(xv_new_json("{}"), "{}")); 737 | assert(xv_int64(xv_new_string("123")) == 123); 738 | assert(xv_int64(xv_new_string("")) == 0); 739 | assert(xv_int64(xv_new_string("123.123")) == 123); 740 | assert(xv_int64(xv_new_string("-123")) == -123); 741 | assert(xv_int64(xv_new_string("-123.123")) == -123); 742 | assert(xv_uint64(xv_new_string("123")) == 123); 743 | assert(xv_uint64(xv_new_string("")) == 0); 744 | assert(xv_uint64(xv_new_string("123.123")) == 123); 745 | assert(xv_double(xv_new_string("123")) == 123.0); 746 | assert(isnan(xv_double(xv_new_string("")))); 747 | assert(xv_double(xv_new_string("123.123")) == 123.123); 748 | assert(xv_double(xv_new_string("-123")) == -123.0); 749 | assert(xv_double(xv_new_string("+123")) == +123.0); 750 | assert(xv_double(xv_new_string("Infinity")) == INFINITY); 751 | assert(xv_double(xv_new_string("+Infinity")) == INFINITY); 752 | assert(xv_double(xv_new_string("-Infinity")) == -INFINITY); 753 | assert(isnan(xv_double(xv_new_string("NaN")))); 754 | 755 | 756 | assert(xv_string_compare(xv_new_double(123.1), "123.1") == 0); 757 | assert(xv_string_compare(xv_new_int64(-123), "-123") == 0); 758 | assert(xv_string_compare(xv_new_uint64(123), "123") == 0); 759 | assert(xv_int64(xv_new_uint64(UINT64_MAX)) == INT64_MAX); 760 | assert(xv_uint64(xv_new_int64(INT64_MIN)) == 0); 761 | assert(xv_uint64(xv_new_int64(100)) == 100); 762 | 763 | 764 | assert(xv_int64(xv_new_double(123.1)) == 123); 765 | assert(xv_int64(xv_new_double(123912039182039810293810293.1)) == INT64_MAX); 766 | assert(xv_int64(xv_new_double(-123912039182039810293810293.1)) == INT64_MIN); 767 | 768 | assert(xv_uint64(xv_new_double(123.1)) == 123); 769 | assert(xv_uint64(xv_new_double(123912039182039810293810293.1)) == UINT64_MAX); 770 | assert(xv_uint64(xv_new_double(-123912039182039810293810293.1)) == 0); 771 | 772 | assert(xv_uint64(xv_new_boolean(true)) == 1); 773 | assert(xv_int64(xv_new_boolean(true)) == 1); 774 | assert(xv_uint64(xv_new_boolean(false)) == 0); 775 | assert(xv_int64(xv_new_boolean(false)) == 0); 776 | 777 | assert(xv_type(xv_new_boolean(false)) == XV_BOOLEAN); 778 | assert(xv_type(xv_new_null()) == XV_OBJECT); 779 | assert(xv_type(xv_new_string("hello")) == XV_STRING); 780 | assert(xv_type(xv_new_double(123)) == XV_NUMBER); 781 | assert(xv_type(xv_new_undefined()) == XV_UNDEFINED); 782 | assert(xv_type(xv_new_function(numobj)) == XV_FUNCTION); 783 | char buf[256]; 784 | xv_string_copy(xv_new_error("oh no"), buf, sizeof(buf)); 785 | assert(strcmp(buf, "oh no") == 0); 786 | xv_string_copy(xv_new_error(""), buf, sizeof(buf)); 787 | assert(strcmp(buf, "") == 0); 788 | 789 | xv_string_copy(xv_new_error("oh no"), buf, 1); 790 | assert(strcmp(buf, "") == 0); 791 | 792 | xv_string_copy(xv_new_error("oh no"), buf, 2); 793 | assert(strcmp(buf, "o") == 0); 794 | 795 | assert(xv_int64(xv_new_int64(-123)) == -123); 796 | assert(xv_uint64(xv_new_uint64(123)) == 123); 797 | assert(xv_int64(xv_new_string("-123")) == -123); 798 | assert(xv_uint64(xv_new_string("123")) == 123); 799 | assert(xv_int64(xv_new_error("-123")) == 0); 800 | assert(xv_uint64(xv_new_error("123")) == 0); 801 | assert(!xv_is_oom(xv_new_error("123"))); 802 | 803 | } 804 | 805 | void test_xv_maxdepth(void) { 806 | char *expr = malloc(10000); 807 | assert(expr); 808 | int mdepth; 809 | 810 | mdepth = 100; 811 | expr[0] = '\0'; 812 | strcat(expr, "1 + "); 813 | for (int i = 0; i < mdepth; i++) strcat(expr, "("); 814 | strcat(expr, "1"); 815 | for (int i = 0; i < mdepth; i++) strcat(expr, ")"); 816 | eval(expr, "2"); 817 | 818 | 819 | mdepth = 101; 820 | expr[0] = '\0'; 821 | strcat(expr, "1 + "); 822 | for (int i = 0; i < mdepth; i++) strcat(expr, "("); 823 | strcat(expr, "1"); 824 | for (int i = 0; i < mdepth; i++) strcat(expr, ")"); 825 | eval(expr, "MaxDepthError"); 826 | 827 | 828 | 829 | 830 | 831 | } 832 | 833 | int main(int argc, char **argv) { 834 | do_test(test_xv_values); 835 | do_test(test_xv_various); 836 | do_sysalloc_test(test_xv_various_sysalloc); 837 | do_chaos_test(test_xv_various_chaos); 838 | do_test(test_xv_maxdepth); 839 | return 0; 840 | } 841 | 842 | -------------------------------------------------------------------------------- /xv.c: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Joshua J Baker. All rights reserved. 2 | // Use of this source code is governed by an MIT-style 3 | // license that can be found in the LICENSE file. 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #define __STDC_FORMAT_MACROS 12 | #include 13 | #include "ryu.h" 14 | #include "json.h" 15 | #include "xv.h" 16 | 17 | #ifndef XV_THREAD_MEMORY_SIZE 18 | #define XV_THREAD_MEMORY_SIZE 1024 19 | #endif 20 | 21 | #ifndef XV_MAXDEPTH 22 | #define XV_MAXDEPTH 100 23 | #endif 24 | 25 | enum kind { 26 | UNDEF_KIND, NULL_KIND, ERR_KIND, FLOAT_KIND, INT_KIND, UINT_KIND, 27 | STR_KIND, BOOL_KIND, FUNC_KIND, JSON_KIND, OBJECT_KIND, ARRAY_KIND, 28 | }; 29 | 30 | enum flag { 31 | FLAG_CHAIN = 1<<1, // undefined ident was chained 32 | FLAG_ESYNTAX = 1<<2, // syntax error 33 | FLAG_EOOM = 1<<3, // out of memory error 34 | FLAG_EUNDEFINED = 1<<4, // undefined ident error 35 | FLAG_ENOTFUNC = 1<<5, // not a function 36 | FLAG_EMSG = 1<<6, // custom user message 37 | FLAG_GLOBAL = 1<<7, // global variable (OBJECT_KIND) 38 | FLAG_EUNSUPKEYWORD = 1<<8, // unsupported keyword 39 | }; 40 | 41 | struct value { 42 | enum kind kind:4; // value kind 43 | enum flag flag:12; // extra flags 44 | uint64_t len:48; // length of str 45 | union { 46 | uint64_t u64; 47 | int64_t i64; 48 | double f64; 49 | bool t; 50 | const uint8_t *str; 51 | const void *obj; 52 | const struct value *arr; 53 | struct xv (*func)(struct xv value, 54 | struct xv args, void *udata); 55 | }; 56 | }; 57 | 58 | // unreachable is just a hint that code is actually unreachable or otherwise 59 | // not needed in product, but that we want to keep it in for verboseness. 60 | #define unreachable(code) { code } 61 | 62 | static void *(*_malloc)(size_t) = NULL; 63 | static void (*_free)(void *) = NULL; 64 | 65 | void xv_set_allocator( 66 | void *(*malloc)(size_t), 67 | void *(*realloc)(void*, size_t), 68 | void (*free)(void*)) 69 | { 70 | (void)realloc; 71 | _malloc = malloc; 72 | _free = free; 73 | } 74 | 75 | struct alloc { 76 | struct alloc *next; 77 | uint8_t mem[]; 78 | }; 79 | 80 | static __thread char tmem[XV_THREAD_MEMORY_SIZE]; 81 | static __thread int tmemcount = 0; 82 | static __thread size_t tmemused = 0; 83 | static __thread size_t tnumallocs = 0; 84 | static __thread size_t theapsize = 0; 85 | static __thread struct alloc *tallocs = NULL; 86 | 87 | static void *emalloc0(size_t sz) { 88 | return (_malloc?_malloc:malloc)(sz); 89 | } 90 | 91 | static void efree0(void *ptr) { 92 | (_free?_free:free)(ptr); 93 | } 94 | 95 | static void *emalloc(size_t sz) { 96 | if (sizeof(tmem)-tmemused >= sz) { 97 | if ((sz&7) != 0) sz += 8-(sz&7); // ensure 8-byte alignment 98 | void *mem = &tmem[tmemused]; 99 | tmemused += sz; 100 | tmemcount++; 101 | return mem; 102 | } else { 103 | struct alloc *alloc = emalloc0(sizeof(struct alloc)+sz); 104 | if (!alloc) return NULL; 105 | alloc->next = tallocs; 106 | tallocs = alloc; 107 | tnumallocs++; 108 | theapsize += sizeof(struct alloc)+sz; 109 | return &alloc->mem[0]; 110 | } 111 | } 112 | 113 | void xv_cleanup(void) { 114 | struct alloc *alloc = tallocs; 115 | while (alloc) { 116 | struct alloc *next = alloc->next; 117 | efree0(alloc); 118 | alloc = next; 119 | } 120 | tmemcount = 0; 121 | tmemused = 0; 122 | tnumallocs = 0; 123 | theapsize = 0; 124 | tallocs = NULL; 125 | } 126 | 127 | struct value to_value(struct xv value) { 128 | void *v = &value; 129 | return *(struct value*)v; 130 | } 131 | 132 | struct xv from_value(struct value value) { 133 | void *v = &value; 134 | return *(struct xv*)v; 135 | } 136 | 137 | struct array { 138 | struct value *items; 139 | size_t len; 140 | size_t cap; 141 | }; 142 | 143 | static bool array_push_back(struct array *arr, struct value value) { 144 | if (arr->len == arr->cap) { 145 | size_t cap = arr->cap ? arr->cap*2 : 1; 146 | struct value *items = emalloc(cap*sizeof(struct value)); 147 | if (!items) return false; 148 | memcpy(items, arr->items, arr->len*sizeof(struct value)); 149 | arr->items = items; 150 | arr->cap = cap; 151 | } 152 | arr->items[arr->len++] = value; 153 | return true; 154 | } 155 | 156 | static struct value undefined(void) { 157 | return (struct value) { 0 }; 158 | } 159 | 160 | static struct value err_syntax(void) { 161 | return (struct value) { 162 | .kind = ERR_KIND, 163 | .flag = FLAG_ESYNTAX, 164 | }; 165 | } 166 | 167 | static struct value err_notfunc(const uint8_t *ident, size_t ilen) { 168 | return (struct value) { 169 | .kind = ERR_KIND, 170 | .flag = FLAG_ENOTFUNC, 171 | .len = ilen, 172 | .str = ident, 173 | }; 174 | } 175 | 176 | static struct value err_oom(void) { 177 | return (struct value) { 178 | .kind = ERR_KIND, 179 | .flag = FLAG_EOOM 180 | }; 181 | } 182 | 183 | static struct value err_undefined(const uint8_t *ident, size_t ilen, bool chain) 184 | { 185 | return (struct value) { 186 | .kind = ERR_KIND, 187 | .flag = FLAG_EUNDEFINED|(chain?FLAG_CHAIN:0), 188 | .len = ilen, 189 | .str = ident, 190 | }; 191 | } 192 | 193 | static struct value err_msg(const char *msg) { 194 | uint8_t *str = emalloc(strlen(msg)+1); 195 | if (!str) return err_oom(); 196 | memcpy(str, msg, strlen(msg)+1); 197 | return (struct value) { 198 | .kind = ERR_KIND, 199 | .flag = FLAG_EMSG, 200 | .len = strlen(msg), 201 | .str = str, 202 | }; 203 | } 204 | 205 | static struct value err_unsupported_keyword(const uint8_t *ident, size_t ilen) { 206 | return (struct value) { 207 | .kind = ERR_KIND, 208 | .flag = FLAG_ESYNTAX|FLAG_EUNSUPKEYWORD, 209 | .len = ilen, 210 | .str = ident, 211 | }; 212 | } 213 | 214 | static struct value make_float(double x) { 215 | return (struct value) { .kind = FLOAT_KIND, .f64 = x }; 216 | } 217 | 218 | static struct value make_int(int64_t x) { 219 | return (struct value) { .kind = INT_KIND, .i64 = x }; 220 | } 221 | 222 | static struct value make_uint(uint64_t x) { 223 | return (struct value) { .kind = UINT_KIND, .u64 = x }; 224 | } 225 | 226 | static struct value make_bool(bool t) { 227 | return (struct value) { .kind = BOOL_KIND, .t = t }; 228 | } 229 | 230 | static struct value make_undefined(void) { 231 | return (struct value) { .kind = UNDEF_KIND }; 232 | } 233 | 234 | static struct value make_global(void) { 235 | return (struct value) { .kind = OBJECT_KIND, .flag = FLAG_GLOBAL }; 236 | } 237 | 238 | static struct value make_null(void) { 239 | return (struct value) { .kind = NULL_KIND }; 240 | } 241 | static struct value make_object(const void *ptr, uint32_t tag) { 242 | return (struct value) { 243 | .len = tag, 244 | .kind = OBJECT_KIND, 245 | .obj = ptr, 246 | }; 247 | } 248 | 249 | static struct value make_string(const uint8_t *str, size_t len) { 250 | return (struct value) { 251 | .kind = STR_KIND, 252 | .str = str, 253 | .len = len, 254 | }; 255 | } 256 | 257 | static struct value make_func(struct xv (*func)( 258 | struct xv value, struct xv args, void *udata)) 259 | { 260 | return (struct value) { 261 | .kind = FUNC_KIND, 262 | .func = func, 263 | }; 264 | } 265 | 266 | static struct value make_array(struct value *values, size_t nvalues) { 267 | return (struct value) { 268 | .kind = ARRAY_KIND, 269 | .len = nvalues, 270 | .arr = values, 271 | }; 272 | } 273 | 274 | 275 | static bool isnum(struct value a) { 276 | switch (a.kind) { 277 | case FLOAT_KIND: case INT_KIND: case UINT_KIND: case BOOL_KIND: 278 | case NULL_KIND: case UNDEF_KIND: 279 | return true; 280 | default: 281 | return false; 282 | } 283 | } 284 | 285 | struct writer { 286 | char *dst; 287 | size_t n; 288 | size_t count; 289 | }; 290 | 291 | static void write_nullterm(struct writer *wr) { 292 | if (wr->n > wr->count) wr->dst[wr->count] = '\0'; 293 | else if (wr->n > 0) wr->dst[wr->n-1] = '\0'; 294 | } 295 | 296 | static void write_char(struct writer *wr, char b) { 297 | if (wr->count < wr->n) wr->dst[wr->count] = b; 298 | wr->count++; 299 | } 300 | 301 | static void write_cstr(struct writer *wr, const char *s) { 302 | while (*s) write_char(wr, *(s++)); 303 | } 304 | 305 | static void write_bytes(struct writer *wr, const uint8_t *s, size_t len) { 306 | for (size_t i = 0; i < len; i++) { 307 | write_char(wr, (char)s[i]); 308 | } 309 | } 310 | 311 | struct eval_context { 312 | const uint8_t *expr; // original expression 313 | size_t len; // 314 | int steps; // all possible steps 315 | void (*iter)(struct value, void *udata); // iterator, if any 316 | void *iter_udata; // iterator udata, if any 317 | struct xv_env *env; // user context 318 | }; 319 | 320 | static struct value make_json(const uint8_t *str, size_t len) { 321 | struct json json = json_parsen((char*)str, len); 322 | size_t rawlen; 323 | switch (json_type(json)) { 324 | case JSON_STRING: 325 | rawlen = json_raw_length(json); 326 | if (json_string_is_escaped(json)) { 327 | // must unescape string into a heap allocation 328 | uint8_t *mem = emalloc(rawlen+1); 329 | if (!mem) return err_oom(); 330 | memset(mem, 0, rawlen+1); 331 | size_t n = json_string_copy(json, (char*)mem, rawlen+1); 332 | return make_string(mem, n); 333 | } else { 334 | // use the raw string 335 | const char *raw = json_raw(json); 336 | if (rawlen <= 2) { 337 | return make_string(NULL, 0); 338 | } 339 | return make_string((uint8_t*)raw+1, rawlen-2); 340 | } 341 | case JSON_NUMBER: 342 | return make_float(json_double(json)); 343 | case JSON_NULL: 344 | if (json_exists(json)) { 345 | return make_null(); 346 | } else { 347 | return make_undefined(); 348 | } 349 | case JSON_TRUE: 350 | return make_bool(true); 351 | case JSON_FALSE: 352 | return make_bool(false); 353 | default: 354 | // JSON_ARRAY, JSON_OBJECT 355 | return (struct value) { 356 | .kind = JSON_KIND, 357 | .str = (uint8_t*)json_raw(json), 358 | .len = json_raw_length(json), 359 | }; 360 | } 361 | } 362 | 363 | ///////////////////////////////// 364 | // JS native type conversions 365 | ///////////////////////////////// 366 | 367 | static int64_t conv_ttoi(bool t); 368 | static uint64_t conv_ttou(bool t); 369 | static double conv_ttof(bool t); 370 | static bool conv_ftot(double f); 371 | static int64_t conv_ftoi(double f); 372 | static uint64_t conv_ftou(double f); 373 | static bool conv_itot(int64_t i); 374 | static double conv_itof(int64_t i); 375 | static uint64_t conv_itou(int64_t i); 376 | static bool conv_utot(uint64_t u); 377 | static double conv_utof(uint64_t u); 378 | static int64_t conv_utoi(uint64_t u); 379 | static bool conv_atot(const char *a, size_t alen); 380 | static double conv_atof(const char *a, size_t alen); 381 | static int64_t conv_atoi(const char *a, size_t alen); 382 | static uint64_t conv_atou(const char *a, size_t alen); 383 | 384 | static double to_f64(struct value a) { 385 | if (a.kind == FLOAT_KIND) return a.f64; 386 | switch (a.kind) { 387 | case UNDEF_KIND: 388 | return NAN; 389 | case NULL_KIND: 390 | return 0; 391 | case BOOL_KIND: 392 | return conv_ttof(a.t); 393 | case INT_KIND: 394 | return conv_itof(a.i64); 395 | case UINT_KIND: 396 | return conv_utof(a.u64); 397 | case STR_KIND: 398 | return conv_atof((char*)a.str, a.len); 399 | case ARRAY_KIND: 400 | if (a.len == 0) { 401 | return 0; 402 | } 403 | if (a.len == 1) { 404 | return to_f64(a.arr[0]); 405 | } 406 | return NAN; 407 | case JSON_KIND: 408 | { 409 | struct json json = json_parsen((char*)a.str, a.len); 410 | if (json_type(json) == JSON_ARRAY) { 411 | struct json val = json_first(json); 412 | if (!json_exists(val)) { 413 | return 0; 414 | } 415 | if (!json_exists(json_next(val))) { 416 | return to_f64(make_json((uint8_t*)json_raw(val), 417 | json_raw_length(val))); 418 | } 419 | } 420 | return NAN; 421 | } 422 | default: 423 | // everything else NaN 424 | return NAN; 425 | } 426 | } 427 | 428 | static int64_t to_i64(struct value a) { 429 | if (a.kind == INT_KIND) return a.i64; 430 | switch (a.kind) { 431 | case NULL_KIND: 432 | return 0; 433 | case BOOL_KIND: 434 | return conv_ttoi(a.t); 435 | case FLOAT_KIND: 436 | return conv_ftoi(a.f64); 437 | case UINT_KIND: 438 | return conv_utoi(a.u64); 439 | case STR_KIND: 440 | return conv_atoi((char*)a.str, a.len); 441 | default: 442 | // everything else to f64 then to u64 443 | return conv_ftoi(to_f64(a)); 444 | } 445 | } 446 | 447 | static uint64_t to_u64(struct value a) { 448 | if (a.kind == UINT_KIND) return a.u64; 449 | switch (a.kind) { 450 | case BOOL_KIND: 451 | return conv_ttou(a.t); 452 | case FLOAT_KIND: 453 | return conv_ftou(a.f64); 454 | case INT_KIND: 455 | return conv_itou(a.i64); 456 | case STR_KIND: 457 | return conv_atou((char*)a.str, a.len); 458 | default: 459 | // everything else to f64 then to u64 460 | return conv_ftou(to_f64(a)); 461 | } 462 | } 463 | 464 | static bool to_bool(struct value a) { 465 | if (a.kind == BOOL_KIND) return a.t; 466 | switch (a.kind) { 467 | case UNDEF_KIND: 468 | return false; 469 | case NULL_KIND: 470 | return false; 471 | case FLOAT_KIND: 472 | return conv_ftot(a.f64); 473 | case INT_KIND: 474 | return conv_itot(a.i64); 475 | case UINT_KIND: 476 | return conv_utot(a.u64); 477 | case STR_KIND: 478 | return conv_atot((char*)a.str, a.len); 479 | default: 480 | return true; 481 | } 482 | } 483 | 484 | static void write_error(struct writer *wr, struct value value); 485 | static void write_double(struct writer *wr, double f); 486 | static void write_int(struct writer *wr, int64_t i); 487 | static void write_uint(struct writer *wr, uint64_t u); 488 | 489 | static void write_value(struct writer *wr, struct value value) { 490 | switch (value.kind) { 491 | case UNDEF_KIND: 492 | write_cstr(wr, "undefined"); 493 | break; 494 | case NULL_KIND: 495 | write_cstr(wr, "null"); 496 | break; 497 | case ERR_KIND: 498 | write_error(wr, value); 499 | break; 500 | case FLOAT_KIND: 501 | write_double(wr, value.f64); 502 | break; 503 | case INT_KIND: 504 | write_int(wr, value.i64); 505 | break; 506 | case UINT_KIND: 507 | write_uint(wr, value.u64); 508 | break; 509 | case STR_KIND: 510 | write_bytes(wr, value.str, value.len); 511 | break; 512 | case BOOL_KIND: 513 | write_cstr(wr, value.t ? "true" : "false"); 514 | break; 515 | case FUNC_KIND: 516 | write_cstr(wr, "[Function]"); 517 | break; 518 | case JSON_KIND: 519 | write_bytes(wr, value.str, value.len); 520 | break; 521 | case OBJECT_KIND: 522 | write_cstr(wr, "[Object]"); 523 | break; 524 | case ARRAY_KIND: 525 | for (size_t i = 0; i < value.len; i++) { 526 | if (i > 0) { 527 | write_char(wr, ','); 528 | } 529 | write_value(wr, value.arr[i]); 530 | } 531 | break; 532 | } 533 | } 534 | 535 | static const uint8_t *to_str(struct value a, size_t *len, 536 | char buf[], size_t bufsize) 537 | { 538 | if (a.kind == STR_KIND) { 539 | *len = a.len; 540 | return a.str; 541 | } 542 | struct writer wr = (struct writer){ .dst = buf, .n = bufsize }; 543 | write_value(&wr, a); 544 | uint8_t *mem; 545 | if (wr.count >= bufsize) { 546 | mem = emalloc(wr.count+1); 547 | if (!mem) { 548 | *len = 0; 549 | return NULL; 550 | } 551 | wr = (struct writer){ .dst = (char*)mem, .n = wr.count+1 }; 552 | write_value(&wr, a); 553 | } else { 554 | mem = (uint8_t*)buf; 555 | } 556 | *len = wr.count; 557 | return mem; 558 | } 559 | 560 | static bool is_err(struct value value) { 561 | return value.kind == ERR_KIND; 562 | } 563 | 564 | static bool has_suffix(const uint8_t *s, size_t len, const char *suffix) { 565 | size_t slen = strlen(suffix); 566 | return len >= slen && memcmp(s+len-slen, suffix, slen) == 0; 567 | } 568 | 569 | static bool isws(uint8_t c) { 570 | if (c > ' ') return false; 571 | switch (c) { 572 | case '\t': case '\n': case '\v': case '\f': case '\r': case ' ': 573 | return true; 574 | default: 575 | return false; 576 | } 577 | } 578 | 579 | static const uint8_t *trim(const uint8_t *s, size_t len, size_t *out_len) { 580 | while (len > 0 && isws(s[0])) { 581 | s++; 582 | len--; 583 | } 584 | while (len > 0 && isws(s[len-1])) { 585 | len--; 586 | } 587 | *out_len = len; 588 | return s; 589 | } 590 | 591 | static uint64_t parse_uint(const uint8_t *s, size_t len, int base, bool *ok) { 592 | char *end = NULL; 593 | uint64_t x = strtoull((char*)s, &end, base); 594 | *ok = (size_t)((uint8_t*)end - s) == len; 595 | return x; 596 | } 597 | 598 | static int64_t parse_int(const uint8_t *s, size_t len, int base, bool *ok) { 599 | char *end = NULL; 600 | int64_t x = strtoll((char*)s, &end, base); 601 | *ok = (size_t)((uint8_t*)end - s) == len; 602 | return x; 603 | } 604 | 605 | static double parse_float(const uint8_t *s, size_t len, bool *ok) { 606 | char *end = NULL; 607 | double x = strtod((char*)s, &end); 608 | *ok = (size_t)((uint8_t*)end - s) == len; 609 | return x; 610 | } 611 | 612 | // Operator Precedence 613 | // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence 614 | enum step { 615 | STEP_COMMA = 1<<1, // 1: Comma / Sequence 616 | STEP_TERNS = 1<<2, // 3: Conditional (ternary) operator 617 | STEP_LOGICAL_OR = 1<<3, // 4: Logical OR (||) Nullish coalescing operator (??) 618 | STEP_LOGICAL_AND = 1<<4, // 5: Logical AND (&&) 619 | STEP_BITWISE_OR = 1<<5, // 6: Bitwise OR (|) 620 | STEP_BITWISE_XOR = 1<<6, // 7: Bitwise XOR (^) 621 | STEP_BITWISE_AND = 1<<7, // 8: Bitwise AND (&) 622 | STEP_EQUALITY = 1<<8, // 9: Equality (==) (!=) 623 | STEP_COMPS = 1<<9, // 10: Comparison (<) (<=) (>) (>=) 624 | STEP_SUMS = 1<<10, // 12: Summation (-) (+) 625 | STEP_FACTS = 1<<11, // 13: Factors (*) (/) 626 | }; 627 | 628 | // all step tokens 629 | // op_steps = { 630 | // ',': stepComma, // ',' 631 | // '?': stepTerns | stepLogicalOR, // '?:' '??' 632 | // ':': stepTerns, // '?:' 633 | // '|': stepLogicalOR | stepBitwiseOR, // '||' '|' 634 | // '&': stepLogicalAND | stepBitwiseAND, // '&&' '&' 635 | // '^': stepBitwiseXOR, // '^' 636 | // '=': stepComps | stepEquality, // '==' '<=' '>=' 637 | // '!': stepEquality, // '!' '!=' 638 | // '<': stepComps, // '<' '<=' 639 | // '>': stepComps, // '>' '>=' 640 | // '+': stepSums, // '+' 641 | // '-': stepSums, // '-' 642 | // '*': stepFacts, // '*' 643 | // '/': stepFacts, // '/' 644 | // '%': stepFacts, // '%' 645 | // } 646 | 647 | static uint16_t op_steps[256] = { 648 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 649 | 0,0,0,0,0,0,0,0,0,0,0,0, // 650 | /*'!'*/ STEP_EQUALITY, // '!' '!=' 651 | 0,0,0, // 652 | /*'%'*/ STEP_FACTS, // '%', 653 | /*'&'*/ STEP_LOGICAL_AND|STEP_BITWISE_AND, // '&&' '&' 654 | 0,0,0, // 655 | /*'*'*/ STEP_FACTS, // '*', 656 | /*'+'*/ STEP_SUMS, // '+' 657 | /*','*/ STEP_COMMA, // ',' 658 | /*'-'*/ STEP_SUMS, // '-' 659 | 0, // 660 | /*'/'*/ STEP_FACTS, // '/', 661 | 0,0,0,0,0,0,0,0,0,0, // 662 | /*':'*/ STEP_TERNS, // '?:' 663 | 0, // 664 | /*'<'*/ STEP_COMPS, // '<' '<=' 665 | /*'='*/ STEP_COMPS|STEP_EQUALITY, // '==' '<=' '>=' 666 | /*'>'*/ STEP_COMPS, // '<' '<=', 667 | /*'?'*/ STEP_TERNS|STEP_LOGICAL_OR, // '?:' '??' 668 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 669 | 0,0,0,0,0,0,0,0,0, // 670 | /*'^'*/ STEP_BITWISE_XOR, // '^' 671 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 672 | 0,0,0,0,0,0,0,0, // 673 | /*'|'*/ STEP_LOGICAL_OR|STEP_BITWISE_OR, // '||' '|' 674 | }; 675 | 676 | 677 | static struct value eval_auto(int step, const uint8_t *expr, size_t len, 678 | struct eval_context *ctx, int depth); 679 | 680 | static const uint8_t *read_group(const uint8_t *data, size_t len, 681 | size_t *len_out); 682 | 683 | static struct value eval_expr(const uint8_t *expr, size_t len, 684 | struct eval_context *ctx, int depth); 685 | 686 | 687 | static struct value eval_comma(const uint8_t *expr, size_t len, 688 | struct eval_context *ctx, int depth) 689 | { 690 | size_t s = 0; 691 | struct value res = { 0 }; 692 | void (*iter)(struct value, void *udata) = ctx->iter; 693 | void *iter_udata = ctx->iter_udata; 694 | size_t glen; 695 | const uint8_t *g; 696 | for (size_t i = 0; i < len; i++) { 697 | switch (expr[i]) { 698 | case ',': 699 | ctx->iter = NULL; // disable the iter 700 | ctx->iter_udata = NULL; // disable the iter 701 | res = eval_auto(STEP_COMMA<<1, expr+s, i-s, ctx, depth); 702 | ctx->iter = iter; // enable the iter 703 | ctx->iter_udata = iter_udata; // enable the iter 704 | if (is_err(res)) return res; 705 | if (ctx->iter) ctx->iter(res, ctx->iter_udata); 706 | s = i + 1; 707 | break; 708 | case '(': case '[': case '{': case '"': case '\'': 709 | g = read_group(expr+i, len-i, &glen); 710 | if (!g) return err_syntax(); 711 | i = i + glen - 1; 712 | break; 713 | } 714 | } 715 | res = eval_auto(STEP_COMMA<<1, expr+s, len-s, ctx, depth); 716 | if (is_err(res)) return res; 717 | if (ctx->iter) { 718 | ctx->iter(res, ctx->iter_udata); 719 | } 720 | return res; 721 | } 722 | 723 | static struct value eval_terns(const uint8_t *expr, size_t len, 724 | struct eval_context *ctx, int depth) 725 | { 726 | const uint8_t *cond = NULL; 727 | size_t condlen = 0; 728 | size_t s = 0; 729 | size_t tdepth = 0; 730 | size_t glen; 731 | const uint8_t *g; 732 | for (size_t i = 0; i < len; i++) { 733 | switch (expr[i]) { 734 | case '?': 735 | if (i+1 < len && (expr[i+1] == '?' || expr[i+1] == '.')) { 736 | // '??' or '?.' operator 737 | i++; 738 | continue; 739 | } 740 | if (tdepth == 0) { 741 | cond = expr; 742 | condlen = i; 743 | s = i + 1; 744 | } 745 | tdepth++; 746 | break; 747 | case ':': 748 | tdepth--; 749 | if (tdepth == 0) { 750 | const uint8_t *left = expr+s; 751 | size_t leftlen = i-s; 752 | const uint8_t *right = expr+i+1; 753 | size_t rightlen = len-(i+1); 754 | struct value res = eval_expr(cond, condlen, ctx, depth); 755 | if (is_err(res)) return res; 756 | if (to_bool(res)) { 757 | return eval_expr(left, leftlen, ctx, depth); 758 | } 759 | return eval_expr(right, rightlen, ctx, depth); 760 | } 761 | break; 762 | case '(': case '[': case '{': case '"': case '\'': 763 | g = read_group(expr+i, len-i, &glen); 764 | if (!g) return err_syntax(); 765 | i = i + glen - 1; 766 | break; 767 | } 768 | } 769 | if (tdepth == 0) { 770 | return eval_auto(STEP_TERNS<<1, expr, len, ctx, depth); 771 | } 772 | return err_syntax(); 773 | } 774 | 775 | static struct value vcoalesce(struct value a, struct value b) { 776 | switch (a.kind) { 777 | case UNDEF_KIND: case NULL_KIND: 778 | return b; 779 | default: 780 | return a; 781 | } 782 | } 783 | 784 | static struct value vor(struct value a, struct value b) { 785 | return make_bool(to_bool(a) || to_bool(b)); 786 | } 787 | 788 | static struct value vand(struct value a, struct value b) { 789 | return make_bool(to_bool(a) && to_bool(b)); 790 | } 791 | 792 | static struct value vband(struct value a, struct value b) { 793 | if (a.kind == b.kind) { 794 | switch (a.kind) { 795 | case INT_KIND: 796 | return make_int(a.i64 & b.i64); 797 | case UINT_KIND: 798 | return make_uint(a.u64 & b.u64); 799 | default: 800 | break; 801 | } 802 | } 803 | return make_float(conv_itof(to_i64(a) & to_i64(b))); 804 | } 805 | 806 | static struct value vbxor(struct value a, struct value b) { 807 | if (a.kind == b.kind) { 808 | switch (a.kind) { 809 | case INT_KIND: 810 | return make_int(a.i64 ^ b.i64); 811 | case UINT_KIND: 812 | return make_uint(a.u64 ^ b.u64); 813 | default: 814 | break; 815 | } 816 | } 817 | return make_float(conv_itof(to_i64(a) ^ to_i64(b))); 818 | } 819 | 820 | static struct value vbor(struct value a, struct value b) { 821 | if (a.kind == b.kind) { 822 | switch (a.kind) { 823 | case INT_KIND: 824 | return make_int(a.i64 | b.i64); 825 | case UINT_KIND: 826 | return make_uint(a.u64 | b.u64); 827 | default: 828 | break; 829 | } 830 | } 831 | return make_float(conv_itof(to_i64(a) | to_i64(b))); 832 | } 833 | 834 | static bool string_less(const uint8_t *a, size_t alen, 835 | const uint8_t *b, size_t blen) 836 | { 837 | size_t n = alen < blen ? alen : blen; 838 | for (size_t i = 0; i < n; i++) { 839 | if (a[i] < b[i]) return true; 840 | if (a[i] > b[i]) return false; 841 | } 842 | return alen < blen; 843 | } 844 | 845 | static bool string_less_insensitive(const uint8_t *a, size_t alen, 846 | const uint8_t *b, size_t blen) 847 | { 848 | size_t n = alen < blen ? alen : blen; 849 | for (size_t i = 0; i < n; i++) { 850 | uint8_t ach = (uint8_t)tolower((char)a[i]); 851 | uint8_t bch = (uint8_t)tolower((char)b[i]); 852 | if (ach < bch) return true; 853 | if (ach > bch) return false; 854 | } 855 | return alen < blen; 856 | } 857 | 858 | static struct value vlt(struct value a, struct value b, 859 | struct eval_context *ctx) 860 | { 861 | if (a.kind == b.kind) { 862 | bool less; 863 | switch (a.kind) { 864 | case FLOAT_KIND: 865 | return make_bool(a.f64 < b.f64); 866 | case INT_KIND: 867 | return make_bool(a.i64 < b.i64); 868 | case UINT_KIND: 869 | return make_bool(a.u64 < b.u64); 870 | case STR_KIND: 871 | if (ctx && ctx->env && ctx->env->no_case) { 872 | less = string_less_insensitive(a.str, a.len, b.str, b.len); 873 | } else { 874 | less = string_less(a.str, a.len, b.str, b.len); 875 | } 876 | return make_bool(less); 877 | default: 878 | break; 879 | } 880 | } 881 | return make_bool(to_f64(a) < to_f64(b)); 882 | } 883 | 884 | static struct value vlte(struct value a, struct value b, 885 | struct eval_context *ctx) 886 | { 887 | struct value t = vlt(a, b, ctx); 888 | if (t.t) return t; 889 | t = vlt(b, a, ctx); 890 | return make_bool(!t.t); 891 | } 892 | 893 | static struct value vgt(struct value a, struct value b, 894 | struct eval_context *ctx) 895 | { 896 | return vlt(b, a, ctx); 897 | } 898 | 899 | static struct value vgte(struct value a, struct value b, 900 | struct eval_context *ctx) 901 | { 902 | struct value t = vgt(a, b, ctx); 903 | if (t.t) return t; 904 | t = vgt(b, a, ctx); 905 | return make_bool(!t.t); 906 | } 907 | 908 | static struct value veq(struct value a, struct value b, 909 | struct eval_context *ctx) 910 | { 911 | if (a.kind != b.kind) { // && a.kind != OBJ_KIND && b.kind != OBJ_KIND) { 912 | return make_bool(to_f64(a) == to_f64(b)); // MARK: float equality 913 | } 914 | struct value t = vlt(a, b, ctx); 915 | if (t.t) return make_bool(false); 916 | t = vlt(b, a, ctx); 917 | return make_bool(!t.t); 918 | } 919 | 920 | static struct value vneq(struct value a, struct value b, 921 | struct eval_context *ctx) 922 | { 923 | struct value t = veq(a, b, ctx); 924 | return make_bool(!t.t); 925 | } 926 | 927 | static struct value vseq(struct value a, struct value b, 928 | struct eval_context *ctx) 929 | { 930 | if (a.kind == b.kind) { 931 | return veq(a, b, ctx); 932 | } 933 | return make_bool(false); 934 | } 935 | 936 | static struct value vsneq(struct value a, struct value b, 937 | struct eval_context *ctx) 938 | { 939 | struct value t = vseq(a, b, ctx); 940 | return make_bool(!t.t); 941 | } 942 | 943 | static struct value logical_or(struct value left, uint8_t op, 944 | const uint8_t *expr, size_t len, struct eval_context *ctx, int depth) 945 | { 946 | expr = trim(expr, len, &len); 947 | if (len == 0) return err_syntax(); 948 | struct value right = eval_auto(STEP_LOGICAL_OR<<1, expr, len, ctx, depth); 949 | if (is_err(right)) return right; 950 | switch (op) { 951 | case '|': 952 | return vor(left, right); 953 | case '?': 954 | return vcoalesce(left, right); 955 | default: 956 | return right; 957 | } 958 | } 959 | 960 | static struct value eval_logical_or(const uint8_t *expr, size_t len, 961 | struct eval_context *ctx, int depth) 962 | { 963 | size_t s = 0; 964 | struct value left = { 0 }; 965 | uint8_t op = 0; 966 | size_t glen; 967 | const uint8_t *g; 968 | for (size_t i = 0; i < len; i++) { 969 | switch (expr[i]) { 970 | case '?': 971 | if (i+1 < len && expr[i+1] == '.') { 972 | // '?.' operator 973 | i++; 974 | continue; 975 | } 976 | // fall through 977 | case '|': 978 | if (i+1 == len) return err_syntax(); 979 | if (expr[i+1] != expr[i]) { 980 | // bitwise OR 981 | i++; 982 | continue; 983 | } 984 | left = logical_or(left, op, expr+s, i-s, ctx, depth); 985 | if (is_err(left)) return left; 986 | op = expr[i]; 987 | i++; 988 | s = i + 1; 989 | break; 990 | case '(': case '[': case '{': case '"': case '\'': 991 | g = read_group(expr+i, len-i, &glen); 992 | if (!g) return err_syntax(); 993 | i = i + glen - 1; 994 | break; 995 | } 996 | } 997 | return logical_or(left, op, expr+s, len-s, ctx, depth); 998 | 999 | } 1000 | 1001 | static struct value logical_and(struct value left, uint8_t op, 1002 | const uint8_t *expr, size_t len, struct eval_context *ctx, int depth) 1003 | { 1004 | expr = trim(expr, len, &len); 1005 | if (len == 0) return err_syntax(); 1006 | struct value right = eval_auto(STEP_LOGICAL_AND<<1, expr, len, ctx, depth); 1007 | if (is_err(right)) return right; 1008 | switch (op) { 1009 | case '&': 1010 | return vand(left, right); 1011 | default: 1012 | return right; 1013 | } 1014 | } 1015 | 1016 | static struct value eval_logical_and(const uint8_t *expr, size_t len, 1017 | struct eval_context *ctx, int depth) 1018 | { 1019 | size_t s = 0; 1020 | struct value left = { 0 }; 1021 | uint8_t op = 0; 1022 | size_t glen; 1023 | const uint8_t *g; 1024 | for (size_t i = 0; i < len; i++) { 1025 | switch (expr[i]) { 1026 | case '&': 1027 | if (i+1 == len) return err_syntax(); 1028 | if (expr[i+1] != '&') { 1029 | // bitwise AND 1030 | i++; 1031 | continue; 1032 | } 1033 | left = logical_and(left, op, expr+s, i-s, ctx, depth); 1034 | if (is_err(left)) return left; 1035 | op = expr[i]; 1036 | i++; 1037 | s = i + 1; 1038 | break; 1039 | case '(': case '[': case '{': case '"': case '\'': 1040 | g = read_group(expr+i, len-i, &glen); 1041 | if (!g) return err_syntax(); 1042 | i = i + glen - 1; 1043 | break; 1044 | } 1045 | } 1046 | return logical_and(left, op, expr+s, len-s, ctx, depth); 1047 | } 1048 | 1049 | static struct value bitwise_or(struct value left, uint8_t op, 1050 | const uint8_t *expr, size_t len, struct eval_context *ctx, int depth) 1051 | { 1052 | expr = trim(expr, len, &len); 1053 | if (len == 0) return err_syntax(); 1054 | struct value right = eval_auto(STEP_BITWISE_OR<<1, expr, len, ctx, depth); 1055 | if (is_err(right)) return right; 1056 | switch (op) { 1057 | case '|': 1058 | return vbor(left, right); 1059 | default: 1060 | return right; 1061 | } 1062 | } 1063 | 1064 | static struct value eval_bitwise_or(const uint8_t *expr, size_t len, 1065 | struct eval_context *ctx, int depth) 1066 | { 1067 | size_t s = 0; 1068 | struct value left = { 0 }; 1069 | uint8_t op = 0; 1070 | size_t glen; 1071 | const uint8_t *g; 1072 | for (size_t i = 0; i < len; i++) { 1073 | switch (expr[i]) { 1074 | case '|': 1075 | left = bitwise_or(left, op, expr+s, i-s, ctx, depth); 1076 | if (is_err(left)) return left; 1077 | op = expr[i]; 1078 | s = i + 1; 1079 | break; 1080 | case '(': case '[': case '{': case '"': case '\'': 1081 | g = read_group(expr+i, len-i, &glen); 1082 | unreachable( 1083 | // unreachable due to eval_logical_or already checking 1084 | // this group. 1085 | if (!g) return err_syntax(); 1086 | ) 1087 | i = i + glen - 1; 1088 | break; 1089 | } 1090 | } 1091 | return bitwise_or(left, op, expr+s, len-s, ctx, depth); 1092 | } 1093 | 1094 | static struct value bitwise_xor(struct value left, uint8_t op, 1095 | const uint8_t *expr, size_t len, struct eval_context *ctx, int depth) 1096 | { 1097 | expr = trim(expr, len, &len); 1098 | if (len == 0) return err_syntax(); 1099 | struct value right = eval_auto(STEP_BITWISE_XOR<<1, expr, len, ctx, depth); 1100 | if (is_err(right)) return right; 1101 | switch (op) { 1102 | case '^': 1103 | return vbxor(left, right); 1104 | default: 1105 | return right; 1106 | } 1107 | } 1108 | 1109 | static struct value eval_bitwise_xor(const uint8_t *expr, size_t len, 1110 | struct eval_context *ctx, int depth) 1111 | { 1112 | size_t s = 0; 1113 | struct value left = { 0 }; 1114 | uint8_t op = 0; 1115 | size_t glen; 1116 | const uint8_t *g; 1117 | for (size_t i = 0; i < len; i++) { 1118 | switch (expr[i]) { 1119 | case '^': 1120 | left = bitwise_xor(left, op, expr+s, i-s, ctx, depth); 1121 | if (is_err(left)) return left; 1122 | op = expr[i]; 1123 | s = i + 1; 1124 | break; 1125 | case '(': case '[': case '{': case '"': case '\'': 1126 | g = read_group(expr+i, len-i, &glen); 1127 | if (!g) return err_syntax(); 1128 | i = i + glen - 1; 1129 | break; 1130 | } 1131 | } 1132 | return bitwise_xor(left, op, expr+s, len-s, ctx, depth); 1133 | } 1134 | 1135 | static struct value bitwise_and(struct value left, uint8_t op, 1136 | const uint8_t *expr, size_t len, struct eval_context *ctx, int depth) 1137 | { 1138 | expr = trim(expr, len, &len); 1139 | if (len == 0) return err_syntax(); 1140 | struct value right = eval_auto(STEP_BITWISE_AND<<1, expr, len, ctx, depth); 1141 | if (is_err(right)) return right; 1142 | switch (op) { 1143 | case '&': 1144 | return vband(left, right); 1145 | default: 1146 | return right; 1147 | } 1148 | } 1149 | 1150 | static struct value eval_bitwise_and(const uint8_t *expr, size_t len, 1151 | struct eval_context *ctx, int depth) 1152 | { 1153 | size_t s = 0; 1154 | struct value left = { 0 }; 1155 | uint8_t op = 0; 1156 | size_t glen; 1157 | const uint8_t *g; 1158 | for (size_t i = 0; i < len; i++) { 1159 | switch (expr[i]) { 1160 | case '&': 1161 | left = bitwise_and(left, op, expr+s, i-s, ctx, depth); 1162 | if (is_err(left)) return left; 1163 | op = expr[i]; 1164 | s = i + 1; 1165 | break; 1166 | case '(': case '[': case '{': case '"': case '\'': 1167 | g = read_group(expr+i, len-i, &glen); 1168 | unreachable( 1169 | // unreachable due to eval_logical_or already checking 1170 | // this group. 1171 | if (!g) return err_syntax(); 1172 | ); 1173 | i = i + glen - 1; 1174 | break; 1175 | } 1176 | } 1177 | return bitwise_and(left, op, expr+s, len-s, ctx, depth); 1178 | } 1179 | 1180 | static struct value equal(struct value left, uint8_t op, 1181 | const uint8_t *expr, size_t len, struct eval_context *ctx, int depth) 1182 | { 1183 | bool neg = false; 1184 | bool boolit = false; 1185 | expr = trim(expr, len, &len); 1186 | while(1) { 1187 | if (len == 0) return err_syntax(); 1188 | if (expr[0] != '!') break; 1189 | neg = !neg; 1190 | boolit = true; 1191 | expr++; 1192 | len--; 1193 | expr = trim(expr, len, &len); 1194 | } 1195 | // parse next expression 1196 | struct value right = eval_auto(STEP_EQUALITY<<1, expr, len, ctx, depth); 1197 | if (is_err(right)) return right; 1198 | if (boolit) { 1199 | if (right.kind != BOOL_KIND) { 1200 | right = make_bool(to_bool(right)); 1201 | } 1202 | if (neg) { 1203 | right = make_bool(!right.t); 1204 | } 1205 | } 1206 | switch (op) { 1207 | case '=': 1208 | return veq(left, right, ctx); 1209 | case '!': 1210 | return vneq(left, right, ctx); 1211 | case '=' + 32: 1212 | return vseq(left, right, ctx); 1213 | case '!' + 32: 1214 | return vsneq(left, right, ctx); 1215 | default: 1216 | return right; 1217 | } 1218 | } 1219 | 1220 | static struct value eval_equality(const uint8_t *expr, size_t len, 1221 | struct eval_context *ctx, int depth) 1222 | { 1223 | size_t s = 0; 1224 | struct value left = { 0 }; 1225 | uint8_t op = 0; 1226 | size_t glen; 1227 | const uint8_t *g; 1228 | uint8_t opch; 1229 | size_t opsz; 1230 | for (size_t i = 0; i < len; i++) { 1231 | switch (expr[i]) { 1232 | case '=': case '!': 1233 | opch = expr[i]; 1234 | opsz = 1; 1235 | switch (opch) { 1236 | case '=': 1237 | if (i > 0 && (expr[i-1] == '>' || expr[i-1] == '<')) { 1238 | continue; 1239 | } 1240 | if (i == len-1 || expr[i+1] != '=') { 1241 | return err_syntax(); 1242 | } 1243 | opsz++; 1244 | break; 1245 | case '!': 1246 | if (i == len-1 || expr[i+1] != '=') { 1247 | continue; 1248 | } 1249 | opsz++; 1250 | break; 1251 | } 1252 | if (i+2 < len && expr[i+2] == '=') { 1253 | // strict 1254 | opch += 32; 1255 | opsz++; 1256 | } 1257 | left = equal(left, op, expr+s, i-s, ctx, depth); 1258 | if (is_err(left)) return left; 1259 | op = opch; 1260 | i = i + opsz - 1; 1261 | s = i + 1; 1262 | break; 1263 | case '(': case '[': case '{': case '"': case '\'': 1264 | g = read_group(expr+i, len-i, &glen); 1265 | if (!g) return err_syntax(); 1266 | i = i + glen - 1; 1267 | break; 1268 | } 1269 | } 1270 | return equal(left, op, expr+s, len-s, ctx, depth); 1271 | } 1272 | 1273 | static struct value comp(struct value left, uint8_t op, 1274 | const uint8_t *expr, size_t len, struct eval_context *ctx, int depth) 1275 | { 1276 | expr = trim(expr, len, &len); 1277 | if (len == 0) return err_syntax(); 1278 | // parse next expression 1279 | struct value right = eval_auto(STEP_COMPS<<1, expr, len, ctx, depth); 1280 | if (is_err(right)) return right; 1281 | switch (op) { 1282 | case '<': 1283 | return vlt(left, right, ctx); 1284 | case '<' + 32: 1285 | return vlte(left, right, ctx); 1286 | case '>': 1287 | return vgt(left, right, ctx); 1288 | case '>' + 32: 1289 | return vgte(left, right, ctx); 1290 | default: 1291 | return right; 1292 | } 1293 | } 1294 | 1295 | static struct value eval_comps(const uint8_t *expr, size_t len, 1296 | struct eval_context *ctx, int depth) 1297 | { 1298 | size_t s = 0; 1299 | struct value left = { 0 }; 1300 | uint8_t op = 0; 1301 | size_t glen; 1302 | const uint8_t *g; 1303 | uint8_t opch; 1304 | size_t opsz; 1305 | for (size_t i = 0; i < len; i++) { 1306 | switch (expr[i]) { 1307 | case '<': case '>': 1308 | opch = expr[i]; 1309 | opsz = 1; 1310 | if (i < len-1 && expr[i+1] == '=') { 1311 | opch += 32; 1312 | opsz++; 1313 | } 1314 | left = comp(left, op, expr+s, i-s, ctx, depth); 1315 | if (is_err(left)) return left; 1316 | op = opch; 1317 | i = i + opsz - 1; 1318 | s = i + 1; 1319 | break; 1320 | case '(': case '[': case '{': case '"': case '\'': 1321 | g = read_group(expr+i, len-i, &glen); 1322 | if (!g) return err_syntax(); 1323 | i = i + glen - 1; 1324 | break; 1325 | } 1326 | } 1327 | return comp(left, op, expr+s, len-s, ctx, depth); 1328 | } 1329 | 1330 | static struct value vmul(struct value a, struct value b) { 1331 | if (a.kind == b.kind) { 1332 | switch (a.kind) { 1333 | case FLOAT_KIND: 1334 | return make_float(a.f64 * b.f64); 1335 | case INT_KIND: 1336 | return make_int(a.i64 * b.i64); 1337 | case UINT_KIND: 1338 | return make_uint(a.u64 * b.u64); 1339 | default: 1340 | break; 1341 | } 1342 | } 1343 | return make_float(to_f64(a) * to_f64(b)); 1344 | } 1345 | 1346 | static struct value vdiv(struct value a, struct value b) { 1347 | if (a.kind == b.kind) { 1348 | switch (a.kind) { 1349 | case FLOAT_KIND: 1350 | return make_float(a.f64 / b.f64); 1351 | case INT_KIND: 1352 | if (b.i64 == 0) { 1353 | return make_float(NAN); 1354 | } 1355 | return make_int(a.i64 / b.i64); 1356 | case UINT_KIND: 1357 | if (b.u64 == 0) { 1358 | return make_float(NAN); 1359 | } 1360 | return make_uint(a.u64 / b.u64); 1361 | default: 1362 | break; 1363 | } 1364 | } 1365 | return make_float(to_f64(a) / to_f64(b)); 1366 | } 1367 | 1368 | static struct value vmod(struct value a, struct value b) { 1369 | if (a.kind == b.kind) { 1370 | switch (a.kind) { 1371 | case INT_KIND: 1372 | if (b.i64 == 0) { 1373 | return make_float(NAN); 1374 | } 1375 | return make_int(a.i64 % b.i64); 1376 | case UINT_KIND: 1377 | if (b.u64 == 0) { 1378 | return make_float(NAN); 1379 | } 1380 | return make_uint(a.u64 % b.u64); 1381 | default: 1382 | break; 1383 | } 1384 | } 1385 | return make_float(fmod(to_f64(a), to_f64(b))); 1386 | } 1387 | 1388 | static struct value string_concat(const uint8_t *astr, size_t alen, 1389 | const uint8_t *bstr, size_t blen) 1390 | { 1391 | uint8_t *str = emalloc(alen+blen+1); 1392 | if (!str) return err_oom(); 1393 | memcpy(str, astr, alen); 1394 | memcpy(str+alen, bstr, blen); 1395 | str[alen+blen] = '\0'; 1396 | return make_string(str, alen+blen); 1397 | } 1398 | 1399 | static struct value vadd(struct value a, struct value b) { 1400 | if (a.kind == b.kind) { 1401 | switch (a.kind) { 1402 | case FLOAT_KIND: 1403 | return make_float(a.f64 + b.f64); 1404 | case INT_KIND: 1405 | return make_int(a.i64 + b.i64); 1406 | case UINT_KIND: 1407 | return make_uint(a.u64 + b.u64); 1408 | case STR_KIND: 1409 | return string_concat(a.str, a.len, b.str, b.len); 1410 | case BOOL_KIND: case UNDEF_KIND: case NULL_KIND: 1411 | return make_float(to_f64(a) + to_f64(b)); 1412 | default: 1413 | break; 1414 | } 1415 | } else if (isnum(a) && isnum(b)) { 1416 | return make_float(to_f64(a) + to_f64(b)); 1417 | } 1418 | char abuf[32]; 1419 | size_t alen; 1420 | const uint8_t *astr = to_str(a, &alen, abuf, sizeof(abuf)); 1421 | if (!astr) return err_oom(); 1422 | char bbuf[32]; 1423 | size_t blen; 1424 | const uint8_t *bstr = to_str(b, &blen, bbuf, sizeof(bbuf)); 1425 | if (!bstr) return err_oom(); 1426 | return string_concat(astr, alen, bstr, blen); 1427 | } 1428 | 1429 | static struct value vsub(struct value a, struct value b) { 1430 | if (a.kind == b.kind) { 1431 | switch (a.kind) { 1432 | case FLOAT_KIND: 1433 | return make_float(a.f64 - b.f64); 1434 | case INT_KIND: 1435 | return make_int(a.i64 - b.i64); 1436 | case UINT_KIND: 1437 | return make_uint(a.u64 - b.u64); 1438 | default: 1439 | break; 1440 | } 1441 | } 1442 | return make_float(to_f64(a) - to_f64(b)); 1443 | } 1444 | 1445 | static struct value sum(struct value left, uint8_t op, const uint8_t *expr, 1446 | size_t len, bool neg, struct eval_context *ctx, int depth) 1447 | { 1448 | expr = trim(expr, len, &len); 1449 | if (len == 0) { 1450 | return err_syntax(); 1451 | } 1452 | // parse factors of expression 1453 | struct value right = eval_auto(STEP_SUMS<<1, expr, len, ctx, depth); 1454 | if (is_err(right)) return right; 1455 | if (neg) { 1456 | // make right negative 1457 | right = vmul(right, make_float(-1)); 1458 | if (is_err(right)) return right; 1459 | } 1460 | switch (op) { 1461 | case '+': 1462 | return vadd(left, right); 1463 | case '-': 1464 | return vsub(left, right); 1465 | default: 1466 | return right; 1467 | } 1468 | } 1469 | 1470 | 1471 | static uint8_t closech(uint8_t open) { 1472 | switch (open) { 1473 | case '(': 1474 | return ')'; 1475 | case '[': 1476 | return ']'; 1477 | case '{': 1478 | return '}'; 1479 | } 1480 | return open; 1481 | } 1482 | 1483 | static const uint8_t *squash(const uint8_t *data, size_t len, size_t *out_len) { 1484 | // expects that the lead character is 1485 | // '[' or '{' or '(' or '"' or '\'' 1486 | // squash the value, ignoring all nested arrays and objects. 1487 | size_t i = 0; 1488 | int depth = 0; 1489 | uint8_t qch; 1490 | size_t s2; 1491 | switch (data[0]) { 1492 | case '"': case '\'': 1493 | break; 1494 | default: 1495 | i = 1; 1496 | depth = 1; 1497 | } 1498 | for (; i < len; i++) { 1499 | if (data[i] < '"' || data[i] > '}') { 1500 | continue; 1501 | } 1502 | switch (data[i]) { 1503 | case '"': case '\'': 1504 | qch = data[i]; 1505 | i++; 1506 | s2 = i; 1507 | for (; i < len; i++) { 1508 | if (data[i] > '\\') { 1509 | continue; 1510 | } 1511 | if (data[i] == qch) { 1512 | // look for an escaped slash 1513 | if (data[i-1] == '\\') { 1514 | int n = 0; 1515 | if (i >= 2) { 1516 | for (size_t j = i - 2; j > s2-1; j--) { 1517 | if (data[j] != '\\') { 1518 | break; 1519 | } 1520 | n++; 1521 | } 1522 | } 1523 | if (n%2 == 0) { 1524 | continue; 1525 | } 1526 | } 1527 | break; 1528 | } 1529 | } 1530 | if (depth == 0) { 1531 | if (i >= len) { 1532 | return NULL; 1533 | } 1534 | *out_len = i+1; 1535 | return data; 1536 | } 1537 | break; 1538 | case '{': case '[': case '(': 1539 | depth++; 1540 | break; 1541 | case '}': case ']': case ')': 1542 | depth--; 1543 | if (depth == 0) { 1544 | *out_len = i+1; 1545 | return data; 1546 | } 1547 | break; 1548 | } 1549 | } 1550 | return NULL; 1551 | } 1552 | 1553 | static const uint8_t *read_group(const uint8_t *data, size_t len, 1554 | size_t *len_out) 1555 | { 1556 | const uint8_t *g = squash(data, len, len_out); 1557 | if (!g) return NULL; 1558 | if (*len_out < 2 || g[*len_out-1] != closech(data[0])) return NULL; 1559 | return g; 1560 | } 1561 | 1562 | 1563 | static size_t read_id_start(const uint8_t *expr, size_t len, bool *ok) { 1564 | if (len == 0) { 1565 | *ok = true; 1566 | return 0; 1567 | } 1568 | if (expr[0] == '$' || expr[0] == '_' || 1569 | (expr[0] >= 'A' && expr[0] <= 'Z') || 1570 | (expr[0] >= 'a' && expr[0] <= 'z')) 1571 | { 1572 | *ok = true; 1573 | return 1; 1574 | } 1575 | *ok = false; 1576 | return 0; 1577 | } 1578 | 1579 | static size_t read_id_continue(const uint8_t *expr, size_t len, bool *ok) { 1580 | if (len == 0) { 1581 | *ok = true; 1582 | return 0; 1583 | } 1584 | if (expr[0] == '$' || expr[0] == '_' || 1585 | (expr[0] >= 'A' && expr[0] <= 'Z') || 1586 | (expr[0] >= 'a' && expr[0] <= 'z') || 1587 | (expr[0] >= '0' && expr[0] <= '9')) 1588 | { 1589 | *ok = true; 1590 | return 1; 1591 | } 1592 | *ok = false; 1593 | return 0; 1594 | } 1595 | 1596 | static const uint8_t *read_ident(const uint8_t *expr, size_t len, 1597 | size_t *len_out) 1598 | { 1599 | // Only ascii identifiers for now 1600 | size_t i = 0; 1601 | size_t z = 0; 1602 | bool ok = false; 1603 | z = read_id_start(expr, len, &ok); 1604 | if (!ok || z == 0) { 1605 | *len_out = 0; 1606 | return NULL; 1607 | } 1608 | i += z; 1609 | while (1) { 1610 | z = read_id_continue(expr+i, len-i, &ok); 1611 | if (!ok || z == 0) { 1612 | *len_out = i; 1613 | return expr; 1614 | } 1615 | i += z; 1616 | } 1617 | } 1618 | 1619 | // read_codepoint returns the codepoint from the the \uXXXX 1620 | static size_t read_codepoint(const uint8_t *expr, size_t len, uint8_t which, 1621 | uint32_t *cp) 1622 | { 1623 | int x = 0; 1624 | size_t n = 0; 1625 | if (which == 'x') { 1626 | x = strtol((char*)expr, NULL, 16); 1627 | n = 2; 1628 | } else { 1629 | size_t s = 0; 1630 | if (expr[0] == '{') { 1631 | s = 1; 1632 | n = len; 1633 | for (size_t i = 0; i < len; i++) { 1634 | if (expr[i] == '}') { 1635 | n = i + 1; 1636 | break; 1637 | } 1638 | } 1639 | } else { 1640 | n = 4; 1641 | } 1642 | x = strtol((char*)expr+s, NULL, 16); 1643 | } 1644 | *cp = x; 1645 | return n; 1646 | } 1647 | 1648 | static bool is_surrogate(uint32_t cp) { 1649 | return cp > 55296 && cp < 57344; 1650 | } 1651 | 1652 | static int decode_codepoint(uint32_t cp1, uint32_t cp2) { 1653 | if (55296 <= cp1 && cp1 < 56320 && 56320 <= cp2 && cp2 < 57344) { 1654 | return (((cp1-55296)<<10) | (cp2 - 56320)) + 65536; 1655 | } 1656 | return 65533; 1657 | } 1658 | 1659 | static void write_codepoint(struct writer *wr, uint32_t r) { 1660 | uint8_t p[4]; 1661 | size_t n; 1662 | if (r <= 127) { 1663 | p[0] = (uint8_t)r; 1664 | n = 1; 1665 | } else if (r <= 2047) { 1666 | p[0] = 192 | ((uint8_t)(r>>6)); 1667 | p[1] = 128 | ((uint8_t)(r)&63); 1668 | n = 2; 1669 | } else if (r > 1114111 || (55296 <= r && r <= 57343)) { 1670 | r = 65533; 1671 | goto next; // fall through 1672 | } else if (r <= 65535) { 1673 | next: 1674 | p[0] = 224 | ((uint8_t)(r>>12)); 1675 | p[1] = 128 | ((uint8_t)(r>>6)&63); 1676 | p[2] = 128 | ((uint8_t)(r)&63); 1677 | n = 3; 1678 | } else { 1679 | p[0] = 240 | ((uint8_t)(r>>18)); 1680 | p[1] = 128 | ((uint8_t)(r>>12)&63); 1681 | p[2] = 128 | ((uint8_t)(r>>6)&63); 1682 | p[3] = 128 | ((uint8_t)(r)&63); 1683 | n = 4; 1684 | } 1685 | write_bytes(wr, p, n); 1686 | } 1687 | 1688 | static const uint8_t *unescape_string(const uint8_t *expr, size_t len, 1689 | size_t *slen, bool *oom) 1690 | { 1691 | void *mem = emalloc(len+1); 1692 | if (!mem) { 1693 | *oom = true; 1694 | *slen = 0; 1695 | return NULL; 1696 | } 1697 | struct writer wr = { .dst = mem, .n = len+1 }; 1698 | uint32_t cp; 1699 | size_t n; 1700 | for (size_t i = 0; i < len; i++) { 1701 | switch (expr[i]) { 1702 | case '\\': 1703 | i++; 1704 | switch (expr[i]) { 1705 | case '0': write_char(&wr, '\0'); break; 1706 | case 'b': write_char(&wr, '\b'); break; 1707 | case 'f': write_char(&wr, '\f'); break; 1708 | case 'n': write_char(&wr, '\n'); break; 1709 | case 'r': write_char(&wr, '\r'); break; 1710 | case 't': write_char(&wr, '\t'); break; 1711 | case 'v': write_char(&wr, '\v'); break; 1712 | case 'u': 1713 | i++; 1714 | n = read_codepoint(expr+i, len-i, 'u', &cp); 1715 | i += n; 1716 | if (is_surrogate(cp)) { 1717 | // need another code 1718 | if (len-i >= 6 && expr[i] == '\\' && expr[i+1] == 'u') { 1719 | // we expect it to be correct so just consume it 1720 | i += 2; 1721 | uint32_t cp2; 1722 | n = read_codepoint(expr+i, len-i, 'u', &cp2); 1723 | i += n; 1724 | cp = decode_codepoint(cp, cp2); 1725 | } 1726 | } 1727 | // provide enough space to encode the largest utf8 possible 1728 | write_codepoint(&wr, cp); 1729 | i--; // backtrack index by one 1730 | break; 1731 | case 'x': 1732 | i++; 1733 | n = read_codepoint(expr+i, len-i, 'x', &cp); 1734 | i += n; 1735 | write_codepoint(&wr, cp); 1736 | i--; // backtrack index by one 1737 | break; 1738 | default: 1739 | write_char(&wr, (char)expr[i]); 1740 | } 1741 | break; 1742 | default: 1743 | write_char(&wr, (char)expr[i]); 1744 | } 1745 | } 1746 | write_nullterm(&wr); 1747 | *slen = wr.count; 1748 | *oom = false; 1749 | return mem; 1750 | } 1751 | 1752 | // parse_string parses a Javascript encoded string. 1753 | static const uint8_t *parse_string(const uint8_t *expr, size_t len, 1754 | size_t *slen, size_t *rlen, bool *oom) 1755 | { 1756 | bool esc = false; 1757 | if (len < 2) goto fail; 1758 | uint8_t qch = expr[0]; 1759 | for (size_t i = 1; i < len; i++) { 1760 | if (expr[i] < ' ') goto fail; 1761 | if (expr[i] == '\\') { 1762 | esc = true; 1763 | i++; 1764 | if (i == len) goto fail; 1765 | switch (expr[i]) { 1766 | case 'u': 1767 | if (i+1 < len && expr[i+1] == '{') { 1768 | i += 2; 1769 | bool end = false; 1770 | int cn = 0; 1771 | for (; i < len; i++) { 1772 | if (expr[i] == '}') { 1773 | end = true; 1774 | break; 1775 | } 1776 | if (!isxdigit(expr[i])) goto fail; 1777 | cn++; 1778 | } 1779 | if (cn == 0) goto fail; 1780 | if (!end) goto fail; 1781 | } else { 1782 | for (size_t j = 0; j < 4; j++) { 1783 | i++; 1784 | if (i >= len || !isxdigit(expr[i])) goto fail; 1785 | } 1786 | } 1787 | break; 1788 | case 'x': 1789 | for (size_t j = 0; j < 2; j++) { 1790 | i++; 1791 | if (i >= len || !isxdigit(expr[i])) goto fail; 1792 | } 1793 | break; 1794 | default: 1795 | // LegacyOctalEscapeSequence and NonOctalDecimalEscapeSequence 1796 | // are not allowed. Strict Mode Only. See 1797 | // https://262.ecma-international.org/12.0/#sec-additional-syntax-string-literals 1798 | if (expr[i] >= '1' && expr[i] <= '9') { 1799 | goto fail; 1800 | } 1801 | } 1802 | } else if (expr[i] == qch) { 1803 | const uint8_t *s = expr+1; 1804 | *slen = i-1; 1805 | if (esc) { 1806 | s = unescape_string(s, *slen, slen, oom); 1807 | if (!s) { 1808 | *slen = 0; 1809 | *rlen = 0; 1810 | return NULL; 1811 | } 1812 | } 1813 | *oom = false; 1814 | *rlen = i+1; 1815 | return s; 1816 | } 1817 | } 1818 | fail: 1819 | *oom = false; 1820 | *slen = 0; 1821 | *rlen = 0; 1822 | return NULL; 1823 | } 1824 | 1825 | // get_ref_value takes the value from an external reference. 1826 | // It's possible that the ref value is on the heap, and if so we need to 1827 | // steal it and place it in the allocs list. 1828 | static struct value get_ref_value(bool chain, struct value left, 1829 | const uint8_t *ident, size_t ilen, bool opt_chain, 1830 | struct eval_context *ctx) 1831 | { 1832 | if (left.kind == JSON_KIND) { 1833 | struct json json = json_parsen((char*)left.str, left.len); 1834 | struct json key; 1835 | struct json val; 1836 | int64_t index; 1837 | enum json_type type = json_type(json); 1838 | if (type == JSON_OBJECT) { 1839 | key = json_first(json); 1840 | while (json_exists(key)) { 1841 | val = json_next(key); 1842 | if (json_string_comparen(key, (char*)ident, ilen) == 0) { 1843 | return make_json((uint8_t*)json_raw(val), 1844 | json_raw_length(val)); 1845 | } 1846 | key = json_next(val); 1847 | } 1848 | } else { // JSON_ARRAY 1849 | index = conv_atoi((char*)ident, ilen); 1850 | if (index >= 0) { 1851 | val = json_first(json); 1852 | while (json_exists(val)) { 1853 | if (index == 0) { 1854 | return make_json((uint8_t*)json_raw(val), 1855 | json_raw_length(val)); 1856 | } 1857 | index--; 1858 | val = json_next(val); 1859 | } 1860 | } 1861 | } 1862 | return make_undefined(); 1863 | } 1864 | if (!ctx->env || !ctx->env->ref) { 1865 | return err_undefined(ident, ilen, chain); 1866 | } 1867 | struct value val = to_value(ctx->env->ref( 1868 | from_value(chain?left:make_global()), 1869 | xv_new_stringn((char*)ident, ilen), ctx->env->udata)); 1870 | if (is_err(val)) return val; 1871 | if (val.kind == UNDEF_KIND && left.kind == UNDEF_KIND) { 1872 | val = err_undefined(ident, ilen, chain); 1873 | } 1874 | if (is_err(val)) { 1875 | bool skip_err = false; 1876 | if (opt_chain) { 1877 | skip_err = true; 1878 | } 1879 | if (skip_err) { 1880 | val = make_undefined(); 1881 | } 1882 | } 1883 | return val; 1884 | } 1885 | 1886 | static struct value eval_foreach(const uint8_t *expr, size_t len, 1887 | struct xv_env *env, void (*iter)(struct value, void *udata), void *udata, 1888 | int depth); 1889 | 1890 | struct multi_iter_context { 1891 | struct array *arr; 1892 | bool oom; 1893 | }; 1894 | 1895 | static void multi_iter(struct value value, void *udata) { 1896 | struct multi_iter_context *ictx = udata; 1897 | if (!ictx->oom) { 1898 | ictx->oom = !array_push_back(ictx->arr, value); 1899 | } 1900 | } 1901 | 1902 | static struct value multi_exprs_to_array(const uint8_t *expr, size_t len, 1903 | struct eval_context *ctx, int depth) 1904 | { 1905 | struct array *arr = emalloc(sizeof(struct array)); 1906 | if (!arr) return err_oom(); 1907 | memset(arr, 0, sizeof(struct array)); 1908 | struct multi_iter_context ictx = { .arr = arr }; 1909 | struct value last = eval_foreach(expr, len, ctx->env, multi_iter, &ictx, 1910 | depth); 1911 | if (is_err(last)) return last; 1912 | if (ictx.oom) return err_oom(); 1913 | struct value v = make_array(arr->items, arr->len); 1914 | return v; 1915 | } 1916 | 1917 | static struct value eval_atom(const uint8_t *expr, size_t len, 1918 | struct eval_context *ctx, int depth) 1919 | { 1920 | expr = trim(expr, len, &len); 1921 | if (len == 0) { 1922 | return err_syntax(); 1923 | } 1924 | struct value left = { 0 }; 1925 | bool left_ready = false; 1926 | size_t glen; 1927 | const uint8_t *g; 1928 | size_t slen; 1929 | size_t rlen; 1930 | bool oom; 1931 | const uint8_t *str; 1932 | 1933 | // first look for non-chainable atoms 1934 | switch (expr[0]) { 1935 | case '0': 1936 | if (len > 1 && (expr[1] == 'x' || expr[1] == 'X')) { 1937 | // hexadecimal 1938 | bool ok = false; 1939 | uint64_t x = parse_uint(expr+2, len-2, 16, &ok); 1940 | if (!ok) { 1941 | return err_syntax(); 1942 | } 1943 | return make_float((double)x); 1944 | } 1945 | // fall through 1946 | case '-': case'.': case '1': case '2': case '3': case '4': case '5': 1947 | case '6': case '7': case '8': case '9': 1948 | if (len > 3 && has_suffix(expr, len, "64")) { 1949 | if (expr[len-3] == 'u') { 1950 | bool ok = false; 1951 | uint64_t x = parse_uint(expr, len-3, 10, &ok); 1952 | if (!ok) { 1953 | return err_syntax(); 1954 | } 1955 | return make_uint(x); 1956 | } 1957 | if (expr[len-3] == 'i') { 1958 | bool ok = false; 1959 | int64_t x = parse_int(expr, len-3, 10, &ok); 1960 | if (!ok) { 1961 | return err_syntax(); 1962 | } 1963 | return make_int(x); 1964 | } 1965 | } 1966 | bool ok = false; 1967 | double x = parse_float(expr, len, &ok); 1968 | if (!ok) { 1969 | return err_syntax(); 1970 | } 1971 | return make_float(x); 1972 | case '"': case '\'': 1973 | str = parse_string(expr, len, &slen, &rlen, &oom); 1974 | if (!str) { 1975 | return oom ? err_oom() : err_syntax(); 1976 | } 1977 | left = make_string(str, slen); 1978 | left_ready = true; 1979 | expr = expr+rlen; 1980 | len -= rlen; 1981 | break; 1982 | case '(': case '{': case '[': 1983 | g = read_group(expr, len, &glen); 1984 | if (!g) return err_syntax(); 1985 | if (g[0] == '(') { 1986 | // paren groups can be evaluated and used as the leading value. 1987 | left = eval_expr(g+1, len-2, ctx, depth); 1988 | if (is_err(left)) return left; 1989 | left_ready = true; 1990 | expr += glen; 1991 | len -= glen; 1992 | } else if (g[0] == '[') { 1993 | left = multi_exprs_to_array(g+1, glen-2, ctx, depth); 1994 | if (is_err(left)) return left; 1995 | left_ready = true; 1996 | expr += glen; 1997 | len -= glen; 1998 | } else { 1999 | // '{' is not currently allowed as a leading value 2000 | // Perhaps in the future. 2001 | return err_syntax(); 2002 | } 2003 | break; 2004 | } 2005 | const uint8_t *left_ident = NULL; 2006 | size_t left_ident_len = 0; 2007 | if (!left_ready) { 2008 | // probably a chainable identifier 2009 | size_t ilen; 2010 | const uint8_t *ident = read_ident(expr, len, &ilen); 2011 | if (!ident) return err_syntax(); 2012 | 2013 | // TODO: maybe use a tiny hashtable 2014 | if (ilen == 4 && memcmp(ident, "true", 4) == 0) { 2015 | left = make_bool(true); 2016 | } else if (ilen == 5 && memcmp(ident, "false", 5) == 0) { 2017 | left = make_bool(false); 2018 | } else if (ilen == 4 && memcmp(ident, "null", 4) == 0) { 2019 | left = make_null(); 2020 | } else if (ilen == 9 && memcmp(ident, "undefined", 9) == 0) { 2021 | left = make_undefined(); 2022 | } else if (ilen == 3 && memcmp(ident, "NaN", 3) == 0) { 2023 | left = make_float(NAN); 2024 | } else if (ilen == 8 && memcmp(ident, "Infinity", 8) == 0) { 2025 | left = make_float(INFINITY); 2026 | } else if ((ilen == 2 && memcmp(ident, "in", 2) == 0) || 2027 | (ilen == 3 && memcmp(ident, "new", 3) == 0) || 2028 | (ilen == 4 && memcmp(ident, "void", 4) == 0) || 2029 | (ilen == 5 && memcmp(ident, "await", 5) == 0) || 2030 | (ilen == 5 && memcmp(ident, "yield", 5) == 0) || 2031 | (ilen == 6 && memcmp(ident, "typeof", 6) == 0) || 2032 | (ilen == 8 && memcmp(ident, "function", 8) == 0) || 2033 | (ilen == 10 && memcmp(ident, "instanceof", 10) == 0)) 2034 | { 2035 | // unsupported keyword 2036 | return err_unsupported_keyword(ident, ilen); 2037 | } else { 2038 | left = get_ref_value(false, make_undefined(), ident, ilen, false, 2039 | ctx); 2040 | if (is_err(left)) return left; 2041 | } 2042 | left_ready = true; 2043 | expr = expr+ilen; 2044 | len -= ilen; 2045 | left_ident = ident; 2046 | left_ident_len = ilen; 2047 | } 2048 | 2049 | struct value left_left = { 0 }; 2050 | bool has_left_left = false; 2051 | 2052 | // read each chained component 2053 | bool opt_chain = false; 2054 | 2055 | const uint8_t *ident; 2056 | size_t ilen; 2057 | struct value val; 2058 | struct value last; 2059 | char nbuf[32]; 2060 | while (1) { 2061 | // There are more components to read 2062 | expr = trim(expr, len, &len); 2063 | if (len == 0) break; 2064 | switch (expr[0]) { 2065 | case '?': 2066 | // Optional chaining 2067 | unreachable( 2068 | if (len == 1 || expr[1] != '.') { 2069 | // Unreachable due to the condition being checked in a 2070 | // previous eval_logical_or step 2071 | return err_syntax(); 2072 | } 2073 | ) 2074 | expr++; 2075 | len--; 2076 | opt_chain = true; 2077 | // fall through 2078 | case '.': 2079 | // Member Access 2080 | expr++; 2081 | len--; 2082 | expr = trim(expr, len, &len); 2083 | ident = read_ident(expr, len, &ilen); 2084 | if (!ident) return err_syntax(); 2085 | val = get_ref_value(true, left, ident, ilen, opt_chain, ctx); 2086 | if (is_err(val)) return val; 2087 | left_left = left; 2088 | has_left_left = true; 2089 | left = val; 2090 | expr = expr+ilen; 2091 | len -= ilen; 2092 | left_ident = ident; 2093 | left_ident_len = ilen; 2094 | break; 2095 | case '(': 2096 | // Function call 2097 | g = read_group(expr, len, &glen); 2098 | if (!g) return err_syntax(); 2099 | if (left.kind != FUNC_KIND) { 2100 | return err_notfunc(left_ident, left_ident_len); 2101 | } 2102 | (void)has_left_left; 2103 | struct value args = multi_exprs_to_array(g+1, glen-2, ctx, depth); 2104 | if (is_err(args)) return args; 2105 | 2106 | val = to_value(left.func(from_value(left_left), 2107 | from_value(args), 2108 | ctx->env?ctx->env->udata:NULL)); 2109 | if (is_err(val)) return val; 2110 | left_left = left; 2111 | has_left_left = true; 2112 | left = val; 2113 | expr += glen; 2114 | len -= glen; 2115 | break; 2116 | case '[': 2117 | // Computed Member Access 2118 | g = read_group(expr, len, &glen); 2119 | if (!g) return err_syntax(); 2120 | last = eval_expr(g+1, glen-2, ctx, depth); 2121 | if (is_err(last)) return last; 2122 | ident = to_str(last, &ilen, nbuf, sizeof(nbuf)); 2123 | val = get_ref_value(true, left, ident, ilen, opt_chain, ctx); 2124 | if (is_err(val)) return val; 2125 | left_left = left; 2126 | has_left_left = true; 2127 | left = val; 2128 | expr += glen; 2129 | len -= glen; 2130 | break; 2131 | default: 2132 | return err_syntax(); 2133 | } 2134 | } 2135 | return left; 2136 | } 2137 | 2138 | static struct value eval_sums(const uint8_t *expr, size_t len, 2139 | struct eval_context *ctx, int depth) 2140 | { 2141 | size_t s = 0; 2142 | struct value left = { 0 }; 2143 | uint8_t op = 0; 2144 | bool fill = false; 2145 | bool neg = false; 2146 | size_t glen; 2147 | const uint8_t *g; 2148 | for (size_t i = 0; i < len; i++) { 2149 | switch (expr[i]) { 2150 | case '-': case '+': 2151 | if (!fill) { 2152 | if (i > 0 && expr[i-1] == expr[i]) { 2153 | // -- not allowed 2154 | return err_syntax(); 2155 | } 2156 | if (expr[i] == '-') { 2157 | neg = !neg; 2158 | } 2159 | s = i + 1; 2160 | continue; 2161 | } 2162 | if (i > 0 && (expr[i-1] == 'e' || expr[i-1] == 'E')) { 2163 | // scientific notation 2164 | continue; 2165 | } 2166 | if (neg) { 2167 | if (s > 0 && s < len && expr[s-1] == '-' && 2168 | expr[s] >= '0' && expr[s] <= '9') 2169 | { 2170 | s--; 2171 | neg = false; 2172 | } 2173 | } 2174 | left = sum(left, op, expr+s, i-s, neg, ctx, depth); 2175 | if (is_err(left)) { 2176 | return left; 2177 | } 2178 | op = expr[i]; 2179 | s = i + 1; 2180 | fill = false; 2181 | neg = false; 2182 | break; 2183 | case '(': case '[': case '{': case '"': case '\'': 2184 | g = read_group(expr+i, len-i, &glen); 2185 | if (!g) return err_syntax(); 2186 | i = i + glen - 1; 2187 | fill = true; 2188 | break; 2189 | default: 2190 | if (!fill && !isws(expr[i])) { 2191 | fill = true; 2192 | } 2193 | } 2194 | } 2195 | if (neg) { 2196 | if (s > 0 && s < len && expr[s-1] == '-' && 2197 | expr[s] >= '0' && expr[s] <= '9') 2198 | { 2199 | s--; 2200 | neg = false; 2201 | } 2202 | } 2203 | return sum(left, op, expr+s, len-s, neg, ctx, depth); 2204 | } 2205 | 2206 | static struct value fact(struct value left, uint8_t op, const uint8_t *expr, 2207 | size_t len, struct eval_context *ctx, int depth) 2208 | { 2209 | expr = trim(expr, len, &len); 2210 | if (len == 0) return err_syntax(); 2211 | struct value right = eval_atom(expr, len, ctx, depth); 2212 | if (is_err(right)) return right; 2213 | switch (op) { 2214 | case '*': 2215 | return vmul(left, right); 2216 | case '/': 2217 | return vdiv(left, right); 2218 | case '%': 2219 | return vmod(left, right); 2220 | default: 2221 | return right; 2222 | } 2223 | } 2224 | 2225 | static struct value eval_facts(const uint8_t *expr, size_t len, 2226 | struct eval_context *ctx, int depth) 2227 | { 2228 | size_t s = 0; 2229 | struct value left = { 0 }; 2230 | uint8_t op = 0; 2231 | size_t glen; 2232 | const uint8_t *g; 2233 | for (size_t i = 0; i < len; i++) { 2234 | switch (expr[i]) { 2235 | case '*': case '/': case '%': 2236 | left = fact(left, op, expr+s, i-s, ctx, depth); 2237 | if (is_err(left)) return left; 2238 | op = expr[i]; 2239 | s = i + 1; 2240 | break; 2241 | case '(': case '[': case '{': case '"': case '\'': 2242 | g = read_group(expr+i, len-i, &glen); 2243 | if (!g) return err_syntax(); 2244 | i = i + glen - 1; 2245 | break; 2246 | } 2247 | } 2248 | return fact(left, op, expr+s, len-s, ctx, depth); 2249 | } 2250 | 2251 | static struct value eval_auto(int step, const uint8_t *expr, size_t len, 2252 | struct eval_context *ctx, int depth) 2253 | { 2254 | if (depth-1 > XV_MAXDEPTH) { 2255 | return err_msg("MaxDepthError"); 2256 | } 2257 | switch (step) { 2258 | case STEP_COMMA: 2259 | if ((ctx->steps & STEP_COMMA) == STEP_COMMA) { 2260 | return eval_comma(expr, len, ctx, depth); 2261 | } 2262 | // fall through 2263 | case STEP_TERNS: 2264 | if ((ctx->steps & STEP_TERNS) == STEP_TERNS) { 2265 | return eval_terns(expr, len, ctx, depth); 2266 | } 2267 | // fall through 2268 | case STEP_LOGICAL_OR: 2269 | if ((ctx->steps & STEP_LOGICAL_OR) == STEP_LOGICAL_OR) { 2270 | return eval_logical_or(expr, len, ctx, depth); 2271 | } 2272 | // fall through 2273 | case STEP_LOGICAL_AND: 2274 | if ((ctx->steps & STEP_LOGICAL_AND) == STEP_LOGICAL_AND) { 2275 | return eval_logical_and(expr, len, ctx, depth); 2276 | } 2277 | // fall through 2278 | case STEP_BITWISE_OR: 2279 | if ((ctx->steps & STEP_BITWISE_OR) == STEP_BITWISE_OR) { 2280 | return eval_bitwise_or(expr, len, ctx, depth); 2281 | } 2282 | // fall through 2283 | case STEP_BITWISE_XOR: 2284 | if ((ctx->steps & STEP_BITWISE_XOR) == STEP_BITWISE_XOR) { 2285 | return eval_bitwise_xor(expr, len, ctx, depth); 2286 | } 2287 | // fall through 2288 | case STEP_BITWISE_AND: 2289 | if ((ctx->steps & STEP_BITWISE_AND) == STEP_BITWISE_AND) { 2290 | return eval_bitwise_and(expr, len, ctx, depth); 2291 | } 2292 | // fall through 2293 | case STEP_EQUALITY: 2294 | if ((ctx->steps & STEP_EQUALITY) == STEP_EQUALITY) { 2295 | return eval_equality(expr, len, ctx, depth); 2296 | } 2297 | // fall through 2298 | case STEP_COMPS: 2299 | if ((ctx->steps & STEP_COMPS) == STEP_COMPS) { 2300 | return eval_comps(expr, len, ctx, depth); 2301 | } 2302 | // fall through 2303 | case STEP_SUMS: 2304 | if ((ctx->steps & STEP_SUMS) == STEP_SUMS) { 2305 | return eval_sums(expr, len, ctx, depth); 2306 | } 2307 | // fall through 2308 | case STEP_FACTS: 2309 | if ((ctx->steps & STEP_FACTS) == STEP_FACTS) { 2310 | return eval_facts(expr, len, ctx, depth); 2311 | } 2312 | // fall through 2313 | default: 2314 | return eval_atom(expr, len, ctx, depth); 2315 | } 2316 | } 2317 | 2318 | static struct value eval_expr(const uint8_t *expr, size_t len, 2319 | struct eval_context *ctx, int depth) 2320 | { 2321 | // the only place where the depth is increased 2322 | return eval_auto(STEP_COMMA, expr, len, ctx, depth+1); 2323 | } 2324 | 2325 | static struct value eval_foreach(const uint8_t *expr, size_t len, 2326 | struct xv_env *env, void (*iter)(struct value, void *udata), void *udata, 2327 | int depth) 2328 | { 2329 | expr = trim(expr, len, &len); 2330 | if (len == 0) return undefined(); 2331 | 2332 | // Determine which steps are (possibly) needed by scanning every byte in 2333 | // the input expression and looking for potential candidate characters. 2334 | int steps = 0; 2335 | for (size_t i = 0; i < len; i++) { 2336 | steps |= (int)op_steps[expr[i]]; 2337 | } 2338 | 2339 | if (iter) { 2340 | // require the comma step when using an iterator. 2341 | steps |= STEP_COMMA; 2342 | } 2343 | 2344 | struct eval_context ctx = { 2345 | .expr = (uint8_t*)expr, 2346 | .len = len, 2347 | .steps = steps, 2348 | .iter = iter, 2349 | .iter_udata = udata, 2350 | .env = env, 2351 | }; 2352 | return eval_expr(expr, len, &ctx, depth); 2353 | } 2354 | 2355 | static struct value eval(const uint8_t *expr, size_t len, 2356 | struct xv_env *env, int depth) 2357 | { 2358 | return eval_foreach(expr, len, env, NULL, NULL, depth); 2359 | } 2360 | 2361 | struct xv xv_eval(const char *expr, struct xv_env *env) { 2362 | return xv_evaln(expr, strlen(expr), env); 2363 | } 2364 | 2365 | struct xv xv_evaln(const char *expr, size_t len, 2366 | struct xv_env *env) 2367 | { 2368 | assert(sizeof(struct value) == sizeof(struct xv)); // static_assert? 2369 | struct value value = eval((uint8_t*)expr, len, env, 0); 2370 | struct xv fvalue; 2371 | memcpy(&fvalue, &value, sizeof(struct xv)); 2372 | return fvalue; 2373 | } 2374 | 2375 | static void write_error(struct writer *wr, struct value value) { 2376 | if ((value.flag&FLAG_ENOTFUNC) == FLAG_ENOTFUNC) { 2377 | write_cstr(wr, "TypeError: "); 2378 | write_bytes(wr, value.str, value.len); 2379 | write_cstr(wr, " is not a function"); 2380 | } else if ((value.flag&FLAG_ESYNTAX) == FLAG_ESYNTAX) { 2381 | write_cstr(wr, "SyntaxError"); 2382 | if ((value.flag&FLAG_EUNSUPKEYWORD) == FLAG_EUNSUPKEYWORD) { 2383 | write_cstr(wr, ": Unsupported keyword '"); 2384 | write_bytes(wr, value.str, value.len); 2385 | write_cstr(wr, "'"); 2386 | } 2387 | } else if ((value.flag&FLAG_EUNDEFINED) == FLAG_EUNDEFINED) { 2388 | if ((value.flag&FLAG_CHAIN) == FLAG_CHAIN) { 2389 | write_cstr(wr, "TypeError: Cannot read properties of undefined " 2390 | "(reading '"); 2391 | write_bytes(wr, value.str, value.len); 2392 | write_cstr(wr, "')"); 2393 | } else { 2394 | write_cstr(wr, "ReferenceError: Can't find variable: '"); 2395 | write_bytes(wr, value.str, value.len); 2396 | write_cstr(wr, "'"); 2397 | } 2398 | } else if ((value.flag&FLAG_EOOM) == FLAG_EOOM) { 2399 | write_cstr(wr, "MemoryError: Out of memory"); 2400 | } else { // if ((value.flag&FLAG_EMSG) == FLAG_EMSG) { 2401 | if (value.len == 0) { 2402 | write_cstr(wr, ""); 2403 | } else { 2404 | write_bytes(wr, value.str, value.len); 2405 | } 2406 | } 2407 | } 2408 | 2409 | size_t xv_string_copy(struct xv value, char *dst, size_t n) { 2410 | struct value fvalue = to_value(value); 2411 | struct writer wr = { .dst = dst, .n = n }; 2412 | write_value(&wr, fvalue); 2413 | write_nullterm(&wr); 2414 | return wr.count; 2415 | } 2416 | 2417 | double xv_double(struct xv value) { 2418 | return to_f64(to_value(value)); 2419 | } 2420 | 2421 | int64_t xv_int64(struct xv value) { 2422 | return to_i64(to_value(value)); 2423 | } 2424 | 2425 | uint64_t xv_uint64(struct xv value) { 2426 | return to_u64(to_value(value)); 2427 | } 2428 | 2429 | bool xv_bool(struct xv value) { 2430 | return to_bool(to_value(value)); 2431 | } 2432 | 2433 | enum xv_type xv_type(struct xv value) { 2434 | switch (to_value(value).kind) { 2435 | case UNDEF_KIND: 2436 | return XV_UNDEFINED; 2437 | case BOOL_KIND: 2438 | return XV_BOOLEAN; 2439 | case FLOAT_KIND: case INT_KIND: case UINT_KIND: 2440 | return XV_NUMBER; 2441 | case FUNC_KIND: 2442 | return XV_FUNCTION; 2443 | case STR_KIND: 2444 | return XV_STRING; 2445 | default: 2446 | return XV_OBJECT; 2447 | } 2448 | } 2449 | 2450 | /////////////////////////////////////////// 2451 | // Bool 2452 | /////////////////////////////////////////// 2453 | 2454 | // conv_ttoi converts bool to int64 2455 | static int64_t conv_ttoi(bool t) { 2456 | return t ? 1 : 0; 2457 | } 2458 | 2459 | // conv_ttou converts bool to uint64 2460 | static uint64_t conv_ttou(bool t) { 2461 | return t ? 1 : 0; 2462 | } 2463 | 2464 | // conv_ttof converts bool to double 2465 | static double conv_ttof(bool t) { 2466 | return t ? 1 : 0; 2467 | } 2468 | 2469 | /////////////////////////////////////////// 2470 | // Float64 2471 | /////////////////////////////////////////// 2472 | 2473 | static const double UINT64_MAX_FLOAT = 18446744073709549568.0; 2474 | static const double INT64_MAX_FLOAT = 9223372036854774784.0; 2475 | static const double INT64_MIN_FLOAT = -9223372036854774784.0; 2476 | 2477 | // Ftot converts float64 to bool 2478 | static bool conv_ftot(double f) { 2479 | return f < 0 || f > 0; 2480 | } 2481 | 2482 | // conv_ftoi converts float64 to int64 2483 | static int64_t conv_ftoi(double f) { 2484 | if (isnan(f)) return 0; 2485 | if (f < -9007199254740991.0 || f > 9007199254740991.0) { 2486 | // The number is outside of the range for correct binary 2487 | // representation of floating point as an integer value. 2488 | // https://tc39.es/ecma262/#sec-number.min_safe_integer 2489 | // https://tc39.es/ecma262/#sec-number.max_safe_integer 2490 | if (f < 0) { 2491 | f = ceil(f); 2492 | if (f < INT64_MIN_FLOAT) return INT64_MIN; 2493 | } else { 2494 | f = floor(f); 2495 | if (f > INT64_MAX_FLOAT) return INT64_MAX; 2496 | } 2497 | } 2498 | return (int64_t)f; 2499 | } 2500 | 2501 | // conv_ftou converts float64 to uint64 2502 | static uint64_t conv_ftou(double f) { 2503 | if (isnan(f) || f < 0) return 0; 2504 | if (f > 9007199254740991.0) { 2505 | // Outside range: See conv_ftoi for description. 2506 | f = floor(f); 2507 | if (f > UINT64_MAX_FLOAT) return UINT64_MAX; 2508 | } 2509 | return (uint64_t)f; 2510 | } 2511 | 2512 | static void write_double(struct writer *wr, double f) { 2513 | size_t dstsz = wr->count < wr->n ? wr->n - wr->count : 0; 2514 | wr->count += ryu_string(f, 'j', wr->dst?wr->dst+wr->count:NULL, dstsz); 2515 | } 2516 | 2517 | /////////////////////////////////////////// 2518 | // Int64 2519 | /////////////////////////////////////////// 2520 | 2521 | // conv_itot converts int64 to bool 2522 | static bool conv_itot(int64_t i) { 2523 | return i != 0; 2524 | } 2525 | 2526 | // conv_itof converts int64 to double 2527 | static double conv_itof(int64_t i) { 2528 | return (double)i; 2529 | } 2530 | 2531 | // conv_itou converts int64 to uint64 2532 | static uint64_t conv_itou(int64_t i) { 2533 | if (i < 0) return 0; 2534 | return (uint64_t)i; 2535 | } 2536 | 2537 | static void write_int(struct writer *wr, int64_t i) { 2538 | char buf[32]; 2539 | snprintf(buf, sizeof(buf), "%" PRId64, i); 2540 | write_cstr(wr, buf); 2541 | } 2542 | 2543 | 2544 | /////////////////////////////////////////// 2545 | // Uint64 2546 | /////////////////////////////////////////// 2547 | 2548 | // conv_utot converts uint64 to bool 2549 | static bool conv_utot(uint64_t u) { 2550 | return u != 0; 2551 | } 2552 | 2553 | // Utof converts uint64 to double 2554 | static double conv_utof(uint64_t u) { 2555 | return (double)u; 2556 | } 2557 | 2558 | // conv_utoi converts uint64 to int64 2559 | static int64_t conv_utoi(uint64_t u) { 2560 | if (u > INT64_MAX) return INT64_MAX; 2561 | return (int64_t)u; 2562 | } 2563 | 2564 | static void write_uint(struct writer *wr, uint64_t u) { 2565 | char buf[32]; 2566 | snprintf(buf, sizeof(buf), "%" PRIu64, u); 2567 | write_cstr(wr, buf); 2568 | } 2569 | 2570 | /////////////////////////////////////////// 2571 | // String 2572 | /////////////////////////////////////////// 2573 | 2574 | // conv_atot converts string to bool 2575 | // Always returns true unless string is empty. 2576 | static bool conv_atot(const char *a, size_t alen) { 2577 | (void)a; 2578 | return alen > 0; 2579 | } 2580 | 2581 | static bool isnumch(char c) { 2582 | return (c >= '0' && c <= '9') || c == '.'; 2583 | } 2584 | 2585 | // conv_atof converts string to double 2586 | // For infinte numbers use 'Infinity' or '-Infinity', not 'Inf' or '-Inf'. 2587 | // Returns NaN for invalid syntax 2588 | static double conv_atof(const char *a, size_t alen) { 2589 | if (alen == 0) return NAN; 2590 | if (!a[1] || isnumch(a[0]) || (a[0] == '-' && isnumch(a[1])) || 2591 | (a[0] == '+' && isnumch(a[1]))) 2592 | { 2593 | char *end = NULL; 2594 | double f = strtod((char*)a, &end); 2595 | return (size_t)(end-a) == alen ? f : NAN; 2596 | } 2597 | else if (alen == 8 && memcmp(a, "Infinity", 8) == 0) return INFINITY; 2598 | else if (alen == 9 && memcmp(a, "+Infinity", 9) == 0) return INFINITY; 2599 | else if (alen == 9 && memcmp(a, "-Infinity", 9) == 0) return -INFINITY; 2600 | return NAN; 2601 | } 2602 | 2603 | // conv_atoi converts string to int64 2604 | // Returns 0 for invalid syntax 2605 | static int64_t conv_atoi(const char *a, size_t alen) { 2606 | if (alen == 0) return 0; 2607 | char *end = NULL; 2608 | int64_t i = strtoll(a, &end, 10); 2609 | if ((size_t)(end-a) == alen) return i; 2610 | return conv_ftoi(conv_atof(a, alen)); 2611 | } 2612 | 2613 | // conv_atou converts string to uint64 2614 | // Returns 0 for invalid syntax 2615 | static uint64_t conv_atou(const char *a, size_t alen) { 2616 | if (alen == 0) return 0; 2617 | char *end = NULL; 2618 | uint64_t u = strtoull(a, &end, 10); 2619 | if ((size_t)(end-a) == alen) return u; 2620 | return conv_ftoi(conv_atof(a, alen)); 2621 | } 2622 | 2623 | //////////////////////////////////// 2624 | // value construction 2625 | //////////////////////////////////// 2626 | 2627 | struct xv xv_new_stringn(const char *s, size_t len) { 2628 | return from_value(make_string((uint8_t*)s, len)); 2629 | } 2630 | 2631 | struct xv xv_new_string(const char *s) { 2632 | return xv_new_stringn(s, s?strlen(s):0); 2633 | } 2634 | 2635 | struct xv xv_new_double(double d) { 2636 | return from_value(make_float(d)); 2637 | } 2638 | 2639 | struct xv xv_new_int64(int64_t i) { 2640 | return from_value(make_int(i)); 2641 | } 2642 | 2643 | struct xv xv_new_uint64(uint64_t u) { 2644 | return from_value(make_uint(u)); 2645 | } 2646 | 2647 | struct xv xv_new_boolean(bool t) { 2648 | return from_value(make_bool(t)); 2649 | } 2650 | 2651 | struct xv xv_new_undefined(void) { 2652 | return from_value(make_undefined()); 2653 | } 2654 | 2655 | struct xv xv_new_null(void) { 2656 | return from_value(make_null()); 2657 | } 2658 | 2659 | struct xv xv_new_function(struct xv (*func)( 2660 | struct xv value, const struct xv args, void *udata)) 2661 | { 2662 | return from_value(make_func(func)); 2663 | } 2664 | 2665 | struct xv xv_new_jsonn(const char *json, size_t len) { 2666 | return from_value(make_json((uint8_t*)json, len)); 2667 | } 2668 | 2669 | struct xv xv_new_json(const char *json) { 2670 | return xv_new_jsonn(json, json?strlen(json):0); 2671 | } 2672 | 2673 | static int strcmpn(const char *a, size_t alen, const char *b, size_t blen) { 2674 | size_t n = alen < blen ? alen : blen; 2675 | int cmp = strncmp(a, b, n); 2676 | if (cmp == 0) { 2677 | cmp = alen < blen ? -1 : alen > blen ? 1 : 0; 2678 | } 2679 | return cmp; 2680 | } 2681 | 2682 | static int nonstr_strcmpn(struct value value, const uint8_t *str, size_t len) { 2683 | char dst[1024]; 2684 | xv_string_copy(from_value(value), dst, sizeof(dst)); 2685 | return strcmpn(dst, strlen(dst), (char*)str, len); 2686 | } 2687 | 2688 | static int string_comparen(struct value value, const uint8_t *str, size_t len) { 2689 | if (value.kind != STR_KIND) { 2690 | return nonstr_strcmpn(value, str, len); 2691 | } 2692 | return strcmpn((char*)value.str, value.len, (char*)str, len); 2693 | } 2694 | 2695 | int xv_string_compare(struct xv value, const char *str) { 2696 | return xv_string_comparen(value, str, str?strlen(str):0); 2697 | } 2698 | 2699 | int xv_string_comparen(struct xv value, const char *str, size_t len) { 2700 | return string_comparen(to_value(value), (uint8_t*)str, len); 2701 | } 2702 | 2703 | static int string_equaln(struct value value, const uint8_t *str, size_t len) { 2704 | if (value.kind != STR_KIND) { 2705 | return nonstr_strcmpn(value, str, len) == 0; 2706 | } 2707 | return value.len == len && memcmp(value.str, str, len) == 0; 2708 | } 2709 | 2710 | int xv_string_equal(struct xv value, const char *str) { 2711 | return xv_string_equaln(value, str, str?strlen(str):0); 2712 | } 2713 | 2714 | int xv_string_equaln(struct xv value, const char *str, size_t len) { 2715 | return string_equaln(to_value(value), (uint8_t*)str, len); 2716 | } 2717 | 2718 | struct xv xv_new_object(const void *ptr, uint32_t tag) { 2719 | return from_value(make_object(ptr, tag)); 2720 | } 2721 | 2722 | const void *xv_object(struct xv value) { 2723 | struct value tvalue = to_value(value); 2724 | if (tvalue.kind == OBJECT_KIND) { 2725 | return tvalue.obj; 2726 | } 2727 | return NULL; 2728 | } 2729 | 2730 | uint32_t xv_object_tag(struct xv value) { 2731 | struct value tvalue = to_value(value); 2732 | if (tvalue.kind == OBJECT_KIND) { 2733 | return (uint32_t)tvalue.len; 2734 | } 2735 | return 0; 2736 | } 2737 | 2738 | // array value 2739 | size_t xv_array_length(struct xv value) { 2740 | struct value tvalue = to_value(value); 2741 | if (tvalue.kind == ARRAY_KIND){ 2742 | return tvalue.len; 2743 | } 2744 | return 0; 2745 | } 2746 | 2747 | struct xv xv_array_at(struct xv value, size_t index) { 2748 | struct value tvalue = to_value(value); 2749 | if (tvalue.kind == ARRAY_KIND){ 2750 | if (index < tvalue.len) { 2751 | return from_value(tvalue.arr[index]); 2752 | } 2753 | } 2754 | return xv_new_undefined(); 2755 | } 2756 | 2757 | struct xv xv_new_error(const char *msg) { 2758 | return from_value(err_msg(msg)); 2759 | } 2760 | 2761 | bool xv_is_undefined(struct xv value) { 2762 | return to_value(value).kind == UNDEF_KIND; 2763 | } 2764 | 2765 | bool xv_is_global(struct xv value) { 2766 | return (to_value(value).flag & FLAG_GLOBAL) == FLAG_GLOBAL; 2767 | } 2768 | 2769 | struct xv xv_new_array(const struct xv *const*values, 2770 | size_t nvalues) 2771 | { 2772 | return from_value(make_array((struct value*)values, nvalues)); 2773 | } 2774 | 2775 | struct xv_memstats xv_memstats(void) { 2776 | return (struct xv_memstats) { 2777 | .thread_total_size = sizeof(tmem), 2778 | .thread_size = tmemused, 2779 | .thread_allocs = tmemcount, 2780 | .heap_allocs = tnumallocs, 2781 | .heap_size = theapsize, 2782 | }; 2783 | } 2784 | 2785 | bool xv_is_error(struct xv value) { 2786 | return is_err(to_value(value)); 2787 | } 2788 | 2789 | bool xv_is_oom(struct xv value) { 2790 | struct value fvalue = to_value(value); 2791 | return (fvalue.flag&FLAG_EOOM) == FLAG_EOOM && fvalue.kind == ERR_KIND; 2792 | } 2793 | 2794 | size_t xv_string_length(struct xv value) { 2795 | struct value fvalue = to_value(value); 2796 | if (fvalue.kind == STR_KIND) { 2797 | return fvalue.len; 2798 | } 2799 | return xv_string_copy(value, NULL, 0); 2800 | } 2801 | 2802 | char *xv_string(struct xv value) { 2803 | size_t len = xv_string_length(value); 2804 | char *str = emalloc0(len+1); 2805 | if (!str) return NULL; 2806 | struct value fvalue = to_value(value); 2807 | if (fvalue.kind == STR_KIND) { 2808 | memcpy(str, fvalue.str, len); 2809 | str[len] = '\0'; 2810 | } else { 2811 | xv_string_copy(value, str, len+1); 2812 | } 2813 | return str; 2814 | } 2815 | --------------------------------------------------------------------------------