Core Concepts¶
Grammar School follows a consistent conceptual model across all language implementations.
The Pipeline¶
Every DSL program flows through this pipeline:
Note: Internally, Grammar School maintains a two-layer architecture (Grammar/Runtime), but this is hidden from users. Methods execute directly when called.
DSL Program¶
A DSL program is a plain string, typically generated by an LLM:
track(name="Drums").add_clip(start=0, length=8)
Cmaj7(1,1), Fmaj7(2,1)
tracks().filter(name~="FX").mute()
Abstract Syntax Tree (AST)¶
Grammar School uses a simple AST structure:
- Value: Represents a value (number, string, identifier, bool)
- Arg: A named argument with a value
- Call: A function call with named arguments
- CallChain: A chain of calls connected by dots (method chaining)
Example AST¶
For the code greet(name="Alice", count=2):
CallChain
└─ Call(name="greet")
├─ Arg(name="name", value=Value(kind="string", value="Alice"))
└─ Arg(name="count", value=Value(kind="number", value=2))
Methods¶
Methods are DSL handlers that contain the actual implementation:
func (d *MyDSL) Track(args gs.Args, ctx *gs.Context) ([]gs.Action, *gs.Context, error) {
name := args["name"].Str
color := ""
if c, ok := args["color"]; ok {
color = c.Str
}
action := gs.Action{
Kind: "create_track",
Payload: map[string]interface{}{
"name": name,
"color": color,
},
}
return []gs.Action{action}, ctx, nil
}
Execution¶
Methods execute directly when called - no Runtime needed:
State Management¶
State is managed using self attributes in your Grammar class:
class MusicDSL(Grammar):
def __init__(self):
super().__init__()
self.tracks = []
self.current_track = None
@method
def track(self, name):
self.current_track = {"name": name}
self.tracks.append(self.current_track)
@method
def add_clip(self, start, length):
if self.current_track:
# Access state via self
self.current_track["clips"].append({"start": start, "length": length})
Grammar¶
Grammars define the syntax of your DSL. Grammar School provides a default grammar, but you can customize it: