Source code for kicadfiles.base_element

"""S-expression parser for KiCad objects.

Architecture:
- Unified token handling via __token_name__ in SExpressionBase
- Central parsing utilities in ParseCursor
- Type-based field classification (PRIMITIVE vs SEXPR_BASE)
- Clean separation between instance fields and list containers
- Field metadata as single source of truth for required/optional behavior
"""

from __future__ import annotations

import logging
from abc import ABC, abstractmethod
from dataclasses import dataclass, fields
from enum import Enum
from typing import (
    Any,
    ClassVar,
    List,
    Optional,
    Type,
    TypeVar,
    Union,
    get_args,
    get_origin,
    get_type_hints,
)

from .sexpdata import Symbol
from .sexpr_parser import SExpr, SExprParser, str_to_sexpr

T = TypeVar("T", bound="NamedObject")


# =============================================================================
# Utility Functions
# =============================================================================


def _convert_to_type(value: Any, target_type: Type) -> Any:
    """Convert value to target type with Enum support."""
    try:
        if isinstance(target_type, type) and issubclass(target_type, Enum):
            if isinstance(value, int):
                return target_type(value)
            try:
                return target_type(str(value))
            except ValueError:
                return target_type[str(value).upper()]
    except (TypeError, KeyError):
        pass

    if target_type == int:
        return int(value)
    elif target_type == float:
        return float(value)
    elif target_type == bool:
        return str(value).lower() in ("yes", "true", "1")
    elif target_type == str:
        return str(value)
    return value


# =============================================================================
# Parsing Infrastructure
# =============================================================================


[docs] class ParseStrictness(Enum): """Parser strictness levels for error handling.""" STRICT = "strict" # Raise exceptions for all parsing errors SILENT = "silent" # Silently use defaults for missing fields FAILSAFE = "failsafe" # Log warnings and use defaults for missing fields
@dataclass class ParseCursor: """Cursor for tracking position in S-expression during parsing. Provides centralized parsing utilities for all primitive types. """ sexpr: SExpr # Current S-expression parser: SExprParser # Parser for tracking used indices path: List[str] # Path for debugging strictness: ParseStrictness # Parse strictness level def enter(self, sexpr: SExpr, name: str) -> "ParseCursor": """Create new cursor for nested object.""" nested_parser = SExprParser(sexpr) return ParseCursor( sexpr=sexpr, parser=nested_parser, path=self.path + [name], strictness=self.strictness, ) def get_path_str(self) -> str: """Get current path as string for debugging.""" return " > ".join(self.path) def log_issue(self, message: str) -> None: """Log parsing issue based on strictness.""" if self.strictness == ParseStrictness.STRICT: raise ValueError(message) elif self.strictness == ParseStrictness.FAILSAFE: logging.warning(message) # SILENT: do nothing def _get_value_at_index( self, index: int, field_name: str, type_name: str, required: bool ) -> Optional[Any]: """Get value at index with validation and error handling. Returns: The raw value at index, or None if not available or invalid """ if index >= len(self.sexpr): if required: self.log_issue( f"{self.get_path_str()}: Missing required {type_name} '{field_name}' at index {index}" ) return None value = self.sexpr[index] if isinstance(value, list): if required: self.log_issue( f"{self.get_path_str()}: Expected {type_name} for '{field_name}', got list" ) return None self.parser.mark_used(index) return value def _parse_typed( self, index: int, field_name: str, target_type: Type, required: bool = True ) -> Optional[Any]: """Generic parser for typed values at given index.""" type_name = getattr(target_type, "__name__", str(target_type)) value = self._get_value_at_index(index, field_name, type_name, required) if value is None: return None try: return _convert_to_type(value, target_type) except (ValueError, TypeError, KeyError) as e: self.log_issue( f"{self.get_path_str()}: Cannot convert '{value}' to {type_name} for '{field_name}': {e}" ) return None def parse_int( self, index: int, field_name: str, required: bool = True ) -> Optional[int]: """Parse integer at given index.""" return self._parse_typed(index, field_name, int, required) def parse_float( self, index: int, field_name: str, required: bool = True ) -> Optional[float]: """Parse float at given index.""" return self._parse_typed(index, field_name, float, required) def parse_str( self, index: int, field_name: str, required: bool = True ) -> Optional[str]: """Parse string at given index.""" return self._parse_typed(index, field_name, str, required) def parse_bool( self, index: int, field_name: str, required: bool = True ) -> Optional[bool]: """Parse boolean at given index (yes/no, true/false, 1/0).""" return self._parse_typed(index, field_name, bool, required) def parse_enum( self, index: int, field_name: str, enum_class: Type[Enum], required: bool = True ) -> Optional[Enum]: """Parse enum at given index.""" return self._parse_typed(index, field_name, enum_class, required) def find_token(self, token_name: str, mark_used: bool = True) -> SExpr: """Find and return S-expression with given token name. Returns: The S-expression as list if found, empty list otherwise. Always returns a list for consistent handling. """ for idx, item in enumerate(self.sexpr[1:], start=1): if isinstance(item, list) and item and str(item[0]) == token_name: if mark_used: self.parser.mark_used(idx) return item elif isinstance(item, (str, Symbol)) and str(item) == token_name: if mark_used: self.parser.mark_used(idx) return [item] return [] # ============================================================================= # Base Classes # ============================================================================= @dataclass class SExpressionBase(ABC): """Abstract base class for all S-expression objects. Attributes: __token_name__: Token identifier for this object (ClassVar) """ __token_name__: ClassVar[str] = "" @classmethod @abstractmethod def from_sexpr( cls: Type, sexpr: Union[str, SExpr], strictness: ParseStrictness = ParseStrictness.STRICT, cursor: Optional[ParseCursor] = None, ) -> Optional[Any]: """Parse from S-expression.""" pass @abstractmethod def to_sexpr(self) -> Union[SExpr, str, int, float]: """Serialize to S-expression.""" pass # ============================================================================= # KiCad Primitives # ============================================================================= @dataclass(eq=False) class NamedValue(SExpressionBase): """Base class for named primitive wrapper types (str, int, float).""" base_type: ClassVar[type] = object def __post_init__(self) -> None: """Post-initialization hook for primitives. Note: Primitives have instance-specific tokens, not class-level tokens. """ pass @property def required(self) -> bool: """Whether this field is required.""" return getattr(self, "_required", True) def __str__(self) -> str: return str(getattr(self, "value", None)) def __repr__(self) -> str: value = getattr(self, "value", None) parts = [repr(value)] if self.__token_name__: parts.append(f"token={repr(self.__token_name__)}") if not self.required: parts.append("optional") return f"{self.__class__.__name__}({', '.join(parts)})" def __bool__(self) -> bool: """Boolean conversion based on value.""" return bool(getattr(self, "value", None)) def __call__(self, new_value: Any) -> "NamedValue": """Allow calling the primitive to update its value. This enables convenient syntax like: pcb.version(20240101) instead of: pcb.version = NamedInt("version", 20240101) Args: new_value: New value to set Returns: Self for method chaining """ self.value = self._convert_value(new_value) return self @classmethod def from_sexpr( cls: Type, sexpr: Union[str, SExpr], strictness: ParseStrictness = ParseStrictness.STRICT, cursor: Optional[ParseCursor] = None, ) -> Optional["NamedValue"]: """Parse primitive from S-expression (named or positional).""" if cursor is None: raise ValueError("NamedValue requires cursor for parsing") value = None token = "" if isinstance(sexpr, list): if len(sexpr) >= 2: token = str(sexpr[0]) value = sexpr[1] else: value = sexpr if value is None: return None try: converted = cls._convert_value(value) instance: "NamedValue" = cls(token=token, value=converted) return instance except (ValueError, TypeError): return None @classmethod def _convert_value(cls, value: Any) -> Any: return _convert_to_type(value, cls.base_type) def to_sexpr(self) -> Union[List[Any], Any]: """Serialize to S-expression.""" value = getattr(self, "value", None) token = getattr(self, "token", "") if token: return [token, value] else: return value def __eq__(self, other: object) -> bool: """Equality comparison based on token and value only.""" if not isinstance(other, NamedValue): return False return ( self.__class__ == other.__class__ and getattr(self, "token", "") == getattr(other, "token", "") and getattr(self, "value", None) == getattr(other, "value", None) )
[docs] @dataclass(eq=False) class NamedString(NamedValue): """String wrapper for named values.""" token: str = "" value: str = "" base_type: ClassVar[type] = str
[docs] @dataclass(eq=False) class NamedInt(NamedValue): """Integer wrapper for named values.""" token: str = "" value: int = 0 base_type: ClassVar[type] = int
[docs] @dataclass(eq=False) class NamedFloat(NamedValue): """Float wrapper for named values.""" token: str = "" value: float = 0.0 base_type: ClassVar[type] = float
# ============================================================================= # Optional Flags # =============================================================================
[docs] class UnquotedToken(str): """Marker class for tokens that should not be quoted in serialization.""" pass
@dataclass(eq=False) class TokenBase(SExpressionBase): """Base class for optional flags with instance-level tokens.""" token: str = "" def __post_init__(self) -> None: """Copy token to __token_name__ for consistency.""" if self.token and not self.__class__.__token_name__: type.__setattr__(self.__class__, "__token_name__", self.token)
[docs] @dataclass(eq=False) class TokenFlag(TokenBase): """Optional flag with optional value. Formats: (token) -> token="token", token_value=None (token value) -> token="token", token_value="value" """ token_value: Optional[str] = None def __str__(self) -> str: if self.token_value: return f"({self.token} {self.token_value})" return f"({self.token})"
[docs] def __bool__(self) -> bool: """Boolean conversion based on token_value.""" if self.token_value: return self.token_value.lower() in ("yes", "true", "1") return True # Presence = True
[docs] def __call__(self, new_value: Optional[str] = None) -> "TokenFlag": """Allow calling the flag to update its value. This enables convenient syntax like: pcb.legacy_teardrops("no") instead of: pcb.legacy_teardrops = TokenFlag("legacy_teardrops", "no") Args: new_value: New token value to set Returns: Self for method chaining """ self.token_value = new_value return self
[docs] @classmethod def from_sexpr( cls: Type["TokenFlag"], sexpr: Union[str, SExpr], strictness: ParseStrictness = ParseStrictness.STRICT, cursor: Optional[ParseCursor] = None, ) -> Optional["TokenFlag"]: """Parse from S-expression. TokenFlag supports only simple flags: - (token) or (token value) where value is NOT a nested list Invalid: (fill (type none)) -> Use proper Fill class instead """ if cursor is None: raise ValueError("TokenFlag requires cursor for parsing") # Standalone symbol: (token) if isinstance(sexpr, (str, Symbol)): instance: TokenFlag = cls(token=str(sexpr), token_value=None) return instance # List format: (token) or (token value) if isinstance(sexpr, list) and len(sexpr) >= 1: token_name = str(sexpr[0]) if len(sexpr) > 2: cursor.log_issue( f"TokenFlag '{token_name}' has {len(sexpr)} elements, max 2 allowed" ) if len(sexpr) == 2 and isinstance(sexpr[1], list): cursor.log_issue( f"TokenFlag '{token_name}' has nested list - use proper class instead" ) # Extract value token_value = str(sexpr[1]) if len(sexpr) == 2 else None instance2: TokenFlag = cls(token=token_name, token_value=token_value) return instance2 return None
[docs] def to_sexpr(self) -> Union[List, str]: """Serialize to S-expression.""" if self.token_value: return [self.token, UnquotedToken(self.token_value)] return [self.token]
def __eq__(self, other: object) -> bool: if not isinstance(other, TokenFlag): return False return self.token == other.token and self.token_value == other.token_value
[docs] @dataclass(eq=False) class SymbolValue(TokenBase): """Simple flag for optional symbols. Represents standalone tokens like 'oval', 'locked' without values. Does not consume positional slots during parsing. """ def __str__(self) -> str: return self.token
[docs] def __bool__(self) -> bool: """Always True if instance exists.""" return True
[docs] @classmethod def from_sexpr( cls: Type, sexpr: Union[str, SExpr], strictness: ParseStrictness = ParseStrictness.STRICT, cursor: Optional[ParseCursor] = None, ) -> Optional["SymbolValue"]: """Parse from S-expression.""" if cursor is None: raise ValueError("SymbolValue requires cursor for parsing") # Handle both direct symbols and lists with single symbol (from find_token) if isinstance(sexpr, (str, Symbol)) and not isinstance(sexpr, list): instance: "SymbolValue" = cls(token=str(sexpr)) return instance elif ( isinstance(sexpr, list) and len(sexpr) == 1 and isinstance(sexpr[0], (str, Symbol)) ): instance2: "SymbolValue" = cls(token=str(sexpr[0])) return instance2 return None
[docs] def to_sexpr(self) -> UnquotedToken: """Serialize to S-expression.""" return UnquotedToken(self.token)
def __eq__(self, other: object) -> bool: if not isinstance(other, SymbolValue): return False return self.token == other.token
# ============================================================================= # NamedObject - Main Parser # ============================================================================= class FieldType(Enum): """Field type classification.""" PRIMITIVE = "primitive" SEXPR_BASE = "sexpr_base" @dataclass class FieldInfo: """Field metadata for parsing. Attributes: name: Field name in dataclass field_type: Type classification of elements (PRIMITIVE, SEXPR_BASE) inner_type: Actual element type (for List[T], this is T) is_optional: Whether field is Optional[T] is_list: Whether field is List[T] token_name: Token name for parsing (from __token_name__ or None) position_index: Positional index in S-expression (for non-named fields) """ name: str field_type: FieldType inner_type: Type[Any] is_optional: bool is_list: bool token_name: Optional[str] = None position_index: int = 0 @property def can_self_parse(self) -> bool: """Whether type is SExpressionBase (has from_sexpr).""" return self.field_type == FieldType.SEXPR_BASE
[docs] @dataclass class NamedObject(SExpressionBase): """Base class for named S-expression objects. Subclasses should define __token_name__ as ClassVar[str]. """ __legacy_token_names__: ClassVar[List[str]] = [] _field_info_cache: ClassVar[List[FieldInfo]]
[docs] def __post_init__(self) -> None: """Validate token name is defined.""" if not self.__token_name__: raise ValueError( f"Class {self.__class__.__name__} must define __token_name__" )
[docs] @classmethod def from_sexpr( cls: Type[T], sexpr: Union[str, SExpr], strictness: ParseStrictness = ParseStrictness.STRICT, cursor: Optional[ParseCursor] = None, ) -> T: """Parse from S-expression.""" # Create root cursor if needed if cursor is None: if isinstance(sexpr, str): sexpr = str_to_sexpr(sexpr) parser = SExprParser(sexpr) cursor = ParseCursor( sexpr=sexpr, parser=parser, path=[cls.__name__], strictness=strictness, ) # Validate token token = str(cursor.sexpr[0]) if cursor.sexpr else "empty" valid_tokens = [cls.__token_name__] + (cls.__legacy_token_names__ or []) if token not in valid_tokens: raise ValueError( f"Token mismatch at {cursor.get_path_str()}: " f"expected '{cls.__token_name__}', got '{token}'" ) # Parse fields using delegation field_infos = cls._classify_fields() parsed_values = {} for field_info in field_infos: value = cls._parse_field(field_info, cursor) # For SymbolValue, always set the value (even if None) to prevent # default_factory from being called when the token is not present if value is not None: parsed_values[field_info.name] = value elif field_info.inner_type.__name__ == "SymbolValue": parsed_values[field_info.name] = None instance = cls(**parsed_values) # Track which fields were actually parsed for roundtrip fidelity object.__setattr__(instance, "_parsed_fields", set(parsed_values.keys())) return instance
[docs] @classmethod def from_str( cls: Type[T], sexpr_string: str, strictness: ParseStrictness = ParseStrictness.STRICT, ) -> T: """Parse from S-expression string - convenience method for better clarity.""" sexpr = str_to_sexpr(sexpr_string) return cls.from_sexpr(sexpr, strictness=strictness)
@classmethod def _parse_field( cls, field_info: FieldInfo, cursor: ParseCursor, ) -> Any: """Parse single field based on field metadata.""" if field_info.is_list: return cls._parse_list_field(field_info, cursor) elif field_info.field_type == FieldType.PRIMITIVE: return cls._parse_primitive_field(field_info, cursor) elif field_info.field_type == FieldType.SEXPR_BASE: return cls._parse_sexpr_base_field(field_info, cursor) else: raise ValueError(f"Unknown field type: {field_info.field_type}") @classmethod def _parse_primitive_field(cls, field_info: FieldInfo, cursor: ParseCursor) -> Any: """Parse primitive field (named token or positional).""" if field_info.token_name: named_sexpr = cursor.find_token(field_info.token_name) if len(named_sexpr) >= 2: return _convert_to_type(named_sexpr[1], field_info.inner_type) if not field_info.is_optional and not named_sexpr: cursor.log_issue( f"{cursor.get_path_str()}: Required token '{field_info.token_name}' not found" ) # If token not found and field is optional, return None early if not named_sexpr: return None # Find next unused positional index # Start from position_index + 1 and skip already used indices index = field_info.position_index + 1 while index < len(cursor.sexpr) and index in cursor.parser.used_indices: index += 1 inner_type = field_info.inner_type required = not field_info.is_optional try: if isinstance(inner_type, type) and issubclass(inner_type, Enum): return cursor.parse_enum(index, field_info.name, inner_type, required) except TypeError: pass parser_map = { int: cursor.parse_int, float: cursor.parse_float, bool: cursor.parse_bool, str: cursor.parse_str, } parser = parser_map.get(inner_type, cursor.parse_str) return parser(index, field_info.name, required) @classmethod def _parse_list_field(cls, field_info: FieldInfo, cursor: ParseCursor) -> List[Any]: """Parse list field by collecting all matching elements.""" if not field_info.can_self_parse: return [] result: List[Any] = [] has_token = field_info.token_name is not None for idx, item in enumerate(cursor.sexpr[1:], start=1): if not isinstance(item, list): continue # Skip already used indices for unnamed tokens if not has_token and idx in cursor.parser.used_indices: continue # Check token match for named tokens if has_token and (not item or str(item[0]) != field_info.token_name): continue # Try to parse element try: if has_token: cursor.parser.mark_used(idx) nested_cursor = cursor.enter(item, f"{field_info.name}[{len(result)}]") element = field_info.inner_type.from_sexpr( item, cursor.strictness, nested_cursor ) if element is not None: if not has_token: cursor.parser.mark_used(idx) result.append(element) except (ValueError, TypeError): # In STRICT mode, propagate exceptions instead of silently skipping if cursor.strictness == ParseStrictness.STRICT: raise continue return result @classmethod def _parse_sexpr_base_field( cls, field_info: FieldInfo, cursor: ParseCursor ) -> Optional[Any]: """Parse SExpressionBase field via from_sexpr delegation.""" result = None if field_info.token_name: named_sexpr = cursor.find_token(field_info.token_name) if named_sexpr: nested_cursor = cursor.enter(named_sexpr, field_info.name) result = field_info.inner_type.from_sexpr( named_sexpr, cursor.strictness, nested_cursor ) if not field_info.is_optional and not named_sexpr: cursor.log_issue( f"{cursor.get_path_str()}: Required token '{field_info.token_name}' not found" ) if not named_sexpr: return None else: index = field_info.position_index + 1 if index >= len(cursor.sexpr): if not field_info.is_optional: cursor.log_issue( f"{cursor.get_path_str()}: Required field '{field_info.name}' " f"not found at index {index}" ) return None try: cursor.parser.mark_used(index) nested_cursor = cursor.enter(cursor.sexpr[index], field_info.name) result = field_info.inner_type.from_sexpr( cursor.sexpr[index], cursor.strictness, nested_cursor ) except (ValueError, TypeError) as e: if not field_info.is_optional: cursor.log_issue( f"{cursor.get_path_str()}: Failed to parse '{field_info.name}': {e}" ) return None # Set _required attribute from field metadata for primitives if result is not None and isinstance(result, NamedValue): object.__setattr__(result, "_required", not field_info.is_optional) return result @classmethod def _classify_fields(cls) -> List[FieldInfo]: """Classify all fields for parsing with caching.""" if hasattr(cls, "_field_info_cache"): return cls._field_info_cache field_types = get_type_hints(cls) field_infos: List[FieldInfo] = [] position_index = 0 for dataclass_field in fields(cls): if dataclass_field.name.startswith("_"): continue field_type = field_types[dataclass_field.name] field_info = cls._classify_field( dataclass_field.name, field_type, position_index, dataclass_field ) field_infos.append(field_info) if not ( field_info.field_type == FieldType.SEXPR_BASE and field_info.inner_type.__name__ == "SymbolValue" ): position_index += 1 cls._field_info_cache = field_infos return field_infos @classmethod def _classify_field( cls, name: str, field_type: Type[Any], position: int, dataclass_field: Any = None, ) -> FieldInfo: """Classify a single field.""" is_union_with_none = get_origin(field_type) is Union and type(None) in get_args( field_type ) inner_type = field_type if is_union_with_none: inner_type = next( arg for arg in get_args(field_type) if arg is not type(None) ) is_optional = is_union_with_none if dataclass_field and dataclass_field.metadata.get("required") is False: is_optional = True is_list = get_origin(inner_type) in (list, List) if is_list: element_type = get_args(inner_type)[0] if get_args(inner_type) else Any token_name = None element_field_type = FieldType.PRIMITIVE try: # Type guard to ensure element_type is a proper type before issubclass check if ( isinstance(element_type, type) and not isinstance(element_type, TypeVar) # type: ignore[arg-type] and issubclass(element_type, SExpressionBase) # type: ignore[arg-type] ): token_name = getattr(element_type, "__token_name__", None) element_field_type = FieldType.SEXPR_BASE except TypeError: pass return FieldInfo( name=name, field_type=element_field_type, inner_type=element_type, # type: ignore[arg-type] is_optional=False, is_list=True, token_name=token_name, position_index=position, ) try: if isinstance(inner_type, type) and issubclass(inner_type, SExpressionBase): # For NamedValue, TokenFlag, and SymbolValue subclasses, # each instance has its own token, so we need to extract it from the default_factory token_name = None if issubclass(inner_type, (NamedValue, TokenFlag, SymbolValue)): if dataclass_field and dataclass_field.default_factory: try: default_instance = dataclass_field.default_factory() token_name = getattr(default_instance, "token", None) except (TypeError, AttributeError): pass else: token_name = getattr(inner_type, "__token_name__", None) return FieldInfo( name=name, field_type=FieldType.SEXPR_BASE, inner_type=inner_type, is_optional=is_optional, is_list=False, token_name=token_name, position_index=position, ) except TypeError: pass return FieldInfo( name=name, field_type=FieldType.PRIMITIVE, inner_type=inner_type, is_optional=is_optional, is_list=False, token_name=None, position_index=position, )
[docs] def to_sexpr(self) -> SExpr: """Serialize to S-expression.""" result: SExpr = [self.__token_name__] parsed_fields = getattr(self, "_parsed_fields", None) for field_info in self._classify_fields(): value = getattr(self, field_info.name) if value is None: continue # For parsed objects, only include fields that were in the original data if parsed_fields is not None and field_info.name not in parsed_fields: continue if isinstance(value, SExpressionBase): result.append(value.to_sexpr()) elif isinstance(value, list): for item in value: if isinstance(item, SExpressionBase): result.append(item.to_sexpr()) else: result.append(item) elif isinstance(value, Enum): result.append(value.value) else: result.append(value) return result
[docs] def to_sexpr_str(self, _indent_level: int = 0) -> str: """Convert to KiCad-formatted S-expression string. Args: _indent_level: Internal parameter for recursion depth Returns: Formatted S-expression string """ sexpr = self.to_sexpr() return self._format_sexpr_kicad_style(sexpr, _indent_level)
def _format_sexpr_kicad_style(self, sexpr: Any, indent_level: int = 0) -> str: """Format S-expression in KiCad style with tabs and unquoted tokens.""" if not isinstance(sexpr, list): return self._format_primitive_value(sexpr) if not sexpr: return "()" current_indent = "\t" * indent_level token_name = str(sexpr[0]) if len(sexpr) == 1: return f"{current_indent}({token_name})" # Separate primitives and nested lists primitive_values = [] nested_lists = [] for item in sexpr[1:]: if isinstance(item, list): nested_lists.append(item) else: primitive_values.append(self._format_primitive_value(item)) # Check for single line format: only primitives and short enough if not nested_lists and len(sexpr) <= 4: all_items = [token_name] + primitive_values return f"{current_indent}({' '.join(all_items)})" # Multi-line format: primitives on first line, nested lists indented primitive_part = f" {' '.join(primitive_values)}" if primitive_values else "" lines = [f"{current_indent}({token_name}{primitive_part}"] for nested_item in nested_lists: nested_formatted = self._format_sexpr_kicad_style( nested_item, indent_level + 1 ) lines.append(nested_formatted) lines.append(f"{current_indent})") return "\n".join(lines) def _format_primitive_value(self, value: Any) -> str: """Format primitive values for S-expression serialization.""" if isinstance(value, bool): return "yes" if value else "no" elif isinstance(value, UnquotedToken): return str(value) elif isinstance(value, Enum): return str(value.value) elif isinstance(value, str): escaped_value = value.replace("\\", "\\\\").replace('"', '\\"') return f'"{escaped_value}"' else: return str(value)
__all__ = [ "NamedString", "NamedInt", "NamedFloat", "NamedObject", "TokenFlag", "SymbolValue", "ParseStrictness", ]