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