#!/usr/bin/env bash source $(dirname $0)/reader.sh source $(dirname $0)/printer.sh source $(dirname $0)/env.sh source $(dirname $0)/core.sh # read proc READ { test ${1} && setvar r = "${1}" || READLINE READ_STR ${r} } # eval proc EVAL_AST { local ast="${1}" env="${2}" #_pr_str "${ast}"; echo "EVAL_AST '${ast}:${r} / ${env}'" _obj_type ${ast}; local ot="${r}" case{ symbol { ENV_GET ${env} ${ast} return } list { _map_with_type _list EVAL ${ast} ${env} } vector { _map_with_type _vector EVAL ${ast} ${env} } hash_map { local res="" key= val="" hm="${ANON["${ast}"]}" _hash_map; local new_hm="${r}" eval local keys="\${!${hm}[@]}" for key in ${keys} { eval val="\${${hm}[\"${key}\"]}" EVAL ${val} ${env} _assoc! ${new_hm} ${key} ${r} } setvar r = "${new_hm}" } * { setvar r = "${ast}" } } } proc EVAL { local ast="${1}" env="${2}" while true { setvar r = '' [[ "${__ERROR}" ]] && return 1 #_pr_str "${ast}"; echo "EVAL '${r} / ${env}'" _obj_type ${ast}; local ot="${r}" if [[ "${ot}" != "list" ]] { EVAL_AST ${ast} ${env} return } _empty? ${ast} && setvar r = "${ast}" && return # apply list _nth ${ast} 0; local a0="${r}" _nth ${ast} 1; local a1="${r}" _nth ${ast} 2; local a2="${r}" case{ def! { EVAL ${a2} ${env} [[ "${__ERROR}" ]] && return 1 ENV_SET ${env} ${a1} ${r} return } let* { ENV ${env}; local let_env="${r}" local let_pairs=(${ANON["${a1}"]}) local idx=0 { EVAL ${let_pairs[$(( idx + 1))]} ${let_env} ENV_SET ${let_env} ${let_pairs[${idx}]} ${r} setvar idx = $(( idx + 2)) } setvar ast = "${a2}" setvar env = "${let_env}" # Continue loop } do { _count ${ast} _slice ${ast} 1 $(( ${r} - 2 )) EVAL_AST ${r} ${env} [[ "${__ERROR}" ]] && setvar r = '' && return 1 _last ${ast} setvar ast = "${r}" # Continue loop } if { EVAL ${a1} ${env} [[ "${__ERROR}" ]] && return 1 if [[ "${r}" == "${__false}" || "${r}" == "${__nil}" ]] { # eval false form _nth ${ast} 3; local a3="${r}" if [[ "${a3}" ]] { setvar ast = "${a3}" } else { setvar r = "${__nil}" return } } else { # eval true condition setvar ast = "${a2}" } # Continue loop } fn* { _function "ENV \"${env}\" \"${a1}\" \"\${@}\"; \ EVAL \"${a2}\" \"\${r}\"" \ ${a2} ${env} ${a1} return } * { EVAL_AST ${ast} ${env} [[ "${__ERROR}" ]] && setvar r = '' && return 1 local el="${r}" _first ${el}; local f="${ANON["${r}"]}" _rest ${el}; local args="${ANON["${r}"]}" #echo "invoke: [${f}] ${args}" if [[ "${f//@/ }" != "${f}" ]] { set -- ${f//@/ } setvar ast = "${2}" ENV ${3} ${4} ${args} setvar env = "${r}" } else { eval ${f%%@*} ${args} return } # Continue loop } } } } # print proc PRINT { if [[ "${__ERROR}" ]] { _pr_str ${__ERROR} yes setvar r = ""Error: ${r}"" setvar __ERROR = '' } else { _pr_str ${1} yes } } # repl ENV; setvar REPL_ENV = "${r}" proc REP { setvar r = '' READ ${1} EVAL ${r} ${REPL_ENV} PRINT ${r} } # core.sh: defined using bash proc _fref { _symbol ${1}; local sym="${r}" _function "${2} \"\${@}\"" ENV_SET ${REPL_ENV} ${sym} ${r} } for n in "${!core_ns[@]}" { _fref ${n} ${core_ns["${n}"]}; } # core.mal: defined using the language itself REP "(def! not (fn* (a) (if a false true)))" # repl loop while true { READLINE "user> " || exit "$?" [[ "${r}" ]] && REP ${r} && echo ${r} }