-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathscope.py
More file actions
executable file
·113 lines (91 loc) · 3.28 KB
/
scope.py
File metadata and controls
executable file
·113 lines (91 loc) · 3.28 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
"""
Scope analysis for Eva compiler
"""
from opcodes import OP_GET_LOCAL, OP_SET_LOCAL, OP_GET_CELL, OP_SET_CELL, OP_GET_GLOBAL, OP_SET_GLOBAL
# Scope types
SCOPE_GLOBAL = 'GLOBAL'
SCOPE_FUNCTION = 'FUNCTION'
SCOPE_BLOCK = 'BLOCK'
SCOPE_CLASS = 'CLASS'
# Allocation types
ALLOC_GLOBAL = 'GLOBAL'
ALLOC_LOCAL = 'LOCAL'
ALLOC_CELL = 'CELL'
def make_scope(scope_type, parent=None):
"""Create a new scope"""
return {
'type': scope_type,
'parent': parent,
'alloc_info': {}, # name -> allocation type
'free': set(), # free variables
'cells': set() # cell variables
}
def scope_add_local(scope, name):
"""Add a local variable to scope"""
alloc_type = ALLOC_GLOBAL if scope['type'] == SCOPE_GLOBAL else ALLOC_LOCAL
scope['alloc_info'][name] = alloc_type
def scope_add_cell(scope, name):
"""Add a cell variable to scope"""
scope['cells'].add(name)
scope['alloc_info'][name] = ALLOC_CELL
def scope_add_free(scope, name):
"""Add a free variable to scope"""
scope['free'].add(name)
scope['alloc_info'][name] = ALLOC_CELL
def scope_resolve(scope, name, alloc_type):
"""Resolve a variable in the scope chain"""
# Found in current scope
if name in scope['alloc_info']:
return scope, alloc_type
# Crossed function boundary - becomes free variable
if scope['type'] == SCOPE_FUNCTION:
alloc_type = ALLOC_CELL
# No parent - error
if scope['parent'] is None:
raise Exception(f"Reference error: {name} is not defined")
# Resolve in global scope as global
if scope['parent']['type'] == SCOPE_GLOBAL:
alloc_type = ALLOC_GLOBAL
return scope_resolve(scope['parent'], name, alloc_type)
def scope_promote(scope, name, owner_scope):
"""Promote a variable from local to cell"""
# Mark as cell in owner scope
scope_add_cell(owner_scope, name)
# Mark as free in all intermediate scopes
current = scope
while current is not owner_scope:
scope_add_free(current, name)
current = current['parent']
def scope_maybe_promote(scope, name):
"""Potentially promote a variable from local to cell"""
init_alloc_type = ALLOC_GLOBAL if scope['type'] == SCOPE_GLOBAL else ALLOC_LOCAL
if name in scope['alloc_info']:
init_alloc_type = scope['alloc_info'][name]
# Already promoted
if init_alloc_type == ALLOC_CELL:
return
# Resolve the variable
owner_scope, alloc_type = scope_resolve(scope, name, init_alloc_type)
# Update allocation type
scope['alloc_info'][name] = alloc_type
# Promote if it's a cell
if alloc_type == ALLOC_CELL:
scope_promote(scope, name, owner_scope)
def scope_get_name_getter(scope, name):
"""Get the opcode for getting a variable"""
alloc_type = scope['alloc_info'][name]
if alloc_type == ALLOC_LOCAL:
return OP_GET_LOCAL
elif alloc_type == ALLOC_CELL:
return OP_GET_CELL
else:
return OP_GET_GLOBAL
def scope_get_name_setter(scope, name):
"""Get the opcode for setting a variable"""
alloc_type = scope['alloc_info'][name]
if alloc_type == ALLOC_LOCAL:
return OP_SET_LOCAL
elif alloc_type == ALLOC_CELL:
return OP_SET_CELL
else:
return OP_SET_GLOBAL