21 #include <ccscript-config.h>
22 #include <ucommon/ucommon.h>
23 #include <ucommon/export.h>
32 static unsigned long icounter = 0;
41 static bool ideq(
const char *id1,
const char *id2)
51 while(id1[count] && id1[count] !=
':') {
52 if(id2[count] != id1[count])
56 if(id2[count] && id2[count] !=
':')
61 static bool iskeyword(
const char *str)
70 if(!isalpha(*str) && *str !=
'.')
77 static bool isend(
const char *str)
82 if(!strncmp(
"%%", str, 2))
85 if(!strncmp(
"//", str, 2))
91 static bool preparse(
char **tokens)
93 if(!tokens || !*tokens)
96 while(*tokens && isspace(**tokens))
107 if(isspace(str[1]) || (str[1] ==
'=' && isspace(str[2]))) {
115 if(isspace(str[1])) {
123 if(eq(str,
">>%", 3)) {
130 if(isspace(str[1]) || (str[1] ==
'=' && isspace(str[2]))) {
137 if(isspace(str[1])) {
144 if(isspace(str[1])) {
151 if(eq(str,
"<<%", 3)) {
158 if(isspace(str[1]) || (str[1] ==
'=' && isspace(str[2])) || (str[1] ==
'>' && isspace(str[2]))) {
165 if(str[1] ==
'=' && isspace(str[2])) {
170 else if(str[1] ==
'?' && isspace(str[2])) {
175 else if(str[1] ==
'$' && isspace(str[2])) {
180 else if(str[1] ==
'~' && isspace(str[2])) {
189 if(str[1] ==
'&' && isspace(str[2])) {
194 else if(isalnum(str[1])) {
200 if(str[1] ==
'|' && isspace(str[2])) {
210 if(str[1] ==
'=' && isspace(str[2]))
214 if(str[1] ==
'=' && isspace(str[2]))
228 if(eq(str,
"++%", 3)) {
235 if(isdigit(str[1]) || str[1] ==
'.') {
241 if(str[1] ==
'=' && isspace(str[2]))
249 if(eq(str,
"--%", 3)) {
256 if(isalnum(str[1]) || str[1] ==
'.')
260 if(str[1] ==
'=' && isspace(str[2]))
273 while(*str && (isalnum(*str) || *str ==
':' || *str ==
'.'))
287 Script::error::error(Script *img,
unsigned line,
const char *msg) :
290 errmsg = img->dup(msg);
292 filename = img->filename;
298 instance = icounter++;
311 shell::log(shell::INFO,
"creating image instance %lu\n", instance);
319 shell::log(shell::INFO,
"releasing image instance %lu\n", instance);
322 void Script::errlog(
unsigned line,
const char *fmt, ...)
328 vsnprintf(text,
sizeof(text), fmt, args);
338 while(keyword && keyword->
name) {
339 keyword->
next = keywords;
349 while(keyword != NULL) {
350 if(eq(cmd, keyword->
name))
352 keyword = keyword->
next;
405 static bool initial =
false;
416 linked_pointer<header> hp = img->
scripts[path];
431 static unsigned serial;
433 char **argv =
new char *[256];
434 stringbuf<512> buffer;
437 std::ifstream cf(fn);
439 const char *name =
"_init_";
443 const char *token = NULL;
447 bool section =
false;
449 const char *err, *op;
452 bool indented, formed;
460 bool requires =
false;
476 img->filename = strrchr(fn,
'/');
478 img->filename = strrchr(fn,
'\\');
480 img->filename = strrchr(fn,
':');
486 img->filename = img->dup(img->filename);
487 img->serial = ++serial;
494 if(img->
first && eq(name,
"_init_")) {
497 while(last && last->
next)
502 scr->
name = img->dup(name);
503 scr->
file = img->dup(img->filename);
511 ep = (
char *)strrchr(scr->
file,
'.');
521 if(!eq(name,
"_init_") || !merge) {
523 scr->enlist(&img->
scripts[path]);
529 while(when || label || define || cf.getline(buffer.c_mem(), 512)) {
545 token = String::token(NULL, &tokens,
" \t",
"{}\'\'\"\"");
552 img->thencheck =
false;
555 tokens = buffer.c_mem();
556 if(isspace(*tokens)) {
557 while(isspace(*tokens))
560 if(!preparse(&tokens)) {
561 img->errlog(lnum,
"malformed line");
566 buffer.trim(
" \t\r\n");
573 token = String::token(buffer.c_mem(), &tokens,
" \t",
"{}\'\'\"\"");
575 if(eq(token,
"endreq") || eq(token,
"endrequires")) {
577 img->errlog(lnum,
"endreq cannot be indented");
583 else if(eq(token,
"requires")) {
585 img->errlog(lnum,
"requires cannot be indented");
589 while(NULL != (token = String::token(NULL, &tokens,
" \t",
"{}\'\'\"\""))) {
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)) {
603 if(rev && !keyword && !invoke) {
614 if(*token ==
'^' || *token ==
'-') {
615 if(scr == img->
first) {
616 img->errlog(lnum,
"events cannot be in init segment");
626 current->enlist(&scr->
events);
628 current->enlist(&scr->
methods);
629 current->
name = (
char *)img->dup(++token);
630 current->
first = NULL;
643 snprintf(localname,
sizeof(localname),
"@%04x%s", serial & 0xffff, token);
649 if(eq(token,
"template")) {
651 img->errlog(lnum,
"templates must be before named sections");
655 img->errlog(lnum,
"templates cannot be indented");
658 token = String::token(NULL, &tokens,
" \t",
"{}\'\'\"\"");
660 img->errlog(lnum,
"template must be named");
665 if(eq(token,
"define")) {
667 img->errlog(lnum,
"defines must be before named sections");
671 img->errlog(lnum,
"define cannot be indented");
674 token = String::token(NULL, &tokens,
" \t",
"{}\'\'\"\"");
675 keyword =
find(token);
677 img->errlog(lnum,
"cannot redefine existing command");
685 img->errlog(lnum,
"unindented statement");
695 if(eq(token,
"goto") || eq(token,
"gosub"))
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,
"#=")))
703 else if(op && (eq(op,
"=") || eq(op,
"==") || eq(op,
"$="))) {
707 else if(op && (eq(op,
".") || eq(op,
".="))) {
711 else if(op && (eq(op,
",") || eq(op,
",="))) {
716 img->errlog(lnum,
"invalid assignment");
720 else if(*token ==
'$') {
721 assigned = img->dup(token);
725 if(!iskeyword(token)) {
726 img->errlog(lnum,
"invalid keyword");
732 keyword =
find(
"_define");
734 img->errlog(lnum,
"define unsupported");
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");
746 img->errlog(lnum,
"unknown keyword \"%s\"", token);
752 token = img->dup(token);
755 memset(line, 0,
sizeof(
line_t));
763 line->
loop = img->loop;
771 argv[argc++] = assigned;
774 argv[argc++] = img->dup(op);
776 while(argc < 249 && formed) {
778 formed = preparse(&tokens);
781 arg = (
char *)String::token(NULL, &tokens,
" \t",
"{}\'\'\"\"");
786 if(eq(token,
"if")) {
791 if(eq(arg,
"then")) {
792 keyword =
find(
"_ifthen");
794 token = String::token(NULL, &tokens,
" \t",
"{}\'\'\"\"");
795 if(!token || !keyword || img->thencheck)
798 img->thencheck = when =
true;
804 if(*arg ==
':' && branching) {
805 snprintf(localname,
sizeof(localname),
"@%04x%s", serial & 0xffff, arg);
808 else if(*arg ==
':' && isalnum(arg[1])) {
809 snprintf(localname,
sizeof(localname),
"%c_%04x_.%s",
810 '%', serial & 0xffff, ++arg);
814 ep = strchr(arg,
'<');
815 if(*arg ==
'%' && ep) {
816 len = strlen(arg) + 10;
818 temp = (
char *)img->alloc(len);
819 String::set(temp, len,
"$map/");
820 String::add(temp, len, ep);
821 ep = strchr(temp,
'>');
824 String::add(temp, len, ++arg);
830 ep = strchr(arg,
'[');
831 if(*arg ==
'%' && ep) {
832 len = strlen(arg) + 10;
834 temp = (
char *)img->alloc(len);
836 String::set(temp, len,
"$offset/");
838 String::set(temp, len,
"$find/");
839 String::add(temp, len, ep);
840 ep = strchr(temp,
']');
843 String::add(temp, len, ++arg);
849 ep = strchr(arg,
'(');
850 if(*arg ==
'%' && ep) {
851 len = strlen(arg) + 10;
853 temp = (
char *)img->alloc(len);
854 String::set(temp, len,
"$index/");
855 String::add(temp, len, ep);
856 ep = strchr(temp,
')');
859 String::add(temp, len, ++arg);
865 argv[argc++] = img->dup(arg);
870 img->errlog(lnum,
"malformed statement or argument");
876 line->
argv = (
char **)img->alloc(
sizeof(
char *) * argc);
877 memcpy(line->
argv, argv,
sizeof(
char *) * argc);
879 err = (*(keyword->
check))(img, scr, line);
881 img->errlog(lnum,
"%s", err);
887 cp = line->
argv[pos++];
889 img->errlog(lnum,
"undefined symbol reference %s\n", cp);
903 linked_pointer<event> ep = prior;
904 while(is(ep) && *ep != current) {
908 current->
first = line;
918 line = img->stack[--img->loop];
919 img->errlog(line->
lnum,
"%s never completed loop", line->
cmd);
922 keyword =
find(
"_close");
924 err = (*(keyword->
check))(img, scr, scr->
first);
926 img->errlog(lnum,
"%s", err);
950 linked_pointer<event> ep = scr->
events;
963 stack[loop++] = line;
974 return stack[--loop]->
method;
982 return stack[loop - 1]->
method;
989 assert(image != NULL);
991 linked_pointer<Script::strict> sp;
994 if(*
id ==
'%' || *
id ==
'=' || *
id ==
'$')
1000 if(*(scr->
name) !=
'@') {
1003 if(ideq(sp->id,
id))
1008 sym->enlist(&scr->
scoped);
1014 if(ideq(sp->id,
id))
1019 sym->enlist(&image->global);
1026 assert(scr != NULL);
1027 assert(image != NULL);
1029 linked_pointer<Script::strict> sp;
1032 if(*
id ==
'%' || *
id ==
'=' || *
id ==
'$')
1035 if(scr && !image->global)
1038 if(scr && *(scr->
name) !=
'@') {
1041 if(ideq(sp->id,
id))
1048 if(ideq(sp->id,
id))
1053 sym->enlist(&image->global);
1060 assert(scr != NULL);
1061 assert(image != NULL);
1063 linked_pointer<Script::strict> sp;
1066 if(*
id ==
'%' || *
id ==
'=' || *
id ==
'$')
1069 if(scr && !image->global)
1072 if(scr && *(scr->
name) !=
'@') {
1075 if(ideq(sp->id,
id))
1082 if(ideq(sp->id,
id))
1087 if(scr && *(scr->
name) !=
'@')
1088 sym->enlist(&scr->
scoped);
1090 sym->enlist(&image->global);
1097 assert(image != NULL);
1099 linked_pointer<Script::strict> sp;
1102 if(*
id ==
'%' || *
id ==
'=' || *
id ==
'$')
1107 if(ideq(sp->id,
id))
1112 sym->enlist(&image->global);
1119 assert(scr != NULL);
1120 assert(image != NULL);
1125 linked_pointer<Script::strict> sp;
1130 if(*
id !=
'%' && *
id !=
'$')
1133 if(eq(
id,
"$map/", 5)) {
1135 String::set(buf + 1,
sizeof(buf) - 1,
id + 5);
1136 ep = strchr(buf,
':');
1140 if(!
find(image, scr, buf))
1145 cp = strchr(
id,
':');
1154 if(*(scr->
name) !=
'@') {
1157 if(ideq(sp->id,
id))
1165 if(ideq(sp->id,
id))
1177 const char *pid = id;
1182 while(*pid && *pid !=
':')
1183 fputc(*(pid++), fp);
1198 if(*data ==
',' && !quote && !paren)
1200 else if(*data == quote && !paren)
1202 else if(*data ==
'\"' && !paren)
1204 else if(*data ==
'(' && !quote)
1206 else if(*data ==
')' && !quote)
1216 assert(item != NULL);
1222 bool leadparen =
false;
1224 if(!list || !*list || *list ==
',') {
1229 while(isspace(*list))
1238 if(*list ==
'\"' || *list ==
'\'') {
1244 while(--size && *list) {
1245 if(paren == 1 && leadparen && *list ==
')' && !quote)
1249 else if(*list ==
')')
1251 if(*list ==
',' && !quote && !paren)
1253 if(*list == quote && lead)
1255 if(*list == quote) {
1259 if(!quote && *list ==
'\"' && prev ==
'=' && !paren)
1262 *(item++) = *(list++);
1269 const char *cp =
get(list, index);
1270 return (
unsigned)(cp - list);
1284 while(*list && index) {
1285 if(*list ==
',' && !quote && !paren) {
1289 }
else if(*list == quote)
1291 else if(*list ==
'(' && !quote)
1293 else if(*list ==
')' && !quote)
1295 else if(*list ==
'\"' && !paren)
1313 while(*list && index) {
1314 if(*list ==
',' && !quote && !paren) {
1318 }
else if(*list == quote)
1320 else if(*list ==
'(' && !quote)
1322 else if(*list ==
')' && !quote)
1324 else if(*list ==
'\"' && !paren)
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)