feat(lang): rename schedule keyword from extends to modifies
Changed the schedule composition keyword from "extends" to "modifies" to better reflect the semantic meaning of schedule inheritance. When a schedule modifies another, it inherits base blocks and can override them by name or add new blocks. This is a breaking change for all existing Storybook files that use schedule composition. The migration is a simple find-and-replace: schedule X extends Y → schedule X modifies Y Changes include: - Grammar: Updated tree-sitter grammar and lexer token - Parser: Updated lalrpop parser and AST field names - Documentation: Updated all reference docs, tutorials, and specs - Examples: Updated baker-family example schedules - Tests: Updated all test cases and corpus files - Testing: Added type system keywords to prop_tests exclusion list - Tooling: Added xtask for workspace cleanup - Version: Bumped to v0.3.1 (skipping v0.3.0) - Spec: Created SBIR v0.3.1 spec documenting the change BREAKING CHANGE: The "extends" keyword for schedules has been replaced with "modifies". Update all schedule declarations.
This commit is contained in:
2
.cargo/config.toml
Normal file
2
.cargo/config.toml
Normal file
@@ -0,0 +1,2 @@
|
||||
[alias]
|
||||
xtask = "run --package xtask --"
|
||||
1257
docs/SBIR-v0.3.1-SPEC.md
Normal file
1257
docs/SBIR-v0.3.1-SPEC.md
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
// auto-generated: "lalrpop 0.21.0"
|
||||
// sha3: 743aa9a8d35318fbf553927304781732c69eaf9c6e511c3951d24e24d2d8d1e8
|
||||
// sha3: b4edee3687f9fcc3202af2ee2aea58c7d41ed2aa394ce46e045436e20a36760d
|
||||
use crate::syntax::{
|
||||
ast::*,
|
||||
lexer::Token,
|
||||
@@ -2761,7 +2761,7 @@ mod __parse__File {
|
||||
r###""schedules""###,
|
||||
r###""tree""###,
|
||||
r###""priority""###,
|
||||
r###""extends""###,
|
||||
r###""modifies""###,
|
||||
r###""override""###,
|
||||
r###""recurrence""###,
|
||||
r###""season""###,
|
||||
@@ -2976,7 +2976,7 @@ mod __parse__File {
|
||||
Token::Schedules if true => Some(36),
|
||||
Token::Tree if true => Some(37),
|
||||
Token::Priority if true => Some(38),
|
||||
Token::Extends if true => Some(39),
|
||||
Token::Modifies if true => Some(39),
|
||||
Token::Override if true => Some(40),
|
||||
Token::Recurrence if true => Some(41),
|
||||
Token::Season if true => Some(42),
|
||||
@@ -12251,7 +12251,7 @@ mod __parse__File {
|
||||
_: core::marker::PhantomData<()>,
|
||||
) -> (usize, usize)
|
||||
{
|
||||
// Schedule = "schedule", Ident, "extends", Ident, "{", ScheduleBody, "}" => ActionFn(434);
|
||||
// Schedule = "schedule", Ident, "modifies", Ident, "{", ScheduleBody, "}" => ActionFn(434);
|
||||
assert!(__symbols.len() >= 7);
|
||||
let __sym6 = __pop_Variant0(__symbols);
|
||||
let __sym5 = __pop_Variant75(__symbols);
|
||||
@@ -14856,7 +14856,7 @@ fn __action77(
|
||||
) -> Schedule {
|
||||
Schedule {
|
||||
name,
|
||||
extends: None,
|
||||
modifies: None,
|
||||
fields: body.0,
|
||||
blocks: body.1,
|
||||
recurrences: body.2,
|
||||
@@ -14886,7 +14886,7 @@ fn __action78(
|
||||
) -> Schedule {
|
||||
Schedule {
|
||||
name,
|
||||
extends: Some(base),
|
||||
modifies: Some(base),
|
||||
fields: body.0,
|
||||
blocks: body.1,
|
||||
recurrences: body.2,
|
||||
|
||||
@@ -57,7 +57,12 @@ fn valid_ident() -> impl Strategy<Value = String> {
|
||||
"timeout" |
|
||||
"cooldown" |
|
||||
"succeed_always" |
|
||||
"fail_always"
|
||||
"fail_always" |
|
||||
// Type system keywords (v0.3.0)
|
||||
"concept" |
|
||||
"sub_concept" |
|
||||
"concept_comparison" |
|
||||
"any"
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
8
xtask/Cargo.toml
Normal file
8
xtask/Cargo.toml
Normal file
@@ -0,0 +1,8 @@
|
||||
[package]
|
||||
name = "xtask"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
142
xtask/src/main.rs
Normal file
142
xtask/src/main.rs
Normal file
@@ -0,0 +1,142 @@
|
||||
use std::{
|
||||
env,
|
||||
path::PathBuf,
|
||||
process::Command,
|
||||
};
|
||||
|
||||
use anyhow::{
|
||||
Context,
|
||||
Result,
|
||||
};
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let task = env::args().nth(1);
|
||||
match task.as_deref() {
|
||||
| Some("clean") => clean()?,
|
||||
| Some(task) => {
|
||||
eprintln!("Unknown task: {}", task);
|
||||
print_help();
|
||||
std::process::exit(1);
|
||||
},
|
||||
| None => {
|
||||
print_help();
|
||||
std::process::exit(1);
|
||||
},
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn print_help() {
|
||||
eprintln!(
|
||||
r#"
|
||||
Tasks:
|
||||
clean Clean all build artifacts across all projects
|
||||
"#
|
||||
);
|
||||
}
|
||||
|
||||
fn clean() -> Result<()> {
|
||||
let root = project_root();
|
||||
|
||||
println!("🧹 Cleaning Storybook workspace...\n");
|
||||
|
||||
// Clean Rust projects
|
||||
println!(" Cleaning Rust artifacts...");
|
||||
run_command(&["cargo", "clean"], &root)?;
|
||||
|
||||
// Clean tree-sitter-storybook
|
||||
let tree_sitter_dir = root.join("tree-sitter-storybook");
|
||||
if tree_sitter_dir.exists() {
|
||||
println!(" Cleaning tree-sitter-storybook...");
|
||||
|
||||
// Remove node_modules
|
||||
let node_modules = tree_sitter_dir.join("node_modules");
|
||||
if node_modules.exists() {
|
||||
println!(" Removing node_modules/");
|
||||
std::fs::remove_dir_all(&node_modules)
|
||||
.context("Failed to remove tree-sitter-storybook/node_modules")?;
|
||||
}
|
||||
|
||||
// Remove target directory
|
||||
let target = tree_sitter_dir.join("target");
|
||||
if target.exists() {
|
||||
println!(" Removing target/");
|
||||
std::fs::remove_dir_all(&target)
|
||||
.context("Failed to remove tree-sitter-storybook/target")?;
|
||||
}
|
||||
|
||||
// Remove Cargo.lock
|
||||
let cargo_lock = tree_sitter_dir.join("Cargo.lock");
|
||||
if cargo_lock.exists() {
|
||||
println!(" Removing Cargo.lock");
|
||||
std::fs::remove_file(&cargo_lock)
|
||||
.context("Failed to remove tree-sitter-storybook/Cargo.lock")?;
|
||||
}
|
||||
|
||||
// Remove build artifacts
|
||||
let build_dir = tree_sitter_dir.join("build");
|
||||
if build_dir.exists() {
|
||||
println!(" Removing build/");
|
||||
std::fs::remove_dir_all(&build_dir)
|
||||
.context("Failed to remove tree-sitter-storybook/build")?;
|
||||
}
|
||||
}
|
||||
|
||||
// Clean zed-storybook
|
||||
let zed_dir = root.join("zed-storybook");
|
||||
if zed_dir.exists() {
|
||||
println!(" Cleaning zed-storybook...");
|
||||
|
||||
// Remove grammars directory (build artifact)
|
||||
let grammars = zed_dir.join("grammars");
|
||||
if grammars.exists() {
|
||||
println!(" Removing grammars/");
|
||||
std::fs::remove_dir_all(&grammars)
|
||||
.context("Failed to remove zed-storybook/grammars")?;
|
||||
}
|
||||
|
||||
// Remove extension.wasm
|
||||
let wasm = zed_dir.join("extension.wasm");
|
||||
if wasm.exists() {
|
||||
println!(" Removing extension.wasm");
|
||||
std::fs::remove_file(&wasm).context("Failed to remove zed-storybook/extension.wasm")?;
|
||||
}
|
||||
}
|
||||
|
||||
// Clean mdbook artifacts
|
||||
let docs_dir = root.join("docs");
|
||||
if docs_dir.exists() {
|
||||
println!(" Cleaning mdbook artifacts...");
|
||||
|
||||
let book_dir = docs_dir.join("book");
|
||||
if book_dir.exists() {
|
||||
println!(" Removing docs/book/");
|
||||
std::fs::remove_dir_all(&book_dir).context("Failed to remove docs/book")?;
|
||||
}
|
||||
}
|
||||
|
||||
println!("\n✨ Clean complete!");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run_command(cmd: &[&str], cwd: &PathBuf) -> Result<()> {
|
||||
let mut command = Command::new(cmd[0]);
|
||||
command.args(&cmd[1..]).current_dir(cwd);
|
||||
|
||||
let status = command
|
||||
.status()
|
||||
.with_context(|| format!("Failed to run command: {:?}", cmd))?;
|
||||
|
||||
if !status.success() {
|
||||
anyhow::bail!("Command failed: {:?}", cmd);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn project_root() -> PathBuf {
|
||||
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
||||
.parent()
|
||||
.unwrap()
|
||||
.to_path_buf()
|
||||
}
|
||||
Reference in New Issue
Block a user