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