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