import tkinter as tk from tkinter import ttk, messagebox import re import uuid class MoralithicChainGeneratorMode2: def __init__(self, root): self.root = root self.root.title("Moralithic Chain Generator - Mode 2") self.valid_statement_source_pairs = { 'ST31': 'SO30', 'ST32': 'SO31', 'ST33': 'SO32', 'ST34': 'SO33', 'ST43': 'SO42', 'ST44': 'SO43', 'ST45': 'SO44', 'ST46': 'SO45', 'ST47': 'SO46', 'ST48': 'SO47', 'ST49': 'SO48', 'ST50': 'SO49', 'ST52': 'SO51', 'ST54': 'SO53', 'ST56': 'SO55', 'ST58': 'SO57', 'ST59': 'SO58', 'ST60': 'SO59', 'ST61': 'SO60', 'ST62': 'SO61', 'ST63': 'SO62', 'ST64': 'SO63', 'ST65': 'SO64', 'ST66': 'SO65', 'ST67': 'SO66', 'ST68': 'SO67', 'ST69': 'SO68', 'ST70': 'SO69', 'ST71': 'SO70', 'ST72': 'SO71', 'ST73': 'SO72', 'ST74': 'SO73' } self.valid_source_statement_pairs = {v: k for k, v in self.valid_statement_source_pairs.items()} self.valid_statement_value_pairs = { 'ST31': {'values': ['VL31'], 'required': 'VL31'}, 'ST32': {'values': ['VL31', 'VL32'], 'required': 'VL32'}, 'ST33': {'values': ['VL33'], 'required': 'VL33'}, 'ST34': {'values': ['VL31', 'VL32', 'VL33', 'VL34'], 'required': 'VL34'}, 'ST43': {'values': ['VL43'], 'required': 'VL43'}, 'ST44': {'values': ['VL43', 'VL44'], 'required': 'VL44'}, 'ST45': {'values': ['VL45'], 'required': 'VL45'}, 'ST46': {'values': ['VL43', 'VL44', 'VL45', 'VL46'], 'required': 'VL46'}, 'ST47': {'values': ['VL47'], 'required': 'VL47'}, 'ST48': {'values': ['VL47', 'VL48'], 'required': 'VL48'}, 'ST49': {'values': ['VL47', 'VL48', 'VL49'], 'required': 'VL49'}, 'ST50': {'values': ['VL47', 'VL48', 'VL49', 'VL50'], 'required': 'VL50'}, 'ST52': {'values': ['VL52'], 'required': 'VL52'}, 'ST54': {'values': ['VL54'], 'required': 'VL54'}, 'ST56': {'values': ['VL56'], 'required': 'VL56'}, 'ST58': {'values': ['VL58'], 'required': 'VL58'}, 'ST59': {'values': ['VL59'], 'required': 'VL59'}, 'ST60': {'values': ['VL59', 'VL60'], 'required': 'VL60'}, 'ST61': {'values': ['VL61'], 'required': 'VL61'}, 'ST62': {'values': ['VL59', 'VL60', 'VL61', 'VL62'], 'required': 'VL62'}, 'ST63': {'values': ['VL63'], 'required': 'VL63'}, 'ST64': {'values': ['VL63', 'VL64'], 'required': 'VL64'}, 'ST65': {'values': ['VL65'], 'required': 'VL65'}, 'ST66': {'values': ['VL63', 'VL64', 'VL65', 'VL66'], 'required': 'VL66'}, 'ST67': {'values': ['VL67'], 'required': 'VL67'}, 'ST68': {'values': ['VL67', 'VL68'], 'required': 'VL68'}, 'ST69': {'values': ['VL69'], 'required': 'VL69'}, 'ST70': {'values': ['VL67', 'VL68', 'VL69', 'VL70'], 'required': 'VL70'}, 'ST71': {'values': ['VL71'], 'required': 'VL71'}, 'ST72': {'values': ['VL71', 'VL72'], 'required': 'VL72'}, 'ST73': {'values': ['VL73'], 'required': 'VL73'}, 'ST74': {'values': ['VL71', 'VL72', 'VL73', 'VL74'], 'required': 'VL74'} } self.valid_ni_tags = ['NI04', 'NI05', 'NI06', 'NI07'] self.valid_ef_tags = [f"EF{i}" for i in range(32, 64)] self.valid_rp_tags = ['RP04', 'RP05', 'RP06', 'RP07'] self.valid_re_tags = [f"RE{i:02d}" for i in range(8, 16)] self.valid_ac_tags = [f"AC{i:02d}" for i in range(8, 16)] self.valid_means_tags = ["ME02", "ME03"] self.valid_displayment_tags = ["DP02", "DP03"] self.valid_statement_tags = list(self.valid_statement_source_pairs.keys()) self.valid_source_tags = list(self.valid_source_statement_pairs.keys()) self.valid_value_tags = sorted(set(sum([d['values'] for d in self.valid_statement_value_pairs.values()], []))) self.required_values = ['VL31', 'VL33', 'VL43', 'VL45', 'VL47', 'VL49', 'VL59', 'VL61', 'VL63', 'VL65', 'VL67', 'VL69', 'VL71', 'VL73'] self.required_ini_tags = ['NI04', 'NI06', 'EF32', 'EF34', 'EF36', 'EF38', 'EF40', 'EF42', 'EF44', 'EF46', 'EF48', 'EF50', 'EF52', 'EF54', 'EF56', 'EF58', 'EF60', 'EF62', 'RP04', 'RP06', 'RE08', 'RE10', 'RE12', 'RE14', 'AC08', 'AC10', 'AC12', 'AC14'] self.ac_compat = { 'AC12': ['EF36', 'EF38'], 'AC13': ['EF37', 'EF39'], 'AC14': ['EF44', 'EF46', 'RP06'], 'AC15': ['EF45', 'EF47', 'RP07'] } self.function_source_tags = ['SO51', 'SO53', 'SO55', 'SO57', 'SO66', 'SO67', 'SO68', 'SO69', 'SO70', 'SO71', 'SO72', 'SO73'] self.process_source_tags = ['SO58', 'SO59', 'SO60', 'SO61', 'SO62', 'SO63', 'SO64', 'SO65'] self.function_statement_tags = ['ST52', 'ST54', 'ST56', 'ST58', 'ST67', 'ST68', 'ST69', 'ST70', 'ST71', 'ST72', 'ST73', 'ST74'] self.process_statement_tags = ['ST59', 'ST60', 'ST61', 'ST62', 'ST63', 'ST64', 'ST65', 'ST66'] self.value_items = [] self.non_interactor_items = [] self.enforcer_items = [] self.repeater_items = [] self.rejecter_items = [] self.accepter_items = [] self.interactors = [] self.chain_text = "" self.conversion_rules = { 'AC08': ['RE08', 'RE12'], 'AC09': ['RE09', 'RE13'], 'AC10': ['RE10', 'RE14'], 'AC11': ['RE11', 'RE15'], 'AC12': ['RE08', 'RE12'], 'AC13': ['RE09', 'RE13'], 'AC14': ['RE10', 'RE14'], 'AC15': ['RE11', 'RE15'], 'RE08': ['AC08', 'AC12'], 'RE09': ['AC09', 'AC13'], 'RE10': ['AC10', 'AC14'], 'RE11': ['AC11', 'AC15'], 'RE12': ['AC08', 'AC12'], 'RE13': ['AC09', 'AC13'], 'RE14': ['AC10', 'AC14'], 'RE15': ['AC11', 'AC15'] } self.transformation_rules = { 'NI04': ['NI06'], 'NI05': ['NI07'], 'NI06': ['NI04'], 'NI07': ['NI05'], 'EF32': ['EF34', 'EF36', 'EF38'], 'EF33': ['EF35', 'EF37', 'EF39'], 'EF34': ['EF32', 'EF36', 'EF38'], 'EF35': ['EF33', 'EF37', 'EF39'], 'EF36': ['EF32', 'EF34', 'EF38'], 'EF37': ['EF33', 'EF35', 'EF39'], 'EF38': ['EF32', 'EF34', 'EF36'], 'EF39': ['EF33', 'EF35', 'EF37'], 'EF40': ['EF42', 'EF44', 'EF46'], 'EF41': ['EF43', 'EF45', 'EF47'], 'EF42': ['EF40', 'EF44', 'EF46'], 'EF43': ['EF41', 'EF45', 'EF47'], 'EF44': ['EF40', 'EF42', 'EF46'], 'EF45': ['EF41', 'EF43', 'EF47'], 'EF46': ['EF40', 'EF42', 'EF44'], 'EF47': ['EF41', 'EF43', 'EF45'], 'EF48': ['EF50', 'EF52', 'EF54'], 'EF49': ['EF51', 'EF53', 'EF55'], 'EF50': ['EF48', 'EF52', 'EF54'], 'EF51': ['EF49', 'EF53', 'EF55'], 'EF52': ['EF48', 'EF50', 'EF54'], 'EF53': ['EF49', 'EF51', 'EF55'], 'EF54': ['EF48', 'EF50', 'EF52'], 'EF55': ['EF49', 'EF51', 'EF53'], 'EF56': ['EF58', 'EF60', 'EF62'], 'EF57': ['EF59', 'EF61', 'EF63'], 'EF58': ['EF56', 'EF60', 'EF62'], 'EF59': ['EF57', 'EF61', 'EF63'], 'EF60': ['EF56', 'EF58', 'EF62'], 'EF61': ['EF57', 'EF59', 'EF63'], 'EF62': ['EF56', 'EF58', 'EF60'], 'EF63': ['EF57', 'EF59', 'EF61'], 'RE08': ['RE12'], 'RE09': ['RE13'], 'RE10': ['RE14'], 'RE11': ['RE15'], 'RE12': ['RE08'], 'RE13': ['RE09'], 'RE14': ['RE10'], 'RE15': ['RE11'], 'AC08': ['AC12'], 'AC09': ['AC13'], 'AC10': ['AC14'], 'AC11': ['AC15'], 'AC12': ['AC08'], 'AC13': ['AC09'], 'AC14': ['AC10'], 'AC15': ['AC11'], 'RP04': [], 'RP05': [], 'RP06': [], 'RP07': [] } self.valid_interaction_tags = ( self.valid_ni_tags + self.valid_ef_tags + self.valid_rp_tags + self.valid_re_tags + self.valid_ac_tags ) self.setup_gui() def setup_gui(self): canvas_frame = ttk.Frame(self.root) canvas_frame.pack(fill=tk.BOTH, expand=True) self.canvas = tk.Canvas(canvas_frame) v_scrollbar = ttk.Scrollbar(canvas_frame, orient=tk.VERTICAL, command=self.canvas.yview) h_scrollbar = ttk.Scrollbar(canvas_frame, orient=tk.HORIZONTAL, command=self.canvas.xview) self.main_frame = ttk.Frame(self.canvas) self.canvas.configure(yscrollcommand=v_scrollbar.set, xscrollcommand=h_scrollbar.set) self.canvas.create_window((0, 0), window=self.main_frame, anchor="nw") v_scrollbar.pack(side=tk.RIGHT, fill=tk.Y) h_scrollbar.pack(side=tk.BOTTOM, fill=tk.X) self.canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=10, pady=10) self.main_frame.bind("", self._update_scrollregion) self.canvas.bind_all("", self._on_mousewheel) self.canvas.bind_all("", self._on_mousewheel) self.canvas.bind_all("", self._on_mousewheel) # Moral Source ttk.Label(self.main_frame, text="1. What is the moral source of the statement?").pack(anchor=tk.W) self.source_text = ttk.Entry(self.main_frame, width=50) self.source_text.pack(anchor=tk.W, pady=2) ttk.Label(self.main_frame, text="Source Moralithic Tag:").pack(anchor=tk.W) self.source_tag = ttk.Combobox(self.main_frame, values=self.valid_source_tags, state="readonly") self.source_tag.pack(anchor=tk.W, pady=5) self.source_tag.bind("<>", self.update_source_extra) self.extra_source_frame = ttk.Frame(self.main_frame) self.extra_source_label = ttk.Label(self.extra_source_frame, text="") self.extra_source_label.pack(anchor=tk.W) self.extra_source_entry = ttk.Entry(self.extra_source_frame, width=50) self.extra_source_entry.pack(anchor=tk.W, pady=2) self.extra_source_frame.pack_forget() # Moral Statement ttk.Label(self.main_frame, text="2. What is the moral statement?").pack(anchor=tk.W) self.statement_text = tk.Text(self.main_frame, height=3, width=50) self.statement_text.pack(anchor=tk.W, pady=2) self.statement_text.tag_configure("values", foreground="red") self.statement_text.tag_configure("parts", foreground="blue") self.statement_text.tag_configure("limits", foreground="green") ttk.Label(self.main_frame, text="Moral Statement Moralithic Tag:").pack(anchor=tk.W) self.statement_tag = ttk.Combobox(self.main_frame, values=self.valid_statement_tags, state="readonly") self.statement_tag.pack(anchor=tk.W, pady=5) self.statement_tag.bind("<>", self.update_statement_extra) self.extra_statement_frame = ttk.Frame(self.main_frame) self.extra_statement_label = ttk.Label(self.extra_statement_frame, text="") self.extra_statement_label.pack(anchor=tk.W) self.extra_statement_entry = ttk.Entry(self.extra_statement_frame, width=50) self.extra_statement_entry.pack(anchor=tk.W, pady=2) self.extra_statement_frame.pack_forget() # Values ttk.Label(self.main_frame, text="3. What are the moral values of the statement? (comma-separated)").pack(anchor=tk.W) self.values_input = ttk.Entry(self.main_frame, width=50) self.values_input.pack(anchor=tk.W, pady=2) ttk.Button(self.main_frame, text="Add Moral Values and Moralithic Tags", command=self.add_value_items).pack(anchor=tk.W, pady=5) self.values_frame = ttk.Frame(self.main_frame) self.values_frame.pack(anchor=tk.W, pady=2) ttk.Label(self.main_frame, text="Moral Values (for reference):").pack(anchor=tk.W) self.values_text = tk.Text(self.main_frame, height=3, width=50) self.values_text.pack(anchor=tk.W, pady=2) # Necessary Parts ttk.Label(self.main_frame, text="4. Necessary Parts (comma-separated):").pack(anchor=tk.W) self.parts_input = ttk.Entry(self.main_frame, width=50) self.parts_input.pack(anchor=tk.W, pady=2) ttk.Button(self.main_frame, text="Add Necessary Parts", command=self.add_parts).pack(anchor=tk.W, pady=5) self.parts_frame = ttk.Frame(self.main_frame) self.parts_frame.pack(anchor=tk.W, pady=2) ttk.Label(self.main_frame, text="Necessary Parts (for reference):").pack(anchor=tk.W) self.parts_text = tk.Text(self.main_frame, height=3, width=50) self.parts_text.pack(anchor=tk.W, pady=2) # Limitations ttk.Label(self.main_frame, text="5. Limitations (optional, comma-separated):").pack(anchor=tk.W) self.limits_input = ttk.Entry(self.main_frame, width=50) self.limits_input.pack(anchor=tk.W, pady=2) ttk.Button(self.main_frame, text="Add Limitations", command=self.add_limits).pack(anchor=tk.W, pady=5) self.limits_frame = ttk.Frame(self.main_frame) self.limits_frame.pack(anchor=tk.W, pady=2) ttk.Label(self.main_frame, text="Limitations (for reference):").pack(anchor=tk.W) self.limits_text = tk.Text(self.main_frame, height=3, width=50) self.limits_text.pack(anchor=tk.W, pady=5) # Displayments ttk.Label(self.main_frame, text="6. What displayment was used to source the statement?").pack(anchor=tk.W) self.displayment_input = ttk.Entry(self.main_frame, width=50) self.displayment_input.pack(anchor=tk.W, pady=2) ttk.Label(self.main_frame, text="Displayment Moralithic Tag:").pack(anchor=tk.W) self.displayment_tag = ttk.Combobox(self.main_frame, values=self.valid_displayment_tags, state="readonly") self.displayment_tag.pack(anchor=tk.W, pady=5) self.values_text.bind("", self.update_statement_colors) self.parts_text.bind("", self.update_statement_colors) self.limits_text.bind("", self.update_statement_colors) # Interactors ttk.Label(self.main_frame, text="7. Interactors (optional):").pack(anchor=tk.W, pady=(10, 0)) # Non-interactors ni_frame = ttk.LabelFrame(self.main_frame, text="Non-interactors") ni_frame.pack(anchor=tk.W, pady=5, padx=5, fill=tk.X) ttk.Button(ni_frame, text="Add Non-interactor", command=self.add_non_interactor).pack(anchor=tk.W, pady=2) self.non_interactors_container = ttk.Frame(ni_frame) self.non_interactors_container.pack(anchor=tk.W, pady=2) # Enforcers ef_frame = ttk.LabelFrame(self.main_frame, text="Enforcers") ef_frame.pack(anchor=tk.W, pady=5, padx=5, fill=tk.X) ttk.Button(ef_frame, text="Add Enforcer", command=self.add_enforcer).pack(anchor=tk.W, pady=2) self.enforcers_container = ttk.Frame(ef_frame) self.enforcers_container.pack(anchor=tk.W, pady=2) # Repeaters rp_frame = ttk.LabelFrame(self.main_frame, text="Repeaters") rp_frame.pack(anchor=tk.W, pady=5, padx=5, fill=tk.X) ttk.Button(rp_frame, text="Add Repeater", command=self.add_repeater).pack(anchor=tk.W, pady=2) self.repeaters_container = ttk.Frame(rp_frame) self.repeaters_container.pack(anchor=tk.W, pady=2) # Rejecters re_frame = ttk.LabelFrame(self.main_frame, text="Rejecters") re_frame.pack(anchor=tk.W, pady=5, padx=5, fill=tk.X) ttk.Button(re_frame, text="Add Rejecter", command=self.add_rejecter).pack(anchor=tk.W, pady=2) self.rejecters_container = ttk.Frame(re_frame) self.rejecters_container.pack(anchor=tk.W, pady=2) # Accepters ac_frame = ttk.LabelFrame(self.main_frame, text="Accepters") ac_frame.pack(anchor=tk.W, pady=5, padx=5, fill=tk.X) ttk.Button(ac_frame, text="Add Accepter", command=self.add_accepter).pack(anchor=tk.W, pady=2) self.accepters_container = ttk.Frame(ac_frame) self.accepters_container.pack(anchor=tk.W, pady=2) ttk.Button(self.main_frame, text="Generate Moralithic Chain", command=self.generate_chain).pack(anchor=tk.W, pady=10) self.result_text = tk.Text(self.main_frame, height=10, width=80) self.result_text.pack(anchor=tk.W, pady=5) self.result_text.tag_configure("values", foreground="red") self.result_text.tag_configure("parts", foreground="blue") self.result_text.tag_configure("limits", foreground="green") self.result_text.tag_configure("bold", font=("TkDefaultFont", 9, "bold")) button_frame = ttk.Frame(self.main_frame) button_frame.pack(anchor=tk.W, pady=5, fill=tk.X) ttk.Button(button_frame, text="Copy Chain", command=self.copy_chain).pack(side=tk.LEFT, padx=5) ttk.Button(button_frame, text="Add Footnotes", command=self.setup_footnotes).pack(side=tk.LEFT, padx=5) self.footnote_container = ttk.Frame(self.main_frame) self.footnote_container.pack(anchor=tk.W, pady=5, fill=tk.X) def update_source_extra(self, event=None): source_tag = self.source_tag.get() if source_tag in self.function_source_tags: self.extra_source_label.config(text="What is the moral function of the moral authoritative source?") self.extra_source_frame.pack(anchor=tk.W, pady=5) elif source_tag in self.process_source_tags: self.extra_source_label.config(text="What is the moral statement production process of the moral non-authoritative source?") self.extra_source_frame.pack(anchor=tk.W, pady=5) else: self.extra_source_frame.pack_forget() def update_statement_extra(self, event=None): st_tag = self.statement_tag.get() if st_tag in self.function_statement_tags: self.extra_statement_label.config(text="What is the moral function of the moral statement?") self.extra_statement_frame.pack(anchor=tk.W, pady=5) elif st_tag in self.process_statement_tags: self.extra_statement_label.config(text="What is the moral statement production process of the moral statement?") self.extra_statement_frame.pack(anchor=tk.W, pady=5) else: self.extra_statement_frame.pack_forget() def add_value_items(self): try: values_str = self.values_input.get().strip() if not values_str: messagebox.showerror("Error", "Please enter values before adding tags.") return value_list = [v.strip() for v in values_str.split(",") if v.strip()] if not value_list: messagebox.showerror("Error", "No valid values provided.") return for widget in self.values_frame.winfo_children(): widget.destroy() self.value_items.clear() for i, value in enumerate(value_list, 1): frame = ttk.Frame(self.values_frame) frame.pack(anchor=tk.W, pady=2) ttk.Label(frame, text=f"Value {i}: {value}").pack(side=tk.LEFT) tag = ttk.Combobox(frame, values=self.valid_value_tags, state="readonly", width=10) tag.pack(side=tk.LEFT, padx=5) self.value_items.append((value, tag)) self.values_text.delete("1.0", tk.END) self.values_text.insert("1.0", ", ".join(f"{i}. {v}" for i, v in enumerate(value_list, 1))) self.canvas.configure(scrollregion=self.canvas.bbox("all")) self.update_statement_colors() self.values_input.delete(0, tk.END) except Exception as e: messagebox.showerror("Error", f"Failed to add moral values: {str(e)}") def add_parts(self): try: parts = self.parts_input.get().strip() if not parts: messagebox.showerror("Error", "Please enter necessary parts before adding.") return parts_list = [p.strip() for p in parts.split(",") if p.strip()] if not parts_list: messagebox.showerror("Error", "No valid necessary parts provided.") return for widget in self.parts_frame.winfo_children(): widget.destroy() for i, part in enumerate(parts_list, 1): frame = ttk.Frame(self.parts_frame) frame.pack(anchor=tk.W, pady=2) ttk.Label(frame, text=f"Part {i}: {part}").pack(side=tk.LEFT) self.parts_text.delete("1.0", tk.END) self.parts_text.insert("1.0", ", ".join(f"{i}. {p}" for i, p in enumerate(parts_list, 1))) self.canvas.configure(scrollregion=self.canvas.bbox("all")) self.update_statement_colors() except Exception as e: messagebox.showerror("Error", f"Failed to add parts: {str(e)}") def add_limits(self): try: limits = self.limits_input.get().strip() if not limits: return limits_list = [l.strip() for l in limits.split(",") if l.strip()] if not limits_list: return for widget in self.limits_frame.winfo_children(): widget.destroy() for i, limit in enumerate(limits_list, 1): frame = ttk.Frame(self.limits_frame) frame.pack(anchor=tk.W, pady=2) ttk.Label(frame, text=f"Limitation {i}: {limit}").pack(side=tk.LEFT) self.limits_text.delete("1.0", tk.END) self.limits_text.insert("1.0", ", ".join(f"{i}. {l}" for i, l in enumerate(limits_list, 1))) self.canvas.configure(scrollregion=self.canvas.bbox("all")) self.update_statement_colors() except Exception as e: messagebox.showerror("Error", f"Failed to add limitations: {str(e)}") def add_non_interactor(self): frame = ttk.Frame(self.non_interactors_container) entity = ttk.Entry(frame, width=40) entity.pack(side=tk.LEFT, padx=(0, 5)) tag = ttk.Combobox(frame, values=self.valid_ni_tags, state="readonly", width=10) tag.pack(side=tk.LEFT) remove_btn = ttk.Button(frame, text="Remove", command=lambda f=frame: self.remove_item(f, self.non_interactor_items)) remove_btn.pack(side=tk.LEFT, padx=5) frame.pack(anchor=tk.W, pady=1) self.non_interactor_items.append({'entity': entity, 'tag': tag}) self.canvas.configure(scrollregion=self.canvas.bbox("all")) def add_enforcer(self): ef_main_frame = ttk.Frame(self.enforcers_container) entity_label = ttk.Label(ef_main_frame, text="Entity:") entity_label.pack(anchor=tk.W) entity = ttk.Entry(ef_main_frame, width=50) entity.pack(anchor=tk.W) tag_label = ttk.Label(ef_main_frame, text="Tag:") tag_label.pack(anchor=tk.W) tag = ttk.Combobox(ef_main_frame, values=self.valid_ef_tags, state="readonly") tag.pack(anchor=tk.W) means_frame = ttk.Frame(ef_main_frame) means_frame.pack(anchor=tk.W, pady=2) add_means_btn = ttk.Button(means_frame, text="Add Means", command=lambda idx=len(self.enforcer_items): self.add_means_to_enforcer(idx)) add_means_btn.pack(anchor=tk.W) means_container = ttk.Frame(means_frame) means_container.pack(anchor=tk.W) remove_btn = ttk.Button(ef_main_frame, text="Remove Enforcer", command=lambda f=ef_main_frame: self.remove_item(f, self.enforcer_items)) remove_btn.pack(anchor=tk.W, pady=2) ef_main_frame.pack(anchor=tk.W, pady=5, fill=tk.X) self.enforcer_items.append({'entity': entity, 'tag': tag, 'means': [], 'means_container': means_container}) self.add_means_to_enforcer(len(self.enforcer_items) - 1) self.canvas.configure(scrollregion=self.canvas.bbox("all")) def add_means_to_enforcer(self, enforcer_idx): item = self.enforcer_items[enforcer_idx] m_frame = ttk.Frame(item['means_container']) mean_entry = ttk.Entry(m_frame, width=40) mean_entry.pack(side=tk.LEFT, padx=(0, 5)) mean_tag = ttk.Combobox(m_frame, values=self.valid_means_tags, state="readonly", width=10) mean_tag.pack(side=tk.LEFT) remove_m_btn = ttk.Button(m_frame, text="Remove", command=lambda mf=m_frame: self.remove_subitem(mf, item['means'])) remove_m_btn.pack(side=tk.LEFT, padx=5) m_frame.pack(anchor=tk.W, pady=1) item['means'].append({'mean_entry': mean_entry, 'mean_tag': mean_tag}) self.canvas.configure(scrollregion=self.canvas.bbox("all")) def add_repeater(self): rp_main_frame = ttk.Frame(self.repeaters_container) entity_label = ttk.Label(rp_main_frame, text="Entity:") entity_label.pack(anchor=tk.W) entity = ttk.Entry(rp_main_frame, width=50) entity.pack(anchor=tk.W) tag_label = ttk.Label(rp_main_frame, text="Tag:") tag_label.pack(anchor=tk.W) tag = ttk.Combobox(rp_main_frame, values=self.valid_rp_tags, state="readonly") tag.pack(anchor=tk.W) dp_frame = ttk.Frame(rp_main_frame) dp_frame.pack(anchor=tk.W, pady=2) add_dp_btn = ttk.Button(dp_frame, text="Add Displayment", command=lambda idx=len(self.repeater_items): self.add_dp_to_repeater(idx)) add_dp_btn.pack(anchor=tk.W) dp_container = ttk.Frame(dp_frame) dp_container.pack(anchor=tk.W) remove_btn = ttk.Button(rp_main_frame, text="Remove Repeater", command=lambda f=rp_main_frame: self.remove_item(f, self.repeater_items)) remove_btn.pack(anchor=tk.W, pady=2) rp_main_frame.pack(anchor=tk.W, pady=5, fill=tk.X) self.repeater_items.append({'entity': entity, 'tag': tag, 'displayments': [], 'dp_container': dp_container}) self.add_dp_to_repeater(len(self.repeater_items) - 1) self.canvas.configure(scrollregion=self.canvas.bbox("all")) def add_dp_to_repeater(self, repeater_idx): item = self.repeater_items[repeater_idx] dp_frame = ttk.Frame(item['dp_container']) dp_entry = ttk.Entry(dp_frame, width=40) dp_entry.pack(side=tk.LEFT, padx=(0, 5)) dp_tag = ttk.Combobox(dp_frame, values=self.valid_displayment_tags, state="readonly", width=10) dp_tag.pack(side=tk.LEFT) remove_dp_btn = ttk.Button(dp_frame, text="Remove", command=lambda df=dp_frame: self.remove_subitem(df, item['displayments'])) remove_dp_btn.pack(side=tk.LEFT, padx=5) dp_frame.pack(anchor=tk.W, pady=1) item['displayments'].append({'dp_entry': dp_entry, 'dp_tag': dp_tag}) self.canvas.configure(scrollregion=self.canvas.bbox("all")) def add_rejecter(self): frame = ttk.Frame(self.rejecters_container) entity = ttk.Entry(frame, width=40) entity.pack(side=tk.LEFT, padx=(0, 5)) tag = ttk.Combobox(frame, values=self.valid_re_tags, state="readonly", width=10) tag.pack(side=tk.LEFT) remove_btn = ttk.Button(frame, text="Remove", command=lambda f=frame: self.remove_item(f, self.rejecter_items)) remove_btn.pack(side=tk.LEFT, padx=5) frame.pack(anchor=tk.W, pady=1) self.rejecter_items.append({'entity': entity, 'tag': tag}) self.canvas.configure(scrollregion=self.canvas.bbox("all")) def add_accepter(self): frame = ttk.Frame(self.accepters_container) entity = ttk.Entry(frame, width=40) entity.pack(side=tk.LEFT, padx=(0, 5)) tag = ttk.Combobox(frame, values=self.valid_ac_tags, state="readonly", width=10) tag.pack(side=tk.LEFT) remove_btn = ttk.Button(frame, text="Remove", command=lambda f=frame: self.remove_item(f, self.accepter_items)) remove_btn.pack(side=tk.LEFT, padx=5) frame.pack(anchor=tk.W, pady=1) self.accepter_items.append({'entity': entity, 'tag': tag}) self.canvas.configure(scrollregion=self.canvas.bbox("all")) def remove_item(self, frame, items_list): for i, item in enumerate(items_list): if item['entity'] in frame.winfo_children() or frame.winfo_children(): del items_list[i] break frame.destroy() self.canvas.configure(scrollregion=self.canvas.bbox("all")) def remove_subitem(self, sub_frame, sub_list): for i, sub in enumerate(sub_list): if ('mean_entry' in sub and sub['mean_entry'] in sub_frame.winfo_children()) or ('dp_entry' in sub and sub['dp_entry'] in sub_frame.winfo_children()): del sub_list[i] break sub_frame.destroy() self.canvas.configure(scrollregion=self.canvas.bbox("all")) def _update_scrollregion(self, event): self.canvas.configure(scrollregion=self.canvas.bbox("all")) def _on_mousewheel(self, event): if event.delta: self.canvas.yview_scroll(int(-1 * (event.delta / 120)), "units") elif event.num == 4: self.canvas.yview_scroll(-1, "units") elif event.num == 5: self.canvas.yview_scroll(1, "units") def copy_chain(self): try: chain_text = self.result_text.get("1.0", tk.END).strip() if not chain_text: messagebox.showwarning("Warning", "No chain to copy. Please generate a chain first.") return self.root.clipboard_clear() self.root.clipboard_append(chain_text) self.root.update() messagebox.showinfo("Success", "Chain copied to clipboard!") except Exception as e: messagebox.showerror("Error", f"Failed to copy chain: {str(e)}") def update_statement_colors(self, event=None): try: statement = self.statement_text.get("1.0", tk.END).strip() values = self.values_text.get("1.0", tk.END).strip() parts = self.parts_text.get("1.0", tk.END).strip() limits = self.limits_text.get("1.0", tk.END).strip() self.statement_text.tag_remove("values", "1.0", tk.END) self.statement_text.tag_remove("parts", "1.0", tk.END) self.statement_text.tag_remove("limits", "1.0", tk.END) value_list = [v.strip() for v in values.split(",") if v.strip()] value_list = [re.sub(r'^\s*[0-9]+\.\s*', '', v).strip() for v in value_list] parts_list = [p.strip() for p in parts.split(",") if p.strip()] parts_list = [re.sub(r'^\s*[0-9]+\.\s*', '', p).strip() for p in parts_list] limits_list = [l.strip() for l in limits.split(",") if l.strip()] limits_list = [re.sub(r'^\s*[0-9]+\.\s*', '', l).strip() for l in limits_list] components = [(v, "values") for v in value_list] + [(p, "parts") for p in parts_list] + [(l, "limits") for l in limits_list] component_positions = [] for comp, tag in components: start = statement.find(comp) if start != -1: component_positions.append((start, start + len(comp), comp, tag)) component_positions.sort() for start, end, comp, tag in component_positions: self.statement_text.tag_add(tag, f"1.0+{start}c", f"1.0+{start + len(comp)}c") return component_positions except Exception as e: self.result_text.configure(state="normal") self.result_text.delete("1.0", tk.END) self.result_text.insert("1.0", f"Error in coloring components: {str(e)}") self.result_text.configure(state="disabled") return [] def validate_chain(self): try: # Source and statement compatibility source_tag = self.source_tag.get() st_tag = self.statement_tag.get() if not source_tag or not st_tag: return False, "Source and statement tags are required." expected_statement = self.valid_source_statement_pairs.get(source_tag) if not expected_statement: return False, f"Source tag '{source_tag}' is not a valid source tag. Valid tags are: {', '.join(self.valid_source_tags)}." if st_tag != expected_statement: return False, f"Statement tag '{st_tag}' is incompatible with source tag '{source_tag}'. Expected statement tag: '{expected_statement}'." # Function/process for source and statement extra_source = self.extra_source_entry.get().strip() if source_tag in self.function_source_tags and not extra_source: return False, f"Source tag '{source_tag}' requires a moral function, but none was provided." if source_tag in self.process_source_tags and not extra_source: return False, f"Source tag '{source_tag}' requires a moral statement production process, but none was provided." if extra_source and source_tag not in (self.function_source_tags + self.process_source_tags): return False, f"Source tag '{source_tag}' does not support a moral function or process, but one was provided: '{extra_source}'." extra_statement = self.extra_statement_entry.get().strip() if st_tag in self.function_statement_tags and not extra_statement: return False, f"Statement tag '{st_tag}' requires a moral function, but none was provided." if st_tag in self.process_statement_tags and not extra_statement: return False, f"Statement tag '{st_tag}' requires a moral statement production process, but none was provided." if extra_statement and st_tag not in (self.function_statement_tags + self.process_statement_tags): return False, f"Statement tag '{st_tag}' does not support a moral function or process, but one was provided: '{extra_statement}'." # Values validation value_tags = [item[1].get() for item in self.value_items] if not value_tags or any(not vt for vt in value_tags): return False, "At least one moral value with a tag is required." valid_values = self.valid_statement_value_pairs.get(st_tag, {}).get('values', []) if not valid_values: return False, f"Statement tag '{st_tag}' is not a valid statement tag. Valid tags are: {', '.join(self.valid_statement_tags)}." required_value = self.valid_statement_value_pairs.get(st_tag, {}).get('required', '') for vt in value_tags: if vt not in valid_values: return False, f"Value tag '{vt}' is invalid for statement '{st_tag}'. Valid value tags are: {', '.join(valid_values)}." if required_value and required_value not in value_tags: return False, f"Required value tag '{required_value}' is missing for statement '{st_tag}'." # Displayment validation displayment_desc = self.displayment_input.get().strip() displayment_tag = self.displayment_tag.get() if displayment_desc and not displayment_tag: return False, "Displayment tag must be provided if a displayment description is specified." if displayment_tag and not displayment_desc: return False, f"Displayment tag '{displayment_tag}' provided without a description." if displayment_tag and displayment_tag not in self.valid_displayment_tags: return False, f"Invalid displayment tag '{displayment_tag}'. Valid tags are: {', '.join(self.valid_displayment_tags)}." # Interactor validation for items, type_name, valid_tags, has_subcomponents in [ (self.non_interactor_items, "Non-interactor", self.valid_ni_tags, False), (self.enforcer_items, "Enforcers", self.valid_ef_tags, True), (self.repeater_items, "Repeaters", self.valid_rp_tags, True), (self.rejecter_items, "Rejecters", self.valid_re_tags, False), (self.accepter_items, "Accepters", self.valid_ac_tags, False) ]: for item in items: entity = item['entity'].get().strip() tag = item['tag'].get() if not entity and tag: return False, f"Entity description required for {type_name} with tag '{tag}'." if entity and not tag: return False, f"Tag required for {type_name} '{entity}'." if tag and tag not in valid_tags: return False, f"Invalid {type_name} tag '{tag}'. Valid tags are: {', '.join(valid_tags)}." if has_subcomponents and entity: subcomponents = item.get('means', []) or item.get('displayments', []) if not subcomponents: return False, f"{type_name} '{entity} ({tag})' requires at least one subcomponent (Means for Enforcers, Displayments for Repeaters)." for sub in subcomponents: sub_desc = sub.get('mean_entry', sub.get('dp_entry')).get().strip() sub_tag = sub.get('mean_tag', sub.get('dp_tag')).get() if not sub_desc and sub_tag: return False, f"Subcomponent description required for {type_name} '{entity}' subcomponent with tag '{sub_tag}'." if sub_desc and not sub_tag: return False, f"Subcomponent tag required for {type_name} '{entity}' subcomponent '{sub_desc}'." valid_sub_tags = self.valid_means_tags if type_name == "Enforcers" else self.valid_displayment_tags if sub_tag and sub_tag not in valid_sub_tags: return False, f"Invalid {type_name} subcomponent tag '{sub_tag}' for '{entity}'. Valid tags are: {', '.join(valid_sub_tags)}." elif (entity and 'means' in item and item['means']) or (entity and 'displayments' in item and item['displayments']): return False, f"{type_name} '{entity} ({tag})' does not support subcomponents, but some were provided." # Required INI tags has_required_value = any(vt in self.required_values for vt in value_tags) if has_required_value: all_ini_tags = ( [item['tag'].get() for item in self.non_interactor_items if item['tag'].get()] + [item['tag'].get() for item in self.enforcer_items if item['tag'].get()] + [item['tag'].get() for item in self.repeater_items if item['tag'].get()] + [item['tag'].get() for item in self.rejecter_items if item['tag'].get()] + [item['tag'].get() for item in self.accepter_items if item['tag'].get()] ) if not any(tag in self.required_ini_tags for tag in all_ini_tags): return False, f"Chain requires at least one required INI tag due to specific moral values ({', '.join(value_tags)}). Valid INI tags are: {', '.join(self.required_ini_tags)}." # Accepter compatibility for item in self.accepter_items: ac_entity = item['entity'].get().strip() ac_tag = item['tag'].get() if ac_entity and ac_tag in ['AC11', 'AC12', 'AC13', 'AC14', 'AC15']: compat_tags = self.ac_compat.get(ac_tag, []) if not compat_tags: return False, f"Accepter tag '{ac_tag}' is not recognized in compatibility rules." found = False for ef_item in self.enforcer_items: ef_entity = ef_item['entity'].get().strip() ef_tag = ef_item['tag'].get() if ef_entity == ac_entity and ef_tag in compat_tags: found = True break if not found: for rp_item in self.repeater_items: rp_entity = rp_item['entity'].get().strip() rp_tag = rp_item['tag'].get() if rp_entity == ac_entity and rp_tag in compat_tags: found = True break if not found: return False, f"Accepter '{ac_entity}' ({ac_tag}) requires a compatible Enforcer or Repeater with the same entity text and a tag in: {', '.join(compat_tags)}." return True, "" except Exception as e: return False, f"Validation error: {str(e)}" def apply_formatting(self, chain_text): component_positions = self.update_statement_colors() statement_start = chain_text.find("moral statement [") + len("moral statement [") statement_end = chain_text.find("]", statement_start) for start, end, comp, tag in component_positions: self.result_text.tag_add(tag, f"1.0+{statement_start + start}c", f"1.0+{statement_start + end}c") keywords = ["moral source", "moral statement", "Moral Values", "Displayments", "Non-interactor", "Enforcers", "Repeaters", "Rejecters", "Accepters", "Means"] for keyword in keywords: start_pos = 0 while True: start_pos = chain_text.find(keyword, start_pos) if start_pos == -1: break self.result_text.tag_add("bold", f"1.0+{start_pos}c", f"1.0+{start_pos + len(keyword)}c") start_pos += len(keyword) def generate_chain(self): try: if not self.source_text.get().strip() or not self.statement_text.get("1.0", tk.END).strip(): messagebox.showerror("Error", "Source identity and statement are required.") return valid, error = self.validate_chain() if not valid: self.result_text.delete("1.0", tk.END) self.result_text.insert("1.0", f"Error: {error}") return source_identity = self.source_text.get().strip() source_tag = self.source_tag.get() extra_source = self.extra_source_entry.get().strip() statement = self.statement_text.get("1.0", tk.END).strip() st_tag = self.statement_tag.get() extra_statement = self.extra_statement_entry.get().strip() value_list = [item[0] for item in self.value_items] value_tags = [item[1].get() for item in self.value_items] displayment_desc = self.displayment_input.get().strip() displayment_tag = self.displayment_tag.get() # Build source part source_part = f"moral source [{source_identity} ({source_tag})" if extra_source: if source_tag in self.function_source_tags: source_part += f", moral function ({extra_source})" elif source_tag in self.process_source_tags: source_part += f", moral statement production process ({extra_source})" source_part += "]" # Build statement part statement_part = f"- moral statement [{statement} ({st_tag})" if extra_statement: if st_tag in self.function_statement_tags: statement_part += f", moral function ({extra_statement})" elif st_tag in self.process_statement_tags: statement_part += f", moral statement production process ({extra_statement})" values_str = ". ".join([f"{v} ({vt})" for v, vt in zip(value_list, value_tags)]) statement_part += f", Moral Values ({values_str})]" chain = source_part + statement_part # Displayments if displayment_desc and displayment_tag: chain += f"- Displayments [{displayment_desc} ({displayment_tag})]" # Non-interactors if self.non_interactor_items: ni_str = ". ".join([f"{item['entity'].get().strip()} ({item['tag'].get()})" for item in self.non_interactor_items]) chain += f"- Non-interactor [{ni_str}]" # Enforcers if self.enforcer_items: ef_str = ". ".join([ f"{item['entity'].get().strip()} ({item['tag'].get()})" + (f", Means ({'. '.join([f"{m['mean_entry'].get().strip()} ({m['mean_tag'].get()})" for m in item['means']])})" if item['means'] else "") for item in self.enforcer_items ]) chain += f"- Enforcers [{ef_str}]" # Repeaters if self.repeater_items: rp_str = ". ".join([ f"{item['entity'].get().strip()} ({item['tag'].get()})" + (f", Displayments ({'. '.join([f"{d['dp_entry'].get().strip()} ({d['dp_tag'].get()})" for d in item['displayments']])})" if item['displayments'] else "") for item in self.repeater_items ]) chain += f"- Repeaters [{rp_str}]" # Rejecters if self.rejecter_items: re_str = ". ".join([f"{item['entity'].get().strip()} ({item['tag'].get()})" for item in self.rejecter_items]) chain += f"- Rejecters [{re_str}]" # Accepters if self.accepter_items: ac_str = ". ".join([f"{item['entity'].get().strip()} ({item['tag'].get()})" for item in self.accepter_items]) chain += f"- Accepters [{ac_str}]" self.result_text.delete("1.0", tk.END) chain_text = chain self.result_text.insert("1.0", chain_text) self.apply_formatting(chain_text) self.chain_text = chain_text except Exception as e: self.result_text.delete("1.0", tk.END) self.result_text.insert("1.0", f"Error: Unexpected issue occurred: {str(e)}") def split_top_level(self, s, delimiter=','): """Split string by delimiter outside of balanced parentheses.""" parts = [] current = '' paren_count = 0 i = 0 while i < len(s): char = s[i] current += char if char == '(': paren_count += 1 elif char == ')': paren_count -= 1 elif char == delimiter and paren_count == 0: parts.append(current[:-1].strip()) # Remove the delimiter current = '' i += 1 if current: parts.append(current.strip()) return parts def extract_balanced(self, s, start_pos): """Extract balanced substring starting from start_pos until matching closing parenthesis.""" paren_count = 0 i = start_pos start_char = s[i] if i < len(s) else None if start_char != '(': return '' paren_count = 1 # Start with the opening ( i += 1 while i < len(s) and paren_count > 0: char = s[i] if char == '(': paren_count += 1 elif char == ')': paren_count -= 1 i += 1 return s[start_pos:i] def parse_entity_tag(self, part): """Parse 'entity (tag)' from a part.""" match = re.match(r'(.*?) \s* \( \s* ([A-Z0-9]+) \s* \) \s* $', part.strip(), re.X) if match: return match.group(1).strip(), match.group(2).strip() return None, None def parse_optional_part(self, part, key_word, tag_set): """Parse optional part like 'Means (text (tag))'.""" part_lower = part.lower().strip() if key_word.lower() not in part_lower: return '' # Find the opening ( after key_word start = part_lower.find('(') if start == -1: return '' content = self.extract_balanced(part, start) if not content: return '' # Parse content as text (tag) match = re.match(r'\s* (.*?) \s* \( \s* ([A-Z0-9]+) \s* \) \s* $', content[1:-1].strip(), re.X) # Skip outer ( ) if match: text = match.group(1).strip() tag = match.group(2).strip() if tag in tag_set: return f"{text} ({tag})" return '' def parse_moralithic_chain(self, chain_text): if chain_text.startswith("Generated Chain: "): chain_text = chain_text.replace("Generated Chain: ", "") sections = re.split(r'-\s*', chain_text) extracted = {'interactors': []} for section in sections: section = section.strip() if not section: continue if section.startswith("moral source ["): source_match = re.match(r"moral source \[\s*(.*?) \s*\((.*?)\)\s*(?:,\s*(moral function|moral statement production process)\s*\((.*?)\))?\s*\]", section) if source_match: extracted['source_text'] = source_match.group(1) extracted['source_tag'] = source_match.group(2) extracted['extra_source_type'] = source_match.group(3) extracted['extra_source'] = source_match.group(4) else: return None # Failed to parse source elif section.startswith("moral statement ["): # Capture the entire content inside [ ] content_match = re.match(r"moral statement \[(.*?)\]", section, re.DOTALL) if not content_match: return None content = content_match.group(1).strip() # Split by top-level , parts = self.split_top_level(content) if len(parts) < 1: return None # First part: statement_text (tag) st_match = self.parse_entity_tag(parts[0]) if not st_match[0] or not st_match[1]: return None extracted['statement_text'] = st_match[0] extracted['statement_tag'] = st_match[1] # Second part if present: extra statement if len(parts) > 1: extra_part = parts[1].strip() extra_match = re.match(r"(moral function|moral statement production process) \s* \( \s* (.*?) \s* \) \s* $", extra_part, re.X) if extra_match: extracted['extra_statement_type'] = extra_match.group(1) extracted['extra_statement'] = extra_match.group(2) # Subsequent parts: Moral Values, Necessary Parts, Limitations moral_values = '' necessary_parts = '' limitations = '' for p in parts[2:]: p_lower = p.lower().strip() if 'moral values' in p_lower: start = p.find('(') if start != -1: moral_values = self.extract_balanced(p, start) elif 'necessary parts' in p_lower: start = p.find('(') if start != -1: necessary_parts = self.extract_balanced(p, start) elif 'limitations' in p_lower: start = p.find('(') if start != -1: limitations = self.extract_balanced(p, start) extracted['moral_values'] = moral_values extracted['necessary_parts'] = necessary_parts extracted['limitations'] = limitations elif section.startswith("Displayments ["): displayments_match = re.match(r"Displayments \[\s*(.*?)\s*\]", section) if displayments_match: extracted['displayments'] = displayments_match.group(1) elif section.startswith("Non-interactor ["): ni_match = re.match(r"Non-interactor \[\s*(.*?)\s*\]", section) if ni_match: ni_str = ni_match.group(1) ni_items = re.findall(r"(\d+\.\s*)?(.*?) \s*\(\s*([A-Z0-9]+)\s*\)", ni_str) for _, entity, tag in ni_items: extracted['interactors'].append({'type': 'Non-interactor', 'entity': entity.strip(), 'tag': tag.strip(), 'transformations': []}) elif section.startswith("Enforcers ["): ef_match = re.match(r"Enforcers \[\s*(.*?)\s*\]", section) if ef_match: ef_content = ef_match.group(1).strip() # Split top-level , ef_parts = self.split_top_level(ef_content) for part in ef_parts: # Parse entity (tag) entity, tag = self.parse_entity_tag(part) if not entity or not tag: continue means = '' # Check if this part has Means if ', Means' in part: means_part = part.split(', Means', 1)[1].strip() start = means_part.find('(') if start != -1: means_content = self.extract_balanced(means_part, start) if means_content: # Parse text (tag) inside means_content m_text, m_tag = self.parse_entity_tag(means_content[1:-1]) # Skip outer ( ) if m_text and m_tag and m_tag in self.valid_means_tags: means = f"{m_text} ({m_tag})" extracted['interactors'].append({'type': 'Enforcer', 'entity': entity, 'tag': tag, 'means': means, 'transformations': []}) elif section.startswith("Repeaters ["): rp_match = re.match(r"Repeaters \[\s*(.*?)\s*\]", section) if rp_match: rp_content = rp_match.group(1).strip() # Split top-level , rp_parts = self.split_top_level(rp_content) for part in rp_parts: # Parse entity (tag) entity, tag = self.parse_entity_tag(part) if not entity or not tag: continue displayments = '' # Check if this part has Displayments if ', Displayments' in part: dp_part = part.split(', Displayments', 1)[1].strip() start = dp_part.find('(') if start != -1: dp_content = self.extract_balanced(dp_part, start) if dp_content: # Parse text (tag) inside dp_content d_text, d_tag = self.parse_entity_tag(dp_content[1:-1]) # Skip outer ( ) if d_text and d_tag and d_tag in self.valid_displayment_tags: displayments = f"{d_text} ({d_tag})" extracted['interactors'].append({'type': 'Repeater', 'entity': entity, 'tag': tag, 'displayments': displayments, 'transformations': []}) elif section.startswith("Rejecters ["): re_match = re.match(r"Rejecters \[\s*(.*?)\s*\]", section) if re_match: re_str = re_match.group(1) re_items = re.findall(r"(\d+\.\s*)?(.*?) \s*\(\s*([A-Z0-9]+)\s*\)", re_str) for _, entity, tag in re_items: extracted['interactors'].append({'type': 'Rejecter', 'entity': entity.strip(), 'tag': tag.strip(), 'transformations': []}) elif section.startswith("Accepters ["): ac_match = re.match(r"Accepters \[\s*(.*?)\s*\]", section) if ac_match: ac_str = ac_match.group(1) ac_items = re.findall(r"(\d+\.\s*)?(.*?) \s*\(\s*([A-Z0-9]+)\s*\)", ac_str) for _, entity, tag in ac_items: extracted['interactors'].append({'type': 'Accepter', 'entity': entity.strip(), 'tag': tag.strip(), 'transformations': []}) if 'statement_tag' not in extracted or not extracted['statement_tag']: return None return extracted def validate_extracted_moralithic(self, extracted): statement_tag = extracted.get('statement_tag', '') source_tag = extracted.get('source_tag', '') if not statement_tag: return False, "No statement tag found. Check the moral statement section." if not source_tag: return False, "No source tag found. Check the moral source section." if statement_tag not in self.valid_statement_source_pairs: return False, f"Invalid statement tag '{statement_tag}'." if self.valid_statement_source_pairs[statement_tag] != source_tag: return False, f"Incompatible statement tag '{statement_tag}' with source tag '{source_tag}'. Expected '{self.valid_statement_source_pairs[statement_tag]}'." moral_values = extracted.get('moral_values', '') value_tags = [] if moral_values: # Extract tags from values content value_items = re.findall(r'\d*\.?\s*(.*?)\s*\(\s*([A-Z0-9]+)\s*\)', moral_values) value_tags = [v[1] for v in value_items] if not value_tags: return False, "No valid value tags extracted from moral values section. Check the format inside Moral Values (...)." if not value_tags: return False, "No moral values found. At least one value is required." valid_values = self.valid_statement_value_pairs.get(statement_tag, {}).get('values', []) invalid_values = [v for v in value_tags if v not in valid_values] if invalid_values: return False, f"Invalid value tags for statement '{statement_tag}': {', '.join(invalid_values)}. Allowed: {', '.join(valid_values)}" for idx, inter in enumerate(extracted['interactors']): tag = inter['tag'] if not tag: return False, f"No tag found for {inter['type']} '{inter['entity']}' at position {idx+1}." if tag not in self.valid_interaction_tags: return False, f"Invalid interaction tag '{tag}' for {inter['type']} '{inter['entity']}'." if inter['type'] == 'Enforcer' and inter.get('means'): means_items = re.findall(r'\d*\.?\s*(.*?)\s*\(\s*([A-Z0-9]+)\s*\)', inter['means']) means_tags = [m[1] for m in means_items] if not means_tags: return False, f"No valid means tags extracted for Enforcer '{inter['entity']}'. Check the Means format." invalid_means = [mt for mt in means_tags if mt not in self.valid_means_tags] if invalid_means: return False, f"Invalid means tags for Enforcer '{inter['entity']}': {', '.join(invalid_means)}. Allowed: {', '.join(self.valid_means_tags)}" if inter['type'] == 'Repeater' and inter.get('displayments'): dp_items = re.findall(r'\d*\.?\s*(.*?)\s*\(\s*([A-Z0-9]+)\s*\)', inter['displayments']) dp_tags = [d[1] for d in dp_items] if not dp_tags: return False, f"No valid displayment tags extracted for Repeater '{inter['entity']}'. Check the Displayments format." invalid_dp = [dt for dt in dp_tags if dt not in self.valid_displayment_tags] if invalid_dp: return False, f"Invalid displayment tags for Repeater '{inter['entity']}': {', '.join(invalid_dp)}. Allowed: {', '.join(self.valid_displayment_tags)}" return True, "" def setup_footnotes(self): chain_input = self.result_text.get("1.0", tk.END).strip() if not chain_input: messagebox.showerror("Error", "Please generate a chain first.") return # Clear previous state self.interactors = [] for widget in self.footnote_container.winfo_children(): widget.destroy() try: extracted = self.parse_moralithic_chain(chain_input) if not extracted: messagebox.showerror("Error", "Invalid moralithic chain format. Failed to parse sections (e.g., moral source, moral statement, interactors). Ensure proper brackets, tags, and separators (-).") return valid, error = self.validate_extracted_moralithic(extracted) if not valid: messagebox.showerror("Error", f"Validation failed: {error}") return self.chain_text = chain_input self.interactors = extracted['interactors'] transformable_interactors = [inter for inter in self.interactors if not inter['tag'].startswith("RP")] if not transformable_interactors: messagebox.showinfo("Info", "No transformable interactors (only Repeaters found).") return for idx, inter in enumerate(transformable_interactors): orig_idx = self.interactors.index(inter) self.add_footnote_frame(inter['tag'], orig_idx) self.root.update_idletasks() except Exception as e: messagebox.showerror("Error", f"Unexpected error during validation: {str(e)}") def add_footnote_frame(self, tag, idx): frame = ttk.Frame(self.footnote_container) frame.pack(anchor=tk.W, pady=5, fill=tk.X) label_text = f"Add Transformation for {self.interactors[idx]['type']} '{self.interactors[idx]['entity']}' ({tag}):" ttk.Label(frame, text=label_text).pack(anchor=tk.W) combobox = ttk.Combobox(frame, state="readonly", width=20) combobox.pack(anchor=tk.W, pady=2) valid_transformations = [] if tag in self.conversion_rules: valid_transformations.extend([f"{tag}->{prev}" for prev in self.conversion_rules[tag]]) if tag in self.transformation_rules: valid_transformations.extend([f"{tag}->{prev}" for prev in self.transformation_rules[tag]]) combobox['values'] = valid_transformations combobox['state'] = 'readonly' if valid_transformations else 'disabled' add_btn = ttk.Button(frame, text="Add Transformation", command=lambda c=combobox, i=idx: self.add_transformation(c, i)) add_btn.pack(anchor=tk.W, pady=5) self.interactors[idx]['footnote_frame'] = frame self.interactors[idx]['combobox'] = combobox self.root.update_idletasks() def add_transformation(self, combobox, idx): try: transformation = combobox.get() if not transformation: messagebox.showerror("Error", "Please select a transformation from the dropdown.") return current_tag, prev_tag = transformation.split("->") valid, error = self.validate_transformation(current_tag, prev_tag) if not valid: messagebox.showerror("Error", f"Invalid transformation: {error}") return self.interactors[idx]['transformations'].append(transformation) current_tag = prev_tag valid_transformations = [] if current_tag in self.conversion_rules: valid_transformations.extend([f"{current_tag}->{p}" for p in self.conversion_rules[current_tag]]) if current_tag in self.transformation_rules: valid_transformations.extend([f"{current_tag}->{p}" for p in self.transformation_rules[current_tag]]) combobox['values'] = valid_transformations combobox.set('') combobox['state'] = 'readonly' if valid_transformations else 'disabled' self.update_chain_with_footnote() except Exception as e: messagebox.showerror("Error", f"Failed to add transformation: {str(e)}") def update_chain_with_footnote(self): try: footnotes = [] for inter in self.interactors: if inter.get('transformations'): trans_chain = [inter['tag']] for trans in inter['transformations']: _, new = trans.split('->') trans_chain.append(new) trans_chain = trans_chain[::-1] trans_str = '->'.join(trans_chain) footnotes.append(f"{inter['type']} '{inter['entity']}' underwent {len(inter['transformations'])} transformation{'s' if len(inter['transformations']) > 1 else ''}: {trans_str}") footnote = "" if footnotes: footnote = f"Footnotes: {'; '.join(footnotes)}\n" self.result_text.configure(state="normal") self.result_text.delete("1.0", tk.END) self.result_text.insert("1.0", self.chain_text + "\n" + footnote) self.result_text.configure(state="disabled") except Exception as e: messagebox.showerror("Error", f"Failed to update chain with footnote: {str(e)}") def validate_transformation(self, current_tag, prev_tag): if current_tag.startswith("RP"): return False, "Repeaters (RP tags) cannot be transformed." if prev_tag.startswith("NI") and not current_tag.startswith("NI"): return False, "Cannot transform NI tags to non-NI types." if current_tag in self.conversion_rules and prev_tag in self.conversion_rules[current_tag]: return True, "" if current_tag in self.transformation_rules and prev_tag in self.transformation_rules[current_tag]: return True, "" return False, f"{current_tag} cannot be transformed from {prev_tag}. Check available transformations for {current_tag}." if __name__ == "__main__": root = tk.Tk() root.geometry("900x800") app = MoralithicChainGeneratorMode2(root) root.mainloop()