Skip to content

Commit 59dc816

Browse files
CascadingCascadegithub-actions[bot]Panquesito7
authored
feat: add Shunting Yard Algorithm (#1219)
* Create shunting_yard.c * updating DIRECTORY.md * Update shunting_yard.c * Update shunting_yard.c * Update shunting_yard.c * updating DIRECTORY.md * Update shunting_yard.c * updating DIRECTORY.md --------- Co-authored-by: github-actions[bot] <[email protected]> Co-authored-by: David Leal <[email protected]>
1 parent 6e94adf commit 59dc816

File tree

2 files changed

+242
-0
lines changed

2 files changed

+242
-0
lines changed

DIRECTORY.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@
200200
* [142](https://github.com/TheAlgorithms/C/blob/HEAD/leetcode/src/142.c)
201201
* [1524](https://github.com/TheAlgorithms/C/blob/HEAD/leetcode/src/1524.c)
202202
* [153](https://github.com/TheAlgorithms/C/blob/HEAD/leetcode/src/153.c)
203+
* [16](https://github.com/TheAlgorithms/C/blob/HEAD/leetcode/src/16.c)
203204
* [160](https://github.com/TheAlgorithms/C/blob/HEAD/leetcode/src/160.c)
204205
* [1653](https://github.com/TheAlgorithms/C/blob/HEAD/leetcode/src/1653.c)
205206
* [1657](https://github.com/TheAlgorithms/C/blob/HEAD/leetcode/src/1657.c)
@@ -266,10 +267,12 @@
266267
* [461](https://github.com/TheAlgorithms/C/blob/HEAD/leetcode/src/461.c)
267268
* [476](https://github.com/TheAlgorithms/C/blob/HEAD/leetcode/src/476.c)
268269
* [485](https://github.com/TheAlgorithms/C/blob/HEAD/leetcode/src/485.c)
270+
* [5](https://github.com/TheAlgorithms/C/blob/HEAD/leetcode/src/5.c)
269271
* [50](https://github.com/TheAlgorithms/C/blob/HEAD/leetcode/src/50.c)
270272
* [509](https://github.com/TheAlgorithms/C/blob/HEAD/leetcode/src/509.c)
271273
* [520](https://github.com/TheAlgorithms/C/blob/HEAD/leetcode/src/520.c)
272274
* [53](https://github.com/TheAlgorithms/C/blob/HEAD/leetcode/src/53.c)
275+
* [540](https://github.com/TheAlgorithms/C/blob/HEAD/leetcode/src/540.c)
273276
* [561](https://github.com/TheAlgorithms/C/blob/HEAD/leetcode/src/561.c)
274277
* [567](https://github.com/TheAlgorithms/C/blob/HEAD/leetcode/src/567.c)
275278
* [6](https://github.com/TheAlgorithms/C/blob/HEAD/leetcode/src/6.c)
@@ -354,6 +357,7 @@
354357
* [Rot13](https://github.com/TheAlgorithms/C/blob/HEAD/misc/rot13.c)
355358
* [Rselect](https://github.com/TheAlgorithms/C/blob/HEAD/misc/rselect.c)
356359
* [Run Length Encoding](https://github.com/TheAlgorithms/C/blob/HEAD/misc/run_length_encoding.c)
360+
* [Shunting Yard](https://github.com/TheAlgorithms/C/blob/HEAD/misc/shunting_yard.c)
357361
* [Sudoku Solver](https://github.com/TheAlgorithms/C/blob/HEAD/misc/sudoku_solver.c)
358362
* [Tower Of Hanoi](https://github.com/TheAlgorithms/C/blob/HEAD/misc/tower_of_hanoi.c)
359363
* [Union Find](https://github.com/TheAlgorithms/C/blob/HEAD/misc/union_find.c)

misc/shunting_yard.c

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

0 commit comments

Comments
 (0)