| 1 | #!/usr/bin/env bash |
| 2 | |
| 3 | source $(dirname $0)/reader.sh |
| 4 | source $(dirname $0)/printer.sh |
| 5 | source $(dirname $0)/env.sh |
| 6 | source $(dirname $0)/core.sh |
| 7 | |
| 8 | # read |
| 9 | READ () { |
| 10 | [ "${1}" ] && r="${1}" || READLINE |
| 11 | READ_STR "${r}" |
| 12 | } |
| 13 | |
| 14 | # eval |
| 15 | EVAL_AST () { |
| 16 | local ast="${1}" env="${2}" |
| 17 | #_pr_str "${ast}"; echo "EVAL_AST '${ast}:${r} / ${env}'" |
| 18 | _obj_type "${ast}"; local ot="${r}" |
| 19 | case "${ot}" in |
| 20 | symbol) |
| 21 | ENV_GET "${env}" "${ast}" |
| 22 | return ;; |
| 23 | list) |
| 24 | _map_with_type _list EVAL "${ast}" "${env}" ;; |
| 25 | vector) |
| 26 | _map_with_type _vector EVAL "${ast}" "${env}" ;; |
| 27 | hash_map) |
| 28 | local res="" val="" hm="${ANON["${ast}"]}" |
| 29 | _hash_map; local new_hm="${r}" |
| 30 | eval local keys="\${!${hm}[@]}" |
| 31 | for key in ${keys}; do |
| 32 | eval val="\${${hm}[\"${key}\"]}" |
| 33 | EVAL "${val}" "${env}" |
| 34 | _assoc! "${new_hm}" "${key}" "${r}" |
| 35 | done |
| 36 | r="${new_hm}" ;; |
| 37 | *) |
| 38 | r="${ast}" ;; |
| 39 | esac |
| 40 | } |
| 41 | |
| 42 | EVAL () { |
| 43 | local ast="${1}" env="${2}" |
| 44 | r= |
| 45 | [[ "${__ERROR}" ]] && return 1 |
| 46 | #_pr_str "${ast}"; echo "EVAL '${r} / ${env}'" |
| 47 | _obj_type "${ast}"; local ot="${r}" |
| 48 | if [[ "${ot}" != "list" ]]; then |
| 49 | EVAL_AST "${ast}" "${env}" |
| 50 | return |
| 51 | fi |
| 52 | |
| 53 | # apply list |
| 54 | _nth "${ast}" 0; local a0="${r}" |
| 55 | _nth "${ast}" 1; local a1="${r}" |
| 56 | _nth "${ast}" 2; local a2="${r}" |
| 57 | case "${ANON["${a0}"]}" in |
| 58 | def!) EVAL "${a2}" "${env}" |
| 59 | [[ "${__ERROR}" ]] && return 1 |
| 60 | ENV_SET "${env}" "${a1}" "${r}" |
| 61 | return ;; |
| 62 | let*) ENV "${env}"; local let_env="${r}" |
| 63 | local let_pairs=(${ANON["${a1}"]}) |
| 64 | local idx=0 |
| 65 | #echo "let: [${let_pairs[*]}] for ${a2}" |
| 66 | while [[ "${let_pairs["${idx}"]}" ]]; do |
| 67 | EVAL "${let_pairs[$(( idx + 1))]}" "${let_env}" |
| 68 | ENV_SET "${let_env}" "${let_pairs[${idx}]}" "${r}" |
| 69 | idx=$(( idx + 2)) |
| 70 | done |
| 71 | EVAL "${a2}" "${let_env}" |
| 72 | return ;; |
| 73 | do) _rest "${ast}" |
| 74 | EVAL_AST "${r}" "${env}" |
| 75 | [[ "${__ERROR}" ]] && r= && return 1 |
| 76 | _last "${r}" |
| 77 | return ;; |
| 78 | if) EVAL "${a1}" "${env}" |
| 79 | [[ "${__ERROR}" ]] && return 1 |
| 80 | if [[ "${r}" == "${__false}" || "${r}" == "${__nil}" ]]; then |
| 81 | # eval false form |
| 82 | _nth "${ast}" 3; local a3="${r}" |
| 83 | if [[ "${a3}" ]]; then |
| 84 | EVAL "${a3}" "${env}" |
| 85 | else |
| 86 | r="${__nil}" |
| 87 | fi |
| 88 | else |
| 89 | # eval true condition |
| 90 | EVAL "${a2}" "${env}" |
| 91 | fi |
| 92 | return ;; |
| 93 | fn*) _function "ENV \"${env}\" \"${a1}\" \"\${@}\"; \ |
| 94 | EVAL \"${a2}\" \"\${r}\"" |
| 95 | return ;; |
| 96 | *) EVAL_AST "${ast}" "${env}" |
| 97 | [[ "${__ERROR}" ]] && r= && return 1 |
| 98 | local el="${r}" |
| 99 | _first "${el}"; local f="${ANON["${r}"]}" |
| 100 | _rest "${el}"; local args="${ANON["${r}"]}" |
| 101 | #echo "invoke: ${f} ${args}" |
| 102 | eval ${f} ${args} |
| 103 | return ;; |
| 104 | esac |
| 105 | } |
| 106 | |
| 107 | # print |
| 108 | PRINT () { |
| 109 | if [[ "${__ERROR}" ]]; then |
| 110 | _pr_str "${__ERROR}" yes |
| 111 | r="Error: ${r}" |
| 112 | __ERROR= |
| 113 | else |
| 114 | _pr_str "${1}" yes |
| 115 | fi |
| 116 | } |
| 117 | |
| 118 | # repl |
| 119 | ENV; REPL_ENV="${r}" |
| 120 | REP () { |
| 121 | r= |
| 122 | READ "${1}" |
| 123 | EVAL "${r}" "${REPL_ENV}" |
| 124 | PRINT "${r}" |
| 125 | } |
| 126 | |
| 127 | # core.sh: defined using bash |
| 128 | _fref () { |
| 129 | _symbol "${1}"; local sym="${r}" |
| 130 | _function "${2} \"\${@}\"" |
| 131 | ENV_SET "${REPL_ENV}" "${sym}" "${r}" |
| 132 | } |
| 133 | for n in "${!core_ns[@]}"; do _fref "${n}" "${core_ns["${n}"]}"; done |
| 134 | |
| 135 | # core.mal: defined using the language itself |
| 136 | REP "(def! not (fn* (a) (if a false true)))" |
| 137 | |
| 138 | # repl loop |
| 139 | while true; do |
| 140 | READLINE "user> " || exit "$?" |
| 141 | [[ "${r}" ]] && REP "${r}" && echo "${r}" |
| 142 | done |