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