README.md

# UzuParser

Parser for Strudel/Tidal-style mini-notation, used in live coding and algorithmic music generation.

## Overview

UzuParser converts text-based pattern notation into an Abstract Syntax Tree (AST). It's designed for live coding environments and algorithmic music systems, providing a simple yet expressive syntax for creating rhythmic and melodic patterns.

**Note:** UzuParser handles parsing only. For event generation and pattern transformations, see [UzuPattern](https://github.com/rpmessner/uzu_pattern).

## Installation

Add `uzu_parser` to your dependencies in `mix.exs`:

```elixir
def deps do
  [
    {:uzu_parser, "~> 0.6.0"}
  ]
end
```

## Quick Start

```elixir
# Parse a pattern - returns AST
{:ok, ast} = UzuParser.parse("bd sd hh")
# => {:ok, {:sequence, [
#      %{type: :atom, value: "bd", source_start: 0, source_end: 2},
#      %{type: :atom, value: "sd", source_start: 3, source_end: 5},
#      %{type: :atom, value: "hh", source_start: 6, source_end: 8}
#    ]}}

# For timed events, use UzuPattern:
pattern = UzuPattern.parse("bd sd hh")
haps = UzuPattern.query(pattern, 0)
# => [%Hap{value: %{sound: "bd"}, part: %{begin: Ratio.new(0,1), ...}}, ...]
```

## Syntax

### Basic Sequences

Space-separated sounds create a sequence:

```elixir
UzuParser.parse("bd sd hh sd")  # 4 elements in sequence
```

### Rests

Use `~` for silence:

```elixir
UzuParser.parse("bd ~ sd ~")  # kick and snare on alternating beats
```

### Subdivisions

Brackets create subdivisions within a step:

```elixir
UzuParser.parse("bd [sd sd] hh")  # snare plays twice as fast
UzuParser.parse("bd [sd hh cp]")  # three sounds in the time of one step
```

### Repetition

Asterisk multiplies elements:

```elixir
UzuParser.parse("bd*4")      # equivalent to "bd bd bd bd"
UzuParser.parse("[bd sd]*2") # repeat the subdivision
```

### Replication

Exclamation mark replicates with weight:

```elixir
UzuParser.parse("bd!4")      # four bds, each with weight 1
UzuParser.parse("[bd!3 sd]") # three bds then one sd in subdivision
```

### Division (Slow)

Slash spreads pattern across cycles:

```elixir
UzuParser.parse("[bd sd]/2")  # pattern takes 2 cycles to complete
```

### Sample Selection

Colon selects sample variants:

```elixir
UzuParser.parse("bd:0 bd:1 bd:2")  # different kick drum samples
```

### Polyphony (Chords)

Comma within brackets plays sounds simultaneously:

```elixir
UzuParser.parse("[bd,sd]")        # kick and snare together
UzuParser.parse("[c3,e3,g3]")     # C major chord
```

### Probability

Question mark adds probability:

```elixir
UzuParser.parse("bd?")       # 50% chance to play
UzuParser.parse("bd?0.25")   # 25% chance to play
```

### Weight

At sign specifies relative duration:

```elixir
UzuParser.parse("bd@2 sd")   # kick twice as long as snare
UzuParser.parse("bd@3 sd@1") # kick takes 3/4, snare takes 1/4
```

### Elongation

Underscore extends the previous sound:

```elixir
UzuParser.parse("bd _ sd")   # kick held for 2/3, snare for 1/3
UzuParser.parse("bd _ _ sd") # kick held for 3/4, snare for 1/4
```

### Alternation

Angle brackets cycle through options:

```elixir
UzuParser.parse("<bd sd hh>")  # bd on cycle 0, sd on cycle 1, hh on cycle 2
```

### Random Choice

Pipe randomly selects one option:

```elixir
UzuParser.parse("bd|sd|hh")  # pick one randomly per cycle
```

### Polymetric

Curly braces create independent timing:

```elixir
UzuParser.parse("{bd sd, hh hh hh}")  # 2-against-3 polyrhythm
UzuParser.parse("{bd sd hh}%8")       # fit pattern into 8 steps
```

### Euclidean Rhythms

Parentheses create Euclidean patterns:

```elixir
UzuParser.parse("bd(3,8)")      # 3 hits distributed over 8 steps
UzuParser.parse("bd(3,8,1)")    # with rotation offset
```

### Parameters

Pipe with key:value sets sound parameters:

```elixir
UzuParser.parse("bd|gain:0.8|speed:2")
UzuParser.parse("bd|lpf:2000|room:0.5")
```

### Period Separator

Period works like space but creates visual grouping:

```elixir
UzuParser.parse("bd sd . hh cp")  # same as "bd sd hh cp"
```

## AST Structure

The parser returns an AST with node types:

- `:sequence` - Sequential elements
- `:stack` - Polyphonic (simultaneous) elements
- `:subdivision` - Bracketed group with optional modifiers
- `:alternation` - Angle bracket alternation
- `:polymetric` - Curly brace polymetric group
- `:atom` - Sound/note with optional modifiers
- `:rest` - Silence (`~`)
- `:elongation` - Underscore continuation (`_`)

Each atom node includes:
- `value` - Sound name
- `sample` - Sample number (from `:n`)
- `repeat` - Repetition count (from `*n`)
- `replicate` - Replication count (from `!n`)
- `euclidean` - `{k, n, offset}` tuple (from `(k,n)` or `(k,n,o)`)
- `probability` - Float 0-1 (from `?` or `?n`)
- `weight` - Float (from `@n`)
- `params` - Map of parameters (from `|key:value`)
- `source_start`, `source_end` - Position in source string

## Source Position Tracking

All AST nodes include source positions for editor integration:

```elixir
{:ok, {:sequence, nodes}} = UzuParser.parse("bd sd")
[bd, sd] = nodes

bd.source_start  # => 0
bd.source_end    # => 2
sd.source_start  # => 3
sd.source_end    # => 5
```

This enables features like syntax highlighting, error reporting, and click-to-edit in live coding environments.

## Ecosystem

```
┌─────────────────┐     ┌─────────────────┐     ┌─────────────────┐
│   UzuParser     │────▶│   UzuPattern    │────▶│    Waveform     │
│   (parsing)     │     │ (interpretation │     │    (audio)      │
│                 │     │  & transforms)  │     │                 │
│ • parse/1       │     │ • Interpreter   │     │ • OSC           │
│ • mini-notation │     │ • Pattern struct│     │ • SuperDirt     │
│ • AST output    │     │ • fast/slow/rev │     │ • Web Audio     │
│                 │     │ • query/2       │     │ • scheduling    │
└─────────────────┘     └─────────────────┘     └─────────────────┘
```

- **UzuParser**: Parses mini-notation strings into AST
- **UzuPattern**: Interprets AST into patterns, applies transformations, queries events
- **Waveform**: Handles audio output via OSC/SuperDirt/Web Audio

## Error Handling

```elixir
# Successful parse
{:ok, ast} = UzuParser.parse("bd sd")

# Parse error
{:error, message} = UzuParser.parse("[bd sd")
# => {:error, "missing terminator: ]"}
```

## Development

```bash
# Run tests
mix test

# Generate documentation
mix docs

# Format code
mix format
```

## License

MIT License - See LICENSE for details

## Credits

Inspired by the pattern mini-notation from [TidalCycles](https://tidalcycles.org/) and [Strudel](https://strudel.cc/).