-
Notifications
You must be signed in to change notification settings - Fork 0
/
memo.py
160 lines (137 loc) · 4.77 KB
/
memo.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
import sexpdata
import io
from show import show
from sqlio import Exp, Props
class cls(object):
"""An object that represents an expression class.
A class has:
- a set of properties.
- zero or more m-expressions: structural representations
of the expressions, or in other words a "strategy"
to compute the results defined by the class.
This implementation is intended for use together with sqlio.Props
for properties. Any m-expression and property type that can render
as a S-expression (via sexpdata.dumps) can be used.
Examples:
>>> cls(Exp('+'))
<cls (+) ()>
>>> cls(Exp('+', [1, 2]), {'a': 1, 'b': 2})
<cls (+ 1 2) (:a 1 :b 2)>
"""
def __init__(self, mexpr, props=None):
if props is None:
props = Props()
if isinstance(props, dict):
props = Props(props)
self.props = props
self.mexprs = [mexpr]
def __repr__(self):
return "<cls %-40s %s>" % (' '.join(sexpdata.dumps(e) for e in self.mexprs),
sexpdata.dumps(self.props))
class memo(object):
"""An object that represents a memo.
A memo is an array of classes, with a "root" index that points to
the class at the top of the expression tree.
Example:
>>> m = memo()
>>> idx1 = m.newcls(Exp('+'), {'a': 123})
>>> idx2 = m.newcls(Exp('-', [idx1]), {'b': 456})
>>> m.root = idx2
>>> print(m)
<memo
root: 1
0 <cls (+) (:a 123)>
1 <cls (- 0) (:b 456)>
>
"""
def __init__(self):
self.root = None
self.classes = []
def __getitem__(self, idx):
"""A memo supports the m[idx] notation."""
return self.classes[idx]
def newcls(self, item, props):
self.classes.append(cls(item, props))
return len(self.classes)-1
def __repr__(self):
return memo_as_string(self)
def memo_as_string(m):
"""Render a memo as a string."""
s = io.StringIO()
print("<memo\nroot:", m.root, file=s)
for i, c in enumerate(m.classes):
print("%2d"%i, c, file=s)
print(">", file=s, end='')
return s.getvalue()
import sys
def print_tree(m):
"""Show a memo as an expression tree.
This displays SQL relational operators as a tree,
and inlines scalar expressions together.
The following operators are recognized as relational:
project, filter, scan, cross.
For example:
>>> m = memo()
>>> a = m.newcls(Exp('lit', [123]), {})
>>> b = m.newcls(Exp('lit', [456]), {})
>>> apb = m.newcls(Exp('+', [a, b]), {})
>>> s = m.newcls(Exp('scan', ['kv']), {'foo': 'bar'})
>>> f = m.newcls(Exp('filter', [s, apb]), {'hello':'world'})
>>> m.root = f
>>> print_tree(m)
( 4) filter
props:
:hello "world"
filter (+ 123 456)
<BLANKLINE>
( 3) scan
props:
:foo "bar"
table kv
<BLANKLINE>
"""
_printtree(0, m.root, m, sys.stdout)
# Helper function for print_tree().
def _printtree(indent, idx, m, buf):
prefix = indent*' '
e = m[idx].mexprs[0]
rest = io.StringIO()
print('%s(%2d) %s' % (prefix, idx, e.op), file=buf)
print('%s props:' % prefix, file=buf)
for k, v in m[idx].props.items():
print('%s :%s %s' % (prefix, k, sexpdata.dumps(v)), file=buf)
if e.op == 'project':
print('%s exprs' % prefix, ', '.join((_printscalar(indent, i, m, rest) for i in m[idx].props.cols)), file=buf)
elif e.op == 'filter':
print('%s filter' % prefix, _printscalar(indent, e.args[1], m, rest), file=buf)
elif e.op == 'scan':
print("%s table" % prefix, e.args[0])
print(file=buf)
if e.op in ['project', 'filter']:
_printtree(indent+4, e.args[0], m, buf)
elif e.op in ['cross']:
for e in e.args:
_printtree(indent+4, e, m, buf)
rest = rest.getvalue()
if len(rest) > 0:
print('%s----' % prefix,file=buf)
print(rest,file=buf)
# Helper function for print_tree().
def _printscalar(indent, i, m, buf):
def ps(exp):
if exp.op == 'lit':
return sexpdata.tosexp(exp.args[0])
elif exp.op == 'var':
return '(@%d %s)' % (i, exp.args[0])
elif exp.op in ['apply', 'exists']:
_printtree(indent, exp.args[0], m, buf)
return sexpdata.tosexp(exp)
else:
return '(%s %s)' % (exp.op,
' '.join(_printscalar(indent, i, m, buf) for i in exp.args))
return ps(m[i].mexprs[0])
if __name__ == "__main__":
print("testing...")
import doctest
doctest.testmod()
print("testing done")