diff --git a/Cargo.lock b/Cargo.lock index 58c2164..9c64e87 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -141,6 +141,7 @@ dependencies = [ "expect-test", "la-arena", "rnix", + "rowan", "salsa", ] diff --git a/Cargo.toml b/Cargo.toml index 9cf511b..b61a9d4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" [dependencies] la-arena = "0.2.1" rnix = { git = "https://github.com/oxalica/rnix-parser.git" } +rowan = "0.15.6" salsa = "0.17.0-pre.2" [dev-dependencies] diff --git a/src/def/lower.rs b/src/def/lower.rs index cf8a38c..9189714 100644 --- a/src/def/lower.rs +++ b/src/def/lower.rs @@ -1,34 +1,32 @@ -use std::collections::HashSet; - -use super::{Expr, ExprId, Literal, Module, ModuleSourceMap, Path, PathAnchor}; +use super::{AstPtr, Expr, ExprId, Literal, Module, ModuleSourceMap, Path, PathAnchor}; use crate::source::{FileId, InFile}; -use rnix::types::{self as ast, ParsedType, TokenWrapper, TypedNode, Wrapper}; +use rnix::types::{ParsedType, Root, TokenWrapper, TypedNode, Wrapper}; use rnix::value::Anchor; -use rnix::{NixValue, SyntaxNode}; +use rnix::{NixValue, SyntaxNode, AST}; -pub(super) fn lower(root: InFile) -> (Module, ModuleSourceMap) { +pub(super) fn lower(ast: InFile) -> (Module, ModuleSourceMap) { let mut ctx = LowerCtx { - file_id: root.file_id, + file_id: ast.file_id, module: Module::default(), - paths: HashSet::default(), + source_map: ModuleSourceMap::default(), }; - ctx.lower_node_opt(Some(root.value.node().clone())); - (ctx.module, ModuleSourceMap::default()) + let node = Root::cast(ast.value.node()).map(|root| root.node().clone()); + ctx.lower_node_opt(node); + (ctx.module, ctx.source_map) } struct LowerCtx { file_id: FileId, module: Module, - paths: HashSet, + source_map: ModuleSourceMap, } impl LowerCtx { - fn alloc_expr(&mut self, expr: Expr) -> ExprId { - self.module.exprs.alloc(expr) - } - - fn alloc_missing(&mut self) -> ExprId { - self.alloc_expr(Expr::Missing) + fn alloc_expr(&mut self, expr: Expr, ptr: AstPtr) -> ExprId { + let id = self.module.exprs.alloc(expr); + self.source_map.expr_map.insert(ptr.clone(), id); + self.source_map.expr_map_rev.insert(id, ptr); + id } fn lower_node_opt(&mut self, node: Option) -> ExprId { @@ -37,10 +35,11 @@ impl LowerCtx { return self.lower_expr(expr); } } - self.alloc_missing() + // Synthetic syntax has no coresponding text. + self.module.exprs.alloc(Expr::Missing) } - fn lower_path(&mut self, anchor: Anchor, segments: &str) -> ExprId { + fn lower_path(&mut self, anchor: Anchor, segments: &str, ptr: AstPtr) -> ExprId { let anchor = match anchor { Anchor::Relative => PathAnchor::Relative(self.file_id), Anchor::Absolute => todo!(), @@ -51,17 +50,17 @@ impl LowerCtx { anchor, raw_segments: segments.into(), }; - self.paths.insert(path.clone()); - self.alloc_expr(Expr::Literal(Literal::Path(path))) + self.alloc_expr(Expr::Literal(Literal::Path(path)), ptr) } fn lower_expr(&mut self, expr: ParsedType) -> ExprId { - match expr { - ParsedType::Root(e) => self.lower_node_opt(e.inner()), - ParsedType::Paren(e) => self.lower_node_opt(e.inner()), + let ptr = AstPtr::new(expr.node()); + let expr = match expr { + ParsedType::Root(e) => return self.lower_node_opt(e.inner()), + ParsedType::Paren(e) => return self.lower_node_opt(e.inner()), ParsedType::Ident(e) => { let ident = e.to_inner_string(); - self.alloc_expr(Expr::Ident(ident.into())) + Expr::Ident(ident.into()) } ParsedType::Value(e) => match e.to_value() { Ok(v) => { @@ -69,16 +68,16 @@ impl LowerCtx { NixValue::Integer(x) => Literal::Int(x), NixValue::Float(_) => todo!(), NixValue::String(s) => Literal::String(s.into()), - NixValue::Path(anchor, path) => return self.lower_path(anchor, &path), + NixValue::Path(anchor, path) => return self.lower_path(anchor, &path, ptr), }; - self.alloc_expr(Expr::Literal(lit)) + Expr::Literal(lit) } - Err(_) => self.alloc_missing(), + Err(_) => Expr::Missing, }, ParsedType::Apply(e) => { let lam = self.lower_node_opt(e.lambda()); let arg = self.lower_node_opt(e.value()); - self.alloc_expr(Expr::Apply(lam, arg)) + Expr::Apply(lam, arg) } ParsedType::Assert(_) => todo!(), ParsedType::IfElse(_) => todo!(), @@ -105,7 +104,8 @@ impl LowerCtx { | ParsedType::PatEntry(_) | ParsedType::Key(_) | ParsedType::Dynamic(_) - | ParsedType::Error(_) => self.alloc_missing(), - } + | ParsedType::Error(_) => Expr::Missing, + }; + self.alloc_expr(expr, ptr) } } diff --git a/src/def/mod.rs b/src/def/mod.rs index 339611b..29eb347 100644 --- a/src/def/mod.rs +++ b/src/def/mod.rs @@ -5,6 +5,7 @@ mod tests; use crate::source::{FileId, SourceDatabase}; use la_arena::{Arena, Idx}; +use std::collections::HashMap; use std::ops; use std::sync::Arc; @@ -13,6 +14,8 @@ pub trait DefDatabase: SourceDatabase { fn module_with_source_map(&self, file_id: FileId) -> (Arc, Arc); fn module(&self, file_id: FileId) -> Arc; + + fn source_map(&self, file_id: FileId) -> Arc; } fn module_with_source_map( @@ -20,7 +23,7 @@ fn module_with_source_map( file_id: FileId, ) -> (Arc, Arc) { let root = db.parse(file_id); - let (module, source_map) = lower::lower(root.map(|ast| ast.root())); + let (module, source_map) = lower::lower(root); (Arc::new(module), Arc::new(source_map)) } @@ -28,6 +31,10 @@ fn module(db: &dyn DefDatabase, file_id: FileId) -> Arc { db.module_with_source_map(file_id).0 } +fn source_map(db: &dyn DefDatabase, file_id: FileId) -> Arc { + db.module_with_source_map(file_id).1 +} + #[derive(Default, Debug, Clone, PartialEq, Eq)] pub struct Module { exprs: Arena, @@ -40,9 +47,13 @@ impl ops::Index for Module { } } -// TODO +pub type AstPtr = rowan::ast::SyntaxNodePtr; + #[derive(Default, Debug, Clone, PartialEq, Eq)] -pub struct ModuleSourceMap {} +pub struct ModuleSourceMap { + expr_map: HashMap, + expr_map_rev: HashMap, +} pub type ExprId = Idx; diff --git a/src/def/tests.rs b/src/def/tests.rs index 100f00b..d0a344a 100644 --- a/src/def/tests.rs +++ b/src/def/tests.rs @@ -48,4 +48,34 @@ fn module_basic() { } "#]] .assert_debug_eq(&db.module(root_id)); + + let source_map = db.source_map(root_id); + let mut expr_map = source_map.expr_map.iter().collect::>(); + expr_map.sort_by_key(|(_, id)| id.into_raw()); + let ptrs = expr_map.iter().map(|(ptr, _)| ptr).collect::>(); + expect![[r#" + [ + SyntaxNodePtr { + kind: NODE_IDENT, + range: 9..12, + }, + SyntaxNodePtr { + kind: NODE_LITERAL, + range: 13..16, + }, + SyntaxNodePtr { + kind: NODE_APPLY, + range: 9..16, + }, + SyntaxNodePtr { + kind: NODE_LITERAL, + range: 17..26, + }, + SyntaxNodePtr { + kind: NODE_APPLY, + range: 9..26, + }, + ] + "#]] + .assert_debug_eq(&ptrs); }