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;
}