WIP: experiment with getting rid of bindLogicalLikeExpression
This commit is contained in:
parent
1ade73df2b
commit
7684d66ba2
|
@ -1433,15 +1433,15 @@ namespace ts {
|
||||||
}
|
}
|
||||||
|
|
||||||
function bindLogicalLikeExpression(node: BinaryExpression, trueTarget: FlowLabel, falseTarget: FlowLabel) {
|
function bindLogicalLikeExpression(node: BinaryExpression, trueTarget: FlowLabel, falseTarget: FlowLabel) {
|
||||||
const preRightLabel = createBranchLabel();
|
const preRightLabel = createBranchLabel(); // V
|
||||||
if (node.operatorToken.kind === SyntaxKind.AmpersandAmpersandToken || node.operatorToken.kind === SyntaxKind.AmpersandAmpersandEqualsToken) {
|
if (node.operatorToken.kind === SyntaxKind.AmpersandAmpersandToken || node.operatorToken.kind === SyntaxKind.AmpersandAmpersandEqualsToken) {
|
||||||
bindCondition(node.left, preRightLabel, falseTarget);
|
bindCondition(node.left, preRightLabel, falseTarget);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
bindCondition(node.left, trueTarget, preRightLabel);
|
bindCondition(node.left, trueTarget, preRightLabel);
|
||||||
}
|
}
|
||||||
currentFlow = finishFlowLabel(preRightLabel);
|
currentFlow = finishFlowLabel(preRightLabel); // V
|
||||||
bind(node.operatorToken);
|
bind(node.operatorToken); // V
|
||||||
|
|
||||||
if (isLogicalOrCoalescingAssignmentOperator(node.operatorToken.kind)) {
|
if (isLogicalOrCoalescingAssignmentOperator(node.operatorToken.kind)) {
|
||||||
doWithConditionalBranches(bind, node.right, trueTarget, falseTarget);
|
doWithConditionalBranches(bind, node.right, trueTarget, falseTarget);
|
||||||
|
@ -1498,11 +1498,21 @@ namespace ts {
|
||||||
}
|
}
|
||||||
|
|
||||||
function createBindBinaryExpressionFlow() {
|
function createBindBinaryExpressionFlow() {
|
||||||
|
interface LogicalLikeState {
|
||||||
|
trueTarget: FlowLabel;
|
||||||
|
falseTarget: FlowLabel;
|
||||||
|
preRightLabel: FlowLabel;
|
||||||
|
postExpressionLabel?: FlowLabel;
|
||||||
|
}
|
||||||
|
|
||||||
interface WorkArea {
|
interface WorkArea {
|
||||||
stackIndex: number;
|
stackIndex: number;
|
||||||
skip: boolean;
|
// skip: boolean;
|
||||||
inStrictModeStack: (boolean | undefined)[];
|
inStrictModeStack: (boolean | undefined)[];
|
||||||
parentStack: (Node | undefined)[];
|
parentStack: (Node | undefined)[];
|
||||||
|
currentBranchTargetStack: ([FlowLabel | undefined, FlowLabel | undefined])[];
|
||||||
|
logicalStack: (LogicalLikeState | undefined)[];
|
||||||
|
isLogicalLike: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
return createBinaryExpressionTrampoline(onEnter, onLeft, onOperator, onRight, onExit, /*foldState*/ undefined);
|
return createBinaryExpressionTrampoline(onEnter, onLeft, onOperator, onRight, onExit, /*foldState*/ undefined);
|
||||||
|
@ -1517,70 +1527,139 @@ namespace ts {
|
||||||
bindWorker(node);
|
bindWorker(node);
|
||||||
const saveParent = parent;
|
const saveParent = parent;
|
||||||
parent = node;
|
parent = node;
|
||||||
state.skip = false;
|
// state.skip = false;
|
||||||
state.inStrictModeStack[state.stackIndex] = saveInStrictMode;
|
state.inStrictModeStack[state.stackIndex] = saveInStrictMode;
|
||||||
state.parentStack[state.stackIndex] = saveParent;
|
state.parentStack[state.stackIndex] = saveParent;
|
||||||
|
state.currentBranchTargetStack[state.stackIndex] = [currentTrueTarget, currentFalseTarget];
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
state = {
|
state = {
|
||||||
stackIndex: 0,
|
stackIndex: 0,
|
||||||
skip: false,
|
// skip: false,
|
||||||
inStrictModeStack: [undefined],
|
inStrictModeStack: [undefined],
|
||||||
parentStack: [undefined]
|
parentStack: [undefined],
|
||||||
|
logicalStack: [undefined],
|
||||||
|
currentBranchTargetStack: [[currentTrueTarget, currentFalseTarget]],
|
||||||
|
isLogicalLike: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
// TODO: bindLogicalExpression is recursive - if we want to handle deeply nested `&&` expressions
|
|
||||||
// we'll need to handle the `bindLogicalExpression` scenarios in this state machine, too
|
|
||||||
// For now, though, since the common cases are chained `+`, leaving it recursive is fine
|
|
||||||
const operator = node.operatorToken.kind;
|
const operator = node.operatorToken.kind;
|
||||||
if (operator === SyntaxKind.AmpersandAmpersandToken ||
|
if (operator === SyntaxKind.AmpersandAmpersandToken ||
|
||||||
operator === SyntaxKind.BarBarToken ||
|
operator === SyntaxKind.BarBarToken ||
|
||||||
operator === SyntaxKind.QuestionQuestionToken ||
|
operator === SyntaxKind.QuestionQuestionToken ||
|
||||||
isLogicalOrCoalescingAssignmentOperator(operator)) {
|
isLogicalOrCoalescingAssignmentOperator(operator)) {
|
||||||
|
state.isLogicalLike = true;
|
||||||
|
|
||||||
|
let logicalState: LogicalLikeState;
|
||||||
|
const preRightLabel = createBranchLabel();
|
||||||
if (isTopLevelLogicalExpression(node)) {
|
if (isTopLevelLogicalExpression(node)) {
|
||||||
const postExpressionLabel = createBranchLabel();
|
const postExpressionLabel = createBranchLabel(); // >> TODO: we need to set current flow afterwards, at the end of onexit
|
||||||
bindLogicalLikeExpression(node, postExpressionLabel, postExpressionLabel);
|
logicalState = {
|
||||||
currentFlow = finishFlowLabel(postExpressionLabel);
|
preRightLabel,
|
||||||
|
trueTarget: postExpressionLabel,
|
||||||
|
falseTarget: postExpressionLabel,
|
||||||
|
postExpressionLabel,
|
||||||
|
};
|
||||||
|
// bindLogicalLikeExpression(node, postExpressionLabel, postExpressionLabel);
|
||||||
|
// currentFlow = finishFlowLabel(postExpressionLabel); // >> TODO: on exit?
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
bindLogicalLikeExpression(node, currentTrueTarget!, currentFalseTarget!);
|
logicalState = {
|
||||||
|
preRightLabel,
|
||||||
|
trueTarget: currentTrueTarget!,
|
||||||
|
falseTarget: currentFalseTarget!,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
state.skip = true;
|
state.logicalStack[state.stackIndex] = logicalState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: bindLogicalExpression is recursive - if we want to handle deeply nested `&&` expressions
|
||||||
|
// we'll need to handle the `bindLogicalExpression` scenarios in this state machine, too
|
||||||
|
// For now, though, since the common cases are chained `+`, leaving it recursive is fine // >> TODO: suspiscious
|
||||||
|
// const operator = node.operatorToken.kind;
|
||||||
|
// if (operator === SyntaxKind.AmpersandAmpersandToken ||
|
||||||
|
// operator === SyntaxKind.BarBarToken ||
|
||||||
|
// operator === SyntaxKind.QuestionQuestionToken ||
|
||||||
|
// isLogicalOrCoalescingAssignmentOperator(operator)) {
|
||||||
|
// if (isTopLevelLogicalExpression(node)) {
|
||||||
|
// const postExpressionLabel = createBranchLabel();
|
||||||
|
// bindLogicalLikeExpression(node, postExpressionLabel, postExpressionLabel);
|
||||||
|
// currentFlow = finishFlowLabel(postExpressionLabel);
|
||||||
|
// }
|
||||||
|
// else {
|
||||||
|
// bindLogicalLikeExpression(node, currentTrueTarget!, currentFalseTarget!);
|
||||||
|
// }
|
||||||
|
// state.skip = true;
|
||||||
|
// }
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
function onLeft(left: Expression, state: WorkArea, _node: BinaryExpression) {
|
function onLeft(left: Expression, state: WorkArea, node: BinaryExpression) {
|
||||||
if (!state.skip) {
|
if (state.isLogicalLike) {
|
||||||
return maybeBind(left);
|
if (node.operatorToken.kind === SyntaxKind.AmpersandAmpersandToken ||
|
||||||
|
node.operatorToken.kind === SyntaxKind.AmpersandAmpersandEqualsToken) {
|
||||||
|
currentTrueTarget = state.logicalStack[state.stackIndex]!.preRightLabel;
|
||||||
|
currentFalseTarget = state.logicalStack[state.stackIndex]!.falseTarget;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
currentTrueTarget = state.logicalStack[state.stackIndex]!.trueTarget;
|
||||||
|
currentFalseTarget = state.logicalStack[state.stackIndex]!.preRightLabel;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return maybeBind(left);
|
||||||
|
// if (!state.skip) {
|
||||||
|
// return maybeBind(left);
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
function onOperator(operatorToken: BinaryOperatorToken, state: WorkArea, node: BinaryExpression) {
|
function onOperator(operatorToken: BinaryOperatorToken, state: WorkArea, node: BinaryExpression) {
|
||||||
if (!state.skip) {
|
if (state.isLogicalLike) {
|
||||||
if (operatorToken.kind === SyntaxKind.CommaToken) {
|
if (!isLogicalAssignmentExpression(node) && !isLogicalExpression(node) && !(isOptionalChain(node) && isOutermostOptionalChain(node))) {
|
||||||
maybeBindExpressionFlowIfCall(node.left);
|
addAntecedent(currentTrueTarget!, createFlowCondition(FlowFlags.TrueCondition, currentFlow, node));
|
||||||
|
addAntecedent(currentFalseTarget!, createFlowCondition(FlowFlags.FalseCondition, currentFlow, node));
|
||||||
}
|
}
|
||||||
bind(operatorToken);
|
// >> TODO: restore current true and current false right here, to emulate end of `bindCondition` call
|
||||||
|
currentFlow = finishFlowLabel(state.logicalStack[state.stackIndex]!.preRightLabel);
|
||||||
}
|
}
|
||||||
|
else if (operatorToken.kind === SyntaxKind.CommaToken) {
|
||||||
|
maybeBindExpressionFlowIfCall(node.left);
|
||||||
|
}
|
||||||
|
bind(operatorToken);
|
||||||
|
// if (!state.skip) {
|
||||||
|
// if (operatorToken.kind === SyntaxKind.CommaToken) {
|
||||||
|
// maybeBindExpressionFlowIfCall(node.left);
|
||||||
|
// }
|
||||||
|
// bind(operatorToken);
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
function onRight(right: Expression, state: WorkArea, _node: BinaryExpression) {
|
function onRight(right: Expression, state: WorkArea, node: BinaryExpression) {
|
||||||
if (!state.skip) {
|
if (state.isLogicalLike) {
|
||||||
return maybeBind(right);
|
// >> TODO: do special stuff if assignment operator
|
||||||
|
currentTrueTarget = state.logicalStack[state.stackIndex]!.trueTarget;
|
||||||
|
currentFalseTarget = state.logicalStack[state.stackIndex]!.falseTarget;
|
||||||
}
|
}
|
||||||
|
return maybeBind(right);
|
||||||
|
// if (!state.skip) {
|
||||||
|
// return maybeBind(right);
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
function onExit(node: BinaryExpression, state: WorkArea) {
|
function onExit(node: BinaryExpression, state: WorkArea) {
|
||||||
if (!state.skip) {
|
const operator = node.operatorToken.kind;
|
||||||
const operator = node.operatorToken.kind;
|
if (state.isLogicalLike) {
|
||||||
if (isAssignmentOperator(operator) && !isAssignmentTarget(node)) {
|
const postExpressionLabel = state.logicalStack[state.stackIndex]?.postExpressionLabel;
|
||||||
bindAssignmentTargetFlow(node.left);
|
if (postExpressionLabel) {
|
||||||
if (operator === SyntaxKind.EqualsToken && node.left.kind === SyntaxKind.ElementAccessExpression) {
|
currentFlow = finishFlowLabel(postExpressionLabel);
|
||||||
const elementAccess = node.left as ElementAccessExpression;
|
}
|
||||||
if (isNarrowableOperand(elementAccess.expression)) {
|
}
|
||||||
currentFlow = createFlowMutation(FlowFlags.ArrayMutation, currentFlow, node);
|
else if (isAssignmentOperator(operator) && !isAssignmentTarget(node)) {
|
||||||
}
|
bindAssignmentTargetFlow(node.left);
|
||||||
|
if (operator === SyntaxKind.EqualsToken && node.left.kind === SyntaxKind.ElementAccessExpression) {
|
||||||
|
const elementAccess = node.left as ElementAccessExpression;
|
||||||
|
if (isNarrowableOperand(elementAccess.expression)) {
|
||||||
|
currentFlow = createFlowMutation(FlowFlags.ArrayMutation, currentFlow, node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1592,7 +1671,9 @@ namespace ts {
|
||||||
if (savedParent !== undefined) {
|
if (savedParent !== undefined) {
|
||||||
parent = savedParent;
|
parent = savedParent;
|
||||||
}
|
}
|
||||||
state.skip = false;
|
currentTrueTarget = state.currentBranchTargetStack[state.stackIndex][0];
|
||||||
|
currentFalseTarget = state.currentBranchTargetStack[state.stackIndex][1];
|
||||||
|
state.isLogicalLike = false;
|
||||||
state.stackIndex--;
|
state.stackIndex--;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1016,7 +1016,15 @@ namespace ts {
|
||||||
* @param frame The current frame
|
* @param frame The current frame
|
||||||
* @returns The new frame
|
* @returns The new frame
|
||||||
*/
|
*/
|
||||||
export function enter<TOuterState, TState, TResult>(machine: BinaryExpressionStateMachine<TOuterState, TState, TResult>, stackIndex: number, stateStack: BinaryExpressionState[], nodeStack: BinaryExpression[], userStateStack: TState[], _resultHolder: { value: TResult }, outerState: TOuterState): number {
|
export function enter<TOuterState, TState, TResult>(
|
||||||
|
machine: BinaryExpressionStateMachine<TOuterState, TState, TResult>,
|
||||||
|
stackIndex: number,
|
||||||
|
stateStack: BinaryExpressionState[],
|
||||||
|
nodeStack: BinaryExpression[],
|
||||||
|
userStateStack: TState[],
|
||||||
|
_resultHolder: { value: TResult },
|
||||||
|
outerState: TOuterState,
|
||||||
|
): number {
|
||||||
const prevUserState = stackIndex > 0 ? userStateStack[stackIndex - 1] : undefined;
|
const prevUserState = stackIndex > 0 ? userStateStack[stackIndex - 1] : undefined;
|
||||||
Debug.assertEqual(stateStack[stackIndex], enter);
|
Debug.assertEqual(stateStack[stackIndex], enter);
|
||||||
userStateStack[stackIndex] = machine.onEnter(nodeStack[stackIndex], prevUserState, outerState);
|
userStateStack[stackIndex] = machine.onEnter(nodeStack[stackIndex], prevUserState, outerState);
|
||||||
|
@ -1030,7 +1038,15 @@ namespace ts {
|
||||||
* @param frame The current frame
|
* @param frame The current frame
|
||||||
* @returns The new frame
|
* @returns The new frame
|
||||||
*/
|
*/
|
||||||
export function left<TOuterState, TState, TResult>(machine: BinaryExpressionStateMachine<TOuterState, TState, TResult>, stackIndex: number, stateStack: BinaryExpressionState[], nodeStack: BinaryExpression[], userStateStack: TState[], _resultHolder: { value: TResult }, _outerState: TOuterState): number {
|
export function left<TOuterState, TState, TResult>(
|
||||||
|
machine: BinaryExpressionStateMachine<TOuterState, TState, TResult>,
|
||||||
|
stackIndex: number,
|
||||||
|
stateStack: BinaryExpressionState[],
|
||||||
|
nodeStack: BinaryExpression[],
|
||||||
|
userStateStack: TState[],
|
||||||
|
_resultHolder: { value: TResult },
|
||||||
|
_outerState: TOuterState,
|
||||||
|
): number {
|
||||||
Debug.assertEqual(stateStack[stackIndex], left);
|
Debug.assertEqual(stateStack[stackIndex], left);
|
||||||
Debug.assertIsDefined(machine.onLeft);
|
Debug.assertIsDefined(machine.onLeft);
|
||||||
stateStack[stackIndex] = nextState(machine, left);
|
stateStack[stackIndex] = nextState(machine, left);
|
||||||
|
|
1005
tests/cases/compiler/bigOrExpression.ts
Normal file
1005
tests/cases/compiler/bigOrExpression.ts
Normal file
File diff suppressed because it is too large
Load diff
1
tests/cases/compiler/bigOrExpressionTemp.ts
Normal file
1
tests/cases/compiler/bigOrExpressionTemp.ts
Normal file
|
@ -0,0 +1 @@
|
||||||
|
const x = "a" + "b" + "c" + "d" + "e" + "f" + "g";
|
Loading…
Reference in a new issue