1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
//! Provides a type system for the AST, in some sense

mod expr_ext;
mod interpol;
mod nodes;
mod operators;
mod path_util;
mod str_util;
mod tokens;

use crate::{NixLanguage, SyntaxKind, SyntaxToken};

pub use expr_ext::LiteralKind;
pub use interpol::*;
pub use nodes::*;
pub use operators::{BinOpKind, UnaryOpKind};
pub use tokens::*;

pub trait AstNode: rowan::ast::AstNode<Language = NixLanguage> {}

impl<T> AstNode for T where T: rowan::ast::AstNode<Language = NixLanguage> {}

pub trait AstToken {
    fn can_cast(from: SyntaxKind) -> bool
    where
        Self: Sized;

    /// Cast an untyped token into this strongly-typed token. This will return
    /// None if the type was not correct.
    fn cast(from: SyntaxToken) -> Option<Self>
    where
        Self: Sized;

    fn syntax(&self) -> &SyntaxToken;
}

mod support {
    use rowan::ast::AstChildren;

    use super::{AstNode, AstToken};
    use crate::{SyntaxElement, SyntaxKind, SyntaxToken};

    pub(super) fn nth<N: AstNode, NN: AstNode>(parent: &N, n: usize) -> Option<NN> {
        parent.syntax().children().flat_map(NN::cast).nth(n)
    }

    pub(super) fn children<N: AstNode, NN: AstNode>(parent: &N) -> AstChildren<NN> {
        rowan::ast::support::children(parent.syntax())
    }

    pub(super) fn token<N: AstNode, T: AstToken>(parent: &N) -> Option<T> {
        children_tokens(parent).nth(0)
    }

    /// Token untyped
    pub(super) fn token_u<N: AstNode>(parent: &N, kind: SyntaxKind) -> Option<SyntaxToken> {
        children_tokens_u(parent).find(|it| it.kind() == kind)
    }

    pub(super) fn children_tokens<N: AstNode, T: AstToken>(parent: &N) -> impl Iterator<Item = T> {
        parent
            .syntax()
            .children_with_tokens()
            .filter_map(SyntaxElement::into_token)
            .filter_map(T::cast)
    }

    pub(super) fn children_tokens_u<N: AstNode>(parent: &N) -> impl Iterator<Item = SyntaxToken> {
        parent.syntax().children_with_tokens().filter_map(SyntaxElement::into_token)
    }
}