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:
@@ -66,15 +66,44 @@ DottedPath: Vec<String> = {
|
||||
// ===== Character =====
|
||||
|
||||
Character: Character = {
|
||||
"character" <name:Ident> <species:(":" <Ident>)?> <template:TemplateClause?> "{" <fields:Field*> "}" => Character {
|
||||
name,
|
||||
species,
|
||||
fields,
|
||||
template,
|
||||
span: Span::new(0, 0),
|
||||
"character" <name:Ident> <species:(":" <Ident>)?> <template:TemplateClause?> "{" <body:CharacterBody> "}" => {
|
||||
Character {
|
||||
name,
|
||||
species,
|
||||
fields: body.0,
|
||||
template,
|
||||
uses_behaviors: body.1,
|
||||
uses_schedule: body.2,
|
||||
span: Span::new(0, 0),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Character body can contain fields and uses clauses in any order
|
||||
CharacterBody: (Vec<Field>, Option<Vec<BehaviorLink>>, Option<Vec<String>>) = {
|
||||
<items:CharacterBodyItem*> => {
|
||||
let mut fields = Vec::new();
|
||||
let mut uses_behaviors = None;
|
||||
let mut uses_schedule = None;
|
||||
|
||||
for item in items {
|
||||
match item {
|
||||
CharacterBodyItem::Field(f) => fields.push(f),
|
||||
CharacterBodyItem::UsesBehaviors(b) => uses_behaviors = Some(b),
|
||||
CharacterBodyItem::UsesSchedule(s) => uses_schedule = Some(s),
|
||||
}
|
||||
}
|
||||
|
||||
(fields, uses_behaviors, uses_schedule)
|
||||
}
|
||||
};
|
||||
|
||||
CharacterBodyItem: CharacterBodyItem = {
|
||||
<Field> => CharacterBodyItem::Field(<>),
|
||||
<UsesBehaviorsClause> => CharacterBodyItem::UsesBehaviors(<>),
|
||||
<UsesScheduleClause> => CharacterBodyItem::UsesSchedule(<>),
|
||||
};
|
||||
|
||||
TemplateClause: Vec<String> = {
|
||||
"from" <t:Ident> <rest:("," <Ident>)*> => {
|
||||
let mut templates = vec![t];
|
||||
@@ -83,18 +112,116 @@ TemplateClause: Vec<String> = {
|
||||
}
|
||||
};
|
||||
|
||||
// ===== Template =====
|
||||
// uses behaviors: [...]
|
||||
UsesBehaviorsClause: Vec<BehaviorLink> = {
|
||||
"uses" "behaviors" ":" "[" <links:Comma<BehaviorLinkItem>> "]" => links,
|
||||
};
|
||||
|
||||
Template: Template = {
|
||||
"template" <name:Ident> <strict:"strict"?> "{" <includes:Include*> <fields:Field*> "}" => Template {
|
||||
name,
|
||||
fields,
|
||||
strict: strict.is_some(),
|
||||
includes,
|
||||
span: Span::new(0, 0),
|
||||
// Individual behavior link: { tree: BehaviorName, priority: high, when: condition }
|
||||
BehaviorLinkItem: BehaviorLink = {
|
||||
"{" <fields:BehaviorLinkField+> "}" => {
|
||||
let mut tree = None;
|
||||
let mut condition = None;
|
||||
let mut priority = Priority::Normal;
|
||||
|
||||
for field in fields {
|
||||
match field {
|
||||
BehaviorLinkField::Tree(t) => tree = Some(t),
|
||||
BehaviorLinkField::Condition(c) => condition = Some(c),
|
||||
BehaviorLinkField::Priority(p) => priority = p,
|
||||
}
|
||||
}
|
||||
|
||||
BehaviorLink {
|
||||
tree: tree.expect("behavior link must have 'tree' field"),
|
||||
condition,
|
||||
priority,
|
||||
span: Span::new(0, 0),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Fields within a behavior link
|
||||
BehaviorLinkField: BehaviorLinkField = {
|
||||
"tree" ":" <path:Path> ","? => BehaviorLinkField::Tree(path),
|
||||
"when" ":" <expr:Expr> ","? => BehaviorLinkField::Condition(expr),
|
||||
"priority" ":" <p:PriorityLevel> ","? => BehaviorLinkField::Priority(p),
|
||||
};
|
||||
|
||||
PriorityLevel: Priority = {
|
||||
<s:Ident> => match s.as_str() {
|
||||
"low" => Priority::Low,
|
||||
"normal" => Priority::Normal,
|
||||
"high" => Priority::High,
|
||||
"critical" => Priority::Critical,
|
||||
_ => Priority::Normal, // Default to normal for invalid values
|
||||
},
|
||||
};
|
||||
|
||||
// uses schedule: ScheduleName or uses schedules: [Name1, Name2]
|
||||
UsesScheduleClause: Vec<String> = {
|
||||
"uses" "schedule" ":" <name:Ident> => vec![name],
|
||||
"uses" "schedules" ":" "[" <names:Comma<Ident>> "]" => names,
|
||||
};
|
||||
|
||||
// ===== Template =====
|
||||
|
||||
Template: Template = {
|
||||
"template" <name:Ident> <strict:"strict"?> "{" <body:TemplateBodyItem*> "}" => {
|
||||
let mut fields = Vec::new();
|
||||
let mut includes = Vec::new();
|
||||
let mut uses_behaviors = None;
|
||||
let mut uses_schedule = None;
|
||||
|
||||
for item in body {
|
||||
match item {
|
||||
TemplateBodyItem::Field(f) => fields.push(f),
|
||||
TemplateBodyItem::Include(inc) => includes.push(inc),
|
||||
TemplateBodyItem::UsesBehaviors(b) => uses_behaviors = Some(b),
|
||||
TemplateBodyItem::UsesSchedule(s) => uses_schedule = Some(s),
|
||||
}
|
||||
}
|
||||
|
||||
Template {
|
||||
name,
|
||||
fields,
|
||||
strict: strict.is_some(),
|
||||
includes,
|
||||
uses_behaviors,
|
||||
uses_schedule,
|
||||
span: Span::new(0, 0),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Template body items (fields, includes, uses behaviors, uses schedule)
|
||||
TemplateBodyItem: TemplateBodyItem = {
|
||||
<Field> => TemplateBodyItem::Field(<>),
|
||||
"include" <name:Ident> => TemplateBodyItem::Include(name),
|
||||
<TemplateUsesBehaviorsClause> => TemplateBodyItem::UsesBehaviors(<>),
|
||||
<TemplateUsesScheduleClause> => TemplateBodyItem::UsesSchedule(<>),
|
||||
};
|
||||
|
||||
// Template-level behavior links (simple list, no priorities/conditions)
|
||||
TemplateUsesBehaviorsClause: Vec<BehaviorLink> = {
|
||||
"uses" "behaviors" ":" <first:Ident> <rest:("," <Ident>)*> => {
|
||||
let mut names = vec![first];
|
||||
names.extend(rest);
|
||||
names.into_iter().map(|name| BehaviorLink {
|
||||
tree: vec![name],
|
||||
condition: None,
|
||||
priority: Priority::Normal,
|
||||
span: Span::new(0, 0),
|
||||
}).collect()
|
||||
},
|
||||
};
|
||||
|
||||
// Template-level schedule links
|
||||
TemplateUsesScheduleClause: Vec<String> = {
|
||||
"uses" "schedule" ":" <name:Ident> => vec![name],
|
||||
};
|
||||
|
||||
// Template/Species include clause
|
||||
Include: String = {
|
||||
"include" <name:Ident> => name
|
||||
};
|
||||
@@ -181,6 +308,11 @@ Duration: Duration = {
|
||||
}
|
||||
};
|
||||
|
||||
// Duration string for decorator timeouts/cooldowns (e.g., "5s", "30m", "2h", "1d")
|
||||
BehaviorDurationLit: String = {
|
||||
<s:DurationLit> => s
|
||||
};
|
||||
|
||||
ProseBlock: ProseBlock = {
|
||||
ProseBlockToken
|
||||
};
|
||||
@@ -233,20 +365,140 @@ Transition: Transition = {
|
||||
// ===== Schedule =====
|
||||
|
||||
Schedule: Schedule = {
|
||||
"schedule" <name:Ident> "{" <fields:Field*> <blocks:ScheduleBlock*> "}" => Schedule {
|
||||
// Simple schedule: schedule Name { ... }
|
||||
"schedule" <name:Ident> "{" <body:ScheduleBody> "}" => Schedule {
|
||||
name,
|
||||
blocks,
|
||||
extends: None,
|
||||
fields: body.0,
|
||||
blocks: body.1,
|
||||
recurrences: body.2,
|
||||
span: Span::new(0, 0),
|
||||
},
|
||||
// Extending schedule: schedule Name extends Base { ... }
|
||||
"schedule" <name:Ident> "extends" <base:Ident> "{" <body:ScheduleBody> "}" => Schedule {
|
||||
name,
|
||||
extends: Some(base),
|
||||
fields: body.0,
|
||||
blocks: body.1,
|
||||
recurrences: body.2,
|
||||
span: Span::new(0, 0),
|
||||
}
|
||||
};
|
||||
|
||||
// Schedule body can contain fields (prose blocks), blocks, and recurrence patterns
|
||||
ScheduleBody: (Vec<Field>, Vec<ScheduleBlock>, Vec<RecurrencePattern>) = {
|
||||
<items:ScheduleBodyItem*> => {
|
||||
let mut fields = Vec::new();
|
||||
let mut blocks = Vec::new();
|
||||
let mut recurrences = Vec::new();
|
||||
|
||||
for item in items {
|
||||
match item {
|
||||
ScheduleBodyItem::Field(f) => fields.push(f),
|
||||
ScheduleBodyItem::Block(b) => blocks.push(b),
|
||||
ScheduleBodyItem::Recurrence(r) => recurrences.push(r),
|
||||
}
|
||||
}
|
||||
|
||||
(fields, blocks, recurrences)
|
||||
}
|
||||
};
|
||||
|
||||
ScheduleBodyItem: ScheduleBodyItem = {
|
||||
<Field> => ScheduleBodyItem::Field(<>),
|
||||
<ScheduleBlock> => ScheduleBodyItem::Block(<>),
|
||||
<RecurrencePattern> => ScheduleBodyItem::Recurrence(<>),
|
||||
};
|
||||
|
||||
ScheduleBlock: ScheduleBlock = {
|
||||
// Legacy syntax: time -> time : activity { fields }
|
||||
<start:Time> "->" <end:Time> ":" <activity:Ident> "{" <fields:Field*> "}" => ScheduleBlock {
|
||||
name: None,
|
||||
is_override: false,
|
||||
start,
|
||||
end,
|
||||
activity,
|
||||
action: None,
|
||||
temporal_constraint: None,
|
||||
fields,
|
||||
span: Span::new(0, 0),
|
||||
},
|
||||
|
||||
// Named block: block name { time, action, fields }
|
||||
"block" <name:Ident> "{" <content:BlockContent> "}" => ScheduleBlock {
|
||||
name: Some(name),
|
||||
is_override: false,
|
||||
start: content.0,
|
||||
end: content.1,
|
||||
activity: String::new(), // Empty for new syntax
|
||||
action: content.2,
|
||||
temporal_constraint: None,
|
||||
fields: content.3,
|
||||
span: Span::new(0, 0),
|
||||
},
|
||||
|
||||
// Override block: override name { time, action, fields }
|
||||
"override" <name:Ident> "{" <content:BlockContent> "}" => ScheduleBlock {
|
||||
name: Some(name),
|
||||
is_override: true,
|
||||
start: content.0,
|
||||
end: content.1,
|
||||
activity: String::new(), // Empty for new syntax
|
||||
action: content.2,
|
||||
temporal_constraint: None,
|
||||
fields: content.3,
|
||||
span: Span::new(0, 0),
|
||||
}
|
||||
};
|
||||
|
||||
// Block content: time range, optional action, and fields
|
||||
BlockContent: (Time, Time, Option<Vec<String>>, Vec<Field>) = {
|
||||
<items:BlockContentItem+> => {
|
||||
let mut start = None;
|
||||
let mut end = None;
|
||||
let mut action = None;
|
||||
let mut fields = Vec::new();
|
||||
|
||||
for item in items {
|
||||
match item {
|
||||
BlockContentItem::TimeRange(s, e) => {
|
||||
start = Some(s);
|
||||
end = Some(e);
|
||||
}
|
||||
BlockContentItem::Field(f) => {
|
||||
if f.name == "action" {
|
||||
// Extract action as qualified path from identifier value
|
||||
if let Value::Identifier(path) = &f.value {
|
||||
action = Some(path.clone());
|
||||
}
|
||||
} else {
|
||||
fields.push(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(
|
||||
start.expect("block must have time range"),
|
||||
end.expect("block must have time range"),
|
||||
action,
|
||||
fields
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
BlockContentItem: BlockContentItem = {
|
||||
<start:Time> "->" <end:Time> ","? => BlockContentItem::TimeRange(start, end),
|
||||
<Field> => BlockContentItem::Field(<>),
|
||||
};
|
||||
|
||||
// Recurrence pattern: recurrence Name on DayOfWeek { blocks }
|
||||
RecurrencePattern: RecurrencePattern = {
|
||||
"recurrence" <name:Ident> "on" <day:Ident> "{" <blocks:ScheduleBlock+> "}" => RecurrencePattern {
|
||||
name,
|
||||
constraint: TemporalConstraint::DayOfWeek(day),
|
||||
blocks,
|
||||
span: Span::new(0, 0),
|
||||
}
|
||||
};
|
||||
|
||||
@@ -263,23 +515,116 @@ Behavior: Behavior = {
|
||||
BehaviorNode: BehaviorNode = {
|
||||
<SelectorNode>,
|
||||
<SequenceNode>,
|
||||
<RepeatNode>,
|
||||
<ConditionNode>,
|
||||
<DecoratorNode>,
|
||||
<ActionNode>,
|
||||
<SubTreeNode>,
|
||||
};
|
||||
|
||||
// Selector node: choose { ... } or choose label { ... }
|
||||
SelectorNode: BehaviorNode = {
|
||||
"?" "{" <nodes:BehaviorNode+> "}" => BehaviorNode::Selector(nodes),
|
||||
"choose" <label:Ident?> "{" <children:BehaviorNode+> "}" => BehaviorNode::Selector {
|
||||
label,
|
||||
children,
|
||||
},
|
||||
};
|
||||
|
||||
// Sequence node: then { ... } or then label { ... }
|
||||
SequenceNode: BehaviorNode = {
|
||||
">" "{" <nodes:BehaviorNode+> "}" => BehaviorNode::Sequence(nodes),
|
||||
"then" <label:Ident?> "{" <children:BehaviorNode+> "}" => BehaviorNode::Sequence {
|
||||
label,
|
||||
children,
|
||||
},
|
||||
};
|
||||
|
||||
RepeatNode: BehaviorNode = {
|
||||
"*" "{" <node:BehaviorNode> "}" => BehaviorNode::Decorator("repeat".to_string(), Box::new(node)),
|
||||
// Condition node: if(expr) or when(expr)
|
||||
// if(expr) { child } is the decorator form (replaces old "guard" keyword)
|
||||
ConditionNode: BehaviorNode = {
|
||||
"if" "(" <condition:Expr> ")" "{" <child:BehaviorNode> "}" => BehaviorNode::Decorator {
|
||||
decorator_type: DecoratorType::If(condition),
|
||||
child: Box::new(child),
|
||||
},
|
||||
"if" "(" <condition:Expr> ")" => BehaviorNode::Condition(condition),
|
||||
"when" "(" <condition:Expr> ")" => BehaviorNode::Condition(condition),
|
||||
};
|
||||
|
||||
// Decorator node: keyword [params] { child }
|
||||
DecoratorNode: BehaviorNode = {
|
||||
<DecoratorRepeat>,
|
||||
<DecoratorRepeatN>,
|
||||
<DecoratorRepeatRange>,
|
||||
<DecoratorInvert>,
|
||||
<DecoratorRetry>,
|
||||
<DecoratorTimeout>,
|
||||
<DecoratorCooldown>,
|
||||
<DecoratorSucceedAlways>,
|
||||
<DecoratorFailAlways>,
|
||||
};
|
||||
|
||||
DecoratorRepeat: BehaviorNode = {
|
||||
"repeat" "{" <child:BehaviorNode> "}" => BehaviorNode::Decorator {
|
||||
decorator_type: DecoratorType::Repeat,
|
||||
child: Box::new(child),
|
||||
},
|
||||
};
|
||||
|
||||
DecoratorRepeatN: BehaviorNode = {
|
||||
"repeat" "(" <n:IntLit> ")" "{" <child:BehaviorNode> "}" => BehaviorNode::Decorator {
|
||||
decorator_type: DecoratorType::RepeatN(n as u32),
|
||||
child: Box::new(child),
|
||||
},
|
||||
};
|
||||
|
||||
DecoratorRepeatRange: BehaviorNode = {
|
||||
"repeat" "(" <min:IntLit> ".." <max:IntLit> ")" "{" <child:BehaviorNode> "}" => BehaviorNode::Decorator {
|
||||
decorator_type: DecoratorType::RepeatRange(min as u32, max as u32),
|
||||
child: Box::new(child),
|
||||
},
|
||||
};
|
||||
|
||||
DecoratorInvert: BehaviorNode = {
|
||||
"invert" "{" <child:BehaviorNode> "}" => BehaviorNode::Decorator {
|
||||
decorator_type: DecoratorType::Invert,
|
||||
child: Box::new(child),
|
||||
},
|
||||
};
|
||||
|
||||
DecoratorRetry: BehaviorNode = {
|
||||
"retry" "(" <n:IntLit> ")" "{" <child:BehaviorNode> "}" => BehaviorNode::Decorator {
|
||||
decorator_type: DecoratorType::Retry(n as u32),
|
||||
child: Box::new(child),
|
||||
},
|
||||
};
|
||||
|
||||
DecoratorTimeout: BehaviorNode = {
|
||||
"timeout" "(" <duration:BehaviorDurationLit> ")" "{" <child:BehaviorNode> "}" => BehaviorNode::Decorator {
|
||||
decorator_type: DecoratorType::Timeout(duration),
|
||||
child: Box::new(child),
|
||||
},
|
||||
};
|
||||
|
||||
DecoratorCooldown: BehaviorNode = {
|
||||
"cooldown" "(" <duration:BehaviorDurationLit> ")" "{" <child:BehaviorNode> "}" => BehaviorNode::Decorator {
|
||||
decorator_type: DecoratorType::Cooldown(duration),
|
||||
child: Box::new(child),
|
||||
},
|
||||
};
|
||||
|
||||
DecoratorSucceedAlways: BehaviorNode = {
|
||||
"succeed_always" "{" <child:BehaviorNode> "}" => BehaviorNode::Decorator {
|
||||
decorator_type: DecoratorType::SucceedAlways,
|
||||
child: Box::new(child),
|
||||
},
|
||||
};
|
||||
|
||||
DecoratorFailAlways: BehaviorNode = {
|
||||
"fail_always" "{" <child:BehaviorNode> "}" => BehaviorNode::Decorator {
|
||||
decorator_type: DecoratorType::FailAlways,
|
||||
child: Box::new(child),
|
||||
},
|
||||
};
|
||||
|
||||
// Action node: action_name or action_name(params)
|
||||
ActionNode: BehaviorNode = {
|
||||
<name:Ident> "(" <params:Comma<ActionParam>> ")" => BehaviorNode::Action(name, params),
|
||||
<name:Ident> => BehaviorNode::Action(name, vec![]),
|
||||
@@ -300,20 +645,50 @@ ActionParam: Field = {
|
||||
},
|
||||
};
|
||||
|
||||
// Subtree node: include path::to::subtree
|
||||
SubTreeNode: BehaviorNode = {
|
||||
"@" <path:Path> => BehaviorNode::SubTree(path),
|
||||
"include" <path:Path> => BehaviorNode::SubTree(path),
|
||||
};
|
||||
|
||||
// ===== Institution =====
|
||||
|
||||
Institution: Institution = {
|
||||
"institution" <name:Ident> "{" <fields:Field*> "}" => Institution {
|
||||
name,
|
||||
fields,
|
||||
span: Span::new(0, 0),
|
||||
"institution" <name:Ident> "{" <body:InstitutionBody> "}" => {
|
||||
Institution {
|
||||
name,
|
||||
fields: body.0,
|
||||
uses_behaviors: body.1,
|
||||
uses_schedule: body.2,
|
||||
span: Span::new(0, 0),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Institution body can contain fields and uses clauses in any order
|
||||
InstitutionBody: (Vec<Field>, Option<Vec<BehaviorLink>>, Option<Vec<String>>) = {
|
||||
<items:InstitutionBodyItem*> => {
|
||||
let mut fields = Vec::new();
|
||||
let mut uses_behaviors = None;
|
||||
let mut uses_schedule = None;
|
||||
|
||||
for item in items {
|
||||
match item {
|
||||
InstitutionBodyItem::Field(f) => fields.push(f),
|
||||
InstitutionBodyItem::UsesBehaviors(b) => uses_behaviors = Some(b),
|
||||
InstitutionBodyItem::UsesSchedule(s) => uses_schedule = Some(s),
|
||||
}
|
||||
}
|
||||
|
||||
(fields, uses_behaviors, uses_schedule)
|
||||
}
|
||||
};
|
||||
|
||||
InstitutionBodyItem: InstitutionBodyItem = {
|
||||
<Field> => InstitutionBodyItem::Field(<>),
|
||||
<UsesBehaviorsClause> => InstitutionBodyItem::UsesBehaviors(<>),
|
||||
<UsesScheduleClause> => InstitutionBodyItem::UsesSchedule(<>),
|
||||
};
|
||||
|
||||
// ===== Relationship =====
|
||||
|
||||
Relationship: Relationship = {
|
||||
@@ -326,40 +701,22 @@ Relationship: Relationship = {
|
||||
};
|
||||
|
||||
Participant: Participant = {
|
||||
// Participant with inline block after name
|
||||
<name:Path> "{" <fields:Field*> "}" => Participant {
|
||||
role: None,
|
||||
name,
|
||||
self_block: Some(fields),
|
||||
other_block: None,
|
||||
span: Span::new(0, 0),
|
||||
},
|
||||
// Participant with role and inline block
|
||||
// Participant with role and block (block required)
|
||||
<name:Path> "as" <role:Ident> "{" <fields:Field*> "}" => Participant {
|
||||
name,
|
||||
role: Some(role),
|
||||
name,
|
||||
self_block: Some(fields),
|
||||
other_block: None,
|
||||
fields,
|
||||
span: Span::new(0, 0),
|
||||
},
|
||||
// Participant without blocks (bare name)
|
||||
<name:Path> => Participant {
|
||||
// Participant without role (block still required)
|
||||
<name:Path> "{" <fields:Field*> "}" => Participant {
|
||||
name,
|
||||
role: None,
|
||||
name,
|
||||
self_block: None,
|
||||
other_block: None,
|
||||
fields,
|
||||
span: Span::new(0, 0),
|
||||
},
|
||||
};
|
||||
|
||||
SelfBlock: Vec<Field> = {
|
||||
"self" "{" <fields:Field*> "}" => fields
|
||||
};
|
||||
|
||||
OtherBlock: Vec<Field> = {
|
||||
"other" "{" <fields:Field*> "}" => fields
|
||||
};
|
||||
|
||||
// ===== Location =====
|
||||
|
||||
Location: Location = {
|
||||
@@ -540,9 +897,32 @@ extern {
|
||||
"include" => Token::Include,
|
||||
"from" => Token::From,
|
||||
"is" => Token::Is,
|
||||
"uses" => Token::Uses,
|
||||
"behaviors" => Token::Behaviors,
|
||||
"schedules" => Token::Schedules,
|
||||
"tree" => Token::Tree,
|
||||
"priority" => Token::Priority,
|
||||
"extends" => Token::Extends,
|
||||
"override" => Token::Override,
|
||||
"recurrence" => Token::Recurrence,
|
||||
"season" => Token::Season,
|
||||
"block" => Token::Block,
|
||||
"true" => Token::True,
|
||||
"false" => Token::False,
|
||||
|
||||
// Behavior tree keywords
|
||||
"choose" => Token::Choose,
|
||||
"then" => Token::Then,
|
||||
"if" => Token::If,
|
||||
"when" => Token::When,
|
||||
"repeat" => Token::Repeat,
|
||||
"invert" => Token::Invert,
|
||||
"retry" => Token::Retry,
|
||||
"timeout" => Token::Timeout,
|
||||
"cooldown" => Token::Cooldown,
|
||||
"succeed_always" => Token::SucceedAlways,
|
||||
"fail_always" => Token::FailAlways,
|
||||
|
||||
// Literals
|
||||
Ident => Token::Ident(<String>),
|
||||
IntLit => Token::IntLit(<i64>),
|
||||
|
||||
Reference in New Issue
Block a user