-
Notifications
You must be signed in to change notification settings - Fork 13
/
Copy pathpybc_emit.py
189 lines (156 loc) · 6.31 KB
/
pybc_emit.py
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
import ast
import dis
import sys
import typing
from typing import NamedTuple
from astpretty import pprint
import yapypy.extended_python.extended_ast as ex_ast
from yapypy.extended_python.symbol_analyzer import SymTable, Tag, to_tagged_ast, ContextType
from yapypy.utils.namedlist import INamedList, as_namedlist, trait
from yapypy.utils.instrs import *
from Redy.Magic.Pattern import Pattern
from bytecode import *
from bytecode.concrete import FreeVar, CellVar, Compare
from bytecode.flags import CompilerFlags
from enum import Enum, auto as _auto
class BlockType(Enum):
LOOP = _auto()
EXCEPT = _auto()
FINALLY_TRY = _auto()
FINALLY_END = _auto()
class IndexedAnalyzedSymTable(NamedTuple):
bounds: list
freevars: list
cellvars: list
@classmethod
def from_raw(cls, tb):
return cls(*[list(each) for each in tb.analyzed])
class Context(INamedList, metaclass=trait(as_namedlist)):
bc: Bytecode
sym_tb: IndexedAnalyzedSymTable
parent: 'Context'
current_block_stack: list
cts: typing.FrozenSet[ContextType]
def enter_new(self, tag_table: SymTable):
sym_tb = IndexedAnalyzedSymTable.from_raw(tag_table)
bc = Bytecode()
try:
bc.filename = self.bc.filename
except IndexError:
bc.filename = ""
cts = tag_table.cts
has_annotation = ContextType.Annotation in cts
under_class_def_or_module = ContextType.ClassDef in cts or ContextType.Module in cts
if has_annotation and under_class_def_or_module:
bc.append(SETUP_ANNOTATIONS())
if not under_class_def_or_module:
bc.flags |= CompilerFlags.OPTIMIZED
bc.flags |= CompilerFlags.NEWLOCALS
if ContextType.Module in self.cts:
bc.flags |= CompilerFlags.NESTED
if ContextType.Coroutine in cts:
if ContextType.Generator in cts:
bc.flags |= CompilerFlags.ASYNC_GENERATOR
else:
bc.flags |= CompilerFlags.COROUTINE
elif ContextType.Generator in cts:
bc.flags |= CompilerFlags.GENERATOR
# not elif for further designing(async lambda)
freevars = sym_tb.freevars
cellvars = sym_tb.cellvars
if ContextType.Module not in cts and ContextType.ClassDef in self.cts and '__class__' not in freevars:
freevars.append('__class__')
if freevars:
bc.freevars.extend(freevars)
else:
bc.flags |= CompilerFlags.NOFREE
if ContextType.ClassDef in cts and '__class__' not in cellvars:
cellvars.append('__class__')
bc.cellvars.extend(cellvars)
return Context(
parent=self,
bc=bc,
sym_tb=sym_tb,
current_block_stack=[],
cts=frozenset(cts),
)
def load_name(self, name, lineno=None):
sym_tb = self.sym_tb
under_class = ContextType.ClassDef in self.cts
if name in sym_tb.cellvars:
self.bc.append(
Instr(
'LOAD_CLASSDEREF' if under_class else 'LOAD_DEREF',
CellVar(name),
lineno=lineno))
elif name in sym_tb.freevars:
self.bc.append(
Instr(
'LOAD_CLASSDEREF' if under_class else 'LOAD_DEREF',
FreeVar(name),
lineno=lineno))
elif name in sym_tb.bounds:
self.bc.append(
Instr('LOAD_NAME' if under_class else 'LOAD_FAST', name, lineno=lineno))
else:
self.bc.append(Instr("LOAD_GLOBAL", name, lineno=lineno))
def del_name(self, name, lineno=None):
sym_tb = self.sym_tb
if name in sym_tb.cellvars:
# TODO: no DELETE_CLASSDEREF?
self.bc.append(Instr('DELETE_DEREF', CellVar(name), lineno=lineno))
elif name in sym_tb.freevars:
self.bc.append(Instr('DELETE_DEREF', FreeVar(name), lineno=lineno))
elif name in sym_tb.bounds:
under_class = ContextType.ClassDef in self.cts
self.bc.append(
Instr(
'DELETE_NAME' if under_class else 'DELETE_FAST', name, lineno=lineno))
else:
self.bc.append(Instr("DELETE_GLOBAL", name, lineno=lineno))
def store_name(self, name, lineno=None):
sym_tb = self.sym_tb
if name in sym_tb.cellvars:
self.bc.append(Instr('STORE_DEREF', CellVar(name), lineno=lineno))
elif name in sym_tb.freevars:
self.bc.append(Instr('STORE_DEREF', FreeVar(name), lineno=lineno))
elif name in sym_tb.bounds:
under_class = ContextType.ClassDef in self.cts
self.bc.append(
Instr('STORE_NAME' if under_class else 'STORE_FAST', name, lineno=lineno))
else:
self.bc.append(Instr("STORE_GLOBAL", name, lineno=lineno))
def load_closure(self, lineno=None):
parent = self.parent
freevars = self.sym_tb.freevars
cellvars = parent.sym_tb.cellvars
for each in freevars:
if each in cellvars:
parent.bc.append(Instr('LOAD_CLOSURE', CellVar(each), lineno=lineno))
else:
assert each in freevars
parent.bc.append(Instr('LOAD_CLOSURE', FreeVar(each), lineno=lineno))
parent.bc.append(Instr('BUILD_TUPLE', len(freevars)))
def push_current_block(self, blktype: BlockType, label: Label = None):
item = (blktype, label)
self.current_block_stack.append(item)
def pop_current_block(self, blktype: BlockType = None, lineno=None):
if blktype is not None:
_blktype, _ = self.current_block_stack[-1]
if _blktype != blktype:
exc = SystemError()
exc.lineno = lineno
exc.msg = "pop block type is not expect, want %s but get %s" % (blktype,
_blktype)
raise exc
return self.current_block_stack.pop()
def get_current_block(self):
return self.current_block_stack[-1]
def get_block_stack(self):
return self.current_block_stack
@property
def is_global(self):
return ContextType.Module in self.cts
@Pattern
def py_emit(node: ast.AST, ctx: Context):
return type(node)