{"id":28823360,"url":"https://github.com/devbisme/simp_sexp","last_synced_at":"2025-07-25T19:42:29.436Z","repository":{"id":292147853,"uuid":"978129711","full_name":"devbisme/simp_sexp","owner":"devbisme","description":"A simple S-expression parser/beautifier written in Python.","archived":false,"fork":false,"pushed_at":"2025-05-19T02:11:24.000Z","size":58,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-05-19T03:27:43.305Z","etag":null,"topics":["kicad","lisp","s-expression","serialization","sexp"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/devbisme.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-05-05T14:12:03.000Z","updated_at":"2025-05-19T02:11:28.000Z","dependencies_parsed_at":"2025-05-08T11:42:30.217Z","dependency_job_id":"4404d218-a120-4635-8c7d-303e55ef866b","html_url":"https://github.com/devbisme/simp_sexp","commit_stats":null,"previous_names":["devbisme/simp_sexp"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/devbisme/simp_sexp","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devbisme%2Fsimp_sexp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devbisme%2Fsimp_sexp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devbisme%2Fsimp_sexp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devbisme%2Fsimp_sexp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/devbisme","download_url":"https://codeload.github.com/devbisme/simp_sexp/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devbisme%2Fsimp_sexp/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":260654685,"owners_count":23042679,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["kicad","lisp","s-expression","serialization","sexp"],"created_at":"2025-06-19T00:08:21.540Z","updated_at":"2025-07-25T19:42:29.425Z","avatar_url":"https://github.com/devbisme.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# simp_sexp\n\nA simple S-expression parser for Python.\n\n## Features\n\n- Simple and lightweight S-expression parser\n- Parse and manipulate S-expressions with an intuitive object-oriented interface\n- Convert between string representations and Python data structures\n- Nested expressions are handled automatically\n- Pretty-printing support for readable output\n- Advanced search capabilities for finding elements within complex S-expressions\n- Support for quoted strings with proper escape handling\n- Automatic type conversion for numbers\n- Convenient `value` property for extracting single values from labeled expressions\n\n## Installation\n\n```bash\npip install simp_sexp\n```\n\n## Usage\n\n### Basic Parsing and Formatting\n\n```python\nfrom simp_sexp import Sexp, prettify_sexp\n\n# Parse a string into an S-expression\nexpr = Sexp(\"(define (factorial n) (if (= n 0) 1 (* n (factorial (- n 1)))))\")\nprint(expr)\n# Output: ['define', ['factorial', 'n'], ['if', ['=', 'n', 0], 1, ['*', 'n', ['factorial', ['-', 'n', 1]]]]]\n\n# Convert an S-expression back to a string\ns_expr = expr.to_str()\nprint(s_expr)\n# Output: (define (factorial \"n\") (if (= \"n\" 0) 1 (* \"n\" (factorial (- \"n\" 1)))))\n\n# Format with pretty printing (default behavior)\npretty = expr.to_str(indent=4)\nprint(pretty)\n\"\"\"\nOutput:\n(define\n    (factorial \"n\")\n    (if\n        (= \"n\" 0)\n        1\n        (* \"n\" (factorial (- \"n\" 1)))))\n\"\"\"\n\n# Format without line breaks\ncompact = expr.to_str(break_inc=0)\nprint(compact)\n# Output: (define (factorial \"n\") (if (= \"n\" 0) 1 (* \"n\" (factorial (- \"n\" 1)))))\n```\n\n### Working with Simple Expressions\n\n```python\nfrom simp_sexp import Sexp\n\n# Simple list\nexpr1 = Sexp(\"(a b c)\")\nprint(expr1)  # ['a', 'b', 'c']\n\n# Numbers are automatically converted\nexpr2 = Sexp(\"(1 2.5 -3)\")\nprint(expr2)  # [1, 2.5, -3]\n\n# Mixed types\nexpr3 = Sexp(\"(add 10 20)\")\nprint(expr3)  # ['add', 10, 20]\n\n# Create S-expressions from Python lists\nlist_expr = Sexp(['define', ['square', 'x'], ['*', 'x', 'x']])\nprint(list_expr.to_str(break_inc=0))\n# Output: (define (square \"x\") (* \"x\" \"x\"))\n\n# Control quoting behavior\nprint(list_expr.to_str(quote_strs=False, break_inc=0))\n# Output: (define (square x) (* x x))\n```\n\n### Extracting Values with the `value` Property\n\nThe `value` property provides a convenient way to extract single values from S-expressions \nthat contain a single labeled element (a two-element list with a label and value).\n\n```python\nfrom simp_sexp import Sexp\n\n# Extract simple values\nversion = Sexp(\"((version 20171130))\")\nprint(version.value)  # 20171130\n\ndescription = Sexp('((description \"A test component\"))')\nprint(description.value)  # A test component\n\n# Combining with search results\nconfig = Sexp(\"\"\"\n(kicad_pcb\n  (version 20171130)\n  (general\n    (thickness 1.6)\n    (drawings 5))\n  (layers\n    (0 F.Cu signal)))\n\"\"\")\n\n# Find and extract version\nprint(f\"PCB version: {config.search('/kicad_pcb/version').value}\")  # PCB version: 20171130\n\n# Find and extract thickness\nprint(f\"Board thickness: {config.search('/kicad_pcb/general/thickness').value}mm\")  # Board thickness: 1.6mm\n\n# Extract values from multiple search results\nprint(f\"Number of drawings: {config.search('/kicad_pcb/general/drawings').value}\")  # Number of drawings: 5\n\n# Error handling - value property requires specific structure\ntry:\n    invalid = Sexp(\"(multiple elements here)\")\n    print(invalid.value)  # This will raise ValueError\nexcept ValueError as e:\n    print(f\"Error: {e}\")  # Error: Sexp isn't in a form that permits extracting a single value.\n\ntry:\n    empty = Sexp(\"()\")\n    print(empty.value)  # This will also raise ValueError\nexcept ValueError as e:\n    print(f\"Error: {e}\")  # Error: Sexp isn't in a form that permits extracting a single value.\n```\n\n### Handling Nested Expressions\n\n```python\nfrom simp_sexp import Sexp\n\n# Nested lists\nnested = Sexp(\"(a (b c) (d (e f)))\")\nprint(nested)  # ['a', ['b', 'c'], ['d', ['e', 'f']]]\n\n# Access elements\nprint(nested[0])  # 'a'\nprint(nested[1])  # ['b', 'c']\nprint(nested[2][1][0])  # 'e'\n\n# Modify elements\nnested[1][1] = 'modified'\nprint(nested)  # ['a', ['b', 'modified'], ['d', ['e', 'f']]]\n\n# Add elements\nnested[2][1].append('g')\nprint(nested)  # ['a', ['b', 'modified'], ['d', ['e', 'f', 'g']]]\n\n# Lisp-like function calls\nlambda_expr = Sexp(\"(lambda (x) (+ x 1))\")\nprint(lambda_expr)  # ['lambda', ['x'], ['+', 'x', 1]]\n```\n\n### Searching S-expressions\n\n```python\nfrom simp_sexp import Sexp\nimport re\n\n# Create a complex S-expression\nconfig = Sexp(\"\"\"\n(config\n  (version 1.0)\n  (settings\n    (theme dark)\n    (font \"Courier New\")\n    (size 12))\n  (keybindings\n    (save \"Ctrl+S\")\n    (open \"Ctrl+O\")\n    (preferences\n      (toggle \"Ctrl+P\")\n      (help \"F1\"))))\n\"\"\")\n\n# Search by key path (relative)\nfont_results = config.search(\"font\")\nprint(font_results[0][1])  # ['font', 'Courier New']\n\n# Search by absolute path\nversion_results = config.search(\"/config/version\")\nprint(version_results[0][1])  # ['version', 1.0]\n\n# Search using a function\nresults = config.search(lambda x: len(x) \u003e 2 and x[0] == 'settings')\nprint(results[0][1])  # ['settings', ['theme', 'dark'], ['font', 'Courier New'], ['size', 12]]\n\n# Search using regex\nctrl_bindings = config.search(re.compile(r'^Ctrl\\+'))\nprint([match[1] for _, match in ctrl_bindings])  # Will show all Ctrl+ keybindings\n\n# Search with contains=True to match any element\ntheme_results = config.search(\"dark\", contains=True)\nprint(theme_results[0][1])  # ['theme', 'dark']\n\n# Case-insensitive search\nprefs = config.search(\"PREFERENCES\", ignore_case=True)\nprint(prefs[0][1])  # ['preferences', ['toggle', 'Ctrl+P'], ['help', 'F1']]\n```\n\n### Manipulating S-expressions\n\n```python\nfrom simp_sexp import Sexp\n\n# Start with a simple expression\nexpr = Sexp(\"(define x 10)\")\n\n# Convert to list and modify\nexpr[2] = 20\nprint(expr.to_str())  # (define \"x\" 20)\n\n# Add elements\nexpr.append(['comment', 'updated value'])\nprint(expr.to_str(break_inc=0))  # (define \"x\" 20 (comment \"updated value\"))\n\n# Create a new expression from scratch\nnew_expr = Sexp()\nnew_expr.append('if')\nnew_expr.append(['\u003e', 'x', 0])\nnew_expr.append('positive')\nnew_expr.append('negative')\nprint(new_expr.to_str(quote_strs=False, break_inc=0))\n# Output: (if (\u003e x 0) positive negative)\n\n# Replace parts of an expression\ndef replace_value(sublist):\n    if sublist and sublist[0] == 'x':\n        return ['y']\n    return sublist\n\n# Find and replace operations in complex expressions\nmath_expr = Sexp(\"(+ (* x 3) (/ x 2))\")\nfor path, match in math_expr.search('x'):\n    # Create the full path to the parent element\n    parent_path = path[:-1]\n    index = path[-1]\n    \n    # Navigate to the parent element\n    parent = math_expr\n    for i in parent_path:\n        parent = parent[i]\n        \n    # Replace 'x' with 'y'\n    parent[index] = 'y'\n\nprint(math_expr.to_str(quote_strs=False, break_inc=0))\n# Output: (+ (* y 3) (/ y 2))\n```\n\n### Working with Files\n\n```python\nfrom simp_sexp import Sexp\n\n# Example of loading an S-expression from a file\ndef load_config(filename):\n    with open(filename, 'r') as f:\n        config_str = f.read()\n    return Sexp(config_str)\n\n# Example of saving an S-expression to a file\ndef save_config(config_sexp, filename):\n    with open(filename, 'w') as f:\n        f.write(config_sexp.to_str(indent=2))\n\n# Usage example (pseudo-code)\n# config = load_config(\"config.sexp\")\n# config[1][2] = \"new_value\"  # Modify the config\n# save_config(config, \"config.sexp\")\n```\n\n## License\n\nMIT","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdevbisme%2Fsimp_sexp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdevbisme%2Fsimp_sexp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdevbisme%2Fsimp_sexp/lists"}