OILS / mycpp / examples / test_cast.py View on Github | oilshell.org

211 lines, 87 significant
1#!/usr/bin/env python2
2"""
3test_cast.py
4"""
5from __future__ import print_function
6
7import os
8from typing import Tuple, cast
9
10from mycpp import mylib
11from mycpp.mylib import log, tagswitch
12
13
14class 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
40def 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
51class 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
62class 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
73class 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
84def 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
101def 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
163def 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
185def run_tests():
186 # type: () -> None
187
188 # This should (void)unused2; on the same line
189 unused2 = 42
190
191 TestCastBufWriter()
192 TestSwitchDowncast(value__Eggex('[0-9]'))
193 TestSwitchDowncast(value__Int(42))
194
195 TestSwitchDowncastBad(value__Eggex('[0-9]'))
196 TestSwitchDowncastBad(value__Int(42))
197
198 TestCastInSwitch()
199
200
201def run_benchmarks():
202 # type: () -> None
203 raise NotImplementedError()
204
205
206if __name__ == '__main__':
207 if os.getenv('BENCHMARK'):
208 log('Benchmarking...')
209 run_benchmarks()
210 else:
211 run_tests()