#
# mal (Make a Lisp) environment definition
#

if [ -z "${__mal_env_included__}" ]; then
__mal_env_included=true

source $(dirname $0)/types.sh

# Any environment is a hash_map with an __outer__ key that refers to
# a parent environment (or nil)
ENV () {
    r=
    _hash_map
    local env="${r}"
    if [[ "${1}" ]]; then
        outer="${1}"; shift
        _assoc! "${env}" "__outer__" "${outer}"
    else
        _assoc! "${env}" "__outer__" "${__nil}"
    fi
    r="${env}"

    if [[ "${1}" && "${@}" ]]; then
        local binds=(${ANON["${1}"]}); shift
        local idx=0
        while [[ "${binds["${idx}"]}" ]]; do
            local fp="${ANON["${binds["${idx}"]}"]}"
            if [[ "${fp}" == "&" ]]; then
                idx=$(( idx + 1 ))
                fp="${ANON["${binds["${idx}"]}"]}"
                _list "${@}"
                _assoc! "${env}" "${fp}" "${r}"
                break
            else
                _assoc! "${env}" "${fp}" "${1}"
                shift
                idx=$(( idx + 1 ))
            fi
        done
    fi
    r="${env}"
}

# Find the environment with the key set and return the environment
ENV_FIND () {
    if _contains? "${1}" "${ANON["${2}"]}"; then
        r="${1}"
    else
        local obj="${ANON["${1}"]}"
        eval 'local outer=${'${obj}'["__outer__"]}'
        if [[ "${outer}" && "${outer}" != "${__nil}" ]]; then
            ENV_FIND "${outer}" "${2}"
        else
            r=
        fi
    fi
}

# Find the environment with the key set and return the value of the
# key in that environment. If no environment contains the key then
# return an error
ENV_GET () {
    ENV_FIND "${1}" "${2}"
    local env="${r}"
    local key="${ANON["${2}"]}"
    if [[ "${r}" ]]; then
        local obj="${ANON["${env}"]}"
        eval 'r=${'${obj}'["'${key}'"]}'
    else
        _error "'${key}' not found"
    fi
}

ENV_SET () {
    local key="${ANON["${2}"]}"
    _assoc! "${1}" "${key}" "${3}"
}

fi
