Python API Reference¶
Complete API reference for the Grammar School Python implementation.
Core Types¶
Value
dataclass
¶
Arg
dataclass
¶
Call
dataclass
¶
CallChain
dataclass
¶
A chain of calls connected by dots (method chaining).
Can be initialized with a list, iterator, or any iterable of Call objects.
Initialize CallChain with calls.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
calls
|
list[Call] | Iterator[Call] | Iterable[Call] | None
|
List, iterator, or iterable of Call objects. If None, creates empty chain. |
None
|
Source code in grammar-school-python/grammar_school/ast.py
Method Decorators¶
method ¶
Decorator to mark a method as a direct implementation handler.
Methods decorated with @method contain the actual implementation. The framework handles the Grammar/Runtime split internally.
Example
@method def greet(self, name): print(f"Hello, {name}!") # Can do anything here - side effects, state changes, etc.
Source code in grammar-school-python/grammar_school/grammar.py
Note: The Action and Runtime types still exist internally for the two-layer architecture, but users don't need to interact with them directly when using the unified @method interface.
Grammar¶
Grammar ¶
Grammar(grammar: str | GrammarBuilder | dict[str, Any] | Path | None = None, grammar_file: str | Path | None = None)
Main Grammar class for Grammar School.
Subclass this and define @verb methods to create your DSL handlers. Then use parse(), compile(), or execute() to process DSL scripts.
The Two-Layer Architecture:
- @verb methods (in Grammar subclass):
- Transform DSL syntax into Action data structures
- Pure functions - no side effects, just return Actions
-
Example:
track(name="Drums")→Action(kind="create_track", payload={...}) -
Runtime (separate class):
- Takes Actions and performs actual side effects
- Handles state management, I/O, database operations, etc.
- Example: Receives
Action(kind="create_track", ...)→ creates actual track in system
This separation allows: - Same Grammar to work with different Runtimes (testing vs production) - @verb methods to be testable without side effects - Runtime to manage state independently of Grammar logic
Example
from grammar_school import Grammar, verb, Action
class MyGrammar(Grammar):
@verb
def greet(self, name, _context=None):
# Pure function - just returns Action, no side effects
return Action(kind="greet", payload={"name": name})
# Default runtime prints actions - no need to import Runtime!
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:
# This is where side effects happen
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")')
Initialize grammar with optional custom grammar definition.
Args:
grammar: Optional custom grammar. Can be:
- String (Lark grammar definition)
- GrammarBuilder instance
- Dict (grammar config - will be loaded via load_grammar_from_config)
- Path (to YAML/TOML grammar config file)
- None (uses Grammar School's default)
grammar_file: Optional path to YAML/TOML grammar config file (alternative to grammar)
Example:
```python
# Using @method handlers - simple and direct
class MyDSL(Grammar):
@method
def greet(self, name):
print(f"Hello, {name}!")
dsl = MyDSL() # No runtime needed
dsl.execute('greet(name="World")')
# Using string
grammar = MyGrammar(grammar="start: call_chain
call_chain: call (DOT call)*")
# Using GrammarBuilder
from grammar_school import GrammarBuilder
builder = GrammarBuilder.default()
grammar = MyGrammar(grammar=builder)
# Using config dict
config = {
"start": "start",
"rules": [
{"name": "start", "definition": "call_chain"},
{"name": "call_chain", "definition": "call (DOT call)*"}
]
}
grammar = MyGrammar(grammar=config)
# Using config file
grammar = MyGrammar(grammar_file="grammar.yaml")
# or
grammar = MyGrammar(grammar="grammar.toml") # Path as string
```
Source code in grammar-school-python/grammar_school/grammar.py
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 | |
Attributes¶
Functions¶
compile ¶
Compile DSL code by executing methods.
Note: Methods execute directly during compilation. Returns a list of None values (one per method call).
Source code in grammar-school-python/grammar_school/grammar.py
execute ¶
Execute DSL code by calling methods directly.
Methods decorated with @method are executed immediately when called. No runtime is needed - methods contain their own implementation.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
code
|
str
|
DSL code string to execute |
required |
Example
Source code in grammar-school-python/grammar_school/grammar.py
parse ¶
stream ¶
Stream method executions from DSL code.
This is a generator that executes methods one at a time, allowing for memory-efficient processing and real-time execution of large DSL programs.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
code
|
str
|
DSL code string to execute and stream |
required |
Yields:
| Name | Type | Description |
|---|---|---|
None |
One None per method executed (methods execute during iteration) |
Example
Source code in grammar-school-python/grammar_school/grammar.py
rule ¶
Decorator to define grammar rules.
Supports three forms: 1. @rule("call_chain: call ('.' call)") 2. @rule(call_chain="call ('.' call)") 3. @rule(call_chain = sym("call") + many(lit(".") + sym("call")))
Source code in grammar-school-python/grammar_school/grammar.py
method ¶
Decorator to mark a method as a direct implementation handler.
Methods decorated with @method contain the actual implementation. The framework handles the Grammar/Runtime split internally.
Example
@method def greet(self, name): print(f"Hello, {name}!") # Can do anything here - side effects, state changes, etc.
Source code in grammar-school-python/grammar_school/grammar.py
Interpreter¶
Interpreter ¶
Interprets CallChain AST and executes methods directly.
Initialize interpreter with a DSL instance containing method handlers.
Source code in grammar-school-python/grammar_school/interpreter.py
Attributes¶
Functions¶
interpret ¶
Interpret a CallChain by executing methods directly.
Note: This method exists for compatibility but methods execute directly during interpret_stream. The returned list will contain None values (one per method call executed).
Source code in grammar-school-python/grammar_school/interpreter.py
interpret_stream ¶
Interpret a CallChain by executing methods directly (streaming).
This is a generator that executes methods one at a time, allowing for memory-efficient processing of large DSL programs.
Yields:
| Name | Type | Description |
|---|---|---|
None |
One None per method executed (for compatibility with Action-based interface) |
Source code in grammar-school-python/grammar_school/interpreter.py
Parser Backend¶
LarkBackend ¶
LarkBackend(grammar: str = DEFAULT_GRAMMAR, transformer: Transformer | None = None, use_smart_transformer: bool = True)
Lark-based parser backend.
Initialize with a Lark grammar string.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
grammar
|
str
|
Lark grammar string |
DEFAULT_GRAMMAR
|
transformer
|
Transformer | None
|
Optional custom transformer (if None, uses SmartTransformer or ASTTransformer) |
None
|
use_smart_transformer
|
bool
|
If True, use SmartTransformer (adapts to any grammar). If False, use ASTTransformer (coupled to default grammar). |
True
|
Source code in grammar-school-python/grammar_school/backend_lark.py
Attributes¶
Functions¶
clean_grammar_for_cfg
staticmethod
¶
Clean Lark grammar for use with CFG systems (e.g., GPT-5).
Removes Lark-specific directives that aren't supported in standard CFG: - %import directives - %ignore directives - Other %-prefixed directives
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
grammar
|
str
|
Lark grammar string with directives |
required |
Returns:
| Type | Description |
|---|---|
str
|
Cleaned grammar string suitable for CFG systems |
Example
Source code in grammar-school-python/grammar_school/backend_lark.py
parse ¶
Parse code into a CallChain AST.
Source code in grammar-school-python/grammar_school/backend_lark.py
DEFAULT_GRAMMAR
module-attribute
¶
DEFAULT_GRAMMAR = '\nstart: statement+\n\n// Statement is a call chain (which can be a single call or multiple chained calls)\nstatement: call_chain\n\ncall_chain: call (DOT call)*\ncall: IDENTIFIER "(" args? ")"\nargs: arg (COMMA arg)*\narg: IDENTIFIER "=" expression\n | expression\n\n// Expression with operator precedence (lowest to highest)\nexpression: comparison\ncomparison: addition (comparison_op addition)*\ncomparison_op: EQ | NE | LT | GT | LE | GE\naddition: multiplication (add_op multiplication)*\nadd_op: PLUS | MINUS\nmultiplication: atom (mul_op atom)*\nmul_op: MUL | DIV\natom: NUMBER\n | STRING\n | BOOL\n | IDENTIFIER\n | property_access\n | function_ref\n | "(" expression ")"\n\n// Property access: track.name\nproperty_access: IDENTIFIER (DOT IDENTIFIER)+\n\n// Function reference: @function_name syntax\nfunction_ref: "@" IDENTIFIER\n\n// Operators\nPLUS: "+"\nMINUS: "-"\nMUL: "*"\nDIV: "/"\nEQ: "=="\nNE: "!="\nLT: "<"\nGT: ">"\nLE: "<="\nGE: ">="\n\nDOT: "."\nCOMMA: ","\nNUMBER: /-?\\d+(\\.\\d+)?/\nSTRING: /"([^"\\\\]|\\\\.)*"|\'([^\'\\\\]|\\\\.)*\'/\nIDENTIFIER: /[a-zA-Z_][a-zA-Z0-9_]*/\nBOOL: "true" | "false"\n\n%import common.WS\n%ignore WS\n'
OpenAI CFG Utilities¶
Grammar School provides utilities for integrating with OpenAI's Context-Free Grammar (CFG) feature, allowing you to use Grammar School grammars as constraints for GPT-5.
CFGConfig¶
Configuration for building an OpenAI CFG tool.
Example:
from grammar_school.openai_utils import CFGConfig
config = CFGConfig(
tool_name="magda_dsl",
description="Generates MAGDA DSL code for REAPER automation",
grammar=grammar_string,
syntax="lark",
)
build_openai_cfg_tool¶
Builds an OpenAI CFG tool payload from a CFGConfig. This function:
- Cleans the grammar using LarkBackend.clean_grammar_for_cfg() to remove unsupported Lark directives
- Returns the properly formatted OpenAI tool structure
- Ensures the syntax defaults to "lark" if not specified
Example:
from grammar_school.openai_utils import CFGConfig, build_openai_cfg_tool
tool = build_openai_cfg_tool(CFGConfig(
tool_name="magda_dsl",
description="Generates MAGDA DSL code for REAPER automation",
grammar=grammar_string,
syntax="lark",
))
# Add tool to OpenAI request: tools = [tool]
get_openai_text_format_for_cfg¶
Returns the text format configuration that should be used when making OpenAI requests with CFG tools. When using CFG, the text format must be set to "text" (not JSON schema) because the output is DSL code, not JSON.
Example:
from grammar_school.openai_utils import get_openai_text_format_for_cfg
params["text"] = get_openai_text_format_for_cfg()
clean_grammar_for_cfg¶
Cleans a Lark grammar for use with CFG systems (e.g., GPT-5). Removes Lark-specific directives that aren't supported in standard CFG:
- %import directives
- %ignore directives
- Other %-prefixed directives
CFGProvider Interface¶
Grammar School provides a CFGProvider interface for integrating with different LLM providers that support CFG. This allows you to use the same API with different LLM providers.
from abc import ABC, abstractmethod
class CFGProvider(ABC):
@abstractmethod
def build_tool(self, tool_name: str, description: str, grammar: str, syntax: str) -> dict[str, Any]:
"""Builds the vendor-specific CFG tool payload."""
pass
@abstractmethod
def get_text_format(self) -> dict[str, Any]:
"""Returns the text format configuration for the vendor's API."""
pass
@abstractmethod
def generate(self, prompt: str, model: str, tools: list[dict], text_format: dict, client=None, **kwargs) -> Any:
"""Generates DSL code using the vendor's LLM."""
pass
@abstractmethod
def extract_dsl_code(self, response: Any) -> str:
"""Extracts DSL code from the vendor's response."""
pass
OpenAICFGProvider¶
from grammar_school.cfg_vendor import OpenAICFGProvider
provider = OpenAICFGProvider()
cfg_tool = provider.build_tool(
tool_name="task_dsl",
description="Task management DSL",
grammar=grammar_string,
syntax="lark",
)
text_format = provider.get_text_format()
The OpenAICFGProvider class implements the CFGProvider interface for OpenAI's API. It handles:
- Building OpenAI-specific CFG tool payloads
- Configuring text format for CFG requests
- Generating DSL code using OpenAI's API
- Extracting DSL code from OpenAI responses
Example:
from grammar_school.cfg_vendor import OpenAICFGProvider
from openai import OpenAI
provider = OpenAICFGProvider()
cfg_tool = provider.build_tool(
tool_name="task_dsl",
description="Task management DSL",
grammar=grammar.backend.grammar,
syntax="lark",
)
text_format = provider.get_text_format()
client = OpenAI()
response = client.chat.completions.create(
model="gpt-5",
messages=[{"role": "user", "content": "Create a task"}],
tools=[cfg_tool],
tool_choice={"type": "required", "tool": {"name": "task_dsl"}},
**text_format,
)
dsl_code = provider.extract_dsl_code(response)
grammar.execute(dsl_code)