### Example Calc Program Source: https://salsa-rs.github.io/salsa/tutorial/structure.html This is a sample program written in the calc language, demonstrating variable assignments, arithmetic operations, and printing. ```calc x = 5 y = 10 z = x + y * 3 print z ``` -------------------------------- ### Example Unit Test with expect-test Source: https://salsa-rs.github.io/salsa/tutorial/debug.html An example unit test using the `parse_string` function and the `expect-test` crate to assert the output of a parser. ```rust #![allow(unused)] fn main() { #[test] fn parse_print() { let actual = parse_string("print 1 + 2"); let expected = expect_test::expect![[r#"( Program { [salsa id]: Id(800), statements: [ Statement { span: Span { [salsa id]: Id(404), start: 0, end: 11, }, data: Print( Expression { span: Span { [salsa id]: Id(403), start: 6, end: 11, }, data: Op( Expression { span: Span { [salsa id]: Id(400), start: 6, end: 7, }, data: Number( 1.0, ), }, Add, Expression { span: Span { [salsa id]: Id(402), start: 10, end: 11, }, data: Number( 2.0, ), }, ), }, ), }, ], }, [], )"#]]; expected.assert_eq(&actual); } } ``` -------------------------------- ### Example of Database Revision and Lifetime Management Source: https://salsa-rs.github.io/salsa/plumbing/db_lifetime.html Demonstrates a scenario where a tracked struct created in one revision might be accessed in a subsequent revision after the original database reference has expired. This highlights the need for raw pointers. ```rust #![allow(unused)] fn main() { let mut db = MyDatabase::default(); let input = MyInput::new(&db, ...); // Revision 1: let result1 = tracked_fn(&db, input); // Revision 2: input.set_field(&mut db).to (...); let result2 = tracked_fn(&db, input); } ``` -------------------------------- ### Lazy Input Database Implementation Source: https://salsa-rs.github.io/salsa/common_patterns/on_demand_inputs.html Implements a 'LazyInputDatabase' that uses a cache ('DashMap') and a file watcher to provide file contents on demand. It includes setup for storage, logs, files cache, and a debouncer for file system events. ```rust #[salsa::db] #[derive(Clone)] struct LazyInputDatabase { storage: Storage, logs: Arc>>, files: DashMap, file_watcher: Arc>>, } impl LazyInputDatabase { fn new(tx: Sender) -> Self { let logs: Arc>> = Default::default(); Self { storage: Storage::new(Some(Box::new({ let logs = logs.clone(); move |event| { // don't log boring events if let salsa::EventKind::WillExecute { .. } = event.kind { logs.lock().unwrap().push(format!("{event:?}")); } } }))), logs, files: DashMap::new(), file_watcher: Arc::new(Mutex::new( new_debouncer(Duration::from_secs(1), move |events| { tx.send(events).unwrap() }) .unwrap(), )), } } } ``` -------------------------------- ### Main Function for Lazy Input Example Source: https://salsa-rs.github.io/salsa/print.html This snippet shows the main driving loop that recompiles code upon file changes. It sets up a channel for file events, initializes the Salsa database, and processes file changes to update inputs, triggering recompilation. ```rust fn main() -> Result<()> { // Create the channel to receive file change events. let (tx, rx) = unbounded(); let mut db = LazyInputDatabase::new(tx); let initial_file_path = std::env::args_os() .nth(1) .ok_or_else(|| eyre!("Usage: ./lazy-input "))?; // Create the initial input using the input method so that changes to it // will be watched like the other files. let initial = db.input(initial_file_path.into())?; loop { // Compile the code starting at the provided input, this will read other // needed files using the on-demand mechanism. let sum = compile(&db, initial); let diagnostics = compile::accumulated::(&db, initial); if diagnostics.is_empty() { println!("Sum is: {sum}"); } else { for diagnostic in diagnostics { println!("{}", diagnostic.0); } } for log in db.logs.lock().unwrap().drain(..) { eprintln!("{log}"); } // Wait for file change events, the output can't change unless the // inputs change. for event in rx.recv()?.unwrap() { let path = event.path.canonicalize().wrap_err_with(|| { format!("Failed to canonicalize path {}", event.path.display()) })?; let file = match db.files.get(&path) { Some(file) => *file, None => continue, }; // `path` has changed, so read it and update the contents to match. // This creates a new revision and causes the incremental algorithm // to kick in, just like any other update to a salsa input. let contents = std::fs::read_to_string(path) .wrap_err_with(|| format!("Failed to read file {}", event.path.display()))?; file.set_contents(&mut db).to(contents); } } } ``` -------------------------------- ### Tracked Struct Lifetime Example Source: https://salsa-rs.github.io/salsa/print.html This example demonstrates a scenario where tracked structs created in one revision might need to be accessed in a subsequent revision, even after the original database reference has expired. This highlights the need for raw pointers to avoid stacked borrow violations. ```rust #![allow(unused)] fn main() { let mut db = MyDatabase::default(); let input = MyInput::new(&db, ...); // Revision 1: let result1 = tracked_fn(&db, input); // Revision 2: input.set_field(&mut db).to(...); let result2 = tracked_fn(&db, input); } ``` -------------------------------- ### Define a Salsa Input Struct Source: https://salsa-rs.github.io/salsa/tutorial/ir.html Define a basic input struct for your Salsa program. Inputs are the starting point for all computations and can be updated. ```rust #![allow(unused)] fn main() { #[salsa::input(debug)] pub struct SourceProgram { #[returns(ref)] pub text: String, } } ``` -------------------------------- ### Create a New Salsa Input Instance Source: https://salsa-rs.github.io/salsa/overview.html Demonstrates how to create a new instance of a Salsa input struct (`ProgramFile`) using its `new` method, requiring a database reference and field values. ```rust #![allow(unused)] fn main() { let file: ProgramFile = ProgramFile::new( &db, PathBuf::from("some_path.txt"), String::from("fn foo() { } নিতে"), ); } ``` -------------------------------- ### Unit Test Harness for Parsers Source: https://salsa-rs.github.io/salsa/tutorial/debug.html A helper function to create a database, set source text, and parse the result for unit testing. It returns formatted statements and diagnostics. ```rust #![allow(unused)] fn main() { /// Create a new database with the given source text and parse the result. /// Returns the statements and the diagnostics generated. #[cfg(test)] fn parse_string(source_text: &str) -> String { use salsa::Database; use crate::db::CalcDatabaseImpl; CalcDatabaseImpl::default().attach(|db| { // Create the source program let source_program = SourceProgram::new(db, source_text.to_string()); // Invoke the parser let statements = parse_statements(db, source_program); // Read out any diagnostics let accumulated = parse_statements::accumulated::(db, source_program); // Format the result as a string and return it format!("{:#?}", (statements, accumulated)) }) } } ``` -------------------------------- ### Cancel Other Workers and Get Mutable Access Source: https://salsa-rs.github.io/salsa/plumbing/database_and_runtime.html This function sets a cancellation flag, waits for other workers to complete, acquires mutable access to the database, resets the cancellation flag, and potentially advances the revision. It should be paired with a call to `reset_cancellation_flag` and can deadlock if called within a query computation or if there's only one worker with two handles to the same database. ```rust #![allow(unused)] fn main() { /// Sets cancellation flag and blocks until all other workers with access /// to this storage have completed. /// /// This could deadlock if there is a single worker with two handles to the /// same database! /// /// Needs to be paired with a call to `reset_cancellation_flag`. fn cancel_others(&mut self) -> &mut Zalsa { debug_assert!( self.zalsa_local .try_with_query_stack(|stack| stack.is_empty()) == Some(true), "attempted to cancel within query computation, this is a deadlock" ); self.handle.zalsa_impl.runtime().set_cancellation_flag(); self.handle .zalsa_impl .event(&|| Event::new(EventKind::DidSetCancellationFlag)); let mut clones = self.handle.coordinate.clones.lock(); while *clones != 1 { clones = self.handle.coordinate.cvar.wait(clones); } // The ref count on the `Arc` should now be 1 let zalsa = Arc::get_mut(&mut self.handle.zalsa_impl).unwrap(); // cancellation is done, so reset the flag zalsa.runtime_mut().reset_cancellation_flag(); // Advance the epoch only after cancelled workers have dropped their handles. Otherwise, // a worker unwinding from cancellation could insert a provisional memo with the new epoch. let overflow = zalsa.runtime_mut().bump_cancellation_count(); if overflow { zalsa.new_revision(); } zalsa } } ``` -------------------------------- ### Create a Salsa Input Instance Source: https://salsa-rs.github.io/salsa/tutorial/ir.html Instantiate a Salsa input struct by calling its `new` method with a database reference and field values. ```rust let source = SourceProgram::new(&db, "print 11 + 11".to_string()); ``` -------------------------------- ### Enable Fixed-Point Iteration for Query Cycles Source: https://salsa-rs.github.io/salsa/cycles.html Use `cycle_fn` and `cycle_initial` arguments in `salsa::tracked` to enable fixed-point iteration for a query. This is necessary for queries that might be involved in a cycle and require a recovery mechanism. ```rust #![allow(unused)] fn main() { #[salsa::tracked(cycle_fn=cycle_fn, cycle_initial=cycle_initial)] fn query(db: &dyn salsa::Database) -> u32 { // ... } fn cycle_fn(_db: &dyn KnobsDatabase, _id: salsa::Id, _last_provisional_value: &u32, value: u32, _count: u32) -> u32 { value } fn cycle_initial(_db: &dyn KnobsDatabase, _id: salsa::Id) -> u32 { 0 } } ``` -------------------------------- ### Demonstrate Interned Struct Equality Source: https://salsa-rs.github.io/salsa/print.html Shows that creating `FunctionId` with the same string input results in the same `FunctionId` value, demonstrating interning. ```rust let f1 = FunctionId::new(&db, "my_string".to_string()); let f2 = FunctionId::new(&db, "my_string".to_string()); assert_eq!(f1, f2); ``` -------------------------------- ### Calc Language Program Source: https://salsa-rs.github.io/salsa/tutorial.html A simple program written in the 'calc' language, demonstrating function definitions and calls for calculating areas and basic arithmetic. ```rust fn area_rectangle(w, h) = w * h fn area_circle(r) = 3.14 * r * r print area_rectangle(3, 4) print area_circle(1) print 11 * 2 ``` -------------------------------- ### Create and Compare Interned Structs Source: https://salsa-rs.github.io/salsa/overview.html Create interned structs using `Word::new(db, value)`. When two interned structs have identical field values, they are guaranteed to have the same ID, enabling efficient equality comparisons. ```rust #![allow(unused)] fn main() { let w1 = Word::new(db, "foo".to_string()); let w2 = Word::new(db, "bar".to_string()); let w3 = Word::new(db, "foo".to_string()); } ``` -------------------------------- ### Salsa Lazy Input Database Implementation Source: https://salsa-rs.github.io/salsa/print.html Implements the `LazyInputDatabase` struct which includes storage, logs, a file cache (`DashMap`), and a file watcher. The `new` function initializes the database with a sender for debounce events. ```rust #[salsa::db] #[derive(Clone)] struct LazyInputDatabase { storage: Storage, logs: Arc> >, files: DashMap, file_watcher: Arc> >, } impl LazyInputDatabase { fn new(tx: Sender) -> Self { let logs: Arc>> = Default::default(); Self { storage: Storage::new(Some(Box::new({ let logs = logs.clone(); move |event| { // don't log boring events if let salsa::EventKind::WillExecute { .. } = event.kind { logs.lock().unwrap().push(format!("{event:?}")); } } }))), logs, files: DashMap::new(), file_watcher: Arc::new(Mutex::new( new_debouncer(Duration::from_secs(1), move |events| { tx.send(events).unwrap() }) .unwrap(), )), } } } ``` -------------------------------- ### Implementing On-Demand File Input Retrieval Source: https://salsa-rs.github.io/salsa/common_patterns/on_demand_inputs.html Implements the 'input' method for LazyInputDatabase. It caches file contents and sets up file system watching to handle updates lazily. If a file is not in the cache, it's read from disk, cached, and its path is added to the file watcher. ```rust #[salsa::db] impl Db for LazyInputDatabase { fn input(&self, path: PathBuf) -> Result { let path = path .canonicalize() .wrap_err_with(|| format!("Failed to read {}", path.display()))?; Ok(match self.files.entry(path.clone()) { // If the file already exists in our cache then just return it. Entry::Occupied(entry) => *entry.get(), // If we haven't read this file yet set up the watch, read the // contents, store it in the cache, and return it. Entry::Vacant(entry) => { // Set up the watch before reading the contents to try to avoid // race conditions. let watcher = &mut *self.file_watcher.lock().unwrap(); watcher .watcher() .watch(&path, RecursiveMode::NonRecursive) .unwrap(); let contents = std::fs::read_to_string(&path) .wrap_err_with(|| format!("Failed to read {}", path.display()))?; *entry.insert(File::new(self, path, contents)) } }) } } ``` -------------------------------- ### Program Source Structure Source: https://salsa-rs.github.io/salsa/tutorial/structure.html Defines the basic structure for representing a program source as a string. ```Rust #![allow(unused)] fn main() { struct ProgramSource { text: String } } ``` -------------------------------- ### Basic Incremental Recomputation Loop Source: https://salsa-rs.github.io/salsa/overview.html Illustrates the fundamental pattern of an incremental recomputation loop where input is modified and the program is re-invoked. ```rust #![allow(unused)] fn main() { let mut input = ...; loop { let output = your_program(&input); modify(&mut input); } } ``` -------------------------------- ### Driving Loop for File Changes Source: https://salsa-rs.github.io/salsa/common_patterns/on_demand_inputs.html Implements a loop that recompiles code when a file changes. It uses a channel to receive file change events and updates Salsa inputs accordingly. Use logs to verify that only relevant queries are re-evaluated. ```rust __ fn main() -> Result<()> { // Create the channel to receive file change events. let (tx, rx) = unbounded(); let mut db = LazyInputDatabase::new(tx); let initial_file_path = std::env::args_os() .nth(1) .ok_or_else(|| eyre!("Usage: ./lazy-input "))?; // Create the initial input using the input method so that changes to it // will be watched like the other files. let initial = db.input(initial_file_path.into())?; loop { // Compile the code starting at the provided input, this will read other // needed files using the on-demand mechanism. let sum = compile(&db, initial); let diagnostics = compile::accumulated::(&db, initial); if diagnostics.is_empty() { println!("Sum is: {sum}"); } else { for diagnostic in diagnostics { println!("{}", diagnostic.0); } } for log in db.logs.lock().unwrap().drain(..) { eprintln!("{log}"); } // Wait for file change events, the output can't change unless the // inputs change. for event in rx.recv()?.unwrap() { let path = event.path.canonicalize().wrap_err_with(|| { format!("Failed to canonicalize path {}", event.path.display()) })?; let file = match db.files.get(&path) { Some(file) => *file, None => continue, }; // `path` has changed, so read it and update the contents to match. // This creates a new revision and causes the incremental algorithm // to kick in, just like any other update to a salsa input. let contents = std::fs::read_to_string(path) .wrap_err_with(|| format!("Failed to read file {}", event.path.display()))?; file.set_contents(&mut db).to(contents); } } } ``` -------------------------------- ### Define Tracked Functions for Parsing Source: https://salsa-rs.github.io/salsa/print.html Defines `parse_module` and `module_text` as tracked functions. `module_text` is marked with `returns(ref)` for potential optimization. It panics if the text is not set. ```rust #![allow(unused)] fn main() { #[salsa::tracked] fn parse_module(db: &dyn Db, module: Module) -> Ast { let module_text: &String = module_text(db, module); Ast::parse_text(module_text) } #[salsa::tracked(returns(ref))] fn module_text(db: &dyn Db, module: Module) -> String { panic!("text for module `{module:?}` not set") } } ``` -------------------------------- ### Define Input Field Returning a Reference Source: https://salsa-rs.github.io/salsa/overview.html Configures an input field (`contents`) to return a reference into the database instead of cloning the value, by using the `#[returns(ref)]` attribute. ```rust #![allow(unused)] fn main() { #[salsa::input] pub struct ProgramFile { pub path: PathBuf, #[returns(ref)] pub contents: String, } } ``` -------------------------------- ### Setting Tracked Function Input with Durability Source: https://salsa-rs.github.io/salsa/reference/algorithm.html Illustrates how to set the value of a tracked function with a specified durability level, such as `HIGH`. ```rust #![allow(unused)] fn main() { module_text::set_with_durability( db, module, "fn foo() { }".to_string(), salsa::Durability::HIGH ); } ``` -------------------------------- ### Access Entire Input Data Source: https://salsa-rs.github.io/salsa/overview.html Demonstrates accessing the entire data of an input struct using the `data` method, which requires an immutable reference to the database. ```rust #![allow(unused)] fn main() { file.data(&db) } ``` -------------------------------- ### Configuration Trait Source: https://salsa-rs.github.io/salsa/plumbing/tracked_structs.html The `Configuration` trait provides the necessary interface for defining how a struct is tracked, including its fields, their properties, and how revisions are managed. It is typically implemented by the `#[salsa::tracked]` macro. ```APIDOC ## Trait: Configuration ### Description Trait that defines the key properties of a tracked struct. Implemented by the `#[salsa::tracked]` macro when applied to a struct. ### Associated Types - `Fields<'db>`: A tuple of the fields for this struct. Must be `Send + Sync`. - `Revisions`: An array of `AtomicRevision` values, one per tracked field. Used for tracking changes. Must be `Send + Sync` and implement `Index`, `Serialize`, and `Deserialize`. - `Struct<'db>`: Represents the struct itself, must be `Copy + FromId + AsId`. ### Associated Constants - `LOCATION`: The location of the tracked struct. - `DEBUG_NAME`: The debug name of the tracked struct. - `TRACKED_FIELD_NAMES`: The debug names of any tracked fields. - `TRACKED_FIELD_INDICES`: The relative indices of any tracked fields. - `PERSIST`: A boolean indicating whether this struct should be persisted with the database. ### Methods - `untracked_fields(fields: &Self::Fields<'_>) -> impl Hash`: Extracts hashable data from untracked fields. - `new_revisions(current_revision: Revision) -> Self::Revisions`: Creates a new revisions array, initializing each element with `current_revision`. - `update_fields<'db>(current_revision: Revision, revisions: &Self::Revisions, old_fields: *mut Self::Fields<'db>, new_fields: Self::Fields<'db>) -> bool`: Updates field data and revisions. Returns `true` if any untracked field was updated, indicating the struct should be re-created. This method is `unsafe` and requires specific conditions related to memory validity and invariants. - `heap_size(_value: &Self::Fields<'_>) -> Option`: Returns the size of any heap allocations in the output value, in bytes. Defaults to `None`. - `serialize(value: &Self::Fields<'_>, serializer: S) -> Result`: Serializes the fields using `serde`. Panics if `PERSIST` is `false`. - `deserialize<'de, D>(deserializer: D) -> Result, D::Error>`: Deserializes the fields using `serde`. Panics if `PERSIST` is `false`. ``` -------------------------------- ### Create a Tracked Struct Instance Source: https://salsa-rs.github.io/salsa/overview.html New instances of tracked structs are created by invoking the `new` function, which requires a database reference and the struct's fields. ```rust #![allow(unused)] fn main() { #[salsa::tracked] fn parse_file(db: &dyn crate::Db, file: ProgramFile) -> Ast { let contents: &str = file.contents(db); let parser = Parser::new(contents); let mut top_level_items = vec![]; while let Some(item) = parser.parse_top_level_item() { top_level_items.push(item); } Ast::new(db, top_level_items) // <-- create an Ast! } } ``` -------------------------------- ### Read Input Field Value Source: https://salsa-rs.github.io/salsa/overview.html Shows how to read the value of an input field (e.g., `contents`) using a getter method, which requires an immutable reference to the database and clones the value. ```rust #![allow(unused)] fn main() { let contents: String = file.contents(&db); } ``` -------------------------------- ### Push Values to an Accumulator Source: https://salsa-rs.github.io/salsa/overview.html Within a tracked function's execution, use `Diagnostics::push(db, value)` to add information to the accumulator. This allows reporting side-channel data like errors. ```rust #![allow(unused)] fn main() { Diagnostics::push(db, "some_string".to_string()) } ``` -------------------------------- ### Salsa Database Trait for On-Demand Input Source: https://salsa-rs.github.io/salsa/common_patterns/on_demand_inputs.html Defines a database trait 'Db' with a method 'input' that retrieves a 'File' input on demand. This method requires only an immutable reference to the database. ```rust #[salsa::db] trait Db: salsa::Database { fn input(&self, path: PathBuf) -> Result; } ``` -------------------------------- ### Define a Tracked Function Source: https://salsa-rs.github.io/salsa/overview.html Defines a tracked function named `parse_file` that takes a database reference and a `ProgramFile` input, returning an `Ast`. Salsa tracks the inputs accessed and memoizes the return value. ```rust #![allow(unused)] fn main() { #[salsa::tracked] fn parse_file(db: &dyn crate::Db, file: ProgramFile) -> Ast { let contents: &str = file.contents(db); ... } } ``` -------------------------------- ### Implement DebugWithDb for Enums Source: https://salsa-rs.github.io/salsa/tutorial/debug.html Forward to the ordinary `Debug` trait for consistency when implementing `DebugWithDb` for types like `Op` that are ordinary enums. ```rust #![allow(unused)] fn main() { } ``` -------------------------------- ### Salsa Input Struct for Files Source: https://salsa-rs.github.io/salsa/common_patterns/on_demand_inputs.html Defines a Salsa input struct 'File' to represent a file with its path and contents. The 'contents' field is marked with #[returns(ref)] for efficient borrowing. ```rust #[salsa::input] struct File { path: PathBuf, #[returns(ref)] contents: String, } ``` -------------------------------- ### Implement Salsa Database Trait Source: https://salsa-rs.github.io/salsa/tutorial/db.html Implements the `salsa::Database` trait for the custom database struct. This is a minimal implementation required by Salsa. ```rust #![allow(unused)] fn main() { #[salsa::db] impl salsa::Database for CalcDatabaseImpl {} } ``` -------------------------------- ### Program Source Struct in Rust Source: https://salsa-rs.github.io/salsa/print.html Defines the `ProgramSource` struct to hold the source code text of a 'calc' program. This is the initial input to the compiler. ```rust #![allow(unused)] fn main() { struct ProgramSource { text: String } } ``` -------------------------------- ### Define Tracked Functions for Module Parsing Source: https://salsa-rs.github.io/salsa/reference/algorithm.html Defines two tracked functions: `parse_module` which depends on `module_text`, and `module_text` which is an input function. ```rust #![allow(unused)] fn main() { #[salsa::tracked] fn parse_module(db: &dyn Db, module: Module) -> Ast { let module_text: &String = module_text(db, module); Ast::parse_text(module_text) } #[salsa::tracked(returns(ref))] fn module_text(db: &dyn Db, module: Module) -> String { panic!("text for module `{module:?}` not set") } } ``` -------------------------------- ### Demonstrate Interned Struct Uniqueness Source: https://salsa-rs.github.io/salsa/tutorial/ir.html Shows that invoking `new` with the same string data on an interned struct returns the same ID. This highlights the efficiency of interning for repeated values. ```rust #![allow(unused)] fn main() { let f1 = FunctionId::new(&db, "my_string".to_string()); let f2 = FunctionId::new(&db, "my_string".to_string()); assert_eq!(f1, f2); } ``` -------------------------------- ### Modify Input Field Value Source: https://salsa-rs.github.io/salsa/overview.html Illustrates how to modify an input field's value using a setter method (e.g., `set_contents`), which requires a mutable reference to the database and returns a builder for advanced options. ```rust #![allow(unused)] fn main() { file.set_contents(&mut db).to(String::from("fn foo() { /* add a comment */ }")); } ``` -------------------------------- ### Re-execution of Tracked Function on Input Change Source: https://salsa-rs.github.io/salsa/reference/algorithm.html Demonstrates how `parse_module` re-executes when the input provided by `module_text::set` changes. ```rust #![allow(unused)] fn main() { module_text::set( db, module, "fn foo() { }".to_string(), ); parse_module(db, module); // executes // ...some time later... module_text::set( db, module, "fn foo() { /* add a comment */ }".to_string(), ); parse_module(db, module); // executes again! } ``` -------------------------------- ### Define Salsa Database Struct Source: https://salsa-rs.github.io/salsa/tutorial/db.html Defines the main database struct for a Salsa application. It requires the `#[salsa::db]` attribute and a `storage` field. Additional fields can be included for custom needs, such as logging for testing. ```rust #![allow(unused)] fn main() { #[salsa::db] #[derive(Clone)] #[cfg_attr(not(test),дніault)] pub struct CalcDatabaseImpl { storage: salsa::Storage, // The logs are only used for testing and demonstrating reuse: #[cfg(test)] logs: Arc>>>, } #[cfg(test)] impl Default for CalcDatabaseImpl { fn default() -> Self { let logs = >>>>::default(); Self { storage: salsa::Storage::new(Some(Box::new({ let logs = logs.clone(); move |event| { eprintln!("Event: {:?}", event); // Log interesting events, if logging is enabled if let Some(logs) = &mut *logs.lock().unwrap() { // only log interesting events if let salsa::EventKind::WillExecute { .. } = event.kind { logs.push(format!("Event: {:?}", event)); } } } }))), logs, } } } } ``` -------------------------------- ### Parse Statements Function Source: https://salsa-rs.github.io/salsa/tutorial/parser.html The `parse_statements` function is the entry point for parsing program source code. It is annotated with `#[salsa::tracked]` to enable memoization and incremental reuse. It reads source text, initializes a parser, and iteratively parses statements until the end of the input is reached. ```rust #![allow(unused)] fn main() { #[salsa::tracked] pub fn parse_statements(db: &dyn crate::Db, source: SourceProgram) -> Program<'_> { // Get the source text from the database let source_text = source.text(db); // Create the parser let mut parser = Parser { db, source_text, position: 0, }; // Read in statements until we reach the end of the input let mut result = vec![]; loop { // Skip over any whitespace parser.skip_whitespace(); // If there are no more tokens, break if parser.peek().is_none() { break; } // Otherwise, there is more input, so parse a statement. if let Some(statement) = parser.parse_statement() { result.push(statement); } else { // If we failed, report an error at whatever position the parser // got stuck. We could recover here by skipping to the end of the line // or something like that. But we leave that as an exercise for the reader! parser.report_error(); break; } } Program::new(db, result) } } ```