From e95ad89f139822254c82b5da0cb035e27f22aea3 Mon Sep 17 00:00:00 2001 From: Justin Garcia Date: Thu, 30 Jan 2025 03:39:11 +0800 Subject: [PATCH] Implement initial query engine --- Cargo.lock | 5 + crates/building/Cargo.toml | 5 + crates/building/src/base.rs | 206 +++++++++++++++++++++++++++++++++++- 3 files changed, 214 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1415f68..7160ebf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -35,11 +35,16 @@ version = "0.1.0" dependencies = [ "dashmap", "id", + "indexing", "indexmap", + "lexing", "parking_lot", + "parsing", "petgraph", "rayon", + "rowan", "rustc-hash 2.1.0", + "syntax", ] [[package]] diff --git a/crates/building/Cargo.toml b/crates/building/Cargo.toml index 4799ded..d84df8b 100644 --- a/crates/building/Cargo.toml +++ b/crates/building/Cargo.toml @@ -6,8 +6,13 @@ edition = "2021" [dependencies] dashmap = "6.1.0" id = { version = "0.1.0", path = "../id" } +indexing = { version = "0.1.0", path = "../indexing" } indexmap = "2.7.1" +lexing = { version = "0.1.0", path = "../lexing" } parking_lot = "0.12.3" +parsing = { version = "0.1.0", path = "../parsing" } petgraph = "0.7.1" rayon = "1.10.0" +rowan = "0.16.1" rustc-hash = "2.1.0" +syntax = { version = "0.1.0", path = "../syntax" } diff --git a/crates/building/src/base.rs b/crates/building/src/base.rs index 4272122..05baf1b 100644 --- a/crates/building/src/base.rs +++ b/crates/building/src/base.rs @@ -1,5 +1,207 @@ +use std::{mem, sync::Arc}; + +use indexing::IndexingResult; +use rowan::ast::AstNode; +use rustc_hash::{FxHashMap, FxHashSet}; +use syntax::cst; + +use crate::files::FileId; + #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum QueryKey { - Index(usize), - Resolve(usize), + Content(FileId), + Parse(FileId), + Index(FileId), +} + +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct Trace { + built: usize, + changed: usize, + dependencies: Arc<[QueryKey]>, +} + +impl Trace { + pub fn create_input(revision: usize) -> Trace { + Trace { built: revision, changed: revision, dependencies: [].into() } + } + + pub fn create_fresh(revision: usize, dependencies: Arc<[QueryKey]>) -> Trace { + Trace { built: revision, changed: revision, dependencies } + } +} + +#[derive(Default)] +pub struct Runtime { + revision: usize, + traces: FxHashMap, + + content: FxHashMap>, + parse: FxHashMap, + index: FxHashMap>, + + parent: Option, + dependencies: FxHashMap>, +} + +impl Runtime { + pub fn compute(&mut self, k: QueryKey, compute: impl Fn(&mut Runtime) -> T) -> T { + let parent = mem::replace(&mut self.parent, Some(k)); + let result = compute(self); + + self.revision += 1; + let revision = self.revision; + + let dependencies = self + .dependencies + .get(&k) + .map(|dependencies| dependencies.iter().copied()) + .unwrap_or_default() + .collect(); + + let v = Trace::create_fresh(revision, dependencies); + self.traces.insert(k, v); + + self.parent = parent; + result + } + + pub fn query( + &mut self, + k: QueryKey, + compute: impl Fn(&mut Runtime) -> T, + get_storage: impl Fn(&mut Runtime) -> Option<(T, &mut Trace)>, + set_storage: impl Fn(&mut Runtime, T), + ) -> T { + if let Some(parent) = self.parent { + self.dependencies.entry(parent).or_default().insert(k); + } + + let revision = self.revision; + if let Some((value, trace)) = get_storage(self) { + if trace.built == revision { + value + } else { + let built = trace.built; + let dependencies = Arc::clone(&trace.dependencies); + + let mut latest = 0; + for dependency in dependencies.iter() { + match dependency { + QueryKey::Content(_) => (), + QueryKey::Parse(id) => { + self.parse(*id); + } + QueryKey::Index(_) => (), + } + if let Some(dependency) = self.traces.get(dependency) { + latest = latest.max(dependency.changed); + } + } + + if built >= latest { + if let Some(trace) = self.traces.get_mut(&k) { + trace.built = revision; + } + value + } else { + let fresh = self.compute(k, compute); + set_storage(self, T::clone(&fresh)); + fresh + } + } + } else { + let fresh = self.compute(k, compute); + set_storage(self, T::clone(&fresh)); + fresh + } + } + + pub fn set_content(&mut self, id: FileId, content: Arc) { + self.revision += 1; + let revision = self.revision; + + self.content.insert(id, content); + + let k = QueryKey::Content(id); + let v = Trace::create_input(revision); + self.traces.insert(k, v); + } + + pub fn content(&mut self, id: FileId) -> Arc { + let k = QueryKey::Content(id); + if let Some(parent) = self.parent { + self.dependencies.entry(parent).or_default().insert(k); + } + let v = self.content.get(&id).expect("invalid violated: invalid query key"); + Arc::clone(v) + } + + pub fn parse(&mut self, id: FileId) -> cst::Module { + let k = QueryKey::Parse(id); + self.query( + k, + |this| { + let content = this.content(id); + + let lexed = lexing::lex(&content); + let tokens = lexing::layout(&lexed); + + let (node, _) = parsing::parse(&lexed, &tokens); + cst::Module::cast(node).expect("invariant violated: cannot cast parse result") + }, + |this| { + let value = this.parse.get(&id).cloned()?; + let trace = this.traces.get_mut(&k)?; + Some((value, trace)) + }, + |this, value| { + this.parse.insert(id, value); + }, + ) + } + + pub fn index(&mut self, id: FileId) -> Arc { + let k = QueryKey::Index(id); + self.query( + k, + |this| { + let module = this.parse(id); + let (result, _) = indexing::index(&module); + Arc::new(result) + }, + |this| { + let value = this.index.get(&id).cloned()?; + let trace = this.traces.get_mut(&k)?; + Some((value, trace)) + }, + |this, value| { + this.index.insert(id, value); + }, + ) + } +} + +#[cfg(test)] +mod tests { + use crate::files::Files; + + use super::Runtime; + + #[test] + fn test_basic() { + let mut runtime = Runtime::default(); + let mut files = Files::default(); + + let id = files.insert("./src/Main.purs", "module Main where\n\nlife = 42"); + let content = files.content(id); + + runtime.set_content(id, content); + dbg!(runtime.index(id)); + dbg!(runtime.index(id)); + + runtime.set_content(id, "module Main where\n\n\n\nlife = 42".into()); + dbg!(runtime.index(id)); + dbg!(runtime.index(id)); + } }