use crate::{NixLanguage, SyntaxKind, SyntaxKind::*, SyntaxNode, SyntaxToken}; use super::{operators::BinOpKind, support::*, AstNode, UnaryOpKind}; use rowan::ast::{AstChildren, AstNode as OtherAstNode}; pub trait HasEntry: AstNode { fn entries(&self) -> AstChildren where Self: Sized, { children(self) } fn attrpath_values(&self) -> AstChildren where Self: Sized, { children(self) } fn inherits(&self) -> AstChildren where Self: Sized, { children(self) } } macro_rules! node { ( #[from($kind:ident)] $(#[$meta:meta])* struct $name:ident; ) => { #[derive(Debug, Clone, PartialEq, Eq, Hash)] $(#[$meta])* pub struct $name(pub(super) SyntaxNode); impl std::fmt::Display for $name { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl rowan::ast::AstNode for $name { type Language = crate::NixLanguage; fn can_cast(kind: crate::SyntaxKind) -> bool { kind == $kind } fn cast(from: SyntaxNode) -> Option { if Self::can_cast(from.kind()) { Some(Self(from)) } else { None } } fn syntax(&self) -> &SyntaxNode { &self.0 } } impl $name { pub const KIND: SyntaxKind = $kind; } }; ( #[from($($variant:ident),* $(,)?)] $(#[$meta:meta])* enum $name:ident; ) => { #[derive(Debug, Clone, PartialEq, Eq, Hash)] $(#[$meta])* pub enum $name { $( $variant($variant), )* } impl std::fmt::Display for $name { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } impl rowan::ast::AstNode for $name { type Language = NixLanguage; fn can_cast(kind: SyntaxKind) -> bool { matches!(kind, $($variant::KIND)|*) } fn cast(syntax: SyntaxNode) -> Option { let res = match syntax.kind() { $( $variant::KIND => $name::$variant($variant(syntax)) ),*, _ => return None, }; Some(res) } fn syntax(&self) -> &SyntaxNode { match self { $( $name::$variant(it) => &it.0, )* } } } $( impl From<$variant> for $name { fn from(node: $variant) -> $name { $name::$variant(node) } } impl TryFrom<$name> for $variant { type Error = (); fn try_from(node: $name) -> Result<$variant, ()> { match node { $name::$variant(it) => Ok(it), _ => Err(()), } } } )* }; } macro_rules! tg { ( $(#[doc=$doc:tt])? $name:ident, $token:tt ) => { $(#[doc=$doc])? pub fn $name(&self) -> Option { token_u(self, T![$token]) } }; } macro_rules! ng { ( $(#[$meta:meta])* $name:ident, $ty:ty, $i:expr ) => { $(#[$meta])* pub fn $name(&self) -> Option<$ty> { nth(self, $i) } }; ( $(#[$meta:meta])* $name:ident, [$ty:ty] ) => { $(#[$meta])* pub fn $name(&self) -> AstChildren<$ty> { children(self) } }; } node! { #[from(NODE_LITERAL)] struct Literal; } node! { #[from( Apply, Assert, Error, IfElse, Select, Str, PathAbs, PathRel, PathHome, PathSearch, Literal, Lambda, LegacyLet, LetIn, List, BinOp, Paren, Root, AttrSet, UnaryOp, Ident, With, HasAttr, CurPos, )] /// An expression. The fundamental nix ast type. enum Expr; } node! { #[from( Ident, Dynamic, Str, )] enum Attr; } node! { #[from(NODE_IDENT)] struct Ident; } impl Ident { // A NODE_IDENT may either have a nested TOKEN_OR if the identifier was "or" or TOKEN_IDENT for everything else tg! { ident_token, TOKEN_IDENT } } node! { #[from(NODE_APPLY)] struct Apply; } impl Apply { ng! { lambda, Expr, 0 } ng! { argument, Expr, 1 } } node! { #[from(NODE_ASSERT)] struct Assert; } impl Assert { tg! { assert_token, assert } ng! { condition, Expr, 0 } ng! { body, Expr, 1 } } node! { #[from(NODE_ATTRPATH)] struct Attrpath; } impl Attrpath { ng! { attrs, [Attr] } } node! { #[from(NODE_DYNAMIC)] struct Dynamic; } impl Dynamic { tg! { interpol_start_token, TOKEN_INTERPOL_START } ng! { expr, Expr, 0 } tg! { interpol_end_token, TOKEN_INTERPOL_END } } node! { #[from(NODE_ERROR)] struct Error; } node! { #[from(NODE_IF_ELSE)] struct IfElse; } impl IfElse { tg! { if_token, if } ng! { condition, Expr, 0 } tg! { then_token, then } ng! { body, Expr, 1 } tg! { else_token, else } ng! { else_body, Expr, 2 } } node! { #[from(NODE_SELECT)] struct Select; } impl Select { ng! { expr, Expr, 0 } tg! { dot_token, . } ng! { attrpath, Attrpath, 0 } tg! { or_token, or } ng! { default_expr, Expr, 1 } } node! { #[from(NODE_INHERIT)] struct Inherit; } impl Inherit { tg! { inherit_token, inherit } ng! { from, InheritFrom, 0 } ng! { attrs, [Attr] } } node! { #[from(NODE_INHERIT_FROM)] struct InheritFrom; } impl InheritFrom { tg! { l_paren_token, '(' } ng! { expr, Expr, 0 } tg! { r_paren_token, ')' } } node! { #[from(NODE_PATH_ABS)] struct PathAbs; } node! { #[from(NODE_PATH_REL)] struct PathRel; } node! { #[from(NODE_PATH_HOME)] struct PathHome; } node! { #[from(NODE_PATH_SEARCH)] struct PathSearch; } node! { #[from( PathAbs, PathRel, PathHome, PathSearch, )] enum Path; } node! { #[from(NODE_STRING)] struct Str; } node! { #[from(NODE_INTERPOL)] struct Interpol; } impl Interpol { ng! { expr, Expr, 0 } } node! { #[from(NODE_LAMBDA)] struct Lambda; } impl Lambda { ng! { param, Param, 0 } tg! { token_colon, : } ng! { body, Expr, 0 } } node! { #[from(NODE_LEGACY_LET)] struct LegacyLet; } impl HasEntry for LegacyLet {} impl LegacyLet { tg! { let_token, let } tg! { curly_open_token, '{' } tg! { curly_close_token, '}' } } node! { #[from(NODE_LET_IN)] struct LetIn; } impl HasEntry for LetIn {} impl LetIn { tg! { let_token, let } tg! { in_token, in } ng! { body, Expr, 0 } } node! { #[from(NODE_LIST)] struct List; } impl List { tg! { l_brack_token, '[' } ng! { items, [Expr] } tg! { r_brack_token, ']' } } node! { #[from(NODE_BIN_OP)] struct BinOp; } impl BinOp { ng! { lhs, Expr, 0 } pub fn operator(&self) -> Option { children_tokens_u(self).find_map(|t| BinOpKind::from_kind(t.kind())) } ng! { rhs, Expr, 1 } } node! { #[from(NODE_PAREN)] struct Paren; } impl Paren { tg! { l_paren_token, '(' } ng! { expr, Expr, 0 } tg! { r_paren_token, ')' } } node! { #[from(NODE_PAT_BIND)] struct PatBind; } impl PatBind { ng! { ident, Ident, 0 } } node! { #[from(NODE_PAT_ENTRY)] struct PatEntry; } impl PatEntry { ng! { ident, Ident, 0 } tg! { question_token, ? } ng! { default, Expr, 1 } } node! { #[from(NODE_IDENT_PARAM)] struct IdentParam; } impl IdentParam { ng! { ident, Ident, 0 } } node! { #[from( Pattern, IdentParam, )] enum Param; } node! { #[from(NODE_PATTERN)] struct Pattern; } impl Pattern { tg! { at_token, @ } ng! { pat_entries, [PatEntry] } tg! { ellipsis_token, ... } ng! { pat_bind, PatBind, 0 } } node! { #[from(NODE_ROOT)] struct Root; } impl Root { ng! { expr, Expr, 0 } } node! { #[from(NODE_ATTR_SET)] struct AttrSet; } impl HasEntry for AttrSet {} impl AttrSet { tg! { rec_token, rec } tg! { l_curly_token, '{' } tg! { r_curly_token, '}' } } node! { #[from( Inherit, AttrpathValue, )] enum Entry; } node! { #[from(NODE_ATTRPATH_VALUE)] struct AttrpathValue; } impl AttrpathValue { ng! { attrpath, Attrpath, 0 } tg! { assign_token, = } ng! { value, Expr, 0 } } node! { #[from(NODE_UNARY_OP)] struct UnaryOp; } impl UnaryOp { pub fn operator(&self) -> Option { children_tokens_u(self).find_map(|t| UnaryOpKind::from_kind(t.kind())) } ng! { expr, Expr, 0 } } node! { #[from(NODE_WITH)] struct With; } impl With { tg! { with_token, with } ng! { namespace, Expr, 0 } tg! { semicolon_token, ; } ng! { body, Expr, 1 } } node! { #[from(NODE_HAS_ATTR)] struct HasAttr; } impl HasAttr { ng! { expr, Expr, 0 } tg! { question_token, ? } ng! { attrpath, Attrpath, 0 } } node! { #[from(NODE_CUR_POS)] struct CurPos; } impl CurPos { tg! { cur_pos_token, __curPos } }