Isis 3 Programmer Reference
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() &&
392  equation[i] != '.' && equation[i] != '_') {
393  // Convert all braces to parens
394  if(equation[i] == '[' || equation[i] == '{') {
395  output += " ( ";
396  }
397  else if(equation[i] == ']' || equation[i] == '}') {
398  output += " ) ";
399  }
400  // Test for multicharacter operators
401  else if(i < equation.size() - 1 && equation[i] == '-' && equation[i+1] == '-') {
402  output += " -- ";
403  i++;
404  }
405  else if(i < equation.size() - 1 && equation[i] == '<' && equation[i+1] == '<') {
406  output += " << ";
407  i++;
408  }
409  else if(i < equation.size() - 1 && equation[i] == '>' && equation[i+1] == '>') {
410  output += " >> ";
411  i++;
412  }
413  else if(i < equation.size() - 1 && equation[i] == '>' && equation[i+1] == '=') {
414  output += " >= ";
415  i++;
416  }
417  else if(i < equation.size() - 1 && equation[i] == '<' && equation[i+1] == '=') {
418  output += " <= ";
419  i++;
420  }
421  else if(i < equation.size() - 1 && equation[i] == '=' && equation[i+1] == '=') {
422  output += " == ";
423  i++;
424  }
425  else if(i < equation.size() - 1 && equation[i] == '!' && equation[i+1] == '=') {
426  output += " != ";
427  i++;
428  }
429  else if(i < equation.size() - 1 && equation[i] == '|' && equation[i+1] == '|') {
430  output += " || ";
431  i++;
432  }
433  else if(i < equation.size() - 1 && equation[i] == '&' && equation[i+1] == '&') {
434  output += " && ";
435  i++;
436  }
437  // Take care of scientific notiation where the exponent is negative
438  else if((i > 1) && equation[i] == '-' && equation[i-1].toLower() == 'e' && equation[i-2].isLetterOrNumber()) {
439  output += equation[i].toLatin1();
440  }
441  // Look for negative operator disguised as '-'
442  else if(equation[i] == '-') {
443  bool isNegative = true;
444 
445  // If we run into a '(' or the beginning, then the '-' must mean '--' really.
446  for(int index = i - 1; index >= 0; index --) {
447  if(equation[index] == ' ') {
448  continue;
449  }
450 
451  if(equation[index] != '(' && equation[index] != '/' &&
452  equation[index] != '*' && equation[index] != '+') {
453  isNegative = false;
454  break;
455  }
456 
457  break;
458  }
459 
460  if(isNegative) {
461  output += " -- ";
462  }
463  else {
464  output += " - ";
465  }
466  }
467  else {
468  output += ' ';
469  output += equation[i].toLatin1();
470  output += ' ';
471  }
472  }
473  else {
474  output += equation[i].toLatin1();
475  }
476  }
477 
478  QString cleanedEquation = cleanSpaces(formatFunctionCalls(output.DownCase().ToQt()));
479 
480  return cleanedEquation;
481  }
482 
494  QString InfixToPostfix::formatFunctionCalls(QString equation) {
495  // Clean our space-delimited equation
496  equation = cleanSpaces(equation);
497  QString output = "";
498 
499  // We'll use tokens to get through the entire equation, which is space-delimited.
500  // So continue processing until we're out of tokens and the string is empty.
501  while(!equation.isEmpty()) {
502  IString tmp = equation;
503 
504  QString element = tmp.Token(" ").ToQt();
505  equation = tmp.ToQt();
506 
507  // Did we find a function? Figure out what it is!
508  if(isFunction(element)) {
509  // Point to the function. We know if IsFunction returned true,
510  // the result is really a InfixFunction*
511  InfixFunction *func = (InfixFunction *)findOperator(element);
512 
513  // We want to wrap the entire thing in parentheses, and it's argument string.
514  // So sin(.5)^2 becomes (sin(.5))^2
515  output += " ( " + func->inputString() + " (";
516 
517  // Deal with 0-argument functions
518  if(func->argumentCount() == 0) {
519  IString tmp = equation;
520  QString next = tmp.Token(" ").ToQt();
521  equation = tmp.ToQt();
522 
523  // If they didn't add parentheses around the zero-argument
524  // function, we still know what they mean. Close the arguments
525  // and the wrapping parentheses.
526  if(next != "(") {
527  output += " ) ) ";
528 
529  // Step back, we did too much
530  equation = next + " " + equation;
531  }
532  else {
533  IString tmp = equation;
534  // We see a zero-arg function, and we grabbed an open parenthesis from it.
535  // Make sure the next thing is a close or we have a problem.
536  if(tmp.Token(" ") != ")") {
537  throw IException(IException::User,
538  "The function " + func->inputString() + " should not have any arguments.",
539  _FILEINFO_);
540  }
541  equation = tmp.ToQt();
542 
543  // Close the arguments and the wrapping parentheses. They wrote their call correct :)
544  output += " ) ) ";
545  }
546  }
547  else {
548  // Deal with 1+ argument functions by parsing out the arguments
549 
550  IString tmp = equation;
551 
552  // Make sure the user put parentheses around these, otherwise we're left in the dark.
553  if (func->argumentCount() > 1 && tmp.Token(" ") != "(") {
554  throw IException(IException::User,
555  "Missing parenthesis after " + func->inputString(),
556  _FILEINFO_);
557  }
558 
559  equation = tmp.ToQt();
560 
561  // Single argument missing parenthesis?
562  if(func->argumentCount() == 1) {
563 
564  IString tmp = equation;
565  QString argument = tmp.Token(" ").ToQt();
566  equation = tmp.ToQt();
567 
568  if(argument != "(") {
569  // We might have a problem. They're calling a function without adding parentheses....
570  // unless it's a negate, because we insert those, tell them their mistake. It's not
571  // my job to figure out what they mean!
572  if(func->inputString() != "--") {
573  throw IException(IException::User,
574  "Missing parenthesis after " + func->inputString(),
575  _FILEINFO_);
576  }
577 
578  // It's a negate without parentheses, so they mean the next term?
579  if(!isFunction(argument)) {
580  // If it isn't a function, it's safe to just append
581  output += " " + formatFunctionCalls(argument) + " ) ) ";
582  continue;
583  }
584  else {
585  // We are negating a function result. We must do a mini-parse to figure out
586  // the function and resursively call, then append the negation.
587  QString functionName = argument;
588 
589  IString tmp = equation;
590  QString openParen = tmp.Token(" ").ToQt();
591  equation = tmp.ToQt();
592 
593  // No open parens? Call ourself again with this supposed function
594  if(openParen != "(") {
595  output += " " + formatFunctionCalls(functionName) + " ) ) ";
596  equation = openParen + " " + equation;
597  continue;
598  }
599  else {
600  functionName += " (";
601  }
602 
603  // Parse out our equation quickly, call ourself with it to properly handle it,
604  // and append the negation.
605  int numParens = 0;
606  while(numParens > -1) {
607  IString tmp = equation;
608  QString newElem = tmp.Token(" ").ToQt();
609  equation = tmp.ToQt();
610 
611  if(newElem == "") {
612  throw IException(IException::User,
613  "Missing closing parentheses after '" + argument + "'.",
614  _FILEINFO_);
615  }
616 
617  if(newElem == "(") numParens++;
618  else if(newElem == ")") numParens--;
619 
620  functionName += " " + newElem;
621  }
622 
623  output += " " + formatFunctionCalls(functionName) + " ) ) ";
624  continue;
625  }
626  }
627  }
628 
638  QString argument = "";
639  int numParens = 0;
640  int argNum = 0;
641  while(argNum < func->argumentCount()) {
642  IString tmp = equation;
643  QString elem = tmp.Token(" ").ToQt();
644  equation = tmp.ToQt();
645 
646  // Ran out of data, the function call is not complete.
647  if(elem == "") {
648  throw IException(IException::User,
649  "The definition of '" + func->inputString() + "' is not complete.",
650  _FILEINFO_);
651  }
652 
653  if(elem == "(") {
654  numParens ++;
655  argument += " (";
656  }
657  else if(elem == ")") {
658  numParens --;
659 
660  // Ignore last close, it's not part of the argument and will be added later
661  if(numParens != -1) {
662  argument += " )";
663  }
664  }
665  else if(elem == "," && numParens == 0) {
666  checkArgument(func->inputString(), argNum, argument);
667  argument = formatFunctionCalls(argument);
668  output += " ( " + argument + " ) , ";
669  argNum++;
670  argument = "";
671 
672  // Too many arguments? We don't expect a comma delimiter on the last argument.
673  if(argNum == func->argumentCount()) {
674  throw IException(IException::User,
675  "There were too many arguments supplied to the function '" + func->inputString() + "'.",
676  _FILEINFO_);
677  }
678  }
679  else {
680  argument += " " + elem;
681  }
682 
683  if(argNum == func->argumentCount() - 1 && numParens == -1) {
684  checkArgument(func->inputString(), argNum, argument);
685  argument = formatFunctionCalls(argument);
686  // close function call & function wrap
687  output += " " + argument + " ) ) ";
688  argNum++;
689  argument = "";
690  }
691  // Closed the function early?
692  else if(numParens == -1) {
693  throw IException(IException::User,
694  "There were not enough arguments supplied to the function '" + func->inputString() + "'.",
695  _FILEINFO_);
696  }
697  }
698  }
699  }
700  // Not a function, preserve & ignore
701  else {
702  output = output + " " + element;
703  }
704  }
705 
706  return output;
707  }
708 
709  void InfixToPostfix::checkArgument(QString funcName, int argNum, QString argument) {
710  argument = argument.remove(QRegExp("[ ()]"));
711 
712  if(argument == "") {
713  throw IException(IException::User,
714  "Argument " + toString(argNum + 1) + " in function " + funcName + " must not be empty.",
715  _FILEINFO_);
716  }
717  }
718 }
Namespace for the standard library.
QString toString(bool boolToConvert)
Global function to convert a boolean to a string.
Definition: IString.cpp:226
double toDouble(const QString &string)
Global function to convert from a string to a double.
Definition: IString.cpp:164
IString Token(const IString &separator)
Returns the first token in the IString.
Definition: IString.cpp:912
InfixOperator and InfixFunction are helper classes for InfixToPostfix.
InfixOperator and InfixFunction are helper classes for InfixToPostfix.
#define _FILEINFO_
Macro for the filename and line number.
Definition: IException.h:40
QString ToQt() const
Retuns the object string as a QString.
Definition: IString.cpp:884
IString Remove(const std::string &del)
Remove all instances of any character in the string from the IString.
Definition: IString.cpp:1281
IString DownCase()
Converts all upper case letters in the object IString into lower case characters. ...
Definition: IString.cpp:659
Isis exception class.
Definition: IException.h:107
Adds specific functionality to C++ strings.
Definition: IString.h:181
Namespace for ISIS/Bullet specific routines.
Definition: Apollo.h:31