diff options
Diffstat (limited to 'opt_recomp.py')
-rw-r--r-- | opt_recomp.py | 381 |
1 files changed, 381 insertions, 0 deletions
diff --git a/opt_recomp.py b/opt_recomp.py new file mode 100644 index 0000000..b7c46c4 --- /dev/null +++ b/opt_recomp.py @@ -0,0 +1,381 @@ +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) |