Files
storybook/src/syntax/ast.rs
Sienna Meridian Satterwhite 9e2cdea6f4 feat(ast): add species base to template declarations
Added optional species_base field to Template struct enabling
template-species inheritance syntax: `template Name: Species { ... }`.
Updated LALRPOP grammar and all Template construction sites.
2026-02-14 14:12:52 +00:00

547 lines
14 KiB
Rust

/// Source location for error reporting with line/column information
#[derive(Debug, Clone, PartialEq)]
pub struct Span {
pub start: usize,
pub end: usize,
pub start_line: usize, // 0-indexed line number
pub start_col: usize, // 0-indexed column number
pub end_line: usize,
pub end_col: usize,
}
impl Span {
pub fn new(start: usize, end: usize) -> Self {
Self {
start,
end,
start_line: 0,
start_col: 0,
end_line: 0,
end_col: 0,
}
}
pub fn with_position(
start: usize,
end: usize,
start_line: usize,
start_col: usize,
end_line: usize,
end_col: usize,
) -> Self {
Self {
start,
end,
start_line,
start_col,
end_line,
end_col,
}
}
/// Convert to LSP Position for the start
pub fn start_position(&self) -> (u32, u32) {
(self.start_line as u32, self.start_col as u32)
}
/// Convert to LSP Position for the end
pub fn end_position(&self) -> (u32, u32) {
(self.end_line as u32, self.end_col as u32)
}
}
/// Top-level file containing multiple declarations
#[derive(Debug, Clone, PartialEq)]
pub struct File {
pub declarations: Vec<Declaration>,
}
/// Any top-level declaration
#[derive(Debug, Clone, PartialEq)]
pub enum Declaration {
Use(UseDecl),
Character(Character),
Template(Template),
LifeArc(LifeArc),
Schedule(Schedule),
Behavior(Behavior),
Institution(Institution),
Relationship(Relationship),
Location(Location),
Species(Species),
Concept(ConceptDecl),
SubConcept(SubConceptDecl),
ConceptComparison(ConceptComparisonDecl),
}
/// Use statement for importing definitions
#[derive(Debug, Clone, PartialEq)]
pub struct UseDecl {
pub path: Vec<String>,
pub kind: UseKind,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq)]
pub enum UseKind {
Single, // use foo::bar
Grouped(Vec<String>), // use foo::{bar, baz}
Wildcard, // use foo::*
}
/// Link to a behavior tree with optional conditions and priority
#[derive(Debug, Clone, PartialEq)]
pub struct BehaviorLink {
pub tree: Vec<String>, // Qualified path to behavior tree
pub condition: Option<Expr>, // Optional when clause
pub priority: Priority, // Execution priority
pub span: Span,
}
/// Priority levels for behavior selection
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum Priority {
Low,
Normal,
High,
Critical,
}
impl std::str::FromStr for Priority {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
| "low" => Ok(Priority::Low),
| "normal" => Ok(Priority::Normal),
| "high" => Ok(Priority::High),
| "critical" => Ok(Priority::Critical),
| _ => Err(()),
}
}
}
/// Character definition
#[derive(Debug, Clone, PartialEq)]
pub struct Character {
pub name: String,
pub species: Option<String>, // `: Species` - what the character fundamentally is
pub fields: Vec<Field>,
pub template: Option<Vec<String>>, // `from Template1, Template2`
pub uses_behaviors: Option<Vec<BehaviorLink>>, // `uses behaviors: [...]`
pub uses_schedule: Option<Vec<String>>, /* `uses schedule: ScheduleName` or `uses schedules:
* [...]` */
pub span: Span,
}
/// Template definition (like Character but allows range values)
#[derive(Debug, Clone, PartialEq)]
pub struct Template {
pub name: String,
pub species_base: Option<String>, // `: Species` - type constraint from species
pub fields: Vec<Field>,
pub strict: bool,
pub includes: Vec<String>,
pub uses_behaviors: Option<Vec<BehaviorLink>>, // `uses behaviors: [...]`
pub uses_schedule: Option<Vec<String>>, /* `uses schedule: ScheduleName` or `uses
* schedules: [...]` */
pub span: Span,
}
/// Field in a structured definition
#[derive(Debug, Clone, PartialEq)]
pub struct Field {
pub name: String,
pub value: Value,
pub span: Span,
}
/// Field value types
#[derive(Debug, Clone, PartialEq)]
pub enum Value {
Number(i64),
Decimal(f64),
Text(String),
Boolean(bool),
Range(Box<Value>, Box<Value>), // For templates: 20..40
Time(Time),
Duration(Duration),
Identifier(Vec<String>), // Qualified path reference
List(Vec<Value>),
Object(Vec<Field>),
ProseBlock(ProseBlock),
Override(Override),
Any, // Special marker for type system - matches any value
}
/// Time literal (HH:MM or HH:MM:SS)
#[derive(Debug, Clone, PartialEq)]
pub struct Time {
pub hour: u8,
pub minute: u8,
pub second: u8,
}
/// Duration literal (e.g., 2h30m)
#[derive(Debug, Clone, PartialEq)]
pub struct Duration {
pub hours: u32,
pub minutes: u32,
pub seconds: u32,
}
/// Prose block with tag
#[derive(Debug, Clone, PartialEq)]
pub struct ProseBlock {
pub tag: String,
pub content: String,
pub span: Span,
}
/// Override specification for template instantiation
#[derive(Debug, Clone, PartialEq)]
pub struct Override {
pub base: Vec<String>, // Template path
pub overrides: Vec<OverrideOp>,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq)]
pub enum OverrideOp {
Set(Field), // field: value
Remove(String), // remove field
Append(Field), // append field
}
/// Life arc state machine
#[derive(Debug, Clone, PartialEq)]
pub struct LifeArc {
pub name: String,
pub states: Vec<ArcState>,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq)]
pub struct ArcState {
pub name: String,
pub on_enter: Option<Vec<Field>>,
pub transitions: Vec<Transition>,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Transition {
pub to: String,
pub condition: Expr,
pub span: Span,
}
/// Schedule definition with composition support
#[derive(Debug, Clone, PartialEq)]
pub struct Schedule {
pub name: String,
pub extends: Option<String>, // Base schedule to extend
pub blocks: Vec<ScheduleBlock>,
pub recurrences: Vec<RecurrencePattern>, // Recurring events
pub fields: Vec<Field>, // Documentation prose blocks, metadata
pub span: Span,
}
/// A time block in a schedule
#[derive(Debug, Clone, PartialEq)]
pub struct ScheduleBlock {
pub name: Option<String>, // Block name for override system
pub is_override: bool, // Whether this block overrides a base block
pub start: Time,
pub end: Time,
pub activity: String, // DEPRECATED: kept for backward compatibility
pub action: Option<Vec<String>>, // Behavior reference (new way)
pub temporal_constraint: Option<TemporalConstraint>, // When this block applies
pub fields: Vec<Field>,
pub span: Span,
}
/// Temporal constraint for when a schedule block applies
#[derive(Debug, Clone, PartialEq)]
pub enum TemporalConstraint {
Season(String), // Applies during specific season (enum value)
DayOfWeek(String), // Applies on specific day of week (enum value)
Month(String), // Applies during specific month (enum value)
DateRange(String, String), // Applies between two dates (TODO: date type)
}
/// Recurring event pattern
#[derive(Debug, Clone, PartialEq)]
pub struct RecurrencePattern {
pub name: String, // Event name (e.g., "MarketDay")
pub constraint: TemporalConstraint, // When it recurs (e.g., "on Earthday")
pub blocks: Vec<ScheduleBlock>, // What happens during the event
pub span: Span,
}
// ===== Parser Helper Types for Schedules =====
/// Helper for parsing schedule bodies with flexible ordering
#[derive(Debug, Clone, PartialEq)]
pub enum ScheduleBodyItem {
Field(Field),
Block(ScheduleBlock),
Recurrence(RecurrencePattern),
}
/// Helper for parsing schedule block content
#[derive(Debug, Clone, PartialEq)]
pub enum BlockContentItem {
TimeRange(Time, Time),
Field(Field),
}
/// Behavior tree definition
#[derive(Debug, Clone, PartialEq)]
pub struct Behavior {
pub name: String,
pub root: BehaviorNode,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq)]
pub enum BehaviorNode {
Selector {
label: Option<String>,
children: Vec<BehaviorNode>,
},
Sequence {
label: Option<String>,
children: Vec<BehaviorNode>,
},
Condition(Expr),
Action(String, Vec<Field>), // Action name + parameters
Decorator {
decorator_type: DecoratorType,
child: Box<BehaviorNode>,
},
SubTree(Vec<String>), // Reference to another behavior
}
#[derive(Debug, Clone, PartialEq)]
pub enum DecoratorType {
Repeat, // infinite loop
RepeatN(u32), // N times
RepeatRange(u32, u32), // min..max times
Invert,
Retry(u32), // max attempts
Timeout(String), // duration string (e.g., "5s", "30m", "2h")
Cooldown(String), // duration string (e.g., "5s", "30m", "2h")
If(Expr),
SucceedAlways,
FailAlways,
}
// BehaviorDuration is used for decorator timeouts/cooldowns (single unit)
// whereas Duration (above) is for general time literals (compound: 2h30m)
#[derive(Debug, Clone, PartialEq)]
pub struct BehaviorDuration {
pub value: u32,
pub unit: DurationUnit,
}
#[derive(Debug, Clone, PartialEq)]
pub enum DurationUnit {
Days,
Hours,
Minutes,
Seconds,
}
impl BehaviorDuration {
pub fn to_milliseconds(&self) -> u64 {
let base_ms = self.value as u64;
match self.unit {
| DurationUnit::Days => base_ms * 24 * 60 * 60 * 1000,
| DurationUnit::Hours => base_ms * 60 * 60 * 1000,
| DurationUnit::Minutes => base_ms * 60 * 1000,
| DurationUnit::Seconds => base_ms * 1000,
}
}
}
/// Institution definition
#[derive(Debug, Clone, PartialEq)]
pub struct Institution {
pub name: String,
pub fields: Vec<Field>,
pub uses_behaviors: Option<Vec<BehaviorLink>>, // `uses behaviors: [...]`
pub uses_schedule: Option<Vec<String>>, /* `uses schedule: ScheduleName` or `uses
* schedules: [...]` */
pub span: Span,
}
/// Relationship definition
#[derive(Debug, Clone, PartialEq)]
pub struct Relationship {
pub name: String,
pub participants: Vec<Participant>,
pub fields: Vec<Field>,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Participant {
pub name: Vec<String>, // Qualified path
pub role: Option<String>, // "as parent" (optional)
pub fields: Vec<Field>, // Participant-specific fields (required block)
pub span: Span,
}
/// Location definition
#[derive(Debug, Clone, PartialEq)]
pub struct Location {
pub name: String,
pub fields: Vec<Field>,
pub span: Span,
}
/// Species definition
#[derive(Debug, Clone, PartialEq)]
pub struct Species {
pub name: String,
pub includes: Vec<String>,
pub fields: Vec<Field>,
pub span: Span,
}
/// Concept declaration - base type definition
#[derive(Debug, Clone, PartialEq)]
pub struct ConceptDecl {
pub name: String,
pub span: Span,
}
/// Sub-concept declaration - enum or record subtype
#[derive(Debug, Clone, PartialEq)]
pub struct SubConceptDecl {
pub name: String,
pub parent_concept: String,
pub kind: SubConceptKind,
pub span: Span,
}
/// Sub-concept can be either enum-like or record-like
#[derive(Debug, Clone, PartialEq)]
pub enum SubConceptKind {
Enum { variants: Vec<String> },
Record { fields: Vec<Field> },
}
/// Concept comparison - compile-time pattern matching for concept variants
#[derive(Debug, Clone, PartialEq)]
pub struct ConceptComparisonDecl {
pub name: String,
pub variants: Vec<VariantPattern>,
pub span: Span,
}
/// A variant pattern with field conditions
#[derive(Debug, Clone, PartialEq)]
pub struct VariantPattern {
pub name: String,
pub conditions: Vec<FieldCondition>,
pub span: Span,
}
/// A condition on a field within a variant pattern
#[derive(Debug, Clone, PartialEq)]
pub struct FieldCondition {
pub field_name: String,
pub condition: Condition,
pub span: Span,
}
/// The type of condition for field matching
#[derive(Debug, Clone, PartialEq)]
pub enum Condition {
Any, // matches any value
Is(Vec<String>), // matches specific values (e.g., "is Glass or is Plastic")
}
/// Expression AST for conditions and queries
#[derive(Debug, Clone, PartialEq)]
pub enum Expr {
NumberLit(i64),
DecimalLit(f64),
TextLit(String),
BooleanLit(bool),
Identifier(Vec<String>),
FieldAccess(Box<Expr>, String),
Comparison(Box<Expr>, CompOp, Box<Expr>),
Logical(Box<Expr>, LogicalOp, Box<Expr>),
Unary(UnaryOp, Box<Expr>),
Quantifier(QuantifierKind, String, Box<Expr>, Box<Expr>), /* forall/exists x in collection:
* predicate */
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum CompOp {
Eq, // ==
Ne, // !=
Lt, // <
Le, // <=
Gt, // >
Ge, // >=
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum LogicalOp {
And,
Or,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum UnaryOp {
Not,
Neg,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum QuantifierKind {
ForAll,
Exists,
}
// ===== Parser Helper Types =====
// These enums are used internally by the LALRPOP parser to handle flexible
// ordering
/// Helper for parsing character/institution bodies with flexible ordering
#[derive(Debug, Clone, PartialEq)]
pub enum CharacterBodyItem {
Field(Field),
UsesBehaviors(Vec<BehaviorLink>),
UsesSchedule(Vec<String>),
}
/// Helper for parsing institution bodies with flexible ordering
#[derive(Debug, Clone, PartialEq)]
pub enum InstitutionBodyItem {
Field(Field),
UsesBehaviors(Vec<BehaviorLink>),
UsesSchedule(Vec<String>),
}
/// Helper for parsing template body items with flexible ordering
#[derive(Debug, Clone, PartialEq)]
pub enum TemplateBodyItem {
Field(Field),
Include(String),
UsesBehaviors(Vec<BehaviorLink>),
UsesSchedule(Vec<String>),
}
/// Helper for parsing behavior link fields
#[derive(Debug, Clone, PartialEq)]
pub enum BehaviorLinkField {
Tree(Vec<String>),
Condition(Expr),
Priority(Priority),
}