-
-
Notifications
You must be signed in to change notification settings - Fork 4.5k
/
Copy pathshunting_yard.c
238 lines (209 loc) · 8.02 KB
/
shunting_yard.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
/**
* @file
* @brief [Shunting Yard Algorithm](https://en.wikipedia.org/wiki/Shunting_yard_algorithm)
* @details From Wikipedia: In computer science,
* the shunting yard algorithm is a method for parsing arithmetical or logical expressions, or a combination of both, specified in infix notation.
* It can produce either a postfix notation string, also known as Reverse Polish notation (RPN), or an abstract syntax tree (AST).
* The algorithm was invented by Edsger Dijkstra and named the "shunting yard" algorithm because its operation resembles that of a railroad shunting yard.
* @author [CascadingCascade](https://github.com/CascadingCascade)
*/
#include <assert.h> /// for assertion
#include <stdio.h> /// for IO operations
#include <stdlib.h> /// for memory management
#include <string.h> /// for string operations
#include <ctype.h> /// for isdigit()
/**
* @brief Helper function that returns each operator's precedence
* @param operator the operator to be queried
* @returns the operator's precedence
*/
int getPrecedence(char operator) {
switch (operator) {
case '+':
case '-': {
return 1;
}
case '*':
case '/': {
return 2;
}
case '^': {
return 3;
}
default:{
fprintf(stderr,"Error: Invalid operator\n");
return -1;
}
}
}
/**
* @brief Helper function that returns each operator's associativity
* @param operator the operator to be queried
* @returns '1' if the operator is left associative
* @returns '0' if the operator is right associative
*/
int getAssociativity(char operator) {
switch (operator) {
case '^': {
return 0;
}
case '+':
case '-':
case '*':
case '/': {
return 1;
}
default: {
fprintf(stderr,"Error: Invalid operator\n");
return -1;
}
}
}
/**
* @brief An implementation of the shunting yard that converts infix notation to reversed polish notation
* @param input pointer to input string
* @param output pointer to output location
* @returns `1` if a parentheses mismatch is detected
* @returns `0` if no mismatches are detected
*/
int shuntingYard(const char *input, char *output) {
const unsigned int inputLength = strlen(input);
char* operatorStack = (char*) malloc(sizeof(char) * inputLength);
// This pointer points at where we should insert the next element,
// Hence stackPointer - 1 is used when accessing elements
unsigned int stackPointer = 0;
// We will parse the input with strtok(),
// Since strtok() is destructive, we make a copy of the input to preserve the original string
char* str = malloc(sizeof(char) * inputLength + 1);
strcpy(str,input);
char* token = strtok(str," ");
// We will push to output with strcat() and strncat(),
// This initializes output to be a string with a length of zero
output[0] = '\0';
while (token != NULL) {
// If it's a number, push it to the output directly
if (isdigit(token[0])) {
strcat(output,token);
strcat(output," ");
token = strtok(NULL," ");
continue;
}
switch (token[0]) {
// If it's a left parenthesis, push it to the operator stack for later matching
case '(': {
operatorStack[stackPointer++] = token[0];
break;
}
// If it's a right parenthesis, search for a left parenthesis to match it
case ')': {
// Guard statement against accessing an empty stack
if(stackPointer < 1) {
fprintf(stderr,"Error: Mismatched parentheses\n");
free(operatorStack);
free(str);
return 1;
}
while (operatorStack[stackPointer - 1] != '(') {
// strncat() with a count of 1 is used to append characters to output
const unsigned int i = (stackPointer--) - 1;
strncat(output, &operatorStack[i], 1);
strcat(output," ");
// If the operator stack is exhausted before a match can be found,
// There must be a mismatch
if(stackPointer == 0) {
fprintf(stderr,"Error: Mismatched parentheses\n");
free(operatorStack);
free(str);
return 1;
}
}
// Discards the parentheses now the matching is complete,
// Simply remove the left parenthesis from the stack is enough,
// Since the right parenthesis didn't enter the stack in the first place
stackPointer--;
break;
}
// If it's an operator(o1), we compare it to whatever is at the top of the operator stack(o2)
default: {
// Places the operator into the stack directly if it's empty
if(stackPointer < 1) {
operatorStack[stackPointer++] = token[0];
break;
}
// We need to check if there's actually a valid operator at the top of the stack
if((stackPointer - 1 > 0) && operatorStack[stackPointer - 1] != '(') {
const int precedence1 = getPrecedence(token[0]);
const int precedence2 = getPrecedence(operatorStack[stackPointer - 1]);
const int associativity = getAssociativity(token[0]);
// We pop operators from the stack, if...
while ( // ... their precedences are equal, and o1 is left associative, ...
((associativity && precedence1 == precedence2) ||
// ... or o2 simply have a higher precedence, ...
precedence2 > precedence1) &&
// ... and there are still operators available to be popped.
((stackPointer - 1 > 0) && operatorStack[stackPointer - 1] != '(')) {
strncat(output,&operatorStack[(stackPointer--) - 1],1);
strcat(output," ");
}
}
// We'll save o1 for later
operatorStack[stackPointer++] = token[0];
break;
}
}
token = strtok(NULL," ");
}
free(str);
// Now all input has been exhausted,
// Pop everything from the operator stack, then push them to the output
while (stackPointer > 0) {
// If there are still leftover left parentheses in the stack,
// There must be a mismatch
if(operatorStack[stackPointer - 1] == '(') {
fprintf(stderr,"Error: Mismatched parentheses\n");
free(operatorStack);
return 1;
}
const unsigned int i = (stackPointer--) - 1;
strncat(output, &operatorStack[i], 1);
if (i != 0) {
strcat(output," ");
}
}
free(operatorStack);
return 0;
}
/**
* @brief Self-test implementations
* @returns void
*/
static void test() {
char* in = malloc(sizeof(char) * 50);
char* out = malloc(sizeof(char) * 50);
int i;
strcpy(in,"3 + 4 * ( 2 - 1 )");
printf("Infix: %s\n",in);
i = shuntingYard(in, out);
printf("RPN: %s\n",out);
printf("Return code: %d\n\n",i);
assert(strcmp(out,"3 4 2 1 - * +") == 0);
assert(i == 0);
strcpy(in,"3 + 4 * 2 / ( 1 - 5 ) ^ 2 ^ 3");
printf("Infix: %s\n",in);
i = shuntingYard(in, out);
printf("RPN: %s\n",out);
printf("Return code: %d\n\n",i);
assert(strcmp(out,"3 4 2 * 1 5 - 2 3 ^ ^ / +") == 0);
assert(i == 0);
printf("Testing successfully completed!\n");
free(in);
free(out);
}
/**
* @brief Main function
* @returns 0 on exit
*/
int main() {
test(); // Run self-test implementations
return 0;
}