diff --git a/src/input.cpp b/src/input.cpp index df4a2dfd7e..97eaa5cd58 100644 --- a/src/input.cpp +++ b/src/input.cpp @@ -265,7 +265,7 @@ char *Input::one(const char *single) command = first word narg = # of args arg[] = individual args - treat text between double quotes as one arg + treat text between single/double quotes as one arg ------------------------------------------------------------------------- */ void Input::parse() @@ -275,19 +275,17 @@ void Input::parse() strcpy(copy,line); // strip any # comment by resetting string terminator - // do not strip # inside double quotes + // do not strip # inside single/double quotes - int level = 0; + char quote = '\0'; char *ptr = copy; while (*ptr) { - if (*ptr == '#' && level == 0) { + if (*ptr == '#' && !quote) { *ptr = '\0'; break; } - if (*ptr == '"') { - if (level == 0) level = 1; - else level = 0; - } + if (*ptr == quote) quote = '\0'; + else if (*ptr == '"' || *ptr == '\'') quote = *ptr; ptr++; } @@ -302,9 +300,12 @@ void Input::parse() if (command == NULL) return; // point arg[] at each subsequent arg - // treat text between double quotes as one arg + // treat text between single/double quotes as one arg // insert string terminators in copy to delimit args + quote = '\0'; + int iarg,argstart; + narg = 0; while (1) { if (narg == maxarg) { @@ -312,19 +313,23 @@ void Input::parse() arg = (char **) memory->srealloc(arg,maxarg*sizeof(char *),"input:arg"); } arg[narg] = strtok(NULL," \t\n\r\f"); - if (arg[narg] && arg[narg][0] == '\"') { + if (!arg[narg]) break; + if (!quote && (arg[narg][0] == '"' || arg[narg][0] == '\'')) { + quote = arg[narg][0]; + argstart = narg; arg[narg] = &arg[narg][1]; - if (arg[narg][strlen(arg[narg])-1] == '\"') - arg[narg][strlen(arg[narg])-1] = '\0'; - else { - arg[narg][strlen(arg[narg])] = ' '; - ptr = strtok(arg[narg],"\""); - if (ptr == NULL) error->all("Unbalanced quotes in input line"); - } } - if (arg[narg]) narg++; - else break; + if (quote && arg[narg][strlen(arg[narg])-1] == quote) { + for (iarg = argstart; iarg < narg; iarg++) + arg[iarg][strlen(arg[iarg])] = ' '; + arg[narg][strlen(arg[narg])-1] = '\0'; + narg = argstart; + quote = '\0'; + } + narg++; } + + if (quote) error->all("Unbalanced quotes in input line"); } /* ---------------------------------------------------------------------- @@ -335,18 +340,18 @@ void Input::parse() void Input::substitute(char *str, int flag) { // use work[] as scratch space to expand str - // do not replace $ inside double quotes as flagged by level + // do not replace $ inside single/double quotes // var = pts at variable name, ended by NULL // if $ is followed by '{', trailing '}' becomes NULL // else $x becomes x followed by NULL // beyond = pts at text following variable char *var,*value,*beyond; - int level = 0; + char quote = '\0'; char *ptr = str; while (*ptr) { - if (*ptr == '$' && level == 0) { + if (*ptr == '$' && !quote) { if (*(ptr+1) == '{') { var = ptr+2; int i = 0; @@ -379,10 +384,8 @@ void Input::substitute(char *str, int flag) } continue; } - if (*ptr == '"') { - if (level == 0) level = 1; - else level = 0; - } + if (*ptr == quote) quote = '\0'; + else if (*ptr == '"' || *ptr == '\'') quote = *ptr; ptr++; } } @@ -521,72 +524,107 @@ void Input::echo() void Input::ifthenelse() { - if (narg < 5) error->all("Illegal if command"); + if (narg < 4) error->all("Illegal if command"); - // flag = 0 for "then" - // flag = 1 for "else" + // substitute for variables in Boolean expression for "if" + // in case expression was enclosed in quotes - int flag = 0; - if (strcmp(arg[1],"==") == 0) { - if (atof(arg[0]) == atof(arg[2])) flag = 1; - } else if (strcmp(arg[1],"!=") == 0) { - if (atof(arg[0]) != atof(arg[2])) flag = 1; - } else if (strcmp(arg[1],"<") == 0) { - if (atof(arg[0]) < atof(arg[2])) flag = 1; - } else if (strcmp(arg[1],"<=") == 0) { - if (atof(arg[0]) <= atof(arg[2])) flag = 1; - } else if (strcmp(arg[1],">") == 0) { - if (atof(arg[0]) > atof(arg[2])) flag = 1; - } else if (strcmp(arg[1],">=") == 0) { - if (atof(arg[0]) >= atof(arg[2])) flag = 1; - } else error->all("Illegal if command"); + substitute(arg[0],0); - // first = arg index of first "then" or "else" command - // last = arg index of last "then" or "else" command - - int iarg,first,last; + // evaluate Boolean expression for "if" - // identify range of commands within arg list for then or else - // for else, if no comands, just return + double btest = variable->evaluate_boolean(arg[0]); - if (strcmp(arg[3],"then") != 0) error->all("Illegal if command"); - if (flag) { - iarg = first = 4; - while (iarg < narg && strcmp(arg[iarg],"else") != 0) iarg++; + // bound "then" commands + + if (strcmp(arg[1],"then") != 0) error->all("Illegal if command"); + + int first = 2; + int iarg = first; + while (iarg < narg && + (strcmp(arg[iarg],"elif") != 0 && strcmp(arg[iarg],"else") != 0)) + iarg++; + int last = iarg-1; + + // execute "then" commands + // make copies of all arg string commands + // required because re-parsing a command via one() will wipe out args + + if (btest != 0.0) { + int ncommands = last-first + 1; + if (ncommands <= 0) error->all("Illegal if command"); + + char **commands = new char*[ncommands]; + ncommands = 0; + for (int i = first; i <= last; i++) { + int n = strlen(arg[i]) + 1; + if (n == 1) error->all("Illegal if command"); + commands[ncommands] = new char[n]; + strcpy(commands[ncommands],arg[i]); + ncommands++; + } + + for (int i = 0; i < ncommands; i++) + char *command = input->one(commands[i]); + + for (int i = 0; i < ncommands; i++) delete [] commands[i]; + delete [] commands; + + return; + } + + // done if no "elif" or "else" + + if (iarg == narg) return; + + // check "elif" or "else" until find commands to execute + // substitute for variables and evaluate Boolean expression for "elif" + // bound and execute "elif" or "else" commands + + while (1) { + if (iarg+2 > narg) error->all("Illegal if command"); + if (strcmp(arg[iarg],"elif") == 0) { + substitute(arg[iarg+1],0); + btest = variable->evaluate_boolean(arg[iarg+1]); + first = iarg+2; + } else { + btest = 1.0; + first = iarg+1; + } + + iarg = first; + while (iarg < narg && + (strcmp(arg[iarg],"elif") != 0 && strcmp(arg[iarg],"else") != 0)) + iarg++; last = iarg-1; - } else { - iarg = 4; - while (iarg < narg && strcmp(arg[iarg],"else") != 0) iarg++; - if (iarg == narg) return; - first = iarg+1; - last = narg-1; + + if (btest == 0.0) continue; + + int ncommands = last-first + 1; + if (ncommands <= 0) error->all("Illegal if command"); + + char **commands = new char*[ncommands]; + ncommands = 0; + for (int i = first; i <= last; i++) { + int n = strlen(arg[i]) + 1; + if (n == 1) error->all("Illegal if command"); + commands[ncommands] = new char[n]; + strcpy(commands[ncommands],arg[i]); + ncommands++; + } + + // execute the list of commands + + for (int i = 0; i < ncommands; i++) + char *command = input->one(commands[i]); + + // clean up + + for (int i = 0; i < ncommands; i++) delete [] commands[i]; + delete [] commands; + + return; } - - int ncommands = last-first + 1; - if (ncommands <= 0) error->all("Illegal if command"); - - // make copies of arg strings that are commands - // required because re-parsing commands via one() will wipe out args - - char **commands = new char*[ncommands]; - ncommands = 0; - for (int i = first; i <= last; i++) { - int n = strlen(arg[i]) + 1; - if (n == 1) error->all("Illegal if command"); - commands[ncommands] = new char[n]; - strcpy(commands[ncommands],arg[i]); - ncommands++; - } - - // execute the list of commands - - for (int i = 0; i < ncommands; i++) - char *command = input->one(commands[i]); - - // clean up - - for (int i = 0; i < ncommands; i++) delete [] commands[i]; - delete [] commands; } /* ---------------------------------------------------------------------- */ @@ -685,17 +723,14 @@ void Input::next_command() void Input::print() { - if (narg == 0) error->all("Illegal print command"); + if (narg != 1) error->all("Illegal print command"); - // substitute for $ variables (no printing) - // print args one at a time, separated by spaces + // substitute for $ variables (no printing) and print arg - for (int i = 0; i < narg; i++) { - substitute(arg[i],0); - if (me == 0) { - if (screen) fprintf(screen,"%s ",arg[i]); - if (logfile) fprintf(logfile,"%s ",arg[i]); - } + substitute(arg[0],0); + if (me == 0) { + if (screen) fprintf(screen,"%s ",arg[0]); + if (logfile) fprintf(logfile,"%s ",arg[0]); } if (me == 0) { diff --git a/src/variable.cpp b/src/variable.cpp index 7bf2fde1cd..b1e4b51773 100644 --- a/src/variable.cpp +++ b/src/variable.cpp @@ -608,7 +608,7 @@ void Variable::copy(int narg, char **from, char **to) constant = PI thermo keyword = ke, vol, atoms, ... math operation = (),-x,x+y,x-y,x*y,x/y,x^y, - x==y,x!=y,xy,x>=y, + x==y,x!=y,xy,x>=y,x&&y,x||y, sqrt(x),exp(x),ln(x),log(x), sin(x),cos(x),tan(x),asin(x),atan2(y,x),... group function = count(group), mass(group), xcm(group,x), ... @@ -3069,3 +3069,175 @@ void Variable::print_tree(Tree *tree, int level) if (tree->right) print_tree(tree->right,level+1); return; } + +/* ---------------------------------------------------------------------- + recursive evaluation of string str + called from "if" command in input script + str is a boolean expression containing one or more items: + number = 0.0, -5.45, 2.8e-4, ... + math operation = (),x==y,x!=y,xy,x>=y,x&&y,x||y +------------------------------------------------------------------------- */ + +double Variable::evaluate_boolean(char *str) +{ + int op,opprevious; + double value1,value2; + char onechar; + char *ptr; + + double argstack[MAXLEVEL]; + int opstack[MAXLEVEL]; + int nargstack = 0; + int nopstack = 0; + + int i = 0; + int expect = ARG; + + while (1) { + onechar = str[i]; + + // whitespace: just skip + + if (isspace(onechar)) i++; + + // ---------------- + // parentheses: recursively evaluate contents of parens + // ---------------- + + else if (onechar == '(') { + if (expect == OP) error->all("Invalid Boolean syntax in if command"); + expect = OP; + + char *contents; + i = find_matching_paren(str,i,contents); + i++; + + // evaluate contents and push on stack + + argstack[nargstack++] = evaluate_boolean(contents); + + delete [] contents; + + // ---------------- + // number: push value onto stack + // ---------------- + + } else if (isdigit(onechar) || onechar == '.') { + if (expect == OP) error->all("Invalid Boolean syntax in if command"); + expect = OP; + + // istop = end of number, including scientific notation + + int istart = i; + while (isdigit(str[i]) || str[i] == '.') i++; + if (str[i] == 'e' || str[i] == 'E') { + i++; + if (str[i] == '+' || str[i] == '-') i++; + while (isdigit(str[i])) i++; + } + int istop = i - 1; + + int n = istop - istart + 1; + char *number = new char[n+1]; + strncpy(number,&str[istart],n); + number[n] = '\0'; + + argstack[nargstack++] = atof(number); + + delete [] number; + + // ---------------- + // Boolean operator, including end-of-string + // ---------------- + + } else if (strchr("<>=!&|\0",onechar)) { + if (onechar == '=') { + if (str[i+1] != '=') + error->all("Invalid Boolean syntax in if command"); + op = EQ; + i++; + } else if (onechar == '!') { + if (str[i+1] != '=') + error->all("Invalid Boolean syntax in if command"); + op = NE; + i++; + } else if (onechar == '<') { + if (str[i+1] != '=') op = LT; + else { + op = LE; + i++; + } + } else if (onechar == '>') { + if (str[i+1] != '=') op = GT; + else { + op = GE; + i++; + } + } else if (onechar == '&') { + if (str[i+1] != '&') + error->all("Invalid Boolean syntax in if command"); + op = AND; + i++; + } else if (onechar == '|') { + if (str[i+1] != '|') + error->all("Invalid Boolean syntax in if command"); + op = OR; + i++; + } else op = DONE; + + i++; + + if (expect == ARG) error->all("Invalid Boolean syntax in if command"); + expect = ARG; + + // evaluate stack as deep as possible while respecting precedence + // before pushing current op onto stack + + while (nopstack && precedence[opstack[nopstack-1]] >= precedence[op]) { + opprevious = opstack[--nopstack]; + + value2 = argstack[--nargstack]; + value1 = argstack[--nargstack]; + + if (opprevious == EQ) { + if (value1 == value2) argstack[nargstack++] = 1.0; + else argstack[nargstack++] = 0.0; + } else if (opprevious == NE) { + if (value1 != value2) argstack[nargstack++] = 1.0; + else argstack[nargstack++] = 0.0; + } else if (opprevious == LT) { + if (value1 < value2) argstack[nargstack++] = 1.0; + else argstack[nargstack++] = 0.0; + } else if (opprevious == LE) { + if (value1 <= value2) argstack[nargstack++] = 1.0; + else argstack[nargstack++] = 0.0; + } else if (opprevious == GT) { + if (value1 > value2) argstack[nargstack++] = 1.0; + else argstack[nargstack++] = 0.0; + } else if (opprevious == GE) { + if (value1 >= value2) argstack[nargstack++] = 1.0; + else argstack[nargstack++] = 0.0; + } else if (opprevious == AND) { + if (value1 != 0.0 && value2 != 0.0) argstack[nargstack++] = 1.0; + else argstack[nargstack++] = 0.0; + } else if (opprevious == OR) { + if (value1 != 0.0 || value2 != 0.0) argstack[nargstack++] = 1.0; + else argstack[nargstack++] = 0.0; + } + } + + // if end-of-string, break out of entire formula evaluation loop + + if (op == DONE) break; + + // push current operation onto stack + + opstack[nopstack++] = op; + + } else error->all("Invalid Boolean syntax in if command"); + } + + if (nopstack) error->all("Invalid Boolean syntax in if command"); + if (nargstack != 1) error->all("Invalid Boolean syntax in if command"); + return argstack[0]; +} diff --git a/src/variable.h b/src/variable.h index 841abb3229..1109f57b4d 100644 --- a/src/variable.h +++ b/src/variable.h @@ -32,6 +32,7 @@ class Variable : protected Pointers { double compute_equal(int); void compute_atom(int, int, double *, int, int); int int_between_brackets(char *&); + double evaluate_boolean(char *); private: int me;