use rnix::ast::{Attr, HasEntry}; 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_ATTRPATH_VALUE => convert_attrpath_value(node), rnix::SyntaxKind::NODE_LET_IN => convert_let_in(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()?, }) } } fn convert_attrpath_value(node: rnix::SyntaxNode) -> Option { let attrpath_value = rnix::ast::AttrpathValue::cast(node)?; let range = attrpath_value.syntax().text_range().into(); let data = attrpath_value.try_into().ok()?; Some(ast::Node { range, data: ast::NodeData::AttrpathValue(data), }) } impl TryFrom for ast::AttrpathValue { type Error = (); fn try_from(attrpath_value: rnix::ast::AttrpathValue) -> Result { let mut attrs = attrpath_value.attrpath().ok_or(())?.attrs(); let key = match attrs.next().ok_or(())? { Attr::Ident(ident) => ident.into(), _ => return Err(()), }; if attrs.next().is_some() { return Err(()); } let value = match attrpath_value.value().ok_or(())? { rnix::ast::Expr::Ident(ident) => ident.into(), _ => return Err(()), }; Ok(Self { key, value }) } } 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_let_in(node: rnix::SyntaxNode) -> Option { let let_in = rnix::ast::LetIn::cast(node)?; Some(ast::Node { range: let_in.syntax().text_range().into(), data: ast::NodeData::LetIn(let_in.into()), }) } impl From for ast::LetIn { fn from(let_in: rnix::ast::LetIn) -> Self { Self { binding_count: let_in.attrpath_values().count() + let_in .inherits() .map(|inherit| inherit.attrs().count()) .sum::(), } } } 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()), } } }