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