A lightweight framework for building tiny LLM-friendly DSLs
Project description
Grammar School - Python Implementation
A lightweight framework for building tiny LLM-friendly DSLs in Python.
Installation
pip install grammar-school
For development:
pip install -e ".[dev]"
Quick Start
from grammar_school import Action, Grammar, verb
class MyGrammar(Grammar):
@verb
def greet(self, name, _context=None):
# @verb methods return Actions (data structures)
# They are pure - no side effects here!
return Action(kind="greet", payload={"name": name})
# Default runtime prints actions - no Runtime import needed!
grammar = MyGrammar()
grammar.execute('greet(name="World")')
# Or provide a custom runtime for actual behavior
from grammar_school import Runtime
class MyRuntime(Runtime):
def __init__(self):
self.greetings = [] # Runtime manages state
def execute(self, action: Action) -> None:
# Runtime performs actual side effects
if action.kind == "greet":
name = action.payload["name"]
self.greetings.append(name)
print(f"Hello, {name}!")
grammar = MyGrammar(runtime=MyRuntime())
grammar.execute('greet(name="World")')
Understanding the Architecture
Grammar School uses a two-layer architecture:
- Grammar + @verb methods: Transform DSL syntax → Actions (pure, no side effects)
- Runtime: Execute Actions → Real world effects (side effects, state management)
Why this separation?
- Same Grammar works with different Runtimes (testing, production, mocking)
- @verb methods are pure and easily testable
- Runtime handles all state and side effects independently
Runtime Output
Default Runtime: Prints actions to stdout (standard output/console)
Custom Runtimes: Can output anywhere:
- Files (write to disk)
- Databases (store in SQL/NoSQL)
- APIs (HTTP requests)
- Logging systems
- In-memory structures
- Or any combination
Example custom runtime that writes to a file:
class FileRuntime(Runtime):
def __init__(self, filename: str):
self.filename = filename
def execute(self, action: Action) -> None:
with open(self.filename, 'a') as f:
f.write(f"{action.kind}: {action.payload}\n")
grammar = MyGrammar(runtime=FileRuntime("output.log"))
Streaming Actions
For large DSL programs or real-time processing, you can stream actions as they're generated:
grammar = MyGrammar()
# Stream actions one at a time (memory efficient)
for action in grammar.stream('track(name="A").track(name="B").track(name="C")'):
print(f"Got action: {action.kind}")
# Process action immediately, don't wait for all actions
runtime.execute(action) # Execute as they arrive
This is useful for:
- Large programs: Don't load all actions into memory at once
- Real-time processing: Start executing actions before compilation completes
- Memory efficiency: Process actions incrementally
Functional Programming Support
Grammar School supports functional programming paradigms through the FunctionalMixin:
from grammar_school import Grammar, FunctionalMixin, verb, Action
class MyGrammar(Grammar, FunctionalMixin):
@verb
def square(self, x, _context=None):
return Action(kind="square", payload={"value": x * x})
@verb
def is_even(self, x, _context=None):
return Action(kind="is_even", payload={"value": x % 2 == 0})
grammar = MyGrammar()
# Use functional operations with function references
grammar.execute('map(@square, data)')
grammar.execute('filter(@is_even, data)')
grammar.execute('map(@square, data).filter(@is_even, data)')
Available functional operations:
map(@function, data)- Map a function over datafilter(@predicate, data)- Filter data using a predicatereduce(@function, data, initial)- Reduce data using a functioncompose(@f, @g, @h)- Compose multiple functionspipe(data, @f, @g, @h)- Pipe data through functions
Function references: Use @function_name syntax to pass functions as arguments.
Examples
See the examples/ directory for complete DSL implementations.
API Reference
Core Types
Value: AST value node (number, string, identifier, bool)Arg: Named argumentCall: Function call with argumentsCallChain: Chain of calls (method chaining)Action: Runtime action produced by interpreterRuntime: Protocol for executing actions
Decorators
@verb: Mark a method as a verb handler@rule: Define grammar rules (for custom grammars)
Classes
Grammar: Main grammar class that orchestrates parsing and interpretationInterpreter: Interprets CallChain AST into ActionsLarkBackend: Lark-based parser backend
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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file grammar_school-0.3.0.tar.gz.
File metadata
- Download URL: grammar_school-0.3.0.tar.gz
- Upload date:
- Size: 22.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3be3192d5839b589ce75167baef8ebd17ac9a37ad58020bbe30b624c86e40008
|
|
| MD5 |
d5db9eb1cde0c97efaa84a04a51814f6
|
|
| BLAKE2b-256 |
8a9ef125be9fd01c25e5254337f5d1ecf80bedd8442f3db425d25c713fbd0bf7
|
File details
Details for the file grammar_school-0.3.0-py3-none-any.whl.
File metadata
- Download URL: grammar_school-0.3.0-py3-none-any.whl
- Upload date:
- Size: 20.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2a43145f7ba6fbb0b7d80852a75b982f34f4ef0945ac443d7a939339046ac462
|
|
| MD5 |
574579847e9b6fb7f0d5b9f799781aab
|
|
| BLAKE2b-256 |
529c9e8d087067655cfe8bde228bd4e102dba632041d436d110ec0c07caed598
|