Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
epidemian
GitHub Repository: epidemian/gravity
Path: blob/master/src/compiler/gravity_semacheck2.c
1214 views
1
//
2
// gravity_semacheck2.c
3
// gravity
4
//
5
// Created by Marco Bambini on 12/09/14.
6
// Copyright (c) 2014 CreoLabs. All rights reserved.
7
//
8
9
#include "gravity_symboltable.h"
10
#include "gravity_semacheck2.h"
11
#include "gravity_compiler.h"
12
#include "gravity_visitor.h"
13
14
struct semacheck_t {
15
gnode_r *declarations; // declarations stack
16
uint16_r statements; // statements stack
17
uint32_t lasterror; // last error line number to prevent reporting more than one error per line
18
};
19
typedef struct semacheck_t semacheck_t;
20
21
#define REPORT_ERROR(node,...) report_error(self, GRAVITY_ERROR_SEMANTIC, (gnode_t *)node, __VA_ARGS__);
22
#define REPORT_WARNING(node,...) report_error(self, GRAVITY_WARNING, (gnode_t *)node, __VA_ARGS__)
23
24
#define STATEMENTS ((semacheck_t *)self->data)->statements
25
#define PUSH_STATEMENT(stat) marray_push(uint16_t, STATEMENTS, (uint16_t)stat)
26
#define POP_STATEMENT() marray_pop(STATEMENTS)
27
#define TOP_STATEMENT() (marray_size(STATEMENTS) ? marray_last(STATEMENTS) : 0)
28
#define TOP_STATEMENT_ISA(stat) (TOP_STATEMENT() == stat)
29
#define TOP_STATEMENT_ISA_SWITCH() (TOP_STATEMENT_ISA(TOK_KEY_SWITCH))
30
#define TOP_STATEMENT_ISA_LOOP() ((TOP_STATEMENT_ISA(TOK_KEY_WHILE)) || \
31
(TOP_STATEMENT_ISA(TOK_KEY_REPEAT)) || \
32
(TOP_STATEMENT_ISA(TOK_KEY_FOR)))
33
34
#define PUSH_DECLARATION(node) gnode_array_push(((semacheck_t *)self->data)->declarations, (gnode_t*)node)
35
#define POP_DECLARATION() gnode_array_pop(((semacheck_t *)self->data)->declarations)
36
#define TOP_DECLARATION() gnode_array_get(((semacheck_t *)self->data)->declarations, gnode_array_size(((semacheck_t *)self->data)->declarations)-1)
37
#define ISA(n1,_tag) ((n1) ? (((gnode_t *)n1)->tag == _tag) : 0)
38
#define ISA_CLASS(n1) (((gnode_t *)n1)->tag == NODE_CLASS_DECL)
39
#define ISA_VAR_DECLARATION(n1) (((gnode_t *)n1)->tag == NODE_VARIABLE_DECL)
40
#define ISA_VARIABLE(n1) (((gnode_t *)n1)->tag == NODE_VARIABLE)
41
#define ISA_LITERAL(n1) (((gnode_t *)n1)->tag == NODE_LITERAL_EXPR)
42
#define ISA_IDENTIFIER(n1) (((gnode_t *)n1)->tag == NODE_IDENTIFIER_EXPR)
43
#define ISA_ID(n1) (((gnode_t *)n1)->tag == NODE_ID_EXPR)
44
45
#define SET_LOCAL_INDEX(var, symtable) var->index = (uint16_t)symboltable_local_index(symtable)
46
#define SET_NODE_LOCATION(_node, _type, _idx, _nup) _node->location.type = _type; _node->location.index = _idx; _node->location.nup = _nup;
47
48
// MARK: -
49
50
static void report_error (gvisitor_t *self, error_type_t error_type, gnode_t *node, const char *format, ...) {
51
// check last error line in order to prevent to emit multiple errors for the same row
52
semacheck_t *current = (semacheck_t *)self->data;
53
if (node->token.lineno == current->lasterror) return;
54
current->lasterror = node->token.lineno;
55
56
// increment internal error counter
57
++self->nerr;
58
59
// get error callback (if any)
60
void *data = (self->delegate) ? ((gravity_delegate_t *)self->delegate)->xdata : NULL;
61
gravity_error_callback error_fn = (self->delegate) ? ((gravity_delegate_t *)self->delegate)->error_callback : NULL;
62
63
// build error message
64
char buffer[1024];
65
va_list arg;
66
if (format) {
67
va_start (arg, format);
68
vsnprintf(buffer, sizeof(buffer), format, arg);
69
va_end (arg);
70
}
71
72
// setup error struct
73
error_desc_t error_desc = {
74
.code = 0,
75
.lineno = node->token.lineno,
76
.colno = node->token.colno,
77
.fileid = node->token.fileid,
78
.offset = node->token.position
79
};
80
81
// finally call error callback
82
if (error_fn) error_fn(error_type, buffer, error_desc, data);
83
else printf("%s\n", buffer);
84
}
85
86
static symboltable_t *symtable_from_node (gnode_t *node) {
87
// globals
88
if (ISA(node, NODE_LIST_STAT)) return ((gnode_compound_stmt_t *)node)->symtable;
89
90
// class symtable
91
if (ISA(node, NODE_CLASS_DECL)) return ((gnode_class_decl_t *)node)->symtable;
92
93
// enum symtable
94
if (ISA(node, NODE_ENUM_DECL)) return ((gnode_enum_decl_t *)node)->symtable;
95
96
// module symtable
97
if (ISA(node, NODE_MODULE_DECL)) return ((gnode_module_decl_t *)node)->symtable;
98
99
// function symtable
100
if (ISA(node, NODE_FUNCTION_DECL)) return ((gnode_function_decl_t *)node)->symtable;
101
102
// should never reach this point
103
assert(0);
104
return NULL;
105
}
106
107
// lookup identifier into node
108
static gnode_t *lookup_node (gnode_t *node, const char *identifier) {
109
symboltable_t *symtable = symtable_from_node(node);
110
return symboltable_lookup(symtable, identifier);
111
}
112
113
// lookup an identifier into a stack of symbol tables
114
// location inside node is updated with the result
115
// and node found is returned
116
static gnode_t *lookup_identifier (gvisitor_t *self, const char *identifier, gnode_identifier_expr_t *node) {
117
gnode_r *decls = ((semacheck_t *)self->data)->declarations;
118
size_t len = gnode_array_size(decls);
119
if (len == 0) return NULL;
120
121
uint16_t nf = 0; // number of functions traversed
122
uint16_t nc = 0; // number of classes traversed
123
124
// get first node (the latest in the decls stack)
125
gnode_t *base_node = gnode_array_get(decls, len-1);
126
bool base_is_class = ISA(base_node, NODE_CLASS_DECL);
127
128
for (int i=(int)len-1; i>=0; --i) {
129
gnode_t *target = gnode_array_get(decls, i);
130
131
// identify target type
132
bool target_is_global = ISA(target, NODE_LIST_STAT);
133
bool target_is_function = ISA(target, NODE_FUNCTION_DECL);
134
bool target_is_class = ISA(target, NODE_CLASS_DECL);
135
bool target_is_module = ISA(target, NODE_MODULE_DECL);
136
137
if (target_is_function) ++nf;
138
else if (target_is_class) ++nc;
139
140
// lookup identifier is current target (obtained traversing the declaration stack)
141
gnode_t *symbol = lookup_node(target, identifier);
142
143
// sanity check: if base_node is a class and symbol was found inside a func then report an error
144
if (symbol && target_is_function && base_is_class) {
145
// added to explicitly prevent cases like:
146
/*
147
func foo() {
148
var a;
149
class b {
150
func bar() {return a;}
151
}
152
}
153
*/
154
REPORT_ERROR(node, "Unable to access local func var %s from within a class.", identifier);
155
}
156
157
// if target is class and symbol is not found then lookup also its superclass hierarchy
158
if (!symbol && target_is_class) {
159
// lookup identifier in super (if not found target class)
160
gnode_class_decl_t *c = (gnode_class_decl_t *)target;
161
gnode_class_decl_t *super = (gnode_class_decl_t *)c->superclass;
162
while (super) {
163
symbol = lookup_node((gnode_t *)super, identifier);
164
if (symbol) {
165
if (NODE_ISA(symbol, NODE_VARIABLE)) {
166
gnode_var_t *p = (gnode_var_t *)symbol;
167
if (p->access == TOK_KEY_PRIVATE) {
168
REPORT_ERROR(node, "Forbidden access to private ivar %s from a subclass.", p->identifier);
169
}
170
}
171
break;
172
}
173
super = (gnode_class_decl_t *)super->superclass;
174
}
175
}
176
177
// continue lookup in declaration stack is symbol is not found
178
if (!symbol) continue;
179
180
// symbol found so process it bases on target type
181
if (target_is_global) {
182
// identifier found in global no other information is needed
183
SET_NODE_LOCATION(node, LOCATION_GLOBAL, 0, 0);
184
DEBUG_LOOKUP("Identifier %s found in GLOBALS", identifier);
185
186
node->symbol = symbol;
187
return symbol;
188
}
189
190
// if symbol is a variable then copy its index
191
uint16_t index = UINT16_MAX;
192
if (NODE_ISA(symbol, NODE_VARIABLE)) {
193
gnode_var_t *p = (gnode_var_t *)symbol;
194
index = p->index;
195
}
196
197
if (target_is_function) {
198
// Symbol found in a function
199
if (nf > 1) {
200
assert(ISA(base_node, NODE_FUNCTION_DECL));
201
202
// symbol is upvalue and its index represents an index inside uplist
203
gnode_var_t *var = (gnode_var_t *)symbol;
204
gnode_function_decl_t *f = ((gnode_function_decl_t *)base_node);
205
uint16_t n = nf - 1;
206
gupvalue_t *upvalue = gnode_function_add_upvalue(f, var, n);
207
208
// add upvalue to all enclosing functions
209
// base_node has index = len - 1 so from (len - 2) up to n-1 levels
210
uint16_t idx = len - 2;
211
while (n > 1) {
212
f = (gnode_function_decl_t *) gnode_array_get(decls, idx);
213
gnode_function_add_upvalue(f, var, --n);
214
--idx;
215
}
216
217
var->upvalue = true;
218
node->upvalue = upvalue;
219
SET_NODE_LOCATION(node, LOCATION_UPVALUE, index, nf);
220
} else {
221
// symbol is local
222
SET_NODE_LOCATION(node, LOCATION_LOCAL, index, nf);
223
}
224
DEBUG_LOOKUP("Identifier %s found in FUNCTION %s (nf: %lu index: %d)", identifier, ((gnode_function_decl_t *)target)->identifier, nf-1, index);
225
}
226
else if (target_is_class) {
227
// Symbol found in a class
228
SET_NODE_LOCATION(node, (nc == 1) ? LOCATION_CLASS_IVAR_SAME : LOCATION_CLASS_IVAR_OUTER, index, nc-1);
229
DEBUG_LOOKUP("Identifier %s found in CLASS %s (up to %zu outer levels)", identifier, ((gnode_class_decl_t *)target)->identifier, nc-1);
230
}
231
else if (target_is_module) {
232
// Symbol found in a module
233
// Module support not yet ready
234
}
235
else {
236
// Should never reach this point
237
assert(0);
238
}
239
240
node->symbol = symbol;
241
return symbol;
242
}
243
244
DEBUG_LOOKUP("Identifier %s NOT FOUND\n", identifier);
245
return NULL;
246
}
247
248
static gnode_t *lookup_symtable_id (gvisitor_t *self, gnode_identifier_expr_t *id, bool isclass) {
249
gnode_t *target = NULL;
250
251
gnode_t *target1 = lookup_identifier(self, id->value, id);
252
if (!target1) {REPORT_ERROR((gnode_t *)id, "%s %s not found.", (isclass) ? "Class" : "Protocol", id->value); return NULL;}
253
target = target1;
254
255
if (id->value2) {
256
gnode_t *target2 = lookup_node(target1, id->value2);
257
if (!target2) {REPORT_ERROR((gnode_t *)id, "%s %s not found in %s.", (isclass) ? "Class" : "Protocol", id->value2, id->value); return NULL;}
258
target = target2;
259
}
260
261
return target;
262
}
263
264
// MARK: -
265
266
static bool is_expression (gnode_t *node) {
267
gnode_n tag = NODE_TAG(node);
268
return ((tag >= NODE_BINARY_EXPR) && (tag <= NODE_ACCESS_EXPR));
269
}
270
271
static bool is_expression_assignment (gnode_t *node) {
272
gnode_n tag = NODE_TAG(node);
273
if (tag == NODE_BINARY_EXPR) {
274
gnode_binary_expr_t *expr = (gnode_binary_expr_t *)node;
275
return (expr->op == TOK_OP_ASSIGN);
276
}
277
return false;
278
}
279
280
static bool is_expression_range (gnode_t *node) {
281
gnode_n tag = NODE_TAG(node);
282
if (tag == NODE_BINARY_EXPR) {
283
gnode_binary_expr_t *expr = (gnode_binary_expr_t *)node;
284
return ((expr->op == TOK_OP_RANGE_INCLUDED) || (expr->op == TOK_OP_RANGE_EXCLUDED));
285
}
286
return false;
287
}
288
289
290
static bool is_expression_valid (gnode_t *node) {
291
if (!node) return false;
292
293
/*
294
From: http://c2.com/cgi/wiki?FirstClass
295
296
| Class of value
297
Manipulation | First Second Third
298
===============================+================================
299
Pass value as a parameter | yes yes no
300
Return value from a procedure | yes no no
301
Assign value into a variable | yes no no
302
303
*/
304
305
/*
306
307
NODE_LIST_STAT, NODE_COMPOUND_STAT, NODE_LABEL_STAT, NODE_FLOW_STAT, NODE_JUMP_STAT,
308
NODE_LOOP_STAT, NODE_EMPTY_STAT,
309
310
// declarations: 6
311
NODE_ENUM_DECL, NODE_FUNCTION_DECL, NODE_VARIABLE_DECL, NODE_CLASS_DECL, NODE_MODULE_DECL,
312
NODE_VARIABLE,
313
314
// expressions: 8
315
NODE_BINARY_EXPR, NODE_UNARY_EXPR, NODE_FILE_EXPR,
316
NODE_LIST_EXPR, NODE_LITERAL_EXPR, NODE_IDENTIFIER_EXPR, NODE_KEYWORD_EXPR,
317
NODE_FUNCTION_EXPR,
318
319
// postfix expression type: 2 + NODE_IDENTIFIER_EXPR
320
NODE_CALL, NODE_SUBSCRIPT
321
322
*/
323
324
// fixme
325
gnode_n tag = NODE_TAG(node);
326
switch (tag) {
327
case NODE_UNARY_EXPR: {
328
return is_expression_valid(((gnode_unary_expr_t *)node)->expr);
329
}
330
331
case NODE_BINARY_EXPR: {
332
gnode_binary_expr_t *expr = (gnode_binary_expr_t *)node;
333
if (expr->op == TOK_OP_ASSIGN) return false;
334
if (!is_expression_valid(expr->left)) return false;
335
return is_expression_valid(expr->right);
336
}
337
338
case NODE_IDENTIFIER_EXPR: {
339
return true;
340
}
341
342
case NODE_MODULE_DECL:
343
case NODE_ENUM_DECL: {
344
return false;
345
}
346
347
default: break;
348
}
349
350
return true;
351
}
352
353
static bool is_init_function (gnode_t *node) {
354
if (ISA(node, NODE_FUNCTION_DECL)) {
355
gnode_function_decl_t *f = (gnode_function_decl_t *)node;
356
if (!f->identifier) return false;
357
return (strcmp(f->identifier, CLASS_CONSTRUCTOR_NAME) == 0);
358
}
359
return false;
360
}
361
362
static bool is_init_infinite_loop(gvisitor_t *self, gnode_identifier_expr_t *identifier, gnode_r *list) {
363
364
// for example:
365
// class c1 {
366
// func init() {
367
// var a = c1(); // INFINITE LOOP
368
// var a = self(); // INFINITE LOOP
369
// }
370
// }
371
372
// conditions for an infinite loop in init:
373
374
// 1. there should be at least 2 declarations in the stack
375
gnode_r *decls = ((semacheck_t *)self->data)->declarations;
376
size_t len = gnode_array_size(decls);
377
if (len < 2) return false;
378
379
// 2. current function is init
380
if (!is_init_function(gnode_array_get(decls, len-1))) return false;
381
382
// 3. outer declaration is a class
383
gnode_t *target_node = gnode_array_get(decls, len-2);
384
if (!ISA(target_node, NODE_CLASS_DECL)) return false;
385
386
// 4. identifier is self OR identifier->symbol points to target_node
387
bool continue_check = false;
388
if (identifier->symbol) continue_check = target_node == identifier->symbol;
389
else continue_check = ((identifier->value) && (strcmp(identifier->value, SELF_PARAMETER_NAME) == 0));
390
if (!continue_check) return false;
391
392
// 5. check if next node is a call
393
size_t count = gnode_array_size(list);
394
if (count < 1) return false;
395
gnode_postfix_subexpr_t *subnode = (gnode_postfix_subexpr_t *)gnode_array_get(list, 0);
396
return (subnode->base.tag == NODE_CALL_EXPR);
397
}
398
399
static void check_access_storage_specifiers (gvisitor_t *self, gnode_t *node, gnode_n env, gtoken_t access, gtoken_t storage) {
400
// check for module node
401
if (NODE_TAG(node) == NODE_MODULE_DECL) {
402
if (access != 0) REPORT_ERROR(node, "Access specifier cannot be used for module.");
403
if (storage != 0) REPORT_ERROR(node, "Storage specifier cannot be used for module.");
404
}
405
406
// check fo access specifiers here
407
// access specifier does make sense only inside module or class declaration
408
// in any other enclosing environment must be considered a semantic error
409
if ((access != 0) && (env != NODE_CLASS_DECL) && (env != NODE_MODULE_DECL)) {
410
REPORT_ERROR(node, "Access specifier does not make sense here.");
411
}
412
413
// storage specifier (STATIC) makes sense only inside a class declaration
414
if ((storage == TOK_KEY_STATIC) && (env != NODE_CLASS_DECL)) {
415
REPORT_ERROR(node, "Static storage specifier does not make sense outside a class declaration.");
416
}
417
}
418
419
static bool check_assignment_expression (gvisitor_t *self, gnode_binary_expr_t *node) {
420
// in case of assignment check left node: assure assignment is made to identifier or other valid expressions
421
// for example left expression cannot be a literal (to prevent 3 = 2)
422
423
gnode_n tag = NODE_TAG(node->left);
424
bool result = ((tag == NODE_IDENTIFIER_EXPR) || (tag == NODE_FILE_EXPR) || (tag == NODE_POSTFIX_EXPR));
425
426
// more checks in the postfix case
427
if (tag == NODE_POSTFIX_EXPR) {
428
gnode_postfix_expr_t *expr = (gnode_postfix_expr_t *)node->left;
429
430
// in case of postfix expression
431
// enum has already been processed so it appears as a literal with expr->list NULL
432
// inside a postfix expression node
433
// check enum case (enum cannot be assigned)
434
if (ISA(expr->id, NODE_LITERAL_EXPR)) {
435
result = false;
436
} else {
437
// basically the LATEST node of a postfix expression cannot be a CALL in an assignment
438
// so we are avoiding expressions like: a(123) = ...; or a.b.c(1,2) = ...;
439
size_t count = gnode_array_size(expr->list);
440
gnode_postfix_subexpr_t *subnode = (gnode_postfix_subexpr_t *)gnode_array_get(expr->list, count-1);
441
result = (NODE_TAG(subnode) != NODE_CALL_EXPR);
442
}
443
}
444
445
// set is_assignment flag (default to false)
446
node->left->is_assignment = result;
447
448
if (!result) REPORT_ERROR(node->left, "Wrong assignment expression.");
449
return result;
450
}
451
452
static bool check_range_expression (gvisitor_t *self, gnode_binary_expr_t *node) {
453
// simple check, if nodes are literals then they must be INT
454
gnode_t *r[2] = {node->left, node->right};
455
456
for (int i=0; i<2; ++i) {
457
gnode_t *range = r[i];
458
if (ISA_LITERAL(range)) {
459
gnode_literal_expr_t *expr = (gnode_literal_expr_t *)range;
460
if (expr->type != LITERAL_INT) {REPORT_ERROR(node, "Range must be integer.");} return false;
461
}
462
}
463
return true;
464
}
465
466
static bool check_class_ivar (gvisitor_t *self, gnode_class_decl_t *classnode, gnode_variable_decl_t *node) {
467
size_t count = gnode_array_size(node->decls);
468
469
gnode_class_decl_t *supernode = (gnode_class_decl_t *)classnode->superclass;
470
for (size_t i=0; i<count; ++i) {
471
gnode_var_t *p = (gnode_var_t *)gnode_array_get(node->decls, i);
472
DEBUG_SEMANTIC("check_ivar %s", p->identifier);
473
474
// do not check internal outer var
475
if (string_cmp(p->identifier, OUTER_IVAR_NAME) == 0) continue;
476
477
while (supernode) {
478
symboltable_t *symtable = supernode->symtable;
479
if (symboltable_lookup(symtable, p->identifier) != NULL) {
480
REPORT_WARNING((gnode_t *)node, "Property '%s' defined in class '%s' already defined in its superclass %s.", p->identifier, classnode->identifier, supernode->identifier);
481
}
482
supernode = (gnode_class_decl_t *)supernode->superclass;
483
}
484
}
485
486
return true;
487
}
488
489
static void free_postfix_subexpr (gnode_postfix_subexpr_t *subnode) {
490
// check refcount
491
if (subnode->base.refcount > 0) {--subnode->base.refcount; return;}
492
493
// manually free postfix subnode
494
gnode_n tag = subnode->base.tag;
495
if (tag == NODE_CALL_EXPR) {
496
if (subnode->args) {
497
gnode_array_each(subnode->args, gnode_free(val););
498
gnode_array_free(subnode->args);
499
}
500
} else {
501
gnode_free(subnode->expr);
502
}
503
504
mem_free((gnode_t*)subnode);
505
}
506
507
// MARK: - Statements -
508
509
static void visit_list_stmt (gvisitor_t *self, gnode_compound_stmt_t *node) {
510
DEBUG_SEMANTIC("visit_list_stmt");
511
512
PUSH_DECLARATION(node);
513
gnode_array_each(node->stmts, {visit(val);});
514
POP_DECLARATION();
515
}
516
517
static void visit_compound_stmt (gvisitor_t *self, gnode_compound_stmt_t *node) {
518
DEBUG_SEMANTIC("visit_compound_stmt");
519
520
gnode_t *top = TOP_DECLARATION();
521
symboltable_t *symtable = symtable_from_node(top);
522
523
symboltable_enter_scope(symtable);
524
gnode_array_each(node->stmts, {visit(val);});
525
526
symboltable_exit_scope(symtable, &node->nclose);
527
}
528
529
static void visit_label_stmt (gvisitor_t *self, gnode_label_stmt_t *node) {
530
DEBUG_SEMANTIC("visit_label_stmt");
531
532
gtoken_t type = NODE_TOKEN_TYPE(node);
533
if (!TOP_STATEMENT_ISA_SWITCH()) {
534
if (type == TOK_KEY_DEFAULT) REPORT_ERROR(node, "'default' statement not in switch statement.");
535
if (type == TOK_KEY_CASE) REPORT_ERROR(node, "'case' statement not in switch statement.");
536
}
537
538
if (type == TOK_KEY_DEFAULT) {visit(node->stmt);}
539
else if (type == TOK_KEY_CASE) {visit(node->expr); visit(node->stmt);}
540
}
541
542
static void visit_flow_stmt (gvisitor_t *self, gnode_flow_stmt_t *node) {
543
DEBUG_SEMANTIC("visit_flow_stmt");
544
545
// assignment has no side effect so report error in case of assignment
546
if (is_expression_assignment(node->cond)) REPORT_ERROR(node->cond, "Assignment not allowed here");
547
548
gtoken_t type = NODE_TOKEN_TYPE(node);
549
if (type == TOK_KEY_IF) {
550
visit(node->cond);
551
visit(node->stmt);
552
if (node->elsestmt) visit(node->elsestmt);
553
} else if (type == TOK_KEY_SWITCH) {
554
PUSH_STATEMENT(type);
555
visit(node->cond);
556
visit(node->stmt);
557
POP_STATEMENT();
558
}
559
}
560
561
static void visit_loop_stmt (gvisitor_t *self, gnode_loop_stmt_t *node) {
562
DEBUG_SEMANTIC("visit_loop_stmt");
563
564
gtoken_t type = NODE_TOKEN_TYPE(node);
565
PUSH_STATEMENT(type);
566
567
// check pre-conditions
568
const char *LOOP_NAME;
569
gnode_t *cond;
570
if (type == TOK_KEY_WHILE) {LOOP_NAME = "WHILE"; cond = node->cond;}
571
else if (type == TOK_KEY_REPEAT) {LOOP_NAME = "REPEAT"; cond = node->expr;}
572
else if (type == TOK_KEY_FOR) {LOOP_NAME = "FOR"; cond = node->cond;}
573
else {cond = NULL; assert(0);}
574
if (is_expression_assignment(cond))
575
REPORT_ERROR(cond, "Assignments in Gravity does not return a value so cannot be used inside a %s condition.", LOOP_NAME);
576
577
// FOR condition MUST be a VARIABLE declaration or an IDENTIFIER
578
if (type == TOK_KEY_FOR) {
579
bool type_check = (NODE_ISA(node->cond, NODE_VARIABLE_DECL) || NODE_ISA(node->cond, NODE_IDENTIFIER_EXPR));
580
if (!type_check) REPORT_ERROR(cond, "FOR declaration must be a variable declaration or a local identifier.");
581
582
if (NODE_ISA(node->cond, NODE_VARIABLE_DECL)) {
583
gnode_variable_decl_t *var = (gnode_variable_decl_t *)node->cond;
584
585
// assure var declares just ONE variable
586
if (gnode_array_size(var->decls) > 1) REPORT_ERROR(cond, "Cannot declare more than one variable inside a FOR loop.");
587
588
// assure that there is no assignment expression
589
gnode_var_t *p = (gnode_var_t *)gnode_array_get(var->decls, 0);
590
if (p->expr) REPORT_ERROR(cond, "Assignment expression prohibited in a FOR loop.");
591
}
592
}
593
594
if (type == TOK_KEY_WHILE) {
595
visit(node->cond);
596
visit(node->stmt);
597
} else if (type == TOK_KEY_REPEAT) {
598
visit(node->stmt);
599
visit(node->expr);
600
} else if (type == TOK_KEY_FOR) {
601
symboltable_t *symtable = symtable_from_node(TOP_DECLARATION());
602
symboltable_enter_scope(symtable);
603
visit(node->cond);
604
if (NODE_ISA(node->cond, NODE_IDENTIFIER_EXPR)) {
605
//if cond is not a var declaration then it must be a local identifier
606
gnode_identifier_expr_t *expr = (gnode_identifier_expr_t *)node->cond;
607
if (expr->location.type != LOCATION_LOCAL) REPORT_ERROR(cond, "FOR declaration must be a variable declaration or a local identifier.");
608
}
609
visit(node->expr);
610
visit(node->stmt);
611
612
symboltable_exit_scope(symtable, &node->nclose);
613
}
614
POP_STATEMENT();
615
}
616
617
static void visit_jump_stmt (gvisitor_t *self, gnode_jump_stmt_t *node) {
618
DEBUG_SEMANTIC("visit_jump_stmt");
619
620
gtoken_t type = NODE_TOKEN_TYPE(node);
621
if (type == TOK_KEY_BREAK) {
622
if (!(TOP_STATEMENT_ISA_LOOP() || TOP_STATEMENT_ISA_SWITCH()))
623
REPORT_ERROR(node, "'break' statement not in loop or switch statement.");
624
}
625
else if (type == TOK_KEY_CONTINUE) {
626
if (!TOP_STATEMENT_ISA_LOOP()) REPORT_ERROR(node, "'continue' statement not in loop statement.");
627
}
628
else if (type == TOK_KEY_RETURN) {
629
gnode_t *n1 = TOP_DECLARATION(); // n1 == NULL means globals
630
if (!ISA(n1, NODE_FUNCTION_DECL)) REPORT_ERROR(node, "'return' statement not in a function definition.");
631
632
if (node->expr) {
633
visit(node->expr);
634
if (!is_expression_valid(node->expr)) {
635
REPORT_ERROR(node->expr, "Invalid expression.");
636
return;
637
}
638
}
639
} else {
640
assert(0);
641
}
642
}
643
644
static void visit_empty_stmt (gvisitor_t *self, gnode_empty_stmt_t *node) {
645
DEBUG_SEMANTIC("visit_empty_stmt");
646
647
// get top declaration
648
gnode_t *top = TOP_DECLARATION();
649
if (!NODE_ISA_FUNCTION(top)) REPORT_ERROR(node, "Extraneous semicolon error.");
650
651
return;
652
}
653
654
// MARK: - Declarations -
655
656
static void visit_function_decl (gvisitor_t *self, gnode_function_decl_t *node) {
657
DEBUG_SEMANTIC("visit_function_decl %s", node->identifier);
658
659
// set top declaration
660
gnode_t *top = TOP_DECLARATION();
661
662
// check if optional access and storage specifiers make sense in current context
663
check_access_storage_specifiers(self, (gnode_t *)node, NODE_TAG(top), node->access, node->storage);
664
665
// get enclosing declaration
666
node->env = top;
667
668
// enter function scope
669
PUSH_DECLARATION(node);
670
symboltable_t *symtable = symboltable_create(false);
671
symboltable_enter_scope(symtable);
672
673
// process parameters
674
node->symtable = symtable;
675
if (node->params) {
676
gnode_array_each(node->params, {
677
gnode_var_t *p = (gnode_var_t *)val;
678
p->env = (gnode_t*)node;
679
if (!symboltable_insert(symtable, p->identifier, (void *)p)) REPORT_ERROR(p, "Parameter %s redeclared.", p->identifier);
680
SET_LOCAL_INDEX(p, symtable);
681
DEBUG_SEMANTIC("Local:%s index:%d", p->identifier, p->index);
682
});
683
}
684
685
// process inner block
686
gnode_compound_stmt_t *block = node->block;
687
if (block) {gnode_array_each(block->stmts, {visit(val);});}
688
689
// exit function scope
690
uint16_t nparams = (node->params) ? (uint16_t)marray_size(*node->params) : 0;
691
uint32_t nlocals = symboltable_exit_scope(symtable, NULL);
692
if (nlocals > MAX_LOCALS) REPORT_ERROR(node, "Maximum number of local variables reached in function %s (max:%d found:%d).",
693
node->identifier, MAX_LOCALS, nlocals);
694
node->nlocals = (uint16_t)nlocals - nparams;
695
node->nparams = nparams;
696
697
// check upvalue limit
698
uint32_t nupvalues = (node->uplist) ? (uint32_t)marray_size(*node->uplist) : 0;
699
if (nupvalues > MAX_UPVALUES) REPORT_ERROR(node, "Maximum number of upvalues reached in function %s (max:%d found:%d).",
700
node->identifier, MAX_LOCALS, nupvalues);
701
702
POP_DECLARATION();
703
704
DEBUG_SEMANTIC("MAX LOCALS for function %s: %d", node->identifier, node->nlocals);
705
}
706
707
static void visit_variable_decl (gvisitor_t *self, gnode_variable_decl_t *node) {
708
gnode_t *top = TOP_DECLARATION();
709
symboltable_t *symtable = symtable_from_node(top);
710
size_t count = gnode_array_size(node->decls);
711
gnode_n env = NODE_TAG(top);
712
bool env_is_function = (env == NODE_FUNCTION_DECL);
713
714
// check if optional access and storage specifiers make sense in current context
715
check_access_storage_specifiers(self, (gnode_t *)node, env, node->access, node->storage);
716
717
// loop to check each individual declaration
718
for (size_t i=0; i<count; ++i) {
719
gnode_var_t *p = (gnode_var_t *)gnode_array_get(node->decls, i);
720
DEBUG_SEMANTIC("visit_variable_decl %s", p->identifier);
721
722
// set enclosing environment
723
p->env = top;
724
725
if (env_is_function) {
726
// local variable defined inside a function
727
if (!symboltable_insert(symtable, p->identifier, (void *)p)) REPORT_ERROR(p, "Identifier %s redeclared.", p->identifier);
728
SET_LOCAL_INDEX(p, symtable);
729
DEBUG_SEMANTIC("Local:%s index:%d", p->identifier, p->index);
730
} else if (env == NODE_CLASS_DECL) {
731
// variable defined inside a class => property
732
gnode_class_decl_t *c = (gnode_class_decl_t *)top;
733
734
// compute new ivar index
735
uint32_t n1 = (node->storage == TOK_KEY_STATIC) ? c->nsvar++ : c->nivar++;
736
uint32_t n2 = 0;
737
738
// super class is a static information so I can solve the fragile class problem at compilation time
739
gnode_class_decl_t *super = (gnode_class_decl_t *)c->superclass;
740
while (super) {
741
n2 = (node->storage == TOK_KEY_STATIC) ? super->nsvar : super->nivar;
742
super = (gnode_class_decl_t *)super->superclass;
743
}
744
745
p->index = n1+n2;
746
DEBUG_SEMANTIC("Class: %s property:%s index:%d (static %d)", c->identifier, p->identifier, p->index, (node->storage == TOK_KEY_STATIC));
747
}
748
749
// variable with a initial value (or with a getter/setter)
750
if (p->expr) visit(p->expr);
751
}
752
}
753
754
static void visit_enum_decl (gvisitor_t *self, gnode_enum_decl_t *node) {
755
DEBUG_SEMANTIC("visit_enum_decl %s", node->identifier);
756
757
// check if optional access and storage specifiers make sense in current context
758
gnode_t *top = TOP_DECLARATION();
759
check_access_storage_specifiers(self, (gnode_t *)node, NODE_TAG(top), node->access, node->storage);
760
}
761
762
static void visit_class_decl (gvisitor_t *self, gnode_class_decl_t *node) {
763
DEBUG_SEMANTIC("visit_class_decl %s", node->identifier);
764
765
gnode_t *top = TOP_DECLARATION();
766
767
// check if optional access and storage specifiers make sense in current context
768
check_access_storage_specifiers(self, (gnode_t *)node, NODE_TAG(top), node->access, node->storage);
769
770
// set class enclosing (can be globals, a class or a function)
771
node->env = top;
772
773
// check superclass
774
if (node->superclass) {
775
gnode_identifier_expr_t *id = (gnode_identifier_expr_t *)node->superclass;
776
gnode_t *target = lookup_symtable_id(self, id, true);
777
if (!target) REPORT_ERROR(id, "Unable to find superclass %s for class %s.", id->value, node->identifier);
778
node->superclass = target;
779
gnode_free((gnode_t*)id);
780
}
781
782
// check protocols (disable in this version because protocols are not yet supported)
783
// if (node->protocols) {
784
// gnode_array_each(node->protocols, {
785
// gnode_id_expr_t *id = (gnode_id_expr_t *)val;
786
// gnode_t *target = lookup_symtable_id(self, id, false);
787
// if (!target) continue;
788
// id->symbol = target;
789
// });
790
// }
791
792
PUSH_DECLARATION(node);
793
gnode_array_each(node->decls, {
794
if ((node->superclass) && (ISA_VAR_DECLARATION(val))) {
795
// check for redeclared ivar and if found report a warning
796
check_class_ivar(self, (gnode_class_decl_t *)node, (gnode_variable_decl_t *)val);
797
}
798
visit(val);
799
});
800
POP_DECLARATION();
801
}
802
803
static void visit_module_decl (gvisitor_t *self, gnode_module_decl_t *node) {
804
DEBUG_SEMANTIC("visit_module_decl %s", node->identifier);
805
806
gnode_t *top = TOP_DECLARATION();
807
808
// set and check module enclosing (only in file)
809
node->env = top;
810
if (NODE_TAG(top) != NODE_LIST_STAT) REPORT_ERROR(node, "Module %s cannot be declared here.", node->identifier);
811
812
// check if optional access and storage specifiers make sense in current context
813
check_access_storage_specifiers(self, (gnode_t *)node, NODE_TAG(top), node->access, node->storage);
814
815
PUSH_DECLARATION(node);
816
gnode_array_each(node->decls, {visit(val);});
817
POP_DECLARATION();
818
}
819
820
// MARK: - Expressions -
821
822
static void visit_binary_expr (gvisitor_t *self, gnode_binary_expr_t *node) {
823
DEBUG_SEMANTIC("visit_binary_expr %s", token_name(node->op));
824
825
// sanity check
826
if (!is_expression(node->left)) REPORT_ERROR(node->left, "LValue must be an expression.");
827
if (!is_expression(node->right)) REPORT_ERROR(node->right, "RValue must be an expression.");
828
829
// fill missing symbols
830
visit(node->left);
831
visit(node->right);
832
833
if (!is_expression_valid(node->left)) REPORT_ERROR(node->left, "Invalid left expression.");
834
if (!is_expression_valid(node->right)) REPORT_ERROR(node->right, "Invalid right expression.");
835
836
// sanity check binary expressions
837
if (is_expression_assignment((gnode_t*)node)) check_assignment_expression(self, node);
838
else if (is_expression_range((gnode_t*)node)) check_range_expression(self, node);
839
}
840
841
static void visit_unary_expr (gvisitor_t *self, gnode_unary_expr_t *node) {
842
DEBUG_SEMANTIC("visit_unary_expr %s", token_name(node->op));
843
visit(node->expr);
844
if (!is_expression_valid(node->expr)) REPORT_ERROR(node->expr, "Invalid expression.");
845
}
846
847
static void visit_postfix_expr (gvisitor_t *self, gnode_postfix_expr_t *node) {
848
DEBUG_SEMANTIC("visit_postfix_expr");
849
850
// a postfix expression is an expression that requires an in-context lookup that depends on id
851
// in a statically typed language the loop should check every member of the postfix expression
852
// usign the context of the previous lookup, for example:
853
// a.b.c.d.e
854
// means
855
// lookup a and get its associated symbol table
856
// lookup b in a
857
// lookup c in the context of the previous lookup
858
// lookup d in the context of the previous lookup
859
// and so on in a loop
860
// Gravity is a dynamically typed language so we cannot statically check membership in a statically way
861
// because the lookup context can vary at runtime, for example
862
// class C1 {...}
863
// class C2 {...}
864
// func foo(n) {if (n % 2 == 0) return C1(); else return C2();}
865
// var c = foo(rand()).bar;
866
// should bar be lookup in C1 or in C2?
867
// we really can't know at compile time but only at runtime
868
869
// lookup common part (and generate an error if id cannot be found)
870
// id can be a primary expression
871
visit(node->id);
872
873
// try to obtain symbol table from id (if any)
874
gnode_t *target = NULL;
875
if (ISA(node->id, NODE_IDENTIFIER_EXPR)) {
876
target = ((gnode_identifier_expr_t *)node->id)->symbol;
877
if (ISA(target, NODE_VARIABLE)) target = NULL; // a variable does not contain a symbol table
878
}
879
880
// special enum case on list[0] (it is a static case)
881
if (ISA(target, NODE_ENUM_DECL)) {
882
// check first expression in the list (in case of enum MUST BE an identifier)
883
gnode_postfix_subexpr_t *subnode = (gnode_postfix_subexpr_t *)gnode_array_get(node->list, 0);
884
885
// enum sanity checks
886
gnode_n tag = subnode->base.tag;
887
if (tag != NODE_ACCESS_EXPR) {REPORT_ERROR(node->id, "Invalid enum expression."); return;}
888
if (node->base.is_assignment) {REPORT_ERROR(node, "Assignment not allowed for an enum type."); return;}
889
if (!ISA(subnode->expr, NODE_IDENTIFIER_EXPR)) {REPORT_ERROR(subnode, "Invalid enum expression."); return;}
890
891
// lookup enum value
892
gnode_identifier_expr_t *expr = (gnode_identifier_expr_t *)subnode->expr;
893
const char *value = expr->value;
894
gnode_t *v = lookup_node(target, value);
895
if (!v) {REPORT_ERROR(subnode, "Unable to find %s in enum %s.", value, ((gnode_enum_decl_t *)target)->identifier); return;}
896
897
// node.subnode must be replaced by a literal enum expression (returned by v)
898
size_t n = gnode_array_size(node->list);
899
if (n == 1) {
900
// replace the entire gnode_postfix_expr_t node with v literal value
901
// gnode_replace(node, v); NODE REPLACEMENT FUNCTION TO BE IMPLEMENTED
902
gnode_free(node->id);
903
904
// we need to explicitly free postfix subexpression here
905
gnode_postfix_subexpr_t *subexpr = (gnode_postfix_subexpr_t *)gnode_array_get(node->list, 0);
906
free_postfix_subexpr(subexpr);
907
908
// list cannot be NULL in a postfix expression, we'll use this flag to identify a transformed enum expression
909
gnode_array_free(node->list);
910
node->list = NULL;
911
node->id = gnode_duplicate(v, false);
912
} else {
913
// postfix expression contains more access nodes so just transform current postfix expression
914
// 1. replace id node
915
gnode_free(node->id);
916
node->id = gnode_duplicate(v, false);
917
918
// 2. free first node from node->list
919
gnode_postfix_subexpr_t *subexpr = (gnode_postfix_subexpr_t *)gnode_array_get(node->list, 0);
920
free_postfix_subexpr(subexpr);
921
922
// 3. remove first node from node->list
923
node->list = gnode_array_remove_byindex(node->list, 0);
924
}
925
926
return;
927
}
928
929
// check to avoid infinite loop in init
930
if (ISA(node->id, NODE_IDENTIFIER_EXPR)) {
931
if (is_init_infinite_loop(self, (gnode_identifier_expr_t *)node->id, node->list)) {
932
REPORT_ERROR(node, "Infinite loop detected in init func.");
933
}
934
}
935
936
bool is_super = (NODE_ISA(node->id, NODE_KEYWORD_EXPR) && (((gnode_keyword_expr_t *)node->id)->base.token.type == TOK_KEY_SUPER));
937
bool is_assignment = node->base.is_assignment;
938
939
// process each subnode
940
size_t count = gnode_array_size(node->list);
941
for (size_t i=0; i<count; ++i) {
942
gnode_postfix_subexpr_t *subnode = (gnode_postfix_subexpr_t *)gnode_array_get(node->list, i);
943
944
// identify postfix type: NODE_CALL_EXPR, NODE_ACCESS_EXPR, NODE_SUBSCRIPT_EXPR
945
gnode_n tag = subnode->base.tag;
946
947
// check assignment flag
948
bool is_real_assigment = (is_assignment && (i+1 == count));
949
950
// assignment sanity check
951
if (is_real_assigment) {
952
if (tag == NODE_CALL_EXPR) {REPORT_ERROR((gnode_t *)subnode, "Unable to assign a value to a function call."); return;}
953
if (is_super) {REPORT_ERROR((gnode_t *)subnode, "Unable to explicitly modify super."); return;}
954
}
955
956
// for a function/method call visit each argument
957
if (tag == NODE_CALL_EXPR) {
958
size_t n = gnode_array_size(subnode->args);
959
for (size_t j=0; j<n; ++j) {
960
gnode_t *val = (gnode_t *)gnode_array_get(subnode->args, j);
961
visit(val);
962
}
963
continue;
964
}
965
966
// for a subscript just visit its index expression
967
if (tag == NODE_SUBSCRIPT_EXPR) {
968
if (subnode->expr) visit(subnode->expr);
969
continue;
970
}
971
972
// for a member access check each lookup type (but do not perform a lookup)
973
if (tag == NODE_ACCESS_EXPR) {
974
if (!ISA(subnode->expr, NODE_IDENTIFIER_EXPR)) REPORT_ERROR(subnode->expr, "Invalid access expression.");
975
continue;
976
}
977
978
// should never reach this point
979
DEBUG_SEMANTIC("UNRECOGNIZED POSTFIX OPTIONAL EXPRESSION");
980
assert(0);
981
}
982
}
983
984
static void visit_file_expr (gvisitor_t *self, gnode_file_expr_t *node) {
985
DEBUG_SEMANTIC("visit_file_expr");
986
987
gnode_r *decls = ((semacheck_t *)self->data)->declarations;
988
gnode_t *globals = gnode_array_get(decls, 0);
989
gnode_t *target = globals;
990
size_t n = gnode_array_size(node->identifiers);
991
assert(n);
992
993
// no need to scan the entire list because lookup must be performed at runtime so check just the first element
994
n = 1;
995
for (size_t i=0; i<n; ++i) {
996
const char *identifier = gnode_array_get(node->identifiers, i);
997
DEBUG_SEMANTIC("LOOKUP %s", identifier);
998
999
gnode_t *symbol = lookup_node(target, identifier);
1000
if (!symbol) {REPORT_ERROR(node, "Module identifier %s not found.", identifier); break;}
1001
SET_NODE_LOCATION(node, LOCATION_GLOBAL, 0, 0);
1002
target = symbol;
1003
}
1004
}
1005
1006
static void visit_literal_expr (gvisitor_t *self, gnode_literal_expr_t *node) {
1007
#pragma unused(self, node)
1008
1009
#if GRAVITY_SEMANTIC_DEBUG
1010
char value[256];
1011
gnode_literal_dump(node, value, sizeof(value));
1012
DEBUG_SEMANTIC("visit_literal_expr %s", value);
1013
DEBUG_SEMANTIC("end visit_literal_expr");
1014
#endif
1015
}
1016
1017
static void visit_identifier_expr (gvisitor_t *self, gnode_identifier_expr_t *node) {
1018
DEBUG_SEMANTIC("visit_identifier_expr %s", node->value);
1019
1020
gnode_t *symbol = lookup_identifier(self, node->value, node);
1021
if (!symbol) REPORT_ERROR(node, "Identifier %s not found.", node->value);
1022
}
1023
1024
static void visit_keyword_expr (gvisitor_t *self, gnode_keyword_expr_t *node) {
1025
#pragma unused(self, node)
1026
DEBUG_SEMANTIC("visit_keyword_expr %s", token_name(node->base.token.type));
1027
}
1028
1029
static void visit_list_expr (gvisitor_t *self, gnode_list_expr_t *node) {
1030
size_t n = gnode_array_size(node->list1);
1031
bool ismap = (node->list2 != NULL);
1032
1033
DEBUG_SEMANTIC("visit_list_expr (n: %zu ismap: %d)", n, ismap);
1034
1035
for (size_t j=0; j<n; ++j) {
1036
gnode_t *e = gnode_array_get(node->list1, j);
1037
visit(e);
1038
1039
if (ismap) {
1040
// key must be unique
1041
for (size_t k=0; k<n; ++k) {
1042
if (k == j) continue; // do not check itself
1043
gnode_t *key = gnode_array_get(node->list1, k);
1044
if (gnode_is_equal(e, key)) {
1045
if (gnode_is_literal_string(key)) {
1046
gnode_literal_expr_t *v = (gnode_literal_expr_t *)key;
1047
REPORT_ERROR(key, "Duplicated key %s in map.", v->value.str);
1048
} else REPORT_ERROR(key, "Duplicated key in map.");
1049
}
1050
}
1051
1052
e = gnode_array_get(node->list2, j);
1053
visit(e);
1054
}
1055
}
1056
}
1057
1058
// MARK: -
1059
1060
bool gravity_semacheck2 (gnode_t *node, gravity_delegate_t *delegate) {
1061
semacheck_t data = {.declarations = gnode_array_create(), .lasterror = 0};
1062
marray_init(data.statements);
1063
1064
gvisitor_t visitor = {
1065
.nerr = 0, // used to store number of found errors
1066
.data = (void *)&data, // used to store a pointer to the semantic check struct
1067
.delegate = (void *)delegate, // compiler delegate to report errors
1068
1069
// STATEMENTS: 7
1070
.visit_list_stmt = visit_list_stmt,
1071
.visit_compound_stmt = visit_compound_stmt,
1072
.visit_label_stmt = visit_label_stmt,
1073
.visit_flow_stmt = visit_flow_stmt,
1074
.visit_loop_stmt = visit_loop_stmt,
1075
.visit_jump_stmt = visit_jump_stmt,
1076
.visit_empty_stmt = visit_empty_stmt,
1077
1078
// DECLARATIONS: 5
1079
.visit_function_decl = visit_function_decl,
1080
.visit_variable_decl = visit_variable_decl,
1081
.visit_enum_decl = visit_enum_decl,
1082
.visit_class_decl = visit_class_decl,
1083
.visit_module_decl = visit_module_decl,
1084
1085
// EXPRESSIONS: 8
1086
.visit_binary_expr = visit_binary_expr,
1087
.visit_unary_expr = visit_unary_expr,
1088
.visit_file_expr = visit_file_expr,
1089
.visit_literal_expr = visit_literal_expr,
1090
.visit_identifier_expr = visit_identifier_expr,
1091
.visit_keyword_expr = visit_keyword_expr,
1092
.visit_list_expr = visit_list_expr,
1093
.visit_postfix_expr = visit_postfix_expr,
1094
};
1095
1096
DEBUG_SEMANTIC("=== SEMANTIC CHECK STEP 2 ===");
1097
gvisit(&visitor, node);
1098
DEBUG_SEMANTIC("\n");
1099
1100
marray_destroy(data.statements);
1101
gnode_array_free(data.declarations);
1102
return (visitor.nerr == 0);
1103
}
1104
1105
1106