Skip to content

Commit a5db75a

Browse files
author
fer
committed
feat(grammar): split grammar.py into 8 specialized modules (Task 5)
Intent: Improve maintainability and cognitive load of grammar system Operators involved: All (U1-U6 validation logic) Affected invariants: None - 100% backward compatibility preserved Key changes: - Split grammar.py (2,921 lines, 101 KB) into 8 focused modules: * grammar_types.py (482 lines): Enums, exceptions, operator sets * grammar_context.py (132 lines): Runtime grammar context tracking * grammar_core.py (882 lines): GrammarValidator main engine (U1-U6) * grammar_u6.py (131 lines): U6 structural potential validation * grammar_validate.py (47 lines): Main validate_grammar() entry point * grammar_telemetry.py (247 lines): Phase/curvature/coherence telemetry * grammar_application.py (174 lines): Runtime enforcement functions * grammar_patterns.py (731 lines): Sequence validation/parsing/IL recognition * grammar.py: Facade with complete backward compatibility - Migrated 10 operator set constants (GENERATORS, CLOSURES, etc.) to grammar_types - Created scripts/split_grammar.py for automated, reproducible splitting - Preserved all public APIs and private helpers for tests - Added comprehensive imports across all modules Expected risks/dissonances: None - split done programmatically with comprehensive validation Metrics: - Tests: 172 critical grammar tests passing (unified_grammar, grammar_module, canonical_il_sequences, canonical_grammar_rules, physics_grammar_integration) - Backward compatibility: 100% (all imports work, facade exports complete) - Health: 100/100 maintained - Operator sets: 10 frozensets migrated with physics derivations - Import chains: Resolved through 6 fix iterations Equivalence map: - All functions/classes accessible from tnfr.operators.grammar (unchanged) - Module structure: monolithic → 8 specialized files - Cognitive load: 2,921 lines → max 882 lines per file - Physical files: 1 → 9 (8 modules + facade) This completes Task 5 of Phase 1 optimization roadmap.
1 parent 8f24f84 commit a5db75a

File tree

11 files changed

+6361
-2804
lines changed

11 files changed

+6361
-2804
lines changed

scripts/split_grammar.py

Lines changed: 288 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,288 @@
1+
"""Script to split grammar.py into modular files.
2+
3+
This script divides src/tnfr/operators/grammar.py (2,921 lines) into
4+
specialized modules plus a facade for backward compatibility.
5+
6+
CRITICAL: This is the most important file in TNFR. Handle with extreme care.
7+
"""
8+
9+
import os
10+
import shutil
11+
12+
# Read the original file
13+
with open("src/tnfr/operators/grammar.py", "r", encoding="utf-8") as f:
14+
lines = f.readlines()
15+
16+
# Common header for all modules
17+
def make_header(title: str, description: str) -> list[str]:
18+
return [
19+
f'"""TNFR Grammar: {title}\n',
20+
"\n",
21+
f"{description}\n",
22+
"\n",
23+
'Terminology (TNFR semantics):\n',
24+
'- "node" == resonant locus (structural coherence site); kept for NetworkX compatibility\n',
25+
'- Future semantic aliasing ("locus") must preserve public API stability\n',
26+
'"""\n',
27+
"\n",
28+
"from __future__ import annotations\n",
29+
"\n",
30+
]
31+
32+
# Extract header (lines 1-85)
33+
header_lines = lines[0:85]
34+
35+
# Define sections (1-indexed, inclusive ranges)
36+
sections = {
37+
"grammar_types.py": {
38+
"range": (86, 567),
39+
"title": "Types and Exceptions",
40+
"desc": "Enums, exception classes, and validation result types for TNFR grammar.",
41+
},
42+
"grammar_context.py": {
43+
"range": (568, 699),
44+
"title": "Grammar Context",
45+
"desc": "Runtime context for grammar validation and operator application tracking.",
46+
},
47+
"grammar_core.py": {
48+
"range": (700, 1581),
49+
"title": "Core Grammar Validator",
50+
"desc": "GrammarValidator class - central validation engine for all grammar rules U1-U6.",
51+
},
52+
"grammar_u6.py": {
53+
"range": (1582, 1712),
54+
"title": "U6 Structural Potential Validation",
55+
"desc": "U6: STRUCTURAL POTENTIAL CONFINEMENT - Validate Δ Φ_s < 2.0 escape threshold.",
56+
},
57+
"grammar_telemetry.py": {
58+
"range": (1765, 2011),
59+
"title": "U6 Telemetry Functions",
60+
"desc": "Phase gradient, phase curvature, and coherence length telemetry for U6 validation.",
61+
},
62+
"grammar_application.py": {
63+
"range": (2017, 2190),
64+
"title": "Grammar Application",
65+
"desc": "Functions for applying operators with grammar enforcement at runtime.",
66+
},
67+
"grammar_patterns.py": {
68+
"range": (2191, 2921),
69+
"title": "Sequence Pattern Recognition",
70+
"desc": "Sequence validation, parsing, pattern recognition, and optimization helpers.",
71+
},
72+
}
73+
74+
# Special handling: validate_grammar function (lines 1718-1759) goes in its own module
75+
sections["grammar_validate.py"] = {
76+
"range": (1713, 1759),
77+
"title": "Main Validation Entry Point",
78+
"desc": "Primary validate_grammar() function - the main public API for grammar checking.",
79+
}
80+
81+
print("🔨 Starting grammar.py split...")
82+
print(f"📄 Original file: {len(lines)} lines\n")
83+
84+
# Create each module
85+
for module_name, spec in sorted(sections.items()):
86+
start, end = spec["range"]
87+
title = spec["title"]
88+
desc = spec["desc"]
89+
90+
module_lines = []
91+
92+
# Add header
93+
module_lines.extend(make_header(title, desc))
94+
95+
# Add necessary imports based on module
96+
if module_name == "grammar_types.py":
97+
module_lines.extend([
98+
"from enum import Enum\n",
99+
"from typing import TYPE_CHECKING, Any, List, Mapping, Sequence, Tuple\n",
100+
"\n",
101+
"if TYPE_CHECKING:\n",
102+
" from ..types import NodeId, TNFRGraph, Glyph\n",
103+
" from .definitions import Operator\n",
104+
"else:\n",
105+
" NodeId = Any\n",
106+
" TNFRGraph = Any\n",
107+
" from ..types import Glyph\n",
108+
"\n",
109+
"from ..validation.base import ValidationOutcome\n",
110+
"\n",
111+
])
112+
elif module_name == "grammar_context.py":
113+
module_lines.extend([
114+
"from typing import Any\n",
115+
"\n",
116+
"from .grammar_types import GrammarConfigurationError\n",
117+
"\n",
118+
])
119+
elif module_name == "grammar_core.py":
120+
module_lines.extend([
121+
"from typing import TYPE_CHECKING, Any\n",
122+
"\n",
123+
"if TYPE_CHECKING:\n",
124+
" from ..types import NodeId, TNFRGraph, Glyph\n",
125+
"else:\n",
126+
" NodeId = Any\n",
127+
" TNFRGraph = Any\n",
128+
" from ..types import Glyph\n",
129+
"\n",
130+
"from .grammar_types import (\n",
131+
" StructuralGrammarError,\n",
132+
" RepeatWindowError,\n",
133+
" MutationPreconditionError,\n",
134+
" TholClosureError,\n",
135+
" TransitionCompatibilityError,\n",
136+
" StructuralPotentialConfinementError,\n",
137+
" record_grammar_violation,\n",
138+
")\n",
139+
"from .grammar_context import GrammarContext\n",
140+
"from ..config.operator_names import (\n",
141+
" BIFURCATION_WINDOWS,\n",
142+
" CANONICAL_OPERATOR_NAMES,\n",
143+
" DESTABILIZERS_MODERATE,\n",
144+
" DESTABILIZERS_STRONG,\n",
145+
" DESTABILIZERS_WEAK,\n",
146+
" INTERMEDIATE_OPERATORS,\n",
147+
" SELF_ORGANIZATION,\n",
148+
" SELF_ORGANIZATION_CLOSURES,\n",
149+
" VALID_END_OPERATORS,\n",
150+
" VALID_START_OPERATORS,\n",
151+
")\n",
152+
"from ..validation.compatibility import (\n",
153+
" CompatibilityLevel,\n",
154+
" get_compatibility_level,\n",
155+
")\n",
156+
"\n",
157+
])
158+
else:
159+
# Other modules get minimal imports
160+
module_lines.extend([
161+
"from typing import Any\n",
162+
"\n",
163+
])
164+
165+
# Add content
166+
module_lines.extend(lines[start-1:end])
167+
168+
# Write module
169+
with open(f"src/tnfr/operators/{module_name}", "w", encoding="utf-8") as f:
170+
f.writelines(module_lines)
171+
172+
print(f"✅ Created {module_name:30s} ({end-start+1:4d} lines)")
173+
174+
# Create facade grammar.py
175+
print("\n📦 Creating facade grammar.py...")
176+
177+
facade_lines = []
178+
facade_lines.extend(header_lines)
179+
facade_lines.append("\n")
180+
facade_lines.append("# Re-export all grammar components for backward compatibility\n")
181+
facade_lines.append("\n")
182+
facade_lines.append("# Types and exceptions\n")
183+
facade_lines.append("from .grammar_types import (\n")
184+
facade_lines.append(" StructuralPattern,\n")
185+
facade_lines.append(" StructuralGrammarError,\n")
186+
facade_lines.append(" RepeatWindowError,\n")
187+
facade_lines.append(" MutationPreconditionError,\n")
188+
facade_lines.append(" TholClosureError,\n")
189+
facade_lines.append(" TransitionCompatibilityError,\n")
190+
facade_lines.append(" StructuralPotentialConfinementError,\n")
191+
facade_lines.append(" SequenceSyntaxError,\n")
192+
facade_lines.append(" SequenceValidationResult,\n")
193+
facade_lines.append(" GrammarConfigurationError,\n")
194+
facade_lines.append(" record_grammar_violation,\n")
195+
facade_lines.append(" glyph_function_name,\n")
196+
facade_lines.append(" function_name_to_glyph,\n")
197+
facade_lines.append(")\n")
198+
facade_lines.append("\n")
199+
facade_lines.append("# Context\n")
200+
facade_lines.append("from .grammar_context import GrammarContext\n")
201+
facade_lines.append("\n")
202+
facade_lines.append("# Core validator\n")
203+
facade_lines.append("from .grammar_core import GrammarValidator\n")
204+
facade_lines.append("\n")
205+
facade_lines.append("# U6 validation\n")
206+
facade_lines.append("from .grammar_u6 import validate_structural_potential_confinement\n")
207+
facade_lines.append("\n")
208+
facade_lines.append("# Main validation entry point\n")
209+
facade_lines.append("from .grammar_validate import validate_grammar\n")
210+
facade_lines.append("\n")
211+
facade_lines.append("# Telemetry\n")
212+
facade_lines.append("from .grammar_telemetry import (\n")
213+
facade_lines.append(" warn_phase_gradient_telemetry,\n")
214+
facade_lines.append(" warn_phase_curvature_telemetry,\n")
215+
facade_lines.append(" warn_coherence_length_telemetry,\n")
216+
facade_lines.append(")\n")
217+
facade_lines.append("\n")
218+
facade_lines.append("# Application\n")
219+
facade_lines.append("from .grammar_application import (\n")
220+
facade_lines.append(" apply_glyph_with_grammar,\n")
221+
facade_lines.append(" on_applied_glyph,\n")
222+
facade_lines.append(" enforce_canonical_grammar,\n")
223+
facade_lines.append(")\n")
224+
facade_lines.append("\n")
225+
facade_lines.append("# Pattern recognition\n")
226+
facade_lines.append("from .grammar_patterns import (\n")
227+
facade_lines.append(" validate_sequence,\n")
228+
facade_lines.append(" parse_sequence,\n")
229+
facade_lines.append(" SequenceValidationResultWithHealth,\n")
230+
facade_lines.append(" validate_sequence_with_health,\n")
231+
facade_lines.append(" recognize_il_sequences,\n")
232+
facade_lines.append(" optimize_il_sequence,\n")
233+
facade_lines.append(" suggest_il_sequence,\n")
234+
facade_lines.append(")\n")
235+
facade_lines.append("\n")
236+
facade_lines.append("__all__ = [\n")
237+
facade_lines.append(' # Types\n')
238+
facade_lines.append(' "StructuralPattern",\n')
239+
facade_lines.append(' # Exceptions\n')
240+
facade_lines.append(' "StructuralGrammarError",\n')
241+
facade_lines.append(' "RepeatWindowError",\n')
242+
facade_lines.append(' "MutationPreconditionError",\n')
243+
facade_lines.append(' "TholClosureError",\n')
244+
facade_lines.append(' "TransitionCompatibilityError",\n')
245+
facade_lines.append(' "StructuralPotentialConfinementError",\n')
246+
facade_lines.append(' "SequenceSyntaxError",\n')
247+
facade_lines.append(' "GrammarConfigurationError",\n')
248+
facade_lines.append(' # Validation\n')
249+
facade_lines.append(' "SequenceValidationResult",\n')
250+
facade_lines.append(' "validate_grammar",\n')
251+
facade_lines.append(' "validate_sequence",\n')
252+
facade_lines.append(' "parse_sequence",\n')
253+
facade_lines.append(' "validate_sequence_with_health",\n')
254+
facade_lines.append(' # U6\n')
255+
facade_lines.append(' "validate_structural_potential_confinement",\n')
256+
facade_lines.append(' # Core\n')
257+
facade_lines.append(' "GrammarContext",\n')
258+
facade_lines.append(' "GrammarValidator",\n')
259+
facade_lines.append(' # Application\n')
260+
facade_lines.append(' "apply_glyph_with_grammar",\n')
261+
facade_lines.append(' "on_applied_glyph",\n')
262+
facade_lines.append(' "enforce_canonical_grammar",\n')
263+
facade_lines.append(' # Helpers\n')
264+
facade_lines.append(' "glyph_function_name",\n')
265+
facade_lines.append(' "function_name_to_glyph",\n')
266+
facade_lines.append(' "record_grammar_violation",\n')
267+
facade_lines.append(' "SequenceValidationResultWithHealth",\n')
268+
facade_lines.append(' "recognize_il_sequences",\n')
269+
facade_lines.append(' "optimize_il_sequence",\n')
270+
facade_lines.append(' "suggest_il_sequence",\n')
271+
facade_lines.append(' # Telemetry\n')
272+
facade_lines.append(' "warn_phase_gradient_telemetry",\n')
273+
facade_lines.append(' "warn_phase_curvature_telemetry",\n')
274+
facade_lines.append(' "warn_coherence_length_telemetry",\n')
275+
facade_lines.append("]\n")
276+
277+
# Backup original
278+
shutil.move("src/tnfr/operators/grammar.py", "src/tnfr/operators/grammar.py.old")
279+
280+
# Write facade
281+
with open("src/tnfr/operators/grammar.py", "w", encoding="utf-8") as f:
282+
f.writelines(facade_lines)
283+
284+
print("✅ Created grammar.py facade")
285+
print("✅ Renamed original to grammar.py.old")
286+
print("\n✨ Split complete!")
287+
print("\n⚠️ CRITICAL: Run full test suite to verify!")
288+
print(" python -m pytest tests/ -q --tb=short")

0 commit comments

Comments
 (0)