mirror of
https://github.com/arendst/Tasmota.git
synced 2025-12-02 14:17:44 +00:00
1576 lines
48 KiB
Markdown
1576 lines
48 KiB
Markdown
# DSL Reference - Animation DSL Language Specification
|
||
|
||
This document provides a comprehensive reference for the Animation DSL syntax, keywords, and grammar. It focuses purely on the language specification without implementation details.
|
||
|
||
For detailed information about the DSL transpiler's internal architecture and processing flow, see [TRANSPILER_ARCHITECTURE.md](TRANSPILER_ARCHITECTURE.md).
|
||
|
||
## Language Overview
|
||
|
||
The Animation DSL is a declarative language for defining LED strip animations. It uses natural, readable syntax with named parameters and supports colors, animations, sequences, and property assignments.
|
||
|
||
## Comments
|
||
|
||
Comments use the `#` character and extend to the end of the line:
|
||
|
||
```berry
|
||
# This is a full-line comment
|
||
# strip length 30 # This is an inline comment (TEMPORARILY DISABLED)
|
||
color bordeaux = 0x6F2C4F # This is an inline comment
|
||
```
|
||
|
||
Comments are preserved in the generated code and can appear anywhere in the DSL.
|
||
|
||
## Program Structure
|
||
|
||
A DSL program consists of statements that can appear in any order:
|
||
|
||
```berry
|
||
# Strip configuration is handled automatically
|
||
# strip length 60 # TEMPORARILY DISABLED
|
||
|
||
# Color definitions
|
||
color bordeaux = 0x6F2C4F
|
||
color majorelle = 0x6050DC
|
||
|
||
# Animation definitions
|
||
animation pulse_bordeaux = pulsating_animation(color=bordeaux, period=2s)
|
||
|
||
# Property assignments
|
||
pulse_red.priority = 10
|
||
|
||
# Sequences
|
||
sequence demo {
|
||
play pulse_bordeaux for 5s
|
||
wait 1s
|
||
}
|
||
|
||
# Execution
|
||
run demo
|
||
```
|
||
|
||
## Keywords
|
||
|
||
### Reserved Keywords
|
||
|
||
The following keywords are reserved and cannot be used as identifiers:
|
||
|
||
**Configuration Keywords:**
|
||
- `strip` - Strip configuration (temporarily disabled, reserved keyword)
|
||
- `set` - Variable assignment
|
||
- `import` - Import Berry modules
|
||
- `berry` - Embed arbitrary Berry code
|
||
|
||
**Definition Keywords:**
|
||
- `color` - Color definition
|
||
- `palette` - Palette definition
|
||
- `animation` - Animation definition
|
||
- `sequence` - Sequence definition
|
||
- `template` - Template definition
|
||
- `param` - Template parameter declaration
|
||
- `type` - Parameter type annotation
|
||
|
||
**Control Flow Keywords:**
|
||
- `play` - Play animation in sequence
|
||
- `wait` - Wait/pause in sequence
|
||
- `repeat` - Repeat loop
|
||
- `times` - Loop count specifier
|
||
- `for` - Duration specifier
|
||
- `run` - Execute animation or sequence
|
||
- `restart` - Restart value provider or animation from beginning
|
||
|
||
**Easing Keywords:**
|
||
- `linear` - Linear/triangle wave easing
|
||
- `triangle` - Triangle wave easing (alias for linear)
|
||
- `smooth` - Smooth cosine easing
|
||
- `sine` - Pure sine wave easing
|
||
- `ease_in` - Ease in transition (quadratic acceleration)
|
||
- `ease_out` - Ease out transition (quadratic deceleration)
|
||
- `ramp` - Ramp/sawtooth easing
|
||
- `sawtooth` - Sawtooth easing (alias for ramp)
|
||
- `square` - Square wave easing
|
||
- `elastic` - Elastic easing with spring-like overshoot
|
||
- `bounce` - Bounce easing like a ball with decreasing amplitude
|
||
|
||
**Value Keywords:**
|
||
- `true` - Boolean true
|
||
- `false` - Boolean false
|
||
- `nil` - Null value
|
||
- `transparent` - Transparent color
|
||
|
||
### Predefined Colors
|
||
|
||
The following color names are predefined and cannot be redefined:
|
||
|
||
**Primary Colors:**
|
||
- `red`, `green`, `blue`
|
||
- `white`, `black`
|
||
|
||
**Extended Colors:**
|
||
- `yellow`, `orange`, `purple`, `pink`
|
||
- `cyan`, `magenta`, `gray`, `grey`
|
||
- `silver`, `gold`, `brown`
|
||
- `lime`, `navy`, `olive`
|
||
- `maroon`, `teal`, `aqua`
|
||
- `fuchsia`, `indigo`, `violet`
|
||
- `crimson`, `coral`, `salmon`
|
||
- `khaki`, `plum`, `orchid`
|
||
- `turquoise`, `tan`, `beige`
|
||
- `ivory`, `snow`
|
||
- `transparent`
|
||
|
||
## Data Types
|
||
|
||
### Numbers
|
||
|
||
```berry
|
||
42 # Integer
|
||
3.14 # Floating point
|
||
-5 # Negative number
|
||
```
|
||
|
||
### Time Values
|
||
|
||
Time values require a unit suffix and are automatically converted to milliseconds:
|
||
|
||
```berry
|
||
500ms # Milliseconds (stays 500)
|
||
2s # Seconds (converted to 2000ms)
|
||
1m # Minutes (converted to 60000ms)
|
||
1h # Hours (converted to 3600000ms)
|
||
```
|
||
|
||
### Percentages
|
||
|
||
Percentages use the `%` suffix and are automatically converted to 0-255 range with possible over-shooting:
|
||
|
||
```berry
|
||
0% # 0 percent (converted to 0)
|
||
50% # 50 percent (converted to 128)
|
||
100% # 100 percent (converted to 255)
|
||
120% # 120 percent (converted to 306)
|
||
```
|
||
|
||
### Colors
|
||
|
||
#### Hexadecimal Colors
|
||
|
||
```berry
|
||
0xFF0000 # Red (RGB format)
|
||
0x80FF0000 # Semi-transparent red (ARGB format)
|
||
```
|
||
|
||
#### Named Colors
|
||
|
||
```berry
|
||
red # Predefined color name
|
||
blue # Predefined color name
|
||
transparent # Transparent color
|
||
```
|
||
|
||
### Strings
|
||
|
||
```berry
|
||
"hello" # Double-quoted string
|
||
'world' # Single-quoted string
|
||
```
|
||
|
||
### Identifiers
|
||
|
||
Identifiers must start with a letter or underscore, followed by letters, digits, or underscores:
|
||
|
||
```berry
|
||
my_color # Valid identifier
|
||
_private_var # Valid identifier
|
||
Color123 # Valid identifier
|
||
```
|
||
|
||
## Configuration Statements
|
||
|
||
### Strip Configuration
|
||
|
||
**Note: The `strip` directive is temporarily disabled.** Strip configuration is handled automatically by the host system.
|
||
|
||
~~The `strip` statement configures the LED strip and must be the first statement if present:~~
|
||
|
||
```berry
|
||
# strip length 60 # TEMPORARILY DISABLED
|
||
```
|
||
|
||
~~If omitted,~~ The system uses the configured strip length from the host system.
|
||
|
||
### Variable Assignment
|
||
|
||
The `set` keyword assigns static values or value providers to global variables:
|
||
|
||
```berry
|
||
set brightness = 200 # Static integer value
|
||
set cycle_time = 5s # Static time value (converted to 5000ms)
|
||
set opacity_level = 80% # Static percentage (converted to 204)
|
||
|
||
# Value providers for dynamic values
|
||
set brightness_osc = smooth(min_value=50, max_value=255, period=3s)
|
||
set position_sweep = triangle(min_value=0, max_value=29, period=5s)
|
||
|
||
# Computed values using strip length
|
||
set strip_len = strip_length() # Get current strip length
|
||
```
|
||
|
||
### Import Statements
|
||
|
||
The `import` keyword imports Berry modules for use in animations:
|
||
|
||
```berry
|
||
import user_functions # Import user-defined functions
|
||
import my_custom_module # Import custom animation libraries
|
||
import math # Import standard Berry modules
|
||
import string # Import utility modules
|
||
```
|
||
|
||
**Import Behavior:**
|
||
- Module names should be valid identifiers (no quotes needed in DSL)
|
||
- Import statements are typically placed at the beginning of DSL files
|
||
- Transpiles to standard Berry `import "module_name"` statements
|
||
- Imported modules become available for the entire animation
|
||
|
||
**Common Use Cases:**
|
||
```berry
|
||
# Import user functions for computed parameters
|
||
import user_functions
|
||
|
||
animation dynamic = solid(color=blue)
|
||
dynamic.opacity = my_custom_function()
|
||
|
||
# Import custom animation libraries
|
||
import fire_effects
|
||
|
||
animation campfire = fire_effects.create_fire(intensity=200)
|
||
```
|
||
|
||
**Transpilation Example:**
|
||
```berry
|
||
# DSL Code
|
||
import user_functions
|
||
|
||
# Transpiles to Berry Code
|
||
import "user_functions"
|
||
```
|
||
|
||
### Berry Code Blocks
|
||
|
||
The `berry` keyword allows embedding arbitrary Berry code within DSL files using triple-quoted strings:
|
||
|
||
```berry
|
||
berry """
|
||
import math
|
||
var custom_value = math.pi * 2
|
||
print("Custom calculation:", custom_value)
|
||
"""
|
||
|
||
berry '''
|
||
# Alternative syntax with single quotes
|
||
def helper_function(x)
|
||
return x * 1.5
|
||
end
|
||
'''
|
||
```
|
||
|
||
**Berry Code Block Features:**
|
||
- Code is copied verbatim to the generated Berry code
|
||
- Supports both `"""` and `'''` triple-quote syntax
|
||
- Can span multiple lines and include complex Berry syntax
|
||
- Variables and functions defined in one block are available in subsequent blocks
|
||
- Can interact with DSL-generated objects (e.g., `animation_name_.property = value`)
|
||
|
||
**Example with DSL Integration:**
|
||
```berry
|
||
animation pulse = pulsating_animation(color=red, period=2s)
|
||
|
||
berry """
|
||
# Modify animation using Berry code
|
||
pulse_.opacity = 200
|
||
pulse_.priority = 10
|
||
print("Animation configured")
|
||
"""
|
||
|
||
run pulse
|
||
```
|
||
|
||
## Color Definitions
|
||
|
||
The `color` keyword defines static colors or color providers:
|
||
|
||
```berry
|
||
# Static colors
|
||
color bordeaux = 0x6F2C4F # Static hex color
|
||
color majorelle = 0x6050DC # Static hex color
|
||
color semi_red = 0x80FF0000 # Static color with alpha channel
|
||
color my_white = white # Reference to predefined color
|
||
|
||
# Color providers for dynamic colors
|
||
color rainbow_cycle = color_cycle(
|
||
palette=bytes("FFFF0000" "FF00FF00" "FF0000FF")
|
||
cycle_period=5s
|
||
)
|
||
color breathing_red = breathe_color(
|
||
base_color=red
|
||
min_brightness=5%
|
||
max_brightness=100%
|
||
duration=3s
|
||
curve_factor=2
|
||
)
|
||
color pulsing_blue = pulsating_color(
|
||
base_color=blue
|
||
min_brightness=20%
|
||
max_brightness=80%
|
||
duration=1s
|
||
)
|
||
```
|
||
|
||
## Palette Definitions
|
||
|
||
Palettes define color gradients using position-color pairs and support two encoding formats with flexible syntax:
|
||
|
||
### Value-Based Palettes (Recommended)
|
||
|
||
Standard palettes use value positions from 0-255:
|
||
|
||
```berry
|
||
# Traditional syntax with commas
|
||
palette fire_colors = [
|
||
(0, 0x000000) # Position 0: Black
|
||
(128, 0xFF0000) # Position 128: Red
|
||
(255, 0xFFFF00) # Position 255: Yellow
|
||
]
|
||
|
||
# New syntax without commas (when entries are on separate lines)
|
||
palette ocean_palette = [
|
||
(0, navy) # Using named colors
|
||
(128, cyan)
|
||
(255, green)
|
||
]
|
||
|
||
# Mixed syntax also works
|
||
palette matrix_greens = [
|
||
(0, 0x000000), (64, 0x003300) # Multiple entries on one line
|
||
(128, 0x006600) # Single entry on separate line
|
||
(192, 0x00AA00)
|
||
(255, 0x00FF00)
|
||
]
|
||
```
|
||
|
||
### Tick-Based Palettes (Advanced)
|
||
|
||
Palettes can also use tick counts for timing-based transitions:
|
||
|
||
```berry
|
||
palette timed_colors = [
|
||
(10, 0xFF0000) # Red for 10 ticks
|
||
(20, 0x00FF00) # Green for 20 ticks
|
||
(15, 0x0000FF) # Blue for 15 ticks
|
||
]
|
||
```
|
||
|
||
**Palette Rules:**
|
||
- **Value-based**: Positions range from 0 to 255, represent intensity/brightness levels
|
||
- **Tick-based**: Positions represent duration in arbitrary time units
|
||
- **Colors**: Only hex values (0xRRGGBB) or predefined color names (red, blue, green, etc.)
|
||
- **Custom colors**: Previously defined custom colors are NOT allowed in palettes
|
||
- **Dynamic palettes**: For palettes with custom colors, use user functions instead
|
||
- Entries are automatically sorted by position
|
||
- Comments are preserved
|
||
- Automatically converted to efficient VRGB bytes format
|
||
|
||
### Palette Color Restrictions
|
||
|
||
Palettes have strict color validation to ensure compile-time safety:
|
||
|
||
**✅ Allowed:**
|
||
```berry
|
||
palette valid_colors = [
|
||
(0, 0xFF0000) # Hex colors
|
||
(128, red) # Predefined color names
|
||
(255, blue) # More predefined colors
|
||
]
|
||
```
|
||
|
||
**❌ Not Allowed:**
|
||
```berry
|
||
color custom_red = 0xFF0000
|
||
palette invalid_colors = [
|
||
(0, custom_red) # ERROR: Custom colors not allowed
|
||
(128, my_color) # ERROR: Undefined color
|
||
]
|
||
```
|
||
|
||
**Alternative for Dynamic Palettes:**
|
||
For palettes that need custom or computed colors, use user functions:
|
||
|
||
```berry
|
||
# Define a user function that creates dynamic palettes
|
||
def create_custom_palette(engine, base_color, intensity)
|
||
# Create palette with custom logic
|
||
var palette_data = create_dynamic_palette_bytes(base_color, intensity)
|
||
return palette_data
|
||
end
|
||
|
||
# Register for DSL use
|
||
animation.register_user_function("custom_palette", create_custom_palette)
|
||
```
|
||
|
||
```berry
|
||
# Use in DSL
|
||
animation dynamic_anim = rich_palette(
|
||
palette=custom_palette(0xFF0000, 200)
|
||
cycle_period=3s
|
||
)
|
||
```
|
||
|
||
## Animation Definitions
|
||
|
||
The `animation` keyword defines instances of animation classes (subclasses of Animation):
|
||
|
||
```berry
|
||
animation red_solid = solid(color=red)
|
||
|
||
animation pulse_effect = pulsating_animation(
|
||
color=blue
|
||
period=2s
|
||
)
|
||
|
||
animation comet_trail = comet_animation(
|
||
color=white
|
||
tail_length=10
|
||
speed=1500
|
||
direction=1
|
||
)
|
||
```
|
||
|
||
**Parameter Syntax:**
|
||
- All parameters use `name=value` format
|
||
- Parameters can reference colors, other animations, or literal values
|
||
- Nested function calls are supported
|
||
|
||
## Property Assignments
|
||
|
||
Animation properties can be modified after creation:
|
||
|
||
```berry
|
||
animation pulse_red = pulsating_animation(color=red, period=2s)
|
||
|
||
# Set properties
|
||
pulse_red.priority = 10
|
||
pulse_red.opacity = 200
|
||
pulse_red.position = 15
|
||
|
||
# Dynamic properties using value providers
|
||
pulse_red.position = triangle(min_value=0, max_value=29, period=5s)
|
||
pulse_red.opacity = smooth(min_value=100, max_value=255, period=2s)
|
||
|
||
# Computed properties using arithmetic expressions
|
||
set strip_len = strip_length()
|
||
pulse_red.position = strip_len / 2 # Center position
|
||
pulse_red.opacity = strip_len * 4 # Scale with strip size
|
||
|
||
# Animation opacity (using another animation as opacity mask)
|
||
animation opacity_mask = pulsating_animation(period=2s)
|
||
pulse_red.opacity = opacity_mask # Dynamic opacity from animation
|
||
```
|
||
|
||
**Common Properties:**
|
||
- `priority` - Animation priority (higher numbers have precedence)
|
||
- `opacity` - Opacity level (number, value provider, or animation)
|
||
- `position` - Position on strip
|
||
- `speed` - Speed multiplier
|
||
- `phase` - Phase offset
|
||
|
||
## Computed Values
|
||
|
||
The DSL supports computed values using arithmetic expressions with value providers and mathematical functions:
|
||
|
||
### Safe Patterns
|
||
|
||
```berry
|
||
# ✅ RECOMMENDED: Single value provider assignment
|
||
set strip_len = strip_length()
|
||
|
||
# ✅ RECOMMENDED: Computation with existing values
|
||
set strip_len2 = (strip_len + 1) / 2
|
||
|
||
# Use computed values in animation parameters
|
||
animation stream1 = comet_animation(
|
||
color=red
|
||
tail_length=strip_len / 4 # Computed: quarter of strip length
|
||
speed=1.5
|
||
priority=10
|
||
)
|
||
```
|
||
|
||
### ⚠️ Dangerous Patterns (Prevented by Transpiler)
|
||
|
||
The transpiler prevents dangerous patterns that would create new value provider instances at each evaluation:
|
||
|
||
```berry
|
||
# ❌ DANGEROUS: Function creation in computed expression
|
||
# This would create a new strip_length() instance at each evaluation
|
||
set strip_len3 = (strip_length() + 1) / 2
|
||
|
||
# ❌ ERROR: Transpiler will reject this with:
|
||
# "Function 'strip_length()' cannot be used in computed expressions.
|
||
# This creates a new instance at each evaluation."
|
||
```
|
||
|
||
**Why This Is Dangerous:**
|
||
- Creates a new function instance every time the expression is evaluated
|
||
- Causes memory leaks and performance degradation
|
||
- Each new instance has its own timing and state, leading to inconsistent behavior
|
||
|
||
**Safe Alternative:**
|
||
```berry
|
||
# ✅ CORRECT: Separate the value provider creation from computation
|
||
set strip_len = strip_length() # Single value provider
|
||
set strip_len3 = (strip_len + 1) / 2 # Computation with existing value
|
||
```
|
||
|
||
**Functions That Are Restricted in Computed Expressions:**
|
||
- Any function that creates instances (value providers, animations, etc.) when called
|
||
- Examples: `strip_length()`, `triangle()`, `smooth()`, `solid()`, etc.
|
||
|
||
**Note:** These functions are allowed in `set` statements as they create the instance once, but they cannot be used inside arithmetic expressions that get wrapped in closures, as this would create new instances at each evaluation.
|
||
|
||
### Advanced Computed Values
|
||
|
||
```berry
|
||
# Complex expressions with multiple operations
|
||
set base_speed = 2.0
|
||
animation stream2 = comet_animation(
|
||
color=blue
|
||
tail_length=strip_len / 8 + 2 # Computed: eighth of strip + 2
|
||
speed=base_speed * 1.5 # Computed: base speed × 1.5
|
||
)
|
||
|
||
# Computed values in property assignments
|
||
stream1.position = strip_len / 2 # Center of strip
|
||
stream2.opacity = strip_len * 4 # Scale opacity with strip size
|
||
|
||
# Using mathematical functions in computed values
|
||
animation pulse = pulsating_animation(
|
||
color=red
|
||
period=2s
|
||
)
|
||
pulse.opacity = abs(sine(strip_len) * 128 + 127) # Sine wave opacity
|
||
pulse.position = max(0, min(strip_len - 1, round(strip_len / 2))) # Clamped center position
|
||
```
|
||
|
||
**Supported Operations:**
|
||
- Addition: `+`
|
||
- Subtraction: `-`
|
||
- Multiplication: `*`
|
||
- Division: `/`
|
||
- Parentheses for grouping: `(expression)`
|
||
|
||
**Mathematical Functions:**
|
||
The following mathematical functions are available in computed parameters and are automatically detected by the transpiler:
|
||
|
||
| Function | Description | Parameters | Return Value |
|
||
|----------|-------------|------------|--------------|
|
||
| `min(a, b, ...)` | Returns the minimum value | Two or more numbers | Minimum value |
|
||
| `max(a, b, ...)` | Returns the maximum value | Two or more numbers | Maximum value |
|
||
| `abs(x)` | Returns the absolute value | One number | Absolute value |
|
||
| `round(x)` | Rounds to nearest integer | One number | Rounded integer |
|
||
| `sqrt(x)` | Returns the square root | One number | Square root (scaled for integers) |
|
||
| `scale(v, from_min, from_max, to_min, to_max)` | Scales value from one range to another | Value and range parameters | Scaled integer |
|
||
| `sin(angle)` | Returns sine of angle | Angle in 0-255 range (0-360°) | Sine value in -255 to 255 range |
|
||
| `cos(angle)` | Returns cosine of angle | Angle in 0-255 range (0-360°) | Cosine value in -255 to 255 range |
|
||
|
||
**Mathematical Function Examples:**
|
||
```berry
|
||
# Basic math functions
|
||
set strip_len = strip_length()
|
||
animation test = pulsating_animation(color=red, period=2s)
|
||
|
||
# Absolute value for ensuring positive results
|
||
test.opacity = abs(strip_len - 200)
|
||
|
||
# Min/max for clamping values
|
||
test.position = max(0, min(strip_len - 1, 15)) # Clamp position to valid range
|
||
|
||
# Rounding for integer positions
|
||
test.position = round(strip_len / 2.5)
|
||
|
||
# Square root for non-linear scaling
|
||
test.brightness = sqrt(strip_len * 4) # Non-linear brightness based on strip size
|
||
|
||
# Scaling values between ranges
|
||
test.opacity = scale(strip_len, 10, 60, 50, 255) # Scale strip length to opacity range
|
||
|
||
# Trigonometric functions for wave patterns
|
||
set angle = 128 # 180 degrees in 0-255 range
|
||
test.opacity = sin(angle) + 128 # Mathematical sine function (not oscillator)
|
||
test.brightness = cos(angle) + 128 # Mathematical cosine function (not oscillator)
|
||
|
||
# Complex expressions combining multiple functions
|
||
test.position = max(0, round(abs(sin(strip_len * 2)) * (strip_len - 1) / 255))
|
||
test.opacity = min(255, max(50, scale(sqrt(strip_len), 0, 16, 100, 255)))
|
||
```
|
||
|
||
**Special Notes:**
|
||
- **Integer Optimization**: `sqrt()` function automatically handles integer scaling for 0-255 range values
|
||
- **Trigonometric Range**: `sin()` and `cos()` use 0-255 input range (mapped to 0-360°) and return -255 to 255 output range
|
||
- **Automatic Detection**: Mathematical functions are automatically detected at transpile time using dynamic introspection
|
||
- **Closure Context**: In computed parameters, mathematical functions are called as `animation._math.<function>()` in the generated closure context
|
||
|
||
**How It Works:**
|
||
When the DSL detects arithmetic expressions containing value providers, variable references, or mathematical functions, it automatically creates closure functions that capture the computation. These closures are called with `(self, param_name, time_ms)` parameters, allowing the computation to be re-evaluated dynamically as needed. Mathematical functions are automatically prefixed with `animation._math.` in the closure context to access the ClosureValueProvider's mathematical methods.
|
||
|
||
**User Functions in Computed Parameters:**
|
||
User-defined functions can also be used in computed parameter expressions, providing powerful custom effects:
|
||
|
||
```berry
|
||
# Simple user function in computed parameter
|
||
animation base = solid(color=blue)
|
||
base.opacity = rand_demo()
|
||
|
||
# User functions mixed with math operations
|
||
animation dynamic = solid(
|
||
color=purple
|
||
opacity=max(50, min(255, rand_demo() + 100))
|
||
)
|
||
```
|
||
|
||
### User Functions
|
||
|
||
User functions are custom Berry functions that can be called from computed parameters. They provide dynamic values that change over time.
|
||
|
||
**Available User Functions:**
|
||
- `rand_demo()` - Returns random values for demonstration purposes
|
||
|
||
**Usage in Computed Parameters:**
|
||
```berry
|
||
# Simple user function
|
||
animation.opacity = rand_demo()
|
||
|
||
# User function with math operations
|
||
animation.opacity = max(100, rand_demo())
|
||
|
||
# User function in arithmetic expressions
|
||
animation.opacity = abs(rand_demo() - 128) + 64
|
||
```
|
||
|
||
**Available User Functions:**
|
||
The following user functions are available by default (see [User Functions Guide](USER_FUNCTIONS.md) for details):
|
||
|
||
| Function | Parameters | Description |
|
||
|----------|------------|-------------|
|
||
| `rand_demo()` | none | Returns a random value (0-255) for demonstration |
|
||
|
||
**User Function Behavior:**
|
||
- User functions are automatically detected by the transpiler
|
||
- They receive `self.engine` as the first parameter in closure context
|
||
- They can be mixed with mathematical functions and arithmetic operations
|
||
- The entire expression is wrapped in a single efficient closure
|
||
|
||
## Sequences
|
||
|
||
Sequences orchestrate multiple animations with timing control. The DSL supports two syntaxes for sequences with repeat functionality:
|
||
|
||
### Basic Sequence Syntax
|
||
|
||
```berry
|
||
sequence demo {
|
||
play red_animation for 3s
|
||
wait 1s
|
||
play blue_animation for 2s
|
||
|
||
repeat 3 times {
|
||
play flash_effect for 200ms
|
||
wait 300ms
|
||
}
|
||
|
||
play final_animation
|
||
}
|
||
```
|
||
|
||
### Repeat Sequence Syntax
|
||
|
||
For sequences that are primarily repeating patterns, you can use the alternative syntax:
|
||
|
||
```berry
|
||
# Option 1: Traditional syntax with repeat sub-sequence
|
||
sequence cylon_eye {
|
||
repeat forever {
|
||
play red_eye for 3s
|
||
red_eye.pos = triangle_val
|
||
play red_eye for 3s
|
||
red_eye.pos = cosine_val
|
||
eye_color.next = 1
|
||
}
|
||
}
|
||
|
||
# Option 2: Alternative syntax - sequence with repeat modifier
|
||
sequence cylon_eye repeat forever {
|
||
play red_eye for 3s
|
||
red_eye.pos = triangle_val
|
||
play red_eye for 3s
|
||
red_eye.pos = cosine_val
|
||
eye_color.next = 1
|
||
}
|
||
|
||
# Option 3: Parametric repeat count
|
||
sequence rainbow_cycle repeat palette.size times {
|
||
play animation for 1s
|
||
palette.next = 1
|
||
}
|
||
```
|
||
|
||
**Note**: All syntaxes are functionally equivalent. The repeat count can be a literal number, variable, or dynamic expression that evaluates at runtime.
|
||
|
||
### Sequence Statements
|
||
|
||
#### Play Statement
|
||
|
||
```berry
|
||
play animation_name # Play indefinitely
|
||
play animation_name for 5s # Play for specific duration
|
||
play animation_name for duration_var # Play for variable duration
|
||
```
|
||
|
||
#### Wait Statement
|
||
|
||
```berry
|
||
wait 1s # Wait for 1 second
|
||
wait 500ms # Wait for 500 milliseconds
|
||
wait duration_var # Wait for variable duration
|
||
```
|
||
|
||
#### Duration Support
|
||
|
||
Both `play` and `wait` statements support flexible duration specifications:
|
||
|
||
**Literal Time Values:**
|
||
```berry
|
||
play animation for 5s # 5 seconds
|
||
play animation for 2000ms # 2000 milliseconds
|
||
play animation for 1m # 1 minute
|
||
```
|
||
|
||
**Variable References:**
|
||
```berry
|
||
set short_time = 2s
|
||
set long_time = 10s
|
||
|
||
sequence demo {
|
||
play animation for short_time # Use variable duration
|
||
wait long_time # Variables work in wait too
|
||
}
|
||
```
|
||
|
||
**Value Providers (Dynamic Duration):**
|
||
```berry
|
||
set dynamic_duration = triangle(min_value=1000, max_value=5000, period=10s)
|
||
|
||
sequence demo {
|
||
play animation for dynamic_duration # Duration changes over time
|
||
}
|
||
```
|
||
|
||
**Examples:**
|
||
```berry
|
||
# Cylon eye with variable duration
|
||
set eye_duration = 5s
|
||
|
||
sequence cylon_eye forever {
|
||
play red_eye for eye_duration # Use variable for consistent timing
|
||
red_eye.pos = triangle_val
|
||
play red_eye for eye_duration # Same duration for both phases
|
||
red_eye.pos = cosine_val
|
||
eye_color.next = 1
|
||
}
|
||
```
|
||
|
||
#### Repeat Statement
|
||
|
||
Repeat statements create runtime sub-sequences that execute repeatedly:
|
||
|
||
```berry
|
||
repeat 3 times { # Repeat exactly 3 times
|
||
play animation for 1s
|
||
wait 500ms
|
||
}
|
||
|
||
repeat forever { # Repeat indefinitely until parent sequence stops
|
||
play animation for 1s
|
||
wait 500ms
|
||
}
|
||
|
||
repeat col1.palette_size times { # Parametric repeat count using property access
|
||
play animation for 1s
|
||
col1.next = 1
|
||
}
|
||
```
|
||
|
||
**Repeat Count Types:**
|
||
- **Literal numbers**: `repeat 5 times` - fixed repeat count
|
||
- **Variables**: `repeat count_var times` - using previously defined variables
|
||
- **Property access**: `repeat color_provider.palette_size times` - dynamic values from object properties
|
||
- **Computed expressions**: `repeat strip_length() / 2 times` - calculated repeat counts
|
||
|
||
**Repeat Behavior:**
|
||
- **Runtime Execution**: Repeats are executed at runtime, not expanded at compile time
|
||
- **Dynamic Evaluation**: Parametric repeat counts are evaluated when the sequence starts
|
||
- **Sub-sequences**: Each repeat block creates a sub-sequence that manages its own iteration state
|
||
- **Nested Repeats**: Supports nested repeats with multiplication (e.g., `repeat 3 times { repeat 2 times { ... } }` executes 6 times total)
|
||
- **Forever Loops**: `repeat forever` continues until the parent sequence is stopped
|
||
- **Efficient**: No memory overhead for large repeat counts
|
||
|
||
#### Assignment Statement
|
||
|
||
Property assignments can be performed within sequences to dynamically modify animation parameters during playback:
|
||
|
||
```berry
|
||
sequence demo {
|
||
play red_eye for 3s
|
||
red_eye.pos = triangle_val # Change position to triangle oscillator
|
||
play red_eye for 3s
|
||
red_eye.pos = cosine_val # Change position to cosine oscillator
|
||
eye_color.next = 1 # Advance color cycle to next color
|
||
}
|
||
```
|
||
|
||
**Assignment Semantics:**
|
||
- Assignments in sequences have exactly the same semantics as assignments outside sequences
|
||
- They can assign static values, value providers, or computed expressions
|
||
- Assignments are executed instantly when the sequence step is reached
|
||
- The assignment is wrapped in a closure: `def (engine) <assign_code> end`
|
||
|
||
**Examples:**
|
||
```berry
|
||
sequence dynamic_show {
|
||
play pulse_anim for 2s
|
||
pulse_anim.opacity = 128 # Set static opacity
|
||
play pulse_anim for 2s
|
||
pulse_anim.opacity = brightness # Use value provider
|
||
play pulse_anim for 2s
|
||
pulse_anim.color = next_color # Change color provider
|
||
play pulse_anim for 2s
|
||
}
|
||
|
||
# Assignments work in repeat blocks too
|
||
sequence cylon_eye {
|
||
repeat 3 times {
|
||
play red_eye for 1s
|
||
red_eye.pos = triangle_val # Change oscillator pattern
|
||
play red_eye for 1s
|
||
red_eye.pos = cosine_val # Change back
|
||
eye_color.next = 1 # Advance color
|
||
}
|
||
}
|
||
```
|
||
|
||
#### Restart Statements
|
||
|
||
Restart statements allow you to restart value providers and animations from their initial state during sequence execution:
|
||
|
||
```berry
|
||
restart value_provider_name # Restart value provider from beginning
|
||
restart animation_name # Restart animation from beginning
|
||
```
|
||
|
||
**Restart Statement:**
|
||
- Restarts value providers (oscillators, color cycles, etc.) from their initial state
|
||
- Restarts animations from their beginning state
|
||
- Calls the `start()` method on the value provider or animation, which resets the time origin only if the object was already started previously
|
||
- Useful for synchronizing oscillators, restarting color cycles, or restarting complex animations
|
||
|
||
**Timing Behavior:**
|
||
- The `start()` method only resets the time origin if `self.start_time` is not nil (i.e., the object was already started)
|
||
- For fresh objects, the first call to `update()`, `render()`, or `produce_value()` initializes the time reference
|
||
- This prevents premature time initialization and ensures proper timing behavior
|
||
|
||
**Examples:**
|
||
```berry
|
||
# Restart oscillators for synchronized movement
|
||
sequence sync_demo {
|
||
play wave_anim for 3s
|
||
restart position_osc # Restart oscillator time origin
|
||
play wave_anim for 3s
|
||
}
|
||
|
||
# Restart animations for clean transitions
|
||
sequence clean_transitions {
|
||
play comet_anim for 5s
|
||
restart comet_anim # Restart from beginning position
|
||
play comet_anim for 5s
|
||
}
|
||
```
|
||
|
||
## Templates
|
||
|
||
Templates provide a powerful way to create reusable, parameterized animation patterns. They allow you to define animation blueprints that can be instantiated with different parameters, promoting code reuse and maintainability.
|
||
|
||
**Template-Only Files**: DSL files containing only template definitions transpile to pure Berry functions without engine initialization or execution code. This allows templates to be used as reusable function libraries.
|
||
|
||
### Template Definition
|
||
|
||
Templates are defined using the `template` keyword followed by a parameter block and body:
|
||
|
||
```berry
|
||
template template_name {
|
||
param parameter1 type color
|
||
param parameter2
|
||
param parameter3 type number
|
||
|
||
# Template body with DSL statements
|
||
animation my_anim = some_animation(color=parameter1, period=parameter2)
|
||
my_anim.opacity = parameter3
|
||
run my_anim
|
||
}
|
||
```
|
||
|
||
### Template Parameters
|
||
|
||
Template parameters are declared using the `param` keyword with optional type annotations:
|
||
|
||
```berry
|
||
template pulse_effect {
|
||
param base_color type color # Parameter with type annotation
|
||
param duration # Parameter without type annotation
|
||
param brightness type number # Another typed parameter
|
||
|
||
# Use parameters in template body
|
||
animation pulse = pulsating_animation(
|
||
color=base_color
|
||
period=duration
|
||
)
|
||
pulse.opacity = brightness
|
||
run pulse
|
||
}
|
||
```
|
||
|
||
**Parameter Types:**
|
||
- `color` - Color values (hex, named colors, color providers)
|
||
- `palette` - Palette definitions
|
||
- `number` - Numeric values (integers, percentages, time values)
|
||
- `animation` - Animation instances
|
||
- Type annotations are optional but improve readability
|
||
|
||
### Template Body
|
||
|
||
The template body can contain any valid DSL statements:
|
||
|
||
**Supported Statements:**
|
||
- Color definitions
|
||
- Palette definitions
|
||
- Animation definitions
|
||
- Property assignments
|
||
- Run statements
|
||
- Variable assignments (set statements)
|
||
|
||
```berry
|
||
template rainbow_pulse {
|
||
param pal1 as palette
|
||
param pal2 as palette
|
||
param duration
|
||
param back_color as color
|
||
|
||
# Create dynamic color cycling
|
||
color cycle_color = color_cycle(
|
||
palette=pal1
|
||
cycle_period=duration
|
||
)
|
||
|
||
# Create animations
|
||
animation pulse = pulsating_animation(
|
||
color=cycle_color
|
||
period=duration
|
||
)
|
||
|
||
animation background = solid(color=back_color)
|
||
|
||
# Set properties
|
||
background.priority = 1
|
||
pulse.priority = 10
|
||
|
||
# Run both animations
|
||
run background
|
||
run pulse
|
||
}
|
||
```
|
||
|
||
### Template Usage
|
||
|
||
Templates are called like functions with positional arguments:
|
||
|
||
```berry
|
||
# Define the template
|
||
template blink_red {
|
||
param speed
|
||
|
||
animation blink = pulsating_animation(
|
||
color=red
|
||
period=speed
|
||
)
|
||
|
||
run blink
|
||
}
|
||
|
||
# Use the template
|
||
blink_red(1s) # Call with 1 second period
|
||
blink_red(500ms) # Call with 500ms period
|
||
```
|
||
|
||
**Complex Template Usage:**
|
||
```berry
|
||
# Create palettes for the template
|
||
palette fire_palette = [
|
||
(0, black)
|
||
(128, red)
|
||
(255, yellow)
|
||
]
|
||
|
||
palette ocean_palette = [
|
||
(0, navy)
|
||
(128, cyan)
|
||
(255, white)
|
||
]
|
||
|
||
# Use the complex template
|
||
rainbow_pulse(fire_palette, ocean_palette, 3s, black)
|
||
```
|
||
|
||
### Template Behavior
|
||
|
||
**Code Generation:**
|
||
Templates generate Berry functions that are registered as user functions:
|
||
|
||
```berry
|
||
# Template definition generates:
|
||
def pulse_effect_template(engine, base_color_, duration_, brightness_)
|
||
var pulse_ = animation.pulsating_animation(engine)
|
||
pulse_.color = base_color_
|
||
pulse_.period = duration_
|
||
pulse_.opacity = brightness_
|
||
engine.add(pulse_)
|
||
end
|
||
|
||
animation.register_user_function('pulse_effect', pulse_effect_template)
|
||
```
|
||
|
||
**Template-Only Transpilation:**
|
||
Files containing only templates generate pure Berry function definitions without `var engine = animation.init_strip()` or `engine.run()` calls, making them suitable as reusable function libraries.
|
||
|
||
**Parameter Handling:**
|
||
- Parameters get `_` suffix in generated code to avoid naming conflicts
|
||
- Templates receive `engine` as the first parameter automatically
|
||
- Template calls are converted to function calls with `engine` as first argument
|
||
|
||
**Execution Model:**
|
||
- Templates don't return values - they add animations directly to the engine
|
||
- Multiple `run` statements in templates add multiple animations
|
||
- Templates can be called multiple times to create multiple instances
|
||
- `engine.run()` is automatically called when templates are used at the top level
|
||
|
||
### Template Parameter Validation
|
||
|
||
The DSL transpiler provides comprehensive validation for template parameters to ensure code quality and catch errors early:
|
||
|
||
**Parameter Name Validation:**
|
||
- **Duplicate Detection**: Prevents using the same parameter name twice
|
||
- **Reserved Keywords**: Prevents conflicts with Berry keywords (`animation`, `color`, `def`, etc.)
|
||
- **Built-in Colors**: Prevents conflicts with predefined color names (`red`, `blue`, etc.)
|
||
|
||
```berry
|
||
template bad_example {
|
||
param color type color # ❌ Error: conflicts with built-in color
|
||
param animation type number # ❌ Error: conflicts with reserved keyword
|
||
param my_param type color
|
||
param my_param type number # ❌ Error: duplicate parameter name
|
||
}
|
||
```
|
||
|
||
**Type Annotation Validation:**
|
||
Valid parameter types are: `color`, `palette`, `animation`, `number`, `string`, `boolean`, `time`, `percentage`, `variable`, `value_provider`
|
||
|
||
```berry
|
||
template type_example {
|
||
param my_color type invalid_type # ❌ Error: invalid type annotation
|
||
param valid_color type color # ✅ Valid type annotation
|
||
}
|
||
```
|
||
|
||
**Parameter Usage Validation:**
|
||
The transpiler generates **warnings** (not errors) for unused parameters:
|
||
|
||
```berry
|
||
template unused_example {
|
||
param used_color type color
|
||
param unused_param type number # ⚠️ Warning: parameter never used
|
||
|
||
animation test = solid(color=used_color)
|
||
run test
|
||
}
|
||
```
|
||
|
||
**Validation Benefits:**
|
||
- **Early Error Detection**: Catches parameter issues at compile time
|
||
- **Clear Error Messages**: Provides helpful suggestions for fixing issues
|
||
- **Code Quality**: Encourages proper parameter naming and usage
|
||
- **Warnings vs Errors**: Unused parameters generate warnings that don't prevent compilation
|
||
|
||
## Execution Statements
|
||
|
||
Execute animations or sequences:
|
||
|
||
```berry
|
||
run animation_name # Run an animation
|
||
run sequence_name # Run a sequence
|
||
```
|
||
|
||
### Debug and Logging
|
||
|
||
Log debug messages during animation execution:
|
||
|
||
```berry
|
||
log("Debug message") # Log message at level 3
|
||
log("Animation started") # Useful for debugging sequences
|
||
log("Color changed to red") # Track animation state changes
|
||
```
|
||
|
||
**Log Function Behavior:**
|
||
- Accepts string literals only (no variables or expressions)
|
||
- Transpiles to Berry `log(f"message", 3)`
|
||
- Messages are logged at level 3 for debugging purposes
|
||
- Can be used anywhere in DSL code: standalone, in sequences, etc.
|
||
|
||
## Operators and Expressions
|
||
|
||
### Arithmetic Operators
|
||
|
||
```berry
|
||
+ # Addition
|
||
- # Subtraction (also unary minus)
|
||
* # Multiplication
|
||
/ # Division
|
||
% # Modulo
|
||
```
|
||
|
||
### Comparison Operators
|
||
|
||
```berry
|
||
== # Equal to
|
||
!= # Not equal to
|
||
< # Less than
|
||
<= # Less than or equal
|
||
> # Greater than
|
||
>= # Greater than or equal
|
||
```
|
||
|
||
### Logical Operators
|
||
|
||
```berry
|
||
&& # Logical AND
|
||
|| # Logical OR
|
||
! # Logical NOT
|
||
```
|
||
|
||
### Assignment Operators
|
||
|
||
```berry
|
||
= # Simple assignment
|
||
```
|
||
|
||
## Function Calls
|
||
|
||
Functions use named parameter syntax with flexible formatting:
|
||
|
||
```berry
|
||
# Single line (commas required)
|
||
function_name(param1=value1, param2=value2)
|
||
|
||
# Multi-line (commas optional when parameters are on separate lines)
|
||
function_name(
|
||
param1=value1
|
||
param2=value2
|
||
param3=value3
|
||
)
|
||
|
||
# Mixed syntax (both commas and newlines work)
|
||
function_name(
|
||
param1=value1, param2=value2
|
||
param3=value3
|
||
)
|
||
```
|
||
|
||
**Examples:**
|
||
```berry
|
||
# Traditional single-line syntax
|
||
solid(color=red)
|
||
pulsating_animation(color=blue, period=2s)
|
||
|
||
# New multi-line syntax (no commas needed)
|
||
pulsating_animation(
|
||
color=blue
|
||
period=2s
|
||
brightness=255
|
||
)
|
||
|
||
# Mixed syntax
|
||
comet_animation(
|
||
color=stream_pattern, tail_length=15
|
||
speed=1.5s
|
||
priority=10
|
||
)
|
||
```
|
||
|
||
**Nested Function Calls:**
|
||
```berry
|
||
pulsating_animation(
|
||
color=solid(color=red)
|
||
period=smooth(
|
||
min_value=1000
|
||
max_value=3000
|
||
period=10s
|
||
)
|
||
)
|
||
```
|
||
|
||
**Mathematical Functions in Computed Parameters:**
|
||
Mathematical functions can be used in computed parameter expressions and are automatically detected by the transpiler:
|
||
|
||
```berry
|
||
animation wave = pulsating_animation(
|
||
color=blue
|
||
period=2s
|
||
)
|
||
|
||
# Mathematical functions in property assignments
|
||
wave.opacity = abs(sine(strip_length()) - 128) # Sine wave opacity
|
||
wave.position = max(0, min(strip_length() - 1, 15)) # Clamped position
|
||
wave.brightness = round(sqrt(strip_length()) * 4) # Non-linear scaling
|
||
```
|
||
|
||
## Supported Classes
|
||
|
||
### Value Providers
|
||
|
||
Value providers create dynamic values that change over time:
|
||
|
||
| Function | Description |
|
||
|----------|-------------|
|
||
| `static_value` | Returns a constant value |
|
||
| `strip_length` | Returns the LED strip length in pixels |
|
||
| `oscillator_value` | Oscillates between min/max values with various waveforms |
|
||
|
||
**Oscillator Aliases:**
|
||
| Function | Description |
|
||
|----------|-------------|
|
||
| `triangle` | Triangle wave oscillation (alias for oscillator with triangle waveform) |
|
||
| `smooth` | Smooth cosine wave (alias for oscillator with smooth waveform) |
|
||
| `cosine_osc` | Cosine wave oscillation (alias for smooth - cosine waveform) |
|
||
| `sine_osc` | Pure sine wave oscillation (alias for oscillator with sine waveform) |
|
||
| `linear` | Linear progression (alias for oscillator with linear waveform) |
|
||
| `ramp` | Sawtooth wave (alias for oscillator with ramp waveform) |
|
||
| `sawtooth` | Sawtooth wave (alias for ramp) |
|
||
| `square` | Square wave oscillation |
|
||
| `ease_in` | Quadratic ease-in (starts slow, accelerates) |
|
||
| `ease_out` | Quadratic ease-out (starts fast, decelerates) |
|
||
| `elastic` | Elastic easing with spring-like overshoot |
|
||
| `bounce` | Bounce easing like a ball with decreasing amplitude |
|
||
|
||
```berry
|
||
# Direct oscillator usage
|
||
triangle(min_value=0, max_value=255, period=2s) # Triangle wave
|
||
smooth(min_value=50, max_value=200, period=3s) # Smooth cosine
|
||
cosine_osc(min_value=3, max_value=1, period=5s) # Cosine wave (alias for smooth)
|
||
sine_osc(min_value=0, max_value=255, period=2s) # Pure sine wave
|
||
linear(min_value=0, max_value=100, period=1s) # Linear progression
|
||
ramp(min_value=0, max_value=255, period=2s) # Sawtooth wave
|
||
square(min_value=0, max_value=255, period=1s) # Square wave
|
||
ease_in(min_value=0, max_value=255, period=2s) # Quadratic ease-in
|
||
ease_out(min_value=0, max_value=255, period=2s) # Quadratic ease-out
|
||
elastic(min_value=0, max_value=255, period=2s) # Elastic spring effect
|
||
bounce(min_value=0, max_value=255, period=2s) # Bouncing ball effect
|
||
|
||
# Value providers can be assigned to variables
|
||
set brightness_oscillator = smooth(min_value=50, max_value=255, period=3s)
|
||
set position_sweep = triangle(min_value=0, max_value=29, period=5s)
|
||
set elastic_movement = elastic(min_value=0, max_value=30, period=4s)
|
||
set sine_wave = sine_osc(min_value=0, max_value=255, period=2s)
|
||
set cosine_wave = cosine_osc(min_value=50, max_value=200, period=3s)
|
||
set strip_len = strip_length() # Get the current strip length
|
||
```
|
||
|
||
### Color Providers
|
||
|
||
Color providers create dynamic colors that change over time:
|
||
|
||
| Function | Description |
|
||
|----------|-------------|
|
||
| `static_color` | Solid color with optional dynamic opacity |
|
||
| `color_cycle` | Cycles through a palette of colors |
|
||
| `rich_palette` | Advanced palette-based color cycling with smooth transitions |
|
||
| `composite_color` | Combines multiple color providers |
|
||
| `breathe_color` | Breathing/pulsing color effect with brightness modulation |
|
||
| `pulsating_color` | Fast pulsing color effect (alias for breathe_color with curve_factor=1) |
|
||
|
||
### Animation Classes
|
||
|
||
Animation classes create visual effects on LED strips:
|
||
|
||
| Function | Description |
|
||
|----------|-------------|
|
||
| `solid` | Solid color fill |
|
||
| `pulsating_animation` | Pulsing brightness effect |
|
||
| `beacon_animation` | Positioned pulse effect |
|
||
| `crenel_position_animation` | Square wave pulse at specific position |
|
||
| `breathe_animation` | Breathing/fading effect |
|
||
| `comet_animation` | Moving comet with trailing tail |
|
||
| `fire_animation` | Realistic fire simulation |
|
||
| `twinkle_animation` | Twinkling stars effect |
|
||
| `gradient_animation` | Color gradient effects |
|
||
| `noise_animation` | Perlin noise-based patterns |
|
||
| `wave_animation` | Wave propagation effects |
|
||
| `rich_palette_animation` | Palette-based color cycling |
|
||
| `palette_wave_animation` | Wave patterns using palettes |
|
||
| `palette_gradient_animation` | Gradient patterns using palettes |
|
||
| `palette_meter_animation` | Meter/bar patterns using palettes |
|
||
|
||
## Error Handling
|
||
|
||
### Validation Rules
|
||
|
||
The DSL performs comprehensive validation at compile time:
|
||
|
||
1. **Reserved Names**: Cannot redefine keywords or predefined colors
|
||
2. **Class Existence**: Animation and color provider factory functions must exist
|
||
3. **Parameter Validation**: Function parameters must exist and be valid for the specific class
|
||
4. **Type Checking**: Values must match expected types
|
||
5. **Reference Resolution**: All referenced identifiers must be defined
|
||
|
||
### Compile-Time Validation
|
||
|
||
The DSL validates class and parameter existence during compilation, catching errors before execution:
|
||
|
||
- **Factory Functions**: Verifies that animation and color provider factories exist in the animation module
|
||
- **Parameter Names**: Checks that all named parameters are valid for the specific class
|
||
- **Parameter Constraints**: Validates parameter values against defined constraints (min/max, enums, types)
|
||
- **Nested Validation**: Validates parameters in nested function calls and value providers
|
||
- **Property Assignment Validation**: Validates parameter names in property assignments (e.g., `animation.invalid_param = value`) against the actual class parameters
|
||
- **Object Reference Validation**: Validates that referenced objects exist in `run` statements and sequence `play` statements
|
||
|
||
### Common Errors
|
||
|
||
```berry
|
||
# Invalid: Redefining predefined color
|
||
color red = 0x800000 # Error: Cannot redefine 'red'
|
||
|
||
# Invalid: Unknown parameter in constructor
|
||
animation bad = pulsating_animation(invalid_param=123) # Error: Unknown parameter
|
||
|
||
# Invalid: Unknown parameter in property assignment
|
||
animation pulse = pulsating_animation(color=red, period=2s)
|
||
pulse.wrong_arg = 15 # Error: Parameter 'wrong_arg' not valid for PulseAnimation
|
||
|
||
# Invalid: Undefined reference in color definition
|
||
animation ref = solid(color=undefined_color) # Error: Undefined reference
|
||
|
||
# Invalid: Undefined reference in run statement
|
||
run undefined_animation # Error: Undefined reference 'undefined_animation' in run
|
||
|
||
# Invalid: Undefined reference in sequence
|
||
sequence demo {
|
||
play undefined_animation for 5s # Error: Undefined reference 'undefined_animation' in sequence play
|
||
}
|
||
|
||
# Valid alternatives
|
||
color my_red = 0x800000 # OK: Different name
|
||
animation good = pulsating_animation(color=red, period=2s) # OK: Valid parameters
|
||
good.priority = 10 # OK: Valid parameter assignment
|
||
```
|
||
|
||
## Formal Grammar (EBNF)
|
||
|
||
```ebnf
|
||
(* Animation DSL Grammar *)
|
||
|
||
program = { statement } ;
|
||
|
||
statement = import_stmt
|
||
| config_stmt
|
||
| definition
|
||
| property_assignment
|
||
| sequence
|
||
| template_def
|
||
| execution_stmt ;
|
||
|
||
(* Import and Configuration *)
|
||
import_stmt = "import" identifier ;
|
||
config_stmt = variable_assignment ;
|
||
(* strip_config = "strip" "length" number ; -- TEMPORARILY DISABLED *)
|
||
variable_assignment = "set" identifier "=" expression ;
|
||
|
||
(* Definitions *)
|
||
definition = color_def | palette_def | animation_def | template_def ;
|
||
color_def = "color" identifier "=" color_expression ;
|
||
palette_def = "palette" identifier "=" palette_array ;
|
||
animation_def = "animation" identifier "=" animation_expression ;
|
||
template_def = "template" identifier "{" template_body "}" ;
|
||
|
||
(* Property Assignments *)
|
||
property_assignment = identifier "." identifier "=" expression ;
|
||
|
||
(* Sequences *)
|
||
sequence = "sequence" identifier [ "repeat" ( expression "times" | "forever" ) ] "{" sequence_body "}" ;
|
||
sequence_body = { sequence_statement } ;
|
||
sequence_statement = play_stmt | wait_stmt | repeat_stmt | sequence_assignment | restart_stmt ;
|
||
|
||
play_stmt = "play" identifier [ "for" time_expression ] ;
|
||
wait_stmt = "wait" time_expression ;
|
||
repeat_stmt = "repeat" ( expression "times" | "forever" ) "{" sequence_body "}" ;
|
||
sequence_assignment = identifier "." identifier "=" expression ;
|
||
restart_stmt = "restart" identifier ;
|
||
|
||
(* Templates *)
|
||
template_def = "template" identifier "{" template_body "}" ;
|
||
template_body = { template_statement } ;
|
||
template_statement = param_decl | color_def | palette_def | animation_def | property_assignment | execution_stmt ;
|
||
param_decl = "param" identifier [ "type" identifier ] ;
|
||
|
||
(* Execution *)
|
||
execution_stmt = "run" identifier | template_call ;
|
||
template_call = identifier "(" [ argument_list ] ")" ;
|
||
argument_list = expression { "," expression } ;
|
||
|
||
(* Expressions *)
|
||
expression = logical_or_expr ;
|
||
logical_or_expr = logical_and_expr { "||" logical_and_expr } ;
|
||
logical_and_expr = equality_expr { "&&" equality_expr } ;
|
||
equality_expr = relational_expr { ( "==" | "!=" ) relational_expr } ;
|
||
relational_expr = additive_expr { ( "<" | "<=" | ">" | ">=" ) additive_expr } ;
|
||
additive_expr = multiplicative_expr { ( "+" | "-" ) multiplicative_expr } ;
|
||
multiplicative_expr = unary_expr { ( "*" | "/" | "%" ) unary_expr } ;
|
||
unary_expr = ( "!" | "-" | "+" ) unary_expr | primary_expr ;
|
||
primary_expr = literal | identifier | function_call | "(" expression ")" ;
|
||
|
||
(* Color Expressions *)
|
||
color_expression = hex_color | named_color | identifier ;
|
||
hex_color = "0x" hex_digit{6} | "0x" hex_digit{8} ;
|
||
named_color = color_name ;
|
||
|
||
(* Animation Expressions *)
|
||
animation_expression = function_call | identifier ;
|
||
|
||
(* Palette Arrays *)
|
||
palette_array = "[" palette_entry { "," palette_entry } "]" ;
|
||
palette_entry = "(" number "," color_expression ")" ;
|
||
|
||
(* Function Calls *)
|
||
function_call = identifier "(" [ named_argument_list ] ")" ;
|
||
named_argument_list = named_argument { "," named_argument } ;
|
||
named_argument = identifier "=" expression ;
|
||
|
||
(* Time Expressions *)
|
||
time_expression = time_literal ;
|
||
time_literal = number time_unit ;
|
||
time_unit = "ms" | "s" | "m" | "h" ;
|
||
|
||
(* Literals *)
|
||
literal = number | string | color_expression | time_expression | percentage | boolean ;
|
||
number = integer | real ;
|
||
integer = [ "-" ] digit { digit } ;
|
||
real = [ "-" ] digit { digit } "." digit { digit } ;
|
||
string = '"' { string_char } '"' | "'" { string_char } "'" ;
|
||
percentage = number "%" ;
|
||
boolean = "true" | "false" ;
|
||
|
||
(* Identifiers *)
|
||
identifier = ( letter | "_" ) { letter | digit | "_" } ;
|
||
color_name = "red" | "green" | "blue" | "white" | "black" | "yellow"
|
||
| "orange" | "purple" | "pink" | "cyan" | "magenta" | "gray"
|
||
| "silver" | "gold" | "brown" | "lime" | "navy" | "olive"
|
||
| "maroon" | "teal" | "aqua" | "fuchsia" | "transparent" ;
|
||
|
||
(* Character Classes *)
|
||
letter = "a" .. "z" | "A" .. "Z" ;
|
||
digit = "0" .. "9" ;
|
||
hex_digit = digit | "A" .. "F" | "a" .. "f" ;
|
||
string_char = (* any character except quote *) ;
|
||
|
||
(* Comments and Whitespace *)
|
||
comment = "#" { (* any character except newline *) } newline ;
|
||
whitespace = " " | "\t" | "\r" | "\n" ;
|
||
newline = "\n" | "\r\n" ;
|
||
```
|
||
|
||
## Flexible Parameter Syntax
|
||
|
||
The DSL supports flexible parameter syntax that makes multi-line function calls more readable:
|
||
|
||
### Traditional Syntax (Commas Required)
|
||
```berry
|
||
animation stream = comet_animation(color=red, tail_length=15, speed=1.5s, priority=10)
|
||
```
|
||
|
||
### New Multi-Line Syntax (Commas Optional)
|
||
```berry
|
||
animation stream = comet_animation(
|
||
color=red
|
||
tail_length=15
|
||
speed=1.5s
|
||
priority=10
|
||
)
|
||
```
|
||
|
||
### Mixed Syntax (Both Supported)
|
||
```berry
|
||
animation stream = comet_animation(
|
||
color=red, tail_length=15
|
||
speed=1.5s
|
||
priority=10
|
||
)
|
||
```
|
||
|
||
### Rules
|
||
- **Single line**: Commas are required between parameters
|
||
- **Multi-line**: Commas are optional when parameters are on separate lines
|
||
- **Mixed**: You can use both commas and newlines as separators
|
||
- **Comments**: Inline comments work with both syntaxes
|
||
|
||
This applies to:
|
||
- Animation function calls
|
||
- Color provider function calls
|
||
- Value provider function calls
|
||
- Palette entries
|
||
|
||
## Language Features Summary
|
||
|
||
### ✅ Currently Implemented
|
||
- Comments with preservation
|
||
- Strip configuration (optional)
|
||
- Color definitions (hex and named)
|
||
- Palette definitions with VRGB conversion
|
||
- Animation definitions with named parameters
|
||
- Property assignments
|
||
- Basic sequences (play, wait, repeat)
|
||
- Variable assignments with type conversion
|
||
- Reserved name validation
|
||
- Parameter validation at compile time
|
||
- Execution statements
|
||
- User-defined functions (with engine-first parameter pattern) - see **[User Functions Guide](USER_FUNCTIONS.md)**
|
||
- **User functions in computed parameters**: User functions can be used in arithmetic expressions alongside mathematical functions
|
||
- **Flexible parameter syntax**: Commas optional when parameters are on separate lines
|
||
- **Computed values**: Arithmetic expressions with value providers automatically create closures
|
||
- **Mathematical functions**: `min`, `max`, `abs`, `round`, `sqrt`, `scale`, `sine`, `cosine` in computed parameters
|
||
|
||
### 🚧 Partially Implemented
|
||
- Expression evaluation (basic support)
|
||
- Nested function calls (working but limited)
|
||
- Error recovery (basic error reporting)
|
||
|
||
### ❌ Planned Features
|
||
- Advanced control flow (if/else, choose random)
|
||
- Event system and handlers
|
||
- Variable references with $ syntax
|
||
- Spatial operations and zones
|
||
- 2D matrix support
|
||
|
||
This reference provides the complete syntax specification for the Animation DSL language as currently implemented and planned for future development. |