feat(grammar): update tree-sitter grammar to v0.3.0

Updated tree-sitter grammar to match v0.3.0 LALRPOP parser:

Grammar updates:
- Schedule: block-based syntax with extends, override, recurrence
- Life arc: requires clause for field validation
- Template: uses behaviors/schedules syntax
- Behavior: correct keywords (choose/then/repeat with optional params)
- Type system: concept_comparison with any/is_condition
- Removed concept semicolon requirement

Query file updates:
- highlights.scm: updated node names to *_declaration
- outline.scm: updated for new declaration node names
- indents.scm: updated node names, removed concept semicolon

Corpus test updates:
- Created schedules.txt with v0.3.0 syntax tests
- Created highlights.txt for highlighting tests
- Updated type_system.txt for v0.3.0 type syntax
- Updated behaviors.txt for correct expression wrapping
- Updated declarations.txt to use correct node names
- Updated basic.txt to use character_declaration/character_body
- Deleted obsolete v0.2.0 syntax tests

Integration tests:
- Added tree_sitter_integration.rs test suite
- Fixed test_any_type to use correct v0.3.0 syntax
- Fixed test_tree_sitter_grammar_builds to use generate command
This commit is contained in:
2026-02-14 17:43:26 +00:00
parent 0c4994acd2
commit e6d297420c
15 changed files with 10449 additions and 7068 deletions

View File

@@ -0,0 +1,444 @@
//! Tree-sitter Grammar Integration Tests
//!
//! These tests validate that:
//! 1. The tree-sitter grammar builds correctly
//! 2. All queries (highlights, injections, etc.) are valid
//! 3. Example files parse without errors
//! 4. The Zed extension builds successfully
//!
//! These are acceptance tests that catch integration issues between
//! the Storybook language and its tree-sitter representation.
use std::{
path::{
Path,
PathBuf,
},
process::Command,
};
fn tree_sitter_dir() -> PathBuf {
let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
path.push("tree-sitter-storybook");
path
}
fn zed_extension_dir() -> PathBuf {
let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
path.push("zed-storybook");
path
}
fn examples_dir() -> PathBuf {
let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
path.push("examples");
path
}
// ============================================================================
// Tree-sitter Grammar Build Tests
// ============================================================================
#[test]
fn test_tree_sitter_grammar_generates() {
let output = Command::new("npx")
.arg("tree-sitter")
.arg("generate")
.current_dir(tree_sitter_dir())
.output()
.expect("Failed to run tree-sitter generate. Is tree-sitter-cli installed?");
if !output.status.success() {
eprintln!("STDOUT: {}", String::from_utf8_lossy(&output.stdout));
eprintln!("STDERR: {}", String::from_utf8_lossy(&output.stderr));
}
assert!(
output.status.success(),
"tree-sitter generate failed. Grammar may have syntax errors."
);
// Verify generated files exist
let parser_c = tree_sitter_dir().join("src").join("parser.c");
assert!(
parser_c.exists(),
"parser.c not generated at {:?}",
parser_c
);
}
#[test]
fn test_tree_sitter_grammar_builds() {
let output = Command::new("npx")
.arg("tree-sitter")
.arg("generate")
.current_dir(tree_sitter_dir())
.output()
.expect("Failed to run tree-sitter generate");
if !output.status.success() {
eprintln!("STDOUT: {}", String::from_utf8_lossy(&output.stdout));
eprintln!("STDERR: {}", String::from_utf8_lossy(&output.stderr));
}
assert!(
output.status.success(),
"tree-sitter generate failed. Grammar may not compile."
);
}
// ============================================================================
// Query Validation Tests
// ============================================================================
#[test]
fn test_highlights_query_valid() {
let output = Command::new("npx")
.arg("tree-sitter")
.arg("test")
.current_dir(tree_sitter_dir())
.output()
.expect("Failed to run tree-sitter test");
if !output.status.success() {
eprintln!("STDOUT: {}", String::from_utf8_lossy(&output.stdout));
eprintln!("STDERR: {}", String::from_utf8_lossy(&output.stderr));
}
assert!(
output.status.success(),
"tree-sitter test failed. Queries (highlights.scm, etc.) may have errors."
);
}
#[test]
fn test_queries_reference_valid_nodes() {
// This test verifies that all node types referenced in queries
// actually exist in the grammar. This catches issues like:
// - Using "any" instead of (any_type)
// - Referencing removed or renamed nodes
// - Typos in node names
let highlights_scm = tree_sitter_dir().join("queries").join("highlights.scm");
assert!(
highlights_scm.exists(),
"highlights.scm not found at {:?}",
highlights_scm
);
let content = std::fs::read_to_string(&highlights_scm).expect("Failed to read highlights.scm");
// Check for common mistakes
let problematic_patterns = vec![
(
"\"any\"",
"Should use (any_type) instead of \"any\" string literal",
),
(
"\"Number\"",
"Number is not a keyword, should use a type node",
),
(
"\"Decimal\"",
"Decimal is not a keyword, should use a type node",
),
("\"Text\"", "Text is not a keyword, should use a type node"),
(
"\"Boolean\"",
"Boolean is not a keyword, should use a type node",
),
];
for (pattern, msg) in problematic_patterns {
if content.contains(pattern) {
// Check if it's in a comment
let lines: Vec<&str> = content.lines().collect();
for line in lines {
if line.contains(pattern) && !line.trim_start().starts_with(';') {
panic!(
"highlights.scm contains problematic pattern '{}': {}",
pattern, msg
);
}
}
}
}
}
// ============================================================================
// Example File Parsing Tests
// ============================================================================
#[test]
fn test_baker_family_example_parses() {
let baker_family = examples_dir().join("baker-family");
assert!(baker_family.exists(), "baker-family example not found");
// Find all .sb files
let sb_files = find_sb_files(&baker_family);
assert!(
!sb_files.is_empty(),
"No .sb files found in baker-family example"
);
for file in &sb_files {
let output = Command::new("npx")
.arg("tree-sitter")
.arg("parse")
.arg(file)
.current_dir(tree_sitter_dir())
.output()
.expect("Failed to run tree-sitter parse");
if !output.status.success() {
eprintln!("Failed to parse: {:?}", file);
eprintln!("STDOUT: {}", String::from_utf8_lossy(&output.stdout));
eprintln!("STDERR: {}", String::from_utf8_lossy(&output.stderr));
}
assert!(
output.status.success(),
"Failed to parse {:?} with tree-sitter",
file
);
// Check for ERROR nodes in the parse tree
let stdout = String::from_utf8_lossy(&output.stdout);
if stdout.contains("ERROR") {
eprintln!("Parse tree for {:?}:", file);
eprintln!("{}", stdout);
panic!("Parse tree contains ERROR nodes for {:?}", file);
}
}
}
#[test]
fn test_all_examples_parse() {
let sb_files = find_sb_files(&examples_dir());
assert!(!sb_files.is_empty(), "No .sb files found in examples/");
let mut failures = Vec::new();
for file in &sb_files {
let output = Command::new("npx")
.arg("tree-sitter")
.arg("parse")
.arg(file)
.current_dir(tree_sitter_dir())
.output()
.expect("Failed to run tree-sitter parse");
if !output.status.success() {
failures.push((file.clone(), "Parse command failed".to_string()));
continue;
}
// Check for ERROR nodes
let stdout = String::from_utf8_lossy(&output.stdout);
if stdout.contains("ERROR") {
failures.push((file.clone(), "Parse tree contains ERROR nodes".to_string()));
}
}
if !failures.is_empty() {
eprintln!("Failed to parse {} files:", failures.len());
for (file, reason) in &failures {
eprintln!(" - {:?}: {}", file, reason);
}
panic!("{} files failed to parse with tree-sitter", failures.len());
}
}
// ============================================================================
// Specific Language Feature Tests
// ============================================================================
#[test]
fn test_type_system_keywords_parse() {
let test_file = tree_sitter_dir()
.join("test")
.join("corpus")
.join("type_system.txt");
if !test_file.exists() {
panic!(
"Type system test corpus not found at {:?}. Please create it.",
test_file
);
}
let output = Command::new("npx")
.arg("tree-sitter")
.arg("test")
.arg("--filter")
.arg("type_system")
.current_dir(tree_sitter_dir())
.output()
.expect("Failed to run tree-sitter test");
if !output.status.success() {
eprintln!("STDOUT: {}", String::from_utf8_lossy(&output.stdout));
eprintln!("STDERR: {}", String::from_utf8_lossy(&output.stderr));
}
assert!(output.status.success(), "Type system test corpus failed");
}
#[test]
fn test_any_type_highlights_correctly() {
// Create a minimal test file with any_type usage
let test_content = r#"
concept_comparison SkillLevel {
Novice: { skill: any },
Expert: { skill: Tier is Expert }
}
"#;
let temp_dir = std::env::temp_dir();
let test_file = temp_dir.join("test_any_type.sb");
std::fs::write(&test_file, test_content).expect("Failed to write test file");
let output = Command::new("npx")
.arg("tree-sitter")
.arg("parse")
.arg(&test_file)
.current_dir(tree_sitter_dir())
.output()
.expect("Failed to run tree-sitter parse");
std::fs::remove_file(&test_file).ok(); // Clean up
if !output.status.success() {
eprintln!("STDOUT: {}", String::from_utf8_lossy(&output.stdout));
eprintln!("STDERR: {}", String::from_utf8_lossy(&output.stderr));
}
assert!(output.status.success(), "Failed to parse any_type usage");
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(
stdout.contains("any_type"),
"Parse tree should contain any_type node"
);
assert!(
!stdout.contains("ERROR"),
"Parse tree should not contain ERROR nodes"
);
}
// ============================================================================
// Zed Extension Build Tests
// ============================================================================
#[test]
#[ignore] // Only run with --ignored flag as this is slow
fn test_zed_extension_builds() {
let build_script = zed_extension_dir().join("build-extension.sh");
if !build_script.exists() {
panic!("build-extension.sh not found at {:?}", build_script);
}
let output = Command::new("bash")
.arg(&build_script)
.current_dir(zed_extension_dir())
.output()
.expect("Failed to run build-extension.sh");
if !output.status.success() {
eprintln!("STDOUT: {}", String::from_utf8_lossy(&output.stdout));
eprintln!("STDERR: {}", String::from_utf8_lossy(&output.stderr));
}
assert!(output.status.success(), "build-extension.sh failed");
// Verify the extension.wasm was created
let wasm = zed_extension_dir().join("extension.wasm");
assert!(wasm.exists(), "extension.wasm not created at {:?}", wasm);
}
#[test]
fn test_extension_toml_has_valid_revision() {
let extension_toml = zed_extension_dir().join("extension.toml");
let content = std::fs::read_to_string(&extension_toml).expect("Failed to read extension.toml");
// Check that rev is set to a valid commit SHA
let rev_line = content
.lines()
.find(|line| line.starts_with("rev = "))
.expect("No 'rev' field found in extension.toml");
let rev = rev_line
.split('"')
.nth(1)
.expect("Could not parse rev value");
assert_eq!(
rev.len(),
40,
"Revision should be a 40-character git SHA, got: {}",
rev
);
// Verify the commit exists
let output = Command::new("git")
.arg("cat-file")
.arg("-t")
.arg(rev)
.current_dir(env!("CARGO_MANIFEST_DIR"))
.output()
.expect("Failed to run git cat-file");
if !output.status.success() {
eprintln!("STDERR: {}", String::from_utf8_lossy(&output.stderr));
}
assert!(
output.status.success(),
"Revision {} does not exist in git history",
rev
);
let obj_type = String::from_utf8_lossy(&output.stdout);
assert_eq!(
obj_type.trim(),
"commit",
"Revision {} is not a commit",
rev
);
}
// ============================================================================
// Helper Functions
// ============================================================================
fn find_sb_files(dir: &Path) -> Vec<PathBuf> {
let mut sb_files = Vec::new();
if !dir.exists() {
return sb_files;
}
visit_dirs(dir, &mut |path| {
if path.extension().and_then(|s| s.to_str()) == Some("sb") {
sb_files.push(path.to_path_buf());
}
});
sb_files
}
fn visit_dirs(dir: &Path, cb: &mut dyn FnMut(&Path)) {
if dir.is_dir() {
for entry in std::fs::read_dir(dir).unwrap() {
let entry = entry.unwrap();
let path = entry.path();
if path.is_dir() {
visit_dirs(&path, cb);
} else {
cb(&path);
}
}
}
}

View File

@@ -21,7 +21,8 @@ module.exports = grammar({
conflicts: $ => [
[$.path_segments],
[$.sub_concept_enum_body, $.sub_concept_record_body]
[$.sub_concept_enum_body, $.sub_concept_record_body],
[$.uses_behaviors]
],
word: $ => $.identifier,
@@ -37,15 +38,15 @@ module.exports = grammar({
// Declarations
declaration: $ => choice(
$.use_declaration,
$.character,
$.template,
$.life_arc,
$.schedule,
$.behavior,
$.institution,
$.relationship,
$.location,
$.species,
$.character_declaration,
$.template_declaration,
$.life_arc_declaration,
$.schedule_declaration,
$.behavior_declaration,
$.institution_declaration,
$.relationship_declaration,
$.location_declaration,
$.species_declaration,
$.enum_declaration,
$.concept_declaration,
$.sub_concept,
@@ -68,28 +69,59 @@ module.exports = grammar({
path_segments: $ => sep1($.identifier, token('::')),
// Character declaration
character: $ => seq(
character_declaration: $ => seq(
'character',
field('name', $.identifier),
optional(seq(':', field('species', $.identifier))),
optional(field('template', $.template_clause)),
field('body', $.block)
field('body', $.character_body)
),
character_body: $ => seq(
'{',
repeat($.field),
'}'
),
template_clause: $ => seq('from', commaSep1($.identifier)),
// Template declaration
template: $ => seq(
template_declaration: $ => seq(
'template',
field('name', $.identifier),
optional(seq(':', field('species', $.identifier))),
optional('strict'),
field('body', $.template_body)
),
template_body: $ => seq(
'{',
repeat($.include),
repeat($.field),
repeat(choice(
$.include,
$.uses_behaviors,
$.uses_schedule,
$.field
)),
'}'
),
uses_behaviors: $ => seq(
'uses',
'behaviors',
':',
commaSep1($.identifier)
),
uses_schedule: $ => seq(
'uses',
choice('schedule', 'schedules'),
':',
choice(
$.identifier,
seq('[', commaSep1($.identifier), ']')
)
),
include: $ => seq('include', $.identifier),
// Fields (key: value pairs)
@@ -177,18 +209,36 @@ module.exports = grammar({
)))),
// Life arc declaration
life_arc: $ => seq(
life_arc_declaration: $ => seq(
'life_arc',
field('name', $.identifier),
optional(field('requires', $.requires_clause)),
'{',
repeat($.field),
repeat($.arc_state),
repeat($.state_block),
'}'
),
arc_state: $ => seq(
requires_clause: $ => seq(
'requires',
'{',
commaSep1($.required_field),
'}'
),
required_field: $ => seq(
field('name', $.identifier),
':',
field('type', $.identifier)
),
state_block: $ => seq(
'state',
field('name', $.identifier),
field('body', $.state_body)
),
state_body: $ => seq(
'{',
optional($.on_enter),
repeat($.field),
@@ -206,28 +256,76 @@ module.exports = grammar({
),
// Schedule declaration
schedule: $ => seq(
schedule_declaration: $ => seq(
'schedule',
field('name', $.identifier),
optional(seq('extends', field('extends', $.identifier))),
field('body', $.schedule_body)
),
schedule_body: $ => seq(
'{',
repeat($.field),
repeat($.schedule_block),
repeat(choice(
$.field,
$.schedule_block,
$.override_block,
$.recurrence_block
)),
'}'
),
// Named block: block name { time range, action, fields }
schedule_block: $ => seq(
'block',
field('name', $.identifier),
'{',
field('time_range', $.time_range),
repeat($.block_field),
'}'
),
// Override block: override name { time range, action, fields }
override_block: $ => seq(
'override',
field('name', $.identifier),
'{',
field('time_range', $.time_range),
repeat($.block_field),
'}'
),
// Recurrence: recurrence Name on DayOfWeek { blocks }
recurrence_block: $ => seq(
'recurrence',
field('name', $.identifier),
'on',
field('day', $.identifier),
'{',
repeat1($.schedule_block),
'}'
),
time_range: $ => seq(
field('start', $.time),
'->',
field('end', $.time),
optional(',')
),
block_field: $ => seq(
field('name', $.identifier),
':',
field('activity', $.identifier),
$.block
field('value', choice($.identifier, $.string, $.integer, $.float))
),
// Behavior tree declaration
behavior: $ => seq(
behavior_declaration: $ => seq(
'behavior',
field('name', $.identifier),
field('body', $.behavior_body)
),
behavior_body: $ => seq(
'{',
repeat($.field),
field('root', $.behavior_node),
@@ -239,6 +337,7 @@ module.exports = grammar({
$.sequence_node,
$.condition_node,
$.if_decorator_node,
$.repeat_node,
$.decorator_node,
$.action_node,
$.subtree_node
@@ -281,7 +380,20 @@ module.exports = grammar({
'}'
),
// Decorator node: repeat/retry/timeout/etc { child }
// Repeat node: repeat { child } or repeat(N) { child } or repeat(min..max) { child }
repeat_node: $ => seq(
'repeat',
optional(field('params', choice(
seq('(', $.integer, ')'),
seq('(', $.integer, '..', $.integer, ')'),
seq('(', $.duration, ')')
))),
'{',
field('child', $.behavior_node),
'}'
),
// Decorator node: retry/timeout/etc { child }
decorator_node: $ => seq(
field('decorator', $.decorator_keyword),
optional(field('params', $.decorator_params)),
@@ -291,7 +403,6 @@ module.exports = grammar({
),
decorator_keyword: $ => choice(
'repeat',
'invert',
'retry',
'timeout',
@@ -330,16 +441,20 @@ module.exports = grammar({
subtree_node: $ => seq('include', $.path),
// Institution declaration
institution: $ => seq(
institution_declaration: $ => seq(
'institution',
field('name', $.identifier),
$.block
field('body', $.block)
),
// Relationship declaration
relationship: $ => seq(
relationship_declaration: $ => seq(
'relationship',
field('name', $.identifier),
field('body', $.relationship_body)
),
relationship_body: $ => seq(
'{',
repeat1($.participant),
repeat($.field),
@@ -354,22 +469,42 @@ module.exports = grammar({
),
// Location declaration
location: $ => seq(
location_declaration: $ => seq(
'location',
field('name', $.identifier),
$.block
field('body', $.block)
),
// Species declaration
species: $ => seq(
species_declaration: $ => seq(
'species',
field('name', $.identifier),
field('body', $.species_body)
),
species_body: $ => seq(
'{',
repeat($.include),
repeat($.field),
repeat($.species_field),
'}'
),
species_field: $ => choice(
// Field with range: name: min..max
seq(
field('name', $.identifier),
':',
field('value', $.range)
),
// Field with single value: name: value
seq(
field('name', $.identifier),
':',
field('value', choice($.integer, $.float, $.boolean, $.string))
),
$.prose_block
),
// Enum declaration
enum_declaration: $ => seq(
'enum',
@@ -382,8 +517,7 @@ module.exports = grammar({
// Concept declaration - base type with no structure
concept_declaration: $ => seq(
'concept',
field('name', $.identifier),
';'
field('name', $.identifier)
),
// Sub-concept declaration - enum or record subtype with dot notation
@@ -403,13 +537,13 @@ module.exports = grammar({
// Enum form: Variant1, Variant2, Variant3
sub_concept_enum_body: $ => commaSep1($.identifier),
// Record form: field_name: Type, field_name: any
// Record form: field_name: value, field_name: value
sub_concept_record_body: $ => commaSep1($.sub_concept_field),
sub_concept_field: $ => seq(
field('name', $.identifier),
':',
field('type', choice($.identifier, $.any_type))
field('value', choice($.integer, $.float, $.string, $.boolean))
),
any_type: $ => 'any',
@@ -432,25 +566,26 @@ module.exports = grammar({
'}'
),
// A condition on a sub_concept field
// Field condition: field_name: any OR field_name: Type is Value [or Type is Value]*
field_condition: $ => seq(
field('sub_concept', $.dotted_path),
field('name', $.identifier),
':',
field('condition', $.condition_expr)
field('condition', choice(
$.any_type,
$.is_condition
))
),
// Condition: either 'any' or 'Type is Value or Type is Value2'
condition_expr: $ => choice(
$.any_type,
$.is_condition
),
// Pattern: DottedPath is Ident [or DottedPath is Ident]*
// Is condition: Type is Value [or Type is Value]*
is_condition: $ => seq(
$.dotted_path,
$.is_value,
repeat(seq('or', $.is_value))
),
is_value: $ => seq(
field('field', $.identifier),
'is',
$.identifier,
repeat(seq('or', $.dotted_path, 'is', $.identifier))
field('value', $.identifier)
),
// Expressions (for conditions in life arcs)

View File

@@ -69,30 +69,30 @@
(string) @string
; Identifiers in different contexts
(character name: (identifier) @type.character)
(template name: (identifier) @type.template)
(life_arc name: (identifier) @type.life_arc)
(schedule name: (identifier) @type.schedule)
(behavior name: (identifier) @type.behavior)
(institution name: (identifier) @type.institution)
(relationship name: (identifier) @type.relationship)
(location name: (identifier) @type.location)
(species name: (identifier) @type.species)
(character_declaration name: (identifier) @type.character)
(template_declaration name: (identifier) @type.template)
(life_arc_declaration name: (identifier) @type.life_arc)
(schedule_declaration name: (identifier) @type.schedule)
(behavior_declaration name: (identifier) @type.behavior)
(institution_declaration name: (identifier) @type.institution)
(relationship_declaration name: (identifier) @type.relationship)
(location_declaration name: (identifier) @type.location)
(species_declaration name: (identifier) @type.species)
(enum_declaration name: (identifier) @type.enum)
(arc_state name: (identifier) @type.state)
(state_block name: (identifier) @type.state)
(concept_declaration name: (identifier) @type.concept)
(sub_concept parent: (identifier) @type.concept)
(sub_concept name: (identifier) @type.sub_concept)
(concept_comparison name: (identifier) @type.concept_comparison)
(variant_pattern name: (identifier) @type.variant)
(template species: (identifier) @type.builtin)
(template_declaration species: (identifier) @type.builtin)
; Field names
(field name: (dotted_path) @property)
(sub_concept_field name: (identifier) @property)
; Species reference
(character species: (identifier) @type.builtin)
(character_declaration species: (identifier) @type.builtin)
; Paths and identifiers
(path) @namespace
@@ -148,7 +148,7 @@
(transition target: (identifier) @type.state)
; Schedule blocks
(schedule_block activity: (identifier) @function.activity)
(schedule_block name: (identifier) @function.schedule_block)
; Override operations
(override "@" @keyword.override)

View File

@@ -24,17 +24,17 @@
; Block structures that should indent their contents
[
(block)
(character)
(template)
(life_arc)
(arc_state)
(schedule)
(character_declaration)
(template_declaration)
(life_arc_declaration)
(state_block)
(schedule_declaration)
(schedule_block)
(behavior)
(institution)
(relationship)
(location)
(species)
(behavior_declaration)
(institution_declaration)
(relationship_declaration)
(location_declaration)
(species_declaration)
(enum_declaration)
(selector_node)
(sequence_node)
@@ -46,4 +46,3 @@
; Dedent after semicolon at top level
(use_declaration ";" @indent.end)
(concept_declaration ";" @indent.end)

View File

@@ -2,52 +2,52 @@
; Defines what symbols appear in the document outline
; Characters
(character
(character_declaration
name: (identifier) @name
) @symbol.character
; Templates
(template
(template_declaration
name: (identifier) @name
) @symbol.template
; Life arcs
(life_arc
(life_arc_declaration
name: (identifier) @name
) @symbol.life_arc
; Life arc states
(arc_state
(state_block
name: (identifier) @name
) @symbol.state
; Schedules
(schedule
(schedule_declaration
name: (identifier) @name
) @symbol.schedule
; Behaviors
(behavior
(behavior_declaration
name: (identifier) @name
) @symbol.behavior
; Institutions
(institution
(institution_declaration
name: (identifier) @name
) @symbol.institution
; Relationships
(relationship
(relationship_declaration
name: (identifier) @name
) @symbol.relationship
; Locations
(location
(location_declaration
name: (identifier) @name
) @symbol.location
; Species
(species
(species_declaration
name: (identifier) @name
) @symbol.species

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -10,9 +10,9 @@ character Alice {
(source_file
(declaration
(character
(character_declaration
name: (identifier)
body: (block
body: (character_body
(field
name: (dotted_path (identifier))
value: (value (integer)))))))
@@ -47,9 +47,9 @@ character Bob {
(source_file
(declaration
(character
(character_declaration
name: (identifier)
body: (block
body: (character_body
(field
(prose_block
marker: (prose_marker)

View File

@@ -1,29 +1,11 @@
==================
Simple Behavior
Basic behavior tree
==================
behavior SimpleBehavior {
walk_around
}
---
(source_file
(declaration
(behavior
name: (identifier)
root: (behavior_node
(action_node (identifier))))))
==================
Selector Behavior
==================
behavior SelectorBehavior {
? {
try_option_a
try_option_b
fallback
behavior BasicNeeds {
then {
CheckHunger
if(hungry) { EatFood }
}
}
@@ -31,23 +13,33 @@ behavior SelectorBehavior {
(source_file
(declaration
(behavior
(behavior_declaration
name: (identifier)
root: (behavior_node
(selector_node
(behavior_node (action_node (identifier)))
(behavior_node (action_node (identifier)))
(behavior_node (action_node (identifier))))))))
body: (behavior_body
root: (behavior_node
(sequence_node
(behavior_node
(action_node
(identifier)))
(behavior_node
(if_decorator_node
condition: (expression
(primary_expression
(path
(path_segments
(identifier)))))
child: (behavior_node
(action_node
(identifier)))))))))))
==================
Sequence Behavior
Behavior with selector
==================
behavior SequenceBehavior {
> {
check_energy
move_to_location
perform_action
behavior Combat {
choose {
if(has_weapon) { Attack }
Flee
}
}
@@ -55,25 +47,32 @@ behavior SequenceBehavior {
(source_file
(declaration
(behavior
(behavior_declaration
name: (identifier)
root: (behavior_node
(sequence_node
(behavior_node (action_node (identifier)))
(behavior_node (action_node (identifier)))
(behavior_node (action_node (identifier))))))))
body: (behavior_body
root: (behavior_node
(selector_node
(behavior_node
(if_decorator_node
condition: (expression
(primary_expression
(path
(path_segments
(identifier)))))
child: (behavior_node
(action_node
(identifier)))))
(behavior_node
(action_node
(identifier)))))))))
==================
Nested Behavior
Behavior with repeat
==================
behavior NestedBehavior {
> {
? {
check_condition_a
check_condition_b
}
perform_action
behavior Patrol {
repeat(5) {
WalkPath
}
}
@@ -81,100 +80,12 @@ behavior NestedBehavior {
(source_file
(declaration
(behavior
(behavior_declaration
name: (identifier)
root: (behavior_node
(sequence_node
(behavior_node
(selector_node
(behavior_node (action_node (identifier)))
(behavior_node (action_node (identifier)))))
(behavior_node (action_node (identifier))))))))
==================
Repeat Behavior
==================
behavior RepeatBehavior {
* {
patrol
}
}
---
(source_file
(declaration
(behavior
name: (identifier)
root: (behavior_node
(repeat_node
(behavior_node (action_node (identifier))))))))
==================
Behavior with Subtree
==================
behavior WithSubtree {
> {
@helpers::check_preconditions
main_action
}
}
---
(source_file
(declaration
(behavior
name: (identifier)
root: (behavior_node
(sequence_node
(behavior_node
(subtree_node
(path
(path_segments
(identifier)
(identifier)))))
(behavior_node (action_node (identifier))))))))
==================
Complex Nested Behavior
==================
behavior ComplexBehavior {
? {
> {
check_threat
flee_to_safety
}
> {
check_resources
gather_resources
}
* {
idle
}
}
}
---
(source_file
(declaration
(behavior
name: (identifier)
root: (behavior_node
(selector_node
(behavior_node
(sequence_node
(behavior_node (action_node (identifier)))
(behavior_node (action_node (identifier)))))
(behavior_node
(sequence_node
(behavior_node (action_node (identifier)))
(behavior_node (action_node (identifier)))))
(behavior_node
(repeat_node
(behavior_node (action_node (identifier))))))))))
body: (behavior_body
root: (behavior_node
(repeat_node
params: (integer)
child: (behavior_node
(action_node
(identifier)))))))))

View File

@@ -67,11 +67,11 @@ character Alice: Human from Curious {
(source_file
(declaration
(character
(character_declaration
name: (identifier)
species: (identifier)
template: (template_clause (identifier))
body: (block
body: (character_body
(field
name: (dotted_path (identifier))
value: (value (integer)))
@@ -110,16 +110,17 @@ template BaseCharacter strict {
(source_file
(declaration
(template
(template_declaration
name: (identifier)
(include (identifier))
(include (identifier))
(field
name: (dotted_path (identifier))
value: (value (integer)))
(field
name: (dotted_path (identifier))
value: (value (string))))))
body: (template_body
(include (identifier))
(include (identifier))
(field
name: (dotted_path (identifier))
value: (value (integer)))
(field
name: (dotted_path (identifier))
value: (value (string)))))))
==================
Life arc with states and transitions
@@ -150,7 +151,7 @@ life_arc Journey {
(source_file
(declaration
(life_arc
(life_arc_declaration
name: (identifier)
(field
(prose_block
@@ -158,115 +159,32 @@ life_arc Journey {
tag: (identifier)
content: (prose_content)
end: (prose_marker)))
(arc_state
(state_block
name: (identifier)
(on_enter
(block
(field
name: (dotted_path (identifier))
value: (value (integer)))))
(transition
condition: (expression
(comparison
(expression (primary_expression (path (path_segments (identifier)))))
(expression (primary_expression (integer)))))
target: (identifier)))
(arc_state
body: (state_body
(on_enter
(block
(field
name: (dotted_path (identifier))
value: (value (integer)))))
(transition
condition: (expression
(comparison
(expression (primary_expression (path (path_segments (identifier)))))
(expression (primary_expression (integer)))))
target: (identifier))))
(state_block
name: (identifier)
(transition
condition: (expression
(comparison
(expression (primary_expression (path (path_segments (identifier)))))
(expression (primary_expression (boolean)))))
target: (identifier)))
(arc_state
name: (identifier)))))
==================
Schedule with time blocks
==================
schedule DailyRoutine {
08:00 -> 09:00: breakfast {
location: kitchen
}
09:00 -> 12:00: work {
duration: 3h
}
}
---
(source_file
(declaration
(schedule
name: (identifier)
(schedule_block
start: (time)
end: (time)
activity: (identifier)
(block
(field
name: (dotted_path (identifier))
value: (value (path (path_segments (identifier)))))))
(schedule_block
start: (time)
end: (time)
activity: (identifier)
(block
(field
name: (dotted_path (identifier))
value: (value (duration))))))))
==================
Behavior tree - all node types
==================
behavior GuardBehavior {
? {
patrol
> {
detect_threat
alert(priority: high, "Guard duty")
}
* {
watch
}
@base::behaviors::Idle
}
}
---
(source_file
(declaration
(behavior
name: (identifier)
root: (behavior_node
(selector_node
(behavior_node (action_node (identifier)))
(behavior_node
(sequence_node
(behavior_node (action_node (identifier)))
(behavior_node
(action_node
(identifier)
(action_param
(dotted_path (identifier))
(value (path (path_segments (identifier)))))
(action_param
(value (string)))))))
(behavior_node
(repeat_node
(behavior_node (action_node (identifier)))))
(behavior_node
(subtree_node
(path
(path_segments
(identifier)
(identifier)
(identifier))))))))))
body: (state_body
(transition
condition: (expression
(comparison
(expression (primary_expression (path (path_segments (identifier)))))
(expression (primary_expression (boolean)))))
target: (identifier))))
(state_block
name: (identifier)
body: (state_body)))))
==================
Institution
@@ -281,9 +199,9 @@ institution Wonderland {
(source_file
(declaration
(institution
(institution_declaration
name: (identifier)
(block
body: (block
(field
name: (dotted_path (identifier))
value: (value (string)))
@@ -291,44 +209,6 @@ institution Wonderland {
name: (dotted_path (identifier))
value: (value (string)))))))
==================
Relationship with participants
==================
relationship Friendship {
Alice {
bond_strength: 5
}
WhiteRabbit as friend {
trust: 0.8
}
Caterpillar
}
---
(source_file
(declaration
(relationship
name: (identifier)
(participant
(path (path_segments (identifier)))
(block
(field
name: (dotted_path (identifier))
value: (value (integer)))))
(participant
(path (path_segments (identifier)))
(identifier)
(block
(field
name: (dotted_path (identifier))
value: (value (float)))))
(participant
(path (path_segments (identifier)))))))
==================
Location
==================
@@ -342,9 +222,9 @@ location TeaParty {
(source_file
(declaration
(location
(location_declaration
name: (identifier)
(block
body: (block
(field
name: (dotted_path (identifier))
value: (value (string)))
@@ -367,15 +247,16 @@ species Cat {
(source_file
(declaration
(species
(species_declaration
name: (identifier)
(include (identifier))
(field
name: (dotted_path (identifier))
value: (value (boolean)))
(field
name: (dotted_path (identifier))
value: (value (integer))))))
body: (species_body
(include (identifier))
(species_field
name: (identifier)
value: (boolean))
(species_field
name: (identifier)
value: (integer))))))
==================
Enum declaration
@@ -413,9 +294,9 @@ character SpecialAlice {
(source_file
(declaration
(character
(character_declaration
name: (identifier)
body: (block
body: (character_body
(field
name: (dotted_path (identifier))
value: (value
@@ -452,49 +333,52 @@ life_arc ComplexLogic {
(source_file
(declaration
(life_arc
(life_arc_declaration
name: (identifier)
(arc_state
(state_block
name: (identifier)
(transition
condition: (expression
(and_expression
(expression
(comparison
(expression
(field_access
(expression (primary_expression))
(identifier)))
(expression (primary_expression (integer)))))
(expression
(comparison
(expression
(field_access
(expression (primary_expression))
(identifier)))
(expression (primary_expression (float)))))))
target: (identifier))
(transition
condition: (expression
(or_expression
(expression
(not_expression
(expression
(field_access
(expression (primary_expression))
(identifier)))))
(expression
(comparison
(expression
(field_access
(expression (primary_expression))
(identifier)))
(expression (primary_expression (boolean)))))))
target: (identifier)))
(arc_state
name: (identifier))
(arc_state
name: (identifier)))))
body: (state_body
(transition
condition: (expression
(and_expression
(expression
(comparison
(expression
(field_access
(expression (primary_expression))
(identifier)))
(expression (primary_expression (integer)))))
(expression
(comparison
(expression
(field_access
(expression (primary_expression))
(identifier)))
(expression (primary_expression (float)))))))
target: (identifier))
(transition
condition: (expression
(or_expression
(expression
(not_expression
(expression
(field_access
(expression (primary_expression))
(identifier)))))
(expression
(comparison
(expression
(field_access
(expression (primary_expression))
(identifier)))
(expression (primary_expression (boolean)))))))
target: (identifier))))
(state_block
name: (identifier)
body: (state_body))
(state_block
name: (identifier)
body: (state_body)))))
==================
List and object values
@@ -513,9 +397,9 @@ character DataRich {
(source_file
(declaration
(character
(character_declaration
name: (identifier)
body: (block
body: (character_body
(field
name: (dotted_path (identifier))
value: (value
@@ -560,9 +444,9 @@ character Nested {
(source_file
(declaration
(character
(character_declaration
name: (identifier)
body: (block
body: (character_body
(field
name: (dotted_path
(identifier)

View File

@@ -0,0 +1,53 @@
==================
Keywords should highlight
==================
use schema::types;
character Alice from Person {
age: 25
}
---
(source_file
(declaration
(use_declaration
(path_segments
(identifier)
(identifier))))
(declaration
(character_declaration
name: (identifier)
template: (template_clause
(identifier))
body: (character_body
(field
name: (dotted_path
(identifier))
value: (value
(integer)))))))
==================
Any type in sub-concept
==================
sub_concept Field.Type {
name: "text",
value: 42
}
---
(source_file
(declaration
(sub_concept
parent: (identifier)
name: (identifier)
body: (sub_concept_record_body
(sub_concept_field
name: (identifier)
value: (string))
(sub_concept_field
name: (identifier)
value: (integer))))))

View File

@@ -0,0 +1,106 @@
==================
Basic schedule
==================
schedule WorkDay {
block morning {
08:00 -> 12:00
action: Work
}
}
---
(source_file
(declaration
(schedule_declaration
name: (identifier)
body: (schedule_body
(schedule_block
name: (identifier)
time_range: (time_range
start: (time)
end: (time))
(block_field
name: (identifier)
value: (identifier)))))))
==================
Schedule with extends
==================
schedule BakerDay extends WorkDay {
block early_prep {
05:00 -> 08:00
}
}
---
(source_file
(declaration
(schedule_declaration
name: (identifier)
extends: (identifier)
body: (schedule_body
(schedule_block
name: (identifier)
time_range: (time_range
start: (time)
end: (time)))))))
==================
Schedule with override
==================
schedule CustomDay extends BaseDay {
override work {
06:00 -> 14:00
intensity: "high"
}
}
---
(source_file
(declaration
(schedule_declaration
name: (identifier)
extends: (identifier)
body: (schedule_body
(override_block
name: (identifier)
time_range: (time_range
start: (time)
end: (time))
(block_field
name: (identifier)
value: (string)))))))
==================
Schedule with recurrence
==================
schedule WeeklySchedule {
recurrence MarketDay on Saturday {
block market {
06:00 -> 14:00
}
}
}
---
(source_file
(declaration
(schedule_declaration
name: (identifier)
body: (schedule_body
(recurrence_block
name: (identifier)
day: (identifier)
(schedule_block
name: (identifier)
time_range: (time_range
start: (time)
end: (time))))))))

View File

@@ -2,7 +2,7 @@
Concept declaration
==================
concept Cup;
concept BakedGood
---
@@ -12,34 +12,13 @@ concept Cup;
name: (identifier))))
==================
Multiple concept declarations
Sub-concept enum declaration
==================
concept Cup;
concept Customer;
concept Vendor;
---
(source_file
(declaration
(concept_declaration
name: (identifier)))
(declaration
(concept_declaration
name: (identifier)))
(declaration
(concept_declaration
name: (identifier))))
==================
Sub-concept - enum form
==================
sub_concept Cup.Size {
Small,
Medium,
Large
sub_concept BakedGood.Category {
Bread,
Pastry,
Cake
}
---
@@ -55,14 +34,12 @@ sub_concept Cup.Size {
(identifier)))))
==================
Sub-concept - record form with any
Sub-concept record declaration with values
==================
sub_concept Vendor.Inventory {
Bread: any,
Pastries: any,
Cakes: any,
Cup: any
sub_concept BakedGood.Quality {
freshness: 1.0,
taste: 0.8
}
---
@@ -75,51 +52,18 @@ sub_concept Vendor.Inventory {
body: (sub_concept_record_body
(sub_concept_field
name: (identifier)
type: (any_type))
value: (float))
(sub_concept_field
name: (identifier)
type: (any_type))
(sub_concept_field
name: (identifier)
type: (any_type))
(sub_concept_field
name: (identifier)
type: (any_type))))))
value: (float))))))
==================
Sub-concept - record form with typed fields
Concept comparison
==================
sub_concept Vendor.Inventory {
Bread: any,
Pastries: Number
}
---
(source_file
(declaration
(sub_concept
parent: (identifier)
name: (identifier)
body: (sub_concept_record_body
(sub_concept_field
name: (identifier)
type: (any_type))
(sub_concept_field
name: (identifier)
type: (identifier))))))
==================
Concept comparison with any conditions
==================
concept_comparison NotInterested {
NotInterested: {
Cup.Size: any,
Cup.Type: any,
Cup.Color: any
}
concept_comparison SkillLevel {
Apprentice: { freshness: any },
Master: { freshness: Tier is Master }
}
---
@@ -131,230 +75,58 @@ concept_comparison NotInterested {
(variant_pattern
name: (identifier)
(field_condition
sub_concept: (dotted_path
(identifier)
(identifier))
condition: (condition_expr
(any_type)))
name: (identifier)
condition: (any_type)))
(variant_pattern
name: (identifier)
(field_condition
sub_concept: (dotted_path
(identifier)
(identifier))
condition: (condition_expr
(any_type)))
(field_condition
sub_concept: (dotted_path
(identifier)
(identifier))
condition: (condition_expr
(any_type)))))))
name: (identifier)
condition: (is_condition
(is_value
field: (identifier)
value: (identifier))))))))
==================
Concept comparison with is conditions
Species declaration
==================
concept_comparison Interest {
Interested: {
Cup.Type: Cup.Type is Glass or Cup.Type is Plastic,
Cup.Color: Cup.Color is Red
}
species Human {
age: 25
energy: 0.75
}
---
(source_file
(declaration
(concept_comparison
(species_declaration
name: (identifier)
(variant_pattern
name: (identifier)
(field_condition
sub_concept: (dotted_path
(identifier)
(identifier))
condition: (condition_expr
(is_condition
(dotted_path
(identifier)
(identifier))
(identifier)
(dotted_path
(identifier)
(identifier))
(identifier))))
(field_condition
sub_concept: (dotted_path
(identifier)
(identifier))
condition: (condition_expr
(is_condition
(dotted_path
(identifier)
(identifier))
(identifier))))))))
body: (species_body
(species_field
name: (identifier)
value: (integer))
(species_field
name: (identifier)
value: (float))))))
==================
Concept comparison with multiple variants
==================
concept_comparison FoodQuality {
Excellent: {
Food.Freshness: Food.Freshness is Fresh
},
Poor: {
Food.Freshness: Food.Freshness is Stale or Food.Freshness is Spoiled
}
}
---
(source_file
(declaration
(concept_comparison
name: (identifier)
(variant_pattern
name: (identifier)
(field_condition
sub_concept: (dotted_path
(identifier)
(identifier))
condition: (condition_expr
(is_condition
(dotted_path
(identifier)
(identifier))
(identifier)))))
(variant_pattern
name: (identifier)
(field_condition
sub_concept: (dotted_path
(identifier)
(identifier))
condition: (condition_expr
(is_condition
(dotted_path
(identifier)
(identifier))
(identifier)
(dotted_path
(identifier)
(identifier))
(identifier))))))))
==================
Template with species extension
Template with species base
==================
template Person: Human {
age: 0..100
name: ""
name: "Unknown"
}
---
(source_file
(declaration
(template
(template_declaration
name: (identifier)
species: (identifier)
(field
name: (dotted_path (identifier))
value: (value (range (integer) (integer))))
(field
name: (dotted_path (identifier))
value: (value (string))))))
==================
Full type system example
==================
concept Cup;
sub_concept Cup.Size {
Small,
Medium,
Large
}
sub_concept Cup.Type {
Ceramic,
Glass,
Plastic
}
concept_comparison CupPreference {
Preferred: {
Cup.Size: any,
Cup.Type: Cup.Type is Glass or Cup.Type is Ceramic
},
Avoided: {
Cup.Size: any,
Cup.Type: Cup.Type is Plastic
}
}
---
(source_file
(declaration
(concept_declaration
name: (identifier)))
(declaration
(sub_concept
parent: (identifier)
name: (identifier)
body: (sub_concept_enum_body
(identifier)
(identifier)
(identifier))))
(declaration
(sub_concept
parent: (identifier)
name: (identifier)
body: (sub_concept_enum_body
(identifier)
(identifier)
(identifier))))
(declaration
(concept_comparison
name: (identifier)
(variant_pattern
name: (identifier)
(field_condition
sub_concept: (dotted_path
(identifier)
body: (template_body
(field
name: (dotted_path
(identifier))
condition: (condition_expr
(any_type)))
(field_condition
sub_concept: (dotted_path
(identifier)
(identifier))
condition: (condition_expr
(is_condition
(dotted_path
(identifier)
(identifier))
(identifier)
(dotted_path
(identifier)
(identifier))
(identifier)))))
(variant_pattern
name: (identifier)
(field_condition
sub_concept: (dotted_path
(identifier)
(identifier))
condition: (condition_expr
(any_type)))
(field_condition
sub_concept: (dotted_path
(identifier)
(identifier))
condition: (condition_expr
(is_condition
(dotted_path
(identifier)
(identifier))
(identifier))))))))
value: (value
(string)))))))

Submodule zed-storybook/grammars/storybook deleted from 26bbef58d3