Isis 3.0 Programmer Reference
Back | 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() &&
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  // Take care of scientific notiation where the exponent is negative
430  else if((i > 1) && equation[i] == '-' && equation[i-1].toLower() == 'e' && equation[i-2].isLetterOrNumber()) {
431  output += equation[i].toLatin1();
432  }
433  // Look for negative operator disguised as '-'
434  else if(equation[i] == '-') {
435  bool isNegative = true;
436 
437  // If we run into a '(' or the beginning, then the '-' must mean '--' really.
438  for(int index = i - 1; index >= 0; index --) {
439  if(equation[index] == ' ') {
440  continue;
441  }
442 
443  if(equation[index] != '(' && equation[index] != '/' &&
444  equation[index] != '*' && equation[index] != '+') {
445  isNegative = false;
446  break;
447  }
448 
449  break;
450  }
451 
452  if(isNegative) {
453  output += " -- ";
454  }
455  else {
456  output += " - ";
457  }
458  }
459  else {
460  output += ' ';
461  output += equation[i].toLatin1();
462  output += ' ';
463  }
464  }
465  else {
466  output += equation[i].toLatin1();
467  }
468  }
469 
470  QString cleanedEquation = cleanSpaces(formatFunctionCalls(output.DownCase().ToQt()));
471 
472  return cleanedEquation;
473  }
474 
486  QString InfixToPostfix::formatFunctionCalls(QString equation) {
487  // Clean our space-delimited equation
488  equation = cleanSpaces(equation);
489  QString output = "";
490 
491  // We'll use tokens to get through the entire equation, which is space-delimited.
492  // So continue processing until we're out of tokens and the string is empty.
493  while(!equation.isEmpty()) {
494  IString tmp = equation;
495 
496  QString element = tmp.Token(" ").ToQt();
497  equation = tmp.ToQt();
498 
499  // Did we find a function? Figure out what it is!
500  if(isFunction(element)) {
501  // Point to the function. We know if IsFunction returned true,
502  // the result is really a InfixFunction*
503  InfixFunction *func = (InfixFunction *)findOperator(element);
504 
505  // We want to wrap the entire thing in parentheses, and it's argument string.
506  // So sin(.5)^2 becomes (sin(.5))^2
507  output += " ( " + func->inputString() + " (";
508 
509  // Deal with 0-argument functions
510  if(func->argumentCount() == 0) {
511  IString tmp = equation;
512  QString next = tmp.Token(" ").ToQt();
513  equation = tmp.ToQt();
514 
515  // If they didn't add parentheses around the zero-argument
516  // function, we still know what they mean. Close the arguments
517  // and the wrapping parentheses.
518  if(next != "(") {
519  output += " ) ) ";
520 
521  // Step back, we did too much
522  equation = next + " " + equation;
523  }
524  else {
525  IString tmp = equation;
526  // We see a zero-arg function, and we grabbed an open parenthesis from it.
527  // Make sure the next thing is a close or we have a problem.
528  if(tmp.Token(" ") != ")") {
529  throw IException(IException::User,
530  "The function " + func->inputString() + " should not have any arguments.",
531  _FILEINFO_);
532  }
533  equation = tmp.ToQt();
534 
535  // Close the arguments and the wrapping parentheses. They wrote their call correct :)
536  output += " ) ) ";
537  }
538  }
539  else {
540  // Deal with 1+ argument functions by parsing out the arguments
541 
542  IString tmp = equation;
543 
544  // Make sure the user put parentheses around these, otherwise we're left in the dark.
545  if (func->argumentCount() > 1 && tmp.Token(" ") != "(") {
546  throw IException(IException::User,
547  "Missing parenthesis after " + func->inputString(),
548  _FILEINFO_);
549  }
550 
551  equation = tmp.ToQt();
552 
553  // Single argument missing parenthesis?
554  if(func->argumentCount() == 1) {
555 
556  IString tmp = equation;
557  QString argument = tmp.Token(" ").ToQt();
558  equation = tmp.ToQt();
559 
560  if(argument != "(") {
561  // We might have a problem. They're calling a function without adding parentheses....
562  // unless it's a negate, because we insert those, tell them their mistake. It's not
563  // my job to figure out what they mean!
564  if(func->inputString() != "--") {
565  throw IException(IException::User,
566  "Missing parenthesis after " + func->inputString(),
567  _FILEINFO_);
568  }
569 
570  // It's a negate without parentheses, so they mean the next term?
571  if(!isFunction(argument)) {
572  // If it isn't a function, it's safe to just append
573  output += " " + formatFunctionCalls(argument) + " ) ) ";
574  continue;
575  }
576  else {
577  // We are negating a function result. We must do a mini-parse to figure out
578  // the function and resursively call, then append the negation.
579  QString functionName = argument;
580 
581  IString tmp = equation;
582  QString openParen = tmp.Token(" ").ToQt();
583  equation = tmp.ToQt();
584 
585  // No open parens? Call ourself again with this supposed function
586  if(openParen != "(") {
587  output += " " + formatFunctionCalls(functionName) + " ) ) ";
588  equation = openParen + " " + equation;
589  continue;
590  }
591  else {
592  functionName += " (";
593  }
594 
595  // Parse out our equation quickly, call ourself with it to properly handle it,
596  // and append the negation.
597  int numParens = 0;
598  while(numParens > -1) {
599  IString tmp = equation;
600  QString newElem = tmp.Token(" ").ToQt();
601  equation = tmp.ToQt();
602 
603  if(newElem == "") {
604  throw IException(IException::User,
605  "Missing closing parentheses after '" + argument + "'.",
606  _FILEINFO_);
607  }
608 
609  if(newElem == "(") numParens++;
610  else if(newElem == ")") numParens--;
611 
612  functionName += " " + newElem;
613  }
614 
615  output += " " + formatFunctionCalls(functionName) + " ) ) ";
616  continue;
617  }
618  }
619  }
620 
630  QString argument = "";
631  int numParens = 0;
632  int argNum = 0;
633  while(argNum < func->argumentCount()) {
634  IString tmp = equation;
635  QString elem = tmp.Token(" ").ToQt();
636  equation = tmp.ToQt();
637 
638  // Ran out of data, the function call is not complete.
639  if(elem == "") {
640  throw IException(IException::User,
641  "The definition of '" + func->inputString() + "' is not complete.",
642  _FILEINFO_);
643  }
644 
645  if(elem == "(") {
646  numParens ++;
647  argument += " (";
648  }
649  else if(elem == ")") {
650  numParens --;
651 
652  // Ignore last close, it's not part of the argument and will be added later
653  if(numParens != -1) {
654  argument += " )";
655  }
656  }
657  else if(elem == "," && numParens == 0) {
658  checkArgument(func->inputString(), argNum, argument);
659  argument = formatFunctionCalls(argument);
660  output += " ( " + argument + " ) , ";
661  argNum++;
662  argument = "";
663 
664  // Too many arguments? We don't expect a comma delimiter on the last argument.
665  if(argNum == func->argumentCount()) {
666  throw IException(IException::User,
667  "There were too many arguments supplied to the function '" + func->inputString() + "'.",
668  _FILEINFO_);
669  }
670  }
671  else {
672  argument += " " + elem;
673  }
674 
675  if(argNum == func->argumentCount() - 1 && numParens == -1) {
676  checkArgument(func->inputString(), argNum, argument);
677  argument = formatFunctionCalls(argument);
678  // close function call & function wrap
679  output += " " + argument + " ) ) ";
680  argNum++;
681  argument = "";
682  }
683  // Closed the function early?
684  else if(numParens == -1) {
685  throw IException(IException::User,
686  "There were not enough arguments supplied to the function '" + func->inputString() + "'.",
687  _FILEINFO_);
688  }
689  }
690  }
691  }
692  // Not a function, preserve & ignore
693  else {
694  output = output + " " + element;
695  }
696  }
697 
698  return output;
699  }
700 
701  void InfixToPostfix::checkArgument(QString funcName, int argNum, QString argument) {
702  argument = argument.remove(QRegExp("[ ()]"));
703 
704  if(argument == "") {
705  throw IException(IException::User,
706  "Argument " + toString(argNum + 1) + " in function " + funcName + " must not be empty.",
707  _FILEINFO_);
708  }
709  }
710 }
QString ToQt() const
Retuns the object string as a QString.
Definition: IString.cpp:884
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:38
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:99
Adds specific functionality to C++ strings.
Definition: IString.h:179

U.S. Department of the Interior | U.S. Geological Survey
ISIS | Privacy & Disclaimers | Astrogeology Research Program
To contact us, please post comments and questions on the ISIS Support Center
File Modified: 07/12/2023 23:20:31