1 #!/usr/bin/env python2
2 """Flag_gen.py."""
3 from __future__ import print_function
4
5 import itertools
6 import sys
7
8 from _devbuild.gen.runtime_asdl import flag_type_e, value_e
9 from mycpp.mylib import log
10 from frontend import args
11 from frontend import flag_def # side effect: flags are defined!
12 from frontend import flag_spec
13 from mycpp.mylib import switch
14 # This causes a circular build dependency! That is annoying.
15 # builtin_comp -> core/completion -> pylib/{os_path,path_stat,...} -> posix_
16 #from osh import builtin_comp
17
18 _ = flag_def
19
20
21 def CString(s):
22 # HACKS for now
23
24 assert '"' not in s, s
25 assert '\\' not in s, s
26
27 # For the default of write --end
28 if s == '\n':
29 return '"\\n"'
30
31 return '"%s"' % s
32
33
34 def _WriteStrArray(f, var_name, a):
35 c_strs = ', '.join(CString(s) for s in sorted(a))
36 f.write('const char* %s[] = {%s, nullptr};\n' % (var_name, c_strs))
37 f.write('\n')
38
39
40 def _WriteActionParams(f, actions, counter):
41 param_names = []
42 for key in sorted(actions):
43 action = actions[key]
44 to_write = None
45
46 if isinstance(action, args.SetToString):
47 if action.valid:
48 to_write = action.valid
49
50 elif isinstance(action, args.SetNamedOption):
51 if action.names:
52 to_write = action.names
53
54 elif isinstance(action, args.SetNamedAction):
55 if action.names:
56 to_write = action.names
57
58 if to_write:
59 uniq = counter.next()
60 var_name = 'params_%d' % uniq
61
62 _WriteStrArray(f, var_name, to_write)
63 else:
64 var_name = None
65
66 param_names.append(var_name)
67
68 return param_names
69
70
71 def _WriteActions(f, var_name, actions, counter):
72 # TODO: 'osh' and 'set' duplicate shopt params!!! Maybe we want the entire
73 # action not to be duplicated?
74 param_names = _WriteActionParams(f, actions, counter)
75
76 f.write('Action_c %s[] = {\n' % var_name)
77 for i, key in enumerate(sorted(actions)):
78 action = actions[key]
79 #log('%s %s', key, action)
80
81 name = None
82 if isinstance(action, args.SetToString):
83 if action.quit_parsing_flags:
84 action_type = 'SetToString_q'
85 else:
86 action_type = 'SetToString'
87 name = action.name
88
89 elif isinstance(action, args.SetToInt):
90 action_type = 'SetToInt'
91 name = action.name
92
93 elif isinstance(action, args.SetToFloat):
94 action_type = 'SetToFloat'
95 name = action.name
96
97 elif isinstance(action, args.SetToTrue):
98 action_type = 'SetToTrue'
99 name = action.name
100
101 elif isinstance(action, args.SetAttachedBool):
102 action_type = 'SetAttachedBool'
103 name = action.name
104
105 elif isinstance(action, args.SetOption):
106 action_type = 'SetOption'
107 name = action.name
108
109 elif isinstance(action, args.SetNamedOption):
110 if action.shopt:
111 action_type = 'SetNamedOption_shopt'
112 else:
113 action_type = 'SetNamedOption'
114
115 elif isinstance(action, args.SetAction):
116 action_type = 'SetAction'
117 name = action.name
118
119 elif isinstance(action, args.SetNamedAction):
120 action_type = 'SetNamedAction'
121
122 else:
123 raise AssertionError(action)
124
125 name_str = ('"%s"' % name) if name else 'nullptr'
126 params_str = param_names[i] or 'nullptr'
127 f.write(' {"%s", ActionType_c::%s, %s, %s},\n' %
128 (key, action_type, name_str, params_str))
129 #cc_f.write('SetToArg_c %s[] = {\n' % arity1_name)
130 f.write('''\
131 {},
132 };
133
134 ''')
135
136
137 def _WriteDefaults(cc_f, defaults_name, defaults):
138 cc_f.write('DefaultPair_c %s[] = {\n' % defaults_name)
139
140 for name in sorted(defaults):
141 val = defaults[name]
142 if val.tag() == value_e.Bool:
143 typ = 'Bool'
144 v = '{.b = %s}' % ('true' if val.b else 'false')
145 elif val.tag() == value_e.Int:
146 typ = 'Int'
147 v = '{.i = -1}'
148 elif val.tag() == value_e.Float:
149 typ = 'Float'
150 v = '{.f = -1.0}'
151 elif val.tag() == value_e.Undef:
152 typ = 'Str' # default for string
153 v = '{}'
154 elif val.tag() == value_e.Str:
155 # NOTE: 'osh' FlagSpecAndMore_ has default='nice' and default='abbrev-text'
156 typ = 'Str'
157 v = '{.s = %s}' % CString(val.s)
158
159 else:
160 raise AssertionError(val)
161
162 cc_f.write(' {%s, flag_type_e::%s, %s},\n' % (CString(name), typ, v))
163
164 cc_f.write('''\
165 {},
166 };
167
168 ''')
169
170
171 def Cpp(specs, header_f, cc_f):
172 counter = itertools.count()
173
174 header_f.write("""\
175 // arg_types.h is generated by frontend/flag_gen.py
176
177 #ifndef ARG_TYPES_H
178 #define ARG_TYPES_H
179
180 #include "cpp/frontend_flag_spec.h" // for FlagSpec_c
181 #include "mycpp/gc_mylib.h"
182
183 using runtime_asdl::value;
184 using runtime_asdl::value_e;
185
186 namespace arg_types {
187 """)
188 for spec_name in sorted(specs):
189 spec = specs[spec_name]
190
191 if not spec.fields:
192 continue # skip empty 'eval' spec
193
194 #
195 # Figure out how to initialize the class
196 #
197
198 init_vals = []
199 field_names = []
200 field_decls = []
201 bits = []
202 for field_name in sorted(spec.fields):
203 typ = spec.fields[field_name]
204 field_name = field_name.replace('-', '_')
205 field_names.append(field_name)
206
207 with switch(typ) as case:
208 if case(flag_type_e.Bool):
209 init_vals.append(
210 'static_cast<value::Bool*>(attrs->index_(StrFromC("%s")))->b'
211 % field_name)
212 field_decls.append('bool %s;' % field_name)
213
214 # Bug that test should find
215 #bits.append('maskbit(offsetof(%s, %s))' % (spec_name, field_name))
216
217 elif case(flag_type_e.Str):
218 # TODO: This code is ugly and inefficient! Generate something
219 # better. At least get rid of 'new' everywhere?
220 init_vals.append('''\
221 attrs->index_(StrFromC("%s"))->tag() == value_e::Undef
222 ? nullptr
223 : static_cast<value::Str*>(attrs->index_(StrFromC("%s")))->s''' %
224 (field_name, field_name))
225
226 field_decls.append('Str* %s;' % field_name)
227
228 # Str* is a pointer type, so add a field here
229 bits.append('maskbit(offsetof(%s, %s))' %
230 (spec_name, field_name))
231
232 elif case(flag_type_e.Int):
233 init_vals.append('''\
234 attrs->index_(StrFromC("%s"))->tag() == value_e::Undef
235 ? -1
236 : static_cast<value::Int*>(attrs->index_(StrFromC("%s")))->i''' %
237 (field_name, field_name))
238 field_decls.append('int %s;' % field_name)
239
240 elif case(flag_type_e.Float):
241 init_vals.append('''\
242 attrs->index_(StrFromC("%s"))->tag() == value_e::Undef
243 ? -1
244 : static_cast<value::Float*>(attrs->index_(StrFromC("%s")))->f''' %
245 (field_name, field_name))
246 field_decls.append('float %s;' % field_name)
247
248 else:
249 raise AssertionError(typ)
250
251 #
252 # Now emit the class
253 #
254
255 if bits:
256 obj_tag = 'HeapTag::FixedSize'
257 mask_str = 'field_mask()'
258 else:
259 obj_tag = 'HeapTag::Opaque'
260 mask_str = 'kZeroMask'
261
262 header_f.write("""
263 class %s {
264 public:
265 %s(Dict<Str*, runtime_asdl::value_t*>* attrs)""" % (spec_name, spec_name))
266
267 if field_names:
268 header_f.write('\n : ')
269 for i, field_name in enumerate(field_names):
270 if i != 0:
271 header_f.write(',\n ')
272 header_f.write('%s(%s)' % (field_name, init_vals[i]))
273 header_f.write(' {\n')
274 header_f.write(' }\n')
275 header_f.write('\n')
276
277 for decl in field_decls:
278 header_f.write(' %s\n' % decl)
279
280 header_f.write('\n')
281 header_f.write(' static constexpr ObjHeader obj_header() {\n')
282 header_f.write(' return ObjHeader::Class(%s, %s, sizeof(%s));\n' %
283 (obj_tag, mask_str, spec_name))
284 header_f.write(' }\n')
285
286 if bits:
287 header_f.write('\n')
288 header_f.write(' static constexpr uint32_t field_mask() {\n')
289 header_f.write(' return\n')
290 header_f.write(' ')
291 header_f.write('\n | '.join(bits))
292 header_f.write(';\n')
293 header_f.write(' }\n')
294 header_f.write('\n')
295
296 header_f.write("""\
297 };
298 """)
299
300 header_f.write("""
301 extern FlagSpec_c kFlagSpecs[];
302 extern FlagSpecAndMore_c kFlagSpecsAndMore[];
303
304 } // namespace arg_types
305
306 #endif // ARG_TYPES_H
307
308 """)
309
310 cc_f.write("""\
311 // arg_types.cc is generated by frontend/flag_gen.py
312
313 #include "arg_types.h"
314 using runtime_asdl::flag_type_e;
315
316 namespace arg_types {
317
318 """)
319
320 var_names = []
321 for i, spec_name in enumerate(sorted(flag_spec.FLAG_SPEC)):
322 spec = specs[spec_name]
323 arity0_name = None
324 arity1_name = None
325 actions_long_name = None
326 plus_name = None
327 defaults_name = None
328
329 if spec.arity0:
330 arity0_name = 'arity0_%d' % i
331 _WriteStrArray(cc_f, arity0_name, spec.arity0)
332
333 if spec.arity1:
334 arity1_name = 'arity1_%d' % i
335 _WriteActions(cc_f, arity1_name, spec.arity1, counter)
336
337 if spec.actions_long:
338 actions_long_name = 'actions_long_%d' % i
339 _WriteActions(cc_f, actions_long_name, spec.actions_long, counter)
340
341 if spec.plus_flags:
342 plus_name = 'plus_%d' % i
343 _WriteStrArray(cc_f, plus_name, spec.plus_flags)
344
345 if spec.defaults:
346 defaults_name = 'defaults_%d' % i
347 _WriteDefaults(cc_f, defaults_name, spec.defaults)
348
349 var_names.append((arity0_name, arity1_name, actions_long_name,
350 plus_name, defaults_name))
351
352 cc_f.write('FlagSpec_c kFlagSpecs[] = {\n')
353
354 # Now print a table
355 for i, spec_name in enumerate(sorted(flag_spec.FLAG_SPEC)):
356 spec = specs[spec_name]
357 names = var_names[i]
358 cc_f.write(' { "%s", %s, %s, %s, %s, %s },\n' % (
359 spec_name,
360 names[0] or 'nullptr',
361 names[1] or 'nullptr',
362 names[2] or 'nullptr',
363 names[3] or 'nullptr',
364 names[4] or 'nullptr',
365 ))
366
367 cc_f.write("""\
368 {},
369 };
370
371 """)
372
373 n = len(var_names)
374 var_names = []
375 for i, spec_name in enumerate(sorted(flag_spec.FLAG_SPEC_AND_MORE)):
376 spec = specs[spec_name]
377 actions_short_name = None
378 actions_long_name = None
379 plus_name = None
380 defaults_name = None
381
382 if spec.actions_short:
383 actions_short_name = 'short_%d' % (n + i)
384 _WriteActions(cc_f, actions_short_name, spec.actions_short, counter)
385
386 #if spec.actions_long:
387 if spec.actions_long:
388 actions_long_name = 'long_%d' % (n + i)
389 _WriteActions(cc_f, actions_long_name, spec.actions_long, counter)
390
391 if spec.plus_flags:
392 plus_name = 'plus_%d' % i
393 _WriteStrArray(cc_f, plus_name, spec.plus_flags)
394
395 if spec.defaults:
396 defaults_name = 'defaults_%d' % (n + i)
397 _WriteDefaults(cc_f, defaults_name, spec.defaults)
398
399 var_names.append(
400 (actions_short_name, actions_long_name, plus_name, defaults_name))
401
402 cc_f.write('FlagSpecAndMore_c kFlagSpecsAndMore[] = {\n')
403 for i, spec_name in enumerate(sorted(flag_spec.FLAG_SPEC_AND_MORE)):
404 names = var_names[i]
405 cc_f.write(' { "%s", %s, %s, %s, %s },\n' % (
406 spec_name,
407 names[0] or 'nullptr',
408 names[1] or 'nullptr',
409 names[2] or 'nullptr',
410 names[3] or 'nullptr',
411 ))
412
413 cc_f.write("""\
414 {},
415 };
416 """)
417
418 cc_f.write("""\
419 } // namespace arg_types
420 """)
421
422
423 def main(argv):
424 try:
425 action = argv[1]
426 except IndexError:
427 raise RuntimeError('Action required')
428
429 if 0:
430 for spec_name in sorted(flag_spec.FLAG_SPEC_AND_MORE):
431 log('%s', spec_name)
432
433 # Both kinds of specs have 'fields' attributes
434 specs = {}
435 specs.update(flag_spec.FLAG_SPEC)
436 specs.update(flag_spec.FLAG_SPEC_AND_MORE)
437 #log('SPECS %s', specs)
438
439 for spec_name in sorted(specs):
440 spec = specs[spec_name]
441 #spec.spec.PrettyPrint(f=sys.stderr)
442 #log('spec.arity1 %s', spec.spec.arity1)
443 #log('%s', spec_name)
444
445 #print(dir(spec))
446 #print(spec.arity0)
447 #print(spec.arity1)
448 #print(spec.options)
449 # Every flag has a default
450 #log('%s', spec.fields)
451
452 if action == 'cpp':
453 prefix = argv[2]
454
455 with open(prefix + '.h', 'w') as header_f:
456 with open(prefix + '.cc', 'w') as cc_f:
457 Cpp(specs, header_f, cc_f)
458
459 elif action == 'mypy':
460 print("""
461 from frontend.args import _Attributes
462 from _devbuild.gen.runtime_asdl import value, value_e, value_t
463 from typing import cast, Dict, Optional
464 """)
465 for spec_name in sorted(specs):
466 spec = specs[spec_name]
467
468 #log('%s spec.fields %s', spec_name, spec.fields)
469 if not spec.fields:
470 continue # skip empty specs, e.g. eval
471
472 print("""
473 class %s(object):
474 def __init__(self, attrs):
475 # type: (Dict[str, value_t]) -> None
476 """ % spec_name)
477
478 i = 0
479 for field_name in sorted(spec.fields):
480 typ = spec.fields[field_name]
481 field_name = field_name.replace('-', '_')
482
483 with switch(typ) as case:
484 if case(flag_type_e.Bool):
485 print(
486 ' self.%s = cast(value.Bool, attrs[%r]).b # type: bool'
487 % (field_name, field_name))
488
489 elif case(flag_type_e.Str):
490 tmp = 'val%d' % i
491 print(' %s = attrs[%r]' % (tmp, field_name))
492 print(
493 ' self.%s = None if %s.tag() == value_e.Undef else cast(value.Str, %s).s # type: Optional[str]'
494 % (field_name, tmp, tmp))
495
496 elif case(flag_type_e.Int):
497 tmp = 'val%d' % i
498 print(' %s = attrs[%r]' % (tmp, field_name))
499 print(
500 ' self.%s = -1 if %s.tag() == value_e.Undef else cast(value.Int, %s).i # type: int'
501 % (field_name, tmp, tmp))
502
503 elif case(flag_type_e.Float):
504 tmp = 'val%d' % i
505 print(' %s = attrs[%r]' % (tmp, field_name))
506 print(
507 ' self.%s = -1.0 if %s.tag() == value_e.Undef else cast(value.Float, %s).f # type: float'
508 % (field_name, tmp, tmp))
509 else:
510 raise AssertionError(typ)
511
512 i += 1
513
514 print()
515
516 else:
517 raise RuntimeError('Invalid action %r' % action)
518
519
520 if __name__ == '__main__':
521 try:
522 main(sys.argv)
523 except RuntimeError as e:
524 print('FATAL: %s' % e, file=sys.stderr)
525 sys.exit(1)