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
27 namespace ucommon {
28
29 static const char *getArgument(
Script::line_t *line,
unsigned *index)
30 {
31 const char *cp;
32
33 for(;;) {
34 if(*index >= line->argc)
35 return NULL;
36 cp = line->argv[*index];
37 ++*index;
38 if(*cp == '=') {
39 ++*index;
40 continue;
41 }
42 return cp;
43 }
44 }
45
46 static const char *getKeyword(
const char *kw,
Script::line_t *line)
47 {
48 unsigned index = 0;
49 const char *cp;
50
51 while(index < line->argc) {
52 cp = line->argv[index++];
53 if(*cp == '=' && eq(kw, ++cp))
54 return line->argv[index];
55 }
56 return NULL;
57 }
58
60 {
61 unsigned count = 0;
62 unsigned index = 0;
63 const char *cp;
64
65 while(index < line->argc) {
66 cp = line->argv[index++];
67 if(*cp == '=') {
68 ++index;
69 continue;
70 }
71 ++count;
72 }
73 return count;
74 }
75
77 {
78 unsigned count = 0;
79 unsigned index = 0;
80 const char *cp;
81
82 while(index < line->argc) {
83 cp = line->argv[index++];
84 if(*cp == '=') {
85 ++index;
86 continue;
87 }
88 if(eq(cp, "required"))
89 ++count;
90 }
91 return count;
92 }
93
95 {
96 while(*text) {
97 if(!isalnum(*text))
98 return false;
99 ++text;
100 }
101 return true;
102 }
103
105 {
106 switch(*text)
107 {
108 case '-':
109 case '+':
110 if(text[1] == '.')
111 return true;
112 case '.':
113 if(isdigit(text[1]))
114 return true;
115 return false;
116 case '0':
117 case '1':
118 case '2':
119 case '3':
120 case '4':
121 case '5':
122 case '6':
123 case '7':
124 case '8':
125 case '9':
126 case '&':
127 case '%':
128 case '$':
129 return true;
130 default:
131 return false;
132 }
133 }
134
136 {
138 return "template define script required";
139
141 return "only one template define can be applied";
142
144 return "apply must be first statement of a new script";
145
147 return "only one apply statement can be used";
148
149 if(!isalnum(line->
argv[0][0]))
150 return "must apply using valid name of a defined script";
151
153 if(!tmp)
154 return "invalid or unknown script applied";
155
158 return NULL;
159 }
160
162 {
164
166 return "cannot be called outside loop";
167
169 goto valid;
170
171 return "cannot be called outside for or foreach block";
172
173 valid:
175 return "command has no arguments";
176
177 return NULL;
178 }
179
181 {
183
185 return "cannot be called outside loop";
186
188 goto valid;
189
191 goto valid;
192
194 goto valid;
195
196 return "cannot be called from conditional block";
197
198 valid:
200 return "command has no arguments";
201
202 return NULL;
203 }
204
206 {
207 unsigned index = 0;
208 const char *cp;
209
210 if(!eq(scr->
name,
"_init_"))
211 return "strict can only be used in initialization section";
212
214 return "strict must be defined at start of initialization section";
215
219 return NULL;
220 }
221
222 while(index < line->argc) {
223 cp = line->
argv[index++];
224 if(!isalpha(*cp))
225 return "strict must only declare names of defined internal global vars";
227 }
228 return NULL;
229 }
230
232 {
234
236 return "cannot be called outside loop";
237
239 return "command has no arguments";
240
241 return NULL;
242 }
243
245 {
247 return "one symbol argument for referencing";
248
249 const char *cp = line->
argv[0];
250 if(*cp != '$')
251 return "only field operations can be referenced alone";
252
253 return NULL;
254 }
255
257 {
259 return "missing conditional expression";
260
261 return chkConditional(img, scr, line);
262 }
263
265 {
267 return "analysis overflow for if command";
268
269 if(img->thencheck)
270 return "cannot nest if in if-then clause";
271
272 return chkConditional(img, scr, line);
273 }
274
276 {
277 if(eq(scr->
name,
"_init_"))
278 return "this command cannot be used to initialize";
279
280 if(img->thencheck)
281 return "cannot use while in if-then clause";
282
284 return "stack overflow for while command";
285
286 return chkConditional(img, scr, line);
287 }
288
290 {
291 unsigned index = 1;
292 const char *cp;
293
295 return "no value to expand";
296
298 return "no symbols to assign";
299
300 if(!isValue(line->
argv[0]))
301 return "cannot expand non-value";
302
303 while(line->
argv[index]) {
305 switch(*cp) {
306 case '&':
307 return "cannot assign literal";
308 case '=':
309 return "no keywords are used in this command";
310 case '$':
311 return "cannot assign to format";
312 case '@':
313 return "cannot assign to label";
314 case '^':
315 return "cannot assign to event";
316 case '?':
317 return "cannot assign to expression";
318 }
320 }
321 return NULL;
322 }
323
325 {
326 const char *cp;
327
328 if(eq(scr->
name,
"_init_"))
329 return "this command cannot be used to initialize";
330
331 if(img->thencheck)
332 return "cannot use for in if-then clause";
333
335 return "stack overflow for do command";
336
338 return "no symbols to assign";
339
340 if(line->
argc < 2 || line->
argc > 3)
341 return "assign from only one source";
342
344 switch(*cp) {
345 case '&':
346 return "cannot assign literal";
347 case '=':
348 return "no keywords are used in this command";
349 case '$':
350 return "cannot assign to format";
351 case '@':
352 return "cannot assign to label";
353 case '^':
354 return "cannot assign to event";
355 case '?':
356 return "cannot assign to expression";
357 }
359
360 if(!isValue(line->
argv[1]))
361 return "cannot assign from label or expression";
362
363 if(line->
argc == 3 && !isValue(line->
argv[2]))
364 return "skip must be a value or symbol";
365
366 return NULL;
367 }
368
370 {
371 if(eq(scr->
name,
"_init_"))
372 return "this command cannot be used to initialize";
373
374 if(img->thencheck)
375 return "cannot use do in if-then clause";
376
378 return "no arguments for do command";
379
381 return "stack overflow for do command";
382
383 return NULL;
384 }
385
387 {
389
390 if(img->thencheck)
391 return "cannot endif in if-then clause";
392
394 return "endif not within an if block";
395
397 return "endif has no arguments";
398
399 return NULL;
400 }
401
403 {
405
406 if(img->thencheck)
407 return "cannot endcase in if-then clause";
408
409 if(eq(scr->
name,
"_init_"))
410 return "this command cannot be used to initialize";
411
413 return "endcase not within a case block";
414
416 return "endcase has no arguments";
417
419 return NULL;
420 }
421
423 {
425
426 if(img->thencheck)
427 return "can not end loop in if-then clause";
428
429 if(eq(scr->
name,
"_init_"))
430 return "this command cannot be used to initialize";
431
433 goto valid;
434
436 goto valid;
437
439 goto valid;
440
442 return "not called from within loop";
443
444 return "not called from valid loop";
445
446 valid:
448 return "loop has no arguments";
449
450 return NULL;
451 }
452
454 {
456
457 if(img->thencheck)
458 return "cannot create case in if-then clause";
459
460 if(eq(scr->
name,
"_init_"))
461 return "this command cannot be used to initialize";
462
464 return "cannot have case after otherwise";
465
468 return "stack overflow for do command";
469 }
470 else {
474 }
475
476 return chkConditional(img, scr, line);
477 }
478
480 {
482
483 if(img->thencheck)
484 return "cannot have elif in if-then clause";
485
487 return "cannot have more if conditions after else";
488 }
489
491 return "cannot have elif outside of if block";
492 }
493
494 return chkConditional(img, scr, line);
495 }
496
498 {
500
501 if(img->thencheck)
502 return "cannot have else in if-then clause";
503
505 return "cannot have multiple else statements";
506 }
507
509 return "cannot have else outside of if block";
510 }
511
512 // replace loop with else member to prevent duplication...
515
517 return "otherwise has no arguments";
518
519 return NULL;
520 }
521
523 {
525
526 if(img->thencheck)
527 return "cannot have otherwise if-then clause";
528
529 if(eq(scr->
name,
"_init_"))
530 return "this command cannot be used to initialize";
531
533 return "cannot have otherwise outside of case block";
534 }
535
536 // replace loop with otherwise member to prevent duplication...
540
542 return "otherwise has no arguments";
543
544 return NULL;
545 }
546
548 {
550
551 if(img->thencheck)
552 return "cannot have until in if-then clause";
553
554 if(eq(scr->
name,
"_init_"))
555 return "this command cannot be used to initialize";
556
558 return "not called from within do loop";
559
560 return chkConditional(img, scr, line);
561 }
562
564 {
566 return "arguments are not used for this command";
567
568 return NULL;
569 }
570
572 {
574 return "arguments are not used for this command";
575
576 if(eq(scr->
name,
"_init_"))
577 return "this command cannot be used to initialize";
578
579 return NULL;
580 }
581
583 {
585 unsigned required = getRequired(sub);
586 unsigned limit = getArguments(sub);
587 unsigned count = getArguments(line);
588 unsigned index = 0;
589 const char *cp, *kw;
590
591 if(eq(scr->
name,
"_init_"))
592 return "this command cannot be used to initialize";
593
594 if(count < required)
595 return "too few arguments for invoked command";
596
597 if(count > limit)
598 return "too many arguments for invoked command";
599
600 index = 0;
601 while(index < sub->argc) {
602 kw = sub->
argv[index++];
603 if(*kw == '=') {
604 cp = sub->
argv[index++];
605 if(eq(cp, "required") && !getKeyword(++kw, line))
606 return "required keyword missing";
607 }
608 }
609 index = 0;
610 while(index < line->argc) {
611 kw = line->
argv[index++];
612 if(*kw == '=') {
613 if(!getKeyword(++kw, sub))
614 return "unknown or invalid keyword used";
615 ++index;
616 }
617 }
618 return NULL;
619 }
620
622 {
623 unsigned count = 0;
624 unsigned index = 0;
625 const char *cp;
626 char idbuf[8];
627
628 if(img->thencheck)
629 return "cannot define in if-then clause";
630
632 return NULL;
633
634 while(index < line->argc) {
635 cp = line->
argv[index++];
636 if(*cp == '=') {
637 if(strchr(cp, ':'))
638 return "no size or type set for referencing";
640 continue;
641 }
642
643 if(isalpha(*cp)) {
644 if(eq(cp, "optional"))
645 line->
argv[index - 1] = (
char *)
"&";
// rewrite to null
646 else if(!eq(cp, "required"))
647 return "invalid keyword used in prototype";
648 }
649 else if(!isValue(cp))
650 return "cannot assign from label or expression";
651
653 snprintf(idbuf, sizeof(idbuf), "%d", ++count);
655 }
656 }
657
658 return NULL;
659 }
660
662 {
663 unsigned index = 1;
664 const char *cp;
665
667 return "no symbols to assign";
668
670 return "no values to assign";
671
673 switch(*cp) {
674 case '&':
675 return "cannot assign literal";
676 case '=':
677 return "cannot assign members before symbol name";
678 case '$':
679 return "cannot assign to format";
680 case '@':
681 return "cannot assign to label";
682 case '^':
683 return "cannot assign to event";
684 case '?':
685 return "cannot assign to expression";
686 }
687
689
690 while(index < line->argc) {
691 cp = line->
argv[index++];
692 if(*cp == '=')
693 continue;
694 if(!isValue(cp))
695 return "cannot assign from label or expression";
696 }
697
698 return NULL;
699 }
700
702 {
703 const char *cp;
704
706 return "no symbol to push";
707
709 return "only use value or key and value";
710
712 switch(*cp) {
713 case '&':
714 return "cannot assign literal";
715 case '=':
716 return "no keywords are used in this command";
717 case '$':
718 return "cannot assign to format";
719 case '@':
720 return "cannot assign to label";
721 case '^':
722 return "cannot assign to event";
723 case '?':
724 return "cannot assign to expression";
725 }
727 if(!isValue(line->
argv[1]) && !isText(line->
argv[1]))
728 return "cannot push from label or expression";
729
730 if(line->
argv[2] && !isValue(line->
argv[2]) && !isText(line->
argv[1]))
731 return "cannot push value from label or expression";
732
733 return NULL;
734 }
735
737 {
738 unsigned pos = 0;
739
741 return "no events listed";
742
743 while(pos < line->argc) {
744 if(line->
argv[pos][0] ==
'^')
746 ++pos;
747 }
748
749 return NULL;
750 }
751
753 {
754 unsigned drop = 0;
755 unsigned index = 1;
756 const char *cp;
757
759 return "no symbols to assign";
760
762 if(eq(line->
argv[1],
":="))
763 drop = 1;
764 else if(eq(line->
argv[1],
"+=")) {
765 drop = 1;
767 }
768 }
769
770 if(drop) {
772 while(drop && drop < line->argc) {
773 line->
argv[drop] = line->
argv[drop + 1];
774 ++drop;
775 }
776 line->
argv[drop] = NULL;
777 }
778
780 return "no values to assign";
781
783 switch(*cp) {
784 case '&':
785 return "cannot assign literal";
786 case '=':
787 return "no keywords are used in this command";
788 case '$':
789 return "cannot assign to format";
790 case '@':
791 return "cannot assign to label";
792 case '^':
793 return "cannot assign to event";
794 case '?':
795 return "cannot assign to expression";
796 }
797
799
800 while(index < line->argc) {
801 cp = line->
argv[index++];
802 if(*cp == '=')
803 return "no keywords are used in this command";
804 if(!isValue(cp))
805 return "cannot assign to format";
806 }
807 return NULL;
808 }
809
811 {
812 unsigned index = 0;
813 const char *cp;
814
816 return "no symbols to clear";
817
818 while(index < line->argc) {
819 cp = line->
argv[index++];
820 if(*cp != '%')
821 return "invalid symbol reference or syntax";
822 if(strchr(cp, ':'))
823 return "invalid size usage for symbol";
824 }
825 return NULL;
826 }
827
829 {
830 unsigned index = 0;
831 const char *cp;
832
834 return "no error message";
835
836 while(index < line->argc) {
837 cp = line->
argv[index++];
838 if(*cp == '=')
839 return "no keywords used in error";
840
841 if(*cp == '^' || *cp == '@')
842 return "cannot use label for error";
843 }
844 return NULL;
845 }
846
848 {
849 unsigned index = 0;
850 const char *cp;
851
853 return "no constants to assign";
854
855 while(index < line->argc) {
856 cp = line->
argv[index++];
857 if(*cp != '=')
858 return "cannot assign data or use uninitialized symbol as const";
859
860 if(strchr(cp, ':'))
861 return "cannot alter size of const symbol";
862
864
865 cp = line->
argv[index++];
866 if(*cp == '=')
867 return "invalid assignment of const";
868 if(!isValue(cp))
869 return "cannot use label or expression for const";
870 }
871 return NULL;
872 }
873
875 {
876 unsigned index = 0;
877 const char *cp;
878
879 if(eq(scr->
name,
"_init_"))
880 return "this command cannot be used to initialize";
881
883 return "goto requires at least one label or event handler";
884
885 while(index < line->argc) {
886 cp = line->
argv[index++];
887 if(*cp != '@' && *cp != '^')
888 return "goto only allows scripts and handlers";
889 }
890 return NULL;
891 }
892
894 {
895 unsigned index = 0;
896 const char *cp;
897
898 if(eq(scr->
name,
"_init_"))
899 return "this command cannot be used to initialize";
900
902 return "gosub requires at least one label";
903
904 while(index < line->argc) {
905 cp = line->
argv[index++];
906 if(*cp != '@' && !isalnum(*cp))
907 return "gosub only allows script sections and named methods";
908 }
909
910 return NULL;
911 }
912
914 {
915 unsigned index = 0;
916 const char *cp;
917
918 while(index < line->argc) {
919 cp = line->
argv[index++];
920 if((*cp == '-' || *cp == '!') && isalpha(cp[1])) {
921 if(index >= line->
argc)
922 return "missing test value";
923 cp = line->
argv[index++];
924 if(*cp == '?')
925 return "cannot test operator";
926 }
927 else {
928 if(*cp == '?')
929 return "cannot use operator as element of expression";
930
931 if(index >= line->
argc)
932 return "missing operator in expression";
933
934 cp = line->
argv[index++];
935
936 if(*cp != '?' && !eq(cp, "eq") && !eq(cp, "ne") && !eq(cp, "lt") && !eq(cp, "gt") && !eq(cp, "le") && !eq(cp, "ge") && !eq(cp, "is") && !eq(cp, "isnot") && !eq(cp, "in") && !eq(cp, "notin"))
937 return "invalid operator in expression";
938
939 if(index >= line->
argc)
940 return "missing value from expression";
941
942 cp = line->
argv[index++];
943 if(*cp == '?')
944 return "cannot use operator as element of expression";
945 }
946 if(index == line->
argc)
947 return NULL;
948
949 cp = line->
argv[index++];
950 if(eq("?&&", cp) || eq("?||", cp) || eq("and", cp) || eq("or", cp))
951 continue;
952
953 return "invalid expression joiner statement";
954 }
955 return "conditional expression missing";
956 }
957
959 {
960 unsigned index = 0;
961 const char *cp;
963
965 return "cannot be called outside loop";
966
968 goto valid;
969
970 return "cannot be called outside for or foreach block";
971
972 valid:
974 return "requires at least position";
975
976 while(index < line->argc) {
977 cp = getArgument(line, &index);
978 if(!isValue(cp))
979 return "malformed expression";
980
981 cp = getArgument(line, &index);
982 if(!cp)
983 break;
984 if(!eq(cp, "*") && !eq(cp, "/") && !eq(cp, "+") && !eq(cp, "-") && !eq(cp, "#"))
985 return "invalid expression used";
986 if(index == line->
argc)
987 return "incomplete expression";
988 }
989
990 return NULL;
991 }
992
994 {
995 unsigned index = 0;
996 const char *cp;
997
999 return "no variable to assign";
1000
1001 while(index < line->argc) {
1002 cp = line->
argv[index++];
1003 if(*cp == '=') {
1004 if(!eq(cp, "=decimals"))
1005 return "invalid keyword for expression";
1006 ++index;
1007 }
1008 }
1009
1010 index = 0;
1011 cp = getArgument(line, &index);
1012 if(!cp)
1013 return "no assignment";
1014
1015 if(*cp == '&')
1016 return "cannot assign literal as symbol";
1017
1018 if(*cp == '$')
1019 return "cannot assign format as symbol";
1020
1021 if(*cp == '@' || *cp == '^')
1022 return "cannot assign label as symbol";
1023
1024 if(*cp == '?')
1025 return "cannot assign expression as symbol";
1026
1028
1029 cp = getArgument(line, &index);
1030 if(!cp)
1031 return "expression incomplete";
1032
1033 if(!eq(cp, ":=") && !eq(cp, "?=") && !eq(cp, "+=") && !eq(cp, "-=") && !eq(cp, "*=") && !eq(cp, "/="))
1034 return "expression must start with assignment expression";
1035
1036 while(index < line->argc) {
1037 cp = getArgument(line, &index);
1038 if(!isValue(cp))
1039 return "malformed expression";
1040
1041 cp = getArgument(line, &index);
1042 if(!cp)
1043 break;
1044 if(!eq(cp, "*") && !eq(cp, "/") && !eq(cp, "+") && !eq(cp, "-") && !eq(cp, "#"))
1045 return "invalid expression used";
1046 if(index == line->
argc)
1047 return "incomplete expression";
1048 }
1049 return NULL;
1050 }
1051
1053 {
1054 unsigned index = 0;
1055 const char *cp;
1056
1058 return "no variables to assign";
1059
1060 while(index < line->argc) {
1061 cp = line->
argv[index++];
1062 if(*cp == '&')
1063 return "cannot assign literal as symbol";
1064
1065 if(*cp == '$')
1066 return "cannot assign format as symbol";
1067
1068 if(*cp == '@' || *cp == '^')
1069 return "cannot assign label as symbol";
1070
1071 if(*cp == '?')
1072 return "cannot assign expression as symbol";
1073
1074 if(*cp != '=')
1075 continue;
1076
1078 cp = line->
argv[index++];
1079 if(*cp == '=')
1080 return "invalid assignment of variables";
1081 if(!isValue(cp))
1082 return "cannot assign from label or expression";
1083 }
1084 return NULL;
1085 }
1086
1088 {
1089 return NULL;
1090 }
1091
1092 } // namespace ucommon
static void createVar(Script *img, header *scr, const char *id)
static const char * chkDefine(Script *img, header *scr, line_t *line)
static const char * chkConst(Script *img, header *scr, line_t *line)
static const char * chkEndif(Script *img, header *scr, line_t *line)
static const char * chkExit(Script *img, header *scr, line_t *line)
static const char * chkIf(Script *img, header *scr, line_t *line)
static const char * chkLoop(Script *img, header *scr, line_t *line)
static const char * chkPrevious(Script *img, header *scr, line_t *line)
static const char * chkClear(Script *img, header *scr, line_t *line)
static const char * chkCase(Script *img, header *scr, line_t *line)
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 * chkConditional(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 const char * chkContinue(Script *img, header *scr, line_t *line)
static const char * chkEndcase(Script *img, header *scr, line_t *line)
static void createAny(Script *img, header *scr, const char *id)
static const char * chkWhen(Script *img, header *scr, line_t *line)
static const char * chkNop(Script *img, header *scr, line_t *line)
static bool isValue(const char *text)
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.
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.
static const char * chkElif(Script *img, header *scr, line_t *line)
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)
static const char * chkGosub(Script *img, header *src, line_t *line)
static const char * chkInvoke(Script *img, header *scr, line_t *line)
static const char * chkOtherwise(Script *img, header *scr, line_t *line)
static bool isText(const char *text)
static const char * chkIgnmask(Script *img, header *scr, line_t *line)
static const char * chkIgnore(Script *img, header *scr, line_t *line)
static const char * chkIndex(Script *img, header *scr, line_t *line)
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)