import seedrandom from 'seedrandom' ; import { MalVal , symbolFor as S , createList as L , MalMap , cloneExp , assocBang , isMap , isString , isSymbol , isKeyword , keywordFor , MalError , MalFunc , isVector , isList , MalAtom , getType , getMeta , withMeta , isSeq , setMeta , } from '@/mal/types' ; import printExp from '@/mal/printer' ; import { partition } from '@/utils' ; import isNodeJS from 'is-node' ; const Exports = [ [ 'type' , x => keywordFor ( getType ( x ) as string ) ] , [ 'nil?' , ( x ) => x === null ] , [ 'true?' , ( x ) => x === true ] , [ 'false?' , ( x ) => x === false ] , [ 'boolean?' , ( x ) => typeof x === 'boolean' ] , [ 'number?' , ( x ) => typeof x === 'number' ] , [ 'string?' , isString ] , [ 'keyword?' , isKeyword ] , [ 'fn?' , ( x ) => getType ( x ) === 'fn' ] , [ 'macro?' , ( x ) => getType ( x ) === 'macro' ] , [ 'keyword' , keywordFor ] , [ 'symbol' , S ] , [ 'symbol?' , isSymbol ] , [ '=' , ( a , b ) => a === b ] , [ '!=' , ( a , b ) => a !== b ] , [ '<' , ( a , b ) => a < b ] , [ '<=' , ( a , b ) => a <= b ] , [ '>' , ( a , b ) => a > b ] , [ '>=' , ( a , b ) => a >= b ] , [ '+' , ( ... a ) => a . reduce ( ( x , y ) => x + y , 0 ) ] , [ '-' , ( ... xs ) => { switch ( xs . length ) { case 0 : return 0 ; case 1 : return - xs [ 0 ] ; case 2 : return xs [ 0 ] - xs [ 1 ] ; default : return xs . slice ( 1 ) . reduce ( ( a , b ) => a - b , xs [ 0 ] ) ; } } , ] , [ '*' , ( ... args ) => args . reduce ( ( a , b ) => a * b , 1 ) ] , [ '/' , ( ... xs ) => { switch ( xs . length ) { case 0 : return 1 ; case 1 : return 1 / xs [ 0 ] ; case 2 : return xs [ 0 ] / xs [ 1 ] ; default : return xs . slice ( 1 ) . reduce ( ( a , b ) => a / b , xs [ 0 ] ) ; } } , ] , [ 'mod' , ( x , y ) => ( ( x % y ) + y ) % y ] , [ 'list' , ( ... coll ) => L ( ... coll ) ] , [ 'lst' , ( coll ) => L ( ... coll ) ] , [ 'list?' , isList ] , [ 'vector' , ( ... xs ) => xs ] , [ 'vector?' , isVector ] , [ 'vec' , ( a ) => [ ... a ] ] , [ 'sequential?' , isSeq ] , [ 'seq' , ( a ) => { if ( isSeq ( a ) ) { return [ ... a ] ; } else if ( isString ( a ) && a . length > 0 ) { return a . split ( '' ) ; } else if ( isMap ( a ) ) { return Object . entries ( a ) ; } else { return null ; } } , ] , [ 'nth' , ( a , i ) => { if ( typeof i !== 'number' ) { throw new MalError ( '[nth] Index should be specified by number' ) ; } else if ( i < 0 ) { if ( - i <= a . length ) { return a [ a . length - i ] ; } else { throw new MalError ( '[nth] index out of range' ) ; } } else { if ( i < a . length ) { return a [ i ] ; } else { throw new MalError ( '[nth] index out of range' ) ; } } } , ] , [ 'first' , ( a ) => ( a !== null && a . length > 0 ? a [ 0 ] : null ) ] , [ 'rest' , ( a ) => ( a === null ? [ ] : a . slice ( 1 ) ) ] , [ 'last' , ( a ) => ( a !== null && a . length > 0 ? a [ a . length - 1 ] : null ) , ] , [ 'butlast' , ( a ) => ( a === null ? [ ] : a . slice ( 0 , a . length - 1 ) ) ] , [ 'count' , ( a ) => ( a === null ? 0 : a . length ) ] , [ 'slice' , ( a , start , end ) => { if ( isSeq ( a ) ) { return a . slice ( start , end ) ; } else { throw new MalError ( `[slice] ${ printExp ( a ) } is not an array` ) ; } } , ] , [ 'apply' , ( f , ... a ) => f ( ... a . slice ( 0 , - 1 ) . concat ( a [ a . length - 1 ] ) ) , ] , [ 'map' , ( f , a ) => a . map ( x => f ( x ) ) ] , [ 'map-indexed' , ( f , a ) => a . map ( ( x , i ) => f ( i , x ) ) ] , [ 'filter' , ( f , a ) => a . filter ( x => f ( x ) ) ] , [ 'remove' , ( f , a ) => a . filter ( x => ! f ( x ) ) ] , [ 'sort' , ( coll ) => [ ... coll ] . sort ( ) ] , [ 'partition' , partition ] , [ 'index-of' , ( value , a ) => value . indexOf ( a ) ] , [ 'last-index-of' , ( value , a ) => value . lastIndexOf ( a ) , ] , [ 'repeat' , ( a , n ) => Array ( n ) . fill ( a ) ] , [ 'reverse' , ( coll ) => [ ... coll ] . reverse ( ) ] , [ 'cons' , ( a , b ) => [ a ] . concat ( b ) ] , [ 'conj' , ( lst , ... args ) => { if ( isList ( lst ) ) { const newList = L ( ... lst ) ; args . forEach ( arg => newList . unshift ( arg ) ) ; return newList ; } else if ( isVector ( lst ) ) { return [ ... lst , ... args ] ; } } , ] , [ 'concat' , ( ... args ) => [ ] . concat ( ... ( args . filter ( v => v !== null ) as any [ ] ) ) , ] , [ 'join' , ( separator , coll ) => coll . map ( v => printExp ( v , false ) ) . join ( separator ) , ] , [ 'hash-map' , ( ... a ) => assocBang ( { } , ... a ) ] , [ 'map?' , isMap ] , [ 'assoc' , ( m , ... a ) => ( { ... m , ... assocBang ( { } , ... a ) } ) ] , [ 'dissoc' , ( m , ... a ) => { const n = cloneExp ( m ) as MalMap ; a . forEach ( k => delete n [ k ] ) ; return n ; } , ] , [ 'get' , ( m , a , notfound = null ) => { if ( isMap ( m ) ) { return a in m ? m [ a ] : notfound ; } else { return notfound ; } } , ] , [ 'contains?' , ( m , a ) => ( typeof a === 'string' ? a in m : false ) , ] , [ 'keys' , ( a ) => Object . keys ( a ) ] , [ 'vals' , ( a ) => Object . values ( a ) ] , [ 'entries' , ( a ) => Object . entries ( a ) ] , [ 'merge' , ( ... xs ) => { return xs . filter ( isMap ) . reduce ( ( ret , m ) => Object . assign ( ret , m ) , { } ) ; } , ] , [ 'pr-str' , ( ... a ) => a . map ( e => printExp ( e , true ) ) . join ( ' ' ) ] , [ 'str' , ( ... a ) => a . map ( e => printExp ( e , false ) ) . join ( '' ) ] , [ 'subs' , ( a , from , to ? ) => a . substr ( from , to ) ] , [ 'meta' , getMeta ] , [ 'console.log' , console . log ] , [ 'with-meta' , withMeta ] , [ 'set-meta!' , setMeta ] , [ 'with-meta-sugar' , ( m , a ) => withMeta ( a , m ) ] , [ 'atom' , ( a ) => new MalAtom ( a ) , ] , [ 'atom?' , ( a ) => a instanceof MalAtom ] , [ 'deref' , ( atm ) => atm . value ] , [ 'reset!' , ( atm , a ) => ( atm . value = a ) ] , [ 'swap!' , ( atm , fn , ... args ) => ( atm . value = fn ( atm . value , ... args ) ) , ] , [ 'range' , ( ... args ) => { const ret = [ ] ; let start = 0 , end = 0 , step = Math . sign ( end - start ) ; if ( args . length === 1 ) { ; [ end ] = args ; } else if ( args . length === 2 ) { ; [ start , end ] = args ; } else { ; [ start , end , step ] = args ; } if ( start !== end ) { if ( ( end - start ) * step <= 0 ) { step = Math . sign ( end - start ) * Math . abs ( step ) || 1 ; } for ( let i = start ; step > 0 ? i < end : i > end ; i += step ) { ret . push ( i ) ; } } return ret ; } , ] , [ 'rnd' , ( a ) => seedrandom ( a ) ( ) ] , [ 'spit' , ( f , content ) => { if ( isNodeJS ) { const fs = require ( 'fs' ) ; const path = require ( 'path' ) ; fs . writeFileSync ( path . join ( process . cwd ( ) , f ) as string , content as string ) ; } else { throw new MalError ( 'Cannot spit on browser' ) ; } return null ; } , ] , ] as [ string , MalVal ] [ ] ; Object . getOwnPropertyNames ( Math ) . forEach ( k => Exports . push ( [ k , ( Math as any ) [ k ] ] ) ) ; const Exp = L ( S ( 'do' ) , ... Exports . map ( ( [ sym , body ] ) => L ( S ( 'def' ) , S ( sym ) , body ) ) ) ; ( globalThis as any ) [ 'glisp_library' ] = Exp ; export default Exp ;