Login
th1ish.th1ish at [2c40e2033b]
Login

File th1ish/th1ish.th1ish artifact a6fd9169b8 part of check-in 2c40e2033b


/**
    This file will be automatically included by the th1ish binary if
    found during startup and if named the same as the binary with a
    ".th1ish" extension. If the binary name ends in ".exe" or ".EXE"
    then that part of the name is ignored.

    This script gets run in the global scope.

    This particular init script performs the following:

    - Extends api.PathFinder with more useful functionality.

    - If loadModule() and/or import() are defined then it adds
    api.loadModule() and api.import(), both of which use PathFinder
    instances to search for modules/scripts. Their PathFinder
    instances are available via api.(loadModule|import).path.


    - If getenv() is defined then environment variables
    TH1ISH_SCRIPT_PATH and TH1ISH_MODULE_PATH are treated as colon- or
    simecolon-delimited directories to search for scripts/modules. By
    default only the current directory is searched. TH1ISH_AUTOLOAD_MODULES
    will be treated as a comma-separated list of module names
    to load at startup.
    

*/
scope {
    if('undefined' === typename api){return}
    else if(api.loadModule && api.loadModule.impl){
        /* Been there, done that. */
        return api
    }
    if('function' === typename strftime){
        api.string.strftime = strftime
    }
    if('undefined' !== typename api.PathFinder){
        const PF = api.PathFinder
        const loadModule = ('function' === typename loadModule) ? loadModule : undefined
        const import = ('function' === typename import) ? import : undefined
        const getenv = ('function' === typename getenv) ? getenv : undefined
        const pwd = getenv ? [getenv 'PWD'] : undefined

        /**
            Utility function which splits str on
            sep and appends the resulting array
            to dest. dest need not be an array but
            must have a push() method which acts like
            Array.push() does.

            Returns this object.

            If v does not contain sep then it falls
            back to trying this.splitEntriesTo.fallbackSeparator.
        */
        PF.splitEntriesTo = proc(dest,str,sep=':'){
            !str && return this
            assert 'function' === typename dest.push
            const fb = argv.callee.fallbackSeparator
            (fb!==sep) &&
                /* BUG: when conventional call() syntax is DISabled
                i'm getting a 'str.indexOf is not a function' exception
                here. ??? */
                ([str.indexOf sep] < 0) &&
                ([str.indexOf fb] >= 0) &&
                (sep = fb)
            var i = 0, li = [str.split sep]
            const n = [li.length]
            for {}{i<n}{i+=1}{
                [dest.push li.(i)]
            }
            return this
        }
        PF.splitEntriesTo.fallbackSeparator = ';'

        /**
            setPathFromEnv() expects an environment variable
            name as an argument. If such an environment variable
            is found then it is treated as a delimited list and
            replaces the current list of paths. This modifies
            this.prefix directly, as opposed to creating a new
            array, so it may have side-effects on client-taken
            references. See splitEntriesTo() for details on
            the splitting.

            Returns a truthy value if finds an environment variable,
            else a falsy value.
        */
        if(getenv){
            PF.setPathFromEnv = $proc(v, sep=':'){
                const e = getenv(v)
                e || return
                if(this.prefix) {[this.prefix.length 0]}
                else {this.prefix = array[]}
                [this.splitEntriesTo this.prefix e sep]
                return true
            }.importSymbols 'getenv'
        }

        if(loadModule){
            /**
                api.loadModule() is a loadModule() proxy which uses the
                api.loadModule.path search path to search for DLLs.

                If onLoad is a function then after loading the module,
                onLoad(namespace, baseName, fullname) is called.

                Returns this object.
            */
            api.loadModule = proc(baseName,namespace = this, onLoad){
                const ce = argv.callee
                const n = [ce.path.search baseName] ||
                    throw ["No loadable module found named [".concat baseName '].']
                [ce.impl n /*baseName?*/ namespace]
                ('function' === typename onLoad) && scope {
                    [onLoad namespace baseName n]
                }
                return this
            }
            api.loadModule.impl = loadModule
            const pf = api.loadModule.path = [PF]
            pf.suffix = array['.so','.dll']
            if(pf.setPathFromEnv && ![pf.setPathFromEnv 'TH1ISH_MODULE_PATH']) {
                /**
                    ^^^ Reminder to self: FIXME: if (ie->skipNLLevel>0) then
                    that behaves _completely_ wrong, causing pf's state
                    to be very unexpected when splitEntriesTo is called.
                */
                pf.prefix = array['','.']
                pwd && [pf.prefix.push pwd]
            }
        }

        if(import){
            /**
                api.import() is an import() proxy which uses the
                api.import.path search path to search for scripts.
                Returns the result of calling import() or throws
                on error.
            */
            api.import = proc(baseName){
                const ce = argv.callee
                const n = [ce.path.search baseName] ||
                    throw ["No script file found named [".concat
                            baseName '].']
                return [ce.impl n]
            }
            api.import.impl = import
            const pf = api.import.path = [PF]
            pf.suffix = array['.th1ish']
            if(!pf.setPathFromEnv || ![pf.setPathFromEnv 'TH1ISH_SCRIPT_PATH']) {
                pf.prefix = array['','.'];
                pwd && [pf.prefix.push pwd];
            }
        }

        // Try to pre-load some plugins...
        while('function' === typename api.loadModule){
            //const testOnLoad = proc(ns,bn,fn){print("Loaded module",bn,"from",fn)}
            var list = getenv ? [getenv 'TH1ISH_AUTOLOAD_MODULES'] : false
            list && (list = [list.split ','])
            list || break
            { Here's one potential list:
                list = array [
                'cpdoish',
                'linenoiseish',
                'pcreish',
                'uuidish',
                'sha1ish',
                'popenish'
                ]
            }
            [list.eachIndex proc(v,i){
                v = (v ? [v.trim] : 0)
                v || return
                //var ex = catch{[api.loadModule v api /*testOnLoad*/]}
                //ex && print(__SRC,"Warning: module not loaded:",ex.message || v)
                [api.loadModule v api /*testOnLoad*/]
            }]
            /* BUG:
                the above call with an outer [...] syntax IF we use
                an inlined array rather than an array reference.
                Evaluates to the array instead of calling the function.
                It never reaches the '.' after the array closer.
                Not sure (===no idea) why. Conventional call syntax
                works. 
            */
            break
        }
    }
    api
}