release: Storybook v0.2.0 - Major syntax and features update
BREAKING CHANGES: - Relationship syntax now requires blocks for all participants - Removed self/other perspective blocks from relationships - Replaced 'guard' keyword with 'if' for behavior tree decorators Language Features: - Add tree-sitter grammar with improved if/condition disambiguation - Add comprehensive tutorial and reference documentation - Add SBIR v0.2.0 binary format specification - Add resource linking system for behaviors and schedules - Add year-long schedule patterns (day, season, recurrence) - Add behavior tree enhancements (named nodes, decorators) Documentation: - Complete tutorial series (9 chapters) with baker family examples - Complete reference documentation for all language features - SBIR v0.2.0 specification with binary format details - Added locations and institutions documentation Examples: - Convert all examples to baker family scenario - Add comprehensive working examples Tooling: - Zed extension with LSP integration - Tree-sitter grammar for syntax highlighting - Build scripts and development tools Version Updates: - Main package: 0.1.0 → 0.2.0 - Tree-sitter grammar: 0.1.0 → 0.2.0 - Zed extension: 0.1.0 → 0.2.0 - Storybook editor: 0.1.0 → 0.2.0
This commit is contained in:
703
docs/reference/17-expressions.md
Normal file
703
docs/reference/17-expressions.md
Normal file
@@ -0,0 +1,703 @@
|
||||
# Expression Language
|
||||
|
||||
The Storybook expression language enables conditions, queries, and logic throughout the system. Expressions appear in life arc transitions, behavior tree guards, decorator conditions, and relationship queries. This chapter provides a complete reference for expression syntax and semantics.
|
||||
|
||||
## What are Expressions?
|
||||
|
||||
Expressions are logical statements that evaluate to `true` or `false`. They combine:
|
||||
|
||||
- **Literals**: Numbers, strings, booleans
|
||||
- **Identifiers**: References to fields and entities
|
||||
- **Comparisons**: `==`, `!=`, `<`, `<=`, `>`, `>=`
|
||||
- **Logical operators**: `and`, `or`, `not`
|
||||
- **Field access**: `self.field`, `other.field`
|
||||
- **Quantifiers**: `forall`, `exists` (for collections)
|
||||
|
||||
## Syntax
|
||||
|
||||
```bnf
|
||||
<expression> ::= <literal>
|
||||
| <identifier>
|
||||
| <field-access>
|
||||
| <comparison>
|
||||
| <logical>
|
||||
| <unary>
|
||||
| <quantifier>
|
||||
| "(" <expression> ")"
|
||||
|
||||
<literal> ::= <int> | <float> | <string> | <bool>
|
||||
|
||||
<identifier> ::= <simple-name>
|
||||
| <qualified-path>
|
||||
|
||||
<field-access> ::= <expression> "." <identifier>
|
||||
| "self" "." <identifier>
|
||||
| "other" "." <identifier>
|
||||
|
||||
<comparison> ::= <expression> <comp-op> <expression>
|
||||
|
||||
<comp-op> ::= "==" | "!=" | "<" | "<=" | ">" | ">="
|
||||
|
||||
<logical> ::= <expression> "and" <expression>
|
||||
| <expression> "or" <expression>
|
||||
|
||||
<unary> ::= "not" <expression>
|
||||
| "-" <expression>
|
||||
|
||||
<quantifier> ::= ("forall" | "exists") <identifier> "in" <expression> ":" <expression>
|
||||
```
|
||||
|
||||
## Literals
|
||||
|
||||
### Integer Literals
|
||||
|
||||
```storybook
|
||||
42
|
||||
-7
|
||||
0
|
||||
1000
|
||||
```
|
||||
|
||||
### Float Literals
|
||||
|
||||
```storybook
|
||||
3.14
|
||||
-0.5
|
||||
0.0
|
||||
100.25
|
||||
```
|
||||
|
||||
### String Literals
|
||||
|
||||
```storybook
|
||||
"Martha"
|
||||
"Sourdough takes patience."
|
||||
"active"
|
||||
```
|
||||
|
||||
Strings are enclosed in double quotes. Escape sequences: `\n`, `\t`, `\\`, `\"`.
|
||||
|
||||
### Boolean Literals
|
||||
|
||||
```storybook
|
||||
true
|
||||
false
|
||||
```
|
||||
|
||||
## Identifiers
|
||||
|
||||
Identifiers reference fields or entities.
|
||||
|
||||
### Simple Identifiers
|
||||
|
||||
```storybook
|
||||
health
|
||||
enemy_count
|
||||
is_ready
|
||||
```
|
||||
|
||||
### Qualified Paths
|
||||
|
||||
```storybook
|
||||
Martha.skill_level
|
||||
Character.emotional_state
|
||||
```
|
||||
|
||||
## Comparison Operators
|
||||
|
||||
### Equality: `==`
|
||||
|
||||
Tests if two values are equal.
|
||||
|
||||
```storybook
|
||||
name == "Martha"
|
||||
count == 5
|
||||
status == active
|
||||
```
|
||||
|
||||
**Type compatibility:**
|
||||
- Both operands must be the same type
|
||||
- Works with: int, float, string, bool, enum values
|
||||
|
||||
### Inequality: `!=`
|
||||
|
||||
Tests if two values are not equal.
|
||||
|
||||
```storybook
|
||||
name != "Gregory"
|
||||
health != 0
|
||||
ready != true
|
||||
```
|
||||
|
||||
### Less Than: `<`
|
||||
|
||||
Tests if left operand is less than right.
|
||||
|
||||
```storybook
|
||||
health < 20
|
||||
age < 18
|
||||
distance < 10.0
|
||||
```
|
||||
|
||||
**Valid types:** int, float
|
||||
|
||||
### Less Than or Equal: `<=`
|
||||
|
||||
```storybook
|
||||
health <= 50
|
||||
count <= max_count
|
||||
```
|
||||
|
||||
### Greater Than: `>`
|
||||
|
||||
```storybook
|
||||
strength > 10
|
||||
bond > 0.5
|
||||
```
|
||||
|
||||
### Greater Than or Equal: `>=`
|
||||
|
||||
```storybook
|
||||
age >= 21
|
||||
score >= 100
|
||||
```
|
||||
|
||||
## Logical Operators
|
||||
|
||||
### AND: `and`
|
||||
|
||||
Both operands must be true.
|
||||
|
||||
```storybook
|
||||
health < 50 and has_potion
|
||||
is_ready and not is_busy
|
||||
age >= 18 and age < 65
|
||||
```
|
||||
|
||||
**Evaluation:** Short-circuit (if left is false, right is not evaluated)
|
||||
|
||||
### OR: `or`
|
||||
|
||||
At least one operand must be true.
|
||||
|
||||
```storybook
|
||||
is_day or is_lit
|
||||
health < 20 or surrounded
|
||||
enemy_count == 0 or all_enemies_dead
|
||||
```
|
||||
|
||||
**Evaluation:** Short-circuit (if left is true, right is not evaluated)
|
||||
|
||||
### Operator Precedence
|
||||
|
||||
From highest to lowest:
|
||||
|
||||
1. **Parentheses**: `(...)`
|
||||
2. **Unary**: `not`, `-`
|
||||
3. **Comparisons**: `==`, `!=`, `<`, `<=`, `>`, `>=`
|
||||
4. **AND**: `and`
|
||||
5. **OR**: `or`
|
||||
|
||||
**Examples:**
|
||||
```storybook
|
||||
not is_ready and is_awake
|
||||
// Equivalent to: (not is_ready) and is_awake
|
||||
|
||||
health < 50 or is_poisoned and has_antidote
|
||||
// Equivalent to: (health < 50) or (is_poisoned and has_antidote)
|
||||
|
||||
// Use parentheses for clarity:
|
||||
(health < 50 or is_poisoned) and has_antidote
|
||||
```
|
||||
|
||||
## Unary Operators
|
||||
|
||||
### NOT: `not`
|
||||
|
||||
Inverts a boolean value.
|
||||
|
||||
```storybook
|
||||
not is_ready
|
||||
not (health < 20)
|
||||
not enemy_nearby and safe
|
||||
```
|
||||
|
||||
### Negation: `-`
|
||||
|
||||
Negates a numeric value.
|
||||
|
||||
```storybook
|
||||
-health
|
||||
-10
|
||||
-(max_value - current_value)
|
||||
```
|
||||
|
||||
## Field Access
|
||||
|
||||
### Direct Field Access
|
||||
|
||||
```storybook
|
||||
health
|
||||
bond
|
||||
emotional_state
|
||||
```
|
||||
|
||||
References a field on the current entity.
|
||||
|
||||
### Dot Access
|
||||
|
||||
```storybook
|
||||
Martha.skill_level
|
||||
Character.emotional_state
|
||||
enemy.health
|
||||
```
|
||||
|
||||
Access fields on other entities.
|
||||
|
||||
### Self Access
|
||||
|
||||
In relationships and certain contexts, `self` refers to the current participant:
|
||||
|
||||
```storybook
|
||||
self.bond
|
||||
self.responsibility
|
||||
self.trust
|
||||
```
|
||||
|
||||
**Use case:** Relationship transitions, symmetric queries
|
||||
|
||||
### Other Access
|
||||
|
||||
In relationships, `other` refers to other participants:
|
||||
|
||||
```storybook
|
||||
other.bond
|
||||
other.aware_of_mentor
|
||||
other.respect
|
||||
```
|
||||
|
||||
**Use case:** Relationship queries with perspective
|
||||
|
||||
### Example in Life Arcs
|
||||
|
||||
```storybook
|
||||
life_arc RelationshipState {
|
||||
state new {
|
||||
on self.bond > 0.7 and other.bond > 0.7 -> stable
|
||||
}
|
||||
|
||||
state stable {
|
||||
on self.bond < 0.3 -> troubled
|
||||
on other.bond < 0.3 -> troubled
|
||||
}
|
||||
|
||||
state troubled {
|
||||
on self.bond < 0.1 or other.bond < 0.1 -> broken
|
||||
}
|
||||
|
||||
state broken {}
|
||||
}
|
||||
```
|
||||
|
||||
## Quantifiers
|
||||
|
||||
Quantifiers test conditions over collections.
|
||||
|
||||
### ForAll: `forall`
|
||||
|
||||
Tests if a condition holds for all elements in a collection.
|
||||
|
||||
```storybook
|
||||
forall e in enemies: e.defeated
|
||||
forall item in inventory: item.weight < 10
|
||||
```
|
||||
|
||||
**Syntax:**
|
||||
```bnf
|
||||
forall <variable> in <collection>: <predicate>
|
||||
```
|
||||
|
||||
**Semantics:**
|
||||
- Returns `true` if predicate is true for every element
|
||||
- Returns `true` for empty collections (vacuously true)
|
||||
|
||||
**Examples:**
|
||||
```storybook
|
||||
// All enemies defeated?
|
||||
forall enemy in enemies: enemy.health <= 0
|
||||
|
||||
// All party members ready?
|
||||
forall member in party: member.is_ready
|
||||
|
||||
// All doors locked?
|
||||
forall door in doors: door.is_locked
|
||||
```
|
||||
|
||||
### Exists: `exists`
|
||||
|
||||
Tests if a condition holds for at least one element.
|
||||
|
||||
```storybook
|
||||
exists e in enemies: e.is_hostile
|
||||
exists item in inventory: item.is_healing_potion
|
||||
```
|
||||
|
||||
**Syntax:**
|
||||
```bnf
|
||||
exists <variable> in <collection>: <predicate>
|
||||
```
|
||||
|
||||
**Semantics:**
|
||||
- Returns `true` if predicate is true for any element
|
||||
- Returns `false` for empty collections
|
||||
|
||||
**Examples:**
|
||||
```storybook
|
||||
// Any enemy nearby?
|
||||
exists enemy in enemies: enemy.distance < 10
|
||||
|
||||
// Any door unlocked?
|
||||
exists door in doors: not door.is_locked
|
||||
|
||||
// Any ally wounded?
|
||||
exists ally in allies: ally.health < ally.max_health * 0.5
|
||||
```
|
||||
|
||||
### Nested Quantifiers
|
||||
|
||||
Quantifiers can nest:
|
||||
|
||||
```storybook
|
||||
forall team in teams: exists player in team: player.is_leader
|
||||
// Every team has at least one leader
|
||||
|
||||
exists room in dungeon: forall enemy in room.enemies: enemy.defeated
|
||||
// At least one room has all enemies defeated
|
||||
```
|
||||
|
||||
## Usage in Context
|
||||
|
||||
### Life Arc Transitions
|
||||
|
||||
```storybook
|
||||
life_arc CombatState {
|
||||
state idle {
|
||||
on enemy_count > 0 -> combat
|
||||
}
|
||||
|
||||
state combat {
|
||||
on health < 20 -> fleeing
|
||||
on enemy_count == 0 -> victorious
|
||||
}
|
||||
|
||||
state fleeing {
|
||||
on distance_from_enemies > 100 -> safe
|
||||
}
|
||||
|
||||
state victorious {
|
||||
on celebration_complete -> idle
|
||||
}
|
||||
|
||||
state safe {
|
||||
on health >= 50 -> idle
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Behavior Tree Conditions
|
||||
|
||||
```storybook
|
||||
behavior GuardedAction {
|
||||
if(health > 50 and has_weapon) {
|
||||
AggressiveAttack
|
||||
}
|
||||
}
|
||||
|
||||
behavior ConditionalChoice {
|
||||
choose tactics {
|
||||
then melee {
|
||||
if(distance < 5 and weapon_type == "sword")
|
||||
MeleeAttack
|
||||
}
|
||||
|
||||
then ranged {
|
||||
if(distance >= 5 and has_arrows)
|
||||
RangedAttack
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Behavior Tree Conditions
|
||||
|
||||
```storybook
|
||||
behavior SmartAI {
|
||||
choose strategy {
|
||||
then aggressive {
|
||||
if(health > 70 and enemy_count < 3)
|
||||
Attack
|
||||
}
|
||||
|
||||
then defensive {
|
||||
if(health < 30 or enemy_count >= 5)
|
||||
Defend
|
||||
}
|
||||
|
||||
then balanced {
|
||||
if(health >= 30 and health <= 70)
|
||||
TacticalManeuver
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Type System
|
||||
|
||||
### Type Compatibility
|
||||
|
||||
Comparisons require compatible types:
|
||||
|
||||
| Operator | Left Type | Right Type | Valid? |
|
||||
|----------|-----------|------------|--------|
|
||||
| `==`, `!=` | int | int | ✓ |
|
||||
| `==`, `!=` | float | float | ✓ |
|
||||
| `==`, `!=` | string | string | ✓ |
|
||||
| `==`, `!=` | bool | bool | ✓ |
|
||||
| `==`, `!=` | enum | same enum | ✓ |
|
||||
| `==`, `!=` | int | float | ✗ |
|
||||
| `<`, `<=`, `>`, `>=` | int | int | ✓ |
|
||||
| `<`, `<=`, `>`, `>=` | float | float | ✓ |
|
||||
| `<`, `<=`, `>`, `>=` | string | string | ✗ |
|
||||
|
||||
### Implicit Coercion
|
||||
|
||||
**None.** Storybook has no implicit type coercion. All comparisons must be between compatible types.
|
||||
|
||||
**Error:**
|
||||
```storybook
|
||||
count == "5" // Error: int vs string
|
||||
health < true // Error: int vs bool
|
||||
```
|
||||
|
||||
**Correct:**
|
||||
```storybook
|
||||
count == 5
|
||||
health < 50
|
||||
```
|
||||
|
||||
## Special Keyword: `is`
|
||||
|
||||
The `is` keyword provides syntactic sugar for equality with enum values:
|
||||
|
||||
```storybook
|
||||
// Instead of:
|
||||
status == active
|
||||
|
||||
// You can write:
|
||||
status is active
|
||||
```
|
||||
|
||||
**More examples:**
|
||||
```storybook
|
||||
name is "Martha"
|
||||
skill_level is master
|
||||
emotional_state is focused
|
||||
```
|
||||
|
||||
This is purely syntactic—`is` and `==` are equivalent.
|
||||
|
||||
## Complete Examples
|
||||
|
||||
### Simple Conditions
|
||||
|
||||
```storybook
|
||||
health < 20
|
||||
enemy_nearby
|
||||
not is_ready
|
||||
count > 5
|
||||
```
|
||||
|
||||
### Complex Conditions
|
||||
|
||||
```storybook
|
||||
(health < 20 and not has_potion) or surrounded
|
||||
forall e in enemies: e.defeated
|
||||
exists item in inventory: item.is_healing_potion and item.quantity > 0
|
||||
```
|
||||
|
||||
### Life Arc with Complex Conditions
|
||||
|
||||
```storybook
|
||||
life_arc CharacterMood {
|
||||
state content {
|
||||
on health < 30 or hunger > 80 -> distressed
|
||||
on social_interaction > 0.8 -> happy
|
||||
}
|
||||
|
||||
state distressed {
|
||||
on health >= 50 and hunger < 30 -> content
|
||||
on (health < 10 or hunger > 95) and help_available -> desperate
|
||||
}
|
||||
|
||||
state happy {
|
||||
on social_interaction < 0.3 -> content
|
||||
on received_bad_news -> distressed
|
||||
}
|
||||
|
||||
state desperate {
|
||||
on help_received -> distressed
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Behavior with Quantifiers
|
||||
|
||||
```storybook
|
||||
behavior SquadLeader {
|
||||
choose leadership {
|
||||
then regroup {
|
||||
if(squad_has_wounded)
|
||||
OrderRetreat
|
||||
}
|
||||
|
||||
then advance {
|
||||
if(squad_all_ready)
|
||||
OrderAdvance
|
||||
}
|
||||
|
||||
then hold_position {
|
||||
if(not squad_all_ready)
|
||||
OrderHold
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Relationship Query
|
||||
|
||||
```storybook
|
||||
life_arc FriendshipQuality {
|
||||
state new_friends {
|
||||
on self.bond > 0.7 and other.bond > 0.7 -> strong_bond
|
||||
on self.trust < 0.3 or other.trust < 0.3 -> shaky
|
||||
}
|
||||
|
||||
state strong_bond {
|
||||
on self.bond < 0.5 -> weakening
|
||||
}
|
||||
|
||||
state weakening {
|
||||
on self.bond < 0.2 or other.bond < 0.2 -> ended
|
||||
on self.bond > 0.7 and other.bond > 0.7 -> strong_bond
|
||||
}
|
||||
|
||||
state shaky {
|
||||
on self.trust > 0.6 and other.trust > 0.6 -> new_friends
|
||||
on self.trust < 0.1 or other.trust < 0.1 -> ended
|
||||
}
|
||||
|
||||
state ended {}
|
||||
}
|
||||
```
|
||||
|
||||
## Validation Rules
|
||||
|
||||
1. **Type consistency**: Both sides of comparison must be compatible types
|
||||
2. **Boolean context**: Logical operators (`and`, `or`, `not`) require boolean operands
|
||||
3. **Field existence**: Referenced fields must exist on the entity
|
||||
4. **Collection validity**: Quantifiers require collection-typed expressions
|
||||
5. **Variable scope**: Quantifier variables only valid within their predicate
|
||||
6. **No division by zero**: Arithmetic operations must not divide by zero
|
||||
7. **Enum validity**: Enum comparisons must reference defined enum values
|
||||
|
||||
## Best Practices
|
||||
|
||||
### 1. Use Parentheses for Clarity
|
||||
|
||||
**Avoid:**
|
||||
```storybook
|
||||
health < 50 or is_poisoned and has_antidote
|
||||
```
|
||||
|
||||
**Prefer:**
|
||||
```storybook
|
||||
(health < 50 or is_poisoned) and has_antidote
|
||||
```
|
||||
|
||||
### 2. Break Complex Conditions
|
||||
|
||||
**Avoid:**
|
||||
```storybook
|
||||
on (health < 20 and not has_potion) or (surrounded and not has_escape) or (enemy_count > 10 and weapon_broken) -> desperate
|
||||
```
|
||||
|
||||
**Prefer:**
|
||||
```storybook
|
||||
state combat {
|
||||
on health < 20 and not has_potion -> desperate
|
||||
on surrounded and not has_escape -> desperate
|
||||
on enemy_count > 10 and weapon_broken -> desperate
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Name Complex Conditions
|
||||
|
||||
For repeated complex conditions, consider using intermediate fields:
|
||||
|
||||
**Instead of:**
|
||||
```storybook
|
||||
on health < (max_health * 0.2) and enemy_count > 5 -> flee
|
||||
```
|
||||
|
||||
**Consider:**
|
||||
```storybook
|
||||
// In character definition:
|
||||
critically_wounded: health < (max_health * 0.2)
|
||||
outnumbered: enemy_count > 5
|
||||
|
||||
// In life arc:
|
||||
on critically_wounded and outnumbered -> flee
|
||||
```
|
||||
|
||||
### 4. Use `is` for Enums
|
||||
|
||||
**Prefer:**
|
||||
```storybook
|
||||
status is active
|
||||
emotional_state is focused
|
||||
```
|
||||
|
||||
**Over:**
|
||||
```storybook
|
||||
status == active
|
||||
emotional_state == focused
|
||||
```
|
||||
|
||||
### 5. Quantifiers for Collections
|
||||
|
||||
**Avoid:**
|
||||
```storybook
|
||||
// Manual checks for each element
|
||||
if enemy1.defeated and enemy2.defeated and enemy3.defeated
|
||||
```
|
||||
|
||||
**Prefer:**
|
||||
```storybook
|
||||
if forall enemy in enemies: enemy.defeated
|
||||
```
|
||||
|
||||
## Cross-References
|
||||
|
||||
- [Life Arcs](./13-life-arcs.md) - Transition conditions
|
||||
- [Behavior Trees](./11-behavior-trees.md) - Guard and condition nodes
|
||||
- [Decorators](./12-decorators.md) - Guard decorator
|
||||
- [Relationships](./15-relationships.md) - Self/other field access
|
||||
- [Value Types](./18-value-types.md) - Literal value types
|
||||
|
||||
## Related Concepts
|
||||
|
||||
- **Type safety**: Strong typing prevents type errors at compile time
|
||||
- **Short-circuit evaluation**: AND/OR operators optimize evaluation
|
||||
- **Quantifiers**: Enable expressive collection queries
|
||||
- **Field access**: Context-sensitive (`self`, `other`) for relationships
|
||||
- **Boolean algebra**: Standard logical operators with expected semantics
|
||||
Reference in New Issue
Block a user