import { NamedNode , Node } from '#ast/node-types' ; import { isNamedNode , isNode } from '#ast/utilities/node-typeguards' ; import { accForEach , mixin } from '@crosstype/common' ; export interface NodeSet < T extends Node > extends NodeIterable < T > , Set < T > { } export interface NodeMap < T extends NamedNode > extends NodeIterable < T > , Map < T [ 'name' ] , T > { } export interface ReadonlyNodeSet < T extends Node > extends Omit < NodeSet < T > , Exclude < keyof Set < T > , keyof ReadonlySet < T > > > { } export interface ReadonlyNodeMap < T extends NamedNode > extends Omit < NodeMap < T > , Exclude < keyof Map < T [ 'name' ] , T > , keyof ReadonlyMap < T [ 'name' ] , T > > > { } function getIterableCreateParam < T extends Node > ( cls , nodes ) { if ( ! nodes ) return [ ] ; const arr = ( nodes instanceof NodeSet ? nodes . toArray ( ) : nodes as Node [ ] ) . filter ( ( i ) => isNode ( i ) && ( ( cls === NodeSet ) || isNamedNode ( i ) ) ) ; return ( cls === NodeMap ) ? ( < NamedNode < any > [ ] > arr ) . map ( n => [ n . name , n ] ) : arr ; } function createFrom < T extends { new ( nodes ? ) : any ; } | { new ( nodes ? ) : any ; } > ( cls , args ) { if ( ! args [ 0 ] ) return new cls ( ) ; let mapFn ; let iterable ; let items ; iterable = args [ 0 ] ; mapFn = args [ 1 ] ; let resNodes ; if ( iterable ) { items = Array . from ( iterable , mapFn ) ; resNodes = items . map ( ( n => n ) ) ; } return new cls ( < any > resNodes ) ; } type MapOrSet = Set < any > | Map < any , any > ; /**
 * Mixin for node iterables
 */ abstract class NodeIterable < T extends Node > { getValueByIndex ( this , index ) { if ( typeof index !== 'number' ) throw new TypeError ( `Index must be a number!` ) ; let i = 0 ; for ( const item of this . values ( ) ) if ( i ++ === index ) return item ; throw new RangeError ( `Index ${ index } out of bounds! [0-${ i - 1 }]` ) ; } get isEmpty ( ) : boolean { return ! ( < any > this ) . size ; } param undefinedIfEmpty toArray < B extends boolean | undefined = undefined > ( undefinedIfEmpty ? ) ; toArray ( this , undefinedIfEmpty ? ) { return ( undefinedIfEmpty && this . isEmpty ) ? void 0 : [ ... this . values ( ) ] ; } returns orUndefinedIfEmpty ( ) { return ! this . isEmpty ? this : void 0 ; } /**
     * Remove invalid (non-node) members
     */ prune ( this ) { if ( this instanceof Set ) [ ... this ] . forEach ( n => ! isNode ( n ) && this . delete ( n ) ) ; else [ ... this . entries ( ) ] . forEach ( ( [ k , n ] ) => ! isNode ( n ) && ( < Map < any , any > > this ) . delete ( k ) ) ; } /**
     * Find node
     */ find < U extends Node > ( predicate ) ; find < U extends Node > ( predicate ) ; find ( predicate ) { return this . toArray ( ) ?. find ( predicate ) ; } /**
     * Add a node
     */ add ( node ) ; add ( this , node ) { if ( this instanceof Set ) Set . prototype . add . call ( this , node ) ; else this . set ( ( < NamedNode < any > > node ) . name , node ) ; return this ; } } @ mixin ( NodeIterable ) export class NodeSet < T extends Node > extends Set < T > { constructor ( nodes ? ) { super ( getIterableCreateParam ( NodeSet , nodes ) ) ; } /**
     * Convert NodeSet to NodeMap
     * Note: Only includes members which have a name property (NamedNode)
     */ toNodeMap ( ) ; toNodeMap ( ) { return accForEach ( this , new NodeMap < NamedNode > ( ) , ( n , res ) => { if ( isNamedNode ( n ) ) res . set ( n . name , n ) ; } ) ; } /**
     * Create NodeSet from nodes (iterable of Node)
     */ static from < R extends Node > ( nodes ) ; /**
     * Create NodeSet -> uses nodes returned by Map function (performed on iterable)
     */ static from < R extends Node , T = any > ( iterable , mapFn ) ; static from ( ) { return createFrom ( NodeSet , arguments ) ; } } @ mixin ( NodeIterable ) export class NodeMap < T extends NamedNode > extends Map < T [ 'name' ] , T > { [ idx ] : any ; constructor ( nodes ? ) { super ( getIterableCreateParam ( NodeMap , nodes ) ) ; } /**
     * Convert NodeMap to NodeSet
     */ toNodeSet ( ) { return new NodeSet ( this . toArray ( ) ) as any ; } /**
     * Create NodeMap from nodes (iterable of Node)
     */ static from < R extends NamedNode > ( nodes ) ; /**
     * Create NodeMap -> uses nodes returned by Map function (performed on iterable)
     */ static from < R extends NamedNode , T = any > ( iterable , mapFn ) ; static from ( ) { return createFrom ( NodeMap , arguments ) ; } } export namespace NodeMap { export type GetIndexType < T > = T extends NodeMap < infer U > ? U [ 'name' ] : never ; export type GetNodeType < T > = T extends NodeMap < infer U > ? U : never ; } export namespace NodeSet { export type GetNodeType < T > = T extends NodeSet < infer U > ? U : never ; }