From 0b643ddc0ddf74bfaa52ee4eb661332df95d9c69 Mon Sep 17 00:00:00 2001 From: oxalica Date: Thu, 28 Jul 2022 01:59:20 +0800 Subject: [PATCH] Impl Attrset lowering with desugaring --- Cargo.lock | 1 + Cargo.toml | 1 + src/def/lower.rs | 435 ++++++++++++++++++++++++++++++++++++++++++++++- src/def/mod.rs | 46 ++++- 4 files changed, 477 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ab4f6a7..9c9f054 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -226,6 +226,7 @@ name = "nil" version = "0.0.0" dependencies = [ "expect-test", + "indexmap", "la-arena", "ordered-float", "rowan", diff --git a/Cargo.toml b/Cargo.toml index e469fed..08cf653 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,7 @@ version = "0.0.0" edition = "2021" [dependencies] +indexmap = "1.9.1" la-arena = "0.2.1" ordered-float = "3.0.0" rowan = "0.15.6" diff --git a/src/def/lower.rs b/src/def/lower.rs index af9cfae..3f3b8c0 100644 --- a/src/def/lower.rs +++ b/src/def/lower.rs @@ -1,13 +1,14 @@ use super::{ - AstPtr, Attrpath, Expr, ExprId, Literal, Module, ModuleSourceMap, NameDef, NameDefId, Pat, - Path, PathAnchor, + AstPtr, Attrpath, BindingKey, BindingValue, Bindings, Expr, ExprId, Literal, Module, + ModuleSourceMap, NameDef, NameDefId, Pat, Path, PathAnchor, }; use crate::base::{FileId, InFile}; +use indexmap::IndexMap; use la_arena::Arena; use rowan::ast::AstNode; use smol_str::SmolStr; use std::str; -use syntax::ast::{self, HasStringParts, LiteralKind}; +use syntax::ast::{self, HasBindings, HasStringParts, LiteralKind}; pub(super) fn lower(root: InFile) -> (Module, ModuleSourceMap) { let mut ctx = LowerCtx { @@ -146,8 +147,26 @@ impl LowerCtx { let elements = e.elements().map(|e| self.lower_expr(e)).collect(); self.alloc_expr(Expr::List(elements), ptr) } - ast::Expr::AttrSet(_) => todo!(), - ast::Expr::LetIn(_) => todo!(), + ast::Expr::LetIn(e) => { + let mut set = MergingSet::new(true, ptr.clone()); + set.merge_bindings(self, &e); + let bindings = set.finish(self); + let body = self.lower_expr_opt(e.body()); + self.alloc_expr(Expr::LetIn(bindings, body), ptr) + } + ast::Expr::AttrSet(e) => { + let (is_rec, ctor): (bool, fn(_) -> _) = if e.rec_token().is_some() { + (true, Expr::Attrset) + } else if e.let_token().is_some() { + (true, Expr::LetAttrset) + } else { + (false, Expr::Attrset) + }; + let mut set = MergingSet::new(is_rec, ptr.clone()); + set.merge_bindings(self, &e); + let bindings = set.finish(self); + self.alloc_expr(ctor(bindings), ptr) + } } } @@ -246,6 +265,246 @@ impl LowerCtx { .collect(); self.alloc_expr(Expr::StringInterpolation(parts), ptr) } + + fn lower_key(&mut self, is_rec: bool, attr: ast::Attr) -> BindingKey { + let ast_string = match attr { + ast::Attr::Name(n) if is_rec => return BindingKey::NameDef(self.lower_name(n)), + ast::Attr::Name(n) => { + return BindingKey::Name( + n.token() + .map_or_else(Default::default, |tok| tok.text().into()), + ) + } + ast::Attr::String(s) => s, + ast::Attr::Dynamic(d) => { + let mut e = d.expr(); + loop { + match e { + Some(ast::Expr::String(s)) => break s, + Some(ast::Expr::Paren(p)) => e = p.expr(), + _ => return BindingKey::Dynamic(self.lower_expr_opt(e)), + } + } + } + }; + + if ast_string + .string_parts() + .all(|part| !matches!(part, ast::StringPart::Dynamic(_))) + { + let ptr = AstPtr::new(ast_string.syntax()); + let content = ast_string + .string_parts() + .fold(String::new(), |prev, part| match part { + ast::StringPart::Dynamic(_) => unreachable!(), + ast::StringPart::Fragment(tok) => prev + tok.text(), + ast::StringPart::Escape(tok) => match tok.text().as_bytes() { + b"\\n" => prev + "\n", + b"\\r" => prev + "\r", + b"\\t" => prev + "\t", + [b'\\', bytes @ ..] => { + prev + str::from_utf8(bytes).expect("Verified by the lexer") + } + _ => unreachable!("Verified by the lexer"), + }, + }); + if is_rec { + return BindingKey::NameDef(self.alloc_name_def(content.into(), ptr)); + } else { + return BindingKey::Name(content.into()); + } + } + + BindingKey::Dynamic(self.lower_string(&ast_string)) + } +} + +struct MergingSet { + is_rec: bool, + ptr: AstPtr, + entries: IndexMap, + inherit_froms: Vec, +} + +#[derive(Default)] +enum MergingValue { + #[default] + Placeholder, + Attrset(MergingSet), + Final(BindingValue), +} + +impl MergingSet { + fn new(is_rec: bool, ptr: AstPtr) -> Self { + Self { + is_rec, + ptr, + entries: Default::default(), + inherit_froms: Vec::new(), + } + } + + // Place an orphaned Expr as dynamic-attrs for error recovery. + // TODO: Report errors. + fn error(&mut self, ctx: &mut LowerCtx, expr: ExprId, ptr: AstPtr) { + let k = BindingKey::Dynamic(ctx.alloc_expr(Expr::Missing, ptr)); + let v = MergingValue::Final(BindingValue::Expr(expr)); + self.entries.insert(k, v); + } + + fn merge_bindings(&mut self, ctx: &mut LowerCtx, n: &impl HasBindings) { + for b in n.bindings() { + match b { + ast::Binding::Inherit(i) => self.merge_inherit(ctx, i), + ast::Binding::AttrpathValue(entry) => self.merge_path_value(ctx, entry), + } + } + } + + fn merge_inherit(&mut self, ctx: &mut LowerCtx, i: ast::Inherit) { + let from_expr = i.from_expr().map(|e| { + let expr = ctx.lower_expr_opt(e.expr()); + let from_id = self.inherit_froms.len() as u32; + self.inherit_froms.push(expr); + from_id + }); + + for attr in i.attrs() { + let ptr = AstPtr::new(attr.syntax()); + let key = match ctx.lower_key(self.is_rec, attr) { + // `inherit ${expr}` or `inherit (expr) ${expr}` is invalid. + BindingKey::Dynamic(expr) => { + self.error(ctx, expr, ptr.clone()); + continue; + } + key => key, + }; + + // Inherited names never merge other values. It must be an error. + if self.entries.contains_key(&key) { + // Report error. + continue; + } + + let value = match from_expr { + Some(id) => MergingValue::Final(BindingValue::InheritFrom(id)), + None => { + let name = match &key { + BindingKey::NameDef(def) => ctx.module[*def].name.clone(), + BindingKey::Name(s) => s.clone(), + BindingKey::Dynamic(_) => unreachable!(), + }; + let ref_expr = ctx.alloc_expr(Expr::Reference(name), ptr); + MergingValue::Final(BindingValue::Inherit(ref_expr)) + } + }; + self.entries.insert(key, value); + } + } + + fn merge_path_value( + mut self: &mut Self, + // &mut self, + ctx: &mut LowerCtx, + entry: ast::AttrpathValue, + ) { + let mut attrs = entry.attrpath().into_iter().flat_map(|path| path.attrs()); + let mut next_attr = match attrs.next() { + Some(first_attr) => first_attr, + // Recover from missing Attrpath. + None => { + let value_expr = ctx.lower_expr_opt(entry.value()); + self.error(ctx, value_expr, AstPtr::new(entry.syntax())); + return; + } + }; + + loop { + let key = ctx.lower_key(self.is_rec, next_attr); + let deep = self.entries.entry(key).or_default(); + match attrs.next() { + Some(attr) => { + self = deep.make_attrset(ctx, false, AstPtr::new(attr.syntax())); + next_attr = attr; + } + None => { + deep.merge_ast(ctx, entry.value()); + return; + } + } + } + } + + fn finish(self, ctx: &mut LowerCtx) -> Bindings { + Bindings { + entries: self + .entries + .into_iter() + .map(|(k, v)| (k, v.finish(ctx))) + .collect(), + inherit_froms: self.inherit_froms.into(), + } + } +} + +impl MergingValue { + fn make_attrset(&mut self, ctx: &mut LowerCtx, is_rec: bool, ptr: AstPtr) -> &mut MergingSet { + match self { + // TOOD: Report warnings if set.is_rec + Self::Attrset(set) => return set, + Self::Placeholder => *self = Self::Attrset(MergingSet::new(is_rec, ptr)), + // We prefer to become a Attrset as a guess, which supports further merging. + Self::Final(v) => { + // TOOD: Report errors. + let mut set = MergingSet::new(is_rec, ptr); + if let BindingValue::Expr(expr) = *v { + // If the previous one is Final, it must be from `inherit` or `key = final_expr`, + // where the source map must already be store. + // Value merging can only make Final -> Attrset but not the reverse. + let prev_ptr = ctx.source_map.expr_map_rev[&expr].clone(); + set.error(ctx, expr, prev_ptr); + } + *self = Self::Attrset(set); + } + } + match self { + Self::Attrset(set) => set, + _ => unreachable!(), + } + } + + fn merge_ast(&mut self, ctx: &mut LowerCtx, mut e: Option) { + loop { + match e { + Some(ast::Expr::Paren(p)) => e = p.expr(), + Some(ast::Expr::AttrSet(e)) if e.let_token().is_none() => { + return self + .make_attrset(ctx, e.rec_token().is_some(), AstPtr::new(e.syntax())) + .merge_bindings(ctx, &e); + } + _ => break, + } + } + // Here we got an unmergable non-Attrset Expr. + match self { + Self::Placeholder => *self = Self::Final(BindingValue::Expr(ctx.lower_expr_opt(e))), + // TODO: Report errors. + Self::Attrset(..) | Self::Final(_) => {} + } + } + + fn finish(self, ctx: &mut LowerCtx) -> BindingValue { + match self { + Self::Placeholder => unreachable!("Should be processed"), + Self::Final(k) => k, + Self::Attrset(set) => { + let ptr = set.ptr.clone(); + let bindings = set.finish(ctx); + let expr = ctx.alloc_expr(Expr::Attrset(bindings), ptr); + BindingValue::Expr(expr) + } + } + } } #[cfg(test)] @@ -263,6 +522,12 @@ mod tests { for (i, e) in module.exprs.iter() { writeln!(got, "{}: {:?}", i.into_raw(), e).unwrap(); } + if !module.name_defs.is_empty() { + writeln!(got).unwrap(); + } + for (i, def) in module.name_defs.iter() { + writeln!(got, "{}: {:?}", i.into_raw(), def).unwrap(); + } expect.assert_eq(&got); } @@ -335,6 +600,8 @@ mod tests { expect![[r#" 0: Literal(Int(0)) 1: Lambda(Some(Idx::(0)), None, Idx::(0)) + + 0: NameDef { name: "a" } "#]], ); check_lower( @@ -349,6 +616,8 @@ mod tests { expect![[r#" 0: Literal(Int(0)) 1: Lambda(Some(Idx::(0)), Some(Pat { fields: [], ellipsis: true }), Idx::(0)) + + 0: NameDef { name: "a" } "#]], ); check_lower( @@ -356,6 +625,8 @@ mod tests { expect![[r#" 0: Literal(Int(0)) 1: Lambda(Some(Idx::(0)), Some(Pat { fields: [], ellipsis: false }), Idx::(0)) + + 0: NameDef { name: "a" } "#]], ); check_lower( @@ -364,6 +635,9 @@ mod tests { 0: Literal(Int(0)) 1: Literal(Int(0)) 2: Lambda(None, Some(Pat { fields: [(Some(Idx::(0)), None), (Some(Idx::(1)), Some(Idx::(0)))], ellipsis: true }), Idx::(1)) + + 0: NameDef { name: "a" } + 1: NameDef { name: "b" } "#]], ); check_lower( @@ -372,6 +646,10 @@ mod tests { 0: Literal(Int(0)) 1: Literal(Int(0)) 2: Lambda(Some(Idx::(0)), Some(Pat { fields: [(Some(Idx::(1)), Some(Idx::(0))), (Some(Idx::(2)), None)], ellipsis: false }), Idx::(1)) + + 0: NameDef { name: "c" } + 1: NameDef { name: "a" } + 2: NameDef { name: "b" } "#]], ); } @@ -483,4 +761,151 @@ mod tests { "#]], ); } + + #[test] + fn attrset_key_kind() { + check_lower( + r#"{ a = 1; ${b} = 2; "c" = 3; ${(("\n"))} = 4; }"#, + expect![[r#" + 0: Literal(Int(1)) + 1: Reference("b") + 2: Literal(Int(2)) + 3: Literal(Int(3)) + 4: Literal(Int(4)) + 5: Attrset(Bindings { entries: [(Name("a"), Expr(Idx::(0))), (Dynamic(Idx::(1)), Expr(Idx::(2))), (Name("c"), Expr(Idx::(3))), (Name("\n"), Expr(Idx::(4)))], inherit_froms: [] }) + "#]], + ); + } + + #[test] + fn attrset_inherit() { + check_lower( + r#"{ inherit; inherit a "b" ${("c")}; inherit (d); inherit (e) f g; }"#, + expect![[r#" + 0: Reference("a") + 1: Reference("b") + 2: Reference("c") + 3: Reference("d") + 4: Reference("e") + 5: Attrset(Bindings { entries: [(Name("a"), Inherit(Idx::(0))), (Name("b"), Inherit(Idx::(1))), (Name("c"), Inherit(Idx::(2))), (Name("f"), InheritFrom(1)), (Name("g"), InheritFrom(1))], inherit_froms: [Idx::(3), Idx::(4)] }) + "#]], + ); + } + + #[test] + fn attrset_merge_non_rec() { + // Path and path. + check_lower( + "{ a.b = 1; a.c = 2; }", + expect![[r#" + 0: Literal(Int(1)) + 1: Literal(Int(2)) + 2: Attrset(Bindings { entries: [(Name("b"), Expr(Idx::(0))), (Name("c"), Expr(Idx::(1)))], inherit_froms: [] }) + 3: Attrset(Bindings { entries: [(Name("a"), Expr(Idx::(2)))], inherit_froms: [] }) + "#]], + ); + // Path and attrset. + check_lower( + "{ a.b = 1; a = { c = 2; }; }", + expect![[r#" + 0: Literal(Int(1)) + 1: Literal(Int(2)) + 2: Attrset(Bindings { entries: [(Name("b"), Expr(Idx::(0))), (Name("c"), Expr(Idx::(1)))], inherit_froms: [] }) + 3: Attrset(Bindings { entries: [(Name("a"), Expr(Idx::(2)))], inherit_froms: [] }) + "#]], + ); + // Attrset and path. + check_lower( + "{ a = { b = 1; }; a.c = 2; }", + expect![[r#" + 0: Literal(Int(1)) + 1: Literal(Int(2)) + 2: Attrset(Bindings { entries: [(Name("b"), Expr(Idx::(0))), (Name("c"), Expr(Idx::(1)))], inherit_froms: [] }) + 3: Attrset(Bindings { entries: [(Name("a"), Expr(Idx::(2)))], inherit_froms: [] }) + "#]], + ); + // Attrset and attrset. + check_lower( + "{ a = { b = 1; }; a = { c = 2; }; }", + expect![[r#" + 0: Literal(Int(1)) + 1: Literal(Int(2)) + 2: Attrset(Bindings { entries: [(Name("b"), Expr(Idx::(0))), (Name("c"), Expr(Idx::(1)))], inherit_froms: [] }) + 3: Attrset(Bindings { entries: [(Name("a"), Expr(Idx::(2)))], inherit_froms: [] }) + "#]], + ); + } + + #[test] + fn attrset_merge_rec() { + // Rec and non-rec. + check_lower( + "{ a = rec { b = 1; }; a.c = 2; }", + expect![[r#" + 0: Literal(Int(1)) + 1: Literal(Int(2)) + 2: Attrset(Bindings { entries: [(NameDef(Idx::(0)), Expr(Idx::(0))), (NameDef(Idx::(1)), Expr(Idx::(1)))], inherit_froms: [] }) + 3: Attrset(Bindings { entries: [(Name("a"), Expr(Idx::(2)))], inherit_froms: [] }) + + 0: NameDef { name: "b" } + 1: NameDef { name: "c" } + "#]], + ); + // Non-rec and rec. + check_lower( + "{ a = { b = 1; }; a = rec { c = 2; }; }", + expect![[r#" + 0: Literal(Int(1)) + 1: Literal(Int(2)) + 2: Attrset(Bindings { entries: [(Name("b"), Expr(Idx::(0))), (Name("c"), Expr(Idx::(1)))], inherit_froms: [] }) + 3: Attrset(Bindings { entries: [(Name("a"), Expr(Idx::(2)))], inherit_froms: [] }) + "#]], + ); + } + + #[test] + fn attrset_rec_deep() { + check_lower( + "rec { a.b = 1; }", + expect![[r#" + 0: Literal(Int(1)) + 1: Attrset(Bindings { entries: [(Name("b"), Expr(Idx::(0)))], inherit_froms: [] }) + 2: Attrset(Bindings { entries: [(NameDef(Idx::(0)), Expr(Idx::(1)))], inherit_froms: [] }) + + 0: NameDef { name: "a" } + "#]], + ); + } + + #[test] + fn attrset_let() { + check_lower( + "{ a.b = let { c.d = 1; }; }", + expect![[r#" + 0: Literal(Int(1)) + 1: Attrset(Bindings { entries: [(Name("d"), Expr(Idx::(0)))], inherit_froms: [] }) + 2: LetAttrset(Bindings { entries: [(NameDef(Idx::(0)), Expr(Idx::(1)))], inherit_froms: [] }) + 3: Attrset(Bindings { entries: [(Name("b"), Expr(Idx::(2)))], inherit_froms: [] }) + 4: Attrset(Bindings { entries: [(Name("a"), Expr(Idx::(3)))], inherit_froms: [] }) + + 0: NameDef { name: "c" } + "#]], + ); + } + + #[test] + fn attrset_dynamic_no_merge() { + check_lower( + "{ ${a}.b = 1; ${a}.b = 2; }", + expect![[r#" + 0: Reference("a") + 1: Literal(Int(1)) + 2: Reference("a") + 3: Literal(Int(2)) + 4: Attrset(Bindings { entries: [(Name("b"), Expr(Idx::(1)))], inherit_froms: [] }) + 5: Attrset(Bindings { entries: [(Name("b"), Expr(Idx::(3)))], inherit_froms: [] }) + 6: Attrset(Bindings { entries: [(Dynamic(Idx::(0)), Expr(Idx::(4))), (Dynamic(Idx::(2)), Expr(Idx::(5)))], inherit_froms: [] }) + "#]], + ); + } } diff --git a/src/def/mod.rs b/src/def/mod.rs index 36c74a9..dd0642a 100644 --- a/src/def/mod.rs +++ b/src/def/mod.rs @@ -109,7 +109,9 @@ pub enum Expr { Select(ExprId, Attrpath, Option), StringInterpolation(Box<[ExprId]>), List(Box<[ExprId]>), - // TODO + LetIn(Bindings, ExprId), + Attrset(Bindings), + LetAttrset(Bindings), } impl Expr { @@ -147,6 +149,13 @@ impl Expr { } } Self::StringInterpolation(xs) | Self::List(xs) => xs.iter().copied().for_each(f), + Self::LetIn(bindings, body) => { + bindings.walk_child_exprs(&mut f); + f(*body); + } + Self::Attrset(bindings) | Self::LetAttrset(bindings) => { + bindings.walk_child_exprs(f); + } } } } @@ -193,3 +202,38 @@ pub struct Pat { } pub type Attrpath = Box<[ExprId]>; + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct Bindings { + pub entries: Box<[(BindingKey, BindingValue)]>, + pub inherit_froms: Box<[ExprId]>, +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum BindingKey { + NameDef(NameDefId), + Name(SmolStr), + Dynamic(ExprId), +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum BindingValue { + Inherit(ExprId), + InheritFrom(u32), + Expr(ExprId), +} + +impl Bindings { + pub(crate) fn walk_child_exprs(&self, mut f: impl FnMut(ExprId)) { + for (key, kind) in self.entries.iter() { + match key { + BindingKey::NameDef(_) | BindingKey::Name(_) => {} + &BindingKey::Dynamic(expr) => f(expr), + } + match *kind { + BindingValue::Inherit(e) | BindingValue::Expr(e) => f(e), + BindingValue::InheritFrom(_) => {} + } + } + } +}