|
| 1 | +/* |
| 2 | + * InfixConverter.c |
| 3 | + * |
| 4 | + * Created on: Jun 2, 2017 |
| 5 | + * Author: dat |
| 6 | + */ |
| 7 | + |
| 8 | +#include "InfixConverter.h" |
| 9 | +#include "AVRString.h" |
| 10 | + |
| 11 | +#include <stdio.h> |
| 12 | +#include <string.h> |
| 13 | +#include <ctype.h> |
| 14 | + |
| 15 | +const char g_infix_supported_ops[] = "+-*/()"; |
| 16 | + |
| 17 | +/** |
| 18 | + * Compare precedence between a and b |
| 19 | + * |
| 20 | + * + - < / * |
| 21 | + * + = -, * = / |
| 22 | + * |
| 23 | + * @param a |
| 24 | + * @param b |
| 25 | + * @return 0 on same |
| 26 | + * 1 if a >b |
| 27 | + * -1 if a < b |
| 28 | + * -2 if error |
| 29 | + */ |
| 30 | +static int8_t comparePriority(char a, char b) |
| 31 | +{ |
| 32 | + int8_t retVal = -2; |
| 33 | + |
| 34 | + uint8_t a_pm = (a == '+') || (a == '-'); |
| 35 | + uint8_t b_pm = (b == '+') || (b == '-'); |
| 36 | + uint8_t a_md = (a == '*') || (a == '/'); |
| 37 | + uint8_t b_md = (b == '*') || (b == '/'); |
| 38 | + |
| 39 | + if (a_pm && b_pm) |
| 40 | + { |
| 41 | + retVal = 0; |
| 42 | + } |
| 43 | + else if (a_pm && b_md) |
| 44 | + { |
| 45 | + retVal = -1; |
| 46 | + } |
| 47 | + else if (a_md && b_pm) |
| 48 | + { |
| 49 | + retVal = 1; |
| 50 | + } |
| 51 | + |
| 52 | + return retVal; |
| 53 | +} |
| 54 | + |
| 55 | +/** |
| 56 | + * append " %c" to str |
| 57 | + * |
| 58 | + * @param str |
| 59 | + * @param c |
| 60 | + */ |
| 61 | +static void stringPushBackChar(char * str, char c) |
| 62 | +{ |
| 63 | + sprintf(str, "%s %c", str, c); |
| 64 | +} |
| 65 | + |
| 66 | +/** |
| 67 | + * appen " %u" to str |
| 68 | + * |
| 69 | + * @param str |
| 70 | + * @param num |
| 71 | + */ |
| 72 | +static void stringPushBackInt(char * str, long num) |
| 73 | +{ |
| 74 | + sprintf(str, "%s %ld", str, num); |
| 75 | +} |
| 76 | + |
| 77 | +/* |
| 78 | + * Make sure buffer is large enough |
| 79 | + * |
| 80 | + * return 2 on unsupported char |
| 81 | + * 0 on success |
| 82 | + */ |
| 83 | +uint8_t addSpaces(char * buffer) |
| 84 | +{ |
| 85 | + // count special chars |
| 86 | + uint8_t i; |
| 87 | + char buffer2[STRING_MAXLEN]; |
| 88 | + uint8_t index2 = 0; |
| 89 | + for (i = 0; i < strlen(buffer); ++i) |
| 90 | + { |
| 91 | + if (strchr(g_infix_supported_ops, buffer[i])) |
| 92 | + { |
| 93 | + buffer2[index2++] = ' '; |
| 94 | + buffer2[index2++] = buffer[i]; |
| 95 | + buffer2[index2++] = ' '; |
| 96 | + } |
| 97 | + else |
| 98 | + { |
| 99 | + buffer2[index2++] = buffer[i]; |
| 100 | + } |
| 101 | + } |
| 102 | + buffer2[index2] = '\0'; |
| 103 | + AVRStrinStripExtraSpace(buffer2); |
| 104 | + AVRStringTrimWhiteSpace(buffer2); |
| 105 | + |
| 106 | + strcpy(buffer, buffer2); |
| 107 | + |
| 108 | + return 0; |
| 109 | +} |
| 110 | + |
| 111 | +/** |
| 112 | + * get top of op_stack |
| 113 | + * |
| 114 | + * @param stack - op stack |
| 115 | + * @return |
| 116 | + */ |
| 117 | +static char getStackTop(const char * stack) |
| 118 | +{ |
| 119 | + if (strlen(stack)) |
| 120 | + { |
| 121 | + return stack[strlen(stack) - 1]; |
| 122 | + } |
| 123 | + |
| 124 | + return '\0'; |
| 125 | +} |
| 126 | + |
| 127 | +/** |
| 128 | + * pop the top of op_stack |
| 129 | + * |
| 130 | + * @param stack |
| 131 | + */ |
| 132 | +static char popStack(char * stack) |
| 133 | +{ |
| 134 | + char retVal = getStackTop(stack); |
| 135 | + |
| 136 | + if (strlen(stack)) |
| 137 | + { |
| 138 | + stack[strlen(stack) - 1] = '\0'; |
| 139 | + } |
| 140 | + |
| 141 | + return retVal; |
| 142 | +} |
| 143 | + |
| 144 | +/** |
| 145 | + * push c to top of op stack |
| 146 | + * |
| 147 | + * @param stack |
| 148 | + * @param c |
| 149 | + */ |
| 150 | +void pushStack(char * stack, char c) |
| 151 | +{ |
| 152 | + stack[strlen(stack) + 1] = '\0'; |
| 153 | + stack[strlen(stack)] = c; |
| 154 | +} |
| 155 | + |
| 156 | +/* |
| 157 | + * Algorithm: |
| 158 | + * |
| 159 | + * 1. Print operands as they arrive. |
| 160 | + * 2. If the stack is empty or contains a left parenthesis on top, push the incoming operator onto the stack. |
| 161 | + * 3. If the incoming symbol is a left parenthesis, push it on the stack. |
| 162 | + * 4. If the incoming symbol is a right parenthesis, pop the stack and print the operators until you see a left parenthesis. Discard the pair of parentheses. |
| 163 | + * 5. If the incoming symbol has higher precedence than the top of the stack, push it on the stack. |
| 164 | + * 6. If the incoming symbol has equal or lower precedence than the symbol on the top of the stack, pop the stack and print the top operator. Then test the incoming operator against the new top of stack. |
| 165 | + * 7. At the end of the expression, pop and print all operators on the stack. (No parentheses should remain.) |
| 166 | + */ |
| 167 | +uint8_t InfixConverterConvertString(const char * input, char * output) |
| 168 | +{ |
| 169 | + uint8_t retVal = 0; |
| 170 | + |
| 171 | + // refine input & cpy to output |
| 172 | + output[0] = '\0'; |
| 173 | + char output_tmp[STRING_MAXLEN]; |
| 174 | + strcpy(output_tmp, input); |
| 175 | + AVRStringRemoveChar(output_tmp, ' '); // rm all spaces |
| 176 | + |
| 177 | + // test for unsupported char |
| 178 | + unsigned i; |
| 179 | + for (i = 0; i < strlen(output_tmp); ++i) |
| 180 | + { |
| 181 | + if (!strchr(g_infix_supported_ops, output_tmp[i]) |
| 182 | + && !isdigit(output_tmp[i])) |
| 183 | + { |
| 184 | + retVal = 2; |
| 185 | + } |
| 186 | + } |
| 187 | + if (retVal) |
| 188 | + return retVal; |
| 189 | + |
| 190 | + retVal = addSpaces(output_tmp); |
| 191 | + if (retVal) |
| 192 | + return retVal; |
| 193 | + |
| 194 | + // parse each token, can be "123" or "+" |
| 195 | + char op_stack[20] = |
| 196 | + { '\0' }; |
| 197 | + char * token = strtok(output_tmp, " "); |
| 198 | + |
| 199 | + while (token) |
| 200 | + { |
| 201 | + char incomingSymbol = token[0]; |
| 202 | + |
| 203 | + // 1. Print operands as they arrive. |
| 204 | + long num; |
| 205 | + if (sscanf(token, "%ld", &num) == 1) |
| 206 | + { |
| 207 | + stringPushBackInt(output, num); |
| 208 | + } |
| 209 | + // error checking, if token isn't number, it must exist in g_infix_supported_ops |
| 210 | + else if (!strchr(g_infix_supported_ops, incomingSymbol)) |
| 211 | + { |
| 212 | + retVal = 2; |
| 213 | + } |
| 214 | + |
| 215 | + // 2. If the stack is empty or contains a left parenthesis on top, push the incoming operator onto the stack. |
| 216 | + else if (!getStackTop(op_stack) || getStackTop(op_stack) == '(') |
| 217 | + { |
| 218 | + pushStack(op_stack, incomingSymbol); |
| 219 | + } |
| 220 | + |
| 221 | + // 3. If the incoming symbol is a left parenthesis, push it on the stack. |
| 222 | + else if (incomingSymbol == '(') |
| 223 | + { |
| 224 | + pushStack(op_stack, incomingSymbol); |
| 225 | + } |
| 226 | + |
| 227 | + // 4. If the incoming symbol is a right parenthesis, pop the stack and print the operators |
| 228 | + // until you see a left parenthesis. Discard the pair of parentheses. |
| 229 | + else if (incomingSymbol == ')') |
| 230 | + { |
| 231 | + // ensure ( exists in stack |
| 232 | + if (strchr(op_stack, '(')) |
| 233 | + { |
| 234 | + while (getStackTop(op_stack) != '(') |
| 235 | + { |
| 236 | + stringPushBackChar(output, popStack(op_stack)); |
| 237 | + } |
| 238 | + |
| 239 | + // discard '(' |
| 240 | + popStack(op_stack); |
| 241 | + } |
| 242 | + else |
| 243 | + { |
| 244 | + retVal = 1; |
| 245 | + } |
| 246 | + } |
| 247 | + |
| 248 | + // 5. If the incoming symbol has higher precedence than the top of the stack, push it on the stack. |
| 249 | + else if (comparePriority(incomingSymbol, getStackTop(op_stack)) == 1) |
| 250 | + { |
| 251 | + pushStack(op_stack, incomingSymbol); |
| 252 | + } |
| 253 | + |
| 254 | + // 6. If the incoming symbol has equal or lower precedence than the symbol on the top of the stack, |
| 255 | + // pop the stack and print the top operator. Then test the incoming operator against the new top of stack. |
| 256 | + else if (comparePriority(incomingSymbol, getStackTop(op_stack)) <=0) |
| 257 | + { |
| 258 | + stringPushBackChar(output, popStack(op_stack)); |
| 259 | + |
| 260 | + while ( strlen(op_stack) && comparePriority(incomingSymbol, getStackTop(op_stack)) <= 0) |
| 261 | + { |
| 262 | + stringPushBackChar(output, popStack(op_stack)); |
| 263 | + } |
| 264 | + |
| 265 | + pushStack(op_stack, incomingSymbol); |
| 266 | + } |
| 267 | + |
| 268 | + else |
| 269 | + { |
| 270 | + retVal = 1; |
| 271 | + } |
| 272 | + |
| 273 | + if (retVal) |
| 274 | + break; |
| 275 | + |
| 276 | + token = strtok(NULL, " "); |
| 277 | + } |
| 278 | + |
| 279 | + // push the rest of op stack |
| 280 | + if (strlen(op_stack)) |
| 281 | + { |
| 282 | + if (!strchr(op_stack, '(')) // check if op stack still contains '(' |
| 283 | + { |
| 284 | + while (getStackTop(op_stack)) |
| 285 | + { |
| 286 | + stringPushBackChar(output, popStack(op_stack)); |
| 287 | + } |
| 288 | + } |
| 289 | + else |
| 290 | + { |
| 291 | + retVal = 1; |
| 292 | + } |
| 293 | + } |
| 294 | + |
| 295 | + if (retVal) |
| 296 | + { |
| 297 | + output[0] = '\0'; |
| 298 | + } |
| 299 | + else |
| 300 | + { |
| 301 | + strcpy(output, AVRStringTrimWhiteSpace(output)); |
| 302 | + } |
| 303 | + |
| 304 | + return retVal; |
| 305 | +} |
0 commit comments