-
Notifications
You must be signed in to change notification settings - Fork 1
/
expression.c
executable file
·255 lines (219 loc) · 6.13 KB
/
expression.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
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
/*
* Simple Interpreter
* Group 3
* CS 2110
*/
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include "expression.h"
#include "helper.h"
#include "error.h"
char * pad_string(char * str)
{
int size = strlen(str);
int num_chars = count_occurrences(str, '(', size) + count_occurrences(str, ')', size)
+ count_occurrences(str, '+', size) + count_occurrences(str, '-', size)
+ count_occurrences(str, '*', size) + count_occurrences(str, '/', size)
+ count_occurrences(str, '%', size);
int new_size = size + num_chars * 2;
char * new_str = malloc(new_size + 1);
long i = 0, j = 0;
for (; j < size && i < new_size; ++i, ++j)
{
if (str[j] == '(' || str[j] == ')' || str[j] == '+' || str[j] == '-' ||
str[j] == '*' || str[j] == '/' || str[j] == '%')
{
new_str[i++] = ' ';
new_str[i++] = str[j];
new_str[i] = ' ';
}
else
{
new_str[i] = str[j];
}
}
new_str[i] = '\0';
return new_str;
}
enum SIMPLE_OPERATOR token_determine_op(char * token)
{
if (strcmp(token, "(") == 0)
{
return LEFT_PARENTHESIS;
}
else if (strcmp(token, ")") == 0)
{
return RIGHT_PARENTHESIS;
}
else if (strcmp(token, "+") == 0)
{
return PLUS;
}
else if (strcmp(token, "-") == 0)
{
return MINUS;
}
else if (strcmp(token, "*") == 0)
{
return TIMES;
}
else if (strcmp(token, "/") == 0)
{
return DIVIDE;
}
else if (strcmp(token, "%") == 0)
{
return MODULUS;
}
else
{
return OP_NO_MATCH;
}
}
int evaluate_operator(enum SIMPLE_OPERATOR op, int operand1, int operand2)
{
if (op == PLUS)
{
return operand1 + operand2;
}
else if (op == MINUS)
{
return operand1 - operand2;
}
else if (op == TIMES)
{
return operand1 * operand2;
}
else if (op == DIVIDE)
{
return operand1 / operand2;
}
else if (op == MODULUS)
{
return operand1 % operand2;
}
return 0;
}
int op_precedence(enum SIMPLE_OPERATOR op_type)
{
/* This is fairly simple in our case. */
if (op_type == TIMES || op_type == DIVIDE || op_type == MODULUS)
{
return 10;
}
else
{
return 0;
}
}
int exp_evaluate(simple_line_t * line, char * expression, simple_variable_t variable_map[26])
{
/* STEP 1: convert the expression from infix to postfix. */
/* We will use two structures, a queue to store the output and a stack to store operators during the conversion. */
queue_t * output_head = NULL, * output_tail = NULL;
stack_struct_t * operators = NULL;
/* For step 2, we will need a stack to facilitate the evaluation of the postfix expression. */
stack_struct_t * eval_stack = NULL;
/* We will traverse the expression with this pointer. */
char * token;
/* We will use strtok, which modifies the line, so we copy the line first. */
char * copy = pad_string(expression);
token = strtok(copy, " ");
for (; token != NULL; token = strtok(NULL, " "))
{
/* We need to figure out what this token represents. */
if (is_integer(token))
{
enqueue(&output_head, &output_tail, atoi(token), NUMBER);
}
else if (token_determine_op(token) != OP_NO_MATCH)
{
enum SIMPLE_OPERATOR op_type = token_determine_op(token);
if (op_type == LEFT_PARENTHESIS)
{
stack_push(&operators, LEFT_PARENTHESIS);
}
else if (op_type == RIGHT_PARENTHESIS)
{
if (stack_empty(operators))
{
simple_error(SYNTAX_ERROR, line, "incorrect matching of parentheses in arithmetic expression; inappropriate right parenthesis");
}
while (operators->data != LEFT_PARENTHESIS)
{
if (stack_empty(operators))
{
simple_error(SYNTAX_ERROR, line, "incorrect matching of parentheses in arithmetic expression");
}
enqueue(&output_head, &output_tail, stack_pop(&operators), OPERATOR);
if (stack_empty(operators))
{
simple_error(SYNTAX_ERROR, line, "incorrect matching of parentheses in arithmetic expression");
}
}
stack_pop(&operators);
}
else
{
while (!stack_empty(operators) && op_precedence(op_type) <= op_precedence((enum SIMPLE_OPERATOR)operators->data)
&& operators->data != LEFT_PARENTHESIS && operators->data != RIGHT_PARENTHESIS)
{
enqueue(&output_head, &output_tail, stack_pop(&operators), OPERATOR);
}
stack_push(&operators, op_type);
}
}
else if (check_variable_name_no_error(token))
{
enqueue(&output_head, &output_tail, simple_lookup_variable(variable_map, token, line), NUMBER);
}
else
{
simple_error(SYNTAX_ERROR, line, "invalid or unrecognizable symbol is in the arithmetic expression");
}
}
while (!stack_empty(operators))
{
if (operators->data == LEFT_PARENTHESIS || operators->data == RIGHT_PARENTHESIS)
{
simple_error(SYNTAX_ERROR, line, "incorrect matching of parentheses in arithmetic expression");
}
enqueue(&output_head, &output_tail, stack_pop(&operators), OPERATOR);
}
/* STEP 2: evaluate the postfix expression represented by the queue output. */
/* We go "left to right" in the output queue. We add numbers to eval_stack, and when we reach an operator, we evaluate the
operator and the last two items in the eval_stack (by popping them) and then we push the result onto eval_stack. If the
expression was well-formed, this will always be possible and one number will result at the end, the value of the
original expression. */
while (!queue_empty(output_head))
{
int operand1 = 0, operand2 = 0;
enum QUEUE_DATA_TYPE type = output_head->type;
int value = dequeue(&output_head, &output_tail);
if (type == OPERATOR)
{
if (stack_empty(eval_stack))
{
simple_error(SYNTAX_ERROR, line, "incorrect syntax for arithmetic expression");
}
operand2 = stack_pop(&eval_stack);
if (stack_empty(eval_stack))
{
simple_error(SYNTAX_ERROR, line, "incorrect syntax for arithmetic expression");
}
operand1 = stack_pop(&eval_stack);
stack_push(&eval_stack, evaluate_operator((enum SIMPLE_OPERATOR)value, operand1, operand2));
}
else if (type == NUMBER)
{
stack_push(&eval_stack, value);
}
}
if (stack_empty(eval_stack) || eval_stack->next != NULL)
{
simple_error(SYNTAX_ERROR, line, "incorrect syntax for arithmetic expression");
}
free(copy);
return eval_stack->data;
}