@@ -141,8 +141,18 @@ def function_name_to_glyph(
141141
142142__all__ = [
143143 "GrammarValidator" ,
144+ "GrammarContext" ,
144145 "validate_grammar" ,
145146 "StructuralPattern" ,
147+ # Error classes
148+ "StructuralGrammarError" ,
149+ "RepeatWindowError" ,
150+ "MutationPreconditionError" ,
151+ "TholClosureError" ,
152+ "TransitionCompatibilityError" ,
153+ "SequenceSyntaxError" ,
154+ "GrammarConfigurationError" ,
155+ "record_grammar_violation" ,
146156 # Glyph mappings
147157 "GLYPH_TO_FUNCTION" ,
148158 "FUNCTION_TO_GLYPH" ,
@@ -152,6 +162,9 @@ def function_name_to_glyph(
152162 "apply_glyph_with_grammar" ,
153163 "on_applied_glyph" ,
154164 "enforce_canonical_grammar" ,
165+ # Sequence validation
166+ "validate_sequence" ,
167+ "parse_sequence" ,
155168 # Operator sets
156169 "GENERATORS" ,
157170 "CLOSURES" ,
@@ -193,6 +206,233 @@ def function_name_to_glyph(
193206TRANSFORMERS = frozenset ({"mutation" , "self_organization" })
194207
195208
209+ # ============================================================================
210+ # Grammar Errors
211+ # ============================================================================
212+
213+
214+ class StructuralGrammarError (RuntimeError ):
215+ """Base class for structural grammar violations.
216+
217+ Attributes
218+ ----------
219+ rule : str
220+ Grammar rule that was violated
221+ candidate : str
222+ Operator/glyph that caused violation
223+ message : str
224+ Error description
225+ window : int | None
226+ Grammar window if applicable
227+ threshold : float | None
228+ Threshold value if applicable
229+ order : Sequence[str] | None
230+ Operator sequence if applicable
231+ context : dict
232+ Additional context information
233+ """
234+
235+ def __init__ (
236+ self ,
237+ * ,
238+ rule : str ,
239+ candidate : str ,
240+ message : str ,
241+ window : int | None = None ,
242+ threshold : float | None = None ,
243+ order : list [str ] | None = None ,
244+ context : dict [str , Any ] | None = None ,
245+ ):
246+ self .rule = rule
247+ self .candidate = candidate
248+ self .message = message
249+ self .window = window
250+ self .threshold = threshold
251+ self .order = order
252+ self .context = context or {}
253+ super ().__init__ (message )
254+
255+ def attach_context (self , ** context : Any ) -> "StructuralGrammarError" :
256+ """Attach additional context to error.
257+
258+ Parameters
259+ ----------
260+ **context : Any
261+ Additional context key-value pairs
262+
263+ Returns
264+ -------
265+ StructuralGrammarError
266+ Self for chaining
267+ """
268+ self .context .update (context )
269+ return self
270+
271+ def to_payload (self ) -> dict [str , Any ]:
272+ """Convert error to dictionary payload.
273+
274+ Returns
275+ -------
276+ dict
277+ Error information as dictionary
278+ """
279+ return {
280+ "rule" : self .rule ,
281+ "candidate" : self .candidate ,
282+ "message" : self .message ,
283+ "window" : self .window ,
284+ "threshold" : self .threshold ,
285+ "order" : self .order ,
286+ "context" : self .context ,
287+ }
288+
289+
290+ class RepeatWindowError (StructuralGrammarError ):
291+ """Error for repeated operator within window."""
292+ pass
293+
294+
295+ class MutationPreconditionError (StructuralGrammarError ):
296+ """Error for mutation without proper preconditions."""
297+ pass
298+
299+
300+ class TholClosureError (StructuralGrammarError ):
301+ """Error for THOL without proper closure."""
302+ pass
303+
304+
305+ class TransitionCompatibilityError (StructuralGrammarError ):
306+ """Error for incompatible transition."""
307+ pass
308+
309+
310+ class SequenceSyntaxError (ValueError ):
311+ """Error in sequence syntax.
312+
313+ Attributes
314+ ----------
315+ index : int
316+ Position in sequence where error occurred
317+ token : object
318+ Token that caused the error
319+ message : str
320+ Error description
321+ """
322+
323+ def __init__ (self , index : int , token : Any , message : str ):
324+ self .index = index
325+ self .token = token
326+ self .message = message
327+ super ().__init__ (f"At index { index } , token '{ token } ': { message } " )
328+
329+
330+ class GrammarConfigurationError (ValueError ):
331+ """Error in grammar configuration.
332+
333+ Attributes
334+ ----------
335+ section : str
336+ Configuration section with error
337+ messages : list[str]
338+ Error messages
339+ details : list[tuple[str, str]]
340+ Additional details
341+ """
342+
343+ def __init__ (
344+ self ,
345+ section : str ,
346+ messages : list [str ],
347+ * ,
348+ details : list [tuple [str , str ]] | None = None ,
349+ ):
350+ self .section = section
351+ self .messages = messages
352+ self .details = details or []
353+ msg = f"Configuration error in { section } : { '; ' .join (messages )} "
354+ super ().__init__ (msg )
355+
356+
357+ def record_grammar_violation (
358+ G : "TNFRGraph" ,
359+ node : "NodeId" ,
360+ error : StructuralGrammarError ,
361+ * ,
362+ stage : str ,
363+ ) -> None :
364+ """Record grammar violation in node metadata.
365+
366+ Parameters
367+ ----------
368+ G : TNFRGraph
369+ Graph containing node
370+ node : NodeId
371+ Node where violation occurred
372+ error : StructuralGrammarError
373+ Grammar error to record
374+ stage : str
375+ Processing stage when error occurred
376+ """
377+ if "grammar_violations" not in G .nodes [node ]:
378+ G .nodes [node ]["grammar_violations" ] = []
379+ G .nodes [node ]["grammar_violations" ].append ({
380+ "stage" : stage ,
381+ "error" : error .to_payload (),
382+ })
383+
384+
385+ # ============================================================================
386+ # Grammar Context
387+ # ============================================================================
388+
389+
390+ class GrammarContext :
391+ """Context object for grammar validation.
392+
393+ Minimal implementation for import compatibility.
394+
395+ Attributes
396+ ----------
397+ G : TNFRGraph
398+ Graph being validated
399+ cfg_soft : dict
400+ Soft configuration parameters
401+ cfg_canon : dict
402+ Canonical configuration parameters
403+ norms : dict
404+ Normalization parameters
405+ """
406+
407+ def __init__ (
408+ self ,
409+ G : "TNFRGraph" ,
410+ cfg_soft : dict [str , Any ] | None = None ,
411+ cfg_canon : dict [str , Any ] | None = None ,
412+ norms : dict [str , Any ] | None = None ,
413+ ):
414+ self .G = G
415+ self .cfg_soft = cfg_soft or {}
416+ self .cfg_canon = cfg_canon or {}
417+ self .norms = norms or {}
418+
419+ @classmethod
420+ def from_graph (cls , G : "TNFRGraph" ) -> "GrammarContext" :
421+ """Create context from graph.
422+
423+ Parameters
424+ ----------
425+ G : TNFRGraph
426+ Graph to create context from
427+
428+ Returns
429+ -------
430+ GrammarContext
431+ New context instance
432+ """
433+ return cls (G )
434+
435+
196436class GrammarValidator :
197437 """Validates sequences using canonical TNFR grammar constraints.
198438
@@ -729,3 +969,59 @@ def enforce_canonical_grammar(
729969 """
730970 # Minimal stub - return candidate as-is
731971 return cand
972+
973+
974+ def validate_sequence (
975+ names : Any = None ,
976+ ** kwargs : Any ,
977+ ) -> Any :
978+ """Validate sequence of operator names.
979+
980+ Minimal stub implementation for import compatibility.
981+
982+ Parameters
983+ ----------
984+ names : Iterable[str] | object, optional
985+ Sequence of operator names
986+ **kwargs : Any
987+ Additional validation options
988+
989+ Returns
990+ -------
991+ ValidationOutcome
992+ Validation result (stub returns success)
993+ """
994+ # Minimal stub - return success
995+ class ValidationStub :
996+ def __init__ (self ):
997+ self .passed = True
998+ self .message = "Validation stub"
999+ self .metadata = {}
1000+ return ValidationStub ()
1001+
1002+
1003+ def parse_sequence (names : Any ) -> Any :
1004+ """Parse sequence of operator names.
1005+
1006+ Minimal stub implementation.
1007+
1008+ Parameters
1009+ ----------
1010+ names : Iterable[str]
1011+ Sequence of operator names
1012+
1013+ Returns
1014+ -------
1015+ SequenceValidationResult
1016+ Parse result (stub)
1017+ """
1018+ # Minimal stub
1019+ class ParseStub :
1020+ def __init__ (self ):
1021+ self .tokens = list (names ) if names else []
1022+ self .canonical_tokens = self .tokens
1023+ self .passed = True
1024+ self .message = "Parse stub"
1025+ self .metadata = {}
1026+ self .error = None
1027+ return ParseStub ()
0 commit comments