-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathmain.c
295 lines (206 loc) · 7.96 KB
/
main.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
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// Create parser structure to keep track of the tokens consumed
typedef struct parser_t {
char* tokens;
int ntokens;
int pos;
} parser_t;
// Create expression tree structure to hold our expression
typedef struct exprtree {
char type;
int value;
struct exprtree* left;
struct exprtree* right;
} exprtree;
#define VALID_TOKENS "+-*/0123456789()"
#define MAX_INPUT_SIZE 100
// Important function declarations
char* tokenize(char*);
exprtree* parse(char*);
int calculate(exprtree*);
// Helper parsing functions
static void free_exprtree(exprtree*);
static exprtree* create_exprtree(char, int, exprtree*, exprtree*);
exprtree* parse_add_expression(parser_t* parser);
exprtree* parse_mult_expression(parser_t* parser);
exprtree* parse_atomic_expression(parser_t* parser);
exprtree* parse_number(parser_t* parser);
/* Main loop */
int main(int argc, char* argv[]) {
while (1) {
// 1. Get input from the user
char in[MAX_INPUT_SIZE];
printf("Enter input: ");
scanf("\n%[^\n]", in); // Read until \n
// 2. Get tokens from the input string
char* tokens = tokenize(in);
// 3. Create expression tree from tokens
exprtree* expression = parse(tokens);
// 4. Calculate the value of the expression
int value = calculate(expression);
// 5. Print result
printf("The result is: %d\n", value);
// 6. Free the memory allocated for the expression
free_exprtree(expression);
}
return 0;
}
/* Convert input string into tokens */
char* tokenize(char* in) {
// Allocate space for the tokens (each token is a character)
char* tokens = malloc(sizeof(char) * MAX_INPUT_SIZE);
int token_pos = 0; // token_pos will track the amount of tokens in the token string
// Iterate over the input string and everytime a token is found, add it to the tokens
int in_len = strlen(in);
for (int i = 0; i < in_len; i++)
// Check if input character is a valid token
if (strchr(VALID_TOKENS, in[i]))
// Add to tokens if it is
tokens[token_pos++] = in[i];
// Terminate the tokens string with null
tokens[token_pos] = '\0';
return tokens;
}
/* Calculator grammar:
*
* add_expression := mult_expression (('+' | '-') mult_expression)*
*
* mult_expression := atomic_expression (('*' | '/') atomic_expression)*
*
* atomic_expression := number | left_parenthesis add_expression right_parenthesis
*
* number := (0-9)+
*
*/
/* Parse tokens into expression tree based on grammar */
exprtree* parse(char* tokens) {
// Allocate memory for the parse structure
parser_t* parser = malloc(sizeof(parser_t));
// Set initial values for the parser
parser->tokens = tokens;
parser->ntokens = strlen(tokens);
parser->pos = 0;
// Calculate the expression starting by parsing the tokens starting with the lowest priority
exprtree* expression = parse_add_expression(parser);
// Free allocated memory
free(parser->tokens);
free(parser);
return expression;
}
int calculate(exprtree* expr) {
if (expr->type == 'n')
return expr->value;
int left = calculate(expr->left);
int right = calculate(expr->right);
if (expr->type == '+')
return left + right;
else if (expr->type == '-')
return left - right;
else if (expr->type == '*')
return left * right;
else if (expr->type == '/')
return left / right ? right : 0;
return 0;
}
exprtree* parse_add_expression(parser_t* parser) {
/* add_expression := mult_expression (('+' | '-') mult_expression) */
// An add_expression is composed firstly of a mult_expression, so we parse one right away
exprtree* expr = parse_mult_expression(parser);
// After the mult_expression, add_expression can find 0 or more (+ or -) followed by another mult_expression
while (parser->pos < parser->ntokens &&
(parser->tokens[parser->pos] == '+' || parser->tokens[parser->pos] == '-')) {
// Set expression type as either addition or subtraction
char type = parser->tokens[parser->pos];
// Consume the addition or subtraction token
parser->pos++;
// Parse a mult_expression that should come right after (+ or -)
exprtree* right_expr = parse_mult_expression(parser);
// We create a new expression with the ... //TODO
expr = create_exprtree(type, 0, expr, right_expr); // value is set to 0 because operations don't use it
}
return expr;
}
exprtree* parse_mult_expression(parser_t* parser) {
/* mult_expression := atomic_expression (('*' | '/') atomic_expression) */
// a mult_expression is composed firstly of an atomic_expression, so we parse one right away
exprtree* expr = parse_atomic_expression(parser);
// mult_expression can find 0 or more (* or /) followed by another atomic_expression
while (parser->pos < parser->ntokens &&
(parser->tokens[parser->pos] == '*' || parser->tokens[parser->pos] == '/')) {
// set expression type as either multiplication or division
char type = parser->tokens[parser->pos];
// consume the multiplication or division token
parser->pos++;
// parse an atomic_expression that should come right after (* or /)
exprtree* right_expr = parse_atomic_expression(parser);
// we create a new expression with the //TODO
expr = create_exprtree(type, 0, expr, right_expr); // value is set to 0 because operations don't use it
}
return expr;
}
exprtree* parse_atomic_expression(parser_t* parser) {
/* atomic_expression := number | left_parenthesis add_expression right_parenthesis */
exprtree* expr;
// If we find parenthesis, then we read an add_expression as an atomic one
if (parser->tokens[parser->pos] == '(') {
parser->pos++; // Consume parenthesis
// Parse add_expression that should come between parenthesis
expr = parse_add_expression(parser);
// Consume the closing parenthesis
if (parser->tokens[parser->pos] == ')')
parser->pos++;
else {
// Error if there aren't any
fprintf(stderr, "Invalid input\n");
exit(1);
}
} else {
// This is the alternative production rule - an atomic expression can be just a number
expr = parse_number(parser);
}
return expr;
}
exprtree* parse_number(parser_t* parser) {
/* number := (0-9)+ */
char number[MAX_INPUT_SIZE];
int numberlen = 0;
// We'll read the consecutive numbers into a character array, and then convert it to a number with atoi
while (strchr("0123456789", parser->tokens[parser->pos]) &&
numberlen < MAX_INPUT_SIZE && parser->pos < parser->ntokens) {
number[numberlen++] = parser->tokens[parser->pos];
parser->pos++;
}
number[numberlen] = '\0';
// When no number characters could be found
if (numberlen == 0) {
fprintf(stderr, "Invalid input, couldn't parse number\n");
exit(1);
}
// Convert the number characters array to an int
int value = atoi(number);
// Create an expression of type 'n' with the value set as the number value
exprtree* number_expr = create_exprtree('n', value, NULL, NULL);
return number_expr;
}
static exprtree* create_exprtree(char type, int value, exprtree* left, exprtree* right) {
// Allocate memory for the expression
exprtree* expr = malloc(sizeof(exprtree));
// Set values for the expression
expr->type = type;
expr->value = value;
expr->left = left;
expr->right = right;
return expr;
}
static void free_exprtree(exprtree* expr) {
// Free the expression recursively
if (expr) {
if (expr->left)
free_exprtree(expr->left);
if (expr->right)
free_exprtree(expr->right);
free(expr);
}
}