PageRenderTime 41ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/archives/310/projects/3.1 (boolean postfix)/boolean.cpp

https://bitbucket.org/meyersh/gimli
C++ | 262 lines | 154 code | 43 blank | 65 comment | 56 complexity | 7562048675f589ff7eff11f75fce1d0f MD5 | raw file
Possible License(s): LGPL-2.1
  1. /*******************************************************************************
  2. * boolean.cpp for S3.P1 in CSCI-310
  3. * by Shaun Meyer (Feb, 2011)
  4. *
  5. * Takes an infix boolean equation and processes it with postfix, returning
  6. * the postfix equation on the way out.
  7. *
  8. * Uses exceptions for error catching and a simplified stack class.
  9. ******************************************************************************/
  10. #include <iostream>
  11. #include <vector>
  12. #include <string>
  13. #include <map>
  14. #include "stack.hpp"
  15. #include "shaun.hpp"
  16. #include <stdexcept>
  17. using namespace std;
  18. /*
  19. * prototypes
  20. */
  21. int precedence(char opcode);
  22. void do_op();
  23. pair<char, int> get_identifier(string);
  24. void print_error(string);
  25. /*
  26. * globals
  27. */
  28. stack<int> numbers;
  29. stack<char> ops; // ~ & |, etc
  30. string postfix_out; // postfix version of our equation
  31. unsigned int open_parens = 0; // number of un-closed parens
  32. int main()
  33. {
  34. /* We're a web-app now! */
  35. cout << "Content-Type: text/plain\n\n";
  36. /* Fetch std input for equation + identifiers */
  37. string line;
  38. std::getline(cin, line);
  39. if (line.size() == 0)
  40. return 0;
  41. /* I expect my input to be one line, comma-separated, beginning with the
  42. * equation and then followed by 1-26 fields (A=1 or B=0, etc) */
  43. vector<string> form_input = split(line, ",");
  44. map<char, int> variables; // this will hold our CHAR->INT mapping.
  45. /* process each identifier field and place results into a map.
  46. * we silently ignore too many fields or improperly formatted
  47. * fields. */
  48. for (int i = 1; i < form_input.size(); i++)
  49. {
  50. if (i > 26)
  51. break;
  52. try {variables.insert(get_identifier(form_input[i]));}
  53. catch (exception) {continue;}
  54. }
  55. //cout << "Assigned identifiers:\n";
  56. //print_map(variables);
  57. try
  58. {
  59. for (int i = 0; i < form_input[0].size(); i++)
  60. /* For each token in our equation:
  61. * - Skip past spaces
  62. * - Maintain two (global) stacks: numbers + ops.
  63. * Basic algorithm:
  64. * - If we encounter an (, remember it. When the )
  65. * is found, do the operands until we find the ( again.
  66. * should it not be there, we've got mis-matched parens.
  67. * - If we encounter an opcode, push it unless it's less than
  68. * the precedence of the last opcode. Should that happen, we
  69. * perform the opcodes until we have better precedence.
  70. * - If we encounter a character, it's an identifier. Look up
  71. * and store its value in the numbers stack.
  72. * - At the end, process all un-processed opcodes.
  73. */
  74. {
  75. char token = form_input[0][i];
  76. /*
  77. * Skip spaces
  78. */
  79. if (token == ' ')
  80. continue;
  81. if (token == '(')
  82. {
  83. open_parens++; // Keep an eye out for closing parens...
  84. ops.push(token);
  85. }
  86. else if (token == '&'
  87. || token == '|'
  88. || token == '~')
  89. {
  90. while (ops.size() && precedence(token) < precedence(ops.top()))
  91. do_op();
  92. ops.push(token);
  93. }
  94. else if (isalpha(token))
  95. {
  96. numbers.push(variables[toupper(token)]);
  97. postfix_out += toupper(token);
  98. }
  99. else if (token == ')')
  100. {
  101. /* process all ops until we find the opening paren
  102. * or die trying. Throw a runtime_error if we have
  103. * ops without enough numbers. */
  104. while (ops.size() && ops.top() != '(')
  105. {
  106. try {do_op();}
  107. catch (exception) {
  108. cout << "TOP: '" << ops.top() << "'\n";
  109. cout << "ops.size(): " << ops.size() << endl;
  110. throw runtime_error("Bailing out!");
  111. }
  112. }
  113. /* based on the above while() loop, there must be
  114. * a ( on top of the ops stack. If the stack is
  115. * empty, we have mis-matched parens. */
  116. if (ops.size() && ops.pop() == '(')
  117. open_parens--;
  118. else
  119. throw runtime_error("Mis-matched parens.");
  120. }
  121. else if (isdigit(token))
  122. throw runtime_error("Numbers are not allowed in this equation.");
  123. else
  124. throw runtime_error("Unhandled problem (invalid equation!)");
  125. if (i == form_input[0].size()-1)
  126. /* We're at the end of the equation; do the remaining OPS. */
  127. {
  128. if (open_parens)
  129. throw runtime_error("Unclosed parens.");
  130. else
  131. while (ops.size())
  132. do_op();
  133. }
  134. }
  135. if (numbers.size() != 1)
  136. throw runtime_error("Too few operators, invalid infix equation.");
  137. } catch (runtime_error e) { print_error(e.what()); }
  138. cout << "remaining_numbers=" << numbers.size() << endl;
  139. cout << "remaining_ops=" << ops.size() << endl;
  140. cout << "result=" << numbers.top() << endl;
  141. cout << "postfix=" << postfix_out << endl;
  142. return 0;
  143. }
  144. void do_op()
  145. /* This function grabs the operand and requisite numbers,
  146. * pops them from the (global) stacks and pushes the result
  147. * back onto the numbers (global) stack.
  148. *
  149. * For error handling, it throws runtime_error exceptions
  150. * with an appropriate description of the problem.
  151. */
  152. {
  153. int op1, op2;
  154. char opcode;
  155. if (ops.size())
  156. opcode = ops.pop();
  157. else
  158. throw runtime_error("Tried to pop operand from empty op stack.");
  159. postfix_out += opcode;
  160. if (numbers.size())
  161. op2 = numbers.pop();
  162. else
  163. throw runtime_error("Tried to pop from empty number stack (op2).");
  164. /*
  165. * one exception, the NOT operator (~) is binary so
  166. * we don't even bother looking for another number.
  167. */
  168. if (opcode != '~')
  169. {
  170. if (numbers.size())
  171. op1 = numbers.pop();
  172. else
  173. throw runtime_error("Tried to pop from empty number stack (op1).");
  174. }
  175. if (opcode == '&')
  176. numbers.push(op1 & op2);
  177. else if (opcode == '|')
  178. numbers.push(op1 | op2);
  179. else if (opcode == '~')
  180. numbers.push(!op2);
  181. #ifdef DEBUG
  182. cout << "Doing: " << op1 << " " <<
  183. opcode << " " << op2 << endl;
  184. #endif
  185. }
  186. int precedence(char opcode)
  187. /* This function returns the order-of-operations precedence
  188. * for a given opcode to be compared with other opcodes. */
  189. {
  190. switch (opcode)
  191. {
  192. case '|':
  193. return 2;
  194. case '&':
  195. return 3;
  196. case '~':
  197. return 4;
  198. default:
  199. return 0;
  200. }
  201. }
  202. pair<char, int> get_identifier(string inpt)
  203. /* given inpt in the form X=500, return a std::pair<char,int>
  204. * (X, 500) for insertion into a map<char,int> container. */
  205. {
  206. char identifier;
  207. int value;
  208. if (inpt.size() < 2 || inpt[1] != '=' || !isalpha(inpt[0])) // invalid input...
  209. throw runtime_error("Bad input at '" + inpt + "'. Ignoring.\n");
  210. identifier = toupper(inpt[0]);
  211. /* Whatever is left, after `N=`, we shove through atoi() and into our
  212. * value. Bam! */
  213. value = atoi(inpt.substr(2, inpt.size()-2).c_str());
  214. return pair<char,int>(identifier, value);
  215. }
  216. void print_error(string err)
  217. {
  218. cout << "error=" << err << endl << flush;
  219. }