import { BinaryExpression , UnaryExpression , VariableDeclaration , } from "@babel/types" ; import { CompletionRecord , Empty , EnvironmentRecord , NormalCompletion , ReferenceRecord , } from "./ExecutionContext" ; import { collectBoundNames } from "./traverse" ; export function IsPropertyReference ( V ) { return V . Base !== "unresolvable" && ! ( V . Base instanceof EnvironmentRecord ) ; } export function InitializeReferencedBinding ( V , W ) { const base = V . Base as EnvironmentRecord ; return base . InitializeBinding ( V . ReferenceName as string , W ) ; } export function CopyDataProperties ( target , source , excludedItems ) { if ( source === undefined || source === null ) { return target ; } const keys = ( Object . getOwnPropertyNames ( source ) as PropertyKey [ ] ) . concat ( Object . getOwnPropertySymbols ( source ) ) ; for ( const nextKey of keys ) { if ( ! excludedItems . has ( nextKey ) ) { const desc = Object . getOwnPropertyDescriptor ( source , nextKey ) ; if ( desc ?. enumerable ) { target [ nextKey ] = ( source as Record < PropertyKey , unknown > ) [ nextKey ] ; } } } return target ; } export function ForDeclarationBindingInstantiation ( forDeclaration , env ) { const isConst = forDeclaration . kind === "const" ; for ( const name of collectBoundNames ( forDeclaration ) ) { if ( isConst ) { env . CreateImmutableBinding ( name , true ) ; } else { env . CreateMutableBinding ( name , false ) ; } } } export function LoopContinues ( completion ) { return completion . Type === "normal" || completion . Type == "continue" ; } export function UpdateEmpty ( completion , value ) { if ( completion . Value !== Empty ) { return completion ; } return new CompletionRecord ( completion . Type , value ) ; } export function GetValue ( V ) { if ( V instanceof CompletionRecord ) { V = V . Value ; } if ( ! ( V instanceof ReferenceRecord ) ) { return V ; } if ( V . Base === "unresolvable" ) { throw new ReferenceError ( `${ V . ReferenceName as string } is not defined` ) ; } if ( V . Base instanceof EnvironmentRecord ) { const base = V . Base as EnvironmentRecord ; return base . GetBindingValue ( V . ReferenceName as string , V . Strict ) ; } return V . Base [ V . ReferenceName ] ; } export function ToPropertyKey ( arg ) { if ( typeof arg === "symbol" ) { return arg ; } return String ( arg ) ; } export function GetV ( V , P ) { return ( V as Record < PropertyKey , unknown > ) [ P ] ; } export function PutValue ( V , W ) { if ( V . Base === "unresolvable" ) { throw new ReferenceError ( `${ V . ReferenceName as string } is not defined` ) ; } if ( V . Base instanceof EnvironmentRecord ) { return V . Base . SetMutableBinding ( V . ReferenceName as string , W , V . Strict ) ; } V . Base [ V . ReferenceName ] = W ; return NormalCompletion ( undefined ) ; } export function CreateListIteratorRecord ( args ) { if ( ! isIterable ( args ) ) { throw new TypeError ( `${ typeof args } is not iterable` ) ; } return args [ Symbol . iterator ] ( ) ; } export function RequireObjectCoercible ( arg ) { if ( arg === null || arg === undefined ) { throw new TypeError ( "Cannot destructure properties of undefined or null" ) ; } } export function GetIdentifierReference ( env , name , strict ) { if ( ! env ) { return new ReferenceRecord ( "unresolvable" , name , strict ) ; } if ( env . HasBinding ( name ) ) { return new ReferenceRecord ( env , name , strict ) ; } return GetIdentifierReference ( env . OuterEnv , name , strict ) ; } export function ApplyStringOrNumericBinaryOperator ( leftValue , operator , rightValue ) { switch ( operator ) { case "+" : return leftValue + rightValue ; case "-" : return leftValue - rightValue ; case "/" : return leftValue / rightValue ; case "%" : return leftValue % rightValue ; case "*" : return leftValue * rightValue ; case "**" : return leftValue ** rightValue ; case "==" : return leftValue == rightValue ; case "===" : return leftValue === rightValue ; case "!=" : return leftValue != rightValue ; case "!==" : return leftValue !== rightValue ; case ">" : return leftValue > rightValue ; case "<" : return leftValue < rightValue ; case ">=" : return leftValue >= rightValue ; case "<=" : return leftValue <= rightValue ; } throw new SyntaxError ( `Unsupported binary operator \`${ operator }\`` ) ; } export function ApplyStringOrNumericAssignment ( leftValue , operator , rightValue ) { switch ( operator ) { case "+=" : case "-=" : case "*=" : case "/=" : case "%=" : case "**=" : return ApplyStringOrNumericBinaryOperator ( leftValue as number , operator . substr ( 0 , operator . length - 1 ) as BinaryExpression [ "operator" ] , rightValue as number ) ; } throw new SyntaxError ( `Unsupported assignment operator \`${ operator }\`` ) ; } export function ApplyUnaryOperator ( target , operator ) { switch ( operator ) { case "!" : return ! target ; case "+" : return + target ; case "-" : return - target ; case "void" : return undefined ; } throw new SyntaxError ( `Unsupported unary operator \`${ operator }\`` ) ; } export function isIterable ( cooked ) { if ( Array . isArray ( cooked ) ) { return true ; } if ( cooked === null || cooked === undefined ) { return false ; } return typeof ( cooked as Iterable < unknown > ) [ Symbol . iterator ] === "function" ; }