OILS / builtin / method_type.py View on Github | oils.pub

141 lines, 91 significant
1"""Methods on Obj instances that represent types"""
2from __future__ import print_function
3
4from _devbuild.gen.value_asdl import value, value_e, value_t, Obj
5
6from core import error
7from core import vm
8from frontend import typed_args
9from mycpp import mylib
10from mycpp.mylib import log, tagswitch, str_switch
11
12from typing import Dict, List, Optional, cast, TYPE_CHECKING
13if TYPE_CHECKING:
14 pass
15
16_ = log
17
18
19def _GetStringField(obj, field_name):
20 # type: (Obj, str) -> Optional[str]
21
22 val = obj.d.get(field_name)
23
24 # This could happen if a user attaches this BuiltinFunc to another
25 # Object? A non-type object. Or the user can mutate the type object.
26 if val is None:
27 return None
28 if val.tag() != value_e.Str:
29 return None
30 return cast(value.Str, val).s
31
32
33class Index__(vm._Callable):
34 """
35 This maintains the invariants:
36
37 List[Int] is List[Int]
38 List[Str] is List[Str]
39
40 i.e. 2 evaluations always yield the same object
41 """
42
43 def __init__(self):
44 # type: () -> None
45 self.unique_instances = {} # type: Dict[str, Obj]
46
47 def Call(self, rd):
48 # type: (typed_args.Reader) -> value_t
49 val = self._Call(rd)
50 if val is None:
51 raise error.Expr(
52 'Obj __index__ method detected a broken type Obj invariant',
53 rd.LeastSpecificLocation())
54 return val
55
56 def _Call(self, rd):
57 # type: (typed_args.Reader) -> Optional[value_t]
58 left_obj = rd.PosObj()
59 right = rd.PosValue()
60 rd.Done()
61
62 left_name = _GetStringField(left_obj, 'name')
63 if left_name is None:
64 return None # all type objects should have 'name'
65
66 # This would mess up the encoding of 'Dict[Str,Int]'
67 assert (',' not in left_name and '[' not in left_name and
68 ']' not in left_name), left_name
69
70 UP_right = right
71
72 objects = [] # type: List[Obj]
73 with tagswitch(right) as case2:
74 if case2(value_e.Obj):
75 right = cast(Obj, UP_right)
76 objects.append(right)
77
78 elif case2(value_e.List):
79 right = cast(value.List, UP_right)
80 for i, val in enumerate(right.items):
81 if val.tag() != value_e.Obj:
82 # List[Str, 3] is invalid
83 return None
84 objects.append(cast(Obj, val))
85 else:
86 raise error.TypeErr(
87 right, 'Obj __index__ method expected Obj or List',
88 rd.LeastSpecificLocation())
89
90 with str_switch(left_name) as case:
91 if case('List'):
92 expected_params = 1
93 elif case('Dict'):
94 expected_params = 2
95 else:
96 expected_params = 0
97
98 actual = len(objects)
99 if expected_params != actual:
100 raise error.Expr(
101 'Obj __index__ method expected %d params, got %d' %
102 (expected_params, actual), rd.LeastSpecificLocation())
103
104 buf = mylib.BufWriter()
105 buf.write(left_name)
106 buf.write('[')
107
108 for i, r in enumerate(objects):
109 if i != 0:
110 buf.write(',')
111
112 #log('OBJ %s', r)
113
114 r_unique_id = _GetStringField(r, 'unique_id')
115 if r_unique_id is not None:
116 buf.write(r_unique_id)
117 else:
118 r_name = _GetStringField(r, 'name')
119 if r_name is None:
120 # every param object should have either:
121 # 'name' - type object
122 # 'unique_id' - parameterized type object
123 return None
124 assert (',' not in r_name and '[' not in r_name and
125 ']' not in r_name), r_name
126 buf.write(r_name)
127
128 buf.write(']')
129
130 unique_id = buf.getvalue()
131 obj_with_params = self.unique_instances.get(unique_id)
132 if obj_with_params is None:
133 # These are parameterized type objects
134 props = {
135 'unique_id': value.Str(unique_id),
136 #'children': value.List(children)
137 } # type: Dict[str, value_t]
138 obj_with_params = Obj(None, props)
139 self.unique_instances[unique_id] = obj_with_params
140
141 return obj_with_params