Source code for kicadfiles.sexpr_parser

"""S-Expression Parser for KiCad files

This module provides a simplified approach to parsing S-expressions.
Only contains the functions actually used by the kicadfiles package.
"""

from __future__ import annotations

from typing import Any, List, cast

from .sexpdata import dumps, loads

# Type definitions
SExprValue = Any  # Can be Symbol, str, int, float, or nested list
SExpr = List[SExprValue]


[docs] def str_to_sexpr(content: str) -> SExpr: """Convert string content to S-expression. Args: content: String content containing S-expression data Returns: Parsed S-expression as nested lists/atoms Raises: ValueError: If content cannot be parsed as valid S-expression """ try: return cast(SExpr, loads(content)) except Exception as e: raise ValueError(f"Failed to parse S-expression: {e}") from e
[docs] def sexpr_to_str(sexpr: SExpr) -> str: """Convert S-expression to string representation. Args: sexpr: S-expression to serialize Returns: String representation of the S-expression Raises: ValueError: If sexpr cannot be serialized """ try: return dumps(sexpr) except Exception as e: raise ValueError(f"Failed to serialize S-expression: {e}") from e
[docs] class SExprParser: """Minimal S-Expression parser with usage tracking always enabled."""
[docs] def __init__(self, sexpr: SExpr) -> None: """Initialize parser with an S-expression. Args: sexpr: Parsed S-expression as nested lists/atoms """ self.sexpr = sexpr self.used_indices: set[int] = set()
[docs] @classmethod def from_string(cls, sexpr_string: str) -> "SExprParser": """Create parser from S-expression string. Args: sexpr_string: String containing S-expression data Returns: New parser instance """ return cls(str_to_sexpr(sexpr_string))
[docs] def mark_used(self, index: int) -> None: """Mark a parameter index as used. Args: index: Index in sexpr that was accessed """ self.used_indices.add(index)
[docs] def get_unused_parameters(self) -> List[Any]: """Get list of unused parameters. Returns: List of unused parameters (excluding token name at index 0) """ unused = [] # Skip index 0 (token name) and check remaining parameters for i in range(1, len(self.sexpr)): if i not in self.used_indices: unused.append(self.sexpr[i]) return unused