tensor-sensor

Форк
0
332 строки · 10.1 Кб
1
"""
2
MIT License
3

4
Copyright (c) 2021 Terence Parr
5

6
Permission is hereby granted, free of charge, to any person obtaining a copy
7
of this software and associated documentation files (the "Software"), to deal
8
in the Software without restriction, including without limitation the rights
9
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
copies of the Software, and to permit persons to whom the Software is
11
furnished to do so, subject to the following conditions:
12

13
The above copyright notice and this permission notice shall be included in all
14
copies or substantial portions of the Software.
15

16
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
SOFTWARE.
23
"""
24
import tsensor
25

26
# Parse tree definitions
27
# I found package ast in python3 lib after I built this. whoops. No biggie.
28
# This tree structure is easier to visit for my purposes here. Also lets me
29
# control the kinds of statements I process.
30

31
class ParseTreeNode:
32
    def __init__(self, parser):
33
        self.parser = parser # which parser object created this node;
34
                             # useful for getting access to the code string from a token
35
        self.value = None # used during evaluation
36
        self.start = None # start token
37
        self.stop = None  # end token
38
    def eval(self, frame):
39
        """
40
        Evaluate the expression represented by this (sub)tree in context of frame.
41
        Try any exception found while evaluating and remember which operation that
42
        was in this tree
43
        """
44
        try:
45
            self.value = eval(str(self), frame.f_globals, frame.f_locals)
46
        except BaseException as e:
47
            raise IncrEvalTrap(self) from e
48
        # print(self, "=>", self.value)
49
        return self.value
50
    @property
51
    def optokens(self): # the associated token if atom or representative token if operation
52
        return None
53
    @property
54
    def kids(self):
55
        return []
56
    def clarify(self):
57
        return None
58
    def __str__(self):
59
        # Extract text from the original code string using token character indexes
60
        return self.parser.code[self.start.cstart_idx:self.stop.cstop_idx]
61
    def __repr__(self):
62
        fields = self.__dict__.copy()
63
        kill = ['start', 'stop', 'lbrack', 'lparen', 'parser']
64
        for name in kill:
65
            if name in fields: del fields[name]
66
        args = [
67
            v + '=' + fields[v].__repr__()
68
            for v in fields
69
            if v != 'value' or fields['value'] is not None
70
        ]
71
        args = ','.join(args)
72
        return f"{self.__class__.__name__}({args})"
73

74
class Assign(ParseTreeNode):
75
    def __init__(self, parser, op, lhs, rhs, start, stop):
76
        super().__init__(parser)
77
        self.op, self.lhs, self.rhs = op, lhs, rhs
78
        self.start, self.stop = start, stop
79
    def eval(self, frame):
80
        self.value = self.rhs.eval(frame)
81
        # Don't eval this node as it causes side effect of making actual assignment to lhs
82
        self.lhs.value = self.value
83
        return self.value
84
    @property
85
    def optokens(self):
86
        return [self.op]
87
    @property
88
    def kids(self):
89
        return [self.lhs, self.rhs]
90

91

92
class Call(ParseTreeNode):
93
    def __init__(self, parser, func, lparen, args, start, stop):
94
        super().__init__(parser)
95
        self.func = func
96
        self.lparen = lparen
97
        self.args = args
98
        self.start, self.stop = start, stop
99
    def eval(self, frame):
100
        self.func.eval(frame)
101
        for a in self.args:
102
            a.eval(frame)
103
        return super().eval(frame)
104
    def clarify(self):
105
        arg_msgs = []
106
        for a in self.args:
107
            ashape = tsensor.analysis._shape(a.value)
108
            if ashape:
109
                arg_msgs.append(f"arg {a} w/shape {ashape}")
110
        if len(arg_msgs)==0:
111
            return f"Cause: {self}"
112
        return f"Cause: {self} tensor " + ', '.join(arg_msgs)
113
    @property
114
    def optokens(self):
115
        f = None # assume complicated like a[i](args) with weird func expr
116
        if isinstance(self.func, Member):
117
            f = self.func.member
118
        elif isinstance(self.func, Atom):
119
            f = self.func
120
        if f:
121
            return [f.token,self.lparen,self.stop]
122
        return [self.lparen,self.stop]
123
    @property
124
    def kids(self):
125
        return [self.func]+self.args
126

127

128
class Return(ParseTreeNode):
129
    def __init__(self, parser, result, start, stop):
130
        super().__init__(parser)
131
        self.result = result
132
        self.start, self.stop = start, stop
133
    def eval(self, frame):
134
        self.value = [a.eval(frame) for a in self.result]
135
        if len(self.value)==1:
136
            self.value = self.value[0]
137
        return self.value
138
    @property
139
    def optokens(self):
140
        return [self.start]
141
    @property
142
    def kids(self):
143
        return self.result
144

145

146
class Index(ParseTreeNode):
147
    def __init__(self, parser, arr, lbrack, index, start, stop):
148
        super().__init__(parser)
149
        self.arr = arr
150
        self.lbrack = lbrack
151
        self.index = index
152
        self.start, self.stop = start, stop
153
    def eval(self, frame):
154
        self.arr.eval(frame)
155
        for i in self.index:
156
            i.eval(frame)
157
        return super().eval(frame)
158
    @property
159
    def optokens(self):
160
        return [self.lbrack,self.stop]
161
    @property
162
    def kids(self):
163
        return [self.arr] + self.index
164

165

166
class Member(ParseTreeNode):
167
    def __init__(self, parser, op, obj, member, start, stop):
168
        super().__init__(parser)
169
        self.op = op # always DOT
170
        self.obj = obj
171
        self.member = member
172
        self.start, self.stop = start, stop
173
    def eval(self, frame):
174
        self.obj.eval(frame)
175
        # don't eval member as it's just a name to look up in obj
176
        return super().eval(frame)
177
    @property
178
    def optokens(self): # the associated token if atom or representative token if operation
179
        return [self.op]
180
    @property
181
    def kids(self):
182
        return [self.obj, self.member]
183

184

185
class BinaryOp(ParseTreeNode):
186
    def __init__(self, parser, op, lhs, rhs, start, stop):
187
        super().__init__(parser)
188
        self.op, self.lhs, self.rhs = op, lhs, rhs
189
        self.start, self.stop = start, stop
190
    def eval(self, frame):
191
        self.lhs.eval(frame)
192
        self.rhs.eval(frame)
193
        return super().eval(frame)
194
    def clarify(self):
195
        opnd_msgs = []
196
        lshape = tsensor.analysis._shape(self.lhs.value)
197
        rshape = tsensor.analysis._shape(self.rhs.value)
198
        if lshape:
199
            opnd_msgs.append(f"operand {self.lhs} w/shape {lshape}")
200
        if rshape:
201
            opnd_msgs.append(f"operand {self.rhs} w/shape {rshape}")
202
        return f"Cause: {self.op} on tensor " + ' and '.join(opnd_msgs)
203
    @property
204
    def optokens(self): # the associated token if atom or representative token if operation
205
        return [self.op]
206
    @property
207
    def kids(self):
208
        return [self.lhs, self.rhs]
209

210

211
class UnaryOp(ParseTreeNode):
212
    def __init__(self, parser, op, opnd, start, stop):
213
        super().__init__(parser)
214
        self.op = op
215
        self.opnd = opnd
216
        self.start, self.stop = start, stop
217
    def eval(self, frame):
218
        self.opnd.eval(frame)
219
        return super().eval(frame)
220
    @property
221
    def optokens(self):
222
        return [self.op]
223
    @property
224
    def kids(self):
225
        return [self.opnd]
226

227

228
class ListLiteral(ParseTreeNode):
229
    def __init__(self, parser, elems, start, stop):
230
        super().__init__(parser)
231
        self.elems = elems
232
        self.start, self.stop = start, stop
233
    def eval(self, frame):
234
        for i in self.elems:
235
            i.eval(frame)
236
        return super().eval(frame)
237
    @property
238
    def kids(self):
239
        return self.elems
240

241

242
class TupleLiteral(ParseTreeNode):
243
    def __init__(self, parser, elems, start, stop):
244
        super().__init__(parser)
245
        self.elems = elems
246
        self.start, self.stop = start, stop
247
    def eval(self, frame):
248
        for i in self.elems:
249
            i.eval(frame)
250
        return super().eval(frame)
251
    @property
252
    def kids(self):
253
        return self.elems
254

255

256
class SubExpr(ParseTreeNode):
257
    # record parens for later display to keep precedence
258
    def __init__(self, parser, e, start, stop):
259
        super().__init__(parser)
260
        self.e = e
261
        self.start, self.stop = start, stop
262
    def eval(self, frame):
263
        self.value = self.e.eval(frame)
264
        return self.value # don't re-evaluate
265
    @property
266
    def optokens(self):
267
        return [self.start, self.stop]
268
    @property
269
    def kids(self):
270
        return [self.e]
271

272

273
class Atom(ParseTreeNode):
274
    def __init__(self, parser, token):
275
        super().__init__(parser)
276
        self.token = token
277
        self.start, self.stop = token, token
278
    def eval(self, frame):
279
        if self.token.type == tsensor.parsing.COLON:
280
            return ':' # fake a value here
281
        return super().eval(frame)
282
    def __repr__(self):
283
        # v = f"{{{self.value}}}" if hasattr(self,'value') and self.value is not None else ""
284
        return self.token.value
285

286

287
def postorder(t):
288
    nodes = []
289
    _postorder(t, nodes)
290
    return nodes
291

292

293
def _postorder(t, nodes):
294
    if t is None:
295
        return
296
    for sub in t.kids:
297
        _postorder(sub, nodes)
298
    nodes.append(t)
299

300

301
def leaves(t):
302
    nodes = []
303
    _leaves(t, nodes)
304
    return nodes
305

306

307
def _leaves(t, nodes):
308
    if t is None:
309
        return
310
    if len(t.kids) == 0:
311
        nodes.append(t)
312
        return
313
    for sub in t.kids:
314
        _leaves(sub, nodes)
315

316

317
def walk(t, pre=lambda x: None, post=lambda x: None):
318
    if t is None:
319
        return
320
    pre(t)
321
    for sub in t.kids:
322
        walk(sub, pre, post)
323
    post(t)
324

325

326
class IncrEvalTrap(BaseException):
327
    """
328
    Used during re-evaluation of python line that threw exception to trap which
329
    subexpression caused the problem.
330
    """
331
    def __init__(self, offending_expr):
332
        self.offending_expr = offending_expr # where in tree did we get exception?
333

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.