mirror of https://git.wuffs.org/MWCC
382 lines
17 KiB
Python
382 lines
17 KiB
Python
|
import json
|
||
|
|
||
|
class OptionCompiler:
|
||
|
def __init__(self):
|
||
|
self.last_id = 0
|
||
|
self.debug_indent = ''
|
||
|
self.root_basename = ''
|
||
|
self.current_basename = ''
|
||
|
self.current_forward_decls = []
|
||
|
self.current_output = []
|
||
|
self.conflict_groups = {}
|
||
|
|
||
|
def debug_push(self):
|
||
|
self.debug_indent += ' '
|
||
|
|
||
|
def debug_pop(self):
|
||
|
self.debug_indent = self.debug_indent[:-2]
|
||
|
|
||
|
def get_new_id(self):
|
||
|
self.last_id += 1
|
||
|
return self.last_id
|
||
|
|
||
|
def skip_id(self):
|
||
|
self.last_id += 1
|
||
|
|
||
|
def add_root(self, root):
|
||
|
self.assign_all_ids(root)
|
||
|
print('-'*80)
|
||
|
self.assign_all_names(root)
|
||
|
print('-'*80)
|
||
|
self.current_forward_decls = []
|
||
|
self.current_output = []
|
||
|
self.conflict_groups = {}
|
||
|
self.emit_root(root)
|
||
|
|
||
|
filename = f"Opts{root['name']}.opt"
|
||
|
with open(f'compiler_and_linker/CmdLine_Tools/MacOS_PPC/Tools_PPC/Lib/mac-ppc-cc/{filename}', 'w') as f:
|
||
|
for line in self.current_forward_decls:
|
||
|
f.write(line)
|
||
|
f.write('\n')
|
||
|
if self.current_forward_decls:
|
||
|
f.write('\n')
|
||
|
for line in self.current_output:
|
||
|
f.write(line)
|
||
|
f.write('\n')
|
||
|
|
||
|
# Phase 1
|
||
|
def assign_all_ids(self, root):
|
||
|
for option in root['options']:
|
||
|
self.assign_id_to_option(option)
|
||
|
root['_list_id'] = self.get_new_id()
|
||
|
|
||
|
def assign_id_to_option(self, option):
|
||
|
print(f"{self.debug_indent}option {option['names']}")
|
||
|
self.debug_push()
|
||
|
if 'sub_options' in option:
|
||
|
for sub_option in reversed(option['sub_options']):
|
||
|
self.assign_id_to_option(sub_option)
|
||
|
option['_sub_list_id'] = self.get_new_id()
|
||
|
print(f"{self.debug_indent}{option['_sub_list_id']}: option sublist")
|
||
|
for param in reversed(option['params']):
|
||
|
self.assign_id_to_param(param)
|
||
|
option['_id'] = self.get_new_id()
|
||
|
print(f"{self.debug_indent}{option['_id']}: option ({option['names']})")
|
||
|
self.debug_pop()
|
||
|
|
||
|
def assign_id_to_param(self, param):
|
||
|
if param['_type'] == 'IfArg':
|
||
|
for sub_param in reversed(param['if_no_arg']):
|
||
|
self.assign_id_to_param(sub_param)
|
||
|
for sub_param in reversed(param['if_arg']):
|
||
|
self.assign_id_to_param(sub_param)
|
||
|
|
||
|
if '_idskip' in param:
|
||
|
self.last_id += param['_idskip']
|
||
|
param['_id'] = self.get_new_id()
|
||
|
print(f"{self.debug_indent}{param['_id']}: param {param['_type']}")
|
||
|
|
||
|
# Phase 2
|
||
|
def assign_all_names(self, root):
|
||
|
self.root_basename = f"optlst{root['name']}"
|
||
|
self.current_basename = f"optlst{root['name']}"
|
||
|
root['_name'] = self.current_basename
|
||
|
root['_list_name'] = f"{self.current_basename}_{root['_list_id']:03d}_list"
|
||
|
for option in root['options']:
|
||
|
self.assign_name_to_option(option)
|
||
|
|
||
|
def assign_name_to_option(self, option):
|
||
|
if 'conflict_group' in option:
|
||
|
option['_name'] = f"{option['conflict_group']}_{option['_id']:03d}"
|
||
|
elif 'custom_name' in option:
|
||
|
option['_name'] = f"{self.current_basename}_{option['custom_name']}"
|
||
|
else:
|
||
|
option['_name'] = f"{self.current_basename}_{option['_id']:03d}"
|
||
|
print(f"{self.debug_indent}{option['_name']} = {option['names']}")
|
||
|
self.debug_push()
|
||
|
for param in option['params']:
|
||
|
self.assign_name_to_param(param)
|
||
|
if 'sub_options' in option:
|
||
|
save_basename = self.current_basename
|
||
|
option['_sub_list_name'] = f"{self.current_basename}_{option['_sub_list_id']:03d}"
|
||
|
if 'sub_options_exclusive' in option and option['sub_options_exclusive']:
|
||
|
self.current_basename = option['_sub_list_name']
|
||
|
for sub_option in option['sub_options']:
|
||
|
self.assign_name_to_option(sub_option)
|
||
|
self.current_basename = save_basename
|
||
|
self.debug_pop()
|
||
|
|
||
|
def assign_name_to_param(self, param):
|
||
|
if param['_type'] == 'IfArg':
|
||
|
self.debug_push()
|
||
|
for sub_param in param['if_no_arg']:
|
||
|
self.assign_name_to_param(sub_param)
|
||
|
for sub_param in param['if_arg']:
|
||
|
self.assign_name_to_param(sub_param)
|
||
|
self.debug_pop()
|
||
|
param['_name'] = f"{self.root_basename}{param['_id']:03d}"
|
||
|
print(f"{self.debug_indent}{param['_name']} = {param['_type']}")
|
||
|
|
||
|
# Phase 3
|
||
|
def emit_root(self, root):
|
||
|
self.current_output.append(f"/* {root['name']} */")
|
||
|
for option in root['options']:
|
||
|
self.emit_option(option)
|
||
|
|
||
|
self.current_output.append(f"Option *{root['_list_name']}[] = {{")
|
||
|
for option in root['options']:
|
||
|
self.current_output.append(f"\t&{option['_name']},")
|
||
|
self.current_output.append('\t0')
|
||
|
self.current_output.append('};')
|
||
|
|
||
|
list_flags = build_list_flags(root)
|
||
|
self.current_output.append(f"OptionList {root['_name']} = {{")
|
||
|
self.current_output.append(f"\t/* help = */ {json.dumps(root['help'])},")
|
||
|
self.current_output.append(f"\t/* flags = */ {list_flags},")
|
||
|
self.current_output.append(f"\t/* list = */ {root['_list_name']}")
|
||
|
self.current_output.append('};')
|
||
|
|
||
|
for (i, (group, option_names)) in enumerate(self.conflict_groups.items()):
|
||
|
self.current_forward_decls.append(f'/* forward declare */ extern OptionList {group}_conflicts;')
|
||
|
self.current_output.append(f'Option *{group}_{i:03d}_list[] = {{')
|
||
|
for option_name in option_names:
|
||
|
self.current_output.append(f'\t&{option_name},')
|
||
|
self.current_output.append('\t0')
|
||
|
self.current_output.append('};')
|
||
|
self.current_output.append(f'OptionList {group}_conflicts = {{')
|
||
|
self.current_output.append(f'\t/* help = */ 0,')
|
||
|
self.current_output.append(f'\t/* flags = */ 0,')
|
||
|
self.current_output.append(f'\t/* list = */ {group}_{i:03d}_list')
|
||
|
self.current_output.append('};')
|
||
|
|
||
|
def emit_option(self, option, is_conflicted=None):
|
||
|
option_flags = build_option_flags(option, is_conflicted)
|
||
|
|
||
|
sub_name = '0'
|
||
|
if 'sub_options' in option:
|
||
|
sub_name = '&' + option['_sub_list_name']
|
||
|
sub_conflicts = None
|
||
|
if ('sub_options_exclusive' in option) and option['sub_options_exclusive']:
|
||
|
sub_conflicts = f"&{option['_sub_list_name']}_conflicts"
|
||
|
self.current_output.append(f"/* forward declare */ extern OptionList {option['_sub_list_name']}_conflicts;")
|
||
|
|
||
|
for sub_option in option['sub_options']:
|
||
|
self.emit_option(sub_option, sub_conflicts)
|
||
|
|
||
|
self.current_output.append(f"Option *{option['_sub_list_name']}_list[] = {{")
|
||
|
for sub_option in option['sub_options']:
|
||
|
self.current_output.append(f"\t&{sub_option['_name']},")
|
||
|
self.current_output.append('\t0')
|
||
|
self.current_output.append('};')
|
||
|
|
||
|
list_flags = build_list_flags(option)
|
||
|
self.current_output.append(f"OptionList {option['_sub_list_name']} = {{")
|
||
|
self.current_output.append(f"\t/* help = */ 0,")
|
||
|
self.current_output.append(f"\t/* flags = */ {list_flags},")
|
||
|
self.current_output.append(f"\t/* list = */ {option['_sub_list_name']}_list")
|
||
|
self.current_output.append('};')
|
||
|
|
||
|
if ('sub_options_exclusive' in option) and option['sub_options_exclusive']:
|
||
|
self.current_output.append(f"OptionList {option['_sub_list_name']}_conflicts = {{")
|
||
|
self.current_output.append(f"\t/* help = */ 0,")
|
||
|
self.current_output.append(f"\t/* flags = */ {list_flags},")
|
||
|
self.current_output.append(f"\t/* list = */ {option['_sub_list_name']}_list")
|
||
|
self.current_output.append('};')
|
||
|
|
||
|
if is_conflicted:
|
||
|
conflicts_name = is_conflicted
|
||
|
elif 'conflict_group' in option:
|
||
|
conflict_group = option['conflict_group']
|
||
|
conflicts_name = f'&{conflict_group}_conflicts'
|
||
|
if conflict_group in self.conflict_groups:
|
||
|
self.conflict_groups[conflict_group].append(option['_name'])
|
||
|
else:
|
||
|
self.conflict_groups[conflict_group] = [option['_name']]
|
||
|
else:
|
||
|
conflicts_name = '0'
|
||
|
|
||
|
last_param = self.emit_params(option['params'])
|
||
|
help_str = json.dumps(option['help']) if ('help' in option and option['help'] is not None) else '0'
|
||
|
|
||
|
self.current_output.append(f"Option {option['_name']} = {{")
|
||
|
self.current_output.append(f"\t/* names = */ {json.dumps(option['names'])},")
|
||
|
self.current_output.append(f"\t/* avail = */ {option_flags},")
|
||
|
self.current_output.append(f"\t/* param = */ {last_param},")
|
||
|
self.current_output.append(f"\t/* sub = */ {sub_name},")
|
||
|
self.current_output.append(f"\t/* conflicts = */ {conflicts_name},")
|
||
|
self.current_output.append(f"\t/* help = */ {help_str}")
|
||
|
self.current_output.append('};')
|
||
|
|
||
|
def emit_params(self, params):
|
||
|
last_param = '0'
|
||
|
|
||
|
for param in reversed(params):
|
||
|
if param['_type'] == 'IfArg':
|
||
|
ia_last_parg = self.emit_params(param['if_arg'])
|
||
|
ia_last_pnone = self.emit_params(param['if_no_arg'])
|
||
|
|
||
|
myname_str = json.dumps(param['myname']) if 'myname' in param else '0'
|
||
|
|
||
|
self.current_output.append(f"{PARAM_TYPES[param['_type']]} {param['_name']} = {{")
|
||
|
self.current_output.append(f"\t/* which = */ PARAMWHICH_{param['_type']},")
|
||
|
self.current_output.append(f"\t/* flags = */ 0x{param['flags']:02X},")
|
||
|
self.current_output.append(f"\t/* myname = */ {myname_str},")
|
||
|
self.current_output.append(f"\t/* next = */ {last_param},")
|
||
|
if param['_type'] == 'FTypeCreator':
|
||
|
self.current_output.append(f"\t/* fc = */ &{param['target']},")
|
||
|
self.current_output.append(f"\t/* iscreator = */ {param['is_creator']}")
|
||
|
elif param['_type'] == 'FilePath':
|
||
|
self.current_output.append(f"\t/* fflags = */ {param['fflags']},")
|
||
|
df = json.dumps(param['default']) if param['default'] is not None else '0'
|
||
|
self.current_output.append(f"\t/* defaultstr = */ {df},")
|
||
|
self.current_output.append(f"\t/* filename = */ {param['target']},")
|
||
|
self.current_output.append(f"\t/* maxlen = */ {param['max_length']}")
|
||
|
elif param['_type'] == 'Number':
|
||
|
self.current_output.append(f"\t/* size = */ {param['byte_size']},")
|
||
|
self.current_output.append(f"\t/* fit = */ {param['fit']},")
|
||
|
self.current_output.append(f"\t/* lo = */ {param['minimum']},")
|
||
|
self.current_output.append(f"\t/* hi = */ {param['maximum']},")
|
||
|
self.current_output.append(f"\t/* num = */ &{param['target']}")
|
||
|
elif param['_type'] in ('String','Id','Sym'):
|
||
|
self.current_output.append(f"\t/* maxlen = */ {param['max_length']},")
|
||
|
self.current_output.append(f"\t/* pstring = */ {int(param['pascal'])},")
|
||
|
self.current_output.append(f"\t/* str = */ {param['target']}")
|
||
|
elif param['_type'] in ('OnOff','OffOn'):
|
||
|
self.current_output.append(f"\t/* var = */ &{param['target']}")
|
||
|
elif param['_type'] == 'Mask':
|
||
|
self.current_output.append(f"\t/* size = */ {param['byte_size']},")
|
||
|
self.current_output.append(f"\t/* ormask = */ 0x{param['or_mask']:X},")
|
||
|
self.current_output.append(f"\t/* andmask = */ 0x{param['and_mask']:X},")
|
||
|
self.current_output.append(f"\t/* num = */ &{param['target']}")
|
||
|
elif param['_type'] == 'Toggle':
|
||
|
self.current_output.append(f"\t/* size = */ {param['byte_size']},")
|
||
|
self.current_output.append(f"\t/* mask = */ 0x{param['mask']:X},")
|
||
|
self.current_output.append(f"\t/* num = */ &{param['target']}")
|
||
|
elif param['_type'] == 'Set':
|
||
|
self.current_output.append(f"\t/* size = */ {param['byte_size']},")
|
||
|
self.current_output.append(f"\t/* value = */ 0x{param['value']:X},")
|
||
|
if param['target']:
|
||
|
self.current_output.append(f"\t/* num = */ (char *) &{param['target']}")
|
||
|
else:
|
||
|
self.current_output.append(f"\t/* num = */ 0")
|
||
|
elif param['_type'] == 'SetString':
|
||
|
self.current_output.append(f"\t/* value = */ {json.dumps(param['value'])},")
|
||
|
self.current_output.append(f"\t/* pstring = */ {int(param['pascal'])},")
|
||
|
self.current_output.append(f"\t/* var = */ {param['target']}")
|
||
|
elif param['_type'] == 'Generic':
|
||
|
self.current_output.append(f"\t/* parse = */ &{param['function']},")
|
||
|
self.current_output.append(f"\t/* arg = */ (void *) {json.dumps(param['arg'])},")
|
||
|
help_str = json.dumps(param['help']) if ('help' in param and param['help'] is not None) else '0'
|
||
|
self.current_output.append(f"\t/* help = */ {help_str}")
|
||
|
elif param['_type'] == 'IfArg':
|
||
|
self.current_output.append(f"\t/* parg = */ {ia_last_parg},")
|
||
|
self.current_output.append(f"\t/* helpa = */ {json.dumps(param['help_a'])},")
|
||
|
self.current_output.append(f"\t/* pnone = */ {ia_last_pnone},")
|
||
|
self.current_output.append(f"\t/* helpn = */ {json.dumps(param['help_n'])}")
|
||
|
elif param['_type'] == 'Setting':
|
||
|
self.current_output.append(f"\t/* parse = */ &{param['function']},")
|
||
|
self.current_output.append(f"\t/* valuename = */ {json.dumps(param['value_name'])}")
|
||
|
self.current_output.append('};')
|
||
|
last_param = '(PARAM_T *) &' + param['_name']
|
||
|
|
||
|
return last_param
|
||
|
|
||
|
PARAM_TYPES = {
|
||
|
'FTypeCreator': 'FTYPE_T',
|
||
|
'FilePath': 'FILEPATH_T',
|
||
|
'Number': 'NUM_T',
|
||
|
'String': 'STRING_T',
|
||
|
'Id': 'STRING_T',
|
||
|
'Sym': 'STRING_T',
|
||
|
'OnOff': 'ONOFF_T',
|
||
|
'OffOn': 'OFFON_T',
|
||
|
'Mask': 'MASK_T',
|
||
|
'Toggle': 'TOGGLE_T',
|
||
|
'Set': 'SET_T',
|
||
|
'SetString': 'SETSTRING_T',
|
||
|
'Generic': 'GENERIC_T',
|
||
|
'IfArg': 'IFARG_T',
|
||
|
'Setting': 'SETTING_T'
|
||
|
}
|
||
|
|
||
|
def build_list_flags(what):
|
||
|
bits = []
|
||
|
if 'sub_options_exclusive' in what and what['sub_options_exclusive']:
|
||
|
bits.append('LISTFLAGS_EXCLUSIVE')
|
||
|
if 'sub_options_allow_unknowns' in what and what['sub_options_allow_unknowns']:
|
||
|
bits.append('LISTFLAGS_ALLOW_UNKNOWNS')
|
||
|
for tool in what['tools']:
|
||
|
if tool == 'compiler':
|
||
|
bits.append('LISTFLAGS_COMPILER')
|
||
|
elif tool == 'linker':
|
||
|
bits.append('LISTFLAGS_LINKER')
|
||
|
elif tool == 'disassembler':
|
||
|
bits.append('LISTFLAGS_DISASSEMBLER')
|
||
|
if bits:
|
||
|
return ' | '.join(bits)
|
||
|
else:
|
||
|
return '0'
|
||
|
|
||
|
def build_option_flags(option, has_conflicts=False):
|
||
|
bits = []
|
||
|
if 'global' in option and option['global']:
|
||
|
bits.append('OTF_GLOBAL')
|
||
|
if 'sticky' in option and option['sticky']:
|
||
|
bits.append('OTF_STICKY')
|
||
|
if 'cased' in option and option['cased']:
|
||
|
bits.append('OTF_CASED')
|
||
|
if 'obsolete' in option and option['obsolete']:
|
||
|
bits.append('OTF_OBSOLETE')
|
||
|
if 'substituted' in option and option['substituted']:
|
||
|
bits.append('OTF_SUBSTITUTED')
|
||
|
if 'deprecated' in option and option['deprecated']:
|
||
|
bits.append('OTF_DEPRECATED')
|
||
|
if 'ignored' in option and option['ignored']:
|
||
|
bits.append('OTF_IGNORED')
|
||
|
if 'secret' in option and option['secret']:
|
||
|
bits.append('OTF_SECRET')
|
||
|
if 'hide_default' in option and option['hide_default']:
|
||
|
bits.append('OTF_HIDE_DEFAULT')
|
||
|
if 'compatibility' in option and option['compatibility']:
|
||
|
bits.append('OTF_COMPATIBILITY')
|
||
|
if 'only_once' in option and option['only_once']:
|
||
|
bits.append('OTF_ONLY_ONCE')
|
||
|
if 'warning' in option and option['warning']:
|
||
|
bits.append('OTF_WARNING')
|
||
|
if 'can_be_negated' in option and option['can_be_negated']:
|
||
|
bits.append('OTF_SLFLAGS_8')
|
||
|
if 'can_be_negated_2' in option and option['can_be_negated_2']:
|
||
|
bits.append('OTF_SLFLAGS_10')
|
||
|
if 'can_be_negated_3' in option and option['can_be_negated_3']:
|
||
|
bits.append('OTF_SLFLAGS_20')
|
||
|
if 'meaningless' in option and option['meaningless']:
|
||
|
bits.append('OTF_MEANINGLESS')
|
||
|
|
||
|
for tool in option['tools']:
|
||
|
if tool == 'compiler':
|
||
|
bits.append('OTF_TOOL_COMPILER')
|
||
|
elif tool == 'linker':
|
||
|
bits.append('OTF_TOOL_LINKER')
|
||
|
elif tool == 'disassembler':
|
||
|
bits.append('OTF_TOOL_DISASSEMBLER')
|
||
|
|
||
|
if 'sub_options' in option:
|
||
|
bits.append('OTF_HAS_SUB_OPTIONS')
|
||
|
if 'sub_options_optional' in option and option['sub_options_optional']:
|
||
|
bits.append('OTF_SUB_OPTIONS_OPTIONAL')
|
||
|
|
||
|
if 'conflict_group' in option or has_conflicts:
|
||
|
bits.append('OTF_HAS_CONFLICTS')
|
||
|
|
||
|
if bits:
|
||
|
return ' | '.join(bits)
|
||
|
else:
|
||
|
return '0'
|
||
|
|
||
|
with open('../../nice_opts.json', 'r') as f:
|
||
|
roots = json.load(f)
|
||
|
|
||
|
oc = OptionCompiler()
|
||
|
for root in roots:
|
||
|
oc.add_root(root)
|