summaryrefslogtreecommitdiff
path: root/opt_recomp.py
diff options
context:
space:
mode:
Diffstat (limited to 'opt_recomp.py')
-rw-r--r--opt_recomp.py381
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)