1 | #!/usr/bin/env python2
|
2 | """
|
3 | test_cast.py
|
4 | """
|
5 | from __future__ import print_function
|
6 |
|
7 | import os
|
8 | from typing import Tuple, cast
|
9 |
|
10 | from mycpp import mylib
|
11 | from mycpp.mylib import log, tagswitch
|
12 |
|
13 |
|
14 | class ColorOutput(object):
|
15 | """Abstract base class for plain text, ANSI color, and HTML color."""
|
16 |
|
17 | def __init__(self, f):
|
18 | # type: (mylib.Writer) -> None
|
19 | self.f = f
|
20 | self.num_chars = 0
|
21 |
|
22 | def WriteRaw(self, raw):
|
23 | # type: (Tuple[str, int]) -> None
|
24 | """
|
25 | Write raw data without escaping, and without counting control codes in the
|
26 | length.
|
27 | """
|
28 | s, num_chars = raw
|
29 | self.f.write(s)
|
30 | self.num_chars += num_chars
|
31 |
|
32 | def GetRaw(self):
|
33 | # type: () -> Tuple[str, int]
|
34 |
|
35 | # NOTE: Ensured by NewTempBuffer()
|
36 | f = cast(mylib.BufWriter, self.f)
|
37 | return f.getvalue(), self.num_chars
|
38 |
|
39 |
|
40 | def TestCastBufWriter():
|
41 | # type: () -> None
|
42 | """For debugging a problem with StackRoots generation"""
|
43 |
|
44 | f = mylib.BufWriter()
|
45 | out = ColorOutput(f)
|
46 | out.WriteRaw(('yo', 2))
|
47 | s, num_chars = out.GetRaw()
|
48 | print(s)
|
49 |
|
50 |
|
51 | class value_t:
|
52 |
|
53 | def __init__(self):
|
54 | # type: () -> None
|
55 | pass
|
56 |
|
57 | def tag(self):
|
58 | # type: () -> int
|
59 | raise NotImplementedError()
|
60 |
|
61 |
|
62 | class value__Int(value_t):
|
63 |
|
64 | def __init__(self, i):
|
65 | # type: (int) -> None
|
66 | self.i = i
|
67 |
|
68 | def tag(self):
|
69 | # type: () -> int
|
70 | return 1
|
71 |
|
72 |
|
73 | class value__Eggex(value_t):
|
74 |
|
75 | def __init__(self, ere):
|
76 | # type: (str) -> None
|
77 | self.ere = ere
|
78 |
|
79 | def tag(self):
|
80 | # type: () -> int
|
81 | return 2
|
82 |
|
83 |
|
84 | def TestSwitchDowncast(val):
|
85 | # type: (value_t) -> None
|
86 | """
|
87 | The common val -> UP_val -> val pattern
|
88 | """
|
89 | UP_val = val
|
90 | with tagswitch(val) as case:
|
91 | if case(1):
|
92 | val = cast(value__Int, UP_val)
|
93 | print('Int = %d' % val.i)
|
94 | elif case(2):
|
95 | val = cast(value__Eggex, UP_val)
|
96 | print('Eggex = %r' % val.ere)
|
97 | else:
|
98 | print('other')
|
99 |
|
100 |
|
101 | def TestSwitchDowncastBad(val):
|
102 | # type: (value_t) -> None
|
103 | """
|
104 | TODO: Could we insert the UP_val automatically?
|
105 |
|
106 | Possible rules:
|
107 |
|
108 | (1) with tagswitch(cell.val) translates to
|
109 |
|
110 | value_t* UP_val = cell->val;
|
111 | switch (val) {
|
112 |
|
113 | (2) You need MyPy casts sometimes
|
114 |
|
115 | unrelated = None
|
116 | with tagswitch(val) as case:
|
117 | if case(value_e.Int):
|
118 | val = cast(value.Int, val)
|
119 | print('i = %d' % val.i)
|
120 |
|
121 | elif case(value_e.Str):
|
122 | unrelated = cast(str, obj)
|
123 | print('String')
|
124 |
|
125 | (3) Then the C++ casts would look like:
|
126 |
|
127 | value_t* UP_val = cell->val;
|
128 | switch (val) {
|
129 | case value_e::Int {
|
130 | # How do we know to generate a NEW var here, without the UP_val
|
131 | # heuristic?
|
132 | #
|
133 | # OK well we can simply use the switch variable name? It it
|
134 | # matches, we create a new var.
|
135 | #
|
136 | # Technical problem: it's INSIDE a block, so we have to "look
|
137 | # ahead" to the first thing in the block.
|
138 |
|
139 | value::Int* val = static_cast<value::Int*>(val);
|
140 | }
|
141 | case value_e::Str {
|
142 | // NOT a new variable
|
143 | unrelated = static_cast<Str*>(obj);
|
144 | }
|
145 | }
|
146 | """
|
147 |
|
148 | #UP_val = val
|
149 | with tagswitch(val) as case:
|
150 | if case(1):
|
151 | val = cast(value__Int, val)
|
152 | print('Int')
|
153 | #print('Int = %d' % val.i)
|
154 | elif case(2):
|
155 | val = cast(value__Eggex, val)
|
156 | print('Eggex')
|
157 | # If we enable this, then it fails to compile
|
158 | #print('Eggex = %r' % val.ere)
|
159 | else:
|
160 | print('other')
|
161 |
|
162 |
|
163 | def TestCastInSwitch():
|
164 | # type: () -> None
|
165 |
|
166 | # Inspired by HasAffix()
|
167 |
|
168 | e = value__Eggex('[0-9]+')
|
169 |
|
170 | pattern_val = e # type: value_t
|
171 |
|
172 | pattern_eggex = None # type: value__Eggex
|
173 | i = 42
|
174 | with tagswitch(pattern_val) as case:
|
175 | if case(1): # Int
|
176 | raise AssertionError()
|
177 | elif case(2):
|
178 | pattern_eggex = cast(value__Eggex, pattern_val)
|
179 | else:
|
180 | raise AssertionError()
|
181 |
|
182 | print('eggex = %r' % pattern_eggex.ere)
|
183 |
|
184 |
|
185 | def run_tests():
|
186 | # type: () -> None
|
187 |
|
188 | TestCastBufWriter()
|
189 | TestSwitchDowncast(value__Eggex('[0-9]'))
|
190 | TestSwitchDowncast(value__Int(42))
|
191 |
|
192 | TestSwitchDowncastBad(value__Eggex('[0-9]'))
|
193 | TestSwitchDowncastBad(value__Int(42))
|
194 |
|
195 | TestCastInSwitch()
|
196 |
|
197 |
|
198 | def run_benchmarks():
|
199 | # type: () -> None
|
200 | raise NotImplementedError()
|
201 |
|
202 |
|
203 | if __name__ == '__main__':
|
204 | if os.getenv('BENCHMARK'):
|
205 | log('Benchmarking...')
|
206 | run_benchmarks()
|
207 | else:
|
208 | run_tests()
|