This repository has been archived by the owner on Dec 15, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathprefix.y
440 lines (375 loc) · 11.2 KB
/
prefix.y
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
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
%{
// Benjamin Steenkamer
// CPEG 621 - Lab 1, part 2
#include <stdio.h>
#include <string.h>
#include <math.h>
#define MAX_NUM_VARS 30 // Max number of declared variables allowed in calculator
#define MAX_VAR_NAME_LEN 20 // How long a variable name can be; must be at least 10
#define MAX_FIX_SIZE 200 // Max size of postfix buf and prefix stack; must be >> MAX_VAR_NAME_LEN
// Identifiers
#define POSTFIX 1
#define PREFIX 0
#define UNARY 2
#define EQUALS 3
#define OPERAND 1
#define OPERATOR 0
int yylex(void); // Will be generated in lex.yy.c by flex
// Following are defined below in sub-routines section
void yyerror(char *);
//Used for infix calculator
int var_assignment(char *, int);
int get_var_value(char *);
int create_var(char *);
void print_var_create_error(int);
// Push and pop values to postfix and prefix stacks
void push_int(int, int);
void push_str(int, char *);
int pop(char*);
// For creating prefix output
int idenify_type(char *);
void strncat_check(char *, char*);
void print_prefix();
struct variable { // Structure to hold a declared variable's data
char name[MAX_VAR_NAME_LEN + 1]; // Allocate space for max var name + \0
int value;
};
struct variable vars[MAX_NUM_VARS]; // Holds declared variables
int num_vars = 0; // Current amount of variables declared
int lineNum = 1; // Used for debugging
char postfix_buf[MAX_FIX_SIZE][MAX_VAR_NAME_LEN + 1]; // Element must hold symbol, integer, or var name
int postfix_size = 0; // Number of elements in postfix buffer
char prefix_stack[MAX_FIX_SIZE][MAX_FIX_SIZE];
int prefix_sp = 0; // Points to the top the stack (one index above top element)
%}
%token INTEGER POWER VARIABLE // bison adds these #defines in infix.tab.h for use in flex
// Union defines all possible values a token can have associated with it
// Allow yylval to hold either an integer or a string (for variable name)
%union
{
int val;
char *str;
}
// When %union is used to specify multiple value types, must declare the
// value type of each symbol for which values are used
%type <val> expr INTEGER
%type <str> VARIABLE
// Make grammar unambiguous
// Low to high precedence and associativity within a precedent rank
// https://en.cppreference.com/w/c/language/operator_precedence
%left '+' '-'
%left '*' '/'
%precedence '!' // Unary bitwise not; No associativity b/c it is unary
%right POWER // ** exponent operator
%start prefix
%%
prefix :
prefix statement '\n'
|
;
statement:
expr { print_prefix(); printf("=%d\n", $1); }
| VARIABLE '=' expr {
push_str(POSTFIX, $1);
push_str(POSTFIX, "=");
print_prefix();
printf("=%d\n", var_assignment($1, $3));
free($1); // Must free the strdup string
}
;
expr :
INTEGER { $$ = $1; push_int(POSTFIX, $1); }
| VARIABLE { $$ = get_var_value($1); push_str(POSTFIX, $1); free($1); }
| expr '+' expr { $$ = $1 + $3; push_str(POSTFIX, "+"); }
| expr '-' expr { $$ = $1 - $3; push_str(POSTFIX, "-"); }
| expr '*' expr { $$ = $1 * $3; push_str(POSTFIX, "*"); }
| expr '/' expr { $$ = $1 / $3; push_str(POSTFIX, "/"); }
| '!' expr { $$ = ~$2; push_str(POSTFIX, "!"); }
| expr POWER expr { $$ = (int)pow($1, $3); push_str(POSTFIX, "**"); }
| '(' expr ')' { $$ = $2; } // Will give syntax error for unmatched parens
;
%%
// Called for var_name = value operations
// Searches the array of vars for var_name; if found, assigns value to it and returns assigned value
// If var_name doesn't exist, create var_name in array, assign new value, return assigned value
int var_assignment(char* var_name, int value)
{
// Search vars to see if var_name was already created
int i = 0;
for(i = 0; i < num_vars; i++)
{
if (strcmp(vars[i].name, var_name) == 0)
{
vars[i].value = value;
return vars[i].value; // Return newly assigned value
}
}
// Try to add new var_name to the array
i = create_var(var_name);
if (i >= 0)
{
vars[i].value = value;
return vars[i].value;
}
print_var_create_error(i);
return 0;
}
// Returns the value of the variable
// If variable wasn't declared previously, create it with a value of zero
int get_var_value(char *var_name)
{
int i = 0;
for(i = 0; i < num_vars; i++)
{
if (strcmp(vars[i].name, var_name) == 0)
{
return vars[i].value;
}
}
i = create_var(var_name);
if (i >= 0)
{
return vars[i].value; // This should always be zero
}
print_var_create_error(i);
return 0; // Return 0 even if a new var couldn't be made
}
// Attempts to add a new variable to the vars array
// Returns index of new variable in array if successful
// Returns negative number if there was an error
int create_var(char *var_name)
{
if (num_vars >= MAX_NUM_VARS)
{
return -1;
}
int len = strlen(var_name);
if(len > MAX_VAR_NAME_LEN)
{
return -2;
}
strncpy(vars[num_vars].name, var_name, len);
vars[num_vars].value = 0; // Initialize variable with a value of zero
num_vars++;
return num_vars - 1;
}
// Helper function to decode errors and print error message
void print_var_create_error(int error)
{
switch(error)
{
case -1:
yyerror("Max number of variables already declared");
break;
case -2:
yyerror("Variable name exceeds max length");
break;
default:
yyerror("Unknown error code");
}
return;
}
// Push integer (converted to string) to the selected buffer
void push_int(int buf_id, int i)
{
if (buf_id == POSTFIX)
{
if(postfix_size >= MAX_FIX_SIZE)
{
yyerror("push_int: postfix_buf is full (MAX_FIX_SIZE elements)");
return;
}
sprintf(postfix_buf[postfix_size], "%d", i); // signed 32-bit int is max 10 characters
postfix_size++;
return;
}
else // Push to prefix stack
{
if(prefix_sp >= MAX_FIX_SIZE)
{
yyerror("push_int: prefix_stack is full (MAX_FIX_SIZE elements)");
return;
}
sprintf(prefix_stack[prefix_sp], "%d", i); // signed 32-bit int is max 10 characters
prefix_sp++;
return;
}
}
// Add string to top of selected buffer
void push_str(int buf_id, char *str)
{
if (buf_id == POSTFIX){
if(postfix_size >= MAX_FIX_SIZE)
{
yyerror("push_str: postfix_buf is full (MAX_FIX_SIZE elements)");
return;
}
strncpy(postfix_buf[postfix_size], str, MAX_VAR_NAME_LEN + 1);
postfix_size++;
return;
}
else
{
if(prefix_sp >= MAX_FIX_SIZE)
{
yyerror("push_str: prefix_stack is full (MAX_FIX_SIZE elements)");
return;
}
strncpy(prefix_stack[prefix_sp], str, MAX_FIX_SIZE);
prefix_sp++;
return;
}
}
// Stores top value popped off prefix stack to top_value
// Only works on prefix stack; postfix won't need this function
// Returns 1 if success, 0 if there is no more data to pop.
int pop(char *top_value){
if(prefix_sp <= 0)
{
return 0;
}
strncpy(top_value, prefix_stack[prefix_sp - 1], MAX_FIX_SIZE);
prefix_sp--;
return 1;
}
// Identify whether string is an operator, unary operator, or operand
int idenify_type(char *c)
{
if(strcmp(c, "+") == 0
|| strcmp(c, "-") == 0
|| strcmp(c, "*") == 0
|| strcmp(c, "/") == 0
|| strcmp(c, "**") == 0)
{
return OPERATOR;
}
else if(strcmp(c, "!") == 0)
{
return UNARY;
}
else if(strcmp(c, "=") == 0)
return EQUALS;
else
{
return OPERAND; // an integer or variable
}
}
// Concatenates two strings, but gives warning if the new string is too long
void strncat_check(char *dest, char *src)
{
int len_dest = strlen(dest);
int len_src = strlen(src);
int space_left = MAX_FIX_SIZE - len_dest - 1; // account for null term that will be added last
int chars_to_copy = space_left; // Amount of space left to copy src into
if (len_src > space_left)
{
yyerror("strncat_check: new stack element too large for buffer, symbols will be lost");
// During the conversion between postfix to prefix, elements on the
// stack are popped, concatenated, and then push back onto the stack. If this
// newly created element is larger than the fixed stack element size,
// strncat will cut off data to prevent buffer overflow.
}
strncat(dest, src, chars_to_copy);
return;
}
// Takes the postfix_buf and prints out converted prefix notation
// Uses a stack and concatenation method for the conversion
void print_prefix()
{
int i;
// Print of Postfix buffer; useful for debugging
// printf("POSTFIX: ");
// for(i = 0; i < postfix_size; i++)
// {
// printf("%s ", postfix_buf[i]);
// }
// printf("\n");
char prf_temp1[MAX_FIX_SIZE];
char prf_temp2[MAX_FIX_SIZE];
char prf_temp3[MAX_FIX_SIZE];
int type;
// Loop through postfix data, left to right
for(i = 0; i < postfix_size; i++)
{
// Determine the type of value from postfix
type = idenify_type(postfix_buf[i]);
if(type == OPERATOR)
{
// Pop top 2 values from prefix stack
pop(prf_temp1);
pop(prf_temp2);
// Concatenate operator + 2nd value + first value
sprintf(prf_temp3, "%s ", postfix_buf[i]); // Don't need to bounds check on these
strncat_check(prf_temp3, prf_temp2); // sprintfs b/c postfix_buf[] have smaller size
strncat_check(prf_temp3, prf_temp1);
// Push new string to stack
push_str(PREFIX, prf_temp3);
}
else if (type == UNARY) // Determine if space or no space between ! and next value
{
pop(prf_temp1); // Get prefix stack element
sprintf(prf_temp2, "%c", prf_temp1[0]); // Get the first value from stack element
if (idenify_type(prf_temp2) == OPERAND) // Determine first value's type
{
sprintf(prf_temp3, "%s", postfix_buf[i]); // Makes unary appear as: !x
}
else
{
sprintf(prf_temp3, "%s ", postfix_buf[i]); // Makes unary appear as: ! +
}
strncat_check(prf_temp3, prf_temp1);
push_str(PREFIX, prf_temp3);
}
else if(type == EQUALS)
{
// Pop top 2 values from prefix stack
pop(prf_temp1);
pop(prf_temp2);
// Concatenate operator + first value + second value
sprintf(prf_temp3, "%s ", postfix_buf[i]);
strncat_check(prf_temp3, prf_temp1);
strncat_check(prf_temp3, prf_temp2);
// Push new string back to stack
push_str(PREFIX, prf_temp3);
}
else // type == OPERAND, push value to prefix stack
{
sprintf(prf_temp1, "%s ", postfix_buf[i]);
push_str(PREFIX, prf_temp1);
}
}
// printf("PREFIX : ");
pop(prf_temp1); // Pop last element, should be entire prefix notation
printf("%s\n", prf_temp1);
// Reset buffers for next operation
postfix_size = 0;
prefix_sp = 0;
return;
}
void yyerror(char *s)
{
printf("%s\n", s);
}
int main()
{
memset(vars, 0, sizeof(int) * MAX_NUM_VARS); // Initialize variables to zero
if(MAX_FIX_SIZE <= MAX_VAR_NAME_LEN)
{
yyerror("Error: To prevent buffer copy overflow, MAX_FIX_SIZE must be > MAX_VAR_NAME_LEN");
// If not caught, this overflow would happen when variable names are pushed to prefix stack,
// and when long prefix stack elements are created via concatenation
return 0;
}
if (MAX_VAR_NAME_LEN < 10)
{
yyerror("Error: Variables should be allowed to be AT LEAST 10 characters long (MAX_VAR_NAME_LEN)");
return 0;
// The postfix buffer stores symbols, var names, and ints in a buffer.
// The buffer needs to be at least 10 + null characters long to store 32-bit signed ints as strings.
// To simplify the logic, the buffer indexes therefore need to be at least 10,
// so var names might as well be be a minimum of 10 + null characters long.
// The extra space for '\0' is accounted for in the allocation.
}
yyparse();
return 0;
}