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)