Skip to content

Commit

Permalink
py/scope: Factor common code to find locals and close over them.
Browse files Browse the repository at this point in the history
Saves 50-100 bytes of code.
  • Loading branch information
dpgeorge committed Sep 30, 2016
1 parent d549596 commit 0d10517
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 39 deletions.
15 changes: 6 additions & 9 deletions py/compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -1172,17 +1172,14 @@ STATIC void compile_global_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) {
STATIC void compile_declare_nonlocal(compiler_t *comp, mp_parse_node_t pn, qstr qst) {
bool added;
id_info_t *id_info = scope_find_or_add_id(comp->scope_cur, qst, &added);
if (!added && id_info->kind != ID_INFO_KIND_FREE) {
if (added) {
scope_find_local_and_close_over(comp->scope_cur, id_info, qst);
if (id_info->kind == ID_INFO_KIND_GLOBAL_IMPLICIT) {
compile_syntax_error(comp, pn, "no binding for nonlocal found");
}
} else if (id_info->kind != ID_INFO_KIND_FREE) {
compile_syntax_error(comp, pn, "identifier redefined as nonlocal");
return;
}
id_info_t *id_info2 = scope_find_local_in_parent(comp->scope_cur, qst);
if (id_info2 == NULL || !(id_info2->kind == ID_INFO_KIND_LOCAL || id_info2->kind == ID_INFO_KIND_CELL || id_info2->kind == ID_INFO_KIND_FREE)) {
compile_syntax_error(comp, pn, "no binding for nonlocal found");
return;
}
id_info->kind = ID_INFO_KIND_FREE;
scope_close_over_in_parents(comp->scope_cur, qst);
}

STATIC void compile_nonlocal_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) {
Expand Down
8 changes: 1 addition & 7 deletions py/emitcommon.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,7 @@ void mp_emit_common_get_id_for_load(scope_t *scope, qstr qst) {
bool added;
id_info_t *id = scope_find_or_add_id(scope, qst, &added);
if (added) {
id_info_t *id2 = scope_find_local_in_parent(scope, qst);
if (id2 != NULL && (id2->kind == ID_INFO_KIND_LOCAL || id2->kind == ID_INFO_KIND_CELL || id2->kind == ID_INFO_KIND_FREE)) {
id->kind = ID_INFO_KIND_FREE;
scope_close_over_in_parents(scope, qst);
} else {
id->kind = ID_INFO_KIND_GLOBAL_IMPLICIT;
}
scope_find_local_and_close_over(scope, id, qst);
}
}

Expand Down
48 changes: 27 additions & 21 deletions py/scope.c
Original file line number Diff line number Diff line change
Expand Up @@ -106,39 +106,45 @@ id_info_t *scope_find_global(scope_t *scope, qstr qst) {
return scope_find(scope, qst);
}

id_info_t *scope_find_local_in_parent(scope_t *scope, qstr qst) {
if (scope->parent == NULL) {
return NULL;
}
for (scope_t *s = scope->parent; s->parent != NULL; s = s->parent) {
id_info_t *id = scope_find(s, qst);
if (id != NULL) {
return id;
}
}
return NULL;
}

void scope_close_over_in_parents(scope_t *scope, qstr qst) {
STATIC void scope_close_over_in_parents(scope_t *scope, qstr qst) {
assert(scope->parent != NULL); // we should have at least 1 parent
for (scope_t *s = scope->parent; s->parent != NULL; s = s->parent) {
for (scope_t *s = scope->parent;; s = s->parent) {
assert(s->parent != NULL); // we should not get to the outer scope
bool added;
id_info_t *id = scope_find_or_add_id(s, qst, &added);
if (added) {
// variable not previously declared in this scope, so declare it as free and keep searching parents
id->kind = ID_INFO_KIND_FREE;
} else {
// variable is declared in this scope, so finish
switch (id->kind) {
case ID_INFO_KIND_LOCAL: id->kind = ID_INFO_KIND_CELL; break; // variable local to this scope, close it over
case ID_INFO_KIND_FREE: break; // variable already closed over in a parent scope
case ID_INFO_KIND_CELL: break; // variable already closed over in this scope
default: assert(0); // TODO
if (id->kind == ID_INFO_KIND_LOCAL) {
// variable local to this scope, close it over
id->kind = ID_INFO_KIND_CELL;
} else {
// ID_INFO_KIND_FREE: variable already closed over in a parent scope
// ID_INFO_KIND_CELL: variable already closed over in this scope
assert(id->kind == ID_INFO_KIND_FREE || id->kind == ID_INFO_KIND_CELL);
}
return;
}
}
assert(0); // we should have found the variable in one of the parents
}

void scope_find_local_and_close_over(scope_t *scope, id_info_t *id, qstr qst) {
if (scope->parent != NULL) {
for (scope_t *s = scope->parent; s->parent != NULL; s = s->parent) {
id_info_t *id2 = scope_find(s, qst);
if (id2 != NULL) {
if (id2->kind == ID_INFO_KIND_LOCAL || id2->kind == ID_INFO_KIND_CELL || id2->kind == ID_INFO_KIND_FREE) {
id->kind = ID_INFO_KIND_FREE;
scope_close_over_in_parents(scope, qst);
return;
}
break;
}
}
}
id->kind = ID_INFO_KIND_GLOBAL_IMPLICIT;
}

#endif // MICROPY_ENABLE_COMPILER
3 changes: 1 addition & 2 deletions py/scope.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,6 @@ void scope_free(scope_t *scope);
id_info_t *scope_find_or_add_id(scope_t *scope, qstr qstr, bool *added);
id_info_t *scope_find(scope_t *scope, qstr qstr);
id_info_t *scope_find_global(scope_t *scope, qstr qstr);
id_info_t *scope_find_local_in_parent(scope_t *scope, qstr qstr);
void scope_close_over_in_parents(scope_t *scope, qstr qstr);
void scope_find_local_and_close_over(scope_t *scope, id_info_t *id, qstr qst);

#endif // __MICROPY_INCLUDED_PY_SCOPE_H__

0 comments on commit 0d10517

Please sign in to comment.