function parseStatement()

in packages/shell-parser/src/parser.ts [618:705]


function parseStatement(
  str: string,
  index: number,
  terminalChar: string,
): BaseNode {
  let i = nextWordIndex(str, index);
  i = i === -1 ? index : i;
  let statement = null;
  if (["{", "("].includes(str.charAt(i))) {
    // Parse compound statement or subshell
    const isCompound = str.charAt(i) === "{";
    const endChar = isCompound ? "}" : ")";

    const { statements: children, terminatorIndex } = parseStatements(
      str,
      i + 1,
      endChar,
      isCompound,
    );
    const hasChildren = children.length > 0;
    const terminated = terminatorIndex !== -1;
    let endIndex = terminatorIndex + 1;
    if (!terminated) {
      endIndex = hasChildren
        ? children[children.length - 1].endIndex
        : str.length;
    }
    statement = createNode(str, {
      startIndex: i,
      type: isCompound ? NodeType.CompoundStatement : NodeType.Subshell,
      endIndex,
      complete: terminated && hasChildren,
      children,
    });
  } else {
    // statement = parseAssignmentListNodeOrCommandNode(str, i, terminalChar)
    statement = parseAssignmentListNodeOrCommandNode(str, i, terminalChar);
  }

  i = statement.endIndex;
  const opIndex = nextWordIndex(str, i);
  const op = opIndex !== -1 && parseOperator(str, opIndex);
  if (
    !op ||
    op === ";" ||
    op === "&" ||
    op === "&;" ||
    (opIndex !== -1 && terminalChar && str.charAt(opIndex) === terminalChar)
  ) {
    return statement;
  }

  // Recursively parse rightHandStatement if theres an operator.
  const rightHandStatement = parseStatement(
    str,
    opIndex + op.length,
    terminalChar,
  );
  if (op === "&&" || op === "||") {
    return reduceStatements(str, statement, rightHandStatement, NodeType.List);
  }

  if (op === "|" || op === "|&") {
    if (rightHandStatement.type === NodeType.List) {
      const [oldFirstChild, ...otherChildren] = rightHandStatement.children;
      const newFirstChild = reduceStatements(
        str,
        statement,
        oldFirstChild,
        NodeType.Pipeline,
      );
      return createNode(str, {
        type: NodeType.List,
        startIndex: newFirstChild.startIndex,
        children: [newFirstChild, ...otherChildren],
        endIndex: rightHandStatement.endIndex,
        complete: newFirstChild.complete && rightHandStatement.complete,
      });
    }
    return reduceStatements(
      str,
      statement,
      rightHandStatement,
      NodeType.Pipeline,
    );
  }
  return statement;
}