feat(lang): complete extends to modifies keyword migration

This commit completes the migration started in the previous commit,
updating all remaining files:

- Lexer: Changed token from Extends to Modifies
- Parser: Updated lalrpop grammar rules and AST field names
- AST: Renamed Schedule.extends field to modifies
- Grammar: Updated tree-sitter grammar.js
- Tree-sitter: Regenerated parser.c and node-types.json
- Examples: Updated baker-family work schedules
- Tests: Updated schedule composition tests and corpus
- Docs: Updated all reference documentation and tutorials
- Validation: Updated error messages and validation logic
- Package: Bumped version to 0.3.1 in all package manifests

All 554 tests pass.
This commit is contained in:
2026-02-16 22:55:04 +00:00
parent 2c898347ee
commit 47fafdc2bf
109 changed files with 5045 additions and 41939 deletions

View File

@@ -319,7 +319,7 @@ fn test_trait_out_of_range_negative() {
fn test_schedule_overlap_error() {
let schedule = Schedule {
name: "DailyRoutine".to_string(),
extends: None,
modifies: None,
fields: Vec::new(),
recurrences: Vec::new(),
blocks: vec![

View File

@@ -373,20 +373,20 @@ pub fn validate_institution_resource_links(
/// Validate schedule composition requirements
///
/// Checks:
/// 1. All blocks in extended schedules have names
/// 1. All blocks in modified schedules have names
/// 2. Override blocks reference valid block names (requires name resolution)
pub fn validate_schedule_composition(schedule: &Schedule, collector: &mut ErrorCollector) {
// If schedule extends another, all blocks must have names for override system
if schedule.extends.is_some() {
// If schedule modifies another, all blocks must have names for override system
if schedule.modifies.is_some() {
for block in &schedule.blocks {
if block.name.is_none() && !block.activity.is_empty() {
collector.add(ResolveError::ValidationError {
message: format!(
"Schedule '{}' extends another schedule but has unnamed blocks",
"Schedule '{}' modifies another schedule but has unnamed blocks",
schedule.name
),
help: Some(
"When a schedule extends another, all blocks must have names to support the override system. Use 'block name { ... }' syntax instead of 'time -> time : activity { ... }'.".to_string()
"When a schedule modifies another, all blocks must have names to support the override system. Use 'block name { ... }' syntax instead of 'time -> time : activity { ... }'.".to_string()
),
});
}

View File

@@ -249,7 +249,7 @@ pub struct Transition {
#[derive(Debug, Clone, PartialEq)]
pub struct Schedule {
pub name: String,
pub extends: Option<String>, // Base schedule to extend
pub modifies: Option<String>, // Base schedule to modify
pub blocks: Vec<ScheduleBlock>,
pub recurrences: Vec<RecurrencePattern>, // Recurring events
pub fields: Vec<Field>, // Documentation prose blocks, metadata

View File

@@ -87,8 +87,8 @@ pub enum Token {
Tree,
#[token("priority")]
Priority,
#[token("extends")]
Extends,
#[token("modifies")]
Modifies,
#[token("override")]
Override,
#[token("recurrence")]

View File

@@ -386,16 +386,16 @@ Schedule: Schedule = {
// Simple schedule: schedule Name { ... }
<start:@L> "schedule" <name:Ident> "{" <body:ScheduleBody> "}" <end:@R> => Schedule {
name,
extends: None,
modifies: None,
fields: body.0,
blocks: body.1,
recurrences: body.2,
span: Span::new(start, end),
},
// Extending schedule: schedule Name extends Base { ... }
<start:@L> "schedule" <name:Ident> "extends" <base:Ident> "{" <body:ScheduleBody> "}" <end:@R> => Schedule {
// Modifying schedule: schedule Name modifies Base { ... }
<start:@L> "schedule" <name:Ident> "modifies" <base:Ident> "{" <body:ScheduleBody> "}" <end:@R> => Schedule {
name,
extends: Some(base),
modifies: Some(base),
fields: body.0,
blocks: body.1,
recurrences: body.2,
@@ -1003,7 +1003,7 @@ extern {
"schedules" => Token::Schedules,
"tree" => Token::Tree,
"priority" => Token::Priority,
"extends" => Token::Extends,
"modifies" => Token::Modifies,
"override" => Token::Override,
"recurrence" => Token::Recurrence,
"season" => Token::Season,

View File

@@ -29,7 +29,7 @@ schedule DailyRoutine {
match &file.declarations[0] {
| Declaration::Schedule(s) => {
assert_eq!(s.name, "DailyRoutine");
assert!(s.extends.is_none());
assert!(s.modifies.is_none());
assert_eq!(s.blocks.len(), 2);
assert_eq!(s.recurrences.len(), 0);
@@ -43,9 +43,9 @@ schedule DailyRoutine {
}
#[test]
fn test_schedule_extends() {
fn test_schedule_modifies() {
let input = r#"
schedule BakerSchedule extends BaseWorkday {
schedule BakerSchedule modifies BaseWorkday {
block work { 05:00 -> 13:00, action: BakingWork }
}
"#;
@@ -60,7 +60,7 @@ schedule BakerSchedule extends BaseWorkday {
match &file.declarations[0] {
| Declaration::Schedule(s) => {
assert_eq!(s.name, "BakerSchedule");
assert_eq!(s.extends, Some("BaseWorkday".to_string()));
assert_eq!(s.modifies, Some("BaseWorkday".to_string()));
},
| _ => panic!("Expected Schedule declaration"),
}
@@ -104,7 +104,7 @@ schedule WorkSchedule {
#[test]
fn test_override_block() {
let input = r#"
schedule BakerSchedule extends BaseWorkday {
schedule BakerSchedule modifies BaseWorkday {
override work { 05:00 -> 13:00, action: BakingWork }
block prep { 03:00 -> 05:00, action: PrepBread }
}
@@ -208,7 +208,7 @@ schedule WorkSchedule {
#[test]
fn test_mixed_blocks_and_recurrences() {
let input = r#"
schedule CompleteSchedule extends BaseSchedule {
schedule CompleteSchedule modifies BaseSchedule {
block morning { 06:00 -> 12:00, action: MorningRoutine }
recurrence Weekend on Spiritday {
@@ -229,7 +229,7 @@ schedule CompleteSchedule extends BaseSchedule {
match &file.declarations[0] {
| Declaration::Schedule(s) => {
assert_eq!(s.name, "CompleteSchedule");
assert_eq!(s.extends, Some("BaseSchedule".to_string()));
assert_eq!(s.modifies, Some("BaseSchedule".to_string()));
assert_eq!(s.blocks.len(), 2); // morning and override evening
assert_eq!(s.recurrences.len(), 1); // Weekend