-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsimpletinyscript.h
More file actions
1557 lines (1467 loc) · 72.2 KB
/
simpletinyscript.h
File metadata and controls
1557 lines (1467 loc) · 72.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/* this file is released into the public domain */
#ifndef SIMPLETINYSCRIPT_H__
#define SIMPLETINYSCRIPT_H__
/* syntax
*
* comment #..\n,\r
*
* value literals:
* string: "...", anything that isnt whitespace or any other literal
* - an identifier is a string with a $ prefix
* number: (+,-)0-9,.
* end of expression: \n,\r,;
* list: [...], {...}, (...)
*
*/
enum sts_value_types
{
STS_EXTERNAL,
STS_NIL,
STS_NUMBER,
STS_STRING,
STS_ARRAY,
STS_FUNCTION,
STS_BOOLEAN
};
enum sts_node_types
{
STS_NODE_EXPRESSION,
STS_NODE_VALUE,
STS_NODE_IDENTIFIER
};
enum sts_row_types
{
STS_ROW_VALUE,
STS_ROW_VOID
};
/* typedefs */
typedef struct sts_script_t sts_script_t;
typedef struct sts_value_t sts_value_t;
typedef struct sts_node_t sts_node_t;
typedef struct sts_ast_container_t sts_ast_container_t;
typedef struct sts_name_container_t sts_name_container_t;
typedef struct sts_map_row_t sts_map_row_t;
typedef struct sts_scope_t sts_scope_t;
typedef sts_value_t *(*sts_router_t)(sts_script_t *script, sts_value_t *action, sts_node_t *args, sts_scope_t *locals, sts_value_t **previous);
/* structures */
struct sts_scope_t
{
sts_scope_t *uplevel;
sts_map_row_t *locals;
};
struct sts_map_row_t
{
sts_map_row_t *next;
unsigned int hash;
char type;
void *value;
};
struct sts_node_t
{
sts_node_t *next, *child;
char type;
sts_value_t *value;
unsigned int line;
sts_name_container_t *name;
#ifdef STS_GOTO_JIT
void *label;
sts_router_t router_id;
#endif
};
struct sts_ast_container_t
{
sts_node_t *node;
unsigned int references;
};
struct sts_name_container_t
{
char *script_name;
unsigned int references;
};
struct sts_value_t
{
char type, readonly:1;
unsigned int references;
union
{
struct
{
void *data_ptr, *data_other; /* able to store qualities about the external type without having to make a container */
int (*refdec)(sts_script_t *script, sts_value_t *value);
void (*refinc)(sts_script_t *script, sts_value_t *value);
} external;
double number;
char boolean;
struct
{
char *data;
unsigned int length;
} string;
struct
{
unsigned int length, allocated;
sts_value_t **data;
} array;
struct
{
sts_value_t *argument_identifiers; /* this is an array. Not a double ptr array for a reason */
sts_ast_container_t *body;
} function;
};
};
struct sts_script_t
{
char *name;
void *userdata;
sts_node_t *script;
sts_scope_t *globals;
sts_map_row_t *interned; /* all parsed strings are interned */
char *(*read_file)(sts_script_t *script, char *file, unsigned int *size);
char *(*import_file)(sts_script_t *script, char *file);
sts_value_t *(*router)(sts_script_t *script, sts_value_t *action, sts_node_t *args, sts_scope_t *locals, sts_value_t **previous);
};
/* function prototypes */
/* parse text into a list of values */
sts_node_t *sts_parse(sts_script_t *script, sts_node_t *parent, char *script_text, char *script_name, unsigned int *offset, unsigned int *line);
/* evaluate value/value list */
sts_value_t *sts_eval(sts_script_t *script, sts_node_t *ast, sts_scope_t *locals, sts_value_t **previous, int single, int newscope);
/* cleanup */
int sts_destroy(sts_script_t *script);
/* the default functions and behavior */
sts_value_t *sts_defaults(sts_script_t *script, sts_value_t *action, sts_node_t *args, sts_scope_t *locals, sts_value_t **previous);
/* apply a name container to the ast */
int sts_ast_apply_name(sts_script_t *script, sts_node_t *node, sts_name_container_t *name);
/* copy an ast from the provided node */
sts_node_t *sts_ast_copy(sts_script_t *script, sts_node_t *node);
/* delete an ast */
void sts_ast_delete(sts_script_t *script, sts_node_t *node);
/* decrement references recursively */
int sts_value_reference_decrement(sts_script_t *script, sts_value_t *value);
/* copy values just once or recursively */
int sts_value_copy(sts_script_t *script, sts_value_t *dest, sts_value_t *source, int recursive);
/* test if value is "true" or "false". 1 is true, 0 is false */
int sts_value_test(sts_value_t *value);
/* pass through strings and it'll either pass through the passed string or refdec the passed string */
sts_value_t *sts_value_string_intern(sts_script_t *script, sts_value_t *value);
/* simple hash map functions */
sts_map_row_t *sts_map_add_set(sts_map_row_t **row, void *key, unsigned int key_size, void *value);
sts_map_row_t *sts_map_get(sts_map_row_t **row, void *key, unsigned int key_size);
int sts_map_remove(sts_map_row_t **row, void *key, unsigned int key_size);
/* duplicates chunks of memory */
void *sts_memdup(void *src, unsigned int size);
#endif
#ifdef STS_IMPLEMENTATION
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <math.h>
/* config */
#ifndef STS_ERROR_PRINT
#define STS_ERROR_PRINT fprintf
#endif
#ifndef STS_ERROR_PRINT_ARG0
#define STS_ERROR_PRINT_ARG0 stderr,
#endif
#ifndef STS_ERROR_CONCAT
#define STS_ERROR_CONCAT "\n"
#endif
#define STS_ERROR_SIMPLE(str) STS_ERROR_PRINT(STS_ERROR_PRINT_ARG0 str STS_ERROR_CONCAT)
#ifndef STS_CALLOC
#define STS_CALLOC calloc
#endif
#ifndef STS_REALLOC
#define STS_REALLOC realloc
#endif
#ifndef STS_FNV_PRIME
#define STS_FNV_PRIME 0x01000193u /* 32-bit version */
#endif
#ifndef STS_FNV_OFFSET
#define STS_FNV_OFFSET 0x811c9dc5u /* 32-bit version */
#endif
#ifndef STS_FREE
#define STS_FREE free
#endif
/* util macros */
#define STS_VALUE_REFINC(script_ptr, value_ptr) do{value_ptr->references++;}while(0)
#define STS_CREATE_VALUE(value_ptr) (value_ptr = STS_CALLOC(1, sizeof(sts_value_t)))
#define STS_CREATE_NODE(node_ptr) (node_ptr = STS_CALLOC(1, sizeof(sts_node_t)))
#define STS_CREATE_ROW(row_ptr) (row_ptr = STS_CALLOC(1, sizeof(sts_map_row_t)))
#define STS_ARRAY_RESIZE(value_ptr, size) do{ \
if(!((value_ptr)->array.data = STS_REALLOC((value_ptr)->array.data, (size) * sizeof(sts_value_t **)))) {STS_ERROR_SIMPLE("could not resize array");} \
else (value_ptr)->array.allocated = (size); \
}while(0)
#define STS_ARRAY_APPEND_INSERT(value_ptr, value_insert, position) do{ \
if((value_ptr)->array.length + 1 > (value_ptr)->array.allocated) STS_ARRAY_RESIZE((value_ptr), (value_ptr)->array.length + 1); \
if(position >= (value_ptr)->array.length) (value_ptr)->array.data[(value_ptr)->array.length] = (value_insert); \
else{ memmove(&(value_ptr)->array.data[position + 1], &(value_ptr)->array.data[position], ((value_ptr)->array.length - position) * sizeof(sts_value_t **)); (value_ptr)->array.data[position] = (value_insert);} \
(value_ptr)->array.length++; \
}while(0)
#define STS_ARRAY_REMOVE(script, value_ptr, position, on_error) do{ \
if((position) >= (value_ptr)->array.length){ STS_ERROR_SIMPLE("position out of bounds"); on_error} \
else if((position) == (value_ptr)->array.length - 1){ if(!sts_value_reference_decrement(script, (value_ptr)->array.data[(position)])){ STS_ERROR_SIMPLE("could not refdec value at position specified"); on_error} (value_ptr)->array.length--;} \
else{ if(!sts_value_reference_decrement(script, (value_ptr)->array.data[(position)])){ STS_ERROR_SIMPLE("could not refdec value at position specified"); on_error} memmove(&(value_ptr)->array.data[(position)], &(value_ptr)->array.data[(position) + 1], ((value_ptr)->array.length - ((position) + 1)) * sizeof(sts_value_t **)); (value_ptr)->array.length--;} \
}while(0)
#define STS_VALUE_EXPECT_MUTABLE(value, on_not) do{ \
if((value)->readonly) \
{ \
STS_ERROR_SIMPLE("cannot mutate a readonly value"); \
{on_not;} \
} \
}while(0)
#define STS_HASH(variable, data, size) do{unsigned int i; for(i = 0; i < (size); ++i){(variable) ^= ((char *)(data))[i]; (variable) *= STS_FNV_PRIME;} }while(0)
#define STS_DESTROY_MAP(row, on_error) do{sts_map_row_t *current, *temp; current = row; do{ \
if(current->value && current->type == STS_ROW_VALUE) if(!sts_value_reference_decrement(script, (sts_value_t *)current->value)){STS_ERROR_SIMPLE("could not decrement reference in map"); {on_error}} \
temp = current; current = current->next; \
STS_FREE(temp); \
}while(current); }while(0)
#define STS_STRING_ASSEMBLE(dest, current_size, middle_str, middle_size, end_str, end_size) do{ \
if(!((dest) = STS_REALLOC((dest), (current_size) + (middle_size) + (end_size) + 1))) STS_ERROR_SIMPLE("could not resize assembled string"); \
memmove(&(dest)[current_size], (middle_str), (middle_size)); \
memmove(&(dest)[current_size + (middle_size)], (end_str), (end_size)); \
(dest)[current_size + (middle_size) + (end_size)] = 0x0; \
current_size += (middle_size) + (end_size); \
}while(0)
#define STS_STRING_ASSEMBLE_FMT(dest, current_size, fmt, fmt_arg, end_str, end_size) do{char buf[512]; \
snprintf(buf, 512, (fmt), (fmt_arg)); \
STS_STRING_ASSEMBLE((dest), (current_size), buf, strlen(buf), (end_str), (end_size)); \
}while(0)
#define STS_STRING_UNESCAPE_STRING(string, length) do{ unsigned int i, modified = 0, orig_len = 0; \
for(i = 0; i < (length); ++i){ orig_len = (length); \
if((string)[i] == '\\'){ \
switch((string)[i + 1]){ \
case 'a': ++modified; (string)[i] = 0x07; break; \
case 'b': ++modified; (string)[i] = 0x08; break; \
case 'e': ++modified; (string)[i] = 0x1B; break; \
case 'f': ++modified; (string)[i] = 0x0C; break; \
case 'n': ++modified; (string)[i] = 0x0A; break; \
case 'r': ++modified; (string)[i] = 0x0D; break; \
case 't': ++modified; (string)[i] = 0x09; break; \
case 'v': ++modified; (string)[i] = 0x0B; break; \
case '\\': ++modified; (string)[i] = 0x5C; break; \
case '\'': ++modified; (string)[i] = 0x27; break; \
case '\"': ++modified; (string)[i] = 0x22; break; \
case '\?': ++modified; (string)[i] = 0x3F; break; \
case '0': ++modified; (string)[i] = 0x00; break; \
} \
if(modified){ memmove(&(string)[i + 1], &(string)[i + 2], (length) - (i + 2)); (string)[orig_len - 1] = 0x0; (length)--;} \
} modified = 0; \
} \
}while(0)
#define STS_SCOPE_CREATE (STS_CALLOC(1, sizeof(sts_scope_t)))
/* can be used to initialize a root scope */
#define STS_SCOPE_PUSH(scope, on_error) do{ sts_scope_t *push_placeholder = NULL; \
if(!(scope)){ \
if(!((scope) = STS_SCOPE_CREATE)){ STS_ERROR_SIMPLE("could not initialize scope"); {on_error}} \
}else{ \
push_placeholder = (scope); \
if(!((scope) = STS_SCOPE_CREATE)){ STS_ERROR_SIMPLE("could not initialize new scope stack level"); (scope) = push_placeholder; {on_error}} \
(scope)->uplevel = push_placeholder; \
} \
}while(0)
#define STS_SCOPE_POP(scope, on_error) do{ sts_scope_t *pop_placeholder = NULL; \
if((scope)){ \
if((scope)->locals) STS_DESTROY_MAP((scope)->locals, {on_error}); \
pop_placeholder = (scope)->uplevel; \
STS_FREE((scope)); (scope) = pop_placeholder; \
} \
}while(0)
#define STS_SCOPE_SEARCH(scope, data, size, result, on_error) do{ sts_map_row_t *internal_row; sts_scope_t *internal_temp = (scope); if((scope)){ \
do{ \
if((internal_row = sts_map_get(&internal_temp->locals, (data), (size)))){ \
(result) = internal_row; break; \
} \
} while((internal_temp = internal_temp->uplevel)); \
}}while(0)
/* definitions */
sts_node_t *sts_parse(sts_script_t *script, sts_node_t *parent, char *script_text, char *script_name, unsigned int *offset, unsigned int *line)
{
sts_node_t *container_node = NULL, *container_progress = NULL, *expression_node = NULL, *expression_progress = NULL, *temp_expression_node = NULL;
unsigned int start = 0, break_out = 0;
sts_value_t *value = NULL;
sts_name_container_t *name = NULL;
#define PARSER_ERROR(str) do{ STS_ERROR_PRINT(STS_ERROR_PRINT_ARG0 "parser error: '%s': line %u, " str STS_ERROR_CONCAT, script_name, *line); return NULL;}while(0)
#define PARSER_SKIP_WHITESPACE() while(script_text[*offset] && script_text[*offset] != '\n' && isspace(script_text[*offset]))(*offset)++
#define PARSER_SKIP_NOT_WHITESPACE() while(script_text[*offset] && script_text[(*offset) + 1] && script_text[(*offset) + 1] != ']' && script_text[(*offset) + 1] != '[' && script_text[(*offset) + 1] != ')' && script_text[(*offset) + 1] != '(' && script_text[(*offset) + 1] != '}' && script_text[(*offset) + 1] != '{' && script_text[(*offset) + 1] != ';' && script_text[(*offset) + 1] != '\"' && !isspace(script_text[(*offset) + 1]))(*offset)++
#define PARSER_ADD_EXPRESSION(expr_start, expr_progress, set_value, set_line) do{ sts_node_t *temp_node = NULL; \
if(!expr_progress){expr_start->value = set_value; expr_start->line = set_line; expr_start->type = STS_NODE_VALUE; expr_progress = expr_start;} \
else{ \
if(!STS_CREATE_NODE(temp_node)) PARSER_ERROR("could not create new node in expression"); \
temp_node->value = set_value; temp_node->line = set_line; temp_node->type = STS_NODE_VALUE; expr_progress->next = temp_node; expr_progress = temp_node;} \
}while(0)
#define PARSER_ADD_NODE(cont_start, cont_progress, node, set_line) do{ sts_node_t *temp_node = NULL; \
if(!cont_progress){cont_start->child = node; cont_start->line = set_line; cont_start->type = STS_NODE_EXPRESSION; cont_progress = cont_start;} \
else{ \
if(!STS_CREATE_NODE(temp_node)) PARSER_ERROR("could not create new node in container node list"); \
temp_node->child = node; temp_node->line = set_line, temp_node->type = STS_NODE_EXPRESSION; cont_progress->next = temp_node; cont_progress = temp_node;} \
}while(0)
if(!script->name) script->name = script_name;
if(!STS_CREATE_NODE(container_node)) PARSER_ERROR("could not create container node");
if(!STS_CREATE_NODE(expression_node)) PARSER_ERROR("could not create expression node");
if(!parent)
{
if(!(temp_expression_node = sts_parse(script, container_node, script_text, script_name, offset, line)))
PARSER_ERROR("could not parse script");
container_node->child = temp_expression_node;
STS_FREE(expression_node);
if(!(name = calloc(1, sizeof(sts_name_container_t))) || !(name->script_name = sts_memdup(script_name, strlen(script_name))) || sts_ast_apply_name(script, container_node, name)) PARSER_ERROR("could not apply the name container to the ast");
return container_node;
}
do
{
PARSER_SKIP_WHITESPACE();
if(!script_text[*offset]) break; /* in case there was whitespace before the EOF */
switch(script_text[*offset])
{
case '#': while(script_text[(*offset) + 1] && script_text[(*offset) + 1] != '\n')(*offset)++; break;
case '\\': /* let expressions continue onto the next line */
if(script_text[(*offset) + 1] == '\r' && script_text[(*offset) + 2] == '\n'){ (*offset) += 2; (*line)++;}
else if(script_text[(*offset) + 1] == '\n'){ (*offset)++; (*line)++;}
break;
case '\n': (*line)++;
case ';': /* append in progress expression node to container node */
/* printf("expression ended\n"); */
if(!expression_progress) continue;
PARSER_ADD_NODE(container_node, container_progress, expression_node, *line);
if(!STS_CREATE_NODE(expression_node)) PARSER_ERROR("could not create expression node");
expression_progress = NULL;
break;
case '\"':
start = ++(*offset);
while(1)
{
while(script_text[*offset] && script_text[*offset] != '\"' && script_text[*offset] != '\n')(*offset)++;
if(script_text[*offset] == '\n') (*line)++;
else if(!script_text[*offset] || ((*offset) > start ? script_text[(*offset) - 1] : 0) != '\\') break;
(*offset)++;
}
if(!STS_CREATE_VALUE(value)) PARSER_ERROR("could not create value"); /* create new string value, duplicate the string from the script, and add it to the expression */
if(!(value->string.data = sts_memdup(&script_text[start], *offset - start))) PARSER_ERROR("could not duplicate string for value");
value->string.length = *offset - start;
/* printf("adding string literal '%s'\n", value->string); */
value->type = STS_STRING; value->references = 1; STS_STRING_UNESCAPE_STRING(value->string.data, value->string.length);
if(!(value = sts_value_string_intern(script, value)))
PARSER_ERROR("could not intern string");
PARSER_ADD_EXPRESSION(expression_node, expression_progress, value, *line);
break;
case '(': case '{': case '[':
/* printf("starting new recursive expression\n"); */
++(*offset);
if(!(temp_expression_node = sts_parse(script, expression_progress, script_text, script_name, offset, line)))
PARSER_ERROR("could not parse expression");
PARSER_ADD_NODE(expression_node, expression_progress, temp_expression_node, *line);
break;
case ')': case '}': case ']':/* printf("exitting sub expression\n"); */ break_out++; break;
default: /* test if starting with number OR make a string to the next \0 or whitespace */
if(!STS_CREATE_VALUE(value)) PARSER_ERROR("could not create value");
if(isdigit(script_text[*offset]) || (script_text[*offset] == '-' && isdigit(script_text[(*offset) + 1])) || (script_text[*offset] == '+' && isdigit(script_text[(*offset) + 1])))
{
value->number = strtod(&script_text[*offset], NULL); value->type = STS_NUMBER; value->references = 1;
/* printf("adding num %f\n", value->number); */
PARSER_SKIP_NOT_WHITESPACE();
}
else
{
start = *offset; PARSER_SKIP_NOT_WHITESPACE();
if(!(value->string.data = sts_memdup(&script_text[start], *offset + 1 - start))) PARSER_ERROR("could not duplicate string for value");
value->string.length = *offset + 1 - start;
/* printf("adding str %s\n", value->string); */
value->type = STS_STRING; value->references = 1;
if(!(value = sts_value_string_intern(script, value)))
PARSER_ERROR("could not intern string");
}
PARSER_ADD_EXPRESSION(expression_node, expression_progress, value, *line);
if(value->type == STS_STRING && value->string.length > 1 && value->string.data[0] == '$' && value->string.data[1] != '$'){expression_progress->type = STS_NODE_IDENTIFIER;}
break;
}
if(break_out) break;
} while(script_text[(*offset)++]);
/* add whatever's left of the current expression if there is anything */
if(expression_progress) PARSER_ADD_NODE(container_node, container_progress, expression_node, *line);/*if(!container_progress) STS_FREE(container_node); //if(!expression_progress) STS_FREE(expression_node);*/
else STS_FREE(expression_node);
return container_node;
}
sts_value_t *sts_eval(sts_script_t *script, sts_node_t *ast, sts_scope_t *locals, sts_value_t **previous, int single, int newscope)
{
int created_previous = 0;
sts_map_row_t *row = NULL;
sts_value_t *ret = NULL, *temp = NULL;
#define EVAL_PREVIOUS_REFDEC() if(!sts_value_reference_decrement(script, *previous)) STS_ERROR_SIMPLE("could not decrement references in previous")
#define STS_EVAL_ERROR_PRINT do{ STS_ERROR_PRINT(STS_ERROR_PRINT_ARG0 "eval error: %s: line %u" STS_ERROR_CONCAT, ast->name->script_name, ast->line);}while(0)
if(!script->globals) STS_SCOPE_PUSH(script->globals, {STS_ERROR_SIMPLE("couldnt initialize global scope"); return NULL;}); /* initialize global scope */
if(!locals) locals = script->globals; /* make locals be pretty much globals outside of functions */
if(newscope) STS_SCOPE_PUSH(locals, {STS_ERROR_SIMPLE("could not create new locals in eval"); return NULL;});
if(!previous)
{
if(!STS_CREATE_VALUE(temp)) STS_ERROR_SIMPLE("could not create previous value");
previous = &temp; temp->type = STS_NUMBER; temp->references = 1;
created_previous = 1;
}
switch(ast->type)
{
case STS_NODE_IDENTIFIER: /* search entire scope */
if(strcmp(ast->value->string.data + 1, "nil") != 0 && strcmp(ast->value->string.data + 1, "true") != 0 && strcmp(ast->value->string.data + 1, "false") != 0)
{
STS_SCOPE_SEARCH(locals, ast->value->string.data + 1, ast->value->string.length - 1, row, {});
if(row){ret = row->value; STS_VALUE_REFINC(script, ret);}
}
if(!ret && !strcmp(ast->value->string.data + 1, "nil")){ if(!(STS_CREATE_VALUE(ret))) STS_ERROR_SIMPLE("could not create and initialize nil value"); else{ret->references = 1; ret->type = STS_NIL;} }
else if(!ret && !strcmp(ast->value->string.data + 1, "true")){ if(!(STS_CREATE_VALUE(ret))) STS_ERROR_SIMPLE("could not create and initialize true boolean value"); else{ret->references = 1; ret->type = STS_BOOLEAN; ret->boolean = 1;} }
else if(!ret && !strcmp(ast->value->string.data + 1, "false")){ if(!(STS_CREATE_VALUE(ret))) STS_ERROR_SIMPLE("could not create and initialize false boolean value"); else{ret->references = 1; ret->type = STS_BOOLEAN; ret->boolean = 0;} }
break;
case STS_NODE_VALUE:
ret = ast->value; STS_VALUE_REFINC(script, ret);
break;
case STS_NODE_EXPRESSION:
do
{
if(ret) if(!sts_value_reference_decrement(script, ret)){STS_ERROR_SIMPLE("could not decrement references for previous value"); return NULL;} /* if this is a list of expressions, the previous value needs to be destroyed before another is run */
if(!ast->child){ret = *previous; STS_VALUE_REFINC(script, (*previous)); return ret;} /* substitute the previous value as the return value */
else if(ast->child->type != STS_NODE_EXPRESSION) /* an expression with a starting value */
{
if(!(temp = sts_eval(script, ast->child, locals, previous, single, 0))){STS_ERROR_SIMPLE("could not eval action"); return NULL;} /* make an action value for the router. Because it is evaluated, it means any kind of substitution works */
#ifdef STS_GOTO_JIT
if(ast->child->router_id)
{if(!(ret = ast->child->router_id(script, temp, ast->child, locals, previous))){STS_EVAL_ERROR_PRINT; return NULL;} /* decide what to do based off of the starting value */}
else
#endif
if(!(ret = script->router(script, temp, ast->child, locals, previous))){STS_EVAL_ERROR_PRINT; return NULL;} /* decide what to do based off of the starting value */
if(!sts_value_reference_decrement(script, temp)){STS_ERROR_SIMPLE("could not decrement action references"); return NULL;} /* clean up the action value used to control what the router does */
EVAL_PREVIOUS_REFDEC(); *previous = ret; STS_VALUE_REFINC(script, ret); /* carry the previous value along the list of expressions */
}
else if(ast->child)
if(!(ret = sts_eval(script, ast->child, locals, previous, single, 0))){return NULL;} /* eval the nested expression */
} while((ast = ast->next) && !single);
break;
}
if(created_previous) if(!sts_value_reference_decrement(script, *previous)) {STS_ERROR_SIMPLE("could not decrement previous value references"); return NULL;}
if(newscope) STS_SCOPE_POP(locals, {STS_ERROR_SIMPLE("couldnt pop eval scope");});
if(!ret){ STS_EVAL_ERROR_PRINT;}
return ret;
}
int sts_destroy(sts_script_t *script)
{
if(script->globals) STS_SCOPE_POP(script->globals, {STS_ERROR_SIMPLE("could not clean up globals");});
sts_ast_delete(script, script->script);
if(script->interned) STS_DESTROY_MAP(script->interned, {STS_ERROR_SIMPLE("could not clean up interned string data"); return 0;});
return 1;
}
int sts_value_reference_decrement(sts_script_t *script, sts_value_t *value)
{
unsigned int i;
if(!value) return 0;
if(!value->references || !--value->references)
{
switch(value->type)
{
case STS_ARRAY:
for(i = 0; i < value->array.length; ++i)
if(!sts_value_reference_decrement(script, value->array.data[i])) STS_ERROR_SIMPLE("could not decrement references in array");
STS_FREE(value->array.data);
break;
case STS_STRING:
STS_FREE(value->string.data);
break;
case STS_EXTERNAL:
if(value->external.refdec) value->external.refdec(script, value);
break;
case STS_FUNCTION:
if(value->function.argument_identifiers)
if(!sts_value_reference_decrement(script, value->function.argument_identifiers)) STS_ERROR_SIMPLE("could not decrement references in function arguments array");
if(value->function.body && ((--value->function.body->references) <= 0))
{
sts_ast_delete(script, value->function.body->node);
STS_FREE(value->function.body);
}
break;
}
STS_FREE(value);
}
return 1;
}
sts_value_t *sts_defaults(sts_script_t *script, sts_value_t *action, sts_node_t *args, sts_scope_t *locals, sts_value_t **previous)
{
unsigned int i = 0, can_loop = 0, temp_uint = 0, temp0_uint = 0;
char *temp_str = NULL;
sts_node_t *temp_node = NULL;
sts_map_row_t *row = NULL, *new_locals = NULL;
sts_ast_container_t *temp_container = NULL;
sts_value_t *ret = NULL, *eval_value = NULL, *temp_value_arg = NULL, *temp_value = NULL;
#define EVAL_ARG(argument) do{if(!(eval_value = sts_eval(script, argument, locals, previous, 1, 0))){STS_ERROR_SIMPLE("could not eval argument"); } }while(0)
#define EVAL_ARG_ALL(argument) do{if(!(eval_value = sts_eval(script, argument, locals, previous, 0, 0))){STS_ERROR_SIMPLE("could not eval argument"); } }while(0)
#define VALUE_FROM_NUMBER(value_ptr, set_number) do{if(!(STS_CREATE_VALUE(value_ptr))) STS_ERROR_SIMPLE("could not create value for number"); else{value_ptr->references = 1; value_ptr->type = STS_NUMBER; value_ptr->number = (double)(set_number);} }while(0)
#define VALUE_INIT(value_ptr, set_type) do{if(!(STS_CREATE_VALUE(value_ptr))) STS_ERROR_SIMPLE("could not create and initialize value"); else{value_ptr->references = 1; value_ptr->type = set_type;} }while(0)
#define ACTION(test, str) test(strcmp(str, action->string.data) == 0)
#define ACTION_BEGIN_ARGLOOP while((args = args->next)) \
{ if(!(eval_value = sts_eval(script, args, locals, previous, 1, 0))){STS_ERROR_SIMPLE("could not eval argument in loop"); break;}
#define ACTION_END_ARGLOOP if(!sts_value_reference_decrement(script, eval_value)){STS_ERROR_SIMPLE("could not decrement references in eval argument"); break;} }
#ifdef STS_GOTO_JIT
#define GOTO_LABEL_CAT_(a, b) a ## b
#define GOTO_LABEL_CAT(a, b) GOTO_LABEL_CAT_(a, b)
#define GOTO_SET(id) do{args->label = && GOTO_LABEL_CAT(sts_jit_, __LINE__); args->router_id = (void *)(id); GOTO_LABEL_CAT(sts_jit_, __LINE__):;}while(0)
#define GOTO_JMP(id) do{if(args->router_id == (void *)(id)){goto *(args->label);}}while(0)
#define GOTO_ACTIVATED (args->router_id)
#else
#define GOTO_SET(id)
#define GOTO_JMP(id)
#define GOTO_ACTIVATED 0
#endif
GOTO_JMP(&sts_defaults);
if(!action){STS_ERROR_SIMPLE("action is NULL"); return NULL;}
if(action->type == STS_STRING)
{
ACTION(if, "print")
{
GOTO_SET(&sts_defaults);
ACTION_BEGIN_ARGLOOP
switch(eval_value->type)
{
case STS_NUMBER: STS_STRING_ASSEMBLE_FMT(temp_str, temp_uint, "%1.17g", eval_value->number, " ", 1); break;
case STS_NIL: STS_STRING_ASSEMBLE_FMT(temp_str, temp_uint, "%s", "nil", " ", 1); break;
case STS_ARRAY: STS_STRING_ASSEMBLE_FMT(temp_str, temp_uint, "[array passed and is %u elements long]", eval_value->array.length, " ", 1); break;
case STS_STRING: STS_STRING_ASSEMBLE(temp_str, temp_uint, eval_value->string.data, eval_value->string.length, " ", 1); break;
case STS_EXTERNAL: STS_STRING_ASSEMBLE_FMT(temp_str, temp_uint, "%p", eval_value->external.data_ptr, " ", 1); break;
case STS_FUNCTION: STS_STRING_ASSEMBLE_FMT(temp_str, temp_uint, "[function passed and it takes %u arguments]", eval_value->function.argument_identifiers->array.length, " ", 1); break;
case STS_BOOLEAN: STS_STRING_ASSEMBLE_FMT(temp_str, temp_uint, "%s", eval_value->boolean ? "true" : "false", " ", 1); break;
}
ACTION_END_ARGLOOP
STS_STRING_ASSEMBLE(temp_str, temp_uint, "\n", 1, "", 0);
for(i = 0; i < temp_uint; ++i)
fputc(temp_str[i], stdout);
STS_FREE(temp_str);
VALUE_FROM_NUMBER(ret, 1);
}
ACTION(else if, "pass")
{
GOTO_SET(&sts_defaults);
ACTION_BEGIN_ARGLOOP
if(ret) if(!sts_value_reference_decrement(script, ret)){STS_ERROR_SIMPLE("could not decrement references in pass action"); sts_value_reference_decrement(script, eval_value); return NULL; }
ret = eval_value; STS_VALUE_REFINC(script, ret);
ACTION_END_ARGLOOP
}
ACTION(else if, "string")
{
GOTO_SET(&sts_defaults);
ACTION_BEGIN_ARGLOOP
switch(eval_value->type)
{
case STS_NUMBER: STS_STRING_ASSEMBLE_FMT(temp_str, temp_uint, "%1.17g", eval_value->number, "", 0); break;
case STS_NIL: STS_STRING_ASSEMBLE_FMT(temp_str, temp_uint, "%s", "nil", "", 0); break;
case STS_ARRAY: STS_STRING_ASSEMBLE_FMT(temp_str, temp_uint, "[array passed and is %u elements long]", eval_value->array.length, "", 0); break;
case STS_STRING: STS_STRING_ASSEMBLE(temp_str, temp_uint, eval_value->string.data, eval_value->string.length, "", 0); break;
case STS_EXTERNAL: STS_STRING_ASSEMBLE_FMT(temp_str, temp_uint, "%p", eval_value->external.data_ptr, "", 0); break;
case STS_FUNCTION: STS_STRING_ASSEMBLE_FMT(temp_str, temp_uint, "[function passed and it takes %u arguments]", eval_value->function.argument_identifiers->array.length, "", 0); break;
case STS_BOOLEAN: STS_STRING_ASSEMBLE_FMT(temp_str, temp_uint, "%s", eval_value->boolean ? "true" : "false", "", 0); break;
}
ACTION_END_ARGLOOP
VALUE_INIT(ret, STS_STRING); ret->string.data = temp_str; ret->string.length = temp_uint;
}
ACTION(else if, "global")
{
GOTO_SET(&sts_defaults);
if(args->next)
{
EVAL_ARG(args->next);
if(eval_value->type != STS_STRING){STS_ERROR_SIMPLE("could not lookup global because first argument is not a string"); sts_value_reference_decrement(script, eval_value); return NULL;}
temp_value_arg = eval_value; /* temp value becomes the global name */
if(script->globals) row = sts_map_get(&script->globals->locals, temp_value_arg->string.data, temp_value_arg->string.length); /* get the row if it exists */
if(args->next->next) /* set global */
{
EVAL_ARG(args->next->next);
if(row)
{
if(row->type == STS_ROW_VALUE){if(!sts_value_reference_decrement(script, row->value)){STS_ERROR_SIMPLE("could not decrement references for second argument in global action"); return NULL;}}
VALUE_INIT(temp_value, eval_value->type); if(sts_value_copy(script, temp_value, eval_value, 0)){ STS_ERROR_SIMPLE("could not set a new value to evaluated argument in global action"); return NULL;}
row->value = temp_value; row->type = STS_ROW_VALUE;
ret = temp_value; STS_VALUE_REFINC(script, temp_value);
}
else
{
VALUE_INIT(temp_value, eval_value->type); if(sts_value_copy(script, temp_value, eval_value, 0)){ STS_ERROR_SIMPLE("could not set a new value to evaluated argument in global action"); return NULL;}
if(!(row = sts_map_add_set(&script->globals->locals, temp_value_arg->string.data, temp_value_arg->string.length, temp_value))){STS_ERROR_SIMPLE("could not add value to global in global action"); return NULL;}
row->type = STS_ROW_VALUE;
ret = temp_value; STS_VALUE_REFINC(script, temp_value);
}
if(!sts_value_reference_decrement(script, eval_value)) STS_ERROR_SIMPLE("could not decrement references for second argument in global action");
}
else /* test if global exists */
{
if(row){VALUE_INIT(ret, STS_NUMBER); ret->number = 1.0;}
else{VALUE_INIT(ret, STS_NUMBER); ret->number = 0.0;}
}
if(!sts_value_reference_decrement(script, temp_value_arg)) STS_ERROR_SIMPLE("could not decrement references for first argument in global action");
}
else {STS_ERROR_SIMPLE("global action requires at least 1 argument"); return NULL;}
}
ACTION(else if, "local") /* will only run if locals exist */
{
GOTO_SET(&sts_defaults);
if(args->next)
{
EVAL_ARG(args->next);
if(eval_value->type != STS_STRING){STS_ERROR_SIMPLE("could not lookup local because first argument is not a string"); sts_value_reference_decrement(script, eval_value); return NULL;}
temp_value_arg = eval_value; /* temp value becomes the local name */
if(locals) row = sts_map_get(&locals->locals, temp_value_arg->string.data, temp_value_arg->string.length); /* get the row if it exists */
if(args->next->next) /* set local */
{
EVAL_ARG(args->next->next);
if(row)
{
if(row->type == STS_ROW_VALUE){if(!sts_value_reference_decrement(script, row->value)){STS_ERROR_SIMPLE("could not decrement references for second argument in local action"); return NULL;}}
VALUE_INIT(temp_value, eval_value->type); if(sts_value_copy(script, temp_value, eval_value, 0)){ STS_ERROR_SIMPLE("could not set a new value to evaluated argument in local action"); return NULL;}
row->value = temp_value; row->type = STS_ROW_VALUE;
ret = temp_value; STS_VALUE_REFINC(script, temp_value);
}
else
{
VALUE_INIT(temp_value, eval_value->type); if(sts_value_copy(script, temp_value, eval_value, 0)){ STS_ERROR_SIMPLE("could not set a new value to evaluated argument in local action"); return NULL;}
if(!(row = sts_map_add_set(&locals->locals, temp_value_arg->string.data, temp_value_arg->string.length, temp_value))){STS_ERROR_SIMPLE("could not add value to locals in local action"); return NULL;}
row->type = STS_ROW_VALUE;
ret = temp_value; STS_VALUE_REFINC(script, temp_value);
}
if(!sts_value_reference_decrement(script, eval_value)) STS_ERROR_SIMPLE("could not decrement references for second argument in local action");
}
else /* test if local exists */
{
if(row){VALUE_INIT(ret, STS_NUMBER); ret->number = 1.0;}
else{VALUE_INIT(ret, STS_NUMBER); ret->number = 0.0;}
}
if(!sts_value_reference_decrement(script, temp_value_arg)) STS_ERROR_SIMPLE("could not decrement references for first argument in local action");
}
else {STS_ERROR_SIMPLE("local action requires at least 1 argument"); return NULL;}
}
ACTION(else if, "string-hash")
{
GOTO_SET(&sts_defaults);
if(args->next)
{
EVAL_ARG(args->next); if(eval_value->type == STS_STRING){ temp_uint = STS_FNV_OFFSET; STS_HASH(temp_uint, eval_value->string.data, eval_value->string.length);}
VALUE_FROM_NUMBER(ret, (double)temp_uint);
if(!sts_value_reference_decrement(script, eval_value)) STS_ERROR_SIMPLE("could not decrement references for first argument in typeof action");
}
else {STS_ERROR_SIMPLE("string-hash action requires at least 1 argument"); return NULL;}
}
ACTION(else if, "const")
{
GOTO_SET(&sts_defaults);
if(args->next)
{
EVAL_ARG(args->next);
eval_value->readonly;
ret = eval_value;
}
else {STS_ERROR_SIMPLE("string-hash action requires at least 1 argument"); return NULL;}
}
ACTION(else if, "typeof")
{
GOTO_SET(&sts_defaults);
if(args->next)
{
EVAL_ARG(args->next); switch(eval_value->type)
{
case STS_EXTERNAL: VALUE_INIT(ret, STS_NUMBER); ret->number = (double)STS_EXTERNAL; break;
case STS_NIL: VALUE_INIT(ret, STS_NUMBER); ret->number = (double)STS_NIL; break;
case STS_NUMBER: VALUE_INIT(ret, STS_NUMBER); ret->number = (double)STS_NUMBER; break;
case STS_STRING: VALUE_INIT(ret, STS_NUMBER); ret->number = (double)STS_STRING; break;
case STS_ARRAY: VALUE_INIT(ret, STS_NUMBER); ret->number = (double)STS_ARRAY; break;
case STS_FUNCTION: VALUE_INIT(ret, STS_NUMBER); ret->number = (double)STS_FUNCTION; break;
case STS_BOOLEAN: VALUE_INIT(ret, STS_NUMBER); ret->number = (double)STS_BOOLEAN; break;
}
if(!sts_value_reference_decrement(script, eval_value)) STS_ERROR_SIMPLE("could not decrement references for first argument in typeof action");
}
else {STS_ERROR_SIMPLE("typeof action requires at least 1 argument"); return NULL;}
}
ACTION(else if, "sizeof")
{
GOTO_SET(&sts_defaults);
if(args->next)
{
EVAL_ARG(args->next); switch(eval_value->type)
{
case STS_STRING: VALUE_INIT(ret, STS_NUMBER); ret->number = (double)eval_value->string.length; break;
case STS_ARRAY: VALUE_INIT(ret, STS_NUMBER); ret->number = (double)eval_value->array.length; break;
case STS_FUNCTION: VALUE_INIT(ret, STS_NUMBER); ret->number = (double)eval_value->function.argument_identifiers->array.length; break;
default: VALUE_INIT(ret, STS_NUMBER); ret->number = 1.0;
}
if(!sts_value_reference_decrement(script, eval_value)) STS_ERROR_SIMPLE("could not decrement references for first argument in sizeof action");
}
else {STS_ERROR_SIMPLE("sizeof action requires at least 1 argument"); return NULL;}
}
else if(strcmp("if", action->string.data) == 0 || strcmp("elseif", action->string.data) == 0 || strcmp("loop", action->string.data) == 0)
{
GOTO_SET(&sts_defaults);
if(strcmp("elseif", action->string.data) == 0 && (*previous)->type == STS_NUMBER && (*previous)->number) {VALUE_INIT(ret, STS_NUMBER); ret->number = 1.0; return ret;} /* cascade previous value and only run if zero */
if(strcmp("loop", action->string.data) == 0) can_loop = 1;
if(args->next && args->next->next)
{
do
{
EVAL_ARG(args->next);
if(!sts_value_test(eval_value))
{
if(!sts_value_reference_decrement(script, eval_value)) STS_ERROR_SIMPLE("could not decrement references for first argument in conditional action");
VALUE_INIT(ret, STS_NUMBER); ret->number = 0.0; return ret;
}
if(!sts_value_reference_decrement(script, eval_value)) STS_ERROR_SIMPLE("could not decrement references for first argument in conditional action");
/* the value was interpreted as true and can eval whatever's below */
EVAL_ARG_ALL(args->next->next); if(!sts_value_reference_decrement(script, eval_value)) STS_ERROR_SIMPLE("could not decrement references for evaluated body argument in conditional action");
} while(can_loop);
VALUE_INIT(ret, STS_NUMBER); ret->number = 1.0; return ret;
}
else {STS_ERROR_SIMPLE("conditional action requires at least 2 arguments"); return NULL;}
}
ACTION(else if, "else")
{
GOTO_SET(&sts_defaults);
if((*previous)->type == STS_NUMBER && (*previous)->number) {VALUE_INIT(ret, STS_NUMBER); ret->number = 1.0; return ret;} /* cascade previous value and only run if zero */
if(args->next)
{
EVAL_ARG_ALL(args->next); if(!sts_value_reference_decrement(script, eval_value)) STS_ERROR_SIMPLE("could not decrement references for evaluated body argument in conditional action");
VALUE_INIT(ret, STS_NUMBER); ret->number = 1.0; return ret;
}
else {STS_ERROR_SIMPLE("conditional action requires at least 1 argument"); return NULL;}
}
ACTION(else if, "function")
{
GOTO_SET(&sts_defaults);
if(args->next && args->next->next)
{
EVAL_ARG(args->next);
if(eval_value->type != STS_STRING && eval_value->type != STS_NIL){STS_ERROR_SIMPLE("expected a string or nil for the function name"); sts_value_reference_decrement(script, eval_value); return NULL;}
temp_value_arg = eval_value; /* temp value becomes the function name. If nil/non-string, don't define the function in the global var map */
args = args->next; /* fast forward to next value */
if(!(temp_container = STS_CALLOC(1, sizeof(sts_ast_container_t)))){ STS_ERROR_SIMPLE("could not allocate an ast container"); sts_value_reference_decrement(script, eval_value); return NULL;}
VALUE_INIT(ret, STS_FUNCTION);VALUE_INIT(ret->function.argument_identifiers, STS_ARRAY); temp_container->references = 1;
if(!args->next->next) /* if a function with no arguments */
{
if(!(temp_container->node = sts_ast_copy(script, args->next))) {STS_ERROR_SIMPLE("could not copy ast to function body in function action"); return NULL;}
ret->function.body = temp_container;
}
else
{
ACTION_BEGIN_ARGLOOP
if(eval_value->type == STS_STRING) /* only add argument identifier if a string */
{
VALUE_INIT(temp_value, STS_STRING);
temp_value->string.data = sts_memdup(eval_value->string.data, eval_value->string.length);
temp_value->string.length = eval_value->string.length;
STS_ARRAY_APPEND_INSERT(ret->function.argument_identifiers, temp_value, ret->function.argument_identifiers->array.length);
}
if(!args->next->next) /* the very last argument is the function body. Checked early as to not eval the argument */
{
if(!(temp_container->node = sts_ast_copy(script, args->next))) {STS_ERROR_SIMPLE("could not copy ast to function body in function action"); return NULL;}
ret->function.body = temp_container;
if(!sts_value_reference_decrement(script, eval_value)) STS_ERROR_SIMPLE("could not decrement references for current argument in function action");
break;
}
ACTION_END_ARGLOOP
}
if(temp_value_arg->type == STS_STRING) /* only put the function in local var space if a string for function name */
{
if((row = sts_map_get(&locals->locals, temp_value_arg->string.data, temp_value_arg->string.length))) if(!sts_value_reference_decrement(script, row->value)) STS_ERROR_SIMPLE("could not decrement references for old function value");
sts_map_add_set(&locals->locals, temp_value_arg->string.data, temp_value_arg->string.length, ret);
STS_VALUE_REFINC(script, ret);
}
if(!sts_value_reference_decrement(script, temp_value_arg)) STS_ERROR_SIMPLE("could not decrement references for first argument in function action");
}
else {STS_ERROR_SIMPLE("function action requires at least 2 arguments"); return NULL;}
}
ACTION(else if, "copy")
{
GOTO_SET(&sts_defaults);
if(args->next)
{
EVAL_ARG(args->next); VALUE_INIT(ret, eval_value->type);
if(sts_value_copy(script, ret, eval_value, 1)) {STS_ERROR_SIMPLE("could not copy value in copy action"); return NULL;}
if(!sts_value_reference_decrement(script, eval_value)) STS_ERROR_SIMPLE("could not decrement references for first argument in copy action");
}
else {STS_ERROR_SIMPLE("copy action requires 1 argument"); return NULL;}
}
ACTION(else if, "self-name")
{
GOTO_SET(&sts_defaults);
VALUE_INIT(ret, STS_STRING);
if(!(ret->string.data = sts_memdup(args->name->script_name, strlen(args->name->script_name)))){STS_ERROR_SIMPLE("could not duplicate script string"); return NULL;} ret->string.length = strlen(args->name->script_name);
}
ACTION(else if, "number")
{
GOTO_SET(&sts_defaults);
if(args->next)
{
EVAL_ARG(args->next); if(eval_value->type == STS_STRING){ VALUE_FROM_NUMBER(ret, strtod(eval_value->string.data, NULL)); }
else {STS_ERROR_SIMPLE("number action requires the argument to be a string"); sts_value_reference_decrement(script, eval_value); return NULL;}
if(!sts_value_reference_decrement(script, eval_value)) STS_ERROR_SIMPLE("could not decrement references for first argument in number action");
}
else {STS_ERROR_SIMPLE("number action requires 1 argument string"); return NULL;}
}
/* undo char (make string from number as char, not as a number). I dont like this syntax but sts is on its way out in my internal usage so i dont care that much */
ACTION(else if, "asc")
{
GOTO_SET(&sts_defaults);
if(args->next)
{
EVAL_ARG(args->next); if(eval_value->type == STS_NUMBER){ STS_STRING_ASSEMBLE_FMT(temp_str, temp_uint, "%c", (int)eval_value->number, "", 0); VALUE_INIT(ret, STS_STRING); ret->string.data = temp_str ? temp_str : sts_memdup("\0", 1); ret->string.length = 1; }
else {STS_ERROR_SIMPLE("asc action requires the argument to be a number"); sts_value_reference_decrement(script, eval_value); return NULL;}
if(!sts_value_reference_decrement(script, eval_value)) STS_ERROR_SIMPLE("could not decrement references for first argument in asc action");
}
else {STS_ERROR_SIMPLE("asc action requires 1 argument number"); return NULL;}
}
ACTION(else if, "char")
{
GOTO_SET(&sts_defaults);
if(args->next && args->next->next)
{
EVAL_ARG(args->next); temp_value_arg = eval_value;
EVAL_ARG(args->next->next);
if(temp_value_arg->type == STS_STRING && eval_value->type == STS_NUMBER && (unsigned int)eval_value->number < temp_value_arg->string.length){ VALUE_FROM_NUMBER(ret, temp_value_arg->string.data[(unsigned int)eval_value->number]); }
else {STS_ERROR_SIMPLE("char action requires the arguments to be a string and a number to fall within the bounds of the string"); sts_value_reference_decrement(script, temp_value_arg); sts_value_reference_decrement(script, eval_value); return NULL;}
if(!sts_value_reference_decrement(script, temp_value_arg)) STS_ERROR_SIMPLE("could not decrement references for first argument in char action");
if(!sts_value_reference_decrement(script, eval_value)) STS_ERROR_SIMPLE("could not decrement references for second argument in char action");
}
else {STS_ERROR_SIMPLE("char action requires 1 argument string and 1 number argument"); return NULL;}
}
ACTION(else if, "get") /* indexes arrays and gets members for more complex types */
{
GOTO_SET(&sts_defaults);
if(args->next && args->next->next)
{
EVAL_ARG(args->next); temp_value_arg = eval_value;
EVAL_ARG(args->next->next);
switch(temp_value_arg->type)
{
case STS_ARRAY:
if(eval_value->type == STS_NUMBER && (eval_value->number < temp_value_arg->array.length && eval_value->number >= 0))
{
ret = temp_value_arg->array.data[(unsigned int)eval_value->number]; STS_VALUE_REFINC(script, ret);
}
else {STS_ERROR_SIMPLE("get action cannot index array without a number value and the number must be within the array bounds"); return NULL;}
break;
case STS_STRING:
if(eval_value->type == STS_NUMBER && (eval_value->number < temp_value_arg->string.length && eval_value->number >= 0))
{
VALUE_INIT(ret, STS_STRING);
ret->string.data = sts_memdup(&(temp_value_arg->string.data[(unsigned int)eval_value->number]), 1); ret->string.length = 1;
}
else {STS_ERROR_SIMPLE("get action cannot index string without a number value and the number must be within the string bounds"); return NULL;}
break;
default:
ret = temp_value_arg; STS_VALUE_REFINC(script, ret);
}
if(!sts_value_reference_decrement(script, temp_value_arg)) STS_ERROR_SIMPLE("could not decrement references for first argument in get action");
if(!sts_value_reference_decrement(script, eval_value)) STS_ERROR_SIMPLE("could not decrement references for second argument in get action");
}
else {STS_ERROR_SIMPLE("get action requires at least 2 arguments"); return NULL;}
}
ACTION(else if, "set") /* sets values to other values */
{
GOTO_SET(&sts_defaults);
if(args->next && args->next->next)
{
EVAL_ARG(args->next); temp_value_arg = eval_value;
EVAL_ARG(args->next->next);
if(sts_value_copy(script, temp_value_arg, eval_value, 0)) {STS_ERROR_SIMPLE("could not shallow copy in set action"); return NULL;}
VALUE_FROM_NUMBER(ret, 1.0);
if(!sts_value_reference_decrement(script, temp_value_arg)) STS_ERROR_SIMPLE("could not decrement references for first argument in set action");
if(!sts_value_reference_decrement(script, eval_value)) STS_ERROR_SIMPLE("could not decrement references for second argument in set action");
}
else {STS_ERROR_SIMPLE("set action requires at least 2 arguments"); return NULL;}
}
ACTION(else if, "array") /* creates an array of arguments */
{
GOTO_SET(&sts_defaults);
VALUE_INIT(ret, STS_ARRAY);
ACTION_BEGIN_ARGLOOP
STS_ARRAY_APPEND_INSERT(ret, eval_value, i); STS_VALUE_REFINC(script, eval_value); ++i;
ACTION_END_ARGLOOP
}
ACTION(else if, "remove") /* removes values at array */
{
GOTO_SET(&sts_defaults);
if(args->next && args->next->next)
{
EVAL_ARG(args->next); temp_value_arg = eval_value;
EVAL_ARG(args->next->next);
STS_VALUE_EXPECT_MUTABLE(temp_value_arg, return NULL);
if(temp_value_arg->type != STS_ARRAY){STS_ERROR_SIMPLE("the remove action requires the first argument to be an array"); return NULL;}
if(eval_value->type != STS_NUMBER){STS_ERROR_SIMPLE("the remove action requires the second argument to be an number"); return NULL;}
if(eval_value->number < 0.0 || eval_value->number >= temp_value_arg->array.length){STS_ERROR_SIMPLE("could not remove value at the position requested because it is out of bounds of the array"); return NULL;}
else STS_ARRAY_REMOVE(script, temp_value_arg, (unsigned int)(eval_value->number), {return NULL;});
VALUE_FROM_NUMBER(ret, 1.0);
if(!sts_value_reference_decrement(script, temp_value_arg)) STS_ERROR_SIMPLE("could not decrement references for first argument in remove action");
if(!sts_value_reference_decrement(script, eval_value)) STS_ERROR_SIMPLE("could not decrement references for second argument in remove action");
}
else {STS_ERROR_SIMPLE("remove action requires at least 2 arguments"); return NULL;}
}
ACTION(else if, "insert") /* inserts values at array */
{
GOTO_SET(&sts_defaults);
if(args->next && args->next->next && args->next->next->next)
{
EVAL_ARG(args->next); temp_value_arg = eval_value;
EVAL_ARG(args->next->next); temp_value = eval_value;
EVAL_ARG(args->next->next->next);
STS_VALUE_EXPECT_MUTABLE(temp_value_arg, return NULL);
if(temp_value_arg->type != STS_ARRAY){STS_ERROR_SIMPLE("the insert action requires the first argument to be an array"); return NULL;}
if(temp_value->type != STS_NUMBER){STS_ERROR_SIMPLE("the insert action requires the second argument to be an number"); return NULL;}
if(temp_value->number < 0.0){STS_ERROR_SIMPLE("could not insert value at the position requested because it is below the bounds of the array"); return NULL;}
else { STS_ARRAY_APPEND_INSERT(temp_value_arg, eval_value, (unsigned int)(temp_value->number)); STS_VALUE_REFINC(script, eval_value);}
VALUE_FROM_NUMBER(ret, 1.0);
if(!sts_value_reference_decrement(script, temp_value_arg)) STS_ERROR_SIMPLE("could not decrement references for first argument in insert action");
if(!sts_value_reference_decrement(script, temp_value)) STS_ERROR_SIMPLE("could not decrement references for second argument in insert action");
if(!sts_value_reference_decrement(script, eval_value)) STS_ERROR_SIMPLE("could not decrement references for third argument in insert action");
}
else {STS_ERROR_SIMPLE("insert action requires at least 3 arguments"); return NULL;}
}
ACTION(else if, "replace") /* replaces values in an array */
{
GOTO_SET(&sts_defaults);
if(args->next && args->next->next && args->next->next->next)
{
EVAL_ARG(args->next); temp_value_arg = eval_value;
EVAL_ARG(args->next->next); temp_value = eval_value;
EVAL_ARG(args->next->next->next);
STS_VALUE_EXPECT_MUTABLE(temp_value_arg, return NULL);