import { ObjectValue, Value } from '../value.mts'; import { Evaluate, type PlainEvaluator, type ValueEvaluator, } from '../evaluator.mts'; import { Q, X, } from '../completion.mts'; import type { ParseNode } from '../parser/ParseNode.mts'; import { Set, ArrayCreate, GetValue, GetIterator, ToString, CreateDataPropertyOrThrow, F, IteratorStepValue, } from '#self'; /** https://tc39.es/ecma262/#sec-runtime-semantics-arrayaccumulation */ // Elision : // `,` // Elision `,` // ElementList : // Elision? AssignmentExpression // Elision? SpreadElement // ElementList `,` Elision? AssignmentExpression // ElementList : ElementList `,` Elision SpreadElement // SpreadElement : // `...` AssignmentExpression function* ArrayAccumulation(ElementList: ParseNode.ElementList, array: ObjectValue, nextIndex: number): PlainEvaluator { let postIndex = nextIndex; for (const element of ElementList) { switch (element.type) { case 'Elision': postIndex += 1; Q(yield* Set(array, Value('length'), F(postIndex), Value.true)); break; case 'SpreadElement': postIndex = Q(yield* ArrayAccumulation_SpreadElement(element, array, postIndex)); break; default: postIndex = Q(yield* ArrayAccumulation_AssignmentExpression(element, array, postIndex)); break; } } return postIndex; } // SpreadElement : `...` AssignmentExpression function* ArrayAccumulation_SpreadElement({ AssignmentExpression }: ParseNode.SpreadElement, array: ObjectValue, nextIndex: number): PlainEvaluator { // 1. Let spreadRef be the result of evaluating AssignmentExpression. const spreadRef = Q(yield* Evaluate(AssignmentExpression)); // 2. Let spreadObj be ? GetValue(spreadRef). const spreadObj = Q(yield* GetValue(spreadRef)); // 3. Let iteratorRecord be ? GetIterator(spreadObj). const iteratorRecord = Q(yield* GetIterator(spreadObj, 'sync')); // 4. Repeat, while (true) { // a. Let next be ? IteratorStep(iteratorRecord). const next = Q(yield* IteratorStepValue(iteratorRecord)); // b. If next is done, return nextIndex. if (next === 'done') { return nextIndex; } // d. Perform ! CreateDataPropertyOrThrow(array, ! ToString(𝔽(nextIndex)), next). X(CreateDataPropertyOrThrow(array, X(ToString(F(nextIndex))), next)); // e. Set nextIndex to nextIndex + 1. nextIndex += 1; } } function* ArrayAccumulation_AssignmentExpression(AssignmentExpression: ParseNode.AssignmentExpressionOrHigher, array: ObjectValue, nextIndex: number): PlainEvaluator { // 2. Let initResult be the result of evaluating AssignmentExpression. const initResult = Q(yield* Evaluate(AssignmentExpression)); // 3. Let initValue be ? GetValue(initResult). const initValue = Q(yield* GetValue(initResult)); // 4. Let created be ! CreateDataPropertyOrThrow(array, ! ToString(𝔽(nextIndex)), initValue). X(CreateDataPropertyOrThrow(array, X(ToString(F(nextIndex))), initValue)); // 5. Return nextIndex + 1. return nextIndex + 1; } /** https://tc39.es/ecma262/#sec-array-initializer-runtime-semantics-evaluation */ // ArrayLiteral : // `[` Elision `]` // `[` ElementList `]` // `[` ElementList `,` Elision `]` export function* Evaluate_ArrayLiteral({ ElementList }: ParseNode.ArrayLiteral): ValueEvaluator { // 1. Let array be ! ArrayCreate(0). const array = X(ArrayCreate(0)); // 2. Let len be the result of performing ArrayAccumulation for ElementList with arguments array and 0. const len = yield* ArrayAccumulation(ElementList, array, 0); Q(len); // 4. Return array. return array; }