| @@ -0,0 +1,3526 @@ |
| | 1 | /* |
| | 2 | * LIL - Little Interpreted Language |
| | 3 | * Copyright (C) 2010-2013 Kostas Michalopoulos |
| | 4 | * |
| | 5 | * This software is provided 'as-is', without any express or implied |
| | 6 | * warranty. In no event will the authors be held liable for any damages |
| | 7 | * arising from the use of this software. |
| | 8 | * |
| | 9 | * Permission is granted to anyone to use this software for any purpose, |
| | 10 | * including commercial applications, and to alter it and redistribute it |
| | 11 | * freely, subject to the following restrictions: |
| | 12 | * |
| | 13 | * 1. The origin of this software must not be misrepresented; you must not |
| | 14 | * claim that you wrote the original software. If you use this software |
| | 15 | * in a product, an acknowledgment in the product documentation would be |
| | 16 | * appreciated but is not required. |
| | 17 | * 2. Altered source versions must be plainly marked as such, and must not be |
| | 18 | * misrepresented as being the original software. |
| | 19 | * 3. This notice may not be removed or altered from any source distribution. |
| | 20 | * |
| | 21 | * Kostas Michalopoulos <badsector@runtimelegend.com> |
| | 22 | */ |
| | 23 | |
| | 24 | #define __LIL_C_FILE__ |
| | 25 | #include <stdlib.h> |
| | 26 | #include <stdio.h> |
| | 27 | #include <string.h> |
| | 28 | #include <ctype.h> |
| | 29 | #include <math.h> |
| | 30 | #include "lil.h" |
| | 31 | |
| | 32 | /* Enable pools for reusing values, lists and environments. This will use more memory and |
| | 33 | * will rely on the runtime/OS to free the pools once the program ends, but will cause |
| | 34 | * considerably less memory fragmentation and improve the script execution performance. */ |
| | 35 | /*#define LIL_ENABLE_POOLS*/ |
| | 36 | |
| | 37 | /* Enable limiting recursive calls to lil_parse - this can be used to avoid call stack |
| | 38 | * overflows and is also useful when running through an automated fuzzer like AFL */ |
| | 39 | /*#define LIL_ENABLE_RECLIMIT 10000*/ |
| | 40 | |
| | 41 | /* Visual C++ does not have atoll (Pelles C does and thinks it is fully MSVC compatible) */ |
| | 42 | #if defined(_MSC_VER) && !defined(__POCC__) |
| | 43 | #define atoll _atoi64 |
| | 44 | /* disable warning about unsafe standard C calls */ |
| | 45 | #pragma warning(disable:4996) |
| | 46 | /* disable float/int conversion warnings */ |
| | 47 | #pragma warning(disable:4244) |
| | 48 | #endif |
| | 49 | |
| | 50 | #define ERROR_NOERROR 0 |
| | 51 | #define ERROR_DEFAULT 1 |
| | 52 | #define ERROR_FIXHEAD 2 |
| | 53 | |
| | 54 | #define CALLBACKS 8 |
| | 55 | #define MAX_CATCHER_DEPTH 16384 |
| | 56 | #define HASHMAP_CELLS 256 |
| | 57 | #define HASHMAP_CELLMASK 0xFF |
| | 58 | |
| | 59 | /* note: static lil_xxx functions might become public later */ |
| | 60 | |
| | 61 | struct hashentry_t |
| | 62 | { |
| | 63 | char* k; |
| | 64 | void* v; |
| | 65 | }; |
| | 66 | |
| | 67 | struct hashcell_t |
| | 68 | { |
| | 69 | struct hashentry_t* e; |
| | 70 | size_t c; |
| | 71 | }; |
| | 72 | |
| | 73 | typedef struct _hashmap_t |
| | 74 | { |
| | 75 | struct hashcell_t cell[HASHMAP_CELLS]; |
| | 76 | } hashmap_t; |
| | 77 | |
| | 78 | struct _lil_value_t |
| | 79 | { |
| | 80 | size_t l; |
| | 81 | #ifdef LIL_ENABLE_POOLS |
| | 82 | size_t c; |
| | 83 | #endif |
| | 84 | char* d; |
| | 85 | }; |
| | 86 | |
| | 87 | struct _lil_var_t |
| | 88 | { |
| | 89 | char* n; |
| | 90 | char* w; |
| | 91 | struct _lil_env_t* env; |
| | 92 | lil_value_t v; |
| | 93 | }; |
| | 94 | |
| | 95 | struct _lil_env_t |
| | 96 | { |
| | 97 | struct _lil_env_t* parent; |
| | 98 | lil_func_t func; |
| | 99 | lil_value_t catcher_for; |
| | 100 | lil_var_t* var; |
| | 101 | size_t vars; |
| | 102 | hashmap_t varmap; |
| | 103 | lil_value_t retval; |
| | 104 | int retval_set; |
| | 105 | int breakrun; |
| | 106 | }; |
| | 107 | |
| | 108 | struct _lil_list_t |
| | 109 | { |
| | 110 | lil_value_t* v; |
| | 111 | size_t c; |
| | 112 | size_t cap; |
| | 113 | }; |
| | 114 | |
| | 115 | struct _lil_func_t |
| | 116 | { |
| | 117 | char* name; |
| | 118 | lil_value_t code; |
| | 119 | lil_list_t argnames; |
| | 120 | lil_func_proc_t proc; |
| | 121 | }; |
| | 122 | |
| | 123 | struct _lil_t |
| | 124 | { |
| | 125 | const char* code; /* need save on parse */ |
| | 126 | const char* rootcode; |
| | 127 | size_t clen; /* need save on parse */ |
| | 128 | size_t head; /* need save on parse */ |
| | 129 | int ignoreeol; |
| | 130 | lil_func_t* cmd; |
| | 131 | size_t cmds; |
| | 132 | size_t syscmds; |
| | 133 | hashmap_t cmdmap; |
| | 134 | char* catcher; |
| | 135 | int in_catcher; |
| | 136 | char* dollarprefix; |
| | 137 | lil_env_t env; |
| | 138 | lil_env_t rootenv; |
| | 139 | lil_env_t downenv; |
| | 140 | lil_value_t empty; |
| | 141 | int error; |
| | 142 | size_t err_head; |
| | 143 | char* err_msg; |
| | 144 | lil_callback_proc_t callback[CALLBACKS]; |
| | 145 | size_t parse_depth; |
| | 146 | void* data; |
| | 147 | char* embed; |
| | 148 | size_t embedlen; |
| | 149 | }; |
| | 150 | |
| | 151 | typedef struct _expreval_t |
| | 152 | { |
| | 153 | const char* code; |
| | 154 | size_t len, head; |
| | 155 | lilint_t ival; |
| | 156 | double dval; |
| | 157 | int type; |
| | 158 | int error; |
| | 159 | } expreval_t; |
| | 160 | |
| | 161 | static lil_value_t next_word(lil_t lil); |
| | 162 | static void register_stdcmds(lil_t lil); |
| | 163 | |
| | 164 | #ifdef LIL_ENABLE_POOLS |
| | 165 | static lil_value_t* pool; |
| | 166 | static int poolsize, poolcap; |
| | 167 | static lil_list_t* listpool; |
| | 168 | static size_t listpoolsize, listpoolcap; |
| | 169 | static lil_env_t* envpool; |
| | 170 | static size_t envpoolsize, envpoolcap; |
| | 171 | #endif |
| | 172 | |
| | 173 | static char* strclone(const char* s) |
| | 174 | { |
| | 175 | size_t len = strlen(s) + 1; |
| | 176 | char* ns = malloc(len); |
| | 177 | if (!ns) return NULL; |
| | 178 | memcpy(ns, s, len); |
| | 179 | return ns; |
| | 180 | } |
| | 181 | |
| | 182 | static unsigned long hm_hash(const char* key) |
| | 183 | { |
| | 184 | unsigned long hash = 5381; |
| | 185 | int c; |
| | 186 | while ((c = *key++)) hash = ((hash << 5) + hash) + c; |
| | 187 | return hash; |
| | 188 | } |
| | 189 | |
| | 190 | static void hm_init(hashmap_t* hm) |
| | 191 | { |
| | 192 | memset(hm, 0, sizeof(hashmap_t)); |
| | 193 | } |
| | 194 | |
| | 195 | static void hm_destroy(hashmap_t* hm) |
| | 196 | { |
| | 197 | size_t i, j; |
| | 198 | for (i=0; i<HASHMAP_CELLS; i++) { |
| | 199 | for (j=0; j<hm->cell[i].c; j++) |
| | 200 | free(hm->cell[i].e[j].k); |
| | 201 | free(hm->cell[i].e); |
| | 202 | } |
| | 203 | } |
| | 204 | |
| | 205 | static void hm_put(hashmap_t* hm, const char* key, void* value) |
| | 206 | { |
| | 207 | struct hashcell_t* cell = hm->cell + (hm_hash(key) & HASHMAP_CELLMASK); |
| | 208 | size_t i; |
| | 209 | for (i=0; i<cell->c; i++) |
| | 210 | if (!strcmp(key, cell->e[i].k)) { |
| | 211 | cell->e[i].v = value; |
| | 212 | return; |
| | 213 | } |
| | 214 | cell->e = realloc(cell->e, sizeof(struct hashentry_t)*(cell->c + 1)); |
| | 215 | cell->e[cell->c].k = strclone(key); |
| | 216 | cell->e[cell->c].v = value; |
| | 217 | cell->c++; |
| | 218 | } |
| | 219 | |
| | 220 | static void* hm_get(hashmap_t* hm, const char* key) |
| | 221 | { |
| | 222 | struct hashcell_t* cell = hm->cell + (hm_hash(key) & HASHMAP_CELLMASK); |
| | 223 | size_t i; |
| | 224 | for (i = 0; i<cell->c; i++) |
| | 225 | if (!strcmp(key, cell->e[i].k)) |
| | 226 | return cell->e[i].v; |
| | 227 | return NULL; |
| | 228 | } |
| | 229 | |
| | 230 | static int hm_has(hashmap_t* hm, const char* key) |
| | 231 | { |
| | 232 | struct hashcell_t* cell = hm->cell + (hm_hash(key) & HASHMAP_CELLMASK); |
| | 233 | size_t i; |
| | 234 | for (i = 0; i < cell->c; i++) |
| | 235 | if (!strcmp(key, cell->e[i].k)) |
| | 236 | return 1; |
| | 237 | return 0; |
| | 238 | } |
| | 239 | |
| | 240 | #ifdef LIL_ENABLE_POOLS |
| | 241 | static lil_value_t alloc_from_pool(void) |
| | 242 | { |
| | 243 | if (poolsize > 0) { |
| | 244 | poolsize--; |
| | 245 | return pool[poolsize]; |
| | 246 | } else { |
| | 247 | lil_value_t val = calloc(1, sizeof(struct _lil_value_t)); |
| | 248 | return val; |
| | 249 | } |
| | 250 | } |
| | 251 | |
| | 252 | static void release_to_pool(lil_value_t val) |
| | 253 | { |
| | 254 | if (poolsize == poolcap) { |
| | 255 | poolcap = poolcap ? (poolcap + poolcap / 2) : 64; |
| | 256 | pool = realloc(pool, sizeof(lil_value_t)*poolcap); |
| | 257 | } |
| | 258 | pool[poolsize++] = val; |
| | 259 | } |
| | 260 | |
| | 261 | static void ensure_capacity(lil_value_t val, size_t cap) |
| | 262 | { |
| | 263 | if (val->c < cap) { |
| | 264 | val->c = cap + 128; |
| | 265 | val->d = realloc(val->d, val->c); |
| | 266 | } |
| | 267 | } |
| | 268 | #endif |
| | 269 | |
| | 270 | static lil_value_t alloc_value_len(const char* str, size_t len) |
| | 271 | { |
| | 272 | #ifdef LIL_ENABLE_POOLS |
| | 273 | lil_value_t val = alloc_from_pool(); |
| | 274 | #else |
| | 275 | lil_value_t val = calloc(1, sizeof(struct _lil_value_t)); |
| | 276 | #endif |
| | 277 | if (!val) return NULL; |
| | 278 | if (str) { |
| | 279 | val->l = len; |
| | 280 | #ifdef LIL_ENABLE_POOLS |
| | 281 | ensure_capacity(val, len + 1); |
| | 282 | #else |
| | 283 | val->d = malloc(len + 1); |
| | 284 | if (!val->d) { |
| | 285 | free(val); |
| | 286 | return NULL; |
| | 287 | } |
| | 288 | #endif |
| | 289 | memcpy(val->d, str, len); |
| | 290 | val->d[len] = 0; |
| | 291 | } else { |
| | 292 | val->l = 0; |
| | 293 | #ifdef LIL_ENABLE_POOLS |
| | 294 | ensure_capacity(val, 1); |
| | 295 | val->d[0] = '\0'; |
| | 296 | #else |
| | 297 | val->d = NULL; |
| | 298 | #endif |
| | 299 | } |
| | 300 | return val; |
| | 301 | } |
| | 302 | |
| | 303 | static lil_value_t alloc_value(const char* str) |
| | 304 | { |
| | 305 | return alloc_value_len(str, str ? strlen(str) : 0); |
| | 306 | } |
| | 307 | |
| | 308 | lil_value_t lil_clone_value(lil_value_t src) |
| | 309 | { |
| | 310 | lil_value_t val; |
| | 311 | if (!src) return NULL; |
| | 312 | #ifdef LIL_ENABLE_POOLS |
| | 313 | val = alloc_from_pool(); |
| | 314 | #else |
| | 315 | val = calloc(1, sizeof(struct _lil_value_t)); |
| | 316 | #endif |
| | 317 | if (!val) return NULL; |
| | 318 | val->l = src->l; |
| | 319 | if (src->l) { |
| | 320 | #ifdef LIL_ENABLE_POOLS |
| | 321 | ensure_capacity(val, val->l + 1); |
| | 322 | #else |
| | 323 | val->d = malloc(val->l + 1); |
| | 324 | if (!val->d) { |
| | 325 | free(val); |
| | 326 | return NULL; |
| | 327 | } |
| | 328 | #endif |
| | 329 | memcpy(val->d, src->d, val->l + 1); |
| | 330 | } else { |
| | 331 | #ifdef LIL_ENABLE_POOLS |
| | 332 | ensure_capacity(val, 1); |
| | 333 | val->d[0] = '\0'; |
| | 334 | #else |
| | 335 | val->d = NULL; |
| | 336 | #endif |
| | 337 | } |
| | 338 | return val; |
| | 339 | } |
| | 340 | |
| | 341 | int lil_append_char(lil_value_t val, char ch) |
| | 342 | { |
| | 343 | #ifdef LIL_ENABLE_POOLS |
| | 344 | ensure_capacity(val, val->l + 2); |
| | 345 | val->d[val->l++] = ch; |
| | 346 | val->d[val->l] = '\0'; |
| | 347 | #else |
| | 348 | char* new = realloc(val->d, val->l + 2); |
| | 349 | if (!new) return 0; |
| | 350 | new[val->l++] = ch; |
| | 351 | new[val->l] = 0; |
| | 352 | val->d = new; |
| | 353 | #endif |
| | 354 | return 1; |
| | 355 | } |
| | 356 | |
| | 357 | int lil_append_string_len(lil_value_t val, const char* s, size_t len) |
| | 358 | { |
| | 359 | #ifndef LIL_ENABLE_POOLS |
| | 360 | char* new; |
| | 361 | #endif |
| | 362 | if (!s || !s[0]) return 1; |
| | 363 | #ifdef LIL_ENABLE_POOLS |
| | 364 | ensure_capacity(val, val->l + len + 1); |
| | 365 | memcpy(val->d + val->l, s, len + 1); |
| | 366 | #else |
| | 367 | new = realloc(val->d, val->l + len + 1); |
| | 368 | if (!new) return 0; |
| | 369 | memcpy(new + val->l, s, len + 1); |
| | 370 | val->d = new; |
| | 371 | #endif |
| | 372 | val->l += len; |
| | 373 | return 1; |
| | 374 | } |
| | 375 | |
| | 376 | int lil_append_string(lil_value_t val, const char* s) |
| | 377 | { |
| | 378 | return lil_append_string_len(val, s, strlen(s)); |
| | 379 | } |
| | 380 | |
| | 381 | int lil_append_val(lil_value_t val, lil_value_t v) |
| | 382 | { |
| | 383 | #ifndef LIL_ENABLE_POOLS |
| | 384 | char* new; |
| | 385 | #endif |
| | 386 | if (!v || !v->l) return 1; |
| | 387 | #ifdef LIL_ENABLE_POOLS |
| | 388 | ensure_capacity(val, val->l + v->l + 1); |
| | 389 | memcpy(val->d + val->l, v->d, v->l + 1); |
| | 390 | #else |
| | 391 | new = realloc(val->d, val->l + v->l + 1); |
| | 392 | if (!new) return 0; |
| | 393 | memcpy(new + val->l, v->d, v->l + 1); |
| | 394 | val->d = new; |
| | 395 | #endif |
| | 396 | val->l += v->l; |
| | 397 | return 1; |
| | 398 | } |
| | 399 | |
| | 400 | void lil_free_value(lil_value_t val) |
| | 401 | { |
| | 402 | if (!val) return; |
| | 403 | #ifdef LIL_ENABLE_POOLS |
| | 404 | release_to_pool(val); |
| | 405 | #else |
| | 406 | free(val->d); |
| | 407 | free(val); |
| | 408 | #endif |
| | 409 | } |
| | 410 | |
| | 411 | lil_list_t lil_alloc_list(void) |
| | 412 | { |
| | 413 | lil_list_t list; |
| | 414 | #ifdef LIL_ENABLE_POOLS |
| | 415 | if (listpoolsize > 0) |
| | 416 | return listpool[--listpoolsize]; |
| | 417 | #endif |
| | 418 | list = calloc(1, sizeof(struct _lil_list_t)); |
| | 419 | list->v = NULL; |
| | 420 | return list; |
| | 421 | } |
| | 422 | |
| | 423 | void lil_free_list(lil_list_t list) |
| | 424 | { |
| | 425 | size_t i; |
| | 426 | if (!list) return; |
| | 427 | for (i = 0; i<list->c; i++) lil_free_value(list->v[i]); |
| | 428 | #ifdef LIL_ENABLE_POOLS |
| | 429 | list->c = 0; |
| | 430 | if (listpoolsize == listpoolcap) { |
| | 431 | listpoolcap = listpoolcap ? (listpoolcap + listpoolcap / 2) : 32; |
| | 432 | listpool = realloc(listpool, sizeof(lil_list_t)*listpoolcap); |
| | 433 | } |
| | 434 | listpool[listpoolsize++] = list; |
| | 435 | #else |
| | 436 | free(list->v); |
| | 437 | free(list); |
| | 438 | #endif |
| | 439 | } |
| | 440 | |
| | 441 | void lil_list_append(lil_list_t list, lil_value_t val) |
| | 442 | { |
| | 443 | if (list->c == list->cap) { |
| | 444 | size_t cap = list->cap ? (list->cap + list->cap / 2) : 32; |
| | 445 | lil_value_t* nv = realloc(list->v, sizeof(lil_value_t)*cap); |
| | 446 | if (!nv) return; |
| | 447 | list->cap = cap; |
| | 448 | list->v = nv; |
| | 449 | } |
| | 450 | list->v[list->c++] = val; |
| | 451 | } |
| | 452 | |
| | 453 | size_t lil_list_size(lil_list_t list) |
| | 454 | { |
| | 455 | return list->c; |
| | 456 | } |
| | 457 | |
| | 458 | lil_value_t lil_list_get(lil_list_t list, size_t index) |
| | 459 | { |
| | 460 | return index >= list->c ? NULL : list->v[index]; |
| | 461 | } |
| | 462 | |
| | 463 | static int needs_escape(const char* str) |
| | 464 | { |
| | 465 | size_t i; |
| | 466 | if (!str || !str[0]) return 1; |
| | 467 | for (i=0; str[i]; i++) |
| | 468 | if (ispunct(str[i]) || isspace(str[i])) return 1; |
| | 469 | return 0; |
| | 470 | } |
| | 471 | |
| | 472 | lil_value_t lil_list_to_value(lil_list_t list, int do_escape) |
| | 473 | { |
| | 474 | lil_value_t val = alloc_value(NULL); |
| | 475 | size_t i, j; |
| | 476 | for (i=0; i<list->c; i++) { |
| | 477 | int escape = do_escape ? needs_escape(lil_to_string(list->v[i])) : 0; |
| | 478 | if (i) lil_append_char(val, ' '); |
| | 479 | if (escape) { |
| | 480 | lil_append_char(val, '{'); |
| | 481 | for (j=0; j < list->v[i]->l; j++) { |
| | 482 | if (list->v[i]->d[j] == '{') |
| | 483 | lil_append_string(val, "}\"\\o\"{"); |
| | 484 | else if (list->v[i]->d[j] == '}') |
| | 485 | lil_append_string(val, "}\"\\c\"{"); |
| | 486 | else lil_append_char(val, list->v[i]->d[j]); |
| | 487 | } |
| | 488 | lil_append_char(val, '}'); |
| | 489 | } else lil_append_val(val, list->v[i]); |
| | 490 | } |
| | 491 | return val; |
| | 492 | } |
| | 493 | |
| | 494 | lil_env_t lil_alloc_env(lil_env_t parent) |
| | 495 | { |
| | 496 | lil_env_t env; |
| | 497 | #ifdef LIL_ENABLE_POOLS |
| | 498 | if (envpoolsize > 0) { |
| | 499 | size_t i, j; |
| | 500 | env = envpool[--envpoolsize]; |
| | 501 | env->parent = parent; |
| | 502 | env->func = NULL; |
| | 503 | env->catcher_for = NULL; |
| | 504 | env->var = NULL; |
| | 505 | env->vars = 0; |
| | 506 | env->retval = NULL; |
| | 507 | env->retval_set = 0; |
| | 508 | env->breakrun = 0; |
| | 509 | for (i = 0; i < HASHMAP_CELLS; i++) { |
| | 510 | for (j = 0; j < env->varmap.cell[i].c; j++) |
| | 511 | free(env->varmap.cell[i].e[j].k); |
| | 512 | env->varmap.cell[i].c = 0; |
| | 513 | } |
| | 514 | return env; |
| | 515 | } |
| | 516 | #endif |
| | 517 | env = calloc(1, sizeof(struct _lil_env_t)); |
| | 518 | env->parent = parent; |
| | 519 | return env; |
| | 520 | } |
| | 521 | |
| | 522 | void lil_free_env(lil_env_t env) |
| | 523 | { |
| | 524 | size_t i; |
| | 525 | if (!env) return; |
| | 526 | lil_free_value(env->retval); |
| | 527 | #ifdef LIL_ENABLE_POOLS |
| | 528 | for (i = 0; i<env->vars; i++) { |
| | 529 | free(env->var[i]->n); |
| | 530 | lil_free_value(env->var[i]->v); |
| | 531 | free(env->var[i]->w); |
| | 532 | free(env->var[i]); |
| | 533 | } |
| | 534 | free(env->var); |
| | 535 | if (envpoolsize == envpoolcap) { |
| | 536 | envpoolcap = envpoolcap ? (envpoolcap + envpoolcap / 2) : 64; |
| | 537 | envpool = realloc(envpool, sizeof(lil_env_t)*envpoolcap); |
| | 538 | } |
| | 539 | envpool[envpoolsize++] = env; |
| | 540 | #else |
| | 541 | hm_destroy(&env->varmap); |
| | 542 | for (i=0; i<env->vars; i++) { |
| | 543 | free(env->var[i]->n); |
| | 544 | lil_free_value(env->var[i]->v); |
| | 545 | free(env->var[i]->w); |
| | 546 | free(env->var[i]); |
| | 547 | } |
| | 548 | free(env->var); |
| | 549 | free(env); |
| | 550 | #endif |
| | 551 | } |
| | 552 | |
| | 553 | static lil_var_t lil_find_local_var(lil_t lil, lil_env_t env, const char* name) |
| | 554 | { |
| | 555 | #if 0 |
| | 556 | if (env->vars > 0) { |
| | 557 | size_t i = env->vars - 1; |
| | 558 | while (1) { |
| | 559 | if (!strcmp(env->var[i]->n, name)) return env->var[i]; |
| | 560 | if (!i) break; |
| | 561 | i--; |
| | 562 | } |
| | 563 | } |
| | 564 | return NULL; |
| | 565 | #else |
| | 566 | return hm_get(&env->varmap, name); |
| | 567 | #endif |
| | 568 | } |
| | 569 | |
| | 570 | static lil_var_t lil_find_var(lil_t lil, lil_env_t env, const char* name) |
| | 571 | { |
| | 572 | lil_var_t r = lil_find_local_var(lil, env, name); |
| | 573 | return r ? r : (env == lil->rootenv ? NULL : lil_find_var(lil, lil->rootenv, name)); |
| | 574 | } |
| | 575 | |
| | 576 | static lil_func_t find_cmd(lil_t lil, const char* name) |
| | 577 | { |
| | 578 | #if 0 |
| | 579 | if (lil->cmds > 0) { |
| | 580 | size_t i = lil->cmds - 1; |
| | 581 | while (1) { |
| | 582 | if (!strcmp(lil->cmd[i]->name, name)) return lil->cmd[i]; |
| | 583 | if (!i) break; |
| | 584 | i--; |
| | 585 | } |
| | 586 | } |
| | 587 | return NULL; |
| | 588 | #else |
| | 589 | return hm_get(&lil->cmdmap, name); |
| | 590 | #endif |
| | 591 | } |
| | 592 | |
| | 593 | static lil_func_t add_func(lil_t lil, const char* name) |
| | 594 | { |
| | 595 | lil_func_t cmd; |
| | 596 | lil_func_t* ncmd; |
| | 597 | cmd = find_cmd(lil, name); |
| | 598 | if (cmd) { |
| | 599 | if (cmd->argnames) lil_free_list(cmd->argnames); |
| | 600 | lil_free_value(cmd->code); |
| | 601 | cmd->argnames = NULL; |
| | 602 | cmd->code = NULL; |
| | 603 | cmd->proc = NULL; |
| | 604 | return cmd; |
| | 605 | } |
| | 606 | cmd = calloc(1, sizeof(struct _lil_func_t)); |
| | 607 | cmd->name = strclone(name); |
| | 608 | ncmd = realloc(lil->cmd, sizeof(lil_func_t)*(lil->cmds + 1)); |
| | 609 | if (!ncmd) { |
| | 610 | free(cmd); |
| | 611 | return NULL; |
| | 612 | } |
| | 613 | lil->cmd = ncmd; |
| | 614 | ncmd[lil->cmds++] = cmd; |
| | 615 | hm_put(&lil->cmdmap, name, cmd); |
| | 616 | return cmd; |
| | 617 | } |
| | 618 | |
| | 619 | static void del_func(lil_t lil, lil_func_t cmd) |
| | 620 | { |
| | 621 | size_t i, index = lil->cmds; |
| | 622 | for (i=0; i < lil->cmds; i++) |
| | 623 | if (lil->cmd[i] == cmd) { |
| | 624 | index = i; |
| | 625 | break; |
| | 626 | } |
| | 627 | if (index == lil->cmds) return; |
| | 628 | hm_put(&lil->cmdmap, cmd->name, 0); |
| | 629 | if (cmd->argnames) lil_free_list(cmd->argnames); |
| | 630 | lil_free_value(cmd->code); |
| | 631 | free(cmd->name); |
| | 632 | free(cmd); |
| | 633 | lil->cmds--; |
| | 634 | for (i=index; i < lil->cmds; i++) lil->cmd[i] = lil->cmd[i + 1]; |
| | 635 | } |
| | 636 | |
| | 637 | int lil_register(lil_t lil, const char* name, lil_func_proc_t proc) |
| | 638 | { |
| | 639 | lil_func_t cmd = add_func(lil, name); |
| | 640 | if (!cmd) return 0; |
| | 641 | cmd->proc = proc; |
| | 642 | return 1; |
| | 643 | } |
| | 644 | |
| | 645 | lil_var_t lil_set_var(lil_t lil, const char* name, lil_value_t val, int local) |
| | 646 | { |
| | 647 | lil_var_t* nvar; |
| | 648 | lil_env_t env = local == LIL_SETVAR_GLOBAL ? lil->rootenv : lil->env; |
| | 649 | int freeval = 0; |
| | 650 | if (!name[0]) return NULL; |
| | 651 | if (local != LIL_SETVAR_LOCAL_NEW) { |
| | 652 | lil_var_t var = lil_find_var(lil, env, name); |
| | 653 | if (local == LIL_SETVAR_LOCAL_ONLY && var && var->env == lil->rootenv && var->env != env) |
| | 654 | var = NULL; |
| | 655 | if (((!var && env == lil->rootenv) || (var && var->env == lil->rootenv)) && lil->callback[LIL_CALLBACK_SETVAR]) { |
| | 656 | lil_setvar_callback_proc_t proc = (lil_setvar_callback_proc_t)lil->callback[LIL_CALLBACK_SETVAR]; |
| | 657 | lil_value_t newval = val; |
| | 658 | int r = proc(lil, name, &newval); |
| | 659 | if (r < 0) return NULL; |
| | 660 | if (r) { |
| | 661 | val = newval; |
| | 662 | freeval = 1; |
| | 663 | } |
| | 664 | } |
| | 665 | if (var) { |
| | 666 | lil_free_value(var->v); |
| | 667 | var->v = freeval ? val : lil_clone_value(val); |
| | 668 | if (var->w) { |
| | 669 | lil_env_t save_env; |
| | 670 | save_env = lil->env; |
| | 671 | lil->env = var->env; |
| | 672 | lil_free_value(lil_parse(lil, var->w, 0, 1)); |
| | 673 | lil->env = save_env; |
| | 674 | } |
| | 675 | return var; |
| | 676 | } |
| | 677 | } |
| | 678 | |
| | 679 | nvar = realloc(env->var, sizeof(lil_var_t)*(env->vars + 1)); |
| | 680 | if (!nvar) { |
| | 681 | /* TODO: report memory error */ |
| | 682 | return NULL; |
| | 683 | } |
| | 684 | env->var = nvar; |
| | 685 | nvar[env->vars] = calloc(1, sizeof(struct _lil_var_t)); |
| | 686 | nvar[env->vars]->n = strclone(name); |
| | 687 | nvar[env->vars]->w = NULL; |
| | 688 | nvar[env->vars]->env = env; |
| | 689 | nvar[env->vars]->v = freeval ? val : lil_clone_value(val); |
| | 690 | hm_put(&env->varmap, name, nvar[env->vars]); |
| | 691 | return nvar[env->vars++]; |
| | 692 | } |
| | 693 | |
| | 694 | lil_value_t lil_get_var(lil_t lil, const char* name) |
| | 695 | { |
| | 696 | return lil_get_var_or(lil, name, lil->empty); |
| | 697 | } |
| | 698 | |
| | 699 | lil_value_t lil_get_var_or(lil_t lil, const char* name, lil_value_t defvalue) |
| | 700 | { |
| | 701 | lil_var_t var = lil_find_var(lil, lil->env, name); |
| | 702 | lil_value_t retval = var ? var->v : defvalue; |
| | 703 | if (lil->callback[LIL_CALLBACK_GETVAR] && (!var || var->env == lil->rootenv)) { |
| | 704 | lil_getvar_callback_proc_t proc = (lil_getvar_callback_proc_t)lil->callback[LIL_CALLBACK_GETVAR]; |
| | 705 | lil_value_t newretval = retval; |
| | 706 | if (proc(lil, name, &newretval)) |
| | 707 | retval = newretval; |
| | 708 | } |
| | 709 | return retval; |
| | 710 | } |
| | 711 | |
| | 712 | lil_env_t lil_push_env(lil_t lil) |
| | 713 | { |
| | 714 | lil_env_t env = lil_alloc_env(lil->env); |
| | 715 | lil->env = env; |
| | 716 | return env; |
| | 717 | } |
| | 718 | |
| | 719 | void lil_pop_env(lil_t lil) |
| | 720 | { |
| | 721 | if (lil->env->parent) { |
| | 722 | lil_env_t next = lil->env->parent; |
| | 723 | lil_free_env(lil->env); |
| | 724 | lil->env = next; |
| | 725 | } |
| | 726 | } |
| | 727 | |
| | 728 | lil_t lil_new(void) |
| | 729 | { |
| | 730 | lil_t lil = calloc(1, sizeof(struct _lil_t)); |
| | 731 | lil->rootenv = lil->env = lil_alloc_env(NULL); |
| | 732 | lil->empty = alloc_value(NULL); |
| | 733 | lil->dollarprefix = strclone("set "); |
| | 734 | hm_init(&lil->cmdmap); |
| | 735 | register_stdcmds(lil); |
| | 736 | return lil; |
| | 737 | } |
| | 738 | |
| | 739 | static int islilspecial(char ch) |
| | 740 | { |
| | 741 | return ch == '$' || ch == '{' || ch == '}' || ch == '[' || ch == ']' || ch == '"' || ch == '\'' || ch == ';'; |
| | 742 | } |
| | 743 | |
| | 744 | static int eolchar(char ch) |
| | 745 | { |
| | 746 | return ch == '\n' || ch == '\r' || ch == ';'; |
| | 747 | } |
| | 748 | |
| | 749 | static int ateol(lil_t lil) |
| | 750 | { |
| | 751 | return !(lil->ignoreeol) && eolchar(lil->code[lil->head]); |
| | 752 | } |
| | 753 | |
| | 754 | static void skip_spaces(lil_t lil) |
| | 755 | { |
| | 756 | while (lil->head < lil->clen) { |
| | 757 | if (lil->code[lil->head] == '#') { |
| | 758 | if (lil->code[lil->head + 1] == '#' && lil->code[lil->head + 2] != '#') { |
| | 759 | lil->head += 2; |
| | 760 | while (lil->head < lil->clen) { |
| | 761 | if ((lil->code[lil->head] == '#') && (lil->code[lil->head + 1] == '#') && (lil->code[lil->head + 2] != '#')) { |
| | 762 | lil->head += 2; |
| | 763 | break; |
| | 764 | } |
| | 765 | lil->head++; |
| | 766 | } |
| | 767 | } else { |
| | 768 | while (lil->head < lil->clen && !eolchar(lil->code[lil->head])) lil->head++; |
| | 769 | } |
| | 770 | } else if (lil->code[lil->head] == '\\' && eolchar(lil->code[lil->head + 1])) { |
| | 771 | lil->head++; |
| | 772 | while (lil->head < lil->clen && eolchar(lil->code[lil->head])) lil->head++; |
| | 773 | } else if (eolchar(lil->code[lil->head])) { |
| | 774 | if (lil->ignoreeol) lil->head++; |
| | 775 | else break; |
| | 776 | } else if (isspace(lil->code[lil->head])) |
| | 777 | lil->head++; |
| | 778 | else break; |
| | 779 | } |
| | 780 | } |
| | 781 | |
| | 782 | static lil_value_t get_bracketpart(lil_t lil) |
| | 783 | { |
| | 784 | size_t cnt = 1; |
| | 785 | int save_eol = lil->ignoreeol; |
| | 786 | lil->ignoreeol = 0; |
| | 787 | lil_value_t val, cmd = alloc_value(NULL); |
| | 788 | lil->head++; |
| | 789 | while (lil->head < lil->clen) { |
| | 790 | if (lil->code[lil->head] == '[') { |
| | 791 | lil->head++; |
| | 792 | cnt++; |
| | 793 | lil_append_char(cmd, '['); |
| | 794 | } else if (lil->code[lil->head] == ']') { |
| | 795 | lil->head++; |
| | 796 | if (--cnt == 0) break; |
| | 797 | else lil_append_char(cmd, ']'); |
| | 798 | } else { |
| | 799 | lil_append_char(cmd, lil->code[lil->head++]); |
| | 800 | } |
| | 801 | } |
| | 802 | val = lil_parse_value(lil, cmd, 0); |
| | 803 | lil_free_value(cmd); |
| | 804 | lil->ignoreeol = save_eol; |
| | 805 | return val; |
| | 806 | } |
| | 807 | |
| | 808 | static lil_value_t get_dollarpart(lil_t lil) |
| | 809 | { |
| | 810 | lil_value_t val, name, tmp; |
| | 811 | lil->head++; |
| | 812 | name = next_word(lil); |
| | 813 | tmp = alloc_value(lil->dollarprefix); |
| | 814 | lil_append_val(tmp, name); |
| | 815 | lil_free_value(name); |
| | 816 | val = lil_parse_value(lil, tmp, 0); |
| | 817 | lil_free_value(tmp); |
| | 818 | return val; |
| | 819 | } |
| | 820 | |
| | 821 | static lil_value_t next_word(lil_t lil) |
| | 822 | { |
| | 823 | lil_value_t val; |
| | 824 | size_t start; |
| | 825 | skip_spaces(lil); |
| | 826 | if (lil->code[lil->head] == '$') { |
| | 827 | val = get_dollarpart(lil); |
| | 828 | } else if (lil->code[lil->head] == '{') { |
| | 829 | size_t cnt = 1; |
| | 830 | lil->head++; |
| | 831 | val = alloc_value(NULL); |
| | 832 | while (lil->head < lil->clen) { |
| | 833 | if (lil->code[lil->head] == '{') { |
| | 834 | lil->head++; |
| | 835 | cnt++; |
| | 836 | lil_append_char(val, '{'); |
| | 837 | } else if (lil->code[lil->head] == '}') { |
| | 838 | lil->head++; |
| | 839 | if (--cnt == 0) break; |
| | 840 | else lil_append_char(val, '}'); |
| | 841 | } else { |
| | 842 | lil_append_char(val, lil->code[lil->head++]); |
| | 843 | } |
| | 844 | } |
| | 845 | } else if (lil->code[lil->head] == '[') { |
| | 846 | val = get_bracketpart(lil); |
| | 847 | } else if (lil->code[lil->head] == '"' || lil->code[lil->head] == '\'') { |
| | 848 | char sc = lil->code[lil->head++]; |
| | 849 | val = alloc_value(NULL); |
| | 850 | while (lil->head < lil->clen) { |
| | 851 | if (lil->code[lil->head] == '[' || lil->code[lil->head] == '$') { |
| | 852 | lil_value_t tmp = lil->code[lil->head] == '$' ? get_dollarpart(lil) : get_bracketpart(lil); |
| | 853 | lil_append_val(val, tmp); |
| | 854 | lil_free_value(tmp); |
| | 855 | lil->head--; /* avoid skipping the char below */ |
| | 856 | } else if (lil->code[lil->head] == '\\') { |
| | 857 | lil->head++; |
| | 858 | switch (lil->code[lil->head]) { |
| | 859 | case 'b': lil_append_char(val, '\b'); break; |
| | 860 | case 't': lil_append_char(val, '\t'); break; |
| | 861 | case 'n': lil_append_char(val, '\n'); break; |
| | 862 | case 'v': lil_append_char(val, '\v'); break; |
| | 863 | case 'f': lil_append_char(val, '\f'); break; |
| | 864 | case 'r': lil_append_char(val, '\r'); break; |
| | 865 | case '0': lil_append_char(val, 0); break; |
| | 866 | case 'a': lil_append_char(val, '\a'); break; |
| | 867 | case 'c': lil_append_char(val, '}'); break; |
| | 868 | case 'o': lil_append_char(val, '{'); break; |
| | 869 | default: lil_append_char(val, lil->code[lil->head]); break; |
| | 870 | } |
| | 871 | } else if (lil->code[lil->head] == sc) { |
| | 872 | lil->head++; |
| | 873 | break; |
| | 874 | } else { |
| | 875 | lil_append_char(val, lil->code[lil->head]); |
| | 876 | } |
| | 877 | lil->head++; |
| | 878 | } |
| | 879 | } else { |
| | 880 | start = lil->head; |
| | 881 | while (lil->head < lil->clen && !isspace(lil->code[lil->head]) && !islilspecial(lil->code[lil->head])) { |
| | 882 | lil->head++; |
| | 883 | } |
| | 884 | val = alloc_value_len(lil->code + start, lil->head - start); |
| | 885 | } |
| | 886 | return val ? val : alloc_value(NULL); |
| | 887 | } |
| | 888 | |
| | 889 | static lil_list_t substitute(lil_t lil) |
| | 890 | { |
| | 891 | lil_list_t words = lil_alloc_list(); |
| | 892 | |
| | 893 | skip_spaces(lil); |
| | 894 | while (lil->head < lil->clen && !ateol(lil) && !lil->error) { |
| | 895 | lil_value_t w = alloc_value(NULL); |
| | 896 | do { |
| | 897 | size_t head = lil->head; |
| | 898 | lil_value_t wp = next_word(lil); |
| | 899 | if (head == lil->head) { /* something wrong, the parser can't proceed */ |
| | 900 | lil_free_value(w); |
| | 901 | lil_free_value(wp); |
| | 902 | lil_free_list(words); |
| | 903 | return NULL; |
| | 904 | } |
| | 905 | lil_append_val(w, wp); |
| | 906 | lil_free_value(wp); |
| | 907 | } while (lil->head < lil->clen && !eolchar(lil->code[lil->head]) && !isspace(lil->code[lil->head]) && !lil->error); |
| | 908 | skip_spaces(lil); |
| | 909 | |
| | 910 | lil_list_append(words, w); |
| | 911 | } |
| | 912 | |
| | 913 | return words; |
| | 914 | } |
| | 915 | |
| | 916 | lil_list_t lil_subst_to_list(lil_t lil, lil_value_t code) |
| | 917 | { |
| | 918 | const char* save_code = lil->code; |
| | 919 | size_t save_clen = lil->clen; |
| | 920 | size_t save_head = lil->head; |
| | 921 | int save_igeol = lil->ignoreeol; |
| | 922 | lil_list_t words; |
| | 923 | lil->code = lil_to_string(code); |
| | 924 | lil->clen = code->l; |
| | 925 | lil->head = 0; |
| | 926 | lil->ignoreeol = 1; |
| | 927 | words = substitute(lil); |
| | 928 | if (!words) words = lil_alloc_list(); |
| | 929 | lil->code = save_code; |
| | 930 | lil->clen = save_clen; |
| | 931 | lil->head = save_head; |
| | 932 | lil->ignoreeol = save_igeol; |
| | 933 | return words; |
| | 934 | } |
| | 935 | |
| | 936 | lil_value_t lil_subst_to_value(lil_t lil, lil_value_t code) |
| | 937 | { |
| | 938 | lil_list_t words = lil_subst_to_list(lil, code); |
| | 939 | lil_value_t val; |
| | 940 | val = lil_list_to_value(words, 0); |
| | 941 | lil_free_list(words); |
| | 942 | return val; |
| | 943 | } |
| | 944 | |
| | 945 | lil_value_t lil_parse(lil_t lil, const char* code, size_t codelen, int funclevel) |
| | 946 | { |
| | 947 | const char* save_code = lil->code; |
| | 948 | size_t save_clen = lil->clen; |
| | 949 | size_t save_head = lil->head; |
| | 950 | lil_value_t val = NULL; |
| | 951 | lil_list_t words = NULL; |
| | 952 | if (!save_code) lil->rootcode = code; |
| | 953 | lil->code = code; |
| | 954 | lil->clen = codelen ? codelen : strlen(code); |
| | 955 | lil->head = 0; |
| | 956 | skip_spaces(lil); |
| | 957 | lil->parse_depth++; |
| | 958 | #ifdef LIL_ENABLE_RECLIMIT |
| | 959 | if (lil->parse_depth > LIL_ENABLE_RECLIMIT) { |
| | 960 | lil_set_error(lil, "Too many recursive calls"); |
| | 961 | goto cleanup; |
| | 962 | } |
| | 963 | #endif |
| | 964 | if (lil->parse_depth == 1) lil->error = 0; |
| | 965 | if (funclevel) lil->env->breakrun = 0; |
| | 966 | while (lil->head < lil->clen && !lil->error) { |
| | 967 | if (words) lil_free_list(words); |
| | 968 | if (val) lil_free_value(val); |
| | 969 | val = NULL; |
| | 970 | |
| | 971 | words = substitute(lil); |
| | 972 | if (!words || lil->error) goto cleanup; |
| | 973 | |
| | 974 | if (words->c) { |
| | 975 | lil_func_t cmd = find_cmd(lil, lil_to_string(words->v[0])); |
| | 976 | if (!cmd) { |
| | 977 | if (words->v[0]->l) { |
| | 978 | if (lil->catcher) { |
| | 979 | if (lil->in_catcher < MAX_CATCHER_DEPTH) { |
| | 980 | lil_value_t args; |
| | 981 | lil->in_catcher++; |
| | 982 | lil_push_env(lil); |
| | 983 | lil->env->catcher_for = words->v[0]; |
| | 984 | args = lil_list_to_value(words, 1); |
| | 985 | lil_set_var(lil, "args", args, LIL_SETVAR_LOCAL_NEW); |
| | 986 | lil_free_value(args); |
| | 987 | val = lil_parse(lil, lil->catcher, 0, 1); |
| | 988 | lil_pop_env(lil); |
| | 989 | lil->in_catcher--; |
| | 990 | } else { |
| | 991 | char* msg = malloc(words->v[0]->l + 64); |
| | 992 | sprintf(msg, "catcher limit reached while trying to call unknown function %s", words->v[0]->d); |
| | 993 | lil_set_error_at(lil, lil->head, msg); |
| | 994 | free(msg); |
| | 995 | goto cleanup; |
| | 996 | } |
| | 997 | } else { |
| | 998 | char* msg = malloc(words->v[0]->l + 32); |
| | 999 | sprintf(msg, "unknown function %s", words->v[0]->d); |
| | 1000 | lil_set_error_at(lil, lil->head, msg); |
| | 1001 | free(msg); |
| | 1002 | goto cleanup; |
| | 1003 | } |
| | 1004 | } |
| | 1005 | } |
| | 1006 | if (cmd) { |
| | 1007 | if (cmd->proc) { |
| | 1008 | size_t shead = lil->head; |
| | 1009 | val = cmd->proc(lil, words->c - 1, words->v + 1); |
| | 1010 | if (lil->error == ERROR_FIXHEAD) { |
| | 1011 | lil->error = ERROR_DEFAULT; |
| | 1012 | lil->err_head = shead; |
| | 1013 | } |
| | 1014 | } else { |
| | 1015 | lil_push_env(lil); |
| | 1016 | lil->env->func = cmd; |
| | 1017 | if (cmd->argnames->c == 1 && !strcmp(lil_to_string(cmd->argnames->v[0]), "args")) { |
| | 1018 | lil_value_t args = lil_list_to_value(words, 1); |
| | 1019 | lil_set_var(lil, "args", args, LIL_SETVAR_LOCAL_NEW); |
| | 1020 | lil_free_value(args); |
| | 1021 | } else { |
| | 1022 | size_t i; |
| | 1023 | for (i=0; i<cmd->argnames->c; i++) { |
| | 1024 | lil_set_var(lil, lil_to_string(cmd->argnames->v[i]), i < words->c - 1 ? words->v[i + 1] : lil->empty, LIL_SETVAR_LOCAL_NEW); |
| | 1025 | } |
| | 1026 | } |
| | 1027 | val = lil_parse_value(lil, cmd->code, 1); |
| | 1028 | lil_pop_env(lil); |
| | 1029 | } |
| | 1030 | } |
| | 1031 | } |
| | 1032 | |
| | 1033 | if (lil->env->breakrun) goto cleanup; |
| | 1034 | |
| | 1035 | skip_spaces(lil); |
| | 1036 | while (ateol(lil)) lil->head++; |
| | 1037 | skip_spaces(lil); |
| | 1038 | } |
| | 1039 | cleanup: |
| | 1040 | if (lil->error && lil->callback[LIL_CALLBACK_ERROR] && lil->parse_depth == 1) { |
| | 1041 | lil_error_callback_proc_t proc = (lil_error_callback_proc_t)lil->callback[LIL_CALLBACK_ERROR]; |
| | 1042 | proc(lil, lil->err_head, lil->err_msg); |
| | 1043 | } |
| | 1044 | if (words) lil_free_list(words); |
| | 1045 | lil->code = save_code; |
| | 1046 | lil->clen = save_clen; |
| | 1047 | lil->head = save_head; |
| | 1048 | if (funclevel && lil->env->retval_set) { |
| | 1049 | if (val) lil_free_value(val); |
| | 1050 | val = lil->env->retval; |
| | 1051 | lil->env->retval = NULL; |
| | 1052 | lil->env->retval_set = 0; |
| | 1053 | lil->env->breakrun = 0; |
| | 1054 | } |
| | 1055 | lil->parse_depth--; |
| | 1056 | return val ? val : alloc_value(NULL); |
| | 1057 | } |
| | 1058 | |
| | 1059 | lil_value_t lil_parse_value(lil_t lil, lil_value_t val, int funclevel) |
| | 1060 | { |
| | 1061 | if (!val || !val->d || !val->l) return alloc_value(NULL); |
| | 1062 | return lil_parse(lil, val->d, val->l, funclevel); |
| | 1063 | } |
| | 1064 | |
| | 1065 | LILAPI lil_value_t lil_call(lil_t lil, const char* funcname, size_t argc, lil_value_t* argv) |
| | 1066 | { |
| | 1067 | lil_func_t cmd = find_cmd(lil, funcname); |
| | 1068 | lil_value_t r = NULL; |
| | 1069 | if (cmd) { |
| | 1070 | if (cmd->proc) |
| | 1071 | r = cmd->proc(lil, argc, argv); |
| | 1072 | else { |
| | 1073 | size_t i; |
| | 1074 | lil_push_env(lil); |
| | 1075 | lil->env->func = cmd; |
| | 1076 | if (cmd->argnames->c == 1 && !strcmp(lil_to_string(cmd->argnames->v[0]), "args")) { |
| | 1077 | lil_list_t args = lil_alloc_list(); |
| | 1078 | lil_value_t argsval; |
| | 1079 | for (i=0; i<argc; i++) |
| | 1080 | lil_list_append(args, lil_clone_value(argv[i])); |
| | 1081 | argsval = lil_list_to_value(args, 0); |
| | 1082 | lil_set_var(lil, "args", argsval, LIL_SETVAR_LOCAL_NEW); |
| | 1083 | lil_free_value(argsval); |
| | 1084 | lil_free_list(args); |
| | 1085 | } else { |
| | 1086 | for (i=0; i<cmd->argnames->c; i++) |
| | 1087 | lil_set_var(lil, lil_to_string(cmd->argnames->v[i]), i < argc ? argv[i] : NULL, LIL_SETVAR_LOCAL_NEW); |
| | 1088 | } |
| | 1089 | r = lil_parse_value(lil, cmd->code, 1); |
| | 1090 | lil_pop_env(lil); |
| | 1091 | } |
| | 1092 | } |
| | 1093 | return r; |
| | 1094 | } |
| | 1095 | |
| | 1096 | void lil_callback(lil_t lil, int cb, lil_callback_proc_t proc) |
| | 1097 | { |
| | 1098 | if (cb < 0 || cb > CALLBACKS) return; |
| | 1099 | lil->callback[cb] = proc; |
| | 1100 | } |
| | 1101 | |
| | 1102 | void lil_set_error(lil_t lil, const char* msg) |
| | 1103 | { |
| | 1104 | if (lil->error) return; |
| | 1105 | free(lil->err_msg); |
| | 1106 | lil->error = ERROR_FIXHEAD; |
| | 1107 | lil->err_head = 0; |
| | 1108 | lil->err_msg = strclone(msg ? msg : ""); |
| | 1109 | } |
| | 1110 | |
| | 1111 | void lil_set_error_at(lil_t lil, size_t pos, const char* msg) |
| | 1112 | { |
| | 1113 | if (lil->error) return; |
| | 1114 | free(lil->err_msg); |
| | 1115 | lil->error = ERROR_DEFAULT; |
| | 1116 | lil->err_head = pos; |
| | 1117 | lil->err_msg = strclone(msg ? msg : ""); |
| | 1118 | } |
| | 1119 | |
| | 1120 | int lil_error(lil_t lil, const char** msg, size_t* pos) |
| | 1121 | { |
| | 1122 | if (!lil->error) return 0; |
| | 1123 | *msg = lil->err_msg; |
| | 1124 | *pos = lil->err_head; |
| | 1125 | lil->error = ERROR_NOERROR; |
| | 1126 | return 1; |
| | 1127 | } |
| | 1128 | |
| | 1129 | #define EE_INT 0 |
| | 1130 | #define EE_FLOAT 1 |
| | 1131 | #define EERR_NO_ERROR 0 |
| | 1132 | #define EERR_SYNTAX_ERROR 1 |
| | 1133 | #define EERR_INVALID_TYPE 2 |
| | 1134 | #define EERR_DIVISION_BY_ZERO 3 |
| | 1135 | #define EERR_INVALID_EXPRESSION 4 |
| | 1136 | |
| | 1137 | static void ee_expr(expreval_t* ee); |
| | 1138 | |
| | 1139 | static int ee_invalidpunct(int ch) |
| | 1140 | { |
| | 1141 | return ispunct(ch) && ch != '!' && ch != '~' && ch != '(' && ch != ')' && ch != '-' && ch != '+'; |
| | 1142 | } |
| | 1143 | |
| | 1144 | static void ee_skip_spaces(expreval_t* ee) |
| | 1145 | { |
| | 1146 | while (ee->head < ee->len && isspace(ee->code[ee->head])) ee->head++; |
| | 1147 | } |
| | 1148 | |
| | 1149 | static void ee_numeric_element(expreval_t* ee) |
| | 1150 | { |
| | 1151 | lilint_t fpart = 0, fpartlen = 1; |
| | 1152 | ee->type = EE_INT; |
| | 1153 | ee_skip_spaces(ee); |
| | 1154 | ee->ival = 0; |
| | 1155 | ee->dval = 0; |
| | 1156 | while (ee->head < ee->len) { |
| | 1157 | if (ee->code[ee->head] == '.') { |
| | 1158 | if (ee->type == EE_FLOAT) break; |
| | 1159 | ee->type = EE_FLOAT; |
| | 1160 | ee->head++; |
| | 1161 | } else if (!isdigit(ee->code[ee->head])) break; |
| | 1162 | if (ee->type == EE_INT) |
| | 1163 | ee->ival = ee->ival*10 + (ee->code[ee->head] - '0'); |
| | 1164 | else { |
| | 1165 | fpart = fpart*10 + (ee->code[ee->head] - '0'); |
| | 1166 | fpartlen *= 10; |
| | 1167 | } |
| | 1168 | ee->head++; |
| | 1169 | } |
| | 1170 | if (ee->type == EE_FLOAT) |
| | 1171 | ee->dval = ee->ival + (double)fpart/(double)fpartlen; |
| | 1172 | } |
| | 1173 | |
| | 1174 | static void ee_element(expreval_t* ee) |
| | 1175 | { |
| | 1176 | if (isdigit(ee->code[ee->head])) { |
| | 1177 | ee_numeric_element(ee); |
| | 1178 | return; |
| | 1179 | } |
| | 1180 | |
| | 1181 | /* for anything else that might creep in (usually from strings), we set the |
| | 1182 | * value to 1 so that strings evaluate as "true" when used in conditional |
| | 1183 | * expressions */ |
| | 1184 | ee->type = EE_INT; |
| | 1185 | ee->ival = 1; |
| | 1186 | ee->error = EERR_INVALID_EXPRESSION; /* special flag, will be cleared */ |
| | 1187 | } |
| | 1188 | |
| | 1189 | static void ee_paren(expreval_t* ee) |
| | 1190 | { |
| | 1191 | ee_skip_spaces(ee); |
| | 1192 | if (ee->code[ee->head] == '(') { |
| | 1193 | ee->head++; |
| | 1194 | ee_expr(ee); |
| | 1195 | ee_skip_spaces(ee); |
| | 1196 | if (ee->code[ee->head] == ')') ee->head++; |
| | 1197 | else ee->error = EERR_SYNTAX_ERROR; |
| | 1198 | } else ee_element(ee); |
| | 1199 | } |
| | 1200 | |
| | 1201 | static void ee_unary(expreval_t* ee) |
| | 1202 | { |
| | 1203 | ee_skip_spaces(ee); |
| | 1204 | if (ee->head < ee->len && !ee->error && |
| | 1205 | (ee->code[ee->head] == '-' || |
| | 1206 | ee->code[ee->head] == '+' || |
| | 1207 | ee->code[ee->head] == '~' || |
| | 1208 | ee->code[ee->head] == '!')) { |
| | 1209 | char op = ee->code[ee->head++]; |
| | 1210 | ee_unary(ee); |
| | 1211 | if (ee->error) return; |
| | 1212 | switch (op) { |
| | 1213 | case '-': |
| | 1214 | switch (ee->type) { |
| | 1215 | case EE_FLOAT: |
| | 1216 | ee->dval = -ee->dval; |
| | 1217 | break; |
| | 1218 | case EE_INT: |
| | 1219 | ee->ival = -ee->ival; |
| | 1220 | break; |
| | 1221 | default: |
| | 1222 | ee->error = EERR_INVALID_TYPE; |
| | 1223 | break; |
| | 1224 | } |
| | 1225 | break; |
| | 1226 | case '+': |
| | 1227 | /* ignore it, doesn't change a thing */ |
| | 1228 | break; |
| | 1229 | case '~': |
| | 1230 | switch (ee->type) { |
| | 1231 | case EE_FLOAT: |
| | 1232 | ee->ival = ~((lilint_t)ee->dval); |
| | 1233 | ee->type = EE_INT; |
| | 1234 | break; |
| | 1235 | case EE_INT: |
| | 1236 | ee->ival = ~ee->ival; |
| | 1237 | break; |
| | 1238 | default: |
| | 1239 | ee->error = EERR_INVALID_TYPE; |
| | 1240 | break; |
| | 1241 | } |
| | 1242 | break; |
| | 1243 | case '!': |
| | 1244 | switch (ee->type) { |
| | 1245 | case EE_FLOAT: |
| | 1246 | ee->dval = !ee->dval; |
| | 1247 | break; |
| | 1248 | case EE_INT: |
| | 1249 | ee->ival = !ee->ival; |
| | 1250 | break; |
| | 1251 | default: |
| | 1252 | ee->error = EERR_INVALID_TYPE; |
| | 1253 | break; |
| | 1254 | } |
| | 1255 | break; |
| | 1256 | } |
| | 1257 | } else { |
| | 1258 | ee_paren(ee); |
| | 1259 | } |
| | 1260 | } |
| | 1261 | |
| | 1262 | static void ee_muldiv(expreval_t* ee) |
| | 1263 | { |
| | 1264 | ee_unary(ee); |
| | 1265 | if (ee->error) return; |
| | 1266 | ee_skip_spaces(ee); |
| | 1267 | while (ee->head < ee->len && !ee->error && !ee_invalidpunct(ee->code[ee->head + 1]) && |
| | 1268 | (ee->code[ee->head] == '*' || |
| | 1269 | ee->code[ee->head] == '/' || |
| | 1270 | ee->code[ee->head] == '\\' || |
| | 1271 | ee->code[ee->head] == '%')) { |
| | 1272 | double odval = ee->dval; |
| | 1273 | lilint_t oival = ee->ival; |
| | 1274 | |
| | 1275 | switch (ee->code[ee->head]) { |
| | 1276 | case '*': |
| | 1277 | switch (ee->type) { |
| | 1278 | case EE_FLOAT: |
| | 1279 | ee->head++; |
| | 1280 | ee_unary(ee); |
| | 1281 | if (ee->error) return; |
| | 1282 | switch (ee->type) { |
| | 1283 | case EE_FLOAT: |
| | 1284 | ee->dval = ee->dval*odval; |
| | 1285 | break; |
| | 1286 | case EE_INT: |
| | 1287 | ee->dval = ee->ival*odval; |
| | 1288 | ee->type = EE_FLOAT; |
| | 1289 | break; |
| | 1290 | default: |
| | 1291 | ee->error = EERR_INVALID_TYPE; |
| | 1292 | break; |
| | 1293 | } |
| | 1294 | break; |
| | 1295 | case EE_INT: |
| | 1296 | ee->head++; |
| | 1297 | ee_unary(ee); |
| | 1298 | if (ee->error) return; |
| | 1299 | switch (ee->type) { |
| | 1300 | case EE_FLOAT: |
| | 1301 | ee->dval = ee->dval*oival; |
| | 1302 | ee->type = EE_FLOAT; |
| | 1303 | break; |
| | 1304 | case EE_INT: |
| | 1305 | ee->ival = ee->ival*oival; |
| | 1306 | break; |
| | 1307 | default: |
| | 1308 | ee->error = EERR_INVALID_TYPE; |
| | 1309 | break; |
| | 1310 | } |
| | 1311 | break; |
| | 1312 | default: |
| | 1313 | ee->error = EERR_INVALID_TYPE; |
| | 1314 | break; |
| | 1315 | } |
| | 1316 | break; |
| | 1317 | case '%': |
| | 1318 | switch (ee->type) { |
| | 1319 | case EE_FLOAT: |
| | 1320 | ee->head++; |
| | 1321 | ee_unary(ee); |
| | 1322 | if (ee->error) return; |
| | 1323 | switch (ee->type) { |
| | 1324 | case EE_FLOAT: |
| | 1325 | if (ee->dval == 0.0) { |
| | 1326 | ee->error = EERR_DIVISION_BY_ZERO; |
| | 1327 | } else { |
| | 1328 | ee->dval = fmod(odval, ee->dval); |
| | 1329 | } |
| | 1330 | break; |
| | 1331 | case EE_INT: |
| | 1332 | if (ee->ival == 0) { |
| | 1333 | ee->error = EERR_DIVISION_BY_ZERO; |
| | 1334 | } else { |
| | 1335 | ee->dval = fmod(odval, ee->ival); |
| | 1336 | } |
| | 1337 | ee->type = EE_FLOAT; |
| | 1338 | break; |
| | 1339 | default: |
| | 1340 | ee->error = EERR_INVALID_TYPE; |
| | 1341 | break; |
| | 1342 | } |
| | 1343 | break; |
| | 1344 | case EE_INT: |
| | 1345 | ee->head++; |
| | 1346 | ee_unary(ee); |
| | 1347 | if (ee->error) return; |
| | 1348 | switch (ee->type) { |
| | 1349 | case EE_FLOAT: |
| | 1350 | if (ee->dval == 0.0) { |
| | 1351 | ee->error = EERR_DIVISION_BY_ZERO; |
| | 1352 | } else { |
| | 1353 | ee->dval = fmod(oival, ee->dval); |
| | 1354 | } |
| | 1355 | break; |
| | 1356 | case EE_INT: |
| | 1357 | if (ee->ival == 0) { |
| | 1358 | ee->error = EERR_DIVISION_BY_ZERO; |
| | 1359 | } else { |
| | 1360 | ee->ival = oival%ee->ival; |
| | 1361 | } |
| | 1362 | break; |
| | 1363 | default: |
| | 1364 | ee->error = EERR_INVALID_TYPE; |
| | 1365 | break; |
| | 1366 | } |
| | 1367 | break; |
| | 1368 | } |
| | 1369 | break; |
| | 1370 | case '/': |
| | 1371 | switch (ee->type) { |
| | 1372 | case EE_FLOAT: |
| | 1373 | ee->head++; |
| | 1374 | ee_unary(ee); |
| | 1375 | if (ee->error) return; |
| | 1376 | switch (ee->type) { |
| | 1377 | case EE_FLOAT: |
| | 1378 | if (ee->dval == 0.0) { |
| | 1379 | ee->error = EERR_DIVISION_BY_ZERO; |
| | 1380 | } else { |
| | 1381 | ee->dval = odval/ee->dval; |
| | 1382 | } |
| | 1383 | break; |
| | 1384 | case EE_INT: |
| | 1385 | if (ee->ival == 0) { |
| | 1386 | ee->error = EERR_DIVISION_BY_ZERO; |
| | 1387 | } else { |
| | 1388 | ee->dval = odval/(double)ee->ival; |
| | 1389 | } |
| | 1390 | ee->type = EE_FLOAT; |
| | 1391 | break; |
| | 1392 | default: |
| | 1393 | ee->error = EERR_INVALID_TYPE; |
| | 1394 | break; |
| | 1395 | } |
| | 1396 | break; |
| | 1397 | case EE_INT: |
| | 1398 | ee->head++; |
| | 1399 | ee_unary(ee); |
| | 1400 | if (ee->error) return; |
| | 1401 | switch (ee->type) { |
| | 1402 | case EE_FLOAT: |
| | 1403 | if (ee->dval == 0.0) { |
| | 1404 | ee->error = EERR_DIVISION_BY_ZERO; |
| | 1405 | } else { |
| | 1406 | ee->dval = (double)oival/ee->dval; |
| | 1407 | } |
| | 1408 | break; |
| | 1409 | case EE_INT: |
| | 1410 | if (ee->ival == 0) { |
| | 1411 | ee->error = EERR_DIVISION_BY_ZERO; |
| | 1412 | } else { |
| | 1413 | ee->dval = (double)oival/(double)ee->ival; |
| | 1414 | } |
| | 1415 | ee->type = EE_FLOAT; |
| | 1416 | break; |
| | 1417 | default: |
| | 1418 | ee->error = EERR_INVALID_TYPE; |
| | 1419 | break; |
| | 1420 | } |
| | 1421 | break; |
| | 1422 | } |
| | 1423 | break; |
| | 1424 | case '\\': |
| | 1425 | switch (ee->type) { |
| | 1426 | case EE_FLOAT: |
| | 1427 | ee->head++; |
| | 1428 | ee_unary(ee); |
| | 1429 | if (ee->error) return; |
| | 1430 | switch (ee->type) { |
| | 1431 | case EE_FLOAT: |
| | 1432 | if (ee->dval == 0.0) { |
| | 1433 | ee->error = EERR_DIVISION_BY_ZERO; |
| | 1434 | } else { |
| | 1435 | ee->ival = (lilint_t)(odval/ee->dval); |
| | 1436 | } |
| | 1437 | ee->type = EE_INT; |
| | 1438 | break; |
| | 1439 | case EE_INT: |
| | 1440 | if (ee->ival == 0) { |
| | 1441 | ee->error = EERR_DIVISION_BY_ZERO; |
| | 1442 | } else { |
| | 1443 | ee->ival = (lilint_t)(odval/(double)ee->ival); |
| | 1444 | } |
| | 1445 | break; |
| | 1446 | default: |
| | 1447 | ee->error = EERR_INVALID_TYPE; |
| | 1448 | break; |
| | 1449 | } |
| | 1450 | break; |
| | 1451 | case EE_INT: |
| | 1452 | ee->head++; |
| | 1453 | ee_unary(ee); |
| | 1454 | if (ee->error) return; |
| | 1455 | switch (ee->type) { |
| | 1456 | case EE_FLOAT: |
| | 1457 | if (ee->dval == 0.0) { |
| | 1458 | ee->error = EERR_DIVISION_BY_ZERO; |
| | 1459 | } else { |
| | 1460 | ee->ival = (lilint_t)((double)oival/ee->dval); |
| | 1461 | } |
| | 1462 | ee->type = EE_INT; |
| | 1463 | break; |
| | 1464 | case EE_INT: |
| | 1465 | if (ee->ival == 0) { |
| | 1466 | ee->error = EERR_DIVISION_BY_ZERO; |
| | 1467 | } else { |
| | 1468 | ee->ival = oival/ee->ival; |
| | 1469 | } |
| | 1470 | break; |
| | 1471 | default: |
| | 1472 | ee->error = EERR_INVALID_TYPE; |
| | 1473 | break; |
| | 1474 | } |
| | 1475 | break; |
| | 1476 | default: |
| | 1477 | ee->error = EERR_INVALID_TYPE; |
| | 1478 | break; |
| | 1479 | } |
| | 1480 | break; |
| | 1481 | } |
| | 1482 | |
| | 1483 | ee_skip_spaces(ee); |
| | 1484 | } |
| | 1485 | } |
| | 1486 | |
| | 1487 | static void ee_addsub(expreval_t* ee) |
| | 1488 | { |
| | 1489 | ee_muldiv(ee); |
| | 1490 | ee_skip_spaces(ee); |
| | 1491 | while (ee->head < ee->len && !ee->error && !ee_invalidpunct(ee->code[ee->head + 1]) && |
| | 1492 | (ee->code[ee->head] == '+' || |
| | 1493 | ee->code[ee->head] == '-')) { |
| | 1494 | double odval = ee->dval; |
| | 1495 | lilint_t oival = ee->ival; |
| | 1496 | |
| | 1497 | switch (ee->code[ee->head]) { |
| | 1498 | case '+': |
| | 1499 | switch (ee->type) { |
| | 1500 | case EE_FLOAT: |
| | 1501 | ee->head++; |
| | 1502 | ee_muldiv(ee); |
| | 1503 | if (ee->error) return; |
| | 1504 | switch (ee->type) { |
| | 1505 | case EE_FLOAT: |
| | 1506 | ee->dval = ee->dval+odval; |
| | 1507 | break; |
| | 1508 | case EE_INT: |
| | 1509 | ee->dval = ee->ival+odval; |
| | 1510 | ee->type = EE_FLOAT; |
| | 1511 | break; |
| | 1512 | default: |
| | 1513 | ee->error = EERR_INVALID_TYPE; |
| | 1514 | break; |
| | 1515 | } |
| | 1516 | break; |
| | 1517 | case EE_INT: |
| | 1518 | ee->head++; |
| | 1519 | ee_muldiv(ee); |
| | 1520 | if (ee->error) return; |
| | 1521 | switch (ee->type) { |
| | 1522 | case EE_FLOAT: |
| | 1523 | ee->dval = ee->dval+oival; |
| | 1524 | ee->type = EE_FLOAT; |
| | 1525 | break; |
| | 1526 | case EE_INT: |
| | 1527 | ee->ival = ee->ival+oival; |
| | 1528 | break; |
| | 1529 | default: |
| | 1530 | ee->error = EERR_INVALID_TYPE; |
| | 1531 | break; |
| | 1532 | } |
| | 1533 | break; |
| | 1534 | default: |
| | 1535 | ee->error = EERR_INVALID_TYPE; |
| | 1536 | break; |
| | 1537 | } |
| | 1538 | break; |
| | 1539 | case '-': |
| | 1540 | switch (ee->type) { |
| | 1541 | case EE_FLOAT: |
| | 1542 | ee->head++; |
| | 1543 | ee_muldiv(ee); |
| | 1544 | if (ee->error) return; |
| | 1545 | switch (ee->type) { |
| | 1546 | case EE_FLOAT: |
| | 1547 | ee->dval = odval-ee->dval; |
| | 1548 | break; |
| | 1549 | case EE_INT: |
| | 1550 | ee->dval = odval-ee->ival; |
| | 1551 | ee->type = EE_FLOAT; |
| | 1552 | break; |
| | 1553 | default: |
| | 1554 | ee->error = EERR_INVALID_TYPE; |
| | 1555 | break; |
| | 1556 | } |
| | 1557 | break; |
| | 1558 | case EE_INT: |
| | 1559 | ee->head++; |
| | 1560 | ee_muldiv(ee); |
| | 1561 | if (ee->error) return; |
| | 1562 | switch (ee->type) { |
| | 1563 | case EE_FLOAT: |
| | 1564 | ee->dval = (double)oival-ee->dval; |
| | 1565 | ee->type = EE_FLOAT; |
| | 1566 | break; |
| | 1567 | case EE_INT: |
| | 1568 | ee->ival = oival-ee->ival; |
| | 1569 | break; |
| | 1570 | default: |
| | 1571 | ee->error = EERR_INVALID_TYPE; |
| | 1572 | break; |
| | 1573 | } |
| | 1574 | break; |
| | 1575 | default: |
| | 1576 | ee->error = EERR_INVALID_TYPE; |
| | 1577 | break; |
| | 1578 | } |
| | 1579 | break; |
| | 1580 | } |
| | 1581 | |
| | 1582 | ee_skip_spaces(ee); |
| | 1583 | } |
| | 1584 | } |
| | 1585 | |
| | 1586 | static void ee_shift(expreval_t* ee) |
| | 1587 | { |
| | 1588 | ee_addsub(ee); |
| | 1589 | ee_skip_spaces(ee); |
| | 1590 | while (ee->head < ee->len && !ee->error && |
| | 1591 | ((ee->code[ee->head] == '<' && ee->code[ee->head + 1] == '<') || |
| | 1592 | (ee->code[ee->head] == '>' && ee->code[ee->head + 1] == '>'))) { |
| | 1593 | double odval = ee->dval; |
| | 1594 | lilint_t oival = ee->ival; |
| | 1595 | ee->head++; |
| | 1596 | |
| | 1597 | switch (ee->code[ee->head]) { |
| | 1598 | case '<': |
| | 1599 | switch (ee->type) { |
| | 1600 | case EE_FLOAT: |
| | 1601 | ee->head++; |
| | 1602 | ee_addsub(ee); |
| | 1603 | if (ee->error) return; |
| | 1604 | switch (ee->type) { |
| | 1605 | case EE_FLOAT: |
| | 1606 | ee->ival = (lilint_t)odval << (lilint_t)ee->dval; |
| | 1607 | ee->type = EE_INT; |
| | 1608 | break; |
| | 1609 | case EE_INT: |
| | 1610 | ee->ival = (lilint_t)odval << ee->ival; |
| | 1611 | break; |
| | 1612 | default: |
| | 1613 | ee->error = EERR_INVALID_TYPE; |
| | 1614 | break; |
| | 1615 | } |
| | 1616 | break; |
| | 1617 | case EE_INT: |
| | 1618 | ee->head++; |
| | 1619 | ee_addsub(ee); |
| | 1620 | if (ee->error) return; |
| | 1621 | switch (ee->type) { |
| | 1622 | case EE_FLOAT: |
| | 1623 | ee->ival = oival << (lilint_t)ee->dval; |
| | 1624 | ee->type = EE_INT; |
| | 1625 | break; |
| | 1626 | case EE_INT: |
| | 1627 | ee->ival = oival << ee->ival; |
| | 1628 | break; |
| | 1629 | default: |
| | 1630 | ee->error = EERR_INVALID_TYPE; |
| | 1631 | break; |
| | 1632 | } |
| | 1633 | break; |
| | 1634 | default: |
| | 1635 | ee->error = EERR_INVALID_TYPE; |
| | 1636 | break; |
| | 1637 | } |
| | 1638 | break; |
| | 1639 | case '>': |
| | 1640 | switch (ee->type) { |
| | 1641 | case EE_FLOAT: |
| | 1642 | ee->head++; |
| | 1643 | ee_addsub(ee); |
| | 1644 | if (ee->error) return; |
| | 1645 | switch (ee->type) { |
| | 1646 | case EE_FLOAT: |
| | 1647 | ee->ival = (lilint_t)odval >> (lilint_t)ee->dval; |
| | 1648 | ee->type = EE_INT; |
| | 1649 | break; |
| | 1650 | case EE_INT: |
| | 1651 | ee->ival = (lilint_t)odval >> ee->ival; |
| | 1652 | break; |
| | 1653 | default: |
| | 1654 | ee->error = EERR_INVALID_TYPE; |
| | 1655 | break; |
| | 1656 | } |
| | 1657 | break; |
| | 1658 | case EE_INT: |
| | 1659 | ee->head++; |
| | 1660 | ee_addsub(ee); |
| | 1661 | if (ee->error) return; |
| | 1662 | switch (ee->type) { |
| | 1663 | case EE_FLOAT: |
| | 1664 | ee->ival = oival >> (lilint_t)ee->dval; |
| | 1665 | ee->type = EE_INT; |
| | 1666 | break; |
| | 1667 | case EE_INT: |
| | 1668 | ee->ival = oival >> ee->ival; |
| | 1669 | break; |
| | 1670 | default: |
| | 1671 | ee->error = EERR_INVALID_TYPE; |
| | 1672 | break; |
| | 1673 | } |
| | 1674 | break; |
| | 1675 | default: |
| | 1676 | ee->error = EERR_INVALID_TYPE; |
| | 1677 | break; |
| | 1678 | } |
| | 1679 | break; |
| | 1680 | } |
| | 1681 | |
| | 1682 | ee_skip_spaces(ee); |
| | 1683 | } |
| | 1684 | } |
| | 1685 | |
| | 1686 | static void ee_compare(expreval_t* ee) |
| | 1687 | { |
| | 1688 | ee_shift(ee); |
| | 1689 | ee_skip_spaces(ee); |
| | 1690 | while (ee->head < ee->len && !ee->error && |
| | 1691 | ((ee->code[ee->head] == '<' && !ee_invalidpunct(ee->code[ee->head + 1])) || |
| | 1692 | (ee->code[ee->head] == '>' && !ee_invalidpunct(ee->code[ee->head + 1])) || |
| | 1693 | (ee->code[ee->head] == '<' && ee->code[ee->head + 1] == '=') || |
| | 1694 | (ee->code[ee->head] == '>' && ee->code[ee->head + 1] == '='))) { |
| | 1695 | double odval = ee->dval; |
| | 1696 | lilint_t oival = ee->ival; |
| | 1697 | int op = 4; |
| | 1698 | if (ee->code[ee->head] == '<' && !ee_invalidpunct(ee->code[ee->head + 1])) op = 1; |
| | 1699 | else if (ee->code[ee->head] == '>' && !ee_invalidpunct(ee->code[ee->head + 1])) op = 2; |
| | 1700 | else if (ee->code[ee->head] == '<' && ee->code[ee->head + 1] == '=') op = 3; |
| | 1701 | |
| | 1702 | ee->head += op > 2 ? 2 : 1; |
| | 1703 | |
| | 1704 | switch (op) { |
| | 1705 | case 1: |
| | 1706 | switch (ee->type) { |
| | 1707 | case EE_FLOAT: |
| | 1708 | ee_shift(ee); |
| | 1709 | if (ee->error) return; |
| | 1710 | switch (ee->type) { |
| | 1711 | case EE_FLOAT: |
| | 1712 | ee->ival = (odval < ee->dval)?1:0; |
| | 1713 | ee->type = EE_INT; |
| | 1714 | break; |
| | 1715 | case EE_INT: |
| | 1716 | ee->ival = (odval < ee->ival)?1:0; |
| | 1717 | break; |
| | 1718 | default: |
| | 1719 | ee->error = EERR_INVALID_TYPE; |
| | 1720 | break; |
| | 1721 | } |
| | 1722 | break; |
| | 1723 | case EE_INT: |
| | 1724 | ee_shift(ee); |
| | 1725 | if (ee->error) return; |
| | 1726 | switch (ee->type) { |
| | 1727 | case EE_FLOAT: |
| | 1728 | ee->ival = (oival < ee->dval)?1:0; |
| | 1729 | ee->type = EE_INT; |
| | 1730 | break; |
| | 1731 | case EE_INT: |
| | 1732 | ee->ival = (oival < ee->ival)?1:0; |
| | 1733 | break; |
| | 1734 | default: |
| | 1735 | ee->error = EERR_INVALID_TYPE; |
| | 1736 | break; |
| | 1737 | } |
| | 1738 | break; |
| | 1739 | default: |
| | 1740 | ee->error = EERR_INVALID_TYPE; |
| | 1741 | break; |
| | 1742 | } |
| | 1743 | break; |
| | 1744 | case 2: |
| | 1745 | switch (ee->type) { |
| | 1746 | case EE_FLOAT: |
| | 1747 | ee_shift(ee); |
| | 1748 | if (ee->error) return; |
| | 1749 | switch (ee->type) { |
| | 1750 | case EE_FLOAT: |
| | 1751 | ee->ival = (odval > ee->dval)?1:0; |
| | 1752 | ee->type = EE_INT; |
| | 1753 | break; |
| | 1754 | case EE_INT: |
| | 1755 | ee->ival = (odval > ee->ival)?1:0; |
| | 1756 | break; |
| | 1757 | default: |
| | 1758 | ee->error = EERR_INVALID_TYPE; |
| | 1759 | break; |
| | 1760 | } |
| | 1761 | break; |
| | 1762 | case EE_INT: |
| | 1763 | ee_shift(ee); |
| | 1764 | if (ee->error) return; |
| | 1765 | switch (ee->type) { |
| | 1766 | case EE_FLOAT: |
| | 1767 | ee->ival = (oival > ee->dval)?1:0; |
| | 1768 | ee->type = EE_INT; |
| | 1769 | break; |
| | 1770 | case EE_INT: |
| | 1771 | ee->ival = (oival > ee->ival)?1:0; |
| | 1772 | break; |
| | 1773 | default: |
| | 1774 | ee->error = EERR_INVALID_TYPE; |
| | 1775 | break; |
| | 1776 | } |
| | 1777 | break; |
| | 1778 | default: |
| | 1779 | ee->error = EERR_INVALID_TYPE; |
| | 1780 | break; |
| | 1781 | } |
| | 1782 | break; |
| | 1783 | case 3: |
| | 1784 | switch (ee->type) { |
| | 1785 | case EE_FLOAT: |
| | 1786 | ee_shift(ee); |
| | 1787 | if (ee->error) return; |
| | 1788 | switch (ee->type) { |
| | 1789 | case EE_FLOAT: |
| | 1790 | ee->ival = (odval <= ee->dval)?1:0; |
| | 1791 | ee->type = EE_INT; |
| | 1792 | break; |
| | 1793 | case EE_INT: |
| | 1794 | ee->ival = (odval <= ee->ival)?1:0; |
| | 1795 | break; |
| | 1796 | default: |
| | 1797 | ee->error = EERR_INVALID_TYPE; |
| | 1798 | break; |
| | 1799 | } |
| | 1800 | break; |
| | 1801 | case EE_INT: |
| | 1802 | ee_shift(ee); |
| | 1803 | if (ee->error) return; |
| | 1804 | switch (ee->type) { |
| | 1805 | case EE_FLOAT: |
| | 1806 | ee->ival = (oival <= ee->dval)?1:0; |
| | 1807 | ee->type = EE_INT; |
| | 1808 | break; |
| | 1809 | case EE_INT: |
| | 1810 | ee->ival = (oival <= ee->ival)?1:0; |
| | 1811 | break; |
| | 1812 | default: |
| | 1813 | ee->error = EERR_INVALID_TYPE; |
| | 1814 | break; |
| | 1815 | } |
| | 1816 | break; |
| | 1817 | default: |
| | 1818 | ee->error = EERR_INVALID_TYPE; |
| | 1819 | break; |
| | 1820 | } |
| | 1821 | break; |
| | 1822 | case 4: |
| | 1823 | switch (ee->type) { |
| | 1824 | case EE_FLOAT: |
| | 1825 | ee_shift(ee); |
| | 1826 | if (ee->error) return; |
| | 1827 | switch (ee->type) { |
| | 1828 | case EE_FLOAT: |
| | 1829 | ee->ival = (odval >= ee->dval)?1:0; |
| | 1830 | ee->type = EE_INT; |
| | 1831 | break; |
| | 1832 | case EE_INT: |
| | 1833 | ee->ival = (odval >= ee->ival)?1:0; |
| | 1834 | break; |
| | 1835 | default: |
| | 1836 | ee->error = EERR_INVALID_TYPE; |
| | 1837 | break; |
| | 1838 | } |
| | 1839 | break; |
| | 1840 | case EE_INT: |
| | 1841 | ee_shift(ee); |
| | 1842 | if (ee->error) return; |
| | 1843 | switch (ee->type) { |
| | 1844 | case EE_FLOAT: |
| | 1845 | ee->ival = (oival >= ee->dval)?1:0; |
| | 1846 | ee->type = EE_INT; |
| | 1847 | break; |
| | 1848 | case EE_INT: |
| | 1849 | ee->ival = (oival >= ee->ival)?1:0; |
| | 1850 | break; |
| | 1851 | default: |
| | 1852 | ee->error = EERR_INVALID_TYPE; |
| | 1853 | break; |
| | 1854 | } |
| | 1855 | break; |
| | 1856 | default: |
| | 1857 | ee->error = EERR_INVALID_TYPE; |
| | 1858 | break; |
| | 1859 | } |
| | 1860 | break; |
| | 1861 | } |
| | 1862 | |
| | 1863 | ee_skip_spaces(ee); |
| | 1864 | } |
| | 1865 | } |
| | 1866 | |
| | 1867 | static void ee_equals(expreval_t* ee) |
| | 1868 | { |
| | 1869 | ee_compare(ee); |
| | 1870 | ee_skip_spaces(ee); |
| | 1871 | while (ee->head < ee->len && !ee->error && |
| | 1872 | ((ee->code[ee->head] == '=' && ee->code[ee->head + 1] == '=') || |
| | 1873 | (ee->code[ee->head] == '!' && ee->code[ee->head + 1] == '='))) { |
| | 1874 | double odval = ee->dval; |
| | 1875 | lilint_t oival = ee->ival; |
| | 1876 | int op = ee->code[ee->head] == '=' ? 1 : 2; |
| | 1877 | ee->head += 2; |
| | 1878 | |
| | 1879 | switch (op) { |
| | 1880 | case 1: |
| | 1881 | switch (ee->type) { |
| | 1882 | case EE_FLOAT: |
| | 1883 | ee_compare(ee); |
| | 1884 | if (ee->error) return; |
| | 1885 | switch (ee->type) { |
| | 1886 | case EE_FLOAT: |
| | 1887 | ee->ival = (odval == ee->dval)?1:0; |
| | 1888 | ee->type = EE_INT; |
| | 1889 | break; |
| | 1890 | case EE_INT: |
| | 1891 | ee->ival = (odval == ee->ival)?1:0; |
| | 1892 | break; |
| | 1893 | default: |
| | 1894 | ee->error = EERR_INVALID_TYPE; |
| | 1895 | break; |
| | 1896 | } |
| | 1897 | break; |
| | 1898 | case EE_INT: |
| | 1899 | ee_compare(ee); |
| | 1900 | if (ee->error) return; |
| | 1901 | switch (ee->type) { |
| | 1902 | case EE_FLOAT: |
| | 1903 | ee->ival = (oival == ee->dval)?1:0; |
| | 1904 | ee->type = EE_INT; |
| | 1905 | break; |
| | 1906 | case EE_INT: |
| | 1907 | ee->ival = (oival == ee->ival)?1:0; |
| | 1908 | break; |
| | 1909 | default: |
| | 1910 | ee->error = EERR_INVALID_TYPE; |
| | 1911 | break; |
| | 1912 | } |
| | 1913 | break; |
| | 1914 | default: |
| | 1915 | ee->error = EERR_INVALID_TYPE; |
| | 1916 | break; |
| | 1917 | } |
| | 1918 | break; |
| | 1919 | case 2: |
| | 1920 | switch (ee->type) { |
| | 1921 | case EE_FLOAT: |
| | 1922 | ee_compare(ee); |
| | 1923 | if (ee->error) return; |
| | 1924 | switch (ee->type) { |
| | 1925 | case EE_FLOAT: |
| | 1926 | ee->ival = (odval != ee->dval)?1:0; |
| | 1927 | ee->type = EE_INT; |
| | 1928 | break; |
| | 1929 | case EE_INT: |
| | 1930 | ee->ival = (odval != ee->ival)?1:0; |
| | 1931 | break; |
| | 1932 | default: |
| | 1933 | ee->error = EERR_INVALID_TYPE; |
| | 1934 | break; |
| | 1935 | } |
| | 1936 | break; |
| | 1937 | case EE_INT: |
| | 1938 | ee_compare(ee); |
| | 1939 | if (ee->error) return; |
| | 1940 | switch (ee->type) { |
| | 1941 | case EE_FLOAT: |
| | 1942 | ee->ival = (oival != ee->dval)?1:0; |
| | 1943 | ee->type = EE_INT; |
| | 1944 | break; |
| | 1945 | case EE_INT: |
| | 1946 | ee->ival = (oival != ee->ival)?1:0; |
| | 1947 | break; |
| | 1948 | default: |
| | 1949 | ee->error = EERR_INVALID_TYPE; |
| | 1950 | break; |
| | 1951 | } |
| | 1952 | break; |
| | 1953 | default: |
| | 1954 | ee->error = EERR_INVALID_TYPE; |
| | 1955 | break; |
| | 1956 | } |
| | 1957 | break; |
| | 1958 | } |
| | 1959 | |
| | 1960 | ee_skip_spaces(ee); |
| | 1961 | } |
| | 1962 | } |
| | 1963 | |
| | 1964 | static void ee_bitand(expreval_t* ee) |
| | 1965 | { |
| | 1966 | ee_equals(ee); |
| | 1967 | ee_skip_spaces(ee); |
| | 1968 | while (ee->head < ee->len && !ee->error && |
| | 1969 | (ee->code[ee->head] == '&' && !ee_invalidpunct(ee->code[ee->head + 1]))) { |
| | 1970 | double odval = ee->dval; |
| | 1971 | lilint_t oival = ee->ival; |
| | 1972 | ee->head++; |
| | 1973 | |
| | 1974 | switch (ee->type) { |
| | 1975 | case EE_FLOAT: |
| | 1976 | ee_equals(ee); |
| | 1977 | if (ee->error) return; |
| | 1978 | switch (ee->type) { |
| | 1979 | case EE_FLOAT: |
| | 1980 | ee->ival = (lilint_t)odval & (lilint_t)ee->dval; |
| | 1981 | ee->type = EE_INT; |
| | 1982 | break; |
| | 1983 | case EE_INT: |
| | 1984 | ee->ival = (lilint_t)odval & ee->ival; |
| | 1985 | break; |
| | 1986 | default: |
| | 1987 | ee->error = EERR_INVALID_TYPE; |
| | 1988 | break; |
| | 1989 | } |
| | 1990 | break; |
| | 1991 | case EE_INT: |
| | 1992 | ee_equals(ee); |
| | 1993 | if (ee->error) return; |
| | 1994 | switch (ee->type) { |
| | 1995 | case EE_FLOAT: |
| | 1996 | ee->ival = oival & (lilint_t)ee->dval; |
| | 1997 | ee->type = EE_INT; |
| | 1998 | break; |
| | 1999 | case EE_INT: |
| | 2000 | ee->ival = oival & ee->ival; |
| | 2001 | break; |
| | 2002 | default: |
| | 2003 | ee->error = EERR_INVALID_TYPE; |
| | 2004 | break; |
| | 2005 | } |
| | 2006 | break; |
| | 2007 | default: |
| | 2008 | ee->error = EERR_INVALID_TYPE; |
| | 2009 | break; |
| | 2010 | } |
| | 2011 | |
| | 2012 | ee_skip_spaces(ee); |
| | 2013 | } |
| | 2014 | } |
| | 2015 | |
| | 2016 | static void ee_bitor(expreval_t* ee) |
| | 2017 | { |
| | 2018 | ee_bitand(ee); |
| | 2019 | ee_skip_spaces(ee); |
| | 2020 | while (ee->head < ee->len && !ee->error && |
| | 2021 | (ee->code[ee->head] == '|' && !ee_invalidpunct(ee->code[ee->head + 1]))) { |
| | 2022 | double odval = ee->dval; |
| | 2023 | lilint_t oival = ee->ival; |
| | 2024 | ee->head++; |
| | 2025 | |
| | 2026 | switch (ee->type) { |
| | 2027 | case EE_FLOAT: |
| | 2028 | ee_bitand(ee); |
| | 2029 | if (ee->error) return; |
| | 2030 | switch (ee->type) { |
| | 2031 | case EE_FLOAT: |
| | 2032 | ee->ival = (lilint_t)odval | (lilint_t)ee->dval; |
| | 2033 | ee->type = EE_INT; |
| | 2034 | break; |
| | 2035 | case EE_INT: |
| | 2036 | ee->ival = (lilint_t)odval | ee->ival; |
| | 2037 | break; |
| | 2038 | default: |
| | 2039 | ee->error = EERR_INVALID_TYPE; |
| | 2040 | break; |
| | 2041 | } |
| | 2042 | break; |
| | 2043 | case EE_INT: |
| | 2044 | ee_bitand(ee); |
| | 2045 | if (ee->error) return; |
| | 2046 | switch (ee->type) { |
| | 2047 | case EE_FLOAT: |
| | 2048 | ee->ival = oival | (lilint_t)ee->dval; |
| | 2049 | ee->type = EE_INT; |
| | 2050 | break; |
| | 2051 | case EE_INT: |
| | 2052 | ee->ival = oival | ee->ival; |
| | 2053 | break; |
| | 2054 | default: |
| | 2055 | ee->error = EERR_INVALID_TYPE; |
| | 2056 | break; |
| | 2057 | } |
| | 2058 | break; |
| | 2059 | default: |
| | 2060 | ee->error = EERR_INVALID_TYPE; |
| | 2061 | break; |
| | 2062 | } |
| | 2063 | |
| | 2064 | ee_skip_spaces(ee); |
| | 2065 | } |
| | 2066 | } |
| | 2067 | |
| | 2068 | static void ee_logand(expreval_t* ee) |
| | 2069 | { |
| | 2070 | ee_bitor(ee); |
| | 2071 | ee_skip_spaces(ee); |
| | 2072 | while (ee->head < ee->len && !ee->error && |
| | 2073 | (ee->code[ee->head] == '&' && ee->code[ee->head + 1] == '&')) { |
| | 2074 | double odval = ee->dval; |
| | 2075 | lilint_t oival = ee->ival; |
| | 2076 | ee->head += 2; |
| | 2077 | |
| | 2078 | switch (ee->type) { |
| | 2079 | case EE_FLOAT: |
| | 2080 | ee_bitor(ee); |
| | 2081 | if (ee->error) return; |
| | 2082 | switch (ee->type) { |
| | 2083 | case EE_FLOAT: |
| | 2084 | ee->ival = (odval && ee->dval)?1:0; |
| | 2085 | ee->type = EE_INT; |
| | 2086 | break; |
| | 2087 | case EE_INT: |
| | 2088 | ee->ival = (odval && ee->ival)?1:0; |
| | 2089 | break; |
| | 2090 | default: |
| | 2091 | ee->error = EERR_INVALID_TYPE; |
| | 2092 | break; |
| | 2093 | } |
| | 2094 | break; |
| | 2095 | case EE_INT: |
| | 2096 | ee_bitor(ee); |
| | 2097 | if (ee->error) return; |
| | 2098 | switch (ee->type) { |
| | 2099 | case EE_FLOAT: |
| | 2100 | ee->ival = (oival && ee->dval)?1:0; |
| | 2101 | ee->type = EE_INT; |
| | 2102 | break; |
| | 2103 | case EE_INT: |
| | 2104 | ee->ival = (oival && ee->ival)?1:0; |
| | 2105 | break; |
| | 2106 | default: |
| | 2107 | ee->error = EERR_INVALID_TYPE; |
| | 2108 | break; |
| | 2109 | } |
| | 2110 | break; |
| | 2111 | default: |
| | 2112 | ee->error = EERR_INVALID_TYPE; |
| | 2113 | break; |
| | 2114 | } |
| | 2115 | |
| | 2116 | ee_skip_spaces(ee); |
| | 2117 | } |
| | 2118 | } |
| | 2119 | |
| | 2120 | static void ee_logor(expreval_t* ee) |
| | 2121 | { |
| | 2122 | ee_logand(ee); |
| | 2123 | ee_skip_spaces(ee); |
| | 2124 | while (ee->head < ee->len && !ee->error && |
| | 2125 | (ee->code[ee->head] == '|' && ee->code[ee->head + 1] == '|')) { |
| | 2126 | double odval = ee->dval; |
| | 2127 | lilint_t oival = ee->ival; |
| | 2128 | ee->head += 2; |
| | 2129 | |
| | 2130 | switch (ee->type) { |
| | 2131 | case EE_FLOAT: |
| | 2132 | ee_logand(ee); |
| | 2133 | if (ee->error) return; |
| | 2134 | switch (ee->type) { |
| | 2135 | case EE_FLOAT: |
| | 2136 | ee->ival = (odval || ee->dval)?1:0; |
| | 2137 | ee->type = EE_INT; |
| | 2138 | break; |
| | 2139 | case EE_INT: |
| | 2140 | ee->ival = (odval || ee->ival)?1:0; |
| | 2141 | break; |
| | 2142 | default: |
| | 2143 | ee->error = EERR_INVALID_TYPE; |
| | 2144 | break; |
| | 2145 | } |
| | 2146 | break; |
| | 2147 | case EE_INT: |
| | 2148 | ee_logand(ee); |
| | 2149 | if (ee->error) return; |
| | 2150 | switch (ee->type) { |
| | 2151 | case EE_FLOAT: |
| | 2152 | ee->ival = (oival || ee->dval)?1:0; |
| | 2153 | ee->type = EE_INT; |
| | 2154 | break; |
| | 2155 | case EE_INT: |
| | 2156 | ee->ival = (oival || ee->ival)?1:0; |
| | 2157 | break; |
| | 2158 | default: |
| | 2159 | ee->error = EERR_INVALID_TYPE; |
| | 2160 | break; |
| | 2161 | } |
| | 2162 | break; |
| | 2163 | default: |
| | 2164 | ee->error = EERR_INVALID_TYPE; |
| | 2165 | break; |
| | 2166 | } |
| | 2167 | |
| | 2168 | ee_skip_spaces(ee); |
| | 2169 | } |
| | 2170 | } |
| | 2171 | |
| | 2172 | static void ee_expr(expreval_t* ee) |
| | 2173 | { |
| | 2174 | ee_logor(ee); |
| | 2175 | /* invalid expression doesn't really matter, it is only used to stop |
| | 2176 | * the expression parsing. */ |
| | 2177 | if (ee->error == EERR_INVALID_EXPRESSION) { |
| | 2178 | ee->error = EERR_NO_ERROR; |
| | 2179 | ee->ival = 1; |
| | 2180 | } |
| | 2181 | } |
| | 2182 | |
| | 2183 | lil_value_t lil_eval_expr(lil_t lil, lil_value_t code) |
| | 2184 | { |
| | 2185 | expreval_t ee; |
| | 2186 | code = lil_subst_to_value(lil, code); |
| | 2187 | if (lil->error) return NULL; |
| | 2188 | ee.code = lil_to_string(code); |
| | 2189 | /* an empty expression equals to 0 so that it can be used as a false value |
| | 2190 | * in conditionals */ |
| | 2191 | if (!ee.code[0]) { |
| | 2192 | lil_free_value(code); |
| | 2193 | return lil_alloc_integer(0); |
| | 2194 | } |
| | 2195 | ee.head = 0; |
| | 2196 | ee.len = code->l; |
| | 2197 | ee.ival = 0; |
| | 2198 | ee.dval = 0; |
| | 2199 | ee.type = EE_INT; |
| | 2200 | ee.error = 0; |
| | 2201 | ee_expr(&ee); |
| | 2202 | lil_free_value(code); |
| | 2203 | if (ee.error) { |
| | 2204 | switch (ee.error) { |
| | 2205 | case EERR_DIVISION_BY_ZERO: |
| | 2206 | lil_set_error(lil, "division by zero in expression"); |
| | 2207 | break; |
| | 2208 | case EERR_INVALID_TYPE: |
| | 2209 | lil_set_error(lil, "mixing invalid types in expression"); |
| | 2210 | break; |
| | 2211 | case EERR_SYNTAX_ERROR: |
| | 2212 | lil_set_error(lil, "expression syntax error"); |
| | 2213 | break; |
| | 2214 | } |
| | 2215 | return NULL; |
| | 2216 | } |
| | 2217 | if (ee.type == EE_INT) |
| | 2218 | return lil_alloc_integer(ee.ival); |
| | 2219 | else |
| | 2220 | return lil_alloc_double(ee.dval); |
| | 2221 | } |
| | 2222 | |
| | 2223 | lil_value_t lil_unused_name(lil_t lil, const char* part) |
| | 2224 | { |
| | 2225 | char* name = malloc(strlen(part) + 64); |
| | 2226 | lil_value_t val; |
| | 2227 | size_t i; |
| | 2228 | for (i=0; i<(size_t)-1; i++) { |
| | 2229 | sprintf(name, "!!un!%s!%09u!nu!!", part, (unsigned int)i); |
| | 2230 | if (find_cmd(lil, name)) continue; |
| | 2231 | if (lil_find_var(lil, lil->env, name)) continue; |
| | 2232 | val = lil_alloc_string(name); |
| | 2233 | free(name); |
| | 2234 | return val; |
| | 2235 | } |
| | 2236 | return NULL; |
| | 2237 | } |
| | 2238 | |
| | 2239 | lil_value_t lil_arg(lil_value_t* argv, size_t index) |
| | 2240 | { |
| | 2241 | return argv ? argv[index] : NULL; |
| | 2242 | } |
| | 2243 | |
| | 2244 | const char* lil_to_string(lil_value_t val) |
| | 2245 | { |
| | 2246 | return (val && val->l) ? val->d : ""; |
| | 2247 | } |
| | 2248 | |
| | 2249 | double lil_to_double(lil_value_t val) |
| | 2250 | { |
| | 2251 | return atof(lil_to_string(val)); |
| | 2252 | } |
| | 2253 | |
| | 2254 | lilint_t lil_to_integer(lil_value_t val) |
| | 2255 | { |
| | 2256 | return (lilint_t)atoll(lil_to_string(val)); |
| | 2257 | } |
| | 2258 | |
| | 2259 | int lil_to_boolean(lil_value_t val) |
| | 2260 | { |
| | 2261 | const char* s = lil_to_string(val); |
| | 2262 | size_t i, dots = 0; |
| | 2263 | if (!s[0]) {return 0;} |
| | 2264 | for (i=0; s[i]; i++) { |
| | 2265 | if (s[i] != '0' && s[i] != '.') return 1; |
| | 2266 | if (s[i] == '.') { |
| | 2267 | if (dots) return 1; |
| | 2268 | dots = 1; |
| | 2269 | } |
| | 2270 | } |
| | 2271 | return 0; |
| | 2272 | } |
| | 2273 | |
| | 2274 | lil_value_t lil_alloc_string(const char* str) |
| | 2275 | { |
| | 2276 | return alloc_value(str); |
| | 2277 | } |
| | 2278 | |
| | 2279 | lil_value_t lil_alloc_string_len(const char* str, size_t len) |
| | 2280 | { |
| | 2281 | return alloc_value_len(str, len); |
| | 2282 | } |
| | 2283 | |
| | 2284 | lil_value_t lil_alloc_double(double num) |
| | 2285 | { |
| | 2286 | char buff[128]; |
| | 2287 | sprintf(buff, "%f", num); |
| | 2288 | return alloc_value(buff); |
| | 2289 | } |
| | 2290 | |
| | 2291 | lil_value_t lil_alloc_integer(lilint_t num) |
| | 2292 | { |
| | 2293 | char buff[128]; |
| | 2294 | sprintf(buff, LILINT_PRINTF, num); |
| | 2295 | return alloc_value(buff); |
| | 2296 | } |
| | 2297 | |
| | 2298 | void lil_free(lil_t lil) |
| | 2299 | { |
| | 2300 | size_t i; |
| | 2301 | if (!lil) return; |
| | 2302 | free(lil->err_msg); |
| | 2303 | lil_free_value(lil->empty); |
| | 2304 | while (lil->env) { |
| | 2305 | lil_env_t next = lil->env->parent; |
| | 2306 | lil_free_env(lil->env); |
| | 2307 | lil->env = next; |
| | 2308 | } |
| | 2309 | for (i=0; i<lil->cmds; i++) { |
| | 2310 | if (lil->cmd[i]->argnames) |
| | 2311 | lil_free_list(lil->cmd[i]->argnames); |
| | 2312 | lil_free_value(lil->cmd[i]->code); |
| | 2313 | free(lil->cmd[i]->name); |
| | 2314 | free(lil->cmd[i]); |
| | 2315 | } |
| | 2316 | hm_destroy(&lil->cmdmap); |
| | 2317 | free(lil->cmd); |
| | 2318 | free(lil->dollarprefix); |
| | 2319 | free(lil->catcher); |
| | 2320 | free(lil); |
| | 2321 | } |
| | 2322 | |
| | 2323 | LILAPI void lil_set_data(lil_t lil, void* data) |
| | 2324 | { |
| | 2325 | lil->data = data; |
| | 2326 | } |
| | 2327 | |
| | 2328 | LILAPI void* lil_get_data(lil_t lil) |
| | 2329 | { |
| | 2330 | return lil->data; |
| | 2331 | } |
| | 2332 | |
| | 2333 | static LILCALLBACK void fnc_embed_write(lil_t lil, const char* msg) |
| | 2334 | { |
| | 2335 | size_t len = strlen(msg) + 1; |
| | 2336 | lil->embed = realloc(lil->embed, lil->embedlen + len); |
| | 2337 | memcpy(lil->embed + lil->embedlen, msg, len); |
| | 2338 | lil->embedlen += len - 1; |
| | 2339 | } |
| | 2340 | |
| | 2341 | char* lil_embedded(lil_t lil, const char* code, unsigned int flags) |
| | 2342 | { |
| | 2343 | char* prev_embed = lil->embed; |
| | 2344 | size_t prev_embedlen = lil->embedlen; |
| | 2345 | lil_callback_proc_t prev_write = lil->callback[LIL_CALLBACK_WRITE]; |
| | 2346 | char* lilcode = NULL; |
| | 2347 | size_t lilcodelen = 0; |
| | 2348 | char* cont = NULL; |
| | 2349 | size_t contlen = 0; |
| | 2350 | size_t head = 0, codelen = strlen(code); |
| | 2351 | char* result; |
| | 2352 | |
| | 2353 | lil->callback[LIL_CALLBACK_WRITE] = (lil_callback_proc_t)fnc_embed_write; |
| | 2354 | lil->embed = NULL; |
| | 2355 | lil->embedlen = 0; |
| | 2356 | |
| | 2357 | while (head < codelen) { |
| | 2358 | if (head < codelen - 4 && |
| | 2359 | code[head] == '<' && |
| | 2360 | code[head + 1] == '?' && |
| | 2361 | code[head + 2] == 'l' && |
| | 2362 | code[head + 3] == 'i' && |
| | 2363 | code[head + 4] == 'l') { |
| | 2364 | head += 5; |
| | 2365 | if (contlen) { |
| | 2366 | lilcode = realloc(lilcode, lilcodelen + contlen + 10); |
| | 2367 | memcpy(lilcode + lilcodelen, "\nwrite {", 8); |
| | 2368 | memcpy(lilcode + lilcodelen + 8, cont, contlen); |
| | 2369 | lilcode[lilcodelen + contlen + 8] = '}'; |
| | 2370 | lilcode[lilcodelen + contlen + 9] = '\n'; |
| | 2371 | lilcodelen += contlen + 10; |
| | 2372 | free(cont); |
| | 2373 | cont = NULL; |
| | 2374 | contlen = 0; |
| | 2375 | } |
| | 2376 | while (head < codelen) { |
| | 2377 | if (head < codelen - 1 && code[head] == '?' && code[head + 1] == '>') { |
| | 2378 | head += 2; |
| | 2379 | break; |
| | 2380 | } |
| | 2381 | lilcode = realloc(lilcode, lilcodelen + 1); |
| | 2382 | lilcode[lilcodelen++] = code[head++]; |
| | 2383 | } |
| | 2384 | lilcode = realloc(lilcode, lilcodelen + 1); |
| | 2385 | lilcode[lilcodelen++] = '\n'; |
| | 2386 | } else { |
| | 2387 | if (code[head] == '{' || code[head] == '}') { |
| | 2388 | cont = realloc(cont, contlen + 6); |
| | 2389 | cont[contlen++] = '}'; |
| | 2390 | cont[contlen++] = '"'; |
| | 2391 | cont[contlen++] = '\\'; |
| | 2392 | if (code[head] == '{') |
| | 2393 | cont[contlen++] = 'o'; |
| | 2394 | else |
| | 2395 | cont[contlen++] = 'c'; |
| | 2396 | cont[contlen++] = '"'; |
| | 2397 | cont[contlen++] = '{'; |
| | 2398 | head++; |
| | 2399 | } else { |
| | 2400 | cont = realloc(cont, contlen + 1); |
| | 2401 | cont[contlen++] = code[head++]; |
| | 2402 | } |
| | 2403 | } |
| | 2404 | } |
| | 2405 | if (contlen) { |
| | 2406 | lilcode = realloc(lilcode, lilcodelen + contlen + 10); |
| | 2407 | memcpy(lilcode + lilcodelen, "\nwrite {", 8); |
| | 2408 | memcpy(lilcode + lilcodelen + 8, cont, contlen); |
| | 2409 | lilcode[lilcodelen + contlen + 8] = '}'; |
| | 2410 | lilcode[lilcodelen + contlen + 9] = '\n'; |
| | 2411 | lilcodelen += contlen + 10; |
| | 2412 | free(cont); |
| | 2413 | } |
| | 2414 | |
| | 2415 | lilcode = realloc(lilcode, lilcodelen + 1); |
| | 2416 | lilcode[lilcodelen] = 0; |
| | 2417 | lil_free_value(lil_parse(lil, lilcode, 0, 1)); |
| | 2418 | free(lilcode); |
| | 2419 | result = lil->embed ? lil->embed : strclone(""); |
| | 2420 | |
| | 2421 | lil->embed = prev_embed; |
| | 2422 | lil->embedlen = prev_embedlen; |
| | 2423 | lil->callback[LIL_CALLBACK_WRITE] = prev_write; |
| | 2424 | |
| | 2425 | return result; |
| | 2426 | } |
| | 2427 | |
| | 2428 | void lil_freemem(void* ptr) |
| | 2429 | { |
| | 2430 | free(ptr); |
| | 2431 | } |
| | 2432 | |
| | 2433 | void lil_write(lil_t lil, const char* msg) |
| | 2434 | { |
| | 2435 | if (lil->callback[LIL_CALLBACK_WRITE]) { |
| | 2436 | lil_write_callback_proc_t proc = (lil_write_callback_proc_t)lil->callback[LIL_CALLBACK_WRITE]; |
| | 2437 | proc(lil, msg); |
| | 2438 | } else printf("%s", msg); |
| | 2439 | } |
| | 2440 | |
| | 2441 | static LILCALLBACK lil_value_t fnc_reflect(lil_t lil, size_t argc, lil_value_t* argv) |
| | 2442 | { |
| | 2443 | lil_func_t func; |
| | 2444 | const char* type; |
| | 2445 | size_t i; |
| | 2446 | lil_value_t r; |
| | 2447 | if (!argc) return NULL; |
| | 2448 | type = lil_to_string(argv[0]); |
| | 2449 | if (!strcmp(type, "version")) { |
| | 2450 | return lil_alloc_string(LIL_VERSION_STRING); |
| | 2451 | } |
| | 2452 | if (!strcmp(type, "args")) { |
| | 2453 | if (argc < 2) return NULL; |
| | 2454 | func = find_cmd(lil, lil_to_string(argv[1])); |
| | 2455 | if (!func || !func->argnames) return NULL; |
| | 2456 | return lil_list_to_value(func->argnames, 1); |
| | 2457 | } |
| | 2458 | if (!strcmp(type, "body")) { |
| | 2459 | if (argc < 2) return NULL; |
| | 2460 | func = find_cmd(lil, lil_to_string(argv[1])); |
| | 2461 | if (!func || func->proc) return NULL; |
| | 2462 | return lil_clone_value(func->code); |
| | 2463 | } |
| | 2464 | if (!strcmp(type, "func-count")) { |
| | 2465 | return lil_alloc_integer(lil->cmds); |
| | 2466 | } |
| | 2467 | if (!strcmp(type, "funcs")) { |
| | 2468 | lil_list_t funcs = lil_alloc_list(); |
| | 2469 | for (i=0; i<lil->cmds; i++) |
| | 2470 | lil_list_append(funcs, lil_alloc_string(lil->cmd[i]->name)); |
| | 2471 | r = lil_list_to_value(funcs, 1); |
| | 2472 | lil_free_list(funcs); |
| | 2473 | return r; |
| | 2474 | } |
| | 2475 | if (!strcmp(type, "vars")) { |
| | 2476 | lil_list_t vars = lil_alloc_list(); |
| | 2477 | lil_env_t env = lil->env; |
| | 2478 | while (env) { |
| | 2479 | for (i=0; i<env->vars; i++) |
| | 2480 | lil_list_append(vars, lil_alloc_string(env->var[i]->n)); |
| | 2481 | env = env->parent; |
| | 2482 | } |
| | 2483 | r = lil_list_to_value(vars, 1); |
| | 2484 | lil_free_list(vars); |
| | 2485 | return r; |
| | 2486 | } |
| | 2487 | if (!strcmp(type, "globals")) { |
| | 2488 | lil_list_t vars = lil_alloc_list(); |
| | 2489 | for (i=0; i<lil->rootenv->vars; i++) |
| | 2490 | lil_list_append(vars, lil_alloc_string(lil->rootenv->var[i]->n)); |
| | 2491 | r = lil_list_to_value(vars, 1); |
| | 2492 | lil_free_list(vars); |
| | 2493 | return r; |
| | 2494 | } |
| | 2495 | if (!strcmp(type, "has-func")) { |
| | 2496 | const char* target; |
| | 2497 | if (argc == 1) return NULL; |
| | 2498 | target = lil_to_string(argv[1]); |
| | 2499 | return hm_has(&lil->cmdmap, target) ? lil_alloc_string("1") : NULL; |
| | 2500 | } |
| | 2501 | if (!strcmp(type, "has-var")) { |
| | 2502 | const char* target; |
| | 2503 | lil_env_t env = lil->env; |
| | 2504 | if (argc == 1) return NULL; |
| | 2505 | target = lil_to_string(argv[1]); |
| | 2506 | while (env) { |
| | 2507 | if (hm_has(&env->varmap, target)) return lil_alloc_string("1"); |
| | 2508 | env = env->parent; |
| | 2509 | } |
| | 2510 | return NULL; |
| | 2511 | } |
| | 2512 | if (!strcmp(type, "has-global")) { |
| | 2513 | const char* target; |
| | 2514 | if (argc == 1) return NULL; |
| | 2515 | target = lil_to_string(argv[1]); |
| | 2516 | for (i=0; i<lil->rootenv->vars; i++) |
| | 2517 | if (!strcmp(target, lil->rootenv->var[i]->n)) return lil_alloc_string("1"); |
| | 2518 | return NULL; |
| | 2519 | } |
| | 2520 | if (!strcmp(type, "error")) { |
| | 2521 | return lil->err_msg ? lil_alloc_string(lil->err_msg) : NULL; |
| | 2522 | } |
| | 2523 | if (!strcmp(type, "dollar-prefix")) { |
| | 2524 | lil_value_t r; |
| | 2525 | if (argc == 1) return lil_alloc_string(lil->dollarprefix); |
| | 2526 | r = lil_alloc_string(lil->dollarprefix); |
| | 2527 | free(lil->dollarprefix); |
| | 2528 | lil->dollarprefix = strclone(lil_to_string(argv[1])); |
| | 2529 | return r; |
| | 2530 | } |
| | 2531 | if (!strcmp(type, "this")) { |
| | 2532 | lil_env_t env = lil->env; |
| | 2533 | while (env != lil->rootenv && !env->catcher_for && !env->func) env = env->parent; |
| | 2534 | if (env->catcher_for) return lil_alloc_string(lil->catcher); |
| | 2535 | if (env == lil->rootenv) return lil_alloc_string(lil->rootcode); |
| | 2536 | return env->func ? env->func->code : NULL; |
| | 2537 | } |
| | 2538 | if (!strcmp(type, "name")) { |
| | 2539 | lil_env_t env = lil->env; |
| | 2540 | while (env != lil->rootenv && !env->catcher_for && !env->func) env = env->parent; |
| | 2541 | if (env->catcher_for) return env->catcher_for; |
| | 2542 | if (env == lil->rootenv) return NULL; |
| | 2543 | return env->func ? lil_alloc_string(env->func->name) : NULL; |
| | 2544 | } |
| | 2545 | return NULL; |
| | 2546 | } |
| | 2547 | |
| | 2548 | static LILCALLBACK lil_value_t fnc_func(lil_t lil, size_t argc, lil_value_t* argv) |
| | 2549 | { |
| | 2550 | lil_value_t name; |
| | 2551 | lil_func_t cmd; |
| | 2552 | lil_list_t fargs; |
| | 2553 | if (argc < 1) return NULL; |
| | 2554 | if (argc >= 3) { |
| | 2555 | name = lil_clone_value(argv[0]); |
| | 2556 | fargs = lil_subst_to_list(lil, argv[1]); |
| | 2557 | cmd = add_func(lil, lil_to_string(argv[0])); |
| | 2558 | cmd->argnames = fargs; |
| | 2559 | cmd->code = lil_clone_value(argv[2]); |
| | 2560 | } else { |
| | 2561 | name = lil_unused_name(lil, "anonymous-function"); |
| | 2562 | if (argc < 2) { |
| | 2563 | lil_value_t tmp = lil_alloc_string("args"); |
| | 2564 | fargs = lil_subst_to_list(lil, tmp); |
| | 2565 | lil_free_value(tmp); |
| | 2566 | cmd = add_func(lil, lil_to_string(name)); |
| | 2567 | cmd->argnames = fargs; |
| | 2568 | cmd->code = lil_clone_value(argv[0]); |
| | 2569 | } else { |
| | 2570 | fargs = lil_subst_to_list(lil, argv[0]); |
| | 2571 | cmd = add_func(lil, lil_to_string(name)); |
| | 2572 | cmd->argnames = fargs; |
| | 2573 | cmd->code = lil_clone_value(argv[1]); |
| | 2574 | } |
| | 2575 | } |
| | 2576 | return name; |
| | 2577 | } |
| | 2578 | |
| | 2579 | static LILCALLBACK lil_value_t fnc_rename(lil_t lil, size_t argc, lil_value_t* argv) |
| | 2580 | { |
| | 2581 | lil_value_t r; |
| | 2582 | lil_func_t func; |
| | 2583 | const char* oldname; |
| | 2584 | const char* newname; |
| | 2585 | if (argc < 2) return NULL; |
| | 2586 | oldname = lil_to_string(argv[0]); |
| | 2587 | newname = lil_to_string(argv[1]); |
| | 2588 | func = find_cmd(lil, oldname); |
| | 2589 | if (!func) { |
| | 2590 | char* msg = malloc(24 + strlen(oldname)); |
| | 2591 | sprintf(msg, "unknown function '%s'", oldname); |
| | 2592 | lil_set_error_at(lil, lil->head, msg); |
| | 2593 | free(msg); |
| | 2594 | return NULL; |
| | 2595 | } |
| | 2596 | r = lil_alloc_string(func->name); |
| | 2597 | if (newname[0]) { |
| | 2598 | hm_put(&lil->cmdmap, oldname, 0); |
| | 2599 | hm_put(&lil->cmdmap, newname, func); |
| | 2600 | free(func->name); |
| | 2601 | func->name = strclone(newname); |
| | 2602 | } else { |
| | 2603 | del_func(lil, func); |
| | 2604 | } |
| | 2605 | return r; |
| | 2606 | } |
| | 2607 | |
| | 2608 | static LILCALLBACK lil_value_t fnc_unusedname(lil_t lil, size_t argc, lil_value_t* argv) |
| | 2609 | { |
| | 2610 | return lil_unused_name(lil, argc > 0 ? lil_to_string(argv[0]) : "unusedname"); |
| | 2611 | } |
| | 2612 | |
| | 2613 | static LILCALLBACK lil_value_t fnc_quote(lil_t lil, size_t argc, lil_value_t* argv) |
| | 2614 | { |
| | 2615 | lil_value_t r; |
| | 2616 | size_t i; |
| | 2617 | if (argc < 1) return NULL; |
| | 2618 | r = alloc_value(NULL); |
| | 2619 | for (i=0; i<argc; i++) { |
| | 2620 | if (i) lil_append_char(r, ' '); |
| | 2621 | lil_append_val(r, argv[i]); |
| | 2622 | } |
| | 2623 | return r; |
| | 2624 | } |
| | 2625 | |
| | 2626 | static LILCALLBACK lil_value_t fnc_set(lil_t lil, size_t argc, lil_value_t* argv) |
| | 2627 | { |
| | 2628 | size_t i = 0; |
| | 2629 | lil_var_t var = NULL; |
| | 2630 | int access = LIL_SETVAR_LOCAL; |
| | 2631 | if (!argc) return NULL; |
| | 2632 | if (!strcmp(lil_to_string(argv[0]), "global")) { |
| | 2633 | i = 1; |
| | 2634 | access = LIL_SETVAR_GLOBAL; |
| | 2635 | } |
| | 2636 | while (i < argc) { |
| | 2637 | if (argc == i + 1) return lil_clone_value(lil_get_var(lil, lil_to_string(argv[i]))); |
| | 2638 | var = lil_set_var(lil, lil_to_string(argv[i]), argv[i + 1], access); |
| | 2639 | i += 2; |
| | 2640 | } |
| | 2641 | return var ? lil_clone_value(var->v) : NULL; |
| | 2642 | } |
| | 2643 | |
| | 2644 | static LILCALLBACK lil_value_t fnc_local(lil_t lil, size_t argc, lil_value_t* argv) |
| | 2645 | { |
| | 2646 | size_t i; |
| | 2647 | for (i=0; i<argc; i++) { |
| | 2648 | const char* varname = lil_to_string(argv[i]); |
| | 2649 | if (!lil_find_local_var(lil, lil->env, varname)) |
| | 2650 | lil_set_var(lil, varname, lil->empty, LIL_SETVAR_LOCAL_NEW); |
| | 2651 | } |
| | 2652 | return NULL; |
| | 2653 | } |
| | 2654 | |
| | 2655 | static LILCALLBACK lil_value_t fnc_write(lil_t lil, size_t argc, lil_value_t* argv) |
| | 2656 | { |
| | 2657 | size_t i; |
| | 2658 | lil_value_t msg = lil_alloc_string(NULL); |
| | 2659 | for (i=0; i<argc; i++) { |
| | 2660 | if (i) lil_append_char(msg, ' '); |
| | 2661 | lil_append_val(msg, argv[i]); |
| | 2662 | } |
| | 2663 | lil_write(lil, lil_to_string(msg)); |
| | 2664 | lil_free_value(msg); |
| | 2665 | return NULL; |
| | 2666 | } |
| | 2667 | |
| | 2668 | static LILCALLBACK lil_value_t fnc_print(lil_t lil, size_t argc, lil_value_t* argv) |
| | 2669 | { |
| | 2670 | fnc_write(lil, argc, argv); |
| | 2671 | lil_write(lil, "\n"); |
| | 2672 | return NULL; |
| | 2673 | } |
| | 2674 | |
| | 2675 | static LILCALLBACK lil_value_t fnc_eval(lil_t lil, size_t argc, lil_value_t* argv) |
| | 2676 | { |
| | 2677 | if (argc == 1) return lil_parse_value(lil, argv[0], 0); |
| | 2678 | if (argc > 1) { |
| | 2679 | lil_value_t val = alloc_value(NULL), r; |
| | 2680 | size_t i; |
| | 2681 | for (i=0; i<argc; i++) { |
| | 2682 | if (i) lil_append_char(val, ' '); |
| | 2683 | lil_append_val(val, argv[i]); |
| | 2684 | } |
| | 2685 | r = lil_parse_value(lil, val, 0); |
| | 2686 | lil_free_value(val); |
| | 2687 | return r; |
| | 2688 | } |
| | 2689 | return NULL; |
| | 2690 | } |
| | 2691 | |
| | 2692 | static LILCALLBACK lil_value_t fnc_topeval(lil_t lil, size_t argc, lil_value_t* argv) |
| | 2693 | { |
| | 2694 | lil_env_t thisenv = lil->env; |
| | 2695 | lil_env_t thisdownenv = lil->downenv; |
| | 2696 | lil_value_t r; |
| | 2697 | lil->env = lil->rootenv; |
| | 2698 | lil->downenv = thisenv; |
| | 2699 | r = fnc_eval(lil, argc, argv); |
| | 2700 | lil->downenv = thisdownenv; |
| | 2701 | lil->env = thisenv; |
| | 2702 | return r; |
| | 2703 | } |
| | 2704 | |
| | 2705 | static LILCALLBACK lil_value_t fnc_upeval(lil_t lil, size_t argc, lil_value_t* argv) |
| | 2706 | { |
| | 2707 | lil_env_t thisenv = lil->env; |
| | 2708 | lil_env_t thisdownenv = lil->downenv; |
| | 2709 | lil_value_t r; |
| | 2710 | if (lil->rootenv == thisenv) return fnc_eval(lil, argc, argv); |
| | 2711 | lil->env = thisenv->parent; |
| | 2712 | lil->downenv = thisenv; |
| | 2713 | r = fnc_eval(lil, argc, argv); |
| | 2714 | lil->env = thisenv; |
| | 2715 | lil->downenv = thisdownenv; |
| | 2716 | return r; |
| | 2717 | } |
| | 2718 | |
| | 2719 | static LILCALLBACK lil_value_t fnc_downeval(lil_t lil, size_t argc, lil_value_t* argv) |
| | 2720 | { |
| | 2721 | lil_value_t r; |
| | 2722 | lil_env_t upenv = lil->env; |
| | 2723 | lil_env_t downenv = lil->downenv; |
| | 2724 | if (!downenv) return fnc_eval(lil, argc, argv); |
| | 2725 | lil->downenv = NULL; |
| | 2726 | lil->env = downenv; |
| | 2727 | r = fnc_eval(lil, argc, argv); |
| | 2728 | lil->downenv = downenv; |
| | 2729 | lil->env = upenv; |
| | 2730 | return r; |
| | 2731 | } |
| | 2732 | |
| | 2733 | static LILCALLBACK lil_value_t fnc_enveval(lil_t lil, size_t argc, lil_value_t* argv) |
| | 2734 | { |
| | 2735 | lil_value_t r; |
| | 2736 | lil_list_t invars = NULL; |
| | 2737 | lil_list_t outvars = NULL; |
| | 2738 | lil_value_t* varvalues = NULL; |
| | 2739 | int codeindex; |
| | 2740 | size_t i; |
| | 2741 | if (argc < 1) return NULL; |
| | 2742 | if (argc == 1) codeindex = 0; |
| | 2743 | else if (argc >= 2) { |
| | 2744 | invars = lil_subst_to_list(lil, argv[0]); |
| | 2745 | varvalues = malloc(sizeof(lil_value_t)*lil_list_size(invars)); |
| | 2746 | for (i=0; i<lil_list_size(invars); i++) |
| | 2747 | varvalues[i] = lil_clone_value(lil_get_var(lil, lil_to_string(lil_list_get(invars, i)))); |
| | 2748 | if (argc > 2) { |
| | 2749 | codeindex = 2; |
| | 2750 | outvars = lil_subst_to_list(lil, argv[1]); |
| | 2751 | } else { |
| | 2752 | codeindex = 1; |
| | 2753 | } |
| | 2754 | } |
| | 2755 | lil_push_env(lil); |
| | 2756 | if (invars) { |
| | 2757 | for (i=0; i<lil_list_size(invars); i++) { |
| | 2758 | lil_set_var(lil, lil_to_string(lil_list_get(invars, i)), varvalues[i], LIL_SETVAR_LOCAL_NEW); |
| | 2759 | lil_free_value(varvalues[i]); |
| | 2760 | } |
| | 2761 | } |
| | 2762 | r = lil_parse_value(lil, argv[codeindex], 0); |
| | 2763 | if (invars || outvars) { |
| | 2764 | if (outvars) { |
| | 2765 | varvalues = realloc(varvalues, sizeof(lil_value_t)*lil_list_size(outvars)); |
| | 2766 | for (i=0; i<lil_list_size(outvars); i++) |
| | 2767 | varvalues[i] = lil_clone_value(lil_get_var(lil, lil_to_string(lil_list_get(outvars, i)))); |
| | 2768 | } else { |
| | 2769 | for (i=0; i<lil_list_size(invars); i++) |
| | 2770 | varvalues[i] = lil_clone_value(lil_get_var(lil, lil_to_string(lil_list_get(invars, i)))); |
| | 2771 | } |
| | 2772 | } |
| | 2773 | lil_pop_env(lil); |
| | 2774 | if (invars) { |
| | 2775 | if (outvars) { |
| | 2776 | for (i=0; i<lil_list_size(outvars); i++) { |
| | 2777 | lil_set_var(lil, lil_to_string(lil_list_get(outvars, i)), varvalues[i], LIL_SETVAR_LOCAL); |
| | 2778 | lil_free_value(varvalues[i]); |
| | 2779 | } |
| | 2780 | } else { |
| | 2781 | for (i=0; i<lil_list_size(invars); i++) { |
| | 2782 | lil_set_var(lil, lil_to_string(lil_list_get(invars, i)), varvalues[i], LIL_SETVAR_LOCAL); |
| | 2783 | lil_free_value(varvalues[i]); |
| | 2784 | } |
| | 2785 | } |
| | 2786 | lil_free_list(invars); |
| | 2787 | if (outvars) lil_free_list(outvars); |
| | 2788 | free(varvalues); |
| | 2789 | } |
| | 2790 | return r; |
| | 2791 | } |
| | 2792 | |
| | 2793 | static LILCALLBACK lil_value_t fnc_jaileval(lil_t lil, size_t argc, lil_value_t* argv) |
| | 2794 | { |
| | 2795 | size_t i; |
| | 2796 | lil_t sublil; |
| | 2797 | lil_value_t r; |
| | 2798 | size_t base = 0; |
| | 2799 | if (!argc) return NULL; |
| | 2800 | if (!strcmp(lil_to_string(argv[0]), "clean")) { |
| | 2801 | base = 1; |
| | 2802 | if (argc == 1) return NULL; |
| | 2803 | } |
| | 2804 | sublil = lil_new(); |
| | 2805 | if (base != 1) { |
| | 2806 | for (i=lil->syscmds; i<lil->cmds; i++) { |
| | 2807 | lil_func_t fnc = lil->cmd[i]; |
| | 2808 | if (!fnc->proc) continue; |
| | 2809 | lil_register(sublil, fnc->name, fnc->proc); |
| | 2810 | } |
| | 2811 | } |
| | 2812 | r = lil_parse_value(sublil, argv[base], 1); |
| | 2813 | lil_free(sublil); |
| | 2814 | return r; |
| | 2815 | } |
| | 2816 | |
| | 2817 | static LILCALLBACK lil_value_t fnc_count(lil_t lil, size_t argc, lil_value_t* argv) |
| | 2818 | { |
| | 2819 | lil_list_t list; |
| | 2820 | char buff[64]; |
| | 2821 | if (!argc) return alloc_value("0"); |
| | 2822 | list = lil_subst_to_list(lil, argv[0]); |
| | 2823 | sprintf(buff, "%u", (unsigned int)list->c); |
| | 2824 | lil_free_list(list); |
| | 2825 | return alloc_value(buff); |
| | 2826 | } |
| | 2827 | |
| | 2828 | static LILCALLBACK lil_value_t fnc_index(lil_t lil, size_t argc, lil_value_t* argv) |
| | 2829 | { |
| | 2830 | lil_list_t list; |
| | 2831 | size_t index; |
| | 2832 | lil_value_t r; |
| | 2833 | if (argc < 2) return NULL; |
| | 2834 | list = lil_subst_to_list(lil, argv[0]); |
| | 2835 | index = (size_t)lil_to_integer(argv[1]); |
| | 2836 | if (index >= list->c) |
| | 2837 | r = NULL; |
| | 2838 | else |
| | 2839 | r = lil_clone_value(list->v[index]); |
| | 2840 | lil_free_list(list); |
| | 2841 | return r; |
| | 2842 | } |
| | 2843 | |
| | 2844 | static LILCALLBACK lil_value_t fnc_indexof(lil_t lil, size_t argc, lil_value_t* argv) |
| | 2845 | { |
| | 2846 | lil_list_t list; |
| | 2847 | size_t index; |
| | 2848 | lil_value_t r = NULL; |
| | 2849 | if (argc < 2) return NULL; |
| | 2850 | list = lil_subst_to_list(lil, argv[0]); |
| | 2851 | for (index = 0; index < list->c; index++) |
| | 2852 | if (!strcmp(lil_to_string(list->v[index]), lil_to_string(argv[1]))) { |
| | 2853 | r = lil_alloc_integer(index); |
| | 2854 | break; |
| | 2855 | } |
| | 2856 | lil_free_list(list); |
| | 2857 | return r; |
| | 2858 | } |
| | 2859 | |
| | 2860 | static LILCALLBACK lil_value_t fnc_append(lil_t lil, size_t argc, lil_value_t* argv) |
| | 2861 | { |
| | 2862 | lil_list_t list; |
| | 2863 | lil_value_t r; |
| | 2864 | size_t i, base = 1; |
| | 2865 | int access = LIL_SETVAR_LOCAL; |
| | 2866 | const char* varname; |
| | 2867 | if (argc < 2) return NULL; |
| | 2868 | varname = lil_to_string(argv[0]); |
| | 2869 | if (!strcmp(varname, "global")) { |
| | 2870 | if (argc < 3) return NULL; |
| | 2871 | varname = lil_to_string(argv[1]); |
| | 2872 | base = 2; |
| | 2873 | access = LIL_SETVAR_GLOBAL; |
| | 2874 | } |
| | 2875 | list = lil_subst_to_list(lil, lil_get_var(lil, varname)); |
| | 2876 | for (i=base; i<argc; i++) |
| | 2877 | lil_list_append(list, lil_clone_value(argv[i])); |
| | 2878 | r = lil_list_to_value(list, 1); |
| | 2879 | lil_free_list(list); |
| | 2880 | lil_set_var(lil, varname, r, access); |
| | 2881 | return r; |
| | 2882 | } |
| | 2883 | |
| | 2884 | static LILCALLBACK lil_value_t fnc_slice(lil_t lil, size_t argc, lil_value_t* argv) |
| | 2885 | { |
| | 2886 | lil_list_t list, slice; |
| | 2887 | size_t i; |
| | 2888 | lilint_t from, to; |
| | 2889 | lil_value_t r; |
| | 2890 | if (argc < 1) return NULL; |
| | 2891 | if (argc < 2) return lil_clone_value(argv[0]); |
| | 2892 | from = lil_to_integer(argv[1]); |
| | 2893 | if (from < 0) from = 0; |
| | 2894 | list = lil_subst_to_list(lil, argv[0]); |
| | 2895 | to = argc > 2 ? lil_to_integer(argv[2]) : (lilint_t)list->c; |
| | 2896 | if (to > (lilint_t)list->c) to = list->c; |
| | 2897 | if (to < from) to = from; |
| | 2898 | slice = lil_alloc_list(); |
| | 2899 | for (i=(size_t)from; i<(size_t)to; i++) |
| | 2900 | lil_list_append(slice, lil_clone_value(list->v[i])); |
| | 2901 | lil_free_list(list); |
| | 2902 | r = lil_list_to_value(slice, 1); |
| | 2903 | lil_free_list(slice); |
| | 2904 | return r; |
| | 2905 | } |
| | 2906 | |
| | 2907 | static LILCALLBACK lil_value_t fnc_filter(lil_t lil, size_t argc, lil_value_t* argv) |
| | 2908 | { |
| | 2909 | lil_list_t list, filtered; |
| | 2910 | size_t i; |
| | 2911 | lil_value_t r; |
| | 2912 | const char* varname = "x"; |
| | 2913 | int base = 0; |
| | 2914 | if (argc < 1) return NULL; |
| | 2915 | if (argc < 2) return lil_clone_value(argv[0]); |
| | 2916 | if (argc > 2) { |
| | 2917 | base = 1; |
| | 2918 | varname = lil_to_string(argv[0]); |
| | 2919 | } |
| | 2920 | list = lil_subst_to_list(lil, argv[base]); |
| | 2921 | filtered = lil_alloc_list(); |
| | 2922 | for (i=0; i<list->c && !lil->env->breakrun; i++) { |
| | 2923 | lil_set_var(lil, varname, list->v[i], LIL_SETVAR_LOCAL_ONLY); |
| | 2924 | r = lil_eval_expr(lil, argv[base + 1]); |
| | 2925 | if (lil_to_boolean(r)) |
| | 2926 | lil_list_append(filtered, lil_clone_value(list->v[i])); |
| | 2927 | lil_free_value(r); |
| | 2928 | } |
| | 2929 | lil_free_list(list); |
| | 2930 | r = lil_list_to_value(filtered, 1); |
| | 2931 | lil_free_list(filtered); |
| | 2932 | return r; |
| | 2933 | } |
| | 2934 | |
| | 2935 | static LILCALLBACK lil_value_t fnc_list(lil_t lil, size_t argc, lil_value_t* argv) |
| | 2936 | { |
| | 2937 | lil_list_t list = lil_alloc_list(); |
| | 2938 | lil_value_t r; |
| | 2939 | size_t i; |
| | 2940 | for (i=0; i<argc; i++) |
| | 2941 | lil_list_append(list, lil_clone_value(argv[i])); |
| | 2942 | r = lil_list_to_value(list, 1); |
| | 2943 | lil_free_list(list); |
| | 2944 | return r; |
| | 2945 | } |
| | 2946 | |
| | 2947 | static LILCALLBACK lil_value_t fnc_subst(lil_t lil, size_t argc, lil_value_t* argv) |
| | 2948 | { |
| | 2949 | if (argc < 1) return NULL; |
| | 2950 | return lil_subst_to_value(lil, argv[0]); |
| | 2951 | } |
| | 2952 | |
| | 2953 | static LILCALLBACK lil_value_t fnc_concat(lil_t lil, size_t argc, lil_value_t* argv) |
| | 2954 | { |
| | 2955 | lil_list_t list; |
| | 2956 | lil_value_t r, tmp; |
| | 2957 | size_t i; |
| | 2958 | if (argc < 1) return NULL; |
| | 2959 | r = lil_alloc_string(""); |
| | 2960 | for (i=0; i<argc; i++) { |
| | 2961 | list = lil_subst_to_list(lil, argv[i]); |
| | 2962 | tmp = lil_list_to_value(list, 1); |
| | 2963 | lil_free_list(list); |
| | 2964 | lil_append_val(r, tmp); |
| | 2965 | lil_free_value(tmp); |
| | 2966 | } |
| | 2967 | return r; |
| | 2968 | } |
| | 2969 | |
| | 2970 | static LILCALLBACK lil_value_t fnc_foreach(lil_t lil, size_t argc, lil_value_t* argv) |
| | 2971 | { |
| | 2972 | lil_list_t list, rlist; |
| | 2973 | lil_value_t r; |
| | 2974 | size_t i, listidx = 0, codeidx = 1; |
| | 2975 | const char* varname = "i"; |
| | 2976 | if (argc < 2) return NULL; |
| | 2977 | if (argc >= 3) { |
| | 2978 | varname = lil_to_string(argv[0]); |
| | 2979 | listidx = 1; |
| | 2980 | codeidx = 2; |
| | 2981 | } |
| | 2982 | rlist = lil_alloc_list(); |
| | 2983 | list = lil_subst_to_list(lil, argv[listidx]); |
| | 2984 | for (i=0; i<list->c; i++) { |
| | 2985 | lil_value_t rv; |
| | 2986 | lil_set_var(lil, varname, list->v[i], LIL_SETVAR_LOCAL_ONLY); |
| | 2987 | rv = lil_parse_value(lil, argv[codeidx], 0); |
| | 2988 | if (rv->l) lil_list_append(rlist, rv); |
| | 2989 | else lil_free_value(rv); |
| | 2990 | if (lil->env->breakrun || lil->error) break; |
| | 2991 | } |
| | 2992 | r = lil_list_to_value(rlist, 1); |
| | 2993 | lil_free_list(list); |
| | 2994 | lil_free_list(rlist); |
| | 2995 | return r; |
| | 2996 | } |
| | 2997 | |
| | 2998 | static LILCALLBACK lil_value_t fnc_return(lil_t lil, size_t argc, lil_value_t* argv) |
| | 2999 | { |
| | 3000 | lil->env->breakrun = 1; |
| | 3001 | lil_free_value(lil->env->retval); |
| | 3002 | lil->env->retval = argc < 1 ? NULL : lil_clone_value(argv[0]); |
| | 3003 | lil->env->retval_set = 1; |
| | 3004 | return argc < 1 ? NULL : lil_clone_value(argv[0]); |
| | 3005 | } |
| | 3006 | |
| | 3007 | static LILCALLBACK lil_value_t fnc_result(lil_t lil, size_t argc, lil_value_t* argv) |
| | 3008 | { |
| | 3009 | if (argc > 0) { |
| | 3010 | lil_free_value(lil->env->retval); |
| | 3011 | lil->env->retval = lil_clone_value(argv[0]); |
| | 3012 | lil->env->retval_set = 1; |
| | 3013 | } |
| | 3014 | return lil->env->retval_set ? lil_clone_value(lil->env->retval) : NULL; |
| | 3015 | } |
| | 3016 | |
| | 3017 | static LILCALLBACK lil_value_t fnc_expr(lil_t lil, size_t argc, lil_value_t* argv) |
| | 3018 | { |
| | 3019 | if (argc == 1) return lil_eval_expr(lil, argv[0]); |
| | 3020 | if (argc > 1) { |
| | 3021 | lil_value_t val = alloc_value(NULL), r; |
| | 3022 | size_t i; |
| | 3023 | for (i=0; i<argc; i++) { |
| | 3024 | if (i) lil_append_char(val, ' '); |
| | 3025 | lil_append_val(val, argv[i]); |
| | 3026 | } |
| | 3027 | r = lil_eval_expr(lil, val); |
| | 3028 | lil_free_value(val); |
| | 3029 | return r; |
| | 3030 | } |
| | 3031 | return NULL; |
| | 3032 | } |
| | 3033 | |
| | 3034 | static lil_value_t real_inc(lil_t lil, const char* varname, float v) |
| | 3035 | { |
| | 3036 | lil_value_t pv = lil_get_var(lil, varname); |
| | 3037 | double dv = lil_to_double(pv) + v; |
| | 3038 | if (fmod(dv, 1)) |
| | 3039 | pv = lil_alloc_double(dv); |
| | 3040 | else |
| | 3041 | pv = lil_alloc_integer((lilint_t)dv); |
| | 3042 | lil_set_var(lil, varname, pv, LIL_SETVAR_LOCAL); |
| | 3043 | return pv; |
| | 3044 | } |
| | 3045 | |
| | 3046 | static LILCALLBACK lil_value_t fnc_inc(lil_t lil, size_t argc, lil_value_t* argv) |
| | 3047 | { |
| | 3048 | if (argc < 1) return NULL; |
| | 3049 | return real_inc(lil, lil_to_string(argv[0]), argc > 1 ? lil_to_double(argv[1]) : 1); |
| | 3050 | } |
| | 3051 | |
| | 3052 | static LILCALLBACK lil_value_t fnc_dec(lil_t lil, size_t argc, lil_value_t* argv) |
| | 3053 | { |
| | 3054 | if (argc < 1) return NULL; |
| | 3055 | return real_inc(lil, lil_to_string(argv[0]), -(argc > 1 ? lil_to_double(argv[1]) : 1)); |
| | 3056 | } |
| | 3057 | |
| | 3058 | static LILCALLBACK lil_value_t fnc_read(lil_t lil, size_t argc, lil_value_t* argv) |
| | 3059 | { |
| | 3060 | FILE* f; |
| | 3061 | size_t size; |
| | 3062 | char* buffer; |
| | 3063 | lil_value_t r; |
| | 3064 | if (argc < 1) return NULL; |
| | 3065 | if (lil->callback[LIL_CALLBACK_READ]) { |
| | 3066 | lil_read_callback_proc_t proc = (lil_read_callback_proc_t) lil->callback[LIL_CALLBACK_READ]; |
| | 3067 | buffer = proc(lil, lil_to_string(argv[0])); |
| | 3068 | } else { |
| | 3069 | f = fopen(lil_to_string(argv[0]), "rb"); |
| | 3070 | if (!f) return NULL; |
| | 3071 | fseek(f, 0, SEEK_END); |
| | 3072 | size = ftell(f); |
| | 3073 | fseek(f, 0, SEEK_SET); |
| | 3074 | buffer = malloc(size + 1); |
| | 3075 | fread(buffer, 1, size, f); |
| | 3076 | buffer[size] = 0; |
| | 3077 | fclose(f); |
| | 3078 | } |
| | 3079 | r = lil_alloc_string(buffer); |
| | 3080 | free(buffer); |
| | 3081 | return r; |
| | 3082 | } |
| | 3083 | |
| | 3084 | static LILCALLBACK lil_value_t fnc_store(lil_t lil, size_t argc, lil_value_t* argv) |
| | 3085 | { |
| | 3086 | FILE* f; |
| | 3087 | const char* buffer; |
| | 3088 | if (argc < 2) return NULL; |
| | 3089 | if (lil->callback[LIL_CALLBACK_STORE]) { |
| | 3090 | lil_store_callback_proc_t proc = (lil_store_callback_proc_t)lil->callback[LIL_CALLBACK_STORE]; |
| | 3091 | proc(lil, lil_to_string(argv[0]), lil_to_string(argv[1])); |
| | 3092 | } else { |
| | 3093 | f = fopen(lil_to_string(argv[0]), "wb"); |
| | 3094 | if (!f) return NULL; |
| | 3095 | buffer = lil_to_string(argv[1]); |
| | 3096 | fwrite(buffer, 1, strlen(buffer), f); |
| | 3097 | fclose(f); |
| | 3098 | } |
| | 3099 | return lil_clone_value(argv[1]); |
| | 3100 | } |
| | 3101 | |
| | 3102 | static LILCALLBACK lil_value_t fnc_if(lil_t lil, size_t argc, lil_value_t* argv) |
| | 3103 | { |
| | 3104 | lil_value_t val, r = NULL; |
| | 3105 | int base = 0, not = 0, v; |
| | 3106 | if (argc < 1) return NULL; |
| | 3107 | if (!strcmp(lil_to_string(argv[0]), "not")) base = not = 1; |
| | 3108 | if (argc < (size_t)base + 2) return NULL; |
| | 3109 | val = lil_eval_expr(lil, argv[base]); |
| | 3110 | if (!val || lil->error) return NULL; |
| | 3111 | v = lil_to_boolean(val); |
| | 3112 | if (not) v = !v; |
| | 3113 | if (v) { |
| | 3114 | r = lil_parse_value(lil, argv[base + 1], 0); |
| | 3115 | } else if (argc > (size_t)base + 2) { |
| | 3116 | r = lil_parse_value(lil, argv[base + 2], 0); |
| | 3117 | } |
| | 3118 | lil_free_value(val); |
| | 3119 | return r; |
| | 3120 | } |
| | 3121 | |
| | 3122 | static LILCALLBACK lil_value_t fnc_while(lil_t lil, size_t argc, lil_value_t* argv) |
| | 3123 | { |
| | 3124 | lil_value_t val, r = NULL; |
| | 3125 | int base = 0, not = 0, v; |
| | 3126 | if (argc < 1) return NULL; |
| | 3127 | if (!strcmp(lil_to_string(argv[0]), "not")) base = not = 1; |
| | 3128 | if (argc < (size_t)base + 2) return NULL; |
| | 3129 | while (!lil->error && !lil->env->breakrun) { |
| | 3130 | val = lil_eval_expr(lil, argv[base]); |
| | 3131 | if (!val || lil->error) return NULL; |
| | 3132 | v = lil_to_boolean(val); |
| | 3133 | if (not) v = !v; |
| | 3134 | if (!v) { |
| | 3135 | lil_free_value(val); |
| | 3136 | break; |
| | 3137 | } |
| | 3138 | if (r) lil_free_value(r); |
| | 3139 | r = lil_parse_value(lil, argv[base + 1], 0); |
| | 3140 | lil_free_value(val); |
| | 3141 | } |
| | 3142 | return r; |
| | 3143 | } |
| | 3144 | |
| | 3145 | static LILCALLBACK lil_value_t fnc_for(lil_t lil, size_t argc, lil_value_t* argv) |
| | 3146 | { |
| | 3147 | lil_value_t val, r = NULL; |
| | 3148 | if (argc < 4) return NULL; |
| | 3149 | lil_free_value(lil_parse_value(lil, argv[0], 0)); |
| | 3150 | while (!lil->error && !lil->env->breakrun) { |
| | 3151 | val = lil_eval_expr(lil, argv[1]); |
| | 3152 | if (!val || lil->error) return NULL; |
| | 3153 | if (!lil_to_boolean(val)) { |
| | 3154 | lil_free_value(val); |
| | 3155 | break; |
| | 3156 | } |
| | 3157 | if (r) lil_free_value(r); |
| | 3158 | r = lil_parse_value(lil, argv[3], 0); |
| | 3159 | lil_free_value(val); |
| | 3160 | lil_free_value(lil_parse_value(lil, argv[2], 0)); |
| | 3161 | } |
| | 3162 | return r; |
| | 3163 | } |
| | 3164 | |
| | 3165 | |
| | 3166 | static LILCALLBACK lil_value_t fnc_char(lil_t lil, size_t argc, lil_value_t* argv) |
| | 3167 | { |
| | 3168 | char s[2]; |
| | 3169 | if (!argc) return NULL; |
| | 3170 | s[0] = (char)lil_to_integer(argv[0]); |
| | 3171 | s[1] = 0; |
| | 3172 | return lil_alloc_string(s); |
| | 3173 | } |
| | 3174 | |
| | 3175 | static LILCALLBACK lil_value_t fnc_charat(lil_t lil, size_t argc, lil_value_t* argv) |
| | 3176 | { |
| | 3177 | size_t index; |
| | 3178 | char chstr[2]; |
| | 3179 | const char* str; |
| | 3180 | if (argc < 2) return NULL; |
| | 3181 | str = lil_to_string(argv[0]); |
| | 3182 | index = (size_t)lil_to_integer(argv[1]); |
| | 3183 | if (index >= strlen(str)) return NULL; |
| | 3184 | chstr[0] = str[index]; |
| | 3185 | chstr[1] = 0; |
| | 3186 | return lil_alloc_string(chstr); |
| | 3187 | } |
| | 3188 | |
| | 3189 | static LILCALLBACK lil_value_t fnc_codeat(lil_t lil, size_t argc, lil_value_t* argv) |
| | 3190 | { |
| | 3191 | size_t index; |
| | 3192 | const char* str; |
| | 3193 | if (argc < 2) return NULL; |
| | 3194 | str = lil_to_string(argv[0]); |
| | 3195 | index = (size_t)lil_to_integer(argv[1]); |
| | 3196 | if (index >= strlen(str)) return NULL; |
| | 3197 | return lil_alloc_integer(str[index]); |
| | 3198 | } |
| | 3199 | |
| | 3200 | static LILCALLBACK lil_value_t fnc_substr(lil_t lil, size_t argc, lil_value_t* argv) |
| | 3201 | { |
| | 3202 | const char* str; |
| | 3203 | lil_value_t r; |
| | 3204 | size_t start, end, i, slen; |
| | 3205 | if (argc < 2) return NULL; |
| | 3206 | str = lil_to_string(argv[0]); |
| | 3207 | if (!str[0]) return NULL; |
| | 3208 | slen = strlen(str); |
| | 3209 | start = (size_t)atoll(lil_to_string(argv[1])); |
| | 3210 | end = argc > 2 ? (size_t)atoll(lil_to_string(argv[2])) : slen; |
| | 3211 | if (end > slen) end = slen; |
| | 3212 | if (start >= end) return NULL; |
| | 3213 | r = lil_alloc_string(""); |
| | 3214 | for (i=start; i<end; i++) |
| | 3215 | lil_append_char(r, str[i]); |
| | 3216 | return r; |
| | 3217 | } |
| | 3218 | |
| | 3219 | static LILCALLBACK lil_value_t fnc_strpos(lil_t lil, size_t argc, lil_value_t* argv) |
| | 3220 | { |
| | 3221 | const char* hay; |
| | 3222 | const char* str; |
| | 3223 | size_t min = 0; |
| | 3224 | if (argc < 2) return lil_alloc_integer(-1); |
| | 3225 | hay = lil_to_string(argv[0]); |
| | 3226 | if (argc > 2) { |
| | 3227 | min = (size_t)atoll(lil_to_string(argv[2])); |
| | 3228 | if (min >= strlen(hay)) return lil_alloc_integer(-1); |
| | 3229 | } |
| | 3230 | str = strstr(hay + min, lil_to_string(argv[1])); |
| | 3231 | if (!str) return lil_alloc_integer(-1); |
| | 3232 | return lil_alloc_integer(str - hay); |
| | 3233 | } |
| | 3234 | |
| | 3235 | static LILCALLBACK lil_value_t fnc_length(lil_t lil, size_t argc, lil_value_t* argv) |
| | 3236 | { |
| | 3237 | size_t i, total = 0; |
| | 3238 | for (i=0; i<argc; i++) { |
| | 3239 | if (i) total++; |
| | 3240 | total += strlen(lil_to_string(argv[i])); |
| | 3241 | } |
| | 3242 | return lil_alloc_integer((lilint_t)total); |
| | 3243 | } |
| | 3244 | |
| | 3245 | static lil_value_t real_trim(const char* str, const char* chars, int left, int right) |
| | 3246 | { |
| | 3247 | int base = 0; |
| | 3248 | lil_value_t r = NULL; |
| | 3249 | if (left) { |
| | 3250 | while (str[base] && strchr(chars, str[base])) base++; |
| | 3251 | if (!right) r = lil_alloc_string(str[base] ? str + base : NULL); |
| | 3252 | } |
| | 3253 | if (right) { |
| | 3254 | size_t len; |
| | 3255 | char* s; |
| | 3256 | s = strclone(str + base); |
| | 3257 | len = strlen(s); |
| | 3258 | while (len && strchr(chars, s[len - 1])) len--; |
| | 3259 | s[len] = 0; |
| | 3260 | r = lil_alloc_string(s); |
| | 3261 | free(s); |
| | 3262 | } |
| | 3263 | return r; |
| | 3264 | } |
| | 3265 | |
| | 3266 | static LILCALLBACK lil_value_t fnc_trim(lil_t lil, size_t argc, lil_value_t* argv) |
| | 3267 | { |
| | 3268 | if (!argc) return NULL; |
| | 3269 | return real_trim(lil_to_string(argv[0]), argc < 2 ? " \f\n\r\t\v" : lil_to_string(argv[1]), 1, 1); |
| | 3270 | } |
| | 3271 | |
| | 3272 | static LILCALLBACK lil_value_t fnc_ltrim(lil_t lil, size_t argc, lil_value_t* argv) |
| | 3273 | { |
| | 3274 | if (!argc) return NULL; |
| | 3275 | return real_trim(lil_to_string(argv[0]), argc < 2 ? " \f\n\r\t\v" : lil_to_string(argv[1]), 1, 0); |
| | 3276 | } |
| | 3277 | |
| | 3278 | static LILCALLBACK lil_value_t fnc_rtrim(lil_t lil, size_t argc, lil_value_t* argv) |
| | 3279 | { |
| | 3280 | if (!argc) return NULL; |
| | 3281 | return real_trim(lil_to_string(argv[0]), argc < 2 ? " \f\n\r\t\v" : lil_to_string(argv[1]), 0, 1); |
| | 3282 | } |
| | 3283 | |
| | 3284 | static LILCALLBACK lil_value_t fnc_strcmp(lil_t lil, size_t argc, lil_value_t* argv) |
| | 3285 | { |
| | 3286 | if (argc < 2) return NULL; |
| | 3287 | return lil_alloc_integer(strcmp(lil_to_string(argv[0]), lil_to_string(argv[1]))); |
| | 3288 | } |
| | 3289 | |
| | 3290 | static LILCALLBACK lil_value_t fnc_streq(lil_t lil, size_t argc, lil_value_t* argv) |
| | 3291 | { |
| | 3292 | if (argc < 2) return NULL; |
| | 3293 | return lil_alloc_integer(strcmp(lil_to_string(argv[0]), lil_to_string(argv[1]))?0:1); |
| | 3294 | } |
| | 3295 | |
| | 3296 | static LILCALLBACK lil_value_t fnc_repstr(lil_t lil, size_t argc, lil_value_t* argv) |
| | 3297 | { |
| | 3298 | const char* from; |
| | 3299 | const char* to; |
| | 3300 | char* src; |
| | 3301 | const char* sub; |
| | 3302 | size_t idx; |
| | 3303 | size_t fromlen; |
| | 3304 | size_t tolen; |
| | 3305 | size_t srclen; |
| | 3306 | lil_value_t r; |
| | 3307 | if (argc < 1) return NULL; |
| | 3308 | if (argc < 3) return lil_clone_value(argv[0]); |
| | 3309 | from = lil_to_string(argv[1]); |
| | 3310 | to = lil_to_string(argv[2]); |
| | 3311 | if (!from[0]) return NULL; |
| | 3312 | src = strclone(lil_to_string(argv[0])); |
| | 3313 | srclen = strlen(src); |
| | 3314 | fromlen = strlen(from); |
| | 3315 | tolen = strlen(to); |
| | 3316 | while ((sub = strstr(src, from))) { |
| | 3317 | char* newsrc = malloc(srclen - fromlen + tolen + 1); |
| | 3318 | idx = sub - src; |
| | 3319 | if (idx) memcpy(newsrc, src, idx); |
| | 3320 | memcpy(newsrc + idx, to, tolen); |
| | 3321 | memcpy(newsrc + idx + tolen, src + idx + fromlen, srclen - idx - fromlen); |
| | 3322 | srclen = srclen - fromlen + tolen; |
| | 3323 | free(src); |
| | 3324 | src = newsrc; |
| | 3325 | src[srclen] = 0; |
| | 3326 | } |
| | 3327 | r = lil_alloc_string(src); |
| | 3328 | free(src); |
| | 3329 | return r; |
| | 3330 | } |
| | 3331 | |
| | 3332 | static LILCALLBACK lil_value_t fnc_split(lil_t lil, size_t argc, lil_value_t* argv) |
| | 3333 | { |
| | 3334 | lil_list_t list; |
| | 3335 | const char* sep = " "; |
| | 3336 | size_t i; |
| | 3337 | lil_value_t val; |
| | 3338 | const char* str; |
| | 3339 | if (argc == 0) return NULL; |
| | 3340 | if (argc > 1) { |
| | 3341 | sep = lil_to_string(argv[1]); |
| | 3342 | if (!sep || !sep[0]) return lil_clone_value(argv[0]); |
| | 3343 | } |
| | 3344 | val = lil_alloc_string(""); |
| | 3345 | str = lil_to_string(argv[0]); |
| | 3346 | list = lil_alloc_list(); |
| | 3347 | for (i=0; str[i]; i++) { |
| | 3348 | if (strchr(sep, str[i])) { |
| | 3349 | lil_list_append(list, val); |
| | 3350 | val = lil_alloc_string(""); |
| | 3351 | } else { |
| | 3352 | lil_append_char(val, str[i]); |
| | 3353 | } |
| | 3354 | } |
| | 3355 | lil_list_append(list, val); |
| | 3356 | val = lil_list_to_value(list, 1); |
| | 3357 | lil_free_list(list); |
| | 3358 | return val; |
| | 3359 | } |
| | 3360 | |
| | 3361 | static LILCALLBACK lil_value_t fnc_try(lil_t lil, size_t argc, lil_value_t* argv) |
| | 3362 | { |
| | 3363 | lil_value_t r; |
| | 3364 | if (argc < 1) return NULL; |
| | 3365 | if (lil->error) return NULL; |
| | 3366 | r = lil_parse_value(lil, argv[0], 0); |
| | 3367 | if (lil->error) { |
| | 3368 | lil->error = ERROR_NOERROR; |
| | 3369 | lil_free_value(r); |
| | 3370 | if (argc > 1) r = lil_parse_value(lil, argv[1], 0); |
| | 3371 | else r = 0; |
| | 3372 | } |
| | 3373 | return r; |
| | 3374 | } |
| | 3375 | |
| | 3376 | static LILCALLBACK lil_value_t fnc_error(lil_t lil, size_t argc, lil_value_t* argv) |
| | 3377 | { |
| | 3378 | lil_set_error(lil, argc > 0 ? lil_to_string(argv[0]) : NULL); |
| | 3379 | return NULL; |
| | 3380 | } |
| | 3381 | |
| | 3382 | static LILCALLBACK lil_value_t fnc_exit(lil_t lil, size_t argc, lil_value_t* argv) |
| | 3383 | { |
| | 3384 | if (lil->callback[LIL_CALLBACK_EXIT]) { |
| | 3385 | lil_exit_callback_proc_t proc = (lil_exit_callback_proc_t)lil->callback[LIL_CALLBACK_EXIT]; |
| | 3386 | proc(lil, argc > 0 ? argv[0] : NULL); |
| | 3387 | } |
| | 3388 | return NULL; |
| | 3389 | } |
| | 3390 | |
| | 3391 | static LILCALLBACK lil_value_t fnc_source(lil_t lil, size_t argc, lil_value_t* argv) |
| | 3392 | { |
| | 3393 | FILE* f; |
| | 3394 | size_t size; |
| | 3395 | char* buffer; |
| | 3396 | lil_value_t r; |
| | 3397 | if (argc < 1) return NULL; |
| | 3398 | if (lil->callback[LIL_CALLBACK_SOURCE]) { |
| | 3399 | lil_source_callback_proc_t proc = (lil_source_callback_proc_t)lil->callback[LIL_CALLBACK_SOURCE]; |
| | 3400 | buffer = proc(lil, lil_to_string(argv[0])); |
| | 3401 | } else if (lil->callback[LIL_CALLBACK_READ]) { |
| | 3402 | lil_read_callback_proc_t proc = (lil_read_callback_proc_t)lil->callback[LIL_CALLBACK_READ]; |
| | 3403 | buffer = proc(lil, lil_to_string(argv[0])); |
| | 3404 | } else { |
| | 3405 | f = fopen(lil_to_string(argv[0]), "rb"); |
| | 3406 | if (!f) return NULL; |
| | 3407 | fseek(f, 0, SEEK_END); |
| | 3408 | size = ftell(f); |
| | 3409 | fseek(f, 0, SEEK_SET); |
| | 3410 | buffer = malloc(size + 1); |
| | 3411 | fread(buffer, 1, size, f); |
| | 3412 | buffer[size] = 0; |
| | 3413 | fclose(f); |
| | 3414 | } |
| | 3415 | r = lil_parse(lil, buffer, 0, 0); |
| | 3416 | free(buffer); |
| | 3417 | return r; |
| | 3418 | } |
| | 3419 | |
| | 3420 | static LILCALLBACK lil_value_t fnc_lmap(lil_t lil, size_t argc, lil_value_t* argv) |
| | 3421 | { |
| | 3422 | lil_list_t list; |
| | 3423 | size_t i; |
| | 3424 | if (argc < 2) return NULL; |
| | 3425 | list = lil_subst_to_list(lil, argv[0]); |
| | 3426 | for (i=1; i<argc; i++) |
| | 3427 | lil_set_var(lil, lil_to_string(argv[i]), lil_list_get(list, i - 1), LIL_SETVAR_LOCAL); |
| | 3428 | lil_free_list(list); |
| | 3429 | return NULL; |
| | 3430 | } |
| | 3431 | |
| | 3432 | static LILCALLBACK lil_value_t fnc_rand(lil_t lil, size_t argc, lil_value_t* argv) |
| | 3433 | { |
| | 3434 | return lil_alloc_double(rand()/(double)RAND_MAX); |
| | 3435 | } |
| | 3436 | |
| | 3437 | static LILCALLBACK lil_value_t fnc_catcher(lil_t lil, size_t argc, lil_value_t* argv) |
| | 3438 | { |
| | 3439 | if (argc == 0) { |
| | 3440 | return lil_alloc_string(lil->catcher); |
| | 3441 | } else { |
| | 3442 | const char* catcher = lil_to_string(argv[0]); |
| | 3443 | free(lil->catcher); |
| | 3444 | lil->catcher = catcher[0] ? strclone(catcher) : NULL; |
| | 3445 | } |
| | 3446 | return NULL; |
| | 3447 | } |
| | 3448 | |
| | 3449 | static LILCALLBACK lil_value_t fnc_watch(lil_t lil, size_t argc, lil_value_t* argv) |
| | 3450 | { |
| | 3451 | size_t i; |
| | 3452 | const char* wcode; |
| | 3453 | if (argc < 2) return NULL; |
| | 3454 | wcode = lil_to_string(argv[argc - 1]); |
| | 3455 | for (i=0; i + 1 < argc; i++) { |
| | 3456 | const char* vname = lil_to_string(argv[i]); |
| | 3457 | lil_var_t v; |
| | 3458 | if (!vname[0]) continue; |
| | 3459 | v = lil_find_var(lil, lil->env, lil_to_string(argv[i])); |
| | 3460 | if (!v) v = lil_set_var(lil, vname, NULL, LIL_SETVAR_LOCAL_NEW); |
| | 3461 | free(v->w); |
| | 3462 | v->w = wcode[0] ? strclone(wcode) : NULL; |
| | 3463 | } |
| | 3464 | return NULL; |
| | 3465 | } |
| | 3466 | |
| | 3467 | static void register_stdcmds(lil_t lil) |
| | 3468 | { |
| | 3469 | lil_register(lil, "reflect", fnc_reflect); |
| | 3470 | lil_register(lil, "func", fnc_func); |
| | 3471 | lil_register(lil, "rename", fnc_rename); |
| | 3472 | lil_register(lil, "unusedname", fnc_unusedname); |
| | 3473 | lil_register(lil, "quote", fnc_quote); |
| | 3474 | lil_register(lil, "set", fnc_set); |
| | 3475 | lil_register(lil, "local", fnc_local); |
| | 3476 | lil_register(lil, "write", fnc_write); |
| | 3477 | lil_register(lil, "print", fnc_print); |
| | 3478 | lil_register(lil, "eval", fnc_eval); |
| | 3479 | lil_register(lil, "topeval", fnc_topeval); |
| | 3480 | lil_register(lil, "upeval", fnc_upeval); |
| | 3481 | lil_register(lil, "downeval", fnc_downeval); |
| | 3482 | lil_register(lil, "enveval", fnc_enveval); |
| | 3483 | lil_register(lil, "jaileval", fnc_jaileval); |
| | 3484 | lil_register(lil, "count", fnc_count); |
| | 3485 | lil_register(lil, "index", fnc_index); |
| | 3486 | lil_register(lil, "indexof", fnc_indexof); |
| | 3487 | lil_register(lil, "filter", fnc_filter); |
| | 3488 | lil_register(lil, "list", fnc_list); |
| | 3489 | lil_register(lil, "append", fnc_append); |
| | 3490 | lil_register(lil, "slice", fnc_slice); |
| | 3491 | lil_register(lil, "subst", fnc_subst); |
| | 3492 | lil_register(lil, "concat", fnc_concat); |
| | 3493 | lil_register(lil, "foreach", fnc_foreach); |
| | 3494 | lil_register(lil, "return", fnc_return); |
| | 3495 | lil_register(lil, "result", fnc_result); |
| | 3496 | lil_register(lil, "expr", fnc_expr); |
| | 3497 | lil_register(lil, "inc", fnc_inc); |
| | 3498 | lil_register(lil, "dec", fnc_dec); |
| | 3499 | lil_register(lil, "read", fnc_read); |
| | 3500 | lil_register(lil, "store", fnc_store); |
| | 3501 | lil_register(lil, "if", fnc_if); |
| | 3502 | lil_register(lil, "while", fnc_while); |
| | 3503 | lil_register(lil, "for", fnc_for); |
| | 3504 | lil_register(lil, "char", fnc_char); |
| | 3505 | lil_register(lil, "charat", fnc_charat); |
| | 3506 | lil_register(lil, "codeat", fnc_codeat); |
| | 3507 | lil_register(lil, "substr", fnc_substr); |
| | 3508 | lil_register(lil, "strpos", fnc_strpos); |
| | 3509 | lil_register(lil, "length", fnc_length); |
| | 3510 | lil_register(lil, "trim", fnc_trim); |
| | 3511 | lil_register(lil, "ltrim", fnc_ltrim); |
| | 3512 | lil_register(lil, "rtrim", fnc_rtrim); |
| | 3513 | lil_register(lil, "strcmp", fnc_strcmp); |
| | 3514 | lil_register(lil, "streq", fnc_streq); |
| | 3515 | lil_register(lil, "repstr", fnc_repstr); |
| | 3516 | lil_register(lil, "split", fnc_split); |
| | 3517 | lil_register(lil, "try", fnc_try); |
| | 3518 | lil_register(lil, "error", fnc_error); |
| | 3519 | lil_register(lil, "exit", fnc_exit); |
| | 3520 | lil_register(lil, "source", fnc_source); |
| | 3521 | lil_register(lil, "lmap", fnc_lmap); |
| | 3522 | lil_register(lil, "rand", fnc_rand); |
| | 3523 | lil_register(lil, "catcher", fnc_catcher); |
| | 3524 | lil_register(lil, "watch", fnc_watch); |
| | 3525 | lil->syscmds = lil->cmds; |
| | 3526 | } |