use rowan::ast::AstNode; use crate::{FileId, ast}; pub fn parse_file(file_id: FileId, source: &str) -> ast::ParsedFile { let parse = rnix::Root::parse(source); let source_len = source.len(); let (nodes, tokens) = convert_syntax(parse.syntax()); ast::ParsedFile { file_id, nodes, tokens, errors: parse .errors() .iter() .map(|error| convert_parse_error(error, source_len)) .collect(), } } fn convert_syntax(syntax: rnix::SyntaxNode) -> (Vec, Vec) { let mut nodes = Vec::new(); let mut tokens = Vec::new(); for element in syntax.descendants_with_tokens() { match element { rnix::NodeOrToken::Node(node) => nodes.extend(convert_node(node)), rnix::NodeOrToken::Token(token) => tokens.extend(convert_token(token)), } } (nodes, tokens) } fn convert_node(node: rnix::SyntaxNode) -> Option { match node.kind() { rnix::SyntaxKind::NODE_APPLY => convert_apply(node), rnix::SyntaxKind::NODE_INHERIT => convert_inherit(node), _ => None, } } fn convert_apply(node: rnix::SyntaxNode) -> Option { let apply = rnix::ast::Apply::cast(node)?; let range = apply.syntax().text_range().into(); let data = apply.try_into().ok()?; Some(ast::Node { range, data: ast::NodeData::Apply(data), }) } impl TryFrom for ast::Apply { type Error = (); fn try_from(apply: rnix::ast::Apply) -> Result { Ok(Self { function: apply.lambda().ok_or(())?.try_into()?, }) } } impl TryFrom for ast::Expr { type Error = (); fn try_from(expr: rnix::ast::Expr) -> Result { match expr { rnix::ast::Expr::Ident(ident) => Ok(Self::Ident(ident.into())), rnix::ast::Expr::Select(select) => Ok(Self::Select(Box::new(select.try_into()?))), _ => Err(()), } } } impl From for ast::Ident { fn from(ident: rnix::ast::Ident) -> Self { Self { name: ident.to_string(), } } } impl TryFrom for ast::Select { type Error = (); fn try_from(select: rnix::ast::Select) -> Result { Ok(Self { receiver: select.expr().ok_or(())?.try_into()?, attrs: select .attrpath() .ok_or(())? .attrs() .map(|attr| match attr { rnix::ast::Attr::Ident(ident) => Ok(ident.into()), _ => Err(()), }) .collect::, _>>()?, }) } } fn convert_inherit(node: rnix::SyntaxNode) -> Option { let inherit = rnix::ast::Inherit::cast(node)?; Some(ast::Node { range: inherit.syntax().text_range().into(), data: ast::NodeData::Inherit(inherit.into()), }) } impl From for ast::Inherit { fn from(inherit: rnix::ast::Inherit) -> Self { Self { attr_count: inherit.attrs().count(), } } } fn convert_token(token: rnix::SyntaxToken) -> Option { match token.kind() { rnix::SyntaxKind::TOKEN_URI => Some(ast::Token { range: token.text_range().into(), data: ast::TokenData::UnquotedUri(token.text().to_string()), }), _ => None, } } fn convert_parse_error(error: &rnix::ParseError, source_len: usize) -> ast::ParseError { ast::ParseError { range: parse_error_range(error, source_len), message: error.to_string(), } } fn parse_error_range(error: &rnix::ParseError, source_len: usize) -> ast::SourceRange { match error { rnix::ParseError::Unexpected(range) | rnix::ParseError::UnexpectedExtra(range) | rnix::ParseError::UnexpectedWanted(_, range, _) | rnix::ParseError::UnexpectedDoubleBind(range) | rnix::ParseError::DuplicatedArgs(range, _) => (*range).into(), rnix::ParseError::UnexpectedEOF | rnix::ParseError::UnexpectedEOFWanted(_) | rnix::ParseError::RecursionLimitExceeded => ast::SourceRange { start: source_len, end: source_len, }, _ => ast::SourceRange { start: source_len, end: source_len, }, } } impl From for ast::SourceRange { fn from(range: rnix::TextRange) -> Self { Self { start: usize::from(range.start()), end: usize::from(range.end()), } } }