# 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 ::= | | | | | | | "(" ")" ::= | | | ::= | ::= "." | "self" "." | "other" "." ::= ::= "==" | "!=" | "<" | "<=" | ">" | ">=" ::= "and" | "or" ::= "not" | "-" ::= ("forall" | "exists") "in" ":" ``` ## 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: number, decimal, text, boolean, 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:** number, decimal ### 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 in : ``` **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 in : ``` **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? | |----------|-----------|------------|--------| | `==`, `!=` | number | number | ✓ | | `==`, `!=` | decimal | decimal | ✓ | | `==`, `!=` | text | text | ✓ | | `==`, `!=` | boolean | boolean | ✓ | | `==`, `!=` | enum | same enum | ✓ | | `==`, `!=` | number | decimal | ✗ | | `<`, `<=`, `>`, `>=` | number | number | ✓ | | `<`, `<=`, `>`, `>=` | decimal | decimal | ✓ | | `<`, `<=`, `>`, `>=` | text | text | ✗ | ### Implicit Coercion **None.** Storybook has no implicit type coercion. All comparisons must be between compatible types. **Error:** ```storybook count == "5" // Error: number vs text health < true // Error: number vs boolean ``` **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