OILS / frontend / flag_gen.py View on Github | oilshell.org

533 lines, 333 significant
1#!/usr/bin/env python2
2"""Flag_gen.py."""
3from __future__ import print_function
4
5import itertools
6import sys
7
8from _devbuild.gen.runtime_asdl import flag_type_e
9from _devbuild.gen.value_asdl import value_e
10from mycpp.mylib import log
11from frontend import args
12from frontend import flag_def # side effect: flags are defined!
13from frontend import flag_spec
14from mycpp import mops
15from 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
23def 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
36def _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
42def _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
73def _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
139def _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
177def 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
189using value_asdl::value;
190using value_asdl::value_e;
191
192namespace 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('''\
227attrs->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('''\
240attrs->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('''\
248attrs->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("""
269class %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("""
307extern FlagSpec_c kFlagSpecs[];
308extern 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"
320using runtime_asdl::flag_type_e;
321
322namespace 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
430def 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("""
468from _devbuild.gen.value_asdl import value, value_e, value_t
469from frontend.args import _Attributes
470from mycpp import mops
471from 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("""
481class %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
528if __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)