1 // Copyright (C) 1995-1999 David Sugar, Tycho Softworks.
2 // Copyright (C) 1999-2005 Open Source Telecom Corp.
3 // Copyright (C) 2005-2014 David Sugar, Tycho Softworks.
4 // Copyright (C) 2015 Cherokees of Idaho.
5 //
6 // This file is part of GNU ccScript.
7 //
8 // GNU ccScript is free software; you can redistribute it and/or modify
9 // it under the terms of the GNU General Public License as published by
10 // the Free Software Foundation; either version 3 of the License, or
11 // (at your option) any later version.
12 //
13 // GNU ccScript is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
16 // GNU General Public License for more details.
17 //
18 // You should have received a copy of the GNU General Public License
19 // along with GNU ccScript. If not, see <http://www.gnu.org/licenses/>.
20
21 #include <ccscript-config.h>
22 #include <ucommon/ucommon.h>
23 #include <ucommon/export.h>
25 #include <ctype.h>
26 #include <new>
27 #include <fstream>
28
29 namespace ucommon {
30
32 static unsigned long icounter = 0;
33
40
41 static bool ideq(const char *id1, const char *id2)
42 {
43 unsigned count = 0;
44
45 if(!id1)
46 id1 = "";
47
48 if(!id2)
49 id2 = "";
50
51 while(id1[count] && id1[count] != ':') {
52 if(id2[count] != id1[count])
53 return false;
54 ++count;
55 }
56 if(id2[count] && id2[count] != ':')
57 return false;
58
59 return true;
60 }
61 static bool iskeyword(const char *str)
62 {
63 if(!str || !*str)
64 return false;
65
66 if(*str == '_')
67 ++str;
68
69 while(*str) {
70 if(!isalpha(*str) && *str != '.')
71 return false;
72 ++str;
73 }
74 return true;
75 }
76
77 static bool isend(const char *str)
78 {
79 if(!str || !*str)
80 return true;
81
82 if(!strncmp("%%", str, 2))
83 return true;
84
85 if(!strncmp("//", str, 2))
86 return true;
87
88 return false;
89 }
90
91 static bool preparse(char **tokens)
92 {
93 if(!tokens || !*tokens)
94 return true;
95
96 while(*tokens && isspace(**tokens))
97 ++*tokens;
98
99 char *str = *tokens;
100 if(isend(str)) {
101 *str = 0;
102 return true;
103 }
104
105 switch(*str) {
106 case '=':
107 if(isspace(str[1]) || (str[1] == '=' && isspace(str[2]))) {
108 --*tokens;
109 **tokens = '?';
110 return true;
111 }
112 return false;
113 #ifdef HAVE_REGEX_H
114 case '~':
115 if(isspace(str[1])) {
116 --*tokens;
117 **tokens = '?';
118 return true;
119 }
120 return false;
121 #endif
122 case '>':
123 if(eq(str, ">>%", 3)) {
124 --*tokens;
125 --str;
126 str[0] = '$';
127 str[3] = ':';
128 return true;
129 }
130 if(isspace(str[1]) || (str[1] == '=' && isspace(str[2]))) {
131 --*tokens;
132 **tokens = '?';
133 return true;
134 }
135 return false;
136 case '$':
137 if(isspace(str[1])) {
138 --*tokens;
139 **tokens = '?';
140 return true;
141 }
142 break;
143 case '?':
144 if(isspace(str[1])) {
145 --*tokens;
146 **tokens = '?';
147 return true;
148 }
149 return false;
150 case '<':
151 if(eq(str, "<<%", 3)) {
152 --*tokens;
153 --str;
154 str[0] = '$';
155 str[3] = ':';
156 return true;
157 }
158 if(isspace(str[1]) || (str[1] == '=' && isspace(str[2])) || (str[1] == '>' && isspace(str[2]))) {
159 --*tokens;
160 **tokens = '?';
161 return true;
162 }
163 return false;
164 case '!':
165 if(str[1] == '=' && isspace(str[2])) {
166 --*tokens;
167 **tokens = '?';
168 return true;
169 }
170 else if(str[1] == '?' && isspace(str[2])) {
171 --*tokens;
172 **tokens = '?';
173 return true;
174 }
175 else if(str[1] == '$' && isspace(str[2])) {
176 --*tokens;
177 **tokens = '?';
178 return true;
179 }
180 else if(str[1] == '~' && isspace(str[2])) {
181 --*tokens;
182 **tokens = '?';
183 return true;
184 }
185 if(isalnum(str[1]))
186 break;
187 return false;
188 case '&':
189 if(str[1] == '&' && isspace(str[2])) {
190 --*tokens;
191 **tokens = '?';
192 return true;
193 }
194 else if(isalnum(str[1])) {
195 *str = '%';
196 break;
197 }
198 return false;
199 case '|':
200 if(str[1] == '|' && isspace(str[2])) {
201 --*tokens;
202 **tokens = '?';
203 return true;
204 }
205 return false;
206 case '/':
207 case '*':
208 if(isspace(str[1]))
209 break;
210 if(str[1] == '=' && isspace(str[2]))
211 break;
212 return false;
213 case ':':
214 if(str[1] == '=' && isspace(str[2]))
215 return true;
216 if(isalnum(str[1]))
217 break;
218 case ',':
219 case '`':
220 case '(':
221 case '[':
222 case ')':
223 case ']':
224 case ';':
225 case '_':
226 return false;
227 case '+':
228 if(eq(str, "++%", 3)) {
229 --*tokens;
230 --str;
231 str[0] = '$';
232 str[3] = ':';
233 return true;
234 }
235 if(isdigit(str[1]) || str[1] == '.') {
236 ++*tokens;
237 return true;
238 }
239 if(isspace(str[1]))
240 break;
241 if(str[1] == '=' && isspace(str[2]))
242 break;
243 return false;
244 case '.':
245 if(isdigit(str[1]))
246 break;
247 return false;
248 case '-':
249 if(eq(str, "--%", 3)) {
250 --*tokens;
251 --str;
252 str[0] = '$';
253 str[3] = ':';
254 return true;
255 }
256 if(isalnum(str[1]) || str[1] == '.')
257 return true;
258 if(isspace(str[1]))
259 break;
260 if(str[1] == '=' && isspace(str[2]))
261 break;
262 return false;
263 case '\'':
264 case '\"':
265 case '{':
266 --str;
267 --*tokens;
268 str[0] = str[1];
269 str[1] = '&';
270 return true;
271 }
272
273 while(*str && (isalnum(*str) || *str == ':' || *str == '.'))
274 ++str;
275
276 // we flip xxx=... into =xxx objects...
277
278 if(*str == '=') {
279 --*tokens;
280 **tokens = '=';
281 *str = ' ';
282 }
283
284 return true;
285 }
286
287 Script::error::error(Script *img, unsigned line, const char *msg) :
289 {
290 errmsg = img->dup(msg);
291 errline = line;
292 filename = img->filename;
293 }
294
295 Script::Script() :
297 {
298 instance = icounter++;
299 errors = 0;
300 loop = 0;
301 lines = 0;
303 global = NULL;
304 headers = NULL;
309
310 if(instance)
311 shell::log(shell::INFO, "creating image instance %lu\n", instance);
312 }
313
315 {
316 purge();
317 shared = NULL;
318 if(instance)
319 shell::log(shell::INFO, "releasing image instance %lu\n", instance);
320 }
321
322 void Script::errlog(unsigned line, const char *fmt, ...)
323 {
324 char text[65];
325 va_list args;
326
327 va_start(args, fmt);
328 vsnprintf(text, sizeof(text), fmt, args);
329
330 ++errors;
333 va_end(args);
334 }
335
337 {
338 while(keyword && keyword->
name) {
339 keyword->
next = keywords;
340 keywords = keyword;
341 ++keyword;
342 }
343 }
344
346 {
348
349 while(keyword != NULL) {
350 if(eq(cmd, keyword->
name))
351 break;
352 keyword = keyword->
next;
353 }
354 return keyword;
355 }
356
358 {
402 {NULL}
403 };
404
405 static bool initial = false;
406
407 if(!initial) {
408 initial = true;
410 }
411 }
412
414 {
416 linked_pointer<header> hp = img->
scripts[path];
417
418 while(is(hp)) {
419 if(eq(hp->name, id))
420 break;
421
422 hp.next();
423 }
424 return *hp;
425 }
426
428 {
429 // linked_pointer<script::strict> sp;
430
431 static unsigned serial;
432
433 char **argv = new char *[256];
434 stringbuf<512> buffer;
435 char localname[128];
437 std::ifstream cf(fn);
439 const char *name = "_init_";
442 char *tokens;
443 const char *token = NULL;
444 char *arg;
445 unsigned lnum = 0;
447 bool section = false;
448 unsigned argc;
449 const char *err, *op;
450 char *assigned;
451 unsigned path;
452 bool indented, formed;
454 bool define = false;
455 bool when = false;
456 bool label = false;
457 bool branching;
458 unsigned pos;
459 const char *cp;
460 bool requires = false;
461 char *ep;
462 size_t len;
463
464 if(!cf.is_open()) {
465 if(merge)
466 return merge;
467 return NULL;
468 }
469
470 if(merge)
471 img = merge;
472 else
474
475 img->shared = cfg;
476 img->filename = strrchr(fn, '/');
477 if(!img->filename)
478 img->filename = strrchr(fn, '\\');
479 if(!img->filename)
480 img->filename = strrchr(fn, ':');
481 if(img->filename)
482 ++img->filename;
483 else
484 img->filename = fn;
485
486 img->filename = img->dup(img->filename);
487 img->serial = ++serial;
488
489 initial:
490 current = NULL;
491 prior = NULL;
492 last = NULL;
493
494 if(img->
first && eq(name,
"_init_")) {
497 while(last && last->
next)
499 }
500 else {
502 scr->
name = img->dup(name);
503 scr->
file = img->dup(img->filename);
510
511 ep = (
char *)strrchr(scr->
file,
'.');
512 if(ep)
513 *ep = 0;
514 }
515
517 img->loop = 0;
518 char *temp;
519
520 // we drop additional initializers during append
521 if(!eq(name, "_init_") || !merge) {
523 scr->enlist(&img->
scripts[path]);
524 }
525
528
529 while(when || label || define || cf.getline(buffer.c_mem(), 512)) {
530 String::fix(buffer);
531 if(define) {
532 indented = true;
533 goto parse;
534 }
535
536 if(when) {
537 indented = true;
538 when = false;
539 goto parse;
540 }
541
542 if(label) {
543 label = false;
544 indented = true;
545 token = String::token(NULL, &tokens, " \t", "{}\'\'\"\"");
546 if(!token)
547 continue;
548 goto parse;
549 }
550
551 img->lines = ++lnum;
552 img->thencheck = false;
553
554 indented = false;
555 tokens = buffer.c_mem();
556 if(isspace(*tokens)) {
557 while(isspace(*tokens))
558 ++tokens;
559 indented = true;
560 if(!preparse(&tokens)) {
561 img->errlog(lnum, "malformed line");
562 continue;
563 }
564 }
565
566 buffer.trim(" \t\r\n");
567
568 // if empty line or comment, continue...
569 if(isend(*buffer))
570 continue;
571
572 tokens = NULL;
573 token = String::token(buffer.c_mem(), &tokens, " \t", "{}\'\'\"\"");
574
575 if(eq(token, "endreq") || eq(token, "endrequires")) {
576 if(indented) {
577 img->errlog(lnum, "endreq cannot be indented");
578 continue;
579 }
580 requires = false;
581 continue;
582 }
583 else if(eq(token, "requires")) {
584 if(indented) {
585 img->errlog(lnum, "requires cannot be indented");
586 continue;
587 }
588 requires = true;
589 while(NULL != (token = String::token(NULL, &tokens, " \t", "{}\'\'\"\""))) {
590 bool rev = false;
591 if(*token == '!') {
592 rev = true;
593 ++token;
594 }
595 keyword =
find(token);
596 invoke =
find(img, token);
597 if(!invoke && is(img->shared))
598 invoke =
find(*(img->shared), token);
599 if(!rev && (keyword || invoke)) {
600 requires = false;
601 break;
602 }
603 if(rev && !keyword && !invoke) {
604 requires = false;
605 break;
606 }
607 }
608 continue;
609 }
610
611 if(requires)
612 continue;
613
614 if(*token == '^' || *token == '-') {
615 if(scr == img->
first) {
616 img->errlog(lnum, "events cannot be in init segment");
617 continue;
618 }
620 if(!prior)
621 prior = current;
622
623 last = NULL;
624
625 if(*token == '^')
626 current->enlist(&scr->
events);
627 else
628 current->enlist(&scr->
methods);
629 current->
name = (
char *)img->dup(++token);
630 current->
first = NULL;
631 continue;
632 }
633
634 if(*token == '@') {
635 section = true;
636 name = token;
637 label = true;
638 goto closure;
639 }
640
641 if(*token == ':') {
642 section = true;
643 snprintf(localname, sizeof(localname), "@%04x%s", serial & 0xffff, token);
644 name = localname;
645 label = true;
646 goto closure;
647 }
648
649 if(eq(token, "template")) {
650 if(section) {
651 img->errlog(lnum, "templates must be before named sections");
652 continue;
653 }
654 if(indented) {
655 img->errlog(lnum, "templates cannot be indented");
656 continue;
657 }
658 token = String::token(NULL, &tokens, " \t", "{}\'\'\"\"");
659 if(!token)
660 img->errlog(lnum, "template must be named");
661 name = token;
662 goto closure;
663 }
664
665 if(eq(token, "define")) {
666 if(section) {
667 img->errlog(lnum, "defines must be before named sections");
668 continue;
669 }
670 if(indented) {
671 img->errlog(lnum, "define cannot be indented");
672 continue;
673 }
674 token = String::token(NULL, &tokens, " \t", "{}\'\'\"\"");
675 keyword =
find(token);
676 if(keyword) {
677 img->errlog(lnum, "cannot redefine existing command");
678 continue;
679 }
680 define = true;
681 name = token;
682 goto closure;
683 }
684 if(!indented) {
685 img->errlog(lnum, "unindented statement");
686 continue;
687 }
688
689 parse:
690 assigned = NULL;
691 op = NULL;
692
693 branching = false;
694
695 if(eq(token, "goto") || eq(token, "gosub"))
696 branching = true;
697
698 if(*token == '%') {
699 assigned = img->dup(token);
700 op = String::token(NULL, &tokens, " \t", "{}\'\'\"\"");
701 if(op && (eq(op, ":=") || eq(op, "+=") || eq(op, "-=") || eq(op, "*=") || eq(op, "/=") || eq(op, "%=") || eq(op, "#=")))
702 token = "expr";
703 else if(op && (eq(op, "=") || eq(op, "==") || eq(op, "$="))) {
704 op = NULL;
705 token = "set";
706 }
707 else if(op && (eq(op, ".") || eq(op, ".="))) {
708 op = NULL;
709 token = "add";
710 }
711 else if(op && (eq(op, ",") || eq(op, ",="))) {
712 op = NULL;
713 token = "pack";
714 }
715 else {
716 img->errlog(lnum, "invalid assignment");
717 continue;
718 }
719 }
720 else if(*token == '$') {
721 assigned = img->dup(token);
722 token = "_ref";
723 }
724 invoke = NULL;
725 if(!iskeyword(token)) {
726 img->errlog(lnum, "invalid keyword");
727 continue;
728 }
729
730 if(define) {
731 define = false;
732 keyword =
find(
"_define");
733 if(!keyword) {
734 img->errlog(lnum, "define unsupported");
735 continue;
736 }
737 }
738 else {
739 keyword =
find(token);
740 if(!keyword && NULL != (invoke =
find(img, token)))
741 keyword =
find(
"_invoke");
742 if(!keyword && is(img->shared) && NULL != (invoke =
find(*(img->shared), token)))
743 keyword =
find(
"_invoke");
744
745 if(!keyword) {
746 img->errlog(lnum, "unknown keyword \"%s\"", token);
747 continue;
748 }
749 }
750
751 // protects in backparse
752 token = img->dup(token);
753
755 memset(line, 0,
sizeof(
line_t));
759 if(invoke)
761 else
763 line->
loop = img->loop;
765
766 formed = true;
767 argc = 0;
768 arg = NULL;
769
770 if(assigned)
771 argv[argc++] = assigned;
772
773 if(op)
774 argv[argc++] = img->dup(op);
775
776 while(argc < 249 && formed) {
777 if(!arg) {
778 formed = preparse(&tokens);
779 if(!formed)
780 break;
781 arg = (char *)String::token(NULL, &tokens, " \t", "{}\'\'\"\"");
782 }
783 if(!arg)
784 break;
785
786 if(eq(token, "if")) {
787 if(eq(arg, "if")) {
788 formed = false;
789 break;
790 }
791 if(eq(arg, "then")) {
792 keyword =
find(
"_ifthen");
794 token = String::token(NULL, &tokens, " \t", "{}\'\'\"\"");
795 if(!token || !keyword || img->thencheck)
796 formed = false;
797 else
798 img->thencheck = when = true;
799 break;
800 }
801 }
802
803 // compute script local label or symbol name...
804 if(*arg == ':' && branching) {
805 snprintf(localname, sizeof(localname), "@%04x%s", serial & 0xffff, arg);
806 arg = localname;
807 }
808 else if(*arg == ':' && isalnum(arg[1])) {
809 snprintf(localname, sizeof(localname), "%c_%04x_.%s",
810 '%', serial & 0xffff, ++arg);
811 arg = localname;
812 }
813
814 ep = strchr(arg, '<');
815 if(*arg == '%' && ep) {
816 len = strlen(arg) + 10;
817 *(ep++) = 0;
818 temp = (char *)img->alloc(len);
819 String::set(temp, len, "$map/");
820 String::add(temp, len, ep);
821 ep = strchr(temp, '>');
822 if(ep)
823 *ep = ':';
824 String::add(temp, len, ++arg);
825 argv[argc++] = temp;
826 arg = NULL;
827 continue;
828 }
829
830 ep = strchr(arg, '[');
831 if(*arg == '%' && ep) {
832 len = strlen(arg) + 10;
833 *(ep++) = 0;
834 temp = (char *)img->alloc(len);
835 if(atoi(ep))
836 String::set(temp, len, "$offset/");
837 else
838 String::set(temp, len, "$find/");
839 String::add(temp, len, ep);
840 ep = strchr(temp, ']');
841 if(ep)
842 *ep = ':';
843 String::add(temp, len, ++arg);
844 argv[argc++] = temp;
845 arg = NULL;
846 continue;
847 }
848
849 ep = strchr(arg, '(');
850 if(*arg == '%' && ep) {
851 len = strlen(arg) + 10;
852 *(ep++) = 0;
853 temp = (char *)img->alloc(len);
854 String::set(temp, len, "$index/");
855 String::add(temp, len, ep);
856 ep = strchr(temp, ')');
857 if(ep)
858 *ep = ':';
859 String::add(temp, len, ++arg);
860 argv[argc++] = temp;
861 arg = NULL;
862 continue;
863 }
864
865 argv[argc++] = img->dup(arg);
866 arg = NULL;
867 }
868
869 if(!formed) {
870 img->errlog(lnum, "malformed statement or argument");
871 continue;
872 }
873
875 argv[argc++] = NULL;
876 line->
argv = (
char **)img->alloc(
sizeof(
char *) * argc);
877 memcpy(line->
argv, argv,
sizeof(
char *) * argc);
878
879 err = (*(keyword->
check))(img, scr, line);
880 if(err) {
881 img->errlog(lnum, "%s", err);
882 continue;
883 }
884
885 pos = 0;
887 cp = line->
argv[pos++];
889 img->errlog(lnum, "undefined symbol reference %s\n", cp);
890 }
891
892 // if compile-time only line, then no runtime method, so we skip...
893
895 continue;
896
897 // after error checking...we may add starting line...
898
899 if(last)
901 else {
902 if(current) {
903 linked_pointer<event> ep = prior;
904 while(is(ep) && *ep != current) {
907 }
908 current->
first = line;
909 }
910 else
911 scr->first = line;
912 }
913 last = line;
914 }
915
916 closure:
917 while(img->loop) {
918 line = img->stack[--img->loop];
919 img->errlog(line->
lnum,
"%s never completed loop", line->
cmd);
920 }
921
922 keyword =
find(
"_close");
923 if(keyword) {
924 err = (*(keyword->
check))(img, scr, scr->
first);
925 if(err)
926 img->errlog(lnum, "%s", err);
927 }
928
929 // sp = scr->scoped;
930 // while(is(sp)) {
931 // sp->put(stdout, scr->name);
932 // sp.next();
933 // }
934
935 if(!cf.eof())
936 goto initial;
937
938 delete[] argv;
939 // sp = img->global;
940 // while(is(sp)) {
941 // sp->put(stdout, "*");
942 // sp.next();
943 // }
944
945 return img;
946 }
947
949 {
950 linked_pointer<event> ep = scr->
events;
951
952 while(is(ep)) {
953 if(eq(ep->name, id))
954 return true;
955 ep.next();
956 }
957 return false;
958 }
959
961 {
963 stack[loop++] = line;
964 return true;
965 }
966 return false;
967 }
968
970 {
971 if(!loop)
972 return NULL;
973
974 return stack[--loop]->
method;
975 }
976
978 {
979 if(!loop)
980 return NULL;
981
982 return stack[loop - 1]->
method;
983 }
984
986 {
987 assert(id && *id);
988 assert(scr != NULL);
989 assert(image != NULL);
990
991 linked_pointer<Script::strict> sp;
993
994 if(*id == '%' || *id == '=' || *id == '$')
995 ++id;
996
997 if(!image->global)
998 return;
999
1000 if(*(scr->
name) !=
'@') {
1002 while(is(sp)) {
1003 if(ideq(sp->id, id))
1004 return;
1005 sp.next();
1006 }
1008 sym->enlist(&scr->
scoped);
1010 return;
1011 }
1012 sp = image->global;
1013 while(is(sp)) {
1014 if(ideq(sp->id, id))
1015 return;
1016 sp.next();
1017 }
1019 sym->enlist(&image->global);
1021 }
1022
1024 {
1025 assert(id && *id);
1026 assert(scr != NULL);
1027 assert(image != NULL);
1028
1029 linked_pointer<Script::strict> sp;
1031
1032 if(*id == '%' || *id == '=' || *id == '$')
1033 ++id;
1034
1035 if(scr && !image->global)
1036 return;
1037
1038 if(scr && *(scr->
name) !=
'@') {
1040 while(is(sp)) {
1041 if(ideq(sp->id, id))
1042 return;
1043 sp.next();
1044 }
1045 }
1046 sp = image->global;
1047 while(is(sp)) {
1048 if(ideq(sp->id, id))
1049 return;
1050 sp.next();
1051 }
1053 sym->enlist(&image->global);
1055 }
1056
1058 {
1059 assert(id && *id);
1060 assert(scr != NULL);
1061 assert(image != NULL);
1062
1063 linked_pointer<Script::strict> sp;
1065
1066 if(*id == '%' || *id == '=' || *id == '$')
1067 ++id;
1068
1069 if(scr && !image->global)
1070 return;
1071
1072 if(scr && *(scr->
name) !=
'@') {
1074 while(is(sp)) {
1075 if(ideq(sp->id, id))
1076 return;
1077 sp.next();
1078 }
1079 }
1080 sp = image->global;
1081 while(is(sp)) {
1082 if(ideq(sp->id, id))
1083 return;
1084 sp.next();
1085 }
1087 if(scr && *(scr->
name) !=
'@')
1088 sym->enlist(&scr->
scoped);
1089 else
1090 sym->enlist(&image->global);
1092 }
1093
1095 {
1096 assert(id && *id);
1097 assert(image != NULL);
1098
1099 linked_pointer<Script::strict> sp;
1101
1102 if(*id == '%' || *id == '=' || *id == '$')
1103 ++id;
1104
1105 sp = image->global;
1106 while(is(sp)) {
1107 if(ideq(sp->id, id))
1108 return;
1109 sp.next();
1110 }
1112 sym->enlist(&image->global);
1114 }
1115
1117 {
1118 assert(id && *id);
1119 assert(scr != NULL);
1120 assert(image != NULL);
1121
1122 char buf[64];
1123 const char *cp;
1124 char *ep;
1125 linked_pointer<Script::strict> sp;
1126
1127 if(!image->global)
1128 return true;
1129
1130 if(*id != '%' && *id != '$')
1131 return true;
1132
1133 if(eq(id, "$map/", 5)) {
1134 buf[0] = '%';
1135 String::set(buf + 1, sizeof(buf) - 1, id + 5);
1136 ep = strchr(buf, ':');
1137 if(ep)
1138 *ep = 0;
1139
1140 if(!
find(image, scr, buf))
1141 return false;
1142 }
1143
1144 if(*id == '$') {
1145 cp = strchr(id, ':');
1146 if(cp)
1147 id = ++cp;
1148 else
1149 ++id;
1150 }
1151 else
1152 ++id;
1153
1154 if(*(scr->
name) !=
'@') {
1156 while(is(sp)) {
1157 if(ideq(sp->id, id))
1158 return true;
1159 sp.next();
1160 }
1161 }
1162
1163 sp = image->global;
1164 while(is(sp)) {
1165 if(ideq(sp->id, id))
1166 return true;
1167 sp.next();
1168 }
1169 return false;
1170 }
1171
1173 {
1174 assert(fp != NULL);
1175 assert(id != NULL);
1176
1177 const char *pid = id;
1178 if(header) {
1179 fputs(header, fp);
1180 fputc(':', fp);
1181 }
1182 while(*pid && *pid != ':')
1183 fputc(*(pid++), fp);
1184 fputc('\n', fp);
1185 }
1186
1188 {
1189 unsigned count = 0;
1190 char quote = 0;
1191 unsigned paren = 0;
1192
1193 if(!data || !*data)
1194 return 0;
1195
1197 while(*data) {
1198 if(*data == ',' && !quote && !paren)
1200 else if(*data == quote && !paren)
1201 quote = 0;
1202 else if(*data == '\"' && !paren)
1203 quote = *data;
1204 else if(*data == '(' && !quote)
1205 ++paren;
1206 else if(*data == ')' && !quote)
1207 --paren;
1208 ++data;
1209 }
1211 }
1212
1214 {
1215 assert(size > 0);
1216 assert(item != NULL);
1217
1218 bool lead = false;
1219 char quote = 0;
1220 char prev = 0;
1221 unsigned paren = 0;
1222 bool leadparen = false;
1223
1224 if(!list || !*list || *list == ',') {
1225 *item = 0;
1226 return;
1227 }
1228
1229 while(isspace(*list))
1230 ++list;
1231
1232 if(*list == '(') {
1233 leadparen = true;
1234 ++paren;
1235 ++list;
1236 }
1237 else {
1238 if(*list == '\"' || *list == '\'') {
1239 quote = *(list++);
1240 lead = true;
1241 }
1242 }
1243
1244 while(--size && *list) {
1245 if(paren == 1 && leadparen && *list == ')' && !quote)
1246 break;
1247 if(*list == '(')
1248 ++paren;
1249 else if(*list == ')')
1250 --paren;
1251 if(*list == ',' && !quote && !paren)
1252 break;
1253 if(*list == quote && lead)
1254 break;
1255 if(*list == quote) {
1256 *(item++) = quote;
1257 break;
1258 }
1259 if(!quote && *list == '\"' && prev == '=' && !paren)
1260 quote = *list;
1261 prev = *item;
1262 *(item++) = *(list++);
1263 }
1264 *item = 0;
1265 }
1266
1268 {
1269 const char *cp = get(list, index);
1270 return (unsigned)(cp - list);
1271 }
1272
1274 {
1275 char quote = 0;
1276 unsigned paren = 0;
1277
1278 if(!list || !*list)
1279 return NULL;
1280
1281 if(!index)
1282 return list;
1283
1284 while(*list && index) {
1285 if(*list == ',' && !quote && !paren) {
1286 --index;
1287 if(!index)
1288 return ++list;
1289 } else if(*list == quote)
1290 quote = 0;
1291 else if(*list == '(' && !quote)
1292 ++paren;
1293 else if(*list == ')' && !quote)
1294 --paren;
1295 else if(*list == '\"' && !paren)
1296 quote = *list;
1297 ++list;
1298 }
1299 return NULL;
1300 }
1301
1303 {
1304 char quote = 0;
1305 unsigned paren = 0;
1306
1307 if(!list || !*list)
1308 return NULL;
1309
1310 if(!index)
1311 return list;
1312
1313 while(*list && index) {
1314 if(*list == ',' && !quote && !paren) {
1315 --index;
1316 if(!index)
1317 return ++list;
1318 } else if(*list == quote)
1319 quote = 0;
1320 else if(*list == '(' && !quote)
1321 ++paren;
1322 else if(*list == ')' && !quote)
1323 --paren;
1324 else if(*list == '\"' && !paren)
1325 quote = *list;
1326 ++list;
1327 }
1328 return NULL;
1329 }
1330
1331 } // namespace ucommon
static void createVar(Script *img, header *scr, const char *id)
static const char * chkDefine(Script *img, header *scr, line_t *line)
struct ucommon::Script::keyword keyword_t
NAMESPACE_UCOMMON A structure to introduce new core commands to the runtime engine.
static const char * chkConst(Script *img, header *scr, line_t *line)
static const char * chkEndif(Script *img, header *scr, line_t *line)
static size_t paging
default heap paging
static const char * chkExit(Script *img, header *scr, line_t *line)
const char *(* check_t)(Script *img, Script::header *scr, Script::line_t *line)
A type for compile-time command verification method invokation.
LinkedObject * scheduler
scheduler list
static const char * chkIf(Script *img, header *scr, line_t *line)
A class to collect compile-time errors.
static const char * chkLoop(Script *img, header *scr, line_t *line)
static Script * compile(Script *merge, const char *filename, Script *config=NULL)
Compiled a file into an existing image.
static unsigned stepping
default stepping increment
static const char * chkPrevious(Script *img, header *scr, line_t *line)
static unsigned indexing
default symbol indexing
static const char * chkClear(Script *img, header *scr, line_t *line)
static const char * chkCase(Script *img, header *scr, line_t *line)
static bool isEvent(header *scr, const char *id)
static const char * get(const char *list, unsigned offset)
static void copy(const char *list, char *item, unsigned size)
const char * name
name of command
NAMESPACE_UCOMMON A structure to introduce new core commands to the runtime engine.
static const char * chkForeach(Script *img, header *scr, line_t *line)
static const char * chkStrict(Script *img, header *scr, line_t *line)
static const char * chkExpr(Script *img, header *scr, line_t *line)
static void createGlobal(Script *img, const char *id)
static const char * chkBreak(Script *img, header *scr, line_t *line)
static const char * chkVar(Script *img, header *scr, line_t *line)
static const char * chkExpand(Script *img, header *scr, line_t *line)
static const char * chkUntil(Script *img, header *scr, line_t *line)
Basic compiled statement.
static const char * chkWhile(Script *ing, header *scr, line_t *line)
static unsigned stacking
stack frames in script runtime
static unsigned count(const char *list)
static const char * chkContinue(Script *img, header *scr, line_t *line)
static const char * chkEndcase(Script *img, header *scr, line_t *line)
static unsigned offset(const char *list, unsigned index)
static void createAny(Script *img, header *scr, const char *id)
static const char * chkWhen(Script *img, header *scr, line_t *line)
Contains defined variables found by scope when strict is used.
static void assign(keyword_t *list)
Assign new keywords from extensions and derived service.
static const char * chkNop(Script *img, header *scr, line_t *line)
static unsigned decimals
default decimal places
static const char * chkElse(Script *img, header *scr, line_t *line)
static keyword_t * find(const char *id)
Find a keyword from internal command table.
bool(Script::interp::* method_t)(void)
A type for runtime script method invokation.
struct keyword * next
linked list set by assign()
static const char * chkRef(Script *img, header *scr, line_t *line)
struct ucommon::Script::line line_t
Basic compiled statement.
static const char * chkGoto(Script *img, header *scr, line_t *line)
Compiled script container.
check_t check
compile-time check routine
static void init(void)
Initialize entire script engine.
static const char * chkElif(Script *img, header *scr, line_t *line)
method_t method
runtime method or NULL if c-t only
static const char * chkError(Script *img, header *scr, line_t *line)
static const char * chkPush(Script *img, header *scr, line_t *line)
static const char * chkPack(Script *img, header *scr, line_t *line)
An event block for a script.
static const char * chkGosub(Script *img, header *src, line_t *line)
void put(FILE *fp, const char *header)
static unsigned sizing
default symbol size
static const char * chkInvoke(Script *img, header *scr, line_t *line)
static const char * chkOtherwise(Script *img, header *scr, line_t *line)
static const char * chkIgnmask(Script *img, header *scr, line_t *line)
static const char * chkIndex(Script *img, header *scr, line_t *line)
static bool find(Script *img, header *scr, const char *id)
static const char * chkSet(Script *img, header *scr, line_t *line)
static const char * chkApply(Script *img, header *scr, line_t *line)
static const char * chkDo(Script *img, header *scr, line_t *line)
static void createSym(Script *img, header *scr, const char *id)