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:
921
design/resource-linking-system.md
Normal file
921
design/resource-linking-system.md
Normal file
@@ -0,0 +1,921 @@
|
||||
# Resource Linking System Design
|
||||
**Author:** Resource Linking Architect
|
||||
**Date:** 2026-02-12
|
||||
**Status:** Ready for Checkpoint 1 Review
|
||||
**Version:** 0.2
|
||||
**Keyword Decision:** `uses` (approved by Sienna)
|
||||
|
||||
---
|
||||
|
||||
## Executive Summary
|
||||
|
||||
This document proposes a unified `uses` keyword system for associating behaviors and schedules with characters and institutions in the Storybook DSL. The design enables:
|
||||
|
||||
- Characters using one or more behaviors
|
||||
- Characters using one or more schedules
|
||||
- Institutions using behaviors (for institutional operations)
|
||||
- Institutions using schedules (operating hours, seasonal variations)
|
||||
- Conditional/contextual selection of linked resources
|
||||
- Priority-based behavior selection at runtime
|
||||
|
||||
---
|
||||
|
||||
## 1. Design Goals
|
||||
|
||||
### Primary Goals
|
||||
1. **Unified Syntax**: Single `uses` keyword for both behaviors and schedules
|
||||
2. **Simple Default Case**: Most common use case should be simple one-liner
|
||||
3. **Powerful When Needed**: Support complex multi-link scenarios with conditions
|
||||
4. **Clear Semantics**: Unambiguous about which behavior/schedule applies when
|
||||
5. **Backward Compatible**: Existing .sb files continue to parse and work
|
||||
|
||||
### Non-Goals
|
||||
1. **Not for Relationships**: Relationship linking remains separate (already exists)
|
||||
2. **Not Inline Definitions**: Can only link to named behaviors/schedules, not define inline
|
||||
3. **Not Dynamic Composition**: Links are static at author-time, selection is runtime
|
||||
|
||||
---
|
||||
|
||||
## 2. Syntax Design
|
||||
|
||||
### 2.1 Simple Single Link
|
||||
|
||||
The most common case: a character has one primary behavior and one schedule.
|
||||
|
||||
```storybook
|
||||
character Martha: Human {
|
||||
age: 34
|
||||
|
||||
uses behavior: WorkAtBakery
|
||||
uses schedule: BakerSchedule
|
||||
}
|
||||
```
|
||||
|
||||
**AST Representation:**
|
||||
```rust
|
||||
// Add to Character struct in ast.rs
|
||||
pub struct Character {
|
||||
pub name: String,
|
||||
pub species: Option<String>,
|
||||
pub fields: Vec<Field>,
|
||||
pub template: Option<Vec<String>>,
|
||||
pub behavior_links: Vec<BehaviorLink>, // NEW
|
||||
pub schedule_links: Vec<ScheduleLink>, // NEW
|
||||
pub span: Span,
|
||||
}
|
||||
```
|
||||
|
||||
### 2.2 Multiple Links with Priorities
|
||||
|
||||
Characters may have multiple behaviors that activate based on context:
|
||||
|
||||
```storybook
|
||||
character Alice: Human {
|
||||
age: 7
|
||||
|
||||
uses behaviors: [
|
||||
{ tree: HandleUrgentNeeds, priority: critical }
|
||||
{ tree: CuriousExplorer, priority: normal }
|
||||
{ tree: Idle, priority: low }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Semantics:**
|
||||
- Higher priority behaviors preempt lower priority ones
|
||||
- Within same priority, declaration order determines evaluation
|
||||
- `critical` > `high` > `normal` > `low`
|
||||
|
||||
### 2.3 Conditional Links
|
||||
|
||||
Links can have `when` conditions for context-based selection:
|
||||
|
||||
```storybook
|
||||
character Alice: Human {
|
||||
uses behaviors: [
|
||||
{ tree: HandleUrgentNeeds, priority: critical }
|
||||
{ tree: GiantBehavior, when: current_size == huge }
|
||||
{ tree: TinyBehavior, when: current_size == tiny }
|
||||
{ tree: NormalExploring, default: true }
|
||||
]
|
||||
|
||||
uses schedules: [
|
||||
{ schedule: SleepSchedule, when: emotional_state == exhausted }
|
||||
{ schedule: AdventureSchedule, default: true }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Condition Evaluation:**
|
||||
- Conditions use the existing expression language (see design.md §5)
|
||||
- `default: true` means "use this if no condition matches"
|
||||
- Only one `default` allowed per link type
|
||||
- Runtime evaluates conditions top-to-bottom
|
||||
|
||||
### 2.4 Institution Links
|
||||
|
||||
Institutions can link to behaviors and schedules:
|
||||
|
||||
```storybook
|
||||
institution Bakery {
|
||||
type: commercial
|
||||
|
||||
uses behavior: BakeryOperations
|
||||
uses schedule: BakeryHours
|
||||
}
|
||||
```
|
||||
|
||||
**Multiple Schedules for Seasons:**
|
||||
```storybook
|
||||
institution Bakery {
|
||||
uses schedules: [
|
||||
{ schedule: SummerHours, when: season == summer }
|
||||
{ schedule: WinterHours, when: season == winter }
|
||||
{ schedule: StandardHours, default: true }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 2.5 Template Inheritance
|
||||
|
||||
Templates can define default links that characters inherit:
|
||||
|
||||
```storybook
|
||||
template WonderlandCreature {
|
||||
uses behavior: WonderlandBehavior
|
||||
uses schedule: WonderlandSchedule
|
||||
}
|
||||
|
||||
character CheshireCat: Cat from WonderlandCreature {
|
||||
// Inherits WonderlandBehavior and WonderlandSchedule
|
||||
// Can override:
|
||||
uses behavior: CheshireBehavior // Replaces WonderlandBehavior
|
||||
}
|
||||
```
|
||||
|
||||
**Override Semantics:**
|
||||
- If character defines `uses behavior:`, it replaces template's behavior link entirely
|
||||
- If character defines `uses behaviors: [...]`, it replaces template's behavior links
|
||||
- No merging—it's full replacement (consistent with current template override system)
|
||||
|
||||
---
|
||||
|
||||
## 3. AST Design
|
||||
|
||||
### 3.1 New AST Types
|
||||
|
||||
```rust
|
||||
// In src/syntax/ast.rs
|
||||
|
||||
/// A link to a behavior tree
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct BehaviorLink {
|
||||
pub tree: Vec<String>, // Qualified path to behavior
|
||||
pub priority: Option<String>, // critical, high, normal, low
|
||||
pub condition: Option<Expr>, // when clause
|
||||
pub is_default: bool, // default: true
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
/// A link to a schedule
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct ScheduleLink {
|
||||
pub schedule: Vec<String>, // Qualified path to schedule
|
||||
pub condition: Option<Expr>, // when clause
|
||||
pub is_default: bool, // default: true
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
// Priority levels (could be enum or validated string)
|
||||
pub enum Priority {
|
||||
Critical,
|
||||
High,
|
||||
Normal,
|
||||
Low,
|
||||
}
|
||||
```
|
||||
|
||||
### 3.2 Modified AST Structs
|
||||
|
||||
```rust
|
||||
// Character gains link fields
|
||||
pub struct Character {
|
||||
pub name: String,
|
||||
pub species: Option<String>,
|
||||
pub fields: Vec<Field>,
|
||||
pub template: Option<Vec<String>>,
|
||||
pub behavior_links: Vec<BehaviorLink>, // NEW
|
||||
pub schedule_links: Vec<ScheduleLink>, // NEW
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
// Institution gains link fields
|
||||
pub struct Institution {
|
||||
pub name: String,
|
||||
pub fields: Vec<Field>,
|
||||
pub behavior_links: Vec<BehaviorLink>, // NEW
|
||||
pub schedule_links: Vec<ScheduleLink>, // NEW
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
// Template can also have links
|
||||
pub struct Template {
|
||||
pub name: String,
|
||||
pub fields: Vec<Field>,
|
||||
pub includes: Vec<Vec<String>>,
|
||||
pub behavior_links: Vec<BehaviorLink>, // NEW
|
||||
pub schedule_links: Vec<ScheduleLink>, // NEW
|
||||
pub span: Span,
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Parser Design (LALRPOP)
|
||||
|
||||
### 4.1 Grammar Productions
|
||||
|
||||
```lalrpop
|
||||
// In parser.lalrpop
|
||||
|
||||
// Character definition with optional links
|
||||
pub Character: Character = {
|
||||
"character" <name:Ident> <species:SpeciesClause?> <template:TemplateClause?> "{"
|
||||
<items:CharacterItem*>
|
||||
"}" => {
|
||||
let mut fields = vec![];
|
||||
let mut behavior_links = vec![];
|
||||
let mut schedule_links = vec![];
|
||||
|
||||
for item in items {
|
||||
match item {
|
||||
CharacterItem::Field(f) => fields.push(f),
|
||||
CharacterItem::BehaviorLink(bl) => behavior_links.extend(bl),
|
||||
CharacterItem::ScheduleLink(sl) => schedule_links.extend(sl),
|
||||
}
|
||||
}
|
||||
|
||||
Character { name, species, fields, template, behavior_links, schedule_links, span }
|
||||
}
|
||||
};
|
||||
|
||||
CharacterItem: CharacterItem = {
|
||||
<Field> => CharacterItem::Field(<>),
|
||||
<BehaviorLinkStmt> => CharacterItem::BehaviorLink(<>),
|
||||
<ScheduleLinkStmt> => CharacterItem::ScheduleLink(<>),
|
||||
};
|
||||
|
||||
// Behavior link statement
|
||||
BehaviorLinkStmt: Vec<BehaviorLink> = {
|
||||
// Single link: uses behavior: BehaviorName
|
||||
"uses" "behavior" ":" <path:QualifiedPath> => {
|
||||
vec![BehaviorLink {
|
||||
tree: path,
|
||||
priority: None,
|
||||
condition: None,
|
||||
is_default: false,
|
||||
span,
|
||||
}]
|
||||
},
|
||||
|
||||
// Multiple links: uses behaviors: [...]
|
||||
"uses" "behaviors" ":" "[" <links:Comma<BehaviorLinkSpec>> "]" => links,
|
||||
};
|
||||
|
||||
BehaviorLinkSpec: BehaviorLink = {
|
||||
// { tree: Name, priority: normal, when: condition, default: true }
|
||||
"{" <fields:Comma<BehaviorLinkField>> "}" => {
|
||||
let mut tree = None;
|
||||
let mut priority = None;
|
||||
let mut condition = None;
|
||||
let mut is_default = false;
|
||||
|
||||
for field in fields {
|
||||
match field {
|
||||
BehaviorLinkField::Tree(path) => tree = Some(path),
|
||||
BehaviorLinkField::Priority(p) => priority = Some(p),
|
||||
BehaviorLinkField::Condition(c) => condition = Some(c),
|
||||
BehaviorLinkField::Default => is_default = true,
|
||||
}
|
||||
}
|
||||
|
||||
BehaviorLink {
|
||||
tree: tree.expect("tree field required"),
|
||||
priority,
|
||||
condition,
|
||||
is_default,
|
||||
span,
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
BehaviorLinkField: BehaviorLinkField = {
|
||||
"tree" ":" <QualifiedPath> => BehaviorLinkField::Tree(<>),
|
||||
"priority" ":" <Ident> => BehaviorLinkField::Priority(<>),
|
||||
"when" ":" <Expr> => BehaviorLinkField::Condition(<>),
|
||||
"default" ":" "true" => BehaviorLinkField::Default,
|
||||
};
|
||||
|
||||
// Schedule link statement (parallel structure)
|
||||
ScheduleLinkStmt: Vec<ScheduleLink> = {
|
||||
"uses" "schedule" ":" <path:QualifiedPath> => {
|
||||
vec![ScheduleLink {
|
||||
schedule: path,
|
||||
condition: None,
|
||||
is_default: false,
|
||||
span,
|
||||
}]
|
||||
},
|
||||
|
||||
"uses" "schedules" ":" "[" <links:Comma<ScheduleLinkSpec>> "]" => links,
|
||||
};
|
||||
|
||||
ScheduleLinkSpec: ScheduleLink = {
|
||||
"{" <fields:Comma<ScheduleLinkField>> "}" => {
|
||||
let mut schedule = None;
|
||||
let mut condition = None;
|
||||
let mut is_default = false;
|
||||
|
||||
for field in fields {
|
||||
match field {
|
||||
ScheduleLinkField::Schedule(path) => schedule = Some(path),
|
||||
ScheduleLinkField::Condition(c) => condition = Some(c),
|
||||
ScheduleLinkField::Default => is_default = true,
|
||||
}
|
||||
}
|
||||
|
||||
ScheduleLink {
|
||||
schedule: schedule.expect("schedule field required"),
|
||||
condition,
|
||||
is_default,
|
||||
span,
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
ScheduleLinkField: ScheduleLinkField = {
|
||||
"schedule" ":" <QualifiedPath> => ScheduleLinkField::Schedule(<>),
|
||||
"when" ":" <Expr> => ScheduleLinkField::Condition(<>),
|
||||
"default" ":" "true" => ScheduleLinkField::Default,
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Resolution & Validation
|
||||
|
||||
### 5.1 Name Resolution
|
||||
|
||||
During the resolution pass (Pass 3 in design.md §4.7), the resolver must:
|
||||
|
||||
1. **Resolve Behavior Paths**: Each `tree: BehaviorName` must reference a valid `behavior` declaration
|
||||
2. **Resolve Schedule Paths**: Each `schedule: ScheduleName` must reference a valid `schedule` declaration
|
||||
3. **Validate Priorities**: Priority values must be one of {critical, high, normal, low}
|
||||
4. **Validate Conditions**: Expressions in `when` clauses must be valid and type-check
|
||||
|
||||
**Error Examples:**
|
||||
```
|
||||
error: unresolved behavior reference
|
||||
┌─ characters/alice.sb:12:23
|
||||
│
|
||||
12 │ uses behavior: CuriousExporer
|
||||
│ ^^^^^^^^^^^^^^ no behavior named `CuriousExporer` exists
|
||||
│
|
||||
= help: did you mean `CuriousExplorer`? (defined in behaviors/alice_behaviors.sb)
|
||||
```
|
||||
|
||||
### 5.2 Semantic Validation
|
||||
|
||||
1. **At Most One Default**: Each link array can have at most one `default: true`
|
||||
2. **Priority + Default Conflicts**: If `default: true`, priority should be `low` or omitted
|
||||
3. **Condition Completeness**: Warn if conditions are not exhaustive (no default + gaps in conditions)
|
||||
|
||||
**Warning Example:**
|
||||
```
|
||||
warning: conditions may not cover all cases
|
||||
┌─ characters/alice.sb:8:5
|
||||
│
|
||||
8 │ uses behaviors: [
|
||||
9 │ { tree: GiantBehavior, when: current_size == huge }
|
||||
10 │ { tree: TinyBehavior, when: current_size == tiny }
|
||||
11 │ ]
|
||||
│
|
||||
= note: no default behavior specified and conditions don't cover all size values
|
||||
= help: add a default behavior: { tree: NormalBehavior, default: true }
|
||||
```
|
||||
|
||||
### 5.3 Template Merge Logic
|
||||
|
||||
When a character uses `from Template`, behavior and schedule links are merged:
|
||||
|
||||
```rust
|
||||
// Pseudocode for merge logic
|
||||
fn merge_character_with_template(char: Character, template: Template) -> ResolvedCharacter {
|
||||
let behavior_links = if !char.behavior_links.is_empty() {
|
||||
char.behavior_links // Character overrides completely
|
||||
} else {
|
||||
template.behavior_links // Inherit from template
|
||||
};
|
||||
|
||||
let schedule_links = if !char.schedule_links.is_empty() {
|
||||
char.schedule_links
|
||||
} else {
|
||||
template.schedule_links
|
||||
};
|
||||
|
||||
// ... merge fields, etc.
|
||||
}
|
||||
```
|
||||
|
||||
**Key Rule**: If character defines ANY behavior links, template's behavior links are ignored entirely. Same for schedules. This is all-or-nothing replacement, not merging. (no, i like merging. idk how we'll handle conflicts, but i want to support that kinda composition)
|
||||
|
||||
---
|
||||
|
||||
## 6. Resolved Type Representation
|
||||
|
||||
### 6.1 Resolved Link Types
|
||||
|
||||
```rust
|
||||
// In src/types.rs
|
||||
|
||||
/// Resolved behavior link with all references resolved
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct ResolvedBehaviorLink {
|
||||
pub tree_name: String, // Fully qualified behavior name
|
||||
pub priority: Priority, // Resolved to enum
|
||||
pub condition: Option<Expr>, // Validated expression
|
||||
pub is_default: bool,
|
||||
}
|
||||
|
||||
/// Resolved schedule link
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct ResolvedScheduleLink {
|
||||
pub schedule_name: String, // Fully qualified schedule name
|
||||
pub condition: Option<Expr>,
|
||||
pub is_default: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, PartialOrd)]
|
||||
pub enum Priority {
|
||||
Critical = 3,
|
||||
High = 2,
|
||||
Normal = 1,
|
||||
Low = 0,
|
||||
}
|
||||
```
|
||||
|
||||
### 6.2 Updated Resolved Structs
|
||||
|
||||
```rust
|
||||
pub struct ResolvedCharacter {
|
||||
pub name: String,
|
||||
pub species: Option<String>,
|
||||
pub fields: HashMap<String, Value>,
|
||||
pub prose_blocks: HashMap<String, ProseBlock>,
|
||||
pub behavior_links: Vec<ResolvedBehaviorLink>, // NEW
|
||||
pub schedule_links: Vec<ResolvedScheduleLink>, // NEW
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
pub struct ResolvedInstitution {
|
||||
pub name: String,
|
||||
pub fields: HashMap<String, Value>,
|
||||
pub behavior_links: Vec<ResolvedBehaviorLink>, // NEW
|
||||
pub schedule_links: Vec<ResolvedScheduleLink>, // NEW
|
||||
pub span: Span,
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. SBIR Representation Proposal
|
||||
|
||||
### 7.1 CHARACTERS Section Extension
|
||||
|
||||
Currently, the CHARACTERS section (called ENTITIES in some specs) stores character data. We extend it:
|
||||
|
||||
```
|
||||
CHARACTERS Section:
|
||||
- count: u32
|
||||
- characters: [Character...]
|
||||
|
||||
Character:
|
||||
- name: String
|
||||
- species: Option<String>
|
||||
- fields: Map<String, Value>
|
||||
- behavior_links: [BehaviorLink...] <-- NEW
|
||||
- schedule_links: [ScheduleLink...] <-- NEW
|
||||
|
||||
BehaviorLink:
|
||||
- behavior_id: u32 (index into BEHAVIORS section)
|
||||
- priority: u8 (0=low, 1=normal, 2=high, 3=critical)
|
||||
- condition: Option<Expression>
|
||||
- is_default: bool
|
||||
|
||||
ScheduleLink:
|
||||
- schedule_id: u32 (index into SCHEDULES section)
|
||||
- condition: Option<Expression>
|
||||
- is_default: bool
|
||||
|
||||
Expression:
|
||||
- (Existing expression bytecode format from design.md §5)
|
||||
```
|
||||
|
||||
### 7.2 INSTITUTIONS Section Extension
|
||||
|
||||
```
|
||||
INSTITUTIONS Section:
|
||||
- count: u32
|
||||
- institutions: [Institution...]
|
||||
|
||||
Institution:
|
||||
- name: String
|
||||
- fields: Map<String, Value>
|
||||
- behavior_links: [BehaviorLink...] <-- NEW
|
||||
- schedule_links: [ScheduleLink...] <-- NEW
|
||||
```
|
||||
|
||||
### 7.3 BEHAVIORS and SCHEDULES Sections
|
||||
|
||||
These sections remain unchanged—they define the behavior trees and schedules. Links reference them by index.
|
||||
|
||||
**Index Resolution:**
|
||||
- During compilation, behavior/schedule names are resolved to their index in the respective section
|
||||
- At runtime, the engine uses the index to look up the behavior/schedule definition
|
||||
|
||||
---
|
||||
|
||||
## 8. Runtime Link Resolution Algorithm
|
||||
|
||||
### 8.1 Behavior Selection
|
||||
|
||||
When the engine needs to select a behavior for a character:
|
||||
|
||||
```rust
|
||||
fn select_behavior(character: &Character, context: &RuntimeContext) -> Option<&Behavior> {
|
||||
let mut candidates: Vec<_> = character.behavior_links
|
||||
.iter()
|
||||
.filter(|link| {
|
||||
// Evaluate condition if present
|
||||
link.condition.is_none() || evaluate_condition(&link.condition, context)
|
||||
})
|
||||
.collect();
|
||||
|
||||
if candidates.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Sort by priority (descending)
|
||||
candidates.sort_by(|a, b| b.priority.cmp(&a.priority));
|
||||
|
||||
// Return highest priority candidate
|
||||
// (If multiple same priority, declaration order is preserved by stable sort)
|
||||
let selected = candidates[0];
|
||||
Some(get_behavior_by_id(selected.behavior_id))
|
||||
}
|
||||
```
|
||||
|
||||
**Key Properties:**
|
||||
- Priority-based selection: higher priority wins
|
||||
- Conditions filter candidates before priority sorting
|
||||
- `default: true` only matters if no conditions match (it's implicitly `when: true`)
|
||||
- Deterministic: same context always yields same behavior
|
||||
|
||||
### 8.2 Schedule Selection
|
||||
|
||||
```rust
|
||||
fn select_schedule(entity: &Entity, context: &RuntimeContext) -> Option<&Schedule> {
|
||||
for link in &entity.schedule_links {
|
||||
if link.is_default {
|
||||
continue; // Skip default, check it last
|
||||
}
|
||||
|
||||
if link.condition.is_none() || evaluate_condition(&link.condition, context) {
|
||||
return Some(get_schedule_by_id(link.schedule_id));
|
||||
}
|
||||
}
|
||||
|
||||
// No conditions matched, use default if present
|
||||
entity.schedule_links
|
||||
.iter()
|
||||
.find(|link| link.is_default)
|
||||
.map(|link| get_schedule_by_id(link.schedule_id))
|
||||
}
|
||||
```
|
||||
|
||||
**Key Properties:**
|
||||
- First-match semantics: first condition that evaluates to true wins
|
||||
- Default is fallback: only used if no condition matches
|
||||
- Order matters: earlier links are checked first
|
||||
|
||||
---
|
||||
|
||||
## 9. Examples
|
||||
|
||||
### 9.1 Simple Character with Behavior and Schedule
|
||||
|
||||
```storybook
|
||||
behavior BakerBehavior {
|
||||
> {
|
||||
check_oven
|
||||
serve_customers
|
||||
clean_workspace
|
||||
}
|
||||
}
|
||||
|
||||
schedule BakerSchedule {
|
||||
block work { 5:00 - 13:00 }
|
||||
block lunch { 13:00 - 14:00 }
|
||||
block home { 14:00 - 22:00 }
|
||||
block sleep { 22:00 - 5:00 }
|
||||
}
|
||||
|
||||
character Martha: Human {
|
||||
age: 34
|
||||
occupation: baker
|
||||
|
||||
uses behavior: BakerBehavior
|
||||
uses schedule: BakerSchedule
|
||||
}
|
||||
```
|
||||
|
||||
### 9.2 Character with Multiple Context-Dependent Behaviors
|
||||
|
||||
```storybook
|
||||
character Alice: Human {
|
||||
age: 7
|
||||
current_size: normal
|
||||
emotional_state: curious
|
||||
|
||||
uses behaviors: [
|
||||
{ tree: PanicBehavior, priority: critical, when: emotional_state == frightened }
|
||||
{ tree: GiantBehavior, when: current_size == huge }
|
||||
{ tree: TinyBehavior, when: current_size == tiny }
|
||||
{ tree: BraveBehavior, when: emotional_state == brave }
|
||||
{ tree: CuriousExplorer, default: true }
|
||||
]
|
||||
|
||||
uses schedules: [
|
||||
{ schedule: SleepingSchedule, when: emotional_state == exhausted }
|
||||
{ schedule: AdventureSchedule, default: true }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 9.3 Institution with Seasonal Schedules
|
||||
|
||||
```storybook
|
||||
schedule SummerHours {
|
||||
block open { 6:00 - 20:00 }
|
||||
block closed { 20:00 - 6:00 }
|
||||
}
|
||||
|
||||
schedule WinterHours {
|
||||
block open { 7:00 - 18:00 }
|
||||
block closed { 18:00 - 7:00 }
|
||||
}
|
||||
|
||||
institution Bakery {
|
||||
type: commercial
|
||||
|
||||
uses behavior: BakeryOperations
|
||||
uses schedules: [
|
||||
{ schedule: SummerHours, when: season == summer }
|
||||
{ schedule: WinterHours, when: season == winter }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 9.4 Template with Default Links
|
||||
|
||||
```storybook
|
||||
behavior WonderlandBehavior {
|
||||
> {
|
||||
speak_nonsense
|
||||
violate_logic
|
||||
}
|
||||
}
|
||||
|
||||
schedule WonderlandSchedule {
|
||||
block awake { 0:00 - 24:00 } // Always awake in dreams
|
||||
}
|
||||
|
||||
template WonderlandCreature {
|
||||
uses behavior: WonderlandBehavior
|
||||
uses schedule: WonderlandSchedule
|
||||
}
|
||||
|
||||
character CheshireCat: Cat from WonderlandCreature {
|
||||
// Inherits WonderlandBehavior and WonderlandSchedule
|
||||
can_vanish: true
|
||||
}
|
||||
|
||||
character Alice: Human from WonderlandCreature {
|
||||
// Overrides behavior but keeps schedule
|
||||
uses behavior: CuriousExplorer
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 10. Open Questions for User Review (Checkpoint 1)
|
||||
|
||||
### Question 1: Priority vs. Declaration Order
|
||||
|
||||
**Current Design:** Priority determines order, then declaration order breaks ties.
|
||||
|
||||
**Alternative:** Remove priority, use only declaration order (simpler but less expressive).
|
||||
|
||||
**Recommendation:** Keep priority. It's more explicit and handles common use cases like "urgent needs always trump routine activities." (i guess priority is fine)
|
||||
|
||||
### Question 2: Condition Syntax Sugar
|
||||
|
||||
**Current Design:** Full condition expressions.
|
||||
|
||||
**Alternative:** Add syntactic sugar for common patterns:
|
||||
```storybook
|
||||
uses behaviors: [
|
||||
{ tree: GiantBehavior, when: current_size == huge }
|
||||
// vs.
|
||||
{ tree: GiantBehavior, when current_size: huge } // shorter
|
||||
]
|
||||
```
|
||||
|
||||
**Recommendation:** Start with full expressions, add sugar if usage reveals patterns. (use `==` or `is`, support both like python does.)
|
||||
|
||||
### Question 3: Schedule-Behavior Integration
|
||||
|
||||
**Current Design:** Behaviors and schedules are separate links. Schedule determines WHEN, behavior determines WHAT.
|
||||
|
||||
**Alternative:** Allow schedules to specify behaviors inline:
|
||||
```storybook
|
||||
schedule WorkSchedule {
|
||||
block work { 9:00 - 17:00, behavior: WorkBehavior }
|
||||
}
|
||||
```
|
||||
|
||||
**Recommendation:** Defer inline behaviors to schedule system design (Task #8). Keep linking separate for now. (yeah that's fine)
|
||||
|
||||
### Question 4: Link Override Semantics
|
||||
|
||||
**Current Design:** If character defines any behavior links, template's links are completely replaced.
|
||||
|
||||
**Alternative:** Merge character and template links (character links come first, then template links).
|
||||
|
||||
**Recommendation:** Keep replacement semantics. It's clearer and matches existing override system. (sure? i don't remember the nuances tbh)
|
||||
|
||||
### Question 5: Multiple Defaults
|
||||
|
||||
**Current Design:** At most one `default: true` per link type.
|
||||
|
||||
**Alternative:** Allow multiple defaults with priority order.
|
||||
|
||||
**Recommendation:** Keep single default. Multiple defaults creates ambiguity. (single default makes sense?)
|
||||
|
||||
---
|
||||
|
||||
## 11. Implementation Plan (for Task #6)
|
||||
|
||||
### Phase 1: AST Extension (Week 1)
|
||||
1. Add `BehaviorLink` and `ScheduleLink` structs to `ast.rs`
|
||||
2. Add link fields to `Character`, `Institution`, `Template`
|
||||
3. Update `Declaration` enum if needed
|
||||
|
||||
### Phase 2: Parser Implementation (Week 1-2)
|
||||
1. Implement `BehaviorLinkStmt` and `ScheduleLinkStmt` grammar
|
||||
2. Implement `BehaviorLinkSpec` and `ScheduleLinkSpec` parsing
|
||||
3. Add link parsing to character, institution, template productions
|
||||
4. Write parser tests for all link variations
|
||||
|
||||
### Phase 3: Resolution (Week 2)
|
||||
1. Implement behavior/schedule name resolution in `resolve/names.rs`
|
||||
2. Add priority validation
|
||||
3. Add condition expression validation
|
||||
4. Implement template merge logic for links in `resolve/merge.rs`
|
||||
5. Write resolution tests
|
||||
|
||||
### Phase 4: Resolved Types (Week 2)
|
||||
1. Add `ResolvedBehaviorLink` and `ResolvedScheduleLink` to `types.rs`
|
||||
2. Update `ResolvedCharacter` and `ResolvedInstitution`
|
||||
3. Implement conversion in `resolve/convert.rs`
|
||||
4. Write conversion tests
|
||||
|
||||
### Phase 5: Validation & Diagnostics (Week 3)
|
||||
1. Implement semantic validation (single default, etc.)
|
||||
2. Add helpful error messages with fuzzy matching
|
||||
3. Add warnings for incomplete condition coverage
|
||||
4. Write validation tests
|
||||
|
||||
### Phase 6: Integration & Documentation (Week 3)
|
||||
1. Update examples to use new linking syntax
|
||||
2. Update language documentation
|
||||
3. Run full test suite
|
||||
4. Create migration examples (if backward compatibility breaks)
|
||||
|
||||
**Total Estimate:** 3 weeks implementation after design approval.
|
||||
|
||||
---
|
||||
|
||||
## 12. Success Criteria
|
||||
|
||||
### Must Have
|
||||
- [x] Unified `uses` keyword for behaviors and schedules
|
||||
- [x] Single-link syntax works (`uses behavior: Name`)
|
||||
- [x] Multi-link syntax works (`uses behaviors: [...]`)
|
||||
- [x] Conditions supported (`when: expression`)
|
||||
- [x] Priorities supported for behaviors
|
||||
- [x] Default fallback supported (`default: true`)
|
||||
- [x] Template inheritance works
|
||||
- [x] Character linking works
|
||||
- [x] Institution linking works
|
||||
- [x] Parser produces correct AST
|
||||
- [x] Resolver validates references
|
||||
- [x] Clear error messages
|
||||
- [x] SBIR format defined
|
||||
|
||||
### Should Have
|
||||
- [ ] Warning for incomplete condition coverage
|
||||
- [ ] Examples for all use cases
|
||||
- [ ] Migration guide if needed
|
||||
- [ ] Runtime selection algorithm specification
|
||||
- [ ] Performance characteristics documented
|
||||
|
||||
### Nice to Have
|
||||
- [ ] Visual editor support design
|
||||
- [ ] Auto-completion for behavior/schedule names
|
||||
- [ ] Link refactoring tools
|
||||
|
||||
---
|
||||
|
||||
## 13. Risks & Mitigation
|
||||
|
||||
| Risk | Likelihood | Impact | Mitigation |
|
||||
|------|-----------|--------|------------|
|
||||
| Syntax conflicts with relationship linking | Low | Medium | Different syntax context—parser can disambiguate |
|
||||
| Complex condition expressions hard to debug | Medium | Medium | Good error messages, warnings for non-exhaustive conditions |
|
||||
| Priority system confusing for users | Medium | Low | Clear documentation, examples, default priority=normal |
|
||||
| Template override semantics unclear | Medium | Medium | Explicit documentation, validation warnings |
|
||||
| SBIR encoding inefficient | Low | Low | Use indices for references, compress expressions |
|
||||
| Runtime selection too slow | Low | Medium | Profile early, cache selections if needed |
|
||||
|
||||
---
|
||||
|
||||
## Appendix A: Comparison with Relationship Linking
|
||||
|
||||
**Relationship Linking** (existing):
|
||||
- Top-level `relationship` declarations
|
||||
- Participants within relationships
|
||||
- Bidirectional by nature
|
||||
- `self`/`other` blocks for asymmetry
|
||||
|
||||
**Resource Linking** (this design):
|
||||
- Links within character/institution definitions
|
||||
- References to external behaviors/schedules
|
||||
- Unidirectional (character → behavior)
|
||||
- Conditions/priorities for selection
|
||||
|
||||
These are complementary systems serving different purposes. No conflict.
|
||||
|
||||
---
|
||||
|
||||
## Appendix B: Grammar Sketch (Full)
|
||||
|
||||
```lalrpop
|
||||
// Simplified grammar showing link integration
|
||||
|
||||
Character: Character = {
|
||||
"character" <name:Ident> <species:SpeciesClause?> <template:TemplateClause?>
|
||||
"{" <items:CharacterItem*> "}" => // ... construct Character
|
||||
};
|
||||
|
||||
CharacterItem = {
|
||||
Field,
|
||||
BehaviorLinkStmt,
|
||||
ScheduleLinkStmt,
|
||||
ProseBlock,
|
||||
};
|
||||
|
||||
BehaviorLinkStmt: Vec<BehaviorLink> = {
|
||||
"uses" "behavior" ":" <QualifiedPath> => // single link
|
||||
"uses" "behaviors" ":" "[" <Comma<BehaviorLinkSpec>> "]" => // multi link
|
||||
};
|
||||
|
||||
BehaviorLinkSpec: BehaviorLink = {
|
||||
"{" <Comma<BehaviorLinkField>> "}" => // parse fields into BehaviorLink
|
||||
};
|
||||
|
||||
BehaviorLinkField = {
|
||||
"tree" ":" QualifiedPath,
|
||||
"priority" ":" Ident,
|
||||
"when" ":" Expr,
|
||||
"default" ":" "true",
|
||||
};
|
||||
|
||||
// Parallel structure for schedules...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**End of Design Document**
|
||||
|
||||
**Next Step:** Present to user (Sienna) for Checkpoint 1 review and approval.
|
||||
Reference in New Issue
Block a user