neptune-lang 0.1.3

An embeddable scripting language
Documentation
const {ecall, generateStackTrace, currentTask} = _getModule('vm')

export class Error {
    construct(message) {
        this.message = message
        this.stack = generateStackTrace(1)
        this.task = currentTask()
    }
    toString() {
        return 'In \(this.task): \(this.getClass().name()): \(this.message)\n\(this.stack)'
    }
}

export class OverflowError extends Error {
    construct(message) {
        super.construct(message)
    }
}

export class TypeError extends Error {
    construct(message) {
        super.construct(message)
    }
}

export class ArgumentError extends Error {
    construct(message) {
        super.construct(message)
    }
}

export class PropertyError extends Error {
    construct(message) {
        super.construct(message)
    }
}

export class NoMethodError extends Error {
    construct(message) {
        super.construct(message)
    }
}

export class NoModuleVariableError extends Error {
    construct(message) {
        super.construct(message)
    }
}

export class IndexError extends Error {
    construct(message) {
        super.construct(message)
    }
}

export class KeyError extends Error {
    construct(message) {
        super.construct(message)
    }
}

export class CompileError extends Error {
    construct(message) {
        super.construct(message)
    }
}

export class EFuncError extends Error{
    construct(message) {
        super.construct(message)
    }
}

export class ModuleNotFoundError extends Error{
    construct(message) {
        super.construct(message)
    }
}

export class DeadlockError extends Error{
    construct(message) {
        super.construct(message)
    }
}

export fun import(moduleName) {
    if moduleName.getClass() !== String {
        throw new TypeError('The first argument must be a String, not \(moduleName.getClass().name())')
    }
    let module = _getModule(moduleName)
    if module {
        return module
    }else {
        moduleName = ecall(@resolveModule,{callerModule: _getCallerModule(), moduleName})
        module = _getModule(moduleName)
        if module {
            return module
        } else {
            let source = ecall(@fetchModule, moduleName)
            ecall(@compile, {moduleName, eval: false, source})()
            return _getModule(moduleName)
        }
    }
}

export fun eval(source) {
    if source.getClass() !== String {
        throw new TypeError('The first argument must be a String, not \(source.getClass().name())')
    }
    let {function,isExpr} = ecall(@compile, {moduleName: _getCallerModule(), eval: true, source})
    if(isExpr){
        return function()
    }else{
        function()
        return null
    }
}

export fun exec(source) {
    if source.getClass() !== String {
        throw new TypeError('The first argument must be a String, not \(source.getClass().name())')
    }
    ecall(@compile, {moduleName: _getCallerModule(), eval: false, source})()
}

export class Iterator {
    each(f) {
        for elem in this {
            f(elem)
        }
    }

    all(f) {
        for elem in this {
            if !f(elem) {
                return false
            }
        }
        return true
    }

    any(f) {
        for elem in this {
            if f(elem) {
                return true
            }
        }
        return false
    }

    map(f) {
        return new IteratorMap(this, f)
    }

    filter(f) {
        return new IteratorFilter(this, f)
    }

    collect() {
        let a = []
        for elem in this {
            a.push(elem)
        }
        return a
    }

    count() {
        let count = 0
        for _ in this {
            count+=1
        }
        return count
    }

    reduce(f, initial) {
        for elem in this {
            initial = f(initial, elem)
        }
        return initial
    }
}

class IteratorMap extends Iterator {
    construct(iter, f) {
        this._iter = iter
        this._f = f
    }

    hasNext() {
        return this._iter.hasNext()
    }

    next() {
        return this._f(this._iter.next())
    }
}

class IteratorFilter extends Iterator {
    construct(iter, f) {
        this._iter = iter
        this._f = f
        this._nextObject = null
        this._nextObjectSet = false
    }

    hasNext() {
        return this._nextObjectSet or this._setNextObject()
    }

    next() {
        if !this._nextObjectSet and !this._setNextObject(){
            return null
        }
        this._nextObjectSet = false
        return this._nextObject
    }

    _setNextObject(){
        while this._iter.hasNext(){
            let obj = this._iter.next()
            if this._f(obj){
                this._nextObject = obj
                this._nextObjectSet = true
                return true
            }
        }
        return false
    }
}

_extendClass(ArrayIterator, Iterator)
_extendClass(MapIterator, Iterator)
_extendClass(StringIterator, Iterator)
_extendClass(Range, Iterator)

class ArraySort {
    _partition(low, high, comp) {
        let i = low - 1
        let pivot = this[high]

        for j in low..high {
            if comp(this[j], pivot) {
                i += 1
                let t = this[i]
                this[i] = this[j]
                this[j] = t
            }
        }
        let t = this[i+1]
        this[i+1] = this[high]
        this[high] = t
        return i+1
    }

    _quickSort(low, high, comp) {
        if low < high {
            let pivot = this._partition(low, high, comp)
            this._quickSort(low, pivot - 1, comp)
            this._quickSort(pivot + 1, high, comp)
        }
    }

    sort(f) {
        this._quickSort(0, this.len()-1, f)
    }
}

_copyMethods(Array, ArraySort)

export fun join(tasks) {
	if tasks.getClass() != Array {
		throw new TypeError('The first argument must be a list of tasks')
	}
	for task in tasks.iter() {
		if task.getClass() != Task {
			throw new TypeError('The first argument must be a list of tasks')
		}
	}
	let monitorChan = new Channel()
	for task in tasks.iter() {
		task.monitor(monitorChan)
	}
	for i in 0..tasks.len() {
		let task = monitorChan.recv()
		if task.status() === @killed {
			let reason = task.getUncaughtException()
			for task in tasks {
				task.kill(reason)
			}	
			break
		}
	}
}