#[macro_use] mod macros; pub mod ast; mod kinds; mod parser; #[cfg(test)] mod tests; mod token_set; mod tokenizer; use std::marker::PhantomData; use self::kinds::SYNTAX_KIND_MAX; pub use self::{kinds::ParseSyntaxKindError, kinds::SyntaxKind, tokenizer::tokenize}; use ast::AstNode; pub use parser::ParseError; use rowan::GreenNode; pub use rowan::{NodeOrToken, TextRange, TextSize, TokenAtOffset, WalkEvent}; pub(crate) use token_set::TokenSet; pub use tokenizer::Token; #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum NixLanguage {} impl rowan::Language for NixLanguage { type Kind = SyntaxKind; fn kind_from_raw(raw: rowan::SyntaxKind) -> Self::Kind { let discriminant: u16 = raw.0; assert!(discriminant <= SYNTAX_KIND_MAX); unsafe { std::mem::transmute::(discriminant) } } fn kind_to_raw(kind: Self::Kind) -> rowan::SyntaxKind { rowan::SyntaxKind(kind as u16) } } pub type SyntaxNode = rowan::SyntaxNode; pub type SyntaxToken = rowan::SyntaxToken; pub type SyntaxElement = rowan::NodeOrToken; pub type SyntaxElementChildren = rowan::SyntaxElementChildren; pub type SyntaxNodeChildren = rowan::SyntaxNodeChildren; pub use ast::Root; impl Root { pub fn parse(s: &str) -> Parse { let (green, errors) = parser::parse(tokenize(s)); Parse { green, errors, _ty: PhantomData } } } /// The result of a parse #[derive(Clone)] pub struct Parse { green: GreenNode, errors: Vec, _ty: PhantomData T>, } impl Parse { pub fn syntax(&self) -> SyntaxNode { SyntaxNode::new_root(self.green.clone()) } } impl Parse { pub fn tree(&self) -> T { T::cast(self.syntax()).unwrap() } /// Return all errors in the tree, if any pub fn errors(&self) -> &[ParseError] { &self.errors } /// Either return the first error in the tree, or if there are none return self pub fn ok(self) -> Result { if let Some(err) = self.errors().first() { return Err(err.clone()); } Ok(self.tree()) } } /// Matches a `SyntaxNode` against an `ast` type. /// /// # Example: /// /// ```ignore /// match_ast! { /// match node { /// ast::Apply(it) => { ... }, /// ast::Assert(it) => { ... }, /// ast::IfElse(it) => { ... }, /// _ => None, /// } /// } /// ``` #[macro_export] macro_rules! match_ast { (match $node:ident { $($tt:tt)* }) => { match_ast!(match ($node) { $($tt)* }) }; (match ($node:expr) { $( ast::$ast:ident($it:pat) => $res:expr, )* _ => $catch_all:expr $(,)? }) => {{ $( if let Some($it) = ast::$ast::cast($node.clone()) { $res } else )* { $catch_all } }}; }