//****************************************************************************** //** SCATMECH: Polarized Light Scattering C++ Class Library //** //** File: scateval.cpp //** //** Thomas A. Germer //** Optical Technology Division, National Institute of Standards and Technology //** 100 Bureau Dr. Stop 8443; Gaithersburg, MD 20899-8443 //** Phone: (301) 975-2876; FAX: (301) 975-6991 //** Email: thomas.germer@nist.gov //** //** Version: 6.00 (February 2008) //** //****************************************************************************** #include "scateval.h" #include "scattabl.h" using namespace std; namespace SCATMECH { namespace { // List of binary operators... static const char binops[] = ",&|><=-+*/^"; // Precedence of each of the binary operators above... static const int precs[] = {0,1,1,2,2,2,3,3,4,4,6}; // This function returns the position of the character c in the string p... // (used to find precedence of operator) int member(char c,const char* p) { for (int i=0;*p;p++,i++) if (*p == c) return i; return -1; } // Check for start of a variable or function name... bool isvstart(char c) { if (isalpha(c)) return true; if (c=='#') return true; // Used by MIST to designate variables sent to model if (c=='$') return true; // Used by MIST to designate variables sent uninterpreted. if (c=='@') return true; // Used by Evaluator to designate file functions return false; } // Check to see if the character is a continuation // of a variable or function name... bool isvcont(char c) { if (isvstart(c)) return true; if (isalnum(c)) return true; if (c=='.') return true; if (c=='_') return true; return false; } } // Evaluate quantities enclosed in parentheses... // Argument specifies whether an empty parentheses is allowed. vector Evaluator:: get_paren(bool allow_empty) { if (input.peek()!='(') { error("Expected left parentheses: " + input.str()); } // Skip the parentheses... input.ignore(); // Skip whitespace... input >> ws; // If the caller doesn't mind empty expressions, i.e. function calls with no arguments... if (allow_empty && input.peek()==')') { input.ignore(); return vector(); } // Get the contents of the parentheses and place them in the string contents... string contents; int level=1; while (level>0) { if (input.eof()) error("Mismatched parentheses"); char next = input.get(); if (next==')') --level; if (next=='(') ++level; if (level>0) contents += next; } // Return the results of what was in the parentheses... return Evaluator(contents,variables,false).result; } // Read in numeric value (as opposed to an operator) from the string... void Evaluator:: get_value() { // Skip whitespace... input >> ws; // Check for a preceding sign (- or +)... char pre = input.peek(); int sign = 1; if (pre == '-') { input.get(); sign = -1; } else if (pre == '+') { input.get(); } // Check and see if the next token is a number double x; input >> x; if (!input.fail()) { // If so, push the number onto the value stack, with its sign... val_stack.push(sign*x); } else { // If not, fix error... input.clear(); // Check and see if there is a paren group... if (input.peek()=='(') { // If so, get the contents of the parentheses... vector temp = get_paren(); // The parentheses should only have one value... if (temp.size()<1) error("Empty parentheses not allowed"); if (temp.size()>1 && !top) error("Embedded parentheses group"); val_stack.push(sign*temp[0]); for (int i=1;i> ws; // Check to see if it is a variable... if (isvstart(input.peek())) { string a; while (isvcont(input.peek())) { a += input.get(); } // Skip whitespace... input >> ws; // Check and see if there are function arguments... vector args; if (input.peek()=='(') args = get_paren(true); // Get the value of the function or variable... double vv = call_function(a,args); val_stack.push(sign*vv); } else { error("Invalid value"); } } } // Skip whitespace... input >> ws; } void Evaluator:: get_operator() { // Skip whitespace... input >> ws; char next = input.peek(); int prec=member(next,binops); if (prec>=0) { while (lower_prec(precs[prec])) operate(); op_stack.push(input.get()); prec_stack.push(precs[prec]); } else error("Undefined operator: " + next); input >> ws; } void Evaluator:: evaluate() { get_value(); while (!input.eof()) { get_operator(); get_value(); } while (!op_stack.empty()) { operate(); } if (!val_stack.empty()) result.insert(result.begin(),val_stack.top()); } void Evaluator:: operate() { int op = op_stack.top(); op_stack.pop(); prec_stack.pop(); double y = val_stack.top(); val_stack.pop(); double x = val_stack.top(); val_stack.pop(); switch (op) { case '+': val_stack.push(x+y); break; case '-': val_stack.push(x-y); break; case '*': val_stack.push(x*y); break; case '/': val_stack.push(x/y); break; case '&': val_stack.push(x&&y); break; case '|': val_stack.push(x||y); break; case '<': val_stack.push(x': val_stack.push(x>y); break; case '=': val_stack.push(x==y); break; case '^': val_stack.push(pow(x,y)); break; case ',': val_stack.push(x); result.push_back(y); break; default: error("Invalid binary operator"); } } double Evaluator:: call_function(const string& s,const vector& args) { if (s[0]=='@') { string filename = s.substr(1); // Remove '@' int asize = args.size(); if (asize<1 || asize>2) error("File function " + s + " requires 1 or 2 arguments"); int column = asize==2 ? (int)(args[1]) : 2; if (column <= 1) error("File function " + s + " requires column number greater than 1"); Table table; table.read(filename,column); return table.value(args[0]); } if (args.size()==1) { if (s=="exp") return exp(args[0]); if (s=="sin") return sin(args[0]); if (s=="cos") return cos(args[0]); if (s=="tan") return tan(args[0]); if (s=="sind") return sin(args[0]*deg); if (s=="cosd") return cos(args[0]*deg); if (s=="tand") return tan(args[0]*deg); if (s=="asin") return asin(args[0]); if (s=="acos") return acos(args[0]); if (s=="atan") return atan(args[0]); if (s=="asind") return asin(args[0])/deg; if (s=="acosd") return acos(args[0])/deg; if (s=="atand") return atan(args[0])/deg; if (s=="sinh") return sinh(args[0]); if (s=="cosh") return cosh(args[0]); if (s=="tanh") return tanh(args[0]); if (s=="log") return log(args[0]); if (s=="log10") return log10(args[0]); if (s=="sqrt") return sqrt(args[0]); if (s=="abs") return fabs(args[0]); if (s=="not") return args[0]==0. ? 1. : 0.; } if (args.size()==2) { if (s=="atan2") return atan2(args[0],args[1]); if (s=="atan2d") return atan2(args[0],args[1])/deg; if (s=="min") return (args[0]args[1]) ? args[0] : args[1]; if (s=="or") return args[0]||args[1]; if (s=="nor") return !(args[0]||args[1]); if (s=="and") return args[0]&&args[1]; if (s=="nand") return !(args[0]&&args[1]); } if (args.size()==0) { VMAP::const_iterator it = variables.find(s); if (it != variables.end()) return it->second; } if (args.size()==3) { if (s=="if") return args[0]!=0 ? args[1] : args[2]; } error("Invalid function or value: " + s); return 0; } bool Evaluator:: lower_prec(int prec) { if (prec_stack.empty()) return false; if (prec<=prec_stack.top()) return true; return false; } string Evaluator:: ResultString() const { ostringstream out; if (result.size()==1) { out << result[0]; return out.str(); } else { out << '('; for (int i=0;i