USGS

Isis 3.0 Object Programmers' Reference

Home

InfixToPostfix.cpp
Go to the documentation of this file.
1 
23 #include "InfixToPostfix.h"
24 
25 #include <iostream>
26 
27 #include "IException.h"
28 #include "IString.h"
29 
30 using namespace std;
31 
32 namespace Isis {
34  InfixToPostfix::InfixToPostfix() {
35  initialize();
36  }
37 
38  InfixToPostfix::~InfixToPostfix() {
39  uninitialize();
40  }
41 
46  void InfixToPostfix::initialize() {
47  p_operators.push_back(new InfixOperator(7, "^"));
48  p_operators.push_back(new InfixOperator(5, "/"));
49  p_operators.push_back(new InfixOperator(5, "*"));
50  p_operators.push_back(new InfixOperator(3, "<<"));
51  p_operators.push_back(new InfixOperator(3, ">>"));
52  p_operators.push_back(new InfixOperator(2, "+"));
53  p_operators.push_back(new InfixOperator(2, "-"));
54  p_operators.push_back(new InfixOperator(1, ">"));
55  p_operators.push_back(new InfixOperator(1, "<"));
56  p_operators.push_back(new InfixOperator(1, ">="));
57  p_operators.push_back(new InfixOperator(1, "<="));
58  p_operators.push_back(new InfixOperator(1, "=="));
59  p_operators.push_back(new InfixOperator(1, "!="));
60  p_operators.push_back(new InfixOperator(-1, "("));
61 
62  // This makes multiple argument functions work
63  p_operators.push_back(new InfixOperator(-1, ","));
64 
65  p_operators.push_back(new InfixFunction("--", 1));
66  p_operators.push_back(new InfixFunction("neg", 1));
67  p_operators.push_back(new InfixFunction("sqrt", 1));
68  p_operators.push_back(new InfixFunction("abs", 1));
69  p_operators.push_back(new InfixFunction("sin", 1));
70  p_operators.push_back(new InfixFunction("cos", 1));
71  p_operators.push_back(new InfixFunction("tan", 1));
72  p_operators.push_back(new InfixFunction("csc", 1));
73  p_operators.push_back(new InfixFunction("sec", 1));
74  p_operators.push_back(new InfixFunction("cot", 1));
75  p_operators.push_back(new InfixFunction("asin", 1));
76  p_operators.push_back(new InfixFunction("acos", 1));
77  p_operators.push_back(new InfixFunction("atan", 1));
78  p_operators.push_back(new InfixFunction("atan2", 2));
79  p_operators.push_back(new InfixFunction("sinh", 1));
80  p_operators.push_back(new InfixFunction("cosh", 1));
81  p_operators.push_back(new InfixFunction("tanh", 1));
82  p_operators.push_back(new InfixFunction("asinh", 1));
83  p_operators.push_back(new InfixFunction("acosh", 1));
84  p_operators.push_back(new InfixFunction("atanh", 1));
85  p_operators.push_back(new InfixFunction("log", 1));
86  p_operators.push_back(new InfixFunction("log10", 1));
87  p_operators.push_back(new InfixFunction("ln", 1));
88  p_operators.push_back(new InfixFunction("degs", 1));
89  p_operators.push_back(new InfixFunction("rads", 1));
90  p_operators.push_back(new InfixFunction("linemin", 1));
91  p_operators.push_back(new InfixFunction("linemax", 1));
92  p_operators.push_back(new InfixFunction("min", 2));
93  p_operators.push_back(new InfixFunction("max", 2));
94  p_operators.push_back(new InfixFunction("line", 0));
95  p_operators.push_back(new InfixFunction("sample", 0));
96  p_operators.push_back(new InfixFunction("band", 0));
97  p_operators.push_back(new InfixFunction("pi", 0));
98  p_operators.push_back(new InfixFunction("e", 0));
99  }
100 
104  void InfixToPostfix::uninitialize() {
105  for(int i = 0; i < p_operators.size(); i ++) {
106  delete p_operators[i];
107  }
108 
109  p_operators.clear();
110  }
111 
124  QString InfixToPostfix::cleanSpaces(QString equation) {
125  IString equationIStr = equation;
126  IString clean = "";
127  while(!equationIStr.empty()) {
128  IString data = equationIStr.Token(" ");
129  if(data.empty()) {
130  continue;
131  }
132 
133  if(clean.empty()) {
134  clean = data;
135  }
136  else {
137  clean += " " + data;
138  }
139  }
140 
141  return clean.ToQt();
142  }
143 
154  QString InfixToPostfix::convert(const QString &infix) {
155  // Prep our equation for the conversion
156  IString equation = tokenizeEquation(infix);
157  IString postfix = "";
158 
159  // The algorithm uses a stack
160  std::stack<InfixOperator> theStack;
161 
162  // Use this to look for two operands in a row. If we find such,
163  // then we know the infix equation is bogus.
164  int numConsecutiveOperands = 0;
165 
166  // Use this to look for two operators in a row. If we find such,
167  // then we know the infix equation is bogus.
168  int numConsecutiveOperators = 0;
169 
170  // We'll use tokens to get through the entire equation, which is space-delimited
171  // because of TokenizeEquation. So continue processing until we're out of tokens
172  // and the string is empty.
173  while(!equation.empty()) {
174 
175  // There will be no empty tokens, so don't worry about checking for it.
176  // TokenizeEquation cleans excess spaces in it's return value.
177  QString data = equation.Token(" ").ToQt();
178 
179  if(data.compare("(") == 0) {
180  theStack.push(*findOperator(data));
181  }
182  else if(data.compare(")") == 0) {
183  QString postfixQStr = postfix.ToQt();
184  closeParenthesis(postfixQStr, theStack);
185  postfix = postfixQStr;
186  }
187  else if(isKnownSymbol(data)) {
188  QString postfixQStr = postfix.ToQt();
189  addOperator(postfixQStr, *findOperator(data), theStack);
190  postfix = postfixQStr;
191 
192  if(isFunction(data)) {
193  // For a general check, zero single argument functions the
194  // same as an operand.
195  if(((InfixFunction *)findOperator(data))->argumentCount() == 0) {
196  numConsecutiveOperators = 0;
197  numConsecutiveOperands ++;
198  }
199  else {
200  // We have a function with arguments, an operand or function call is expected next
201  numConsecutiveOperators = 1;
202  numConsecutiveOperands = 0;
203  }
204  }
205  else {
206  // We found an operator, not a function, so we expect a number next
207  numConsecutiveOperators ++;
208  numConsecutiveOperands = 0;
209  }
210  }
211  else {
212  try {
213  // Make sure this is truly an operand and not an operator by casting it
214  toDouble(data);
215  }
216  catch(IException &) {
217  throw IException(IException::User,
218  "The operator '" + data + "' is not recognized.",
219  _FILEINFO_);
220  }
221 
222  // This was clearly an operand at this point
223  numConsecutiveOperators = 0;
224  numConsecutiveOperands ++;
225 
226  postfix += IString(' ' + data + ' ');
227  }
228 
229  // If we found consecutive operators or operands, tell the user
230  if(numConsecutiveOperators > 1) {
231  throw IException(IException::User, "Missing an operand near the operator '" + data + "'.", _FILEINFO_);
232  }
233  else if(numConsecutiveOperands > 1) {
234  throw IException(IException::User, "Missing an operator before " + data + ".", _FILEINFO_);
235  }
236  }
237 
238  while(!theStack.empty()) {
239  IString op = theStack.top().outputString();
240 
241  // Any opening parentheses here are invalid at this point
242  if(op == "(") {
243  throw IException(IException::User,
244  "There are too many opening parentheses ('(') in the equation.",
245  _FILEINFO_);
246  }
247 
248  postfix += ' ' + op + ' ';
249  theStack.pop();
250  }
251 
252  // The , is set to an operator that causes multiple-argument functions to work, but
253  // it needs to be stripped out for postfix. This is the correct way to do it.
254  postfix = postfix.Remove(",");
255 
256  // Clean spaces just to double check and return our postfix answer
257  return cleanSpaces(postfix.ToQt());
258  }
259 
268  bool InfixToPostfix::isKnownSymbol(QString representation) {
269  for(int i = 0; i < p_operators.size(); i++) {
270  if(representation.compare(p_operators[i]->inputString()) == 0) {
271  return true;
272  }
273  }
274 
275  return false;
276  }
277 
285  bool InfixToPostfix::isFunction(QString representation) {
286  if(isKnownSymbol(representation)) {
287  return findOperator(representation)->isFunction();
288  }
289  else {
290  return false;
291  }
292  }
293 
302  void InfixToPostfix::addOperator(QString &postfix, const InfixOperator &op, std::stack<InfixOperator> &theStack) {
303  while(!theStack.empty()) {
304  InfixOperator top = theStack.top();
305  theStack.pop();
306 
307  if(top.inputString().compare("(") == 0) {
308  theStack.push(top);
309  break;
310  }
311 
312  if(top.precedence() < op.precedence()) {
313  theStack.push(top);
314  break;
315  }
316  else {
317  postfix += ' ' + top.outputString() + ' ';
318  }
319  }
320 
321  theStack.push(op);
322  }
323 
331  void InfixToPostfix::closeParenthesis(QString &postfix, std::stack<InfixOperator> &theStack) {
332  bool openingFound = false;
333  while(!theStack.empty()) {
334  InfixOperator op = theStack.top();
335  theStack.pop();
336 
337  if(op.inputString().compare("(") == 0) {
338  openingFound = true;
339  break;
340  }
341  else {
342  postfix += ' ' + op.outputString() + ' ';
343  }
344  }
345 
346  if(!openingFound) {
347  throw IException(IException::User,
348  "There are too many closing parentheses (')') in the equation.",
349  _FILEINFO_);
350  }
351  }
352 
362  InfixOperator *InfixToPostfix::findOperator(QString representation) {
363  for(int i = 0; i < p_operators.size(); i++) {
364  if(representation.compare(p_operators[i]->inputString()) == 0) {
365  return p_operators[i];
366  }
367  }
368 
369  // Nothing found
370  throw IException(IException::User, "The operator '" + representation + "' is not recognized.", _FILEINFO_);
371  }
372 
384  QString InfixToPostfix::tokenizeEquation(const QString &equation) {
385  IString output = "";
386 
387  // Insert whitespace, make everything lowercase, and change all braces to
388  // parenthesis
389  for(int i = 0; i < equation.size(); i++) {
390  // Ensure there is whitespace in the equation
391  if(!equation[i].isLetterOrNumber() && !equation[i].isSpace() && equation[i] != '.') {
392  // Convert all braces to parens
393  if(equation[i] == '[' || equation[i] == '{') {
394  output += " ( ";
395  }
396  else if(equation[i] == ']' || equation[i] == '}') {
397  output += " ) ";
398  }
399  // Test for multicharacter operators
400  else if(i < equation.size() - 1 && equation[i] == '-' && equation[i+1] == '-') {
401  output += " -- ";
402  i++;
403  }
404  else if(i < equation.size() - 1 && equation[i] == '<' && equation[i+1] == '<') {
405  output += " << ";
406  i++;
407  }
408  else if(i < equation.size() - 1 && equation[i] == '>' && equation[i+1] == '>') {
409  output += " >> ";
410  i++;
411  }
412  else if(i < equation.size() - 1 && equation[i] == '>' && equation[i+1] == '=') {
413  output += " >= ";
414  i++;
415  }
416  else if(i < equation.size() - 1 && equation[i] == '<' && equation[i+1] == '=') {
417  output += " <= ";
418  i++;
419  }
420  else if(i < equation.size() - 1 && equation[i] == '=' && equation[i+1] == '=') {
421  output += " == ";
422  i++;
423  }
424  else if(i < equation.size() - 1 && equation[i] == '!' && equation[i+1] == '=') {
425  output += " != ";
426  i++;
427  }
428  // Take care of scientific notiation where the exponent is negative
429  else if((i > 1) && equation[i] == '-' && equation[i-1].toLower() == 'e' && equation[i-2].isLetterOrNumber()) {
430  output += equation[i].toAscii();
431  }
432  // Look for negative operator disguised as '-'
433  else if(equation[i] == '-') {
434  bool isNegative = true;
435 
436  // If we run into a '(' or the beginning, then the '-' must mean '--' really.
437  for(int index = i - 1; index >= 0; index --) {
438  if(equation[index] == ' ') {
439  continue;
440  }
441 
442  if(equation[index] != '(' && equation[index] != '/' &&
443  equation[index] != '*' && equation[index] != '+') {
444  isNegative = false;
445  break;
446  }
447 
448  break;
449  }
450 
451  if(isNegative) {
452  output += " -- ";
453  }
454  else {
455  output += " - ";
456  }
457  }
458  else {
459  output += ' ';
460  output += equation[i].toAscii();
461  output += ' ';
462  }
463  }
464  else {
465  output += equation[i].toAscii();
466  }
467  }
468 
469  QString cleanedEquation = cleanSpaces(formatFunctionCalls(output.DownCase().ToQt()));
470 
471  return cleanedEquation;
472  }
473 
485  QString InfixToPostfix::formatFunctionCalls(QString equation) {
486  // Clean our space-delimited equation
487  equation = cleanSpaces(equation);
488  QString output = "";
489 
490  // We'll use tokens to get through the entire equation, which is space-delimited.
491  // So continue processing until we're out of tokens and the string is empty.
492  while(!equation.isEmpty()) {
493  IString tmp = equation;
494 
495  QString element = tmp.Token(" ").ToQt();
496  equation = tmp.ToQt();
497 
498  // Did we find a function? Figure out what it is!
499  if(isFunction(element)) {
500  // Point to the function. We know if IsFunction returned true,
501  // the result is really a InfixFunction*
502  InfixFunction *func = (InfixFunction *)findOperator(element);
503 
504  // We want to wrap the entire thing in parentheses, and it's argument string.
505  // So sin(.5)^2 becomes (sin(.5))^2
506  output += " ( " + func->inputString() + " (";
507 
508  // Deal with 0-argument functions
509  if(func->argumentCount() == 0) {
510  IString tmp = equation;
511  QString next = tmp.Token(" ").ToQt();
512  equation = tmp.ToQt();
513 
514  // If they didn't add parentheses around the zero-argument
515  // function, we still know what they mean. Close the arguments
516  // and the wrapping parentheses.
517  if(next != "(") {
518  output += " ) ) ";
519 
520  // Step back, we did too much
521  equation = next + " " + equation;
522  }
523  else {
524  IString tmp = equation;
525  // We see a zero-arg function, and we grabbed an open parenthesis from it.
526  // Make sure the next thing is a close or we have a problem.
527  if(tmp.Token(" ") != ")") {
528  throw IException(IException::User,
529  "The function " + func->inputString() + " should not have any arguments.",
530  _FILEINFO_);
531  }
532  equation = tmp.ToQt();
533 
534  // Close the arguments and the wrapping parentheses. They wrote their call correct :)
535  output += " ) ) ";
536  }
537  }
538  else {
539  // Deal with 1+ argument functions by parsing out the arguments
540 
541  IString tmp = equation;
542 
543  // Make sure the user put parentheses around these, otherwise we're left in the dark.
544  if (func->argumentCount() > 1 && tmp.Token(" ") != "(") {
545  throw IException(IException::User,
546  "Missing parenthesis after " + func->inputString(),
547  _FILEINFO_);
548  }
549 
550  equation = tmp.ToQt();
551 
552  // Single argument missing parenthesis?
553  if(func->argumentCount() == 1) {
554 
555  IString tmp = equation;
556  QString argument = tmp.Token(" ").ToQt();
557  equation = tmp.ToQt();
558 
559  if(argument != "(") {
560  // We might have a problem. They're calling a function without adding parentheses....
561  // unless it's a negate, because we insert those, tell them their mistake. It's not
562  // my job to figure out what they mean!
563  if(func->inputString() != "--") {
564  throw IException(IException::User,
565  "Missing parenthesis after " + func->inputString(),
566  _FILEINFO_);
567  }
568 
569  // It's a negate without parentheses, so they mean the next term?
570  if(!isFunction(argument)) {
571  // If it isn't a function, it's safe to just append
572  output += " " + formatFunctionCalls(argument) + " ) ) ";
573  continue;
574  }
575  else {
576  // We are negating a function result. We must do a mini-parse to figure out
577  // the function and resursively call, then append the negation.
578  QString functionName = argument;
579 
580  IString tmp = equation;
581  QString openParen = tmp.Token(" ").ToQt();
582  equation = tmp.ToQt();
583 
584  // No open parens? Call ourself again with this supposed function
585  if(openParen != "(") {
586  output += " " + formatFunctionCalls(functionName) + " ) ) ";
587  equation = openParen + " " + equation;
588  continue;
589  }
590  else {
591  functionName += " (";
592  }
593 
594  // Parse out our equation quickly, call ourself with it to properly handle it,
595  // and append the negation.
596  int numParens = 0;
597  while(numParens > -1) {
598  IString tmp = equation;
599  QString newElem = tmp.Token(" ").ToQt();
600  equation = tmp.ToQt();
601 
602  if(newElem == "") {
603  throw IException(IException::User,
604  "Missing closing parentheses after '" + argument + "'.",
605  _FILEINFO_);
606  }
607 
608  if(newElem == "(") numParens++;
609  else if(newElem == ")") numParens--;
610 
611  functionName += " " + newElem;
612  }
613 
614  output += " " + formatFunctionCalls(functionName) + " ) ) ";
615  continue;
616  }
617  }
618  }
619 
629  QString argument = "";
630  int numParens = 0;
631  int argNum = 0;
632  while(argNum < func->argumentCount()) {
633  IString tmp = equation;
634  QString elem = tmp.Token(" ").ToQt();
635  equation = tmp.ToQt();
636 
637  // Ran out of data, the function call is not complete.
638  if(elem == "") {
639  throw IException(IException::User,
640  "The definition of '" + func->inputString() + "' is not complete.",
641  _FILEINFO_);
642  }
643 
644  if(elem == "(") {
645  numParens ++;
646  argument += " (";
647  }
648  else if(elem == ")") {
649  numParens --;
650 
651  // Ignore last close, it's not part of the argument and will be added later
652  if(numParens != -1) {
653  argument += " )";
654  }
655  }
656  else if(elem == "," && numParens == 0) {
657  checkArgument(func->inputString(), argNum, argument);
658  argument = formatFunctionCalls(argument);
659  output += " ( " + argument + " ) , ";
660  argNum++;
661  argument = "";
662 
663  // Too many arguments? We don't expect a comma delimiter on the last argument.
664  if(argNum == func->argumentCount()) {
665  throw IException(IException::User,
666  "There were too many arguments supplied to the function '" + func->inputString() + "'.",
667  _FILEINFO_);
668  }
669  }
670  else {
671  argument += " " + elem;
672  }
673 
674  if(argNum == func->argumentCount() - 1 && numParens == -1) {
675  checkArgument(func->inputString(), argNum, argument);
676  argument = formatFunctionCalls(argument);
677  // close function call & function wrap
678  output += " " + argument + " ) ) ";
679  argNum++;
680  argument = "";
681  }
682  // Closed the function early?
683  else if(numParens == -1) {
684  throw IException(IException::User,
685  "There were not enough arguments supplied to the function '" + func->inputString() + "'.",
686  _FILEINFO_);
687  }
688  }
689  }
690  }
691  // Not a function, preserve & ignore
692  else {
693  output = output + " " + element;
694  }
695  }
696 
697  return output;
698  }
699 
700  void InfixToPostfix::checkArgument(QString funcName, int argNum, QString argument) {
701  argument = argument.remove(QRegExp("[ ()]"));
702 
703  if(argument == "") {
704  throw IException(IException::User,
705  "Argument " + toString(argNum + 1) + " in function " + funcName + " must not be empty.",
706  _FILEINFO_);
707  }
708  }
709 }