summaryrefslogtreecommitdiff
path: root/scope.c
blob: cae71075aa11d550c146be2221c474f587c48fb0 (plain)
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
enum {
	  SCOPE_CHECK_REDECL = 0x01,
};


static void val_free(Value *v, Type *t);

static bool add_ident_decls(Block *b, Declaration *d, U16 flags) {
	bool ret = true;
	arr_foreach(d->idents, Identifier, ident) {
		IdentDecl *decls = (*ident)->decls;
		if ((flags & SCOPE_CHECK_REDECL) && arr_len(decls)) {
			/* check that it hasn't been declared in this block */
			IdentDecl *prev = arr_last(decls);
			if (prev->scope == b) {
				err_print(d->where, "Re-declaration of identifier in the same block.");
				info_print(prev->decl->where, "Previous declaration was here.");
				ret = false;
				continue;
			}
		}
		ident_add_decl(*ident, d, b);
	}
	return ret;
}

static void remove_ident_decls(Block *b, Declaration *d) {
	U64 i = 0;
	bool is_tuple = d->type.kind == TYPE_TUPLE;
	arr_foreach(d->idents, Identifier, ident) {
		IdentTree *id_info = *ident;
		IdentDecl **decls = &id_info->decls;
		IdentDecl *last_decl = arr_last(*decls);
		if (last_decl && last_decl->scope == b) {
			if ((last_decl->flags & IDECL_HAS_VAL)
				/* don't free const vals (there's only one per decl) */
				&& !(last_decl->decl->flags & DECL_IS_CONST)) {
				val_free(&last_decl->val, is_tuple ? &d->type.tuple[i++] : &d->type);
			}
			arr_remove_last(decls); /* remove that declaration */
		}
	}
}

/* pass NULL for block for global scope */
static bool block_enter(Block *b, Statement *stmts, U16 flags) {
	bool ret = true;
	arr_foreach(stmts, Statement, stmt) {
		if (stmt->kind == STMT_DECL) {
			Declaration *decl = &stmt->decl;
			if (!add_ident_decls(b, decl, flags))
				ret = false;
		}
	}
	return ret;
}

static void block_exit(Block *b, Statement *stmts) {
	arr_foreach(stmts, Statement, stmt) {
		if (stmt->kind == STMT_DECL) {
			Declaration *decl = &stmt->decl;
			remove_ident_decls(b, decl);
		}
	}
}

/* does NOT enter function's block body */
static bool fn_enter(FnExpr *f, U16 flags) {
	arr_foreach(f->params, Declaration, decl)
		if (!add_ident_decls(&f->body, decl, flags))
			return false;
	arr_foreach(f->ret_decls, Declaration, decl)
		if (!add_ident_decls(&f->body, decl, flags))
			return false;
	return true;
}

static void fn_exit(FnExpr *f) {
	arr_foreach(f->params, Declaration, decl)
		remove_ident_decls(&f->body, decl);
	arr_foreach(f->ret_decls, Declaration, decl)
		remove_ident_decls(&f->body, decl);
}

static bool each_enter(Expression *e) {
	assert(e->kind == EXPR_EACH);
	EachExpr *ea = &e->each;
    if (ea->index && ea->index == ea->value) {
		err_print(e->where, "The identifier for the index of an each loop must be different from the identifier for the value.");
		return false;
	}
	if (ea->index) {
		IdentDecl *id = arr_add(&ea->index->decls);
		id->flags = 0;
		id->kind = IDECL_EXPR;
		id->scope = &ea->body;
		id->expr = e;
	}
	if (ea->value) {
		IdentDecl *id = arr_add(&ea->value->decls);
		id->flags = 0;
		id->kind = IDECL_EXPR;
		id->scope = &ea->body;
		id->expr = e;
	}
	return true;
}

static void each_exit(Expression *e) {
	assert(e->kind == EXPR_EACH);
}