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

532 lines, 332 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 frontend import args
11from frontend import flag_def # side effect: flags are defined!
12from frontend import flag_spec
13from mycpp import mops
14from 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
22def 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
35def _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
41def _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
72def _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
138def _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
176def 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
188using value_asdl::value;
189using value_asdl::value_e;
190
191namespace 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('''\
226attrs->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('''\
239attrs->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('''\
247attrs->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("""
268class %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("""
306extern FlagSpec_c kFlagSpecs[];
307extern 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"
319using runtime_asdl::flag_type_e;
320
321namespace 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
429def 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("""
467from _devbuild.gen.value_asdl import value, value_e, value_t
468from frontend.args import _Attributes
469from mycpp import mops
470from 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("""
480class %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
527if __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)