const char * createuserfunc(const char * name, const char * arguments, const char * content) { if (!confirmqpar(content)) return "Mismatched parentheses"; for (int i=0;i<numuserfunc;i++) { if (!strcmp(name, userfunc[i].name)) { return "Duplicate function name"; } } funcdat& thisone=userfunc[numuserfunc]; thisone.name=strdup(name); thisone.argbuf=strdup(arguments); thisone.arguments=qsplit(thisone.argbuf, ",", &(thisone.numargs)); thisone.content=strdup(content); for (int i=0;thisone.arguments[i];i++) { if (!confirmname(thisone.arguments[i])) { userfunc.remove(numuserfunc); return "Invalid argument name"; } } numuserfunc++; return NULL; }
void startmacro(const char * line_) { thisone=NULL; if (!confirmqpar(line_)) error<errblock>(0, "Broken macro declaration"); string line=line_; clean(line); char * startpar=strqchr(line.str, '('); if (!startpar) error<errblock>(0, "Broken macro declaration"); *startpar=0; startpar++; if (!confirmname(line)) error<errblock>(0, "Bad macro name"); thisname=line; char * endpar=strqrchr(startpar, ')'); //confirmqpar requires that all parentheses are matched, and a starting one exists, therefore it is harmless to not check for nulls if (endpar[1]) error<errblock>(0, "Broken macro declaration"); *endpar=0; for (int i=0;startpar[i];i++) { char c=startpar[i]; if (!isalnum(c) && c!='_' && c!=',') error<errline>(0, "Broken macro declaration"); if (c==',' && isdigit(startpar[i+1])) error<errline>(0, "Broken macro declaration"); } if (*startpar==',' || isdigit(*startpar) || strstr(startpar, ",,") || endpar[-1]==',') error<errline>(0, "Broken macro declaration"); macrodata * ignored; if (macros.find(thisname, ignored)) error<errblock>(0, "Duplicate macro"); thisone=(macrodata*)malloc(sizeof(macrodata)); new(thisone) macrodata; if (*startpar) { thisone->arguments=(const char**)qpsplit(strdup(startpar), ",", &thisone->numargs); } else { const char ** noargs=(const char**)malloc(sizeof(const char**)); *noargs=NULL; thisone->arguments=noargs; thisone->numargs=0; } for (int i=0;thisone->arguments[i];i++) { if (!confirmname(thisone->arguments[i])) error<errblock>(0, "Bad macro argument name"); for (int j=i+1;thisone->arguments[j];j++) { if (!strcmp(thisone->arguments[i], thisone->arguments[j])) error<errblock>(0, S"Duplicate macro argument '"+thisone->arguments[i]+"'"); } } thisone->fname=strdup(thisfilename); thisone->startline=thisline; numlines=0; }
void callmacro(const char * data) { int numcm=reallycalledmacros++; macrodata * thisone; if (!confirmqpar(data)) merror("Broken macro usage"); string line=data; clean(line); char * startpar=strqchr(line.str, '('); if (!startpar) merror("Broken macro usage"); *startpar=0; startpar++; if (!confirmname(line)) merror("Bad macro name"); if (!macros.find(line, thisone)) merror("Unknown macro"); char * endpar=strqrchr(startpar, ')'); if (endpar[1]) merror("Broken macro usage"); *endpar=0; autoptr<const char **> args; int numargs=0; char *tmp_asd=strdup(startpar); if (*startpar) args=(const char**)qpsplit(tmp_asd, ",", &numargs); //free(tmp_asd); if (numargs!=thisone->numargs) merror("Wrong number of arguments to macro"); macrorecursion++; int startif=numif; for (int i=0;i<thisone->numlines;i++) { try { thisfilename=thisone->fname; thisline=thisone->startline+i+1; thisblock=NULL; string out; string intmp=thisone->lines[i]; for (char * in=intmp.str;*in;) { if (*in=='<' && in[1]=='<') { out+="<<"; in+=2; } else if (*in=='<' && isalnum(in[1])) { char * end=in+1; while (*end && *end!='<' && *end!='>') end++; if (*end!='>') { out+=*(in++); continue; } *end=0; in++; if (!confirmname(in)) error<errline>(0, "Broken macro contents"); bool found=false; for (int i=0;thisone->arguments[i];i++) { if (!strcmp(in, thisone->arguments[i])) { found=true; if (args[i][0]=='"') { string s=args[i]; out+=dequote(s.str); } else out+=args[i]; break; } } if (!found) error<errline>(0, "Unknown macro argument"); in=end+1; } else out+=*(in++); } calledmacros=numcm; assembleline(thisone->fname, thisone->startline+i, out); } catch(errline&){} } macrorecursion--; if (repeatnext!=1) { thisblock=NULL; repeatnext=1; merror("rep or if at the end of a macro"); } if (numif!=startif) { thisblock=NULL; numif=startif; numtrue=startif; merror("Unclosed if statement"); } }