-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathAstSemaVisitor.cpp
More file actions
590 lines (468 loc) · 19.6 KB
/
Copy pathAstSemaVisitor.cpp
File metadata and controls
590 lines (468 loc) · 19.6 KB
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
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
#include "Semantikin.h"
#include "AstVisitors.h"
#include "SymbolTable.h"
#include <memory>
#include <list>
/*
* These are the checks performed:
* 1. adicionar declarações. // OK
* 2. verificar uso de simbolos inexistentes. // ok
* 3. checagem de tipos. // ok
* 4. indexacao com ponto flutuante. // ok
* 5. usando variável como chamada de função. // ok
* 6. usando função como variável. // ok
* 7. tipo de argumentos // ok
* 8. quantidade correta de argumentos. // ok
* 9. size of array dimensions must be integers contants. // ok
* 10. check (non-)existence of return statements and its type. // ok
* 11. Check if we aren't taking the address of a non-lefthand exp.// ok
* 12. check the consistency of operators and operand types.
*/
using namespace Parser;
#ifndef CHECK_TYPE
#define CHECK_TYPE(_exp_, _tp_) ((_exp_)->type() == (Parser::_tp_))
#endif
#ifndef IS_SUBTYPE
#define IS_SUBTYPE(tp1, tp2) ((tp1 == tp2) || \
((tp1 != Parser::VOID && tp2 != Parser::VOID) && \
(tp1 <= tp2)))
#endif
/// Validate a compilation unit.
/// Allocate a new symbol table and add to it all default symbols to the language and
/// then go to validate the semantic of each function in the module.
void AstSemaVisitor::visit(Parser::CompilationUnit* module) {
if (module->getSymbTable() != nullptr) {
cout << "The SymbolTable for this module has already been computed. " << endl;
cout << "Semantic analysis cannot proceed." << endl;
exit(1);
}
/// Create new symbol table for this scope
shared_ptr<SymbolTable> table(new SymbolTable(nullptr));
/// Set the symbol table in the module
module->setSymbTable(table);
/// Add all native global variables and functions
addNativeFunctions(table);
/// Update pointer to new current symbol table
this->currentSymbolTable = table;
/// Visit each function declared inside this compilation unit
for (auto it : *module->getFunctions()) {
it->accept(this);
}
/// Reset pointer to previous symbol table
this->currentSymbolTable = table->getParent();
}
/// Check the semantic of a Function definition.
void AstSemaVisitor::visit(Parser::Function* function) {
if (function->getSymbTable() != nullptr) {
cout << "The SymbolTable for this function has already been computed. " << endl;
cout << "Semantic analysis cannot proceed." << endl;
exit(1);
}
string type = function->getReturnType();
string name = function->getName();
/// Check if there isn't already a symbol table entry with this name
if (this->currentSymbolTable->lookup(name) != nullptr) {
cout << "Error in semantic analysis." << endl;
cout << "\tRedefinition of the symbol \"" << name << "\"." << endl;
exit(1);
}
/// Insert the function definition into its own symbol table, therefore
/// providing the basis for recursive calls.
shared_ptr<Parser::STFunctionDeclaration> funDecl(new Parser::STFunctionDeclaration(name, name, translateType(type)));
this->currentSymbolTable->add(funDecl);
/// Save pointer to declaration of current function. This will be handy for debugging
this->_curFunDecl = funDecl;
/* Offset is always relative to the function frame,
* so we reset when we enter a function. */
this->_currentOffset = 0;
/* Reset the counter of number of return statements of this function. */
this->currentFunReturns = 0;
/* Set the (inherited attribute) representing the return type of the
* current function. The analyze of return statements with check this
* type. */
this->currentFunRetType = translateType(type);
/* Create new symbol table for this scope. */
shared_ptr<SymbolTable> table(new SymbolTable(currentSymbolTable));
/* Set the symbol table in the function. */
function->setSymbTable(table);
/* Update pointer to new current symbol table. */
this->currentSymbolTable = table;
/* Add the node of parameters if actually there is parameters. */
list<shared_ptr<ParamDecl>>::iterator itt = function->getParams()->begin();
for (itt = function->getParams()->begin(); itt != function->getParams()->end(); itt++) {
/* The parameter is added to the symbol table. */
(*itt)->accept(this);
/* Now set set in the Function entry in the ST a pointer to its parameter entry in the ST. */
funDecl->addParam( this->currentSymbolTable->lookup((*itt)->getName()) );
}
/* Continue the visiting. */
function->getBody()->accept(this);
/* Check if return type and return statements make sense. */
if (this->currentFunRetType != Parser::VOID && this->currentFunReturns == 0) {
cout << "Error in semantic analysis." << endl;
cout << "\tMissing return statement in function (\"" << name << "\") returning non-void." << endl;
exit(1);
}
/* We save this because when we will emit temporaries we want their indices continue
* after the indices of local vars and parameters. */
function->currentOffset(this->_currentOffset);
/* Reset pointer to previous symbol table. */
this->currentSymbolTable = table->getParent();
}
void AstSemaVisitor::visit(const Parser::ParamDecl* param) {
list<shared_ptr<Parser::Expression>>::iterator it;
string type = param->getType();
string name = param->getName();
if (!isValidType(type)) {
cout << "Error in semantic analysis." << endl;
cout << "\t\"" << type << "\" is not a valid type." << endl;
exit(1);
}
if (this->currentSymbolTable->lookup(name) != nullptr) {
cout << "Error in semantic analysis." << endl;
cout << "\tThere is a previous definition of the symbol \"" << name << "\"." << endl;
exit(1);
}
/* Calculate the size of the variable and check if the array
* dimensions were specified with constants. */
int varSize = variableSize(typeWidth(translateType(type)), param->getDims());
if (varSize == 0) {
cout << "Error in semantic analysis." << endl;
cout << "\tThe size of array dimensions should be integer constants." << endl;
exit(1);
}
/* Multidimentional parameters are passed only as pointers, so we just need a pointer.*/
if (param->getDims() != nullptr && param->getDims()->size() > 0)
varSize = System::POINTER_SIZE;
/* Create the parameter definition. */
auto parDecl = make_shared<Parser::STParamDecl>(name, translateType(type), varSize, this->_currentOffset);
/* Collect the number and size of each dimension. */
for (auto _par : *param->getDims()) {
IntegerExpr *expr = dynamic_cast<IntegerExpr*>(_par.get());
parDecl->addDim(expr->value());
}
/* Include the parameter definition in the ST. */
this->currentSymbolTable->add(parDecl);
this->_currentOffset += varSize;
}
void AstSemaVisitor::visit(const Parser::VarSpec* spec) {
}
void AstSemaVisitor::visit(const Parser::VarDecl* varDec) {
string type = varDec->getType();
list<shared_ptr<Parser::VarSpec>>* specs = varDec->getVars();
list<shared_ptr<Parser::VarSpec>>::iterator it = specs->begin();
while (it != specs->end()) {
string name = (*it)->getName();
shared_ptr<SymbolTableEntry> prev = this->currentSymbolTable->lookup(name);
if (!isValidType(type)) {
cout << "Error in semantic analysis." << endl;
cout << "\t\"" << type << "\" is not a valid type." << endl;
exit(1);
}
if (prev != nullptr) {
cout << "Error in semantic analysis." << endl;
cout << "\tThere is a previous definition of the symbol \"" << name << "\"." << endl;
exit(1);
}
int dims = 0, varSize = typeWidth(translateType(type));
if ((*it)->getDimsExpr() != nullptr) {
dims = (*it)->getDimsExpr()->size();
varSize = variableSize(typeWidth(translateType(type)), (*it)->getDimsExpr());
}
if ((*it)->getInitializer() != nullptr) {
Expression* initializer = (*it)->getInitializer();
initializer->accept(this);
if ( !IS_SUBTYPE(initializer->type(), translateType(type)) ) {
cout << "Error in semantic analysis." << endl;
cout << "\tThe initializer of the variable \"" << name << "\" is not a subtype of \"" << type << "\"." << endl;
exit(1);
}
}
/* Create the parameter */
shared_ptr<Parser::STLocalVarDecl> param(new Parser::STLocalVarDecl(name, translateType(type), varSize, this->_currentOffset));
/* Collect the number and size of each dimension. */
if ((*it)->getDimsExpr() != nullptr) {
for (auto _par : *(*it)->getDimsExpr()) {
IntegerExpr *expr = dynamic_cast<IntegerExpr*>(_par.get());
if (expr == nullptr) {
cout << "Error in semantic analysis." << endl;
cout << "\tDimensions size must be integer constants." << endl;
exit(1);
}
param->addDim(expr->value());
}
}
/* Add the parameter to the current ST. */
this->currentSymbolTable->add(param);
this->_currentOffset += varSize;
it++;
}
}
void AstSemaVisitor::visit(Parser::LoopStmt* loop) {
/* Continue visiting. */
loop->getCondition()->accept(this);
if (loop->getBody() != nullptr)
loop->getBody()->accept(this);
}
void AstSemaVisitor::visit(Parser::IfStmt* ifStmt) {
Parser::Expression* condition = ifStmt->getCondition();
Parser::CodeBlock* thenBlock = ifStmt->getThenBlock();
list<shared_ptr<Parser::ElseIfStmt>>* elseIfChain = ifStmt->getElseIfChain();
Parser::CodeBlock* elseBlock = ifStmt->getElseBlock();
/* Continue visiting. */
condition->accept(this);
if (thenBlock != nullptr)
thenBlock->accept(this);
if (elseIfChain != nullptr) {
list<shared_ptr<Parser::ElseIfStmt>>::iterator it = elseIfChain->begin();
while (it != elseIfChain->end()) {
(*it)->accept(this);
it++;
}
}
if (elseBlock != nullptr)
elseBlock->accept(this);
}
void AstSemaVisitor::visit(Parser::ElseIfStmt* elseIfStmt) {
/* Continue visiting. */
elseIfStmt->getCondition()->accept(this);
if (elseIfStmt->getElseIfBlock() != nullptr)
elseIfStmt->getElseIfBlock()->accept(this);
}
void AstSemaVisitor::visit(const Parser::ReturnStmt* ret) {
if (this->currentFunRetType == Parser::VOID) {
cout << "Error in semantic analysis." << endl;
cout << "\tReturn statement present in function with void return type." << endl;
exit(1);
}
/* Continue visiting */
ret->getExpression()->accept(this);
if ( ! IS_SUBTYPE(ret->getExpression()->type(), this->currentFunRetType) ) {
cout << "Error in semantic analysis." << endl;
cout << "\tA return-statement's expression in function (\"" << this->_curFunDecl->getName()
<< "\") is not a subtype of the function return type." << endl;
exit(1);
}
/* Increment number of valid return statements. */
this->currentFunReturns++;
}
void AstSemaVisitor::visit(Parser::CodeBlock* block) {
if (block->getSymbTable() != nullptr) {
cout << "The SymbolTable for this block has already been computed. " << endl;
return ;
}
/* Create new symbol table for this scope. */
shared_ptr<SymbolTable> table(new SymbolTable(this->currentSymbolTable));
/* Set the symbol table in the function. */
block->setSymbTable(table);
/* Update pointer to new current symbol table. */
this->currentSymbolTable = table;
/* Continue the visiting. */
list<shared_ptr<Parser::Statement>>* statements = block->getStatements();
if (statements != nullptr) {
list<shared_ptr<Parser::Statement>>::iterator it = statements->begin();
while (it != statements->end()) {
(*it)->accept(this);
it++;
}
}
/* Reset pointer to previous symbol table. */
this->currentSymbolTable = table->getParent();
}
void AstSemaVisitor::visit(Parser::IntegerExpr* integer) {
integer->type(Parser::INT);
}
void AstSemaVisitor::visit(Parser::IdentifierExpr* id) {
string name = id->value();
shared_ptr<SymbolTableEntry> decl = this->currentSymbolTable->lookup(name);
list<shared_ptr<Parser::Expression>>* dims = id->dimsExprs();
/* If the symbol was not declared is a error. */
if (decl == nullptr) {
cout << "Error in semantic analysis." << endl;
cout << "\tThere is not a definition of the symbol \"" << name << "\"." << endl;
exit(1);
}
/* If the symbol is of the wrong type is not a variable is a error. */
if (dynamic_cast<STFunctionDeclaration*>(decl.get())) {
cout << "Error in semantic analysis." << endl;
cout << "\tThe symbol \"" << name << "\" is a function, a variable was expected." << endl;
exit(1);
}
/* Begin: Check matrix and variables indexing constraints. */
STVariableDeclaration* varDecl = dynamic_cast<STVariableDeclaration*>(decl.get());
/* Specified indices for acessing scalar variable. */
if (varDecl->getNumDims() == 0 && dims != nullptr && dims->size() > 0) {
cout << "Error in semantic analysis." << endl;
cout << "\tVariable \"" << name << "\" cannot be indexed as an array." << endl;
exit(1);
}
/* It was specified more dimensions than necessary. */
if (varDecl->getNumDims() < dims->size()) {
cout << "Error in semantic analysis." << endl;
cout << "\tMismatch in the number of dimensions used to access the symbol \"" << name << "\"." << endl;
exit(1);
}
/* Just the initial indices were specified, fill the remainder wit [0]. */
// if ((varDecl->getNumDims() > 0 && dims == nullptr) || (varDecl->getNumDims() > dims->size())) {
// int remainder = (dims == nullptr) ? varDecl->getNumDims() : varDecl->getNumDims() - dims->size();
//
// for (int i=0; i<remainder; i++)
// dims->push_back(shared_ptr<Parser::IntegerExpr>(new Parser::IntegerExpr(location(), 0)));
// }
/* Continue visiting. */
if (dims != nullptr) {
list<shared_ptr<Parser::Expression>>::iterator it = dims->begin();
while (it != dims->end()) {
(*it)->accept(this);
it++;
}
/* Check if the expressions used to index the array are all constants. */
for (it = dims->begin(); it != dims->end(); it++) {
if ( ! CHECK_TYPE((*it), INT) ) {
cout << "Error in semantic analysis." << endl;
cout << "\tYou must index arrays using integer values.." << endl;
exit(1);
}
}
}
/* Register to which symbol table entry this 'id' refers to. This will
* be helpfull when generating intermediate representation. */
id->addr(decl);
/* This reference to a variable has the type of the variable. */
id->type(varDecl->type());
}
void AstSemaVisitor::visit(Parser::FunctionCall* funCall) {
string name = funCall->name();
shared_ptr<SymbolTableEntry> decl = this->currentSymbolTable->lookup(name);
list<shared_ptr<Parser::Expression>>* arguments = funCall->arguments();
list<shared_ptr<Parser::Expression>>::iterator it = arguments->begin();
/* If the symbol was not declared is a error. */
if (decl == nullptr) {
cout << "Error in semantic analysis." << endl;
cout << "\tThere is not a definition of the symbol \"" << name << "\"." << endl;
exit(1);
}
/* Check if there is a *function* definition. */
STFunctionDeclaration *funDecl = dynamic_cast<STFunctionDeclaration*>(decl.get());
if (funDecl == nullptr) {
cout << "Error in semantic analysis." << endl;
cout << "\tThere is an attempt to use the symbol \"" << name << "\" as a function but it's declared type is variable." << endl;
exit(1);
}
/* Check number of parameters. */
if (funDecl->params().size() != funCall->arguments()->size()) {
cout << "Error in semantic analysis." << endl;
cout << "\tIncorrect number of parameters were passed to function \"" << name << "\"." << endl;
cout << "\t" << funDecl->params().size() << " were expected, but " << arguments->size() << " was passed." << endl;
exit(1);
}
/* Continue visiting. */
if (arguments != nullptr) {
list<shared_ptr<Parser::Expression>>::iterator it = arguments->begin();
while (it != arguments->end()) {
(*it)->accept(this);
it++;
}
}
/* Need to check the type of the parameters. */
if (arguments != nullptr) {
list<shared_ptr<Parser::Expression>>::iterator it = arguments->begin();
const vector<shared_ptr<SymbolTableEntry>> parameters = funDecl->params();
for (int i=0; it != arguments->end(); it++, i++) {
Parser::Expression* exp = it->get();
STParamDecl* param = dynamic_cast<STParamDecl*>(parameters[i].get());
/* If the argument is not a subtype of the parameter type it is an error. */
if ( ! IS_SUBTYPE(exp->type(), param->type()) ) {
cout << "Error in semantic analysis." << endl;
cout << "\tThe " << (i+1) << "'th parameter of the function \"" << funDecl->getName() << "\" is a \"" << Util::typeName(param->type()) << "\"";
cout << " but an \"" << Util::typeName(exp->type()) << "\" was informed." << endl;
exit(1);
}
}
}
/* Register to which symbol table entry this 'funCall' refers to.
* This will be helpfull when generating intermediate representation. */
funCall->addr(decl);
/* The type of the function call is the function return type. */
funCall->type(funDecl->type());
}
void AstSemaVisitor::visit(Parser::UnaryExpr* unary) {
/* Continue visiting */
unary->exp()->accept(this);
/* We can only take the address of left-hand operands. */
if (unary->opr() == UnaryExpr::ADDR && !dynamic_cast<Parser::IdentifierExpr*>(unary->exp())) {
cout << "Error in semantic analysis." << endl;
cout << "\tIt is only allowed to take the address of variables." << endl;
exit(1);
}
/* Currently we assume we can't negate anything that isn't a integer.*/
if (unary->opr() == UnaryExpr::BIT_NOT && unary->exp()->type() != Parser::INT) {
cout << "Error in semantic analysis." << endl;
cout << "\tIt is only allowed to negate integer expressions." << endl;
exit(1);
}
unary->type(unary->exp()->type());
}
void AstSemaVisitor::visit(Parser::BinaryExpr* binop) {
/* Continue visiting. */
binop->getExp1()->accept(this);
binop->getExp2()->accept(this);
Expression* exp1 = binop->getExp1();
Expression* exp2 = binop->getExp2();
/* Arithmetic expressions are allowed only between numerical types. */
if ((binop->opr() == BinaryExpr::BIT_AND || binop->opr() == BinaryExpr::BIT_OR || binop->opr() == BinaryExpr::BIT_XOR) &&
(exp1->type() != Parser::INT || exp2->type() != Parser::INT)) {
cout << "Error in semantic analysis." << endl;
cout << "\tBinary expressions currently are only allowed only between integer types." << endl;
exit(1);
}
else if (binop->opr() == BinaryExpr::MOD && (exp1->type() != Parser::INT || exp2->type() != Parser::INT)) {
cout << "Error in semantic analysis." << endl;
cout << "\tWe don't support module for floating expressions." << endl;
exit(1);
}
else if (exp1->type() == exp2->type()) {
binop->type(exp1->type());
}
}
void AstSemaVisitor::addNativeFunctions(shared_ptr<Parser::SymbolTable> table) {
/* Global functions for outputing data. */
shared_ptr<STFunctionDeclaration> printInt(new STFunctionDeclaration("printInt", "_printInt", Parser::VOID));
/* We need to add the function_formal parameters. */
shared_ptr<STParamDecl> printIntParam = shared_ptr<STParamDecl>( new STParamDecl("_printInt_1", Parser::INT, typeWidth(Parser::INT), this->_currentOffset+=typeWidth(Parser::INT)));
table->add(printIntParam);
printInt->addParam(printIntParam);
table->add(printInt);
/* Global functions for reading data. */
shared_ptr<STFunctionDeclaration> readInt(new STFunctionDeclaration("readInt", "_readInt", Parser::INT));
}
bool AstSemaVisitor::isValidType(string name) {
if (name == "int") return true;
else if (name == "void") return true;
else return false;
}
string Util::typeName(Parser::NativeType type) {
if (type == Parser::INT) return "int";
else if (type == Parser::VOID) return "void";
else throw -1;
}
NativeType AstSemaVisitor::translateType(string name) {
if (name == "int") return Parser::INT;
else if (name == "void") return Parser::VOID;
else return Parser::NOT_A_TYPE;
}
TypeWidth AstSemaVisitor::typeWidth(NativeType name) {
if (name == Parser::INT) return Parser::INT_WIDTH;
else if (name == Parser::VOID) return Parser::VOID_WIDTH;
else throw -1;
}
int AstSemaVisitor::variableSize(int typeSize, list<shared_ptr<Expression>>* dims) {
int units = 1;
list<shared_ptr<Expression>>::iterator it;
for (it = dims->begin(); it != dims->end(); it++) {
IntegerExpr *expr = dynamic_cast<IntegerExpr*>((*it).get());
if (expr == nullptr) return 0;
units *= expr->value();
}
return typeSize * units;
}