-
Notifications
You must be signed in to change notification settings - Fork 3
/
pretty.c
558 lines (482 loc) · 19.3 KB
/
pretty.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
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
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
/* pretty.c */
/*-----------------------------------------------------------------------
Developed by: #undef TEAMNAME
Based on template code provided by Harald Sondergard for COMP90045.
Provides a pretty printer for the Wiz programming language. Takes
an abstract syntax tree as defined in ast.h and prints according
to the provided rules.
Original message included as follows:
" A stub for a pretty-printer for Iz programs.
For use in the COMP90045 project 2014."
-----------------------------------------------------------------------*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "ast.h"
#include "pretty.h"
#include "helper.h"
/*----------------------------------------------------------------------
Definitions for functions that organise the printing process and
sort the procs to print.
-----------------------------------------------------------------------*/
void print_procedures(FILE *fp, Procs *processes);
/*----------------------------------------------------------------------
Definitions for functions that are responsibile for managing the
printing of a procedure
-----------------------------------------------------------------------*/
void print_body(FILE *fp, Body *body);
/*----------------------------------------------------------------------
Definitions for functions that are responsible for printing
declarations
-----------------------------------------------------------------------*/
void print_array_decl(FILE *fp, Intervals *intervals);
void print_declarations(FILE *fp, Decls *declarations, int indents);
/*----------------------------------------------------------------------
Definitions for functions that are responsible for printing
statements
-----------------------------------------------------------------------*/
void print_conds(FILE *fp, Cond *info, int indents);
void print_while(FILE *fp, While *loop, int indents);
void print_statement(FILE *fp, Stmt *statement, int indents);
void print_statements(FILE *fp, Stmts *statements, int indents);
/*----------------------------------------------------------------------
Definitions for functions that are responsible for printing
individual expressions and constants
-----------------------------------------------------------------------*/
void print_binop(FILE *fp, Expr *bin_expr, int prec);
void print_unop(FILE *fp, Expr *unop_expr, int prec);
void print_expr_list(FILE *fp, Exprs *elems);
void print_constant(FILE *fp, Constant *cons);
/*----------------------------------------------------------------------
Definitions for utility functions to help with printing minimal
brackets and printing correct indentation
-----------------------------------------------------------------------*/
BOOL is_associative_arithmetic(Expr *expr);
BOOL is_associative_logical(Expr *expr);
/*-----------------------------------------------------------------------
Defines our start precedence and indentation. Increasing precedence
will increase the number of brackets used, increasing the indent
level will shift the output to the right be 4*n spaces.
-----------------------------------------------------------------------*/
#define START_PREC 0
#define START_INDENT 1
/*----------------------------------------------------------------------
Functions that organise the printing process and
sort the procs to print.
-----------------------------------------------------------------------*/
// Simple wrapper responsible for printing all procedures, could be
// extended if the language required it for global variables etc.
void pretty_prog(FILE *fp, Program *prog) {
//Print all procedures
print_procedures(fp, prog->procedures);
}
// Simple function to use with qsort to sort procedures. Simply uses
// strcmp after extracting the procedure ids and returns the value
int proc_comparison(const void *a, const void *b) {
// Cast to procs
Proc *proc_a = *((Proc **) a);
Proc *proc_b = *((Proc **) b);
char *id_a = proc_a->header->id;
char *id_b = proc_b->header->id;
// Sort by string name
return (strcmp(id_a, id_b));
}
// Simple function responsible for sorting all the procedures and
// then calling the appropriate printing function on each procedure
void print_procedures(FILE *fp, Procs *procs) {
// Go through and count the number of procedures
int num_procs = 1;
Procs *rest = procs->rest;
while (rest != NULL) {
num_procs++;
rest = rest->rest;
}
// Create the array of procedures to then sort
Proc **proc_ptrs = (Proc **) checked_malloc(num_procs * sizeof(Proc *));
Procs *c = procs;
int i = 0;
for (i = 0; i < num_procs; i++) {
proc_ptrs[i] = c->first;
c = c->rest;
}
// Sort the array using qsort and the proc_comparision function
// which compares by proc id lexicographically
qsort(proc_ptrs, num_procs, sizeof(Proc *), proc_comparison);
// Go through the array and print each procedure
for (i = 0; i < num_procs; i++) {
// If we follow a previous proc then give ourselves
// some breathing room.
if (i > 0) {
fprintf(fp, "\n");
}
// Get the current process
Proc *current = proc_ptrs[i];
// Print the header
print_header(fp, current->header);
// Print the body
print_body(fp, current->body);
// Print a new line
fprintf(fp, "end\n");
}
// Free all the pointers like a responsible c programmer.
free(proc_ptrs);
}
/*----------------------------------------------------------------------
Functions that are responsibile for managing the
printing of a procedure
-----------------------------------------------------------------------*/
// Prints a header with the appropriate wrapping (i.e. proc id(params))
void print_header(FILE *fp, Header *header) {
// Print opening line
fprintf(fp, "proc %s", header->id);
// Print parameters
if (header->params != NULL) {
fprintf(fp, " ("); // White space before params
print_params(fp, header->params);
} else {
// If there are no parameters, print empty brackets
fprintf(fp, " ()\n");
}
}
// Parameters are a linked list, this function goes through the linked list
// and prints the parameter as required. It is recursive by definiton.
void print_params(FILE *fp, Params *params) {
// Print the current param
Param *current = params->first;
// Check the type of the value and print appropriately
ParamsInd ind = current->ind;
Type type = current->type;
char *id = current->id;
fprintf(fp, "%s %s %s", valnames[ind], typenames[type], id);
// If we have more parameters then we can add the comma and
// print the rest recursively.
if (params->rest != NULL) {
fprintf(fp, ", ");
print_params(fp, params->rest);
} else {
// Otherwise we have reached the end of the list so close
// the brackets
fprintf(fp, ")\n");
}
}
// Simple wrapper function that delegates the printing of the appropriate
// part of a declaration and statemetns.
void print_body(FILE *fp, Body *body) {
// Print declarations
print_declarations(fp, body->decls, START_INDENT);
// Add the one line break required
fprintf(fp, "\n");
// Print the statements
print_statements(fp, body->statements, START_INDENT);
}
/*----------------------------------------------------------------------
Functions that are responsible for printing
declarations
-----------------------------------------------------------------------*/
// Progress through the linked list of declarations and print each one
// as required according to its type. This function is recursive by nature
void print_declarations(FILE *fp, Decls *declarations, int indents) {
// Check non null conditions due to recursive nature
if (declarations == NULL) {
return;
}
// Get the current declaration
Decl *decl = declarations->first;
// Print the appropriate level of indents on this line
print_indents(fp, indents);
// Check if array declaration or standard variable declaration and then
// print as appropriate
if (decl->array != NULL) {
fprintf(fp, "%s %s[", typenames[decl->type], decl->id);
print_array_decl(fp, decl->array);
fprintf(fp, "];\n");
} else {
fprintf(fp, "%s %s;\n", typenames[decl->type], decl->id);
}
// Continue along our data structure
print_declarations(fp, declarations->rest, indents);
}
// Responsible for traversing the array declaration linked list and printing
// each interval.
void print_array_decl(FILE *fp, Intervals *intervals) {
Interval *i = intervals->first;
fprintf(fp, "%d..%d", i->lower, i->upper);
//Check we need to print the next one
if (intervals->rest != NULL) {
fprintf(fp, ", ");
print_array_decl(fp, intervals->rest);
}
}
/*----------------------------------------------------------------------
Functions that are responsible for printing statements
-----------------------------------------------------------------------*/
// A simple function that recursively walks through the linked list of
// statements and calls the appropriate function to print each one.
void print_statements(FILE *fp, Stmts *statements, int indents) {
// Check for null condition before printing
if (statements == NULL) {
return;
}
// Assign current value
Stmt *statement = statements->first;
print_indents(fp, indents);
// Print the current statement
print_statement(fp, statement, indents);
//Continue along the datastructure
print_statements(fp, statements->rest, indents);
}
// A rather beefy function that is responsible for detmerining the type of
// the statement and then either printing (for simple statements) or calling
// helper functions on (for more complex statements) each statement
void print_statement(FILE *fp, Stmt *statement, int indents) {
// Find the type of statement and the info associated
StmtKind kind = statement->kind;
SInfo *info = &(statement->info);
// Switch on kind of statement and print appropriately
switch (kind) {
case STMT_ASSIGN:
// Print the left expression
print_expression(fp, info->assign.asg_ident, START_PREC);
fprintf(fp, " := ");
// Print the right expression
print_expression(fp, info->assign.asg_expr, START_PREC);
fprintf(fp, ";\n");
break;
case STMT_COND:
// Hand off to seperate function to print if statements
print_conds(fp, &info->cond, indents);
break;
case STMT_READ:
fprintf(fp, "read ");
// Print the read expressions using helper function
print_expression(fp, info->read, START_PREC);
fprintf(fp, ";\n");
break;
case STMT_WHILE:
// Print while statements using helper functions
print_while(fp, &info->loop, indents);
break;
case STMT_WRITE:
// Write the command
fprintf(fp, "write ");
// Print the expression
print_expression(fp, info->write, START_PREC);
fprintf(fp, ";\n");
break;
case STMT_FUNC:
//Write the funciton name
fprintf(fp, "%s(", info->func->id);
//Print the arguments
print_exprs(fp, info->func->args);
//Close the braces
fprintf(fp, ");\n");
break;
}
}
// Helper function to print the while statements, which consist of
// a condition followed by a series of statements.
void print_while(FILE *fp, While *loop, int indents) {
//Print the while
fprintf(fp, "while ");
//Print the expression
Expr *while_cond = loop->cond;
print_expression(fp, while_cond, START_PREC);
//Print the do
fprintf(fp, " do\n");
//Print the statements
print_statements(fp, loop->body, indents + 1);
//Print the closing do at appropriate indentation
print_indents(fp, indents);
fprintf(fp, "od\n");
return;
}
// Helper function to print the if statements which consist of a condition
// followed by a series of statements and possibly an else statement.
void print_conds(FILE *fp, Cond *info, int indents) {
// Print the if branch with condition
fprintf(fp, "if ");
print_expression(fp, info->cond, START_PREC);
// Print then branch with statements
fprintf(fp, " then\n");
print_statements(fp, info->then_branch, indents + 1);
// Possibly print else brach if it exists at the appropriate indent level
if (info->else_branch != NULL) {
print_indents(fp, indents);
fprintf(fp, "else\n");
print_statements(fp, info->else_branch, indents + 1);
}
// Print the fi at the appropriate indentation level
print_indents(fp, indents);
fprintf(fp, "fi\n");
}
/*----------------------------------------------------------------------
Functions that are responsible for printing
individual expressions and constants
-----------------------------------------------------------------------*/
// Simple function to recursively walkt through an expression list printing
// the commas if the expression list contains multiple expressions. This
// function is recursive.
void print_exprs(FILE *fp, Exprs *args) {
// Given that expression lists may be empty
if (args == NULL) {
return;
}
// Get and print the current expression
Expr *current = args->first;
print_expression(fp, current, START_PREC);
// Check if we have more expressions and if so, continue printing
// after adding the comma and white space.
if (args->rest != NULL) {
fprintf(fp, ", ");
print_exprs(fp, args->rest);
}
}
// A function to identify the kind of expression and then if appropriate
// print it (for simple expressions) or call a helper function if required.
void print_expression(FILE *fp, Expr *expr, int prec) {
//Find the type of expression and print it
ExprKind kind = expr->kind;
// Switch on kind to print
switch (kind) {
case EXPR_ID:
//Simply print the identifier
fprintf(fp, "%s", expr->id);
break;
case EXPR_CONST:
print_constant(fp, &(expr->constant));
break;
case EXPR_BINOP:
print_binop(fp, expr, prec);
break;
case EXPR_UNOP:
print_unop(fp, expr, prec);
break;
case EXPR_ARRAY:
// Print the array identifier and opening bracket.
fprintf(fp, "%s[", expr->id);
// Then print all the indices using the helper function
print_exprs(fp, expr->indices);
// Now close the brackets
fprintf(fp, "]");
break;
}
}
// A helper function to print a binary operation. Will print to the minimum
// number of brackets if precedence is passed in correctly.
void print_binop(FILE *fp, Expr *bin_expr, int prec) {
// Check if our precedence dictates the need for brackets
BOOL brackets = prec > binopprec[bin_expr->binop];
// If we need brackets then add them, otherwise we are the highest
// level of precedence accounted and should set the precedence
// thusly.
if (brackets) {
fprintf(fp, "(");
}
prec = binopprec[bin_expr->binop];
// Print the left expression, if the left function is a nonassociative
// logical function (i.e. greater than, equal to etc) then we spoof the
// precedence level to force the expression to bracket itself. Otherwise
// print as normal.
if (is_associative_logical(bin_expr)) {
print_expression(fp, bin_expr->e1, prec + 1);
} else {
print_expression(fp, bin_expr->e1, prec);
}
// Print the binary operation name
fprintf(fp, " %s ", binopname[bin_expr->binop]);
// If the binop expresion is left associative, then we need to
// print brackets to handle cases like 24/(6/2). Therefore we spoof
// the precedence level to force the expression to bracket itself.
// otherwise print as normal
if (is_associative_arithmetic(bin_expr) ||
is_associative_logical(bin_expr)) {
print_expression(fp, bin_expr->e2, prec + 1);
} else {
print_expression(fp, bin_expr->e2, prec);
}
// Close bracket if required
if (brackets) {
fprintf(fp, ")");
}
return;
}
// A helper function to print a unary operation. Will print to the minimum
// number of brackets if precedence is passed in correctly.
void print_unop(FILE *fp, Expr *unop_expr, int prec) {
// Check if our precedence dictates the need for brackets
BOOL brackets = prec > unopprec[unop_expr->unop];
// If we need brackets then add them, otherwise we are the highest
// level of precedence accounted and should set the precedence
// thusly.
if (brackets) {
fprintf(fp, "(");
}
prec = unopprec[unop_expr->unop];
// Print the expression and pass on precedence.
fprintf(fp, "%s", unopname[unop_expr->unop]);
print_expression(fp, unop_expr->e1, prec);
// Close bracket if required
if (brackets) {
fprintf(fp, ")");
}
}
// A helper function to print constants as required.
void print_constant(FILE *fp, Constant *cons) {
//Find the type of constant.
Type type = cons->type;
//Switch on the type of constant
switch (type) {
case BOOL_TYPE:
// Conditionally print either true or false based on the boolean
// value stored in the constant.
fprintf(fp, "%s", cons->val.bool_val ? "true" : "false");
break;
case INT_TYPE:
fprintf(fp, "%d", cons->val.int_val);
break;
case FLOAT_TYPE:
fprintf(fp, "%f", cons->val.float_val);
break;
case STRING_CONST:
fprintf(fp, "\"%s\"", cons->val.string);
break;
default:
// Should not get her as only remaining types are int_array_type
// and float array type. Therefore return
return;
}
}
/*----------------------------------------------------------------------
Functions to help with printing minimal
brackets and printing correct indentation
-----------------------------------------------------------------------*/
// A function to help return whether or not a arithmetic expression
// is comutative. For the purpose of this language, we are only concerned
// with multiplication and addition. We include
BOOL is_associative_arithmetic(Expr *expr) {
if (expr->binop == BINOP_ADD || expr->binop == BINOP_MUL) {
return FALSE;
} else {
return TRUE;
}
}
// A function to help return whether or not a logical expression
// is comutative. For the purpose of this language, we are only concerned
// with the comparison operators
BOOL is_associative_logical(Expr *expr) {
if (expr->binop == BINOP_EQ || expr->binop == BINOP_NTEQ ||
expr->binop == BINOP_LT || expr->binop == BINOP_LTEQ ||
expr->binop == BINOP_GT || expr->binop == BINOP_GTEQ) {
return TRUE;
} else {
return FALSE;
}
}
// A simple function to print the requisite four spaces per indent level
// on the current line.
void print_indents(FILE *fp, int indents) {
int i;
for (i = 0; i < indents; i++) {
fprintf(fp, " ");
}
}