mirror of
https://github.com/bendtherules/ask262.git
synced 2026-08-18 13:21:55 +00:00
129 lines
6.4 KiB
TypeScript
129 lines
6.4 KiB
TypeScript
import { ReferenceRecord, Value } from '../value.mts';
|
|
import { Evaluate, type ExpressionEvaluator } from '../evaluator.mts';
|
|
import { Q, X } from '../completion.mts';
|
|
import { IsInTailPosition, StringValue } from '../static-semantics/all.mts';
|
|
import { OutOfRange } from '../helpers.mts';
|
|
import type { ParseNode } from '../parser/ParseNode.mts';
|
|
import {
|
|
EvaluateCall,
|
|
EvaluatePropertyAccessWithExpressionKey,
|
|
EvaluatePropertyAccessWithIdentifierKey,
|
|
} from './all.mts';
|
|
import { GetValue, MakePrivateReference } from '#self';
|
|
|
|
/** https://tc39.es/ecma262/#sec-optional-chaining-evaluation */
|
|
// OptionalExpression :
|
|
// MemberExpression OptionalChain
|
|
// CallExpression OptionalChain
|
|
// OptionalExpression OptionalChain
|
|
export function* Evaluate_OptionalExpression({ MemberExpression, OptionalChain }: ParseNode.OptionalExpression) {
|
|
// 1. Let baseReference be the result of evaluating MemberExpression.
|
|
const baseReference = Q(yield* Evaluate(MemberExpression));
|
|
// 2. Let baseValue be ? GetValue(baseReference).
|
|
const baseValue = Q(yield* GetValue(baseReference));
|
|
// 3. If baseValue is undefined or null, then
|
|
if (baseValue === Value.undefined || baseValue === Value.null) {
|
|
// a. Return undefined.
|
|
return Value.undefined;
|
|
}
|
|
// 4. Return the result of performing ChainEvaluation of OptionalChain with arguments baseValue and baseReference.
|
|
return yield* ChainEvaluation(OptionalChain, baseValue, X(baseReference));
|
|
}
|
|
|
|
/** https://tc39.es/ecma262/#sec-optional-chaining-chain-evaluation */
|
|
// OptionalChain :
|
|
// `?.` Arguments
|
|
// `?.` `[` Expression `]`
|
|
// `?.` IdentifierName
|
|
// `?.` PrivateIdentifier
|
|
// OptionalChain Arguments
|
|
// OptionalChain `[` Expression `]`
|
|
// OptionalChain `.` IdentifierName
|
|
// OptionalChain `.` PrivateIdentifier
|
|
function* ChainEvaluation(node: ParseNode.OptionalChain, baseValue: Value, baseReference: Value | ReferenceRecord): ExpressionEvaluator {
|
|
const {
|
|
OptionalChain,
|
|
Arguments,
|
|
Expression,
|
|
IdentifierName,
|
|
PrivateIdentifier,
|
|
} = node;
|
|
if (Arguments) {
|
|
if (OptionalChain) {
|
|
// 1. Let optionalChain be OptionalChain.
|
|
const optionalChain = OptionalChain;
|
|
// 2. Let newReference be ? ChainEvaluation of optionalChain with arguments baseValue and baseReference.
|
|
const newReference = Q(yield* ChainEvaluation(optionalChain, baseValue, baseReference));
|
|
// 3. Let newValue be ? GetValue(newReference).
|
|
const newValue = Q(yield* GetValue(newReference));
|
|
// 4. Let thisChain be this OptionalChain.
|
|
const thisChain = node;
|
|
// 5. Let tailCall be IsInTailPosition(thisChain).
|
|
const tailCall = IsInTailPosition(thisChain);
|
|
// 6. Return ? EvaluateCall(newValue, newReference, Arguments, tailCall).
|
|
return Q(yield* EvaluateCall(newValue, newReference, Arguments, tailCall));
|
|
}
|
|
// 1. Let thisChain be this OptionalChain.
|
|
const thisChain = node;
|
|
// 2. Let tailCall be IsInTailPosition(thisChain).
|
|
const tailCall = IsInTailPosition(thisChain);
|
|
// 3. Return ? EvaluateCall(baseValue, baseReference, Arguments, tailCall).
|
|
return Q(yield* EvaluateCall(baseValue, baseReference, Arguments, tailCall));
|
|
}
|
|
if (Expression) {
|
|
if (OptionalChain) {
|
|
// 1. Let optionalChain be OptionalChain.
|
|
const optionalChain = OptionalChain;
|
|
// 2. Let newReference be ? ChainEvaluation of optionalChain with arguments baseValue and baseReference.
|
|
const newReference = Q(yield* ChainEvaluation(optionalChain, baseValue, baseReference));
|
|
// 3. Let newValue be ? GetValue(newReference).
|
|
const newValue = Q(yield* GetValue(newReference));
|
|
// 4. If the code matched by this OptionalChain is strict mode code, let strict be true; else let strict be false.
|
|
const strict = node.strict;
|
|
// 5. Return ? EvaluatePropertyAccessWithExpressionKey(newValue, Expression, strict).
|
|
return Q(yield* EvaluatePropertyAccessWithExpressionKey(newValue, Expression, strict));
|
|
}
|
|
// 1. If the code matched by this OptionalChain is strict mode code, let strict be true; else let strict be false.
|
|
const strict = node.strict;
|
|
// 2. Return ? EvaluatePropertyAccessWithExpressionKey(baseValue, Expression, strict).
|
|
return Q(yield* EvaluatePropertyAccessWithExpressionKey(baseValue, Expression, strict));
|
|
}
|
|
if (IdentifierName) {
|
|
if (OptionalChain) {
|
|
// 1. Let optionalChain be OptionalChain.
|
|
const optionalChain = OptionalChain;
|
|
// 2. Let newReference be ? ChainEvaluation of optionalChain with arguments baseValue and baseReference.
|
|
const newReference = Q(yield* ChainEvaluation(optionalChain, baseValue, baseReference));
|
|
// 3. Let newValue be ? GetValue(newReference).
|
|
const newValue = Q(yield* GetValue(newReference));
|
|
// 4. If the code matched by this OptionalChain is strict mode code, let strict be true; else let strict be false.
|
|
const strict = node.strict;
|
|
// 5. Return ! EvaluatePropertyAccessWithIdentifierKey(newValue, IdentifierName, strict).
|
|
return X(EvaluatePropertyAccessWithIdentifierKey(newValue, IdentifierName, strict));
|
|
}
|
|
// 1. If the code matched by this OptionalChain is strict mode code, let strict be true; else let strict be false.
|
|
const strict = node.strict;
|
|
// 2. Return ! EvaluatePropertyAccessWithIdentifierKey(baseValue, IdentifierName, strict).
|
|
return X(EvaluatePropertyAccessWithIdentifierKey(baseValue, IdentifierName, strict));
|
|
}
|
|
if (PrivateIdentifier) {
|
|
if (OptionalChain) {
|
|
// 1. Let optionalChain be OptionalChain.
|
|
const optionalChain = OptionalChain;
|
|
// 2. Let newReference be ? ChainEvaluation of optionalChain with arguments baseValue and baseReference.
|
|
const newReference = Q(yield* ChainEvaluation(optionalChain, baseValue, baseReference));
|
|
// 3. Let newValue be ? GetValue(newReference).
|
|
const newValue = Q(yield* GetValue(newReference));
|
|
// 4. Let fieldNameString be the StringValue of PrivateIdentifier.
|
|
const fieldNameString = StringValue(PrivateIdentifier);
|
|
// 5. Return ! MakePrivateReference(nv, fieldNameString).
|
|
return X(MakePrivateReference(newValue, fieldNameString));
|
|
}
|
|
// 1. Let fieldNameString be the StringValue of PrivateIdentifier.
|
|
const fieldNameString = StringValue(PrivateIdentifier);
|
|
// 2. Return ! MakePrivateReference(bv, fieldNameString).
|
|
return X(MakePrivateReference(baseValue, fieldNameString));
|
|
}
|
|
throw new OutOfRange('ChainEvaluation', node);
|
|
}
|