use tree_sitter::{Node, Tree};
use crate::ast::*;
use crate::error::ParseError;
pub(crate) fn tree_to_program(tree: &Tree, source: &[u8]) -> Result<Program, ParseError> {
let cx = Cx { source };
cx.program(tree.root_node())
}
struct Cx<'a> {
source: &'a [u8],
}
impl<'a> Cx<'a> {
fn text(&self, node: Node) -> String {
node.utf8_text(self.source).unwrap_or("").to_owned()
}
fn span(&self, node: Node) -> Span {
node.range().into()
}
fn unknown(&self, node: Node) -> ParseError {
ParseError::UnknownNode {
kind: node.kind().to_owned(),
range: node.range(),
}
}
fn missing(&self, parent: Node, field: &'static str) -> ParseError {
ParseError::MissingField {
parent_kind: parent.kind().to_owned(),
field,
range: parent.range(),
}
}
fn program(&self, node: Node) -> Result<Program, ParseError> {
if node.has_error() {
}
let mut statements = Vec::new();
let mut comments = Vec::new();
let mut cursor = node.walk();
for child in node.named_children(&mut cursor) {
match child.kind() {
"comment" => comments.push(self.comment(child)?),
k if is_statement_kind(k) => statements.push(self.statement(child)?),
_ => return Err(self.unknown(child)),
}
}
Ok(Program {
span: self.span(node),
statements,
comments,
})
}
fn statement(&self, node: Node) -> Result<Statement, ParseError> {
Ok(match node.kind() {
"command" => Statement::Command(self.command(node)?),
"pipeline" => Statement::Pipeline(self.pipeline(node)?),
"list" => Statement::List(self.list(node)?),
"compound_statement" => Statement::CompoundStatement(self.compound_statement(node)?),
"subshell" => Statement::Subshell(self.subshell(node)?),
"if_statement" => Statement::If(self.if_statement(node)?),
"while_statement" => Statement::While(self.while_statement(node)?),
"for_statement" => Statement::For(self.for_statement(node)?),
"c_style_for_statement" => Statement::CStyleFor(self.c_style_for_statement(node)?),
"case_statement" => Statement::Case(self.case_statement(node)?),
"function_definition" => Statement::FunctionDefinition(self.function_definition(node)?),
"redirected_statement" => Statement::Redirected(self.redirected_statement(node)?),
"declaration_command" => Statement::Declaration(self.declaration_command(node)?),
"unset_command" => Statement::Unset(self.unset_command(node)?),
"test_command" => Statement::Test(self.test_command(node)?),
"negated_command" => Statement::Negated(self.negated_command(node)?),
"variable_assignment" => Statement::VariableAssignment(self.variable_assignment(node)?),
"variable_assignments" => {
Statement::VariableAssignments(self.variable_assignments(node)?)
}
_ => return Err(self.unknown(node)),
})
}
fn command(&self, node: Node) -> Result<Command, ParseError> {
let name_node = node
.child_by_field_name("name")
.ok_or_else(|| self.missing(node, "name"))?;
let name = self.command_name(name_node)?;
let mut arguments = Vec::new();
let mut redirects = Vec::new();
let mut cursor = node.walk();
for (i, child) in node.children(&mut cursor).enumerate() {
let _ = i;
let _ = child;
}
let mut cursor = node.walk();
let mut leading_assignments = Vec::new();
let mut subshells = Vec::new();
if cursor.goto_first_child() {
loop {
let field = cursor.field_name();
let child = cursor.node();
match (field, child.kind(), child.is_named()) {
(Some("name"), _, _) => {}
(Some("argument"), _, _) => arguments.push(self.command_argument(child)?),
(Some("redirect"), _, _) => redirects.push(self.simple_redirect(child)?),
(None, "variable_assignment", true) => {
leading_assignments.push(self.variable_assignment(child)?)
}
(None, "subshell", true) => subshells.push(self.subshell(child)?),
(None, _, false) => {}
(None, _, true) => return Err(self.unknown(child)),
(Some(_), _, _) => {}
}
if !cursor.goto_next_sibling() {
break;
}
}
}
Ok(Command {
span: self.span(node),
name,
arguments,
redirects,
leading_assignments,
subshells,
})
}
fn command_name(&self, node: Node) -> Result<CommandName, ParseError> {
let mut cursor = node.walk();
let inner_node = node
.named_children(&mut cursor)
.next()
.ok_or_else(|| self.missing(node, "<inner>"))?;
let inner = if inner_node.kind() == "concatenation" {
CommandNameInner::Concatenation(self.concatenation(inner_node)?)
} else {
CommandNameInner::Primary(self.primary_expression(inner_node)?)
};
Ok(CommandName {
span: self.span(node),
inner,
})
}
fn command_argument(&self, node: Node) -> Result<CommandArgument, ParseError> {
Ok(match node.kind() {
"concatenation" => CommandArgument::Concatenation(self.concatenation(node)?),
"regex" => CommandArgument::Regex(self.regex(node)?),
"$" | "==" | "=~" => CommandArgument::Operator {
span: self.span(node),
text: self.text(node),
},
_ if is_primary_kind(node.kind()) => {
CommandArgument::Primary(self.primary_expression(node)?)
}
_ => return Err(self.unknown(node)),
})
}
fn pipeline(&self, node: Node) -> Result<Pipeline, ParseError> {
let mut stages = Vec::new();
let mut cursor = node.walk();
for child in node.named_children(&mut cursor) {
if is_statement_kind(child.kind()) {
stages.push(self.statement(child)?);
} else {
return Err(self.unknown(child));
}
}
Ok(Pipeline {
span: self.span(node),
stages,
})
}
fn list(&self, node: Node) -> Result<List, ParseError> {
let mut cursor = node.walk();
let mut left: Option<Statement> = None;
let mut operator: Option<ListOperator> = None;
let mut right: Option<Statement> = None;
if cursor.goto_first_child() {
loop {
let child = cursor.node();
if child.is_named() && is_statement_kind(child.kind()) {
let s = self.statement(child)?;
if left.is_none() {
left = Some(s);
} else {
right = Some(s);
}
} else if !child.is_named() {
let t = self.text(child);
match t.as_str() {
"&&" => operator = Some(ListOperator::And),
"||" => operator = Some(ListOperator::Or),
_ => {}
}
}
if !cursor.goto_next_sibling() {
break;
}
}
}
Ok(List {
span: self.span(node),
left: Box::new(left.ok_or_else(|| self.missing(node, "left"))?),
operator: operator.ok_or_else(|| self.missing(node, "operator"))?,
right: Box::new(right.ok_or_else(|| self.missing(node, "right"))?),
})
}
fn compound_statement(&self, node: Node) -> Result<CompoundStatement, ParseError> {
let mut statements = Vec::new();
let mut cursor = node.walk();
for child in node.named_children(&mut cursor) {
if child.kind() == "comment" {
continue;
}
statements.push(self.statement(child)?);
}
Ok(CompoundStatement {
span: self.span(node),
statements,
})
}
fn subshell(&self, node: Node) -> Result<Subshell, ParseError> {
let mut statements = Vec::new();
let mut cursor = node.walk();
for child in node.named_children(&mut cursor) {
if child.kind() == "comment" {
continue;
}
statements.push(self.statement(child)?);
}
Ok(Subshell {
span: self.span(node),
statements,
})
}
fn if_statement(&self, node: Node) -> Result<IfStatement, ParseError> {
let mut condition = Vec::new();
let mut body = Vec::new();
let mut elif_clauses = Vec::new();
let mut else_clause: Option<ElseClause> = None;
let mut cursor = node.walk();
if cursor.goto_first_child() {
loop {
let field = cursor.field_name();
let child = cursor.node();
match (field, child.kind()) {
(Some("condition"), _) => condition.push(self.condition_part(child)?),
(None, "elif_clause") => elif_clauses.push(self.elif_clause(child)?),
(None, "else_clause") => else_clause = Some(self.else_clause(child)?),
(None, k) if is_statement_kind(k) => body.push(self.statement(child)?),
(None, "comment") => {}
_ if !child.is_named() => {}
_ => {}
}
if !cursor.goto_next_sibling() {
break;
}
}
}
Ok(IfStatement {
span: self.span(node),
condition,
body,
elif_clauses,
else_clause,
})
}
fn elif_clause(&self, node: Node) -> Result<ElifClause, ParseError> {
let mut condition = Vec::new();
let mut body = Vec::new();
let mut cursor = node.walk();
if cursor.goto_first_child() {
loop {
let field = cursor.field_name();
let child = cursor.node();
match (field, child.kind()) {
(Some("condition"), _) => condition.push(self.condition_part(child)?),
(None, k) if is_statement_kind(k) => body.push(self.statement(child)?),
(None, "comment") => {}
_ if !child.is_named() => {}
_ => {}
}
if !cursor.goto_next_sibling() {
break;
}
}
}
Ok(ElifClause {
span: self.span(node),
condition,
body,
})
}
fn else_clause(&self, node: Node) -> Result<ElseClause, ParseError> {
let mut body = Vec::new();
let mut cursor = node.walk();
for child in node.named_children(&mut cursor) {
if child.kind() == "comment" {
continue;
}
body.push(self.statement(child)?);
}
Ok(ElseClause {
span: self.span(node),
body,
})
}
fn condition_part(&self, node: Node) -> Result<ConditionPart, ParseError> {
if is_statement_kind(node.kind()) {
Ok(ConditionPart::Statement(Box::new(self.statement(node)?)))
} else {
Ok(ConditionPart::Terminator {
span: self.span(node),
text: self.text(node),
})
}
}
fn while_statement(&self, node: Node) -> Result<WhileStatement, ParseError> {
let mut condition = Vec::new();
let mut body: Option<LoopBody> = None;
let mut cursor = node.walk();
if cursor.goto_first_child() {
loop {
let field = cursor.field_name();
let child = cursor.node();
match field {
Some("condition") => condition.push(self.condition_part(child)?),
Some("body") => body = Some(self.loop_body(child)?),
_ => {}
}
if !cursor.goto_next_sibling() {
break;
}
}
}
Ok(WhileStatement {
span: self.span(node),
condition,
body: body.ok_or_else(|| self.missing(node, "body"))?,
})
}
fn for_statement(&self, node: Node) -> Result<ForStatement, ParseError> {
let var_node = node
.child_by_field_name("variable")
.ok_or_else(|| self.missing(node, "variable"))?;
let body_node = node
.child_by_field_name("body")
.ok_or_else(|| self.missing(node, "body"))?;
let mut values = Vec::new();
let mut cursor = node.walk();
if cursor.goto_first_child() {
loop {
if cursor.field_name() == Some("value") {
values.push(self.case_pattern(cursor.node())?);
}
if !cursor.goto_next_sibling() {
break;
}
}
}
Ok(ForStatement {
span: self.span(node),
variable: self.variable_name(var_node)?,
values,
body: self.loop_body(body_node)?,
})
}
fn c_style_for_statement(&self, node: Node) -> Result<CStyleForStatement, ParseError> {
let body_node = node
.child_by_field_name("body")
.ok_or_else(|| self.missing(node, "body"))?;
let mut initializer = Vec::new();
let mut condition = Vec::new();
let mut update = Vec::new();
let mut cursor = node.walk();
if cursor.goto_first_child() {
loop {
let field = cursor.field_name();
let child = cursor.node();
let part = match field {
Some("initializer") | Some("condition") | Some("update") => {
self.c_style_for_part(child)?
}
_ => {
if !cursor.goto_next_sibling() {
break;
}
continue;
}
};
match field {
Some("initializer") => initializer.push(part),
Some("condition") => condition.push(part),
Some("update") => update.push(part),
_ => {}
}
if !cursor.goto_next_sibling() {
break;
}
}
}
Ok(CStyleForStatement {
span: self.span(node),
initializer,
condition,
update,
body: self.loop_body(body_node)?,
})
}
fn c_style_for_part(&self, node: Node) -> Result<CStyleForPart, ParseError> {
Ok(match node.kind() {
"," => CStyleForPart::Comma {
span: self.span(node),
},
"binary_expression" => CStyleForPart::Binary(self.binary_expression(node)?),
"command_substitution" => {
CStyleForPart::CommandSubstitution(self.command_substitution(node)?)
}
"expansion" => CStyleForPart::Expansion(self.expansion(node)?),
"number" => CStyleForPart::Number(self.number(node)?),
"parenthesized_expression" => {
CStyleForPart::Parenthesized(self.parenthesized_expression(node)?)
}
"postfix_expression" => CStyleForPart::Postfix(self.postfix_expression(node)?),
"simple_expansion" => CStyleForPart::SimpleExpansion(self.simple_expansion(node)?),
"string" => CStyleForPart::StringNode(self.string_node(node)?),
"unary_expression" => CStyleForPart::Unary(self.unary_expression(node)?),
"variable_assignment" => {
CStyleForPart::VariableAssignment(self.variable_assignment(node)?)
}
"word" => CStyleForPart::Word(self.word(node)?),
_ => return Err(self.unknown(node)),
})
}
fn case_statement(&self, node: Node) -> Result<CaseStatement, ParseError> {
let value_node = node
.child_by_field_name("value")
.ok_or_else(|| self.missing(node, "value"))?;
let value = self.case_pattern(value_node)?;
let mut items = Vec::new();
let mut cursor = node.walk();
for child in node.named_children(&mut cursor) {
if child.kind() == "case_item" {
items.push(self.case_item(child)?);
}
}
Ok(CaseStatement {
span: self.span(node),
value,
items,
})
}
fn case_item(&self, node: Node) -> Result<CaseItem, ParseError> {
let mut patterns = Vec::new();
let mut body = Vec::new();
let mut termination = None;
let mut fallthrough = None;
let mut cursor = node.walk();
if cursor.goto_first_child() {
loop {
let field = cursor.field_name();
let child = cursor.node();
match field {
Some("value") => patterns.push(self.case_pattern(child)?),
Some("termination") => termination = Some(CaseTermination::DoubleSemi),
Some("fallthrough") => {
fallthrough = Some(match self.text(child).as_str() {
";&" => CaseFallthrough::Semiamp,
";;&" => CaseFallthrough::DoubleSemiAmp,
_ => return Err(self.unknown(child)),
})
}
None if child.is_named() && is_statement_kind(child.kind()) => {
body.push(self.statement(child)?)
}
_ => {}
}
if !cursor.goto_next_sibling() {
break;
}
}
}
Ok(CaseItem {
span: self.span(node),
patterns,
body,
termination,
fallthrough,
})
}
fn case_pattern(&self, node: Node) -> Result<CasePattern, ParseError> {
Ok(match node.kind() {
"concatenation" => CasePattern::Concatenation(self.concatenation(node)?),
"extglob_pattern" => CasePattern::ExtglobPattern(self.extglob_pattern(node)?),
k if is_primary_kind(k) => CasePattern::Primary(self.primary_expression(node)?),
_ => return Err(self.unknown(node)),
})
}
fn function_definition(&self, node: Node) -> Result<FunctionDefinition, ParseError> {
let name_node = node
.child_by_field_name("name")
.ok_or_else(|| self.missing(node, "name"))?;
let body_node = node
.child_by_field_name("body")
.ok_or_else(|| self.missing(node, "body"))?;
let body = match body_node.kind() {
"compound_statement" => FunctionBody::Compound(self.compound_statement(body_node)?),
"if_statement" => FunctionBody::If(self.if_statement(body_node)?),
"subshell" => FunctionBody::Subshell(self.subshell(body_node)?),
"test_command" => FunctionBody::Test(self.test_command(body_node)?),
_ => return Err(self.unknown(body_node)),
};
let redirect = node
.child_by_field_name("redirect")
.map(|n| self.simple_redirect(n))
.transpose()?;
Ok(FunctionDefinition {
span: self.span(node),
name: self.word(name_node)?,
body,
redirect,
})
}
fn redirected_statement(&self, node: Node) -> Result<RedirectedStatement, ParseError> {
let body = node
.child_by_field_name("body")
.map(|n| -> Result<_, _> { Ok(Box::new(self.statement(n)?)) })
.transpose()?;
let mut redirects = Vec::new();
let mut bare_herestring = None;
let mut cursor = node.walk();
if cursor.goto_first_child() {
loop {
let field = cursor.field_name();
let child = cursor.node();
if field == Some("redirect") {
redirects.push(self.redirect(child)?);
} else if field.is_none() && child.kind() == "herestring_redirect" {
bare_herestring = Some(self.herestring_redirect(child)?);
}
if !cursor.goto_next_sibling() {
break;
}
}
}
Ok(RedirectedStatement {
span: self.span(node),
body,
redirects,
bare_herestring,
})
}
fn declaration_command(&self, node: Node) -> Result<DeclarationCommand, ParseError> {
let mut parts = Vec::new();
let mut cursor = node.walk();
for child in node.named_children(&mut cursor) {
parts.push(match child.kind() {
"word" => DeclarationPart::Word(self.word(child)?),
"variable_name" => DeclarationPart::VariableName(self.variable_name(child)?),
"variable_assignment" => {
DeclarationPart::VariableAssignment(self.variable_assignment(child)?)
}
_ => return Err(self.unknown(child)),
});
}
Ok(DeclarationCommand {
span: self.span(node),
parts,
})
}
fn unset_command(&self, node: Node) -> Result<UnsetCommand, ParseError> {
let mut parts = Vec::new();
let mut cursor = node.walk();
for child in node.named_children(&mut cursor) {
parts.push(match child.kind() {
"word" => UnsetPart::Word(self.word(child)?),
"variable_name" => UnsetPart::VariableName(self.variable_name(child)?),
_ => return Err(self.unknown(child)),
});
}
Ok(UnsetCommand {
span: self.span(node),
parts,
})
}
fn test_command(&self, node: Node) -> Result<TestCommand, ParseError> {
let mut expressions = Vec::new();
let mut cursor = node.walk();
for child in node.named_children(&mut cursor) {
expressions.push(self.expression(child)?);
}
Ok(TestCommand {
span: self.span(node),
expressions,
})
}
fn negated_command(&self, node: Node) -> Result<NegatedCommand, ParseError> {
let mut cursor = node.walk();
let inner = node
.named_children(&mut cursor)
.next()
.ok_or_else(|| self.missing(node, "<inner>"))?;
Ok(NegatedCommand {
span: self.span(node),
inner: Box::new(self.statement(inner)?),
})
}
fn variable_assignment(&self, node: Node) -> Result<VariableAssignment, ParseError> {
let name_node = node
.child_by_field_name("name")
.ok_or_else(|| self.missing(node, "name"))?;
let value_node = node
.child_by_field_name("value")
.ok_or_else(|| self.missing(node, "value"))?;
let name = match name_node.kind() {
"variable_name" => AssignmentTarget::VariableName(self.variable_name(name_node)?),
"subscript" => AssignmentTarget::Subscript(self.subscript(name_node)?),
_ => return Err(self.unknown(name_node)),
};
let value = self.assignment_value(value_node)?;
Ok(VariableAssignment {
span: self.span(node),
name,
value,
})
}
fn assignment_value(&self, node: Node) -> Result<AssignmentValue, ParseError> {
Ok(match node.kind() {
"array" => AssignmentValue::Array(self.array(node)?),
"binary_expression" => AssignmentValue::Binary(self.binary_expression(node)?),
"concatenation" => AssignmentValue::Concatenation(self.concatenation(node)?),
"parenthesized_expression" => {
AssignmentValue::Parenthesized(self.parenthesized_expression(node)?)
}
"postfix_expression" => AssignmentValue::Postfix(self.postfix_expression(node)?),
"unary_expression" => AssignmentValue::Unary(self.unary_expression(node)?),
"variable_assignment" => {
AssignmentValue::Nested(Box::new(self.variable_assignment(node)?))
}
k if is_primary_kind(k) => AssignmentValue::Primary(self.primary_expression(node)?),
_ => return Err(self.unknown(node)),
})
}
fn variable_assignments(&self, node: Node) -> Result<VariableAssignments, ParseError> {
let mut assignments = Vec::new();
let mut cursor = node.walk();
for child in node.named_children(&mut cursor) {
if child.kind() == "variable_assignment" {
assignments.push(self.variable_assignment(child)?);
} else {
return Err(self.unknown(child));
}
}
Ok(VariableAssignments {
span: self.span(node),
assignments,
})
}
fn expression(&self, node: Node) -> Result<Expression, ParseError> {
Ok(match node.kind() {
"binary_expression" => Expression::Binary(self.binary_expression(node)?),
"concatenation" => Expression::Concatenation(self.concatenation(node)?),
"parenthesized_expression" => {
Expression::Parenthesized(self.parenthesized_expression(node)?)
}
"postfix_expression" => Expression::Postfix(self.postfix_expression(node)?),
"ternary_expression" => Expression::Ternary(self.ternary_expression(node)?),
"unary_expression" => Expression::Unary(self.unary_expression(node)?),
k if is_primary_kind(k) => Expression::Primary(self.primary_expression(node)?),
_ => return Err(self.unknown(node)),
})
}
fn primary_expression(&self, node: Node) -> Result<PrimaryExpression, ParseError> {
Ok(match node.kind() {
"ansi_c_string" => PrimaryExpression::AnsiCString(self.ansi_c_string(node)?),
"arithmetic_expansion" => {
PrimaryExpression::ArithmeticExpansion(self.arithmetic_expansion(node)?)
}
"brace_expression" => PrimaryExpression::BraceExpression(self.brace_expression(node)?),
"command_substitution" => {
PrimaryExpression::CommandSubstitution(self.command_substitution(node)?)
}
"expansion" => PrimaryExpression::Expansion(self.expansion(node)?),
"number" => PrimaryExpression::Number(self.number(node)?),
"process_substitution" => {
PrimaryExpression::ProcessSubstitution(self.process_substitution(node)?)
}
"raw_string" => PrimaryExpression::RawString(self.raw_string(node)?),
"simple_expansion" => PrimaryExpression::SimpleExpansion(self.simple_expansion(node)?),
"string" => PrimaryExpression::StringNode(self.string_node(node)?),
"translated_string" => {
PrimaryExpression::TranslatedString(self.translated_string(node)?)
}
"word" => PrimaryExpression::Word(self.word(node)?),
_ => return Err(self.unknown(node)),
})
}
fn binary_expression(&self, node: Node) -> Result<BinaryExpression, ParseError> {
let left = node
.child_by_field_name("left")
.map(|n| self.test_operand(n))
.transpose()?;
let operator_node = node
.child_by_field_name("operator")
.ok_or_else(|| self.missing(node, "operator"))?;
let operator = self.binary_operator(operator_node);
let mut right = Vec::new();
let mut extras = Vec::new();
let mut cursor = node.walk();
if cursor.goto_first_child() {
loop {
let field = cursor.field_name();
let child = cursor.node();
match field {
Some("right") => right.push(self.binary_right_operand(child)?),
Some(_) => {}
None if !child.is_named() => {}
None => extras.push(match child.kind() {
"binary_expression" => {
BinaryExtra::Binary(Box::new(self.binary_expression(child)?))
}
"expansion" => BinaryExtra::Expansion(self.expansion(child)?),
"number" => BinaryExtra::Number(self.number(child)?),
"variable_name" => BinaryExtra::VariableName(self.variable_name(child)?),
_ => return Err(self.unknown(child)),
}),
}
if !cursor.goto_next_sibling() {
break;
}
}
}
Ok(BinaryExpression {
span: self.span(node),
left,
operator,
right,
extras,
})
}
fn binary_operator(&self, node: Node) -> BinaryOperator {
let text = self.text(node);
let kind = match text.as_str() {
"!=" => BinaryOperatorKind::NotEq,
"%" => BinaryOperatorKind::Mod,
"%=" => BinaryOperatorKind::ModAssign,
"&" => BinaryOperatorKind::BitAnd,
"&&" => BinaryOperatorKind::AndAnd,
"&=" => BinaryOperatorKind::BitAndAssign,
"*" => BinaryOperatorKind::Mul,
"**" => BinaryOperatorKind::Pow,
"**=" => BinaryOperatorKind::PowAssign,
"*=" => BinaryOperatorKind::MulAssign,
"+" => BinaryOperatorKind::Plus,
"+=" => BinaryOperatorKind::PlusAssign,
"-" => BinaryOperatorKind::Minus,
"-=" => BinaryOperatorKind::MinusAssign,
"-a" => BinaryOperatorKind::DashA,
"-o" => BinaryOperatorKind::DashO,
"/" => BinaryOperatorKind::Div,
"/=" => BinaryOperatorKind::DivAssign,
"<" => BinaryOperatorKind::Lt,
"<<" => BinaryOperatorKind::ShlOrHeredoc,
"<<=" => BinaryOperatorKind::ShlAssign,
"<=" => BinaryOperatorKind::Le,
"=" => BinaryOperatorKind::Assign,
"==" => BinaryOperatorKind::EqEq,
"=~" => BinaryOperatorKind::MatchRegex,
">" => BinaryOperatorKind::Gt,
">=" => BinaryOperatorKind::Ge,
">>" => BinaryOperatorKind::ShrOrHeredocAppend,
">>=" => BinaryOperatorKind::ShrAssign,
"^" => BinaryOperatorKind::BitXor,
"^=" => BinaryOperatorKind::BitXorAssign,
"|" => BinaryOperatorKind::BitOr,
"|=" => BinaryOperatorKind::BitOrAssign,
"||" => BinaryOperatorKind::OrOr,
_ if node.kind() == "test_operator" => BinaryOperatorKind::TestOp,
_ => BinaryOperatorKind::TestOp,
};
BinaryOperator {
span: self.span(node),
kind,
text,
}
}
fn test_operand(&self, node: Node) -> Result<TestOperand, ParseError> {
Ok(match node.kind() {
"command_substitution" => TestOperand::CommandSubstitution(self.command_substitution(node)?),
"expansion" => TestOperand::Expansion(self.expansion(node)?),
"number" => TestOperand::Number(self.number(node)?),
"simple_expansion" => TestOperand::SimpleExpansion(self.simple_expansion(node)?),
"string" => TestOperand::StringNode(self.string_node(node)?),
"subscript" => TestOperand::Subscript(Box::new(self.subscript(node)?)),
"variable_name" => TestOperand::VariableName(self.variable_name(node)?),
_ => TestOperand::Expression(Box::new(self.expression(node)?)),
})
}
fn binary_right_operand(&self, node: Node) -> Result<BinaryRightOperand, ParseError> {
Ok(match node.kind() {
"command_substitution" => {
BinaryRightOperand::CommandSubstitution(self.command_substitution(node)?)
}
"expansion" => BinaryRightOperand::Expansion(self.expansion(node)?),
"extglob_pattern" => BinaryRightOperand::ExtglobPattern(self.extglob_pattern(node)?),
"number" => BinaryRightOperand::Number(self.number(node)?),
"regex" => BinaryRightOperand::Regex(self.regex(node)?),
"simple_expansion" => BinaryRightOperand::SimpleExpansion(self.simple_expansion(node)?),
"string" => BinaryRightOperand::StringNode(self.string_node(node)?),
"subscript" => BinaryRightOperand::Subscript(Box::new(self.subscript(node)?)),
"variable_name" => BinaryRightOperand::VariableName(self.variable_name(node)?),
_ => BinaryRightOperand::Expression(Box::new(self.expression(node)?)),
})
}
fn concatenation(&self, node: Node) -> Result<Concatenation, ParseError> {
let mut parts = Vec::new();
let mut cursor = node.walk();
for child in node.named_children(&mut cursor) {
parts.push(self.primary_expression(child)?);
}
Ok(Concatenation {
span: self.span(node),
parts,
})
}
fn parenthesized_expression(
&self,
node: Node,
) -> Result<ParenthesizedExpression, ParseError> {
let mut cursor = node.walk();
let inner = node
.named_children(&mut cursor)
.next()
.ok_or_else(|| self.missing(node, "<inner>"))?;
Ok(ParenthesizedExpression {
span: self.span(node),
inner: Box::new(self.expression(inner)?),
})
}
fn postfix_expression(&self, node: Node) -> Result<PostfixExpression, ParseError> {
let op_node = node
.child_by_field_name("operator")
.ok_or_else(|| self.missing(node, "operator"))?;
let operator = match self.text(op_node).as_str() {
"++" => PostfixOperator::Inc,
"--" => PostfixOperator::Dec,
_ => return Err(self.unknown(op_node)),
};
let mut cursor = node.walk();
let inner_node = node
.named_children(&mut cursor)
.find(|n| n.id() != op_node.id())
.ok_or_else(|| self.missing(node, "<inner>"))?;
Ok(PostfixExpression {
span: self.span(node),
inner: self.test_operand(inner_node)?,
operator,
})
}
fn ternary_expression(&self, node: Node) -> Result<TernaryExpression, ParseError> {
let cond = node
.child_by_field_name("condition")
.ok_or_else(|| self.missing(node, "condition"))?;
let cons = node
.child_by_field_name("consequence")
.ok_or_else(|| self.missing(node, "consequence"))?;
let alt = node
.child_by_field_name("alternative")
.ok_or_else(|| self.missing(node, "alternative"))?;
Ok(TernaryExpression {
span: self.span(node),
condition: self.test_operand(cond)?,
consequence: self.test_operand(cons)?,
alternative: self.test_operand(alt)?,
})
}
fn unary_expression(&self, node: Node) -> Result<UnaryExpression, ParseError> {
let op_node = node
.child_by_field_name("operator")
.ok_or_else(|| self.missing(node, "operator"))?;
let text = self.text(op_node);
let kind = match text.as_str() {
"!" => UnaryOperatorKind::Not,
"+" => UnaryOperatorKind::Plus,
"++" => UnaryOperatorKind::Inc,
"-" => UnaryOperatorKind::Minus,
"--" => UnaryOperatorKind::Dec,
"~" => UnaryOperatorKind::BitNot,
_ => UnaryOperatorKind::TestOp,
};
let operator = UnaryOperator {
span: self.span(op_node),
kind,
text,
};
let mut cursor = node.walk();
let inner_node = node
.named_children(&mut cursor)
.find(|n| n.id() != op_node.id())
.ok_or_else(|| self.missing(node, "<inner>"))?;
Ok(UnaryExpression {
span: self.span(node),
operator,
inner: self.test_operand(inner_node)?,
})
}
fn ansi_c_string(&self, node: Node) -> Result<AnsiCString, ParseError> {
Ok(AnsiCString {
span: self.span(node),
text: self.text(node),
})
}
fn arithmetic_expansion(&self, node: Node) -> Result<ArithmeticExpansion, ParseError> {
let mut inner = Vec::new();
let mut cursor = node.walk();
for child in node.named_children(&mut cursor) {
inner.push(self.expression(child)?);
}
Ok(ArithmeticExpansion {
span: self.span(node),
inner,
})
}
fn brace_expression(&self, node: Node) -> Result<BraceExpression, ParseError> {
Ok(BraceExpression {
span: self.span(node),
text: self.text(node),
})
}
fn command_substitution(&self, node: Node) -> Result<CommandSubstitution, ParseError> {
let mut body = Vec::new();
let mut redirect = None;
let mut cursor = node.walk();
if cursor.goto_first_child() {
loop {
let field = cursor.field_name();
let child = cursor.node();
if field == Some("redirect") {
redirect = Some(self.file_redirect(child)?);
} else if child.is_named() && is_statement_kind(child.kind()) {
body.push(self.statement(child)?);
}
if !cursor.goto_next_sibling() {
break;
}
}
}
Ok(CommandSubstitution {
span: self.span(node),
body,
redirect,
})
}
fn expansion(&self, node: Node) -> Result<Expansion, ParseError> {
let mut operators = Vec::new();
let mut elements = Vec::new();
let mut cursor = node.walk();
if cursor.goto_first_child() {
loop {
let field = cursor.field_name();
let child = cursor.node();
if field == Some("operator") {
operators.push(ExpansionOperator {
span: self.span(child),
text: self.text(child),
});
} else if child.is_named() {
elements.push(match child.kind() {
"array" => ExpansionElement::Array(self.array(child)?),
"binary_expression" => {
ExpansionElement::Binary(self.binary_expression(child)?)
}
"concatenation" => ExpansionElement::Concatenation(self.concatenation(child)?),
"parenthesized_expression" => ExpansionElement::Parenthesized(
self.parenthesized_expression(child)?,
),
"regex" => ExpansionElement::Regex(self.regex(child)?),
"special_variable_name" => ExpansionElement::SpecialVariableName(
self.special_variable_name(child)?,
),
"subscript" => ExpansionElement::Subscript(self.subscript(child)?),
"variable_name" => ExpansionElement::VariableName(self.variable_name(child)?),
k if is_primary_kind(k) => {
ExpansionElement::Primary(self.primary_expression(child)?)
}
_ => return Err(self.unknown(child)),
});
}
if !cursor.goto_next_sibling() {
break;
}
}
}
Ok(Expansion {
span: self.span(node),
operators,
elements,
})
}
fn number(&self, node: Node) -> Result<Number, ParseError> {
Ok(Number {
span: self.span(node),
text: self.text(node),
})
}
fn process_substitution(&self, node: Node) -> Result<ProcessSubstitution, ParseError> {
let mut direction = '<';
let mut body = Vec::new();
let mut cursor = node.walk();
if cursor.goto_first_child() {
loop {
let child = cursor.node();
if !child.is_named() {
let t = self.text(child);
if t.starts_with("<(") {
direction = '<';
} else if t.starts_with(">(") {
direction = '>';
}
} else if is_statement_kind(child.kind()) {
body.push(self.statement(child)?);
}
if !cursor.goto_next_sibling() {
break;
}
}
}
Ok(ProcessSubstitution {
span: self.span(node),
body,
direction,
})
}
fn raw_string(&self, node: Node) -> Result<RawString, ParseError> {
Ok(RawString {
span: self.span(node),
text: self.text(node),
})
}
fn simple_expansion(&self, node: Node) -> Result<SimpleExpansion, ParseError> {
let mut cursor = node.walk();
let inner = node
.named_children(&mut cursor)
.next()
.ok_or_else(|| self.missing(node, "<element>"))?;
let element = match inner.kind() {
"variable_name" => SimpleExpansionElement::VariableName(self.variable_name(inner)?),
"special_variable_name" => {
SimpleExpansionElement::SpecialVariableName(self.special_variable_name(inner)?)
}
_ => return Err(self.unknown(inner)),
};
Ok(SimpleExpansion {
span: self.span(node),
element,
})
}
fn string_node(&self, node: Node) -> Result<StringNode, ParseError> {
let parts = self.string_parts(node)?;
Ok(StringNode {
span: self.span(node),
parts,
})
}
fn translated_string(&self, node: Node) -> Result<TranslatedString, ParseError> {
let parts = self.string_parts(node)?;
Ok(TranslatedString {
span: self.span(node),
parts,
})
}
fn string_parts(&self, node: Node) -> Result<Vec<StringPart>, ParseError> {
let mut parts = Vec::new();
let mut cursor = node.walk();
for child in node.named_children(&mut cursor) {
parts.push(match child.kind() {
"string_content" => StringPart::Content(StringContent {
span: self.span(child),
text: self.text(child),
}),
"expansion" => StringPart::Expansion(self.expansion(child)?),
"simple_expansion" => StringPart::SimpleExpansion(self.simple_expansion(child)?),
"command_substitution" => {
StringPart::CommandSubstitution(self.command_substitution(child)?)
}
"arithmetic_expansion" => {
StringPart::ArithmeticExpansion(self.arithmetic_expansion(child)?)
}
_ => StringPart::Raw {
span: self.span(child),
text: self.text(child),
},
});
}
Ok(parts)
}
fn word(&self, node: Node) -> Result<Word, ParseError> {
Ok(Word {
span: self.span(node),
text: self.text(node),
})
}
fn redirect(&self, node: Node) -> Result<Redirect, ParseError> {
Ok(match node.kind() {
"file_redirect" => Redirect::File(self.file_redirect(node)?),
"heredoc_redirect" => Redirect::Heredoc(self.heredoc_redirect(node)?),
"herestring_redirect" => Redirect::Herestring(self.herestring_redirect(node)?),
_ => return Err(self.unknown(node)),
})
}
fn simple_redirect(&self, node: Node) -> Result<SimpleRedirect, ParseError> {
Ok(match node.kind() {
"file_redirect" => SimpleRedirect::File(self.file_redirect(node)?),
"herestring_redirect" => SimpleRedirect::Herestring(self.herestring_redirect(node)?),
_ => return Err(self.unknown(node)),
})
}
fn file_redirect(&self, node: Node) -> Result<FileRedirect, ParseError> {
let descriptor = node
.child_by_field_name("descriptor")
.map(|n| self.file_descriptor(n))
.transpose()?;
let mut destinations = Vec::new();
let mut operator = String::new();
let mut cursor = node.walk();
if cursor.goto_first_child() {
loop {
let field = cursor.field_name();
let child = cursor.node();
if field == Some("destination") {
destinations.push(match child.kind() {
"concatenation" => RedirectDestination::Concatenation(self.concatenation(child)?),
k if is_primary_kind(k) => {
RedirectDestination::Primary(self.primary_expression(child)?)
}
_ => return Err(self.unknown(child)),
});
} else if !child.is_named() && operator.is_empty() {
operator = self.text(child);
}
if !cursor.goto_next_sibling() {
break;
}
}
}
Ok(FileRedirect {
span: self.span(node),
descriptor,
operator,
destinations,
})
}
fn heredoc_redirect(&self, node: Node) -> Result<HeredocRedirect, ParseError> {
let descriptor = node
.child_by_field_name("descriptor")
.map(|n| self.file_descriptor(n))
.transpose()?;
let operator = node
.child_by_field_name("operator")
.map(|n| match self.text(n).as_str() {
"&&" => Ok(HeredocOperator::AndAnd),
"||" => Ok(HeredocOperator::OrOr),
_ => Err(self.unknown(n)),
})
.transpose()?;
let mut argument = Vec::new();
let mut redirects = Vec::new();
let mut right: Option<Box<Statement>> = None;
let mut start = None;
let mut body = None;
let mut end = None;
let mut pipelines = Vec::new();
let mut cursor = node.walk();
if cursor.goto_first_child() {
loop {
let field = cursor.field_name();
let child = cursor.node();
match (field, child.kind(), child.is_named()) {
(Some("argument"), k, _) => argument.push(match k {
"concatenation" => RedirectDestination::Concatenation(self.concatenation(child)?),
kk if is_primary_kind(kk) => {
RedirectDestination::Primary(self.primary_expression(child)?)
}
_ => return Err(self.unknown(child)),
}),
(Some("redirect"), _, _) => redirects.push(self.simple_redirect(child)?),
(Some("right"), _, _) => right = Some(Box::new(self.statement(child)?)),
(None, "heredoc_start", true) => {
start = Some(HeredocStart {
span: self.span(child),
text: self.text(child),
})
}
(None, "heredoc_body", true) => {
body = Some(HeredocBody {
span: self.span(child),
text: self.text(child),
})
}
(None, "heredoc_end", true) => {
end = Some(HeredocEnd {
span: self.span(child),
text: self.text(child),
})
}
(None, "pipeline", true) => pipelines.push(self.pipeline(child)?),
_ => {}
}
if !cursor.goto_next_sibling() {
break;
}
}
}
Ok(HeredocRedirect {
span: self.span(node),
descriptor,
operator,
argument,
redirects,
right,
start,
body,
end,
pipelines,
})
}
fn herestring_redirect(&self, node: Node) -> Result<HerestringRedirect, ParseError> {
let descriptor = node
.child_by_field_name("descriptor")
.map(|n| self.file_descriptor(n))
.transpose()?;
let mut value: Option<HerestringValue> = None;
let mut cursor = node.walk();
for child in node.named_children(&mut cursor) {
if child.kind() == "file_descriptor" {
continue;
}
value = Some(match child.kind() {
"concatenation" => HerestringValue::Concatenation(self.concatenation(child)?),
k if is_primary_kind(k) => HerestringValue::Primary(self.primary_expression(child)?),
_ => return Err(self.unknown(child)),
});
}
Ok(HerestringRedirect {
span: self.span(node),
descriptor,
value: value.ok_or_else(|| self.missing(node, "<value>"))?,
})
}
fn array(&self, node: Node) -> Result<Array, ParseError> {
let mut elements = Vec::new();
let mut cursor = node.walk();
for child in node.named_children(&mut cursor) {
elements.push(match child.kind() {
"concatenation" => ArrayElement::Concatenation(self.concatenation(child)?),
"variable_assignment" => {
ArrayElement::VariableAssignment(self.variable_assignment(child)?)
}
k if is_primary_kind(k) => ArrayElement::Primary(self.primary_expression(child)?),
_ => return Err(self.unknown(child)),
});
}
Ok(Array {
span: self.span(node),
elements,
})
}
fn do_group(&self, node: Node) -> Result<DoGroup, ParseError> {
let mut statements = Vec::new();
let mut cursor = node.walk();
for child in node.named_children(&mut cursor) {
if child.kind() == "comment" {
continue;
}
statements.push(self.statement(child)?);
}
Ok(DoGroup {
span: self.span(node),
statements,
})
}
fn loop_body(&self, node: Node) -> Result<LoopBody, ParseError> {
Ok(match node.kind() {
"compound_statement" => LoopBody::Compound(self.compound_statement(node)?),
"do_group" => LoopBody::DoGroup(self.do_group(node)?),
_ => return Err(self.unknown(node)),
})
}
fn subscript(&self, node: Node) -> Result<Subscript, ParseError> {
let name_node = node
.child_by_field_name("name")
.ok_or_else(|| self.missing(node, "name"))?;
let index_node = node
.child_by_field_name("index")
.ok_or_else(|| self.missing(node, "index"))?;
let index = match index_node.kind() {
"binary_expression" => SubscriptIndex::Binary(self.binary_expression(index_node)?),
"concatenation" => SubscriptIndex::Concatenation(self.concatenation(index_node)?),
"parenthesized_expression" => {
SubscriptIndex::Parenthesized(self.parenthesized_expression(index_node)?)
}
"unary_expression" => SubscriptIndex::Unary(self.unary_expression(index_node)?),
k if is_primary_kind(k) => SubscriptIndex::Primary(self.primary_expression(index_node)?),
_ => return Err(self.unknown(index_node)),
};
Ok(Subscript {
span: self.span(node),
name: self.variable_name(name_node)?,
index,
})
}
fn comment(&self, node: Node) -> Result<Comment, ParseError> {
Ok(Comment {
span: self.span(node),
text: self.text(node),
})
}
fn extglob_pattern(&self, node: Node) -> Result<ExtglobPattern, ParseError> {
Ok(ExtglobPattern {
span: self.span(node),
text: self.text(node),
})
}
fn file_descriptor(&self, node: Node) -> Result<FileDescriptor, ParseError> {
Ok(FileDescriptor {
span: self.span(node),
text: self.text(node),
})
}
fn regex(&self, node: Node) -> Result<Regex, ParseError> {
Ok(Regex {
span: self.span(node),
text: self.text(node),
})
}
fn special_variable_name(&self, node: Node) -> Result<SpecialVariableName, ParseError> {
Ok(SpecialVariableName {
span: self.span(node),
text: self.text(node),
})
}
fn variable_name(&self, node: Node) -> Result<VariableName, ParseError> {
Ok(VariableName {
span: self.span(node),
text: self.text(node),
})
}
}
fn is_statement_kind(kind: &str) -> bool {
matches!(
kind,
"command"
| "pipeline"
| "list"
| "compound_statement"
| "subshell"
| "if_statement"
| "while_statement"
| "for_statement"
| "c_style_for_statement"
| "case_statement"
| "function_definition"
| "redirected_statement"
| "declaration_command"
| "unset_command"
| "test_command"
| "negated_command"
| "variable_assignment"
| "variable_assignments"
)
}
fn is_primary_kind(kind: &str) -> bool {
matches!(
kind,
"ansi_c_string"
| "arithmetic_expansion"
| "brace_expression"
| "command_substitution"
| "expansion"
| "number"
| "process_substitution"
| "raw_string"
| "simple_expansion"
| "string"
| "translated_string"
| "word"
)
}