Skip to main content

A simple S-expression parser

Project description

simp_sexp

A simple S-expression parser for Python.

Features

  • Simple and lightweight S-expression parser
  • Parse and manipulate S-expressions with an intuitive object-oriented interface
  • Convert between string representations and Python data structures
  • Nested expressions are handled automatically
  • Pretty-printing support for readable output
  • Advanced search capabilities for finding elements within complex S-expressions
  • Support for quoted strings with proper escape handling
  • Automatic type conversion for numbers

Installation

pip install simp_sexp

Usage

Basic Parsing and Formatting

from simp_sexp import Sexp, prettify_sexp

# Parse a string into an S-expression
expr = Sexp("(define (factorial n) (if (= n 0) 1 (* n (factorial (- n 1)))))")
print(expr)
# Output: ['define', ['factorial', 'n'], ['if', ['=', 'n', 0], 1, ['*', 'n', ['factorial', ['-', 'n', 1]]]]]

# Convert an S-expression back to a string
s_expr = expr.to_str()
print(s_expr)
# Output: (define (factorial "n") (if (= "n" 0) 1 (* "n" (factorial (- "n" 1)))))

# Format with pretty printing (default behavior)
pretty = expr.to_str(indent=4)
print(pretty)
"""
Output:
(define
    (factorial "n")
    (if
        (= "n" 0)
        1
        (* "n" (factorial (- "n" 1)))))
"""

# Format without line breaks
compact = expr.to_str(break_inc=0)
print(compact)
# Output: (define (factorial "n") (if (= "n" 0) 1 (* "n" (factorial (- "n" 1)))))

Working with Simple Expressions

from simp_sexp import Sexp

# Simple list
expr1 = Sexp("(a b c)")
print(expr1)  # ['a', 'b', 'c']

# Numbers are automatically converted
expr2 = Sexp("(1 2.5 -3)")
print(expr2)  # [1, 2.5, -3]

# Mixed types
expr3 = Sexp("(add 10 20)")
print(expr3)  # ['add', 10, 20]

# Create S-expressions from Python lists
list_expr = Sexp(['define', ['square', 'x'], ['*', 'x', 'x']])
print(list_expr.to_str(break_inc=0))
# Output: (define (square "x") (* "x" "x"))

# Control quoting behavior
print(list_expr.to_str(quote_strs=False, break_inc=0))
# Output: (define (square x) (* x x))

Handling Nested Expressions

from simp_sexp import Sexp

# Nested lists
nested = Sexp("(a (b c) (d (e f)))")
print(nested)  # ['a', ['b', 'c'], ['d', ['e', 'f']]]

# Access elements
print(nested[0])  # 'a'
print(nested[1])  # ['b', 'c']
print(nested[2][1][0])  # 'e'

# Modify elements
nested[1][1] = 'modified'
print(nested)  # ['a', ['b', 'modified'], ['d', ['e', 'f']]]

# Add elements
nested[2][1].append('g')
print(nested)  # ['a', ['b', 'modified'], ['d', ['e', 'f', 'g']]]

# Lisp-like function calls
lambda_expr = Sexp("(lambda (x) (+ x 1))")
print(lambda_expr)  # ['lambda', ['x'], ['+', 'x', 1]]

Searching S-expressions

from simp_sexp import Sexp
import re

# Create a complex S-expression
config = Sexp("""
(config
  (version 1.0)
  (settings
    (theme dark)
    (font "Courier New")
    (size 12))
  (keybindings
    (save "Ctrl+S")
    (open "Ctrl+O")
    (preferences
      (toggle "Ctrl+P")
      (help "F1"))))
""")

# Search by key path (relative)
font_results = config.search("font")
print(font_results[0][1])  # ['font', 'Courier New']

# Search by absolute path
version_results = config.search("/config/version")
print(version_results[0][1])  # ['version', 1.0]

# Search using a function
results = config.search(lambda x: len(x) > 2 and x[0] == 'settings')
print(results[0][1])  # ['settings', ['theme', 'dark'], ['font', 'Courier New'], ['size', 12]]

# Search using regex
ctrl_bindings = config.search(re.compile(r'^Ctrl\+'))
print([match[1] for _, match in ctrl_bindings])  # Will show all Ctrl+ keybindings

# Search with contains=True to match any element
theme_results = config.search("dark", contains=True)
print(theme_results[0][1])  # ['theme', 'dark']

# Case-insensitive search
prefs = config.search("PREFERENCES", ignore_case=True)
print(prefs[0][1])  # ['preferences', ['toggle', 'Ctrl+P'], ['help', 'F1']]

Manipulating S-expressions

from simp_sexp import Sexp

# Start with a simple expression
expr = Sexp("(define x 10)")

# Convert to list and modify
expr[2] = 20
print(expr.to_str())  # (define "x" 20)

# Add elements
expr.append(['comment', 'updated value'])
print(expr.to_str(break_inc=0))  # (define "x" 20 (comment "updated value"))

# Create a new expression from scratch
new_expr = Sexp()
new_expr.append('if')
new_expr.append(['>', 'x', 0])
new_expr.append('positive')
new_expr.append('negative')
print(new_expr.to_str(quote_strs=False, break_inc=0))
# Output: (if (> x 0) positive negative)

# Replace parts of an expression
def replace_value(sublist):
    if sublist and sublist[0] == 'x':
        return ['y']
    return sublist

# Find and replace operations in complex expressions
math_expr = Sexp("(+ (* x 3) (/ x 2))")
for path, match in math_expr.search('x'):
    # Create the full path to the parent element
    parent_path = path[:-1]
    index = path[-1]
    
    # Navigate to the parent element
    parent = math_expr
    for i in parent_path:
        parent = parent[i]
        
    # Replace 'x' with 'y'
    parent[index] = 'y'

print(math_expr.to_str(quote_strs=False, break_inc=0))
# Output: (+ (* y 3) (/ y 2))

Working with Files

from simp_sexp import Sexp

# Example of loading an S-expression from a file
def load_config(filename):
    with open(filename, 'r') as f:
        config_str = f.read()
    return Sexp(config_str)

# Example of saving an S-expression to a file
def save_config(config_sexp, filename):
    with open(filename, 'w') as f:
        f.write(config_sexp.to_str(indent=2))

# Usage example (pseudo-code)
# config = load_config("config.sexp")
# config[1][2] = "new_value"  # Modify the config
# save_config(config, "config.sexp")

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

simp_sexp-0.2.2.tar.gz (16.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

simp_sexp-0.2.2-py3-none-any.whl (11.6 kB view details)

Uploaded Python 3

File details

Details for the file simp_sexp-0.2.2.tar.gz.

File metadata

  • Download URL: simp_sexp-0.2.2.tar.gz
  • Upload date:
  • Size: 16.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.12.8

File hashes

Hashes for simp_sexp-0.2.2.tar.gz
Algorithm Hash digest
SHA256 6f1b5dab59ffa933bfdcd593930aecb10e9412dd1ad6bf6c6c3e49d3495cd29d
MD5 2d36c78d5ed4c8fed804ca3d57cf60c2
BLAKE2b-256 feeedbfcacf8a4e290021abc62a9ffc9fc78d5247f5a977dee717dcf2df90e2e

See more details on using hashes here.

File details

Details for the file simp_sexp-0.2.2-py3-none-any.whl.

File metadata

  • Download URL: simp_sexp-0.2.2-py3-none-any.whl
  • Upload date:
  • Size: 11.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.12.8

File hashes

Hashes for simp_sexp-0.2.2-py3-none-any.whl
Algorithm Hash digest
SHA256 dc0ff5a018fabd806fc86733ce936c5dda1b8a5739085b237349cb0e9694d4e4
MD5 f5c044e12e6a00ff1e2a92ab3180fa94
BLAKE2b-256 84b5a05155c51873136bd7ca09cffc73223ef377be909a71d37f4516ad8aed30

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page