diff options
| author | Joel Martin <github@martintribe.org> | 2014-04-16 23:57:50 -0500 |
|---|---|---|
| committer | Joel Martin <github@martintribe.org> | 2014-04-16 23:57:50 -0500 |
| commit | 8cb5cda46cf3aef847ae3926dc53a5e5f87fe261 (patch) | |
| tree | 13e5b2878f19ee24272ead8a92a9cb84b33ad0e5 | |
| parent | a05f7822b10ed4cdd61ed8384299a003baf1c1c6 (diff) | |
| download | mal-8cb5cda46cf3aef847ae3926dc53a5e5f87fe261.tar.gz mal-8cb5cda46cf3aef847ae3926dc53a5e5f87fe261.zip | |
All: move some fns to core. Major cleanup.
- Don't import/require core until step4.
- Define cond/or macros from step8
118 files changed, 1706 insertions, 2136 deletions
diff --git a/bash/core.sh b/bash/core.sh index d5750e6..e0cba38 100644 --- a/bash/core.sh +++ b/bash/core.sh @@ -6,6 +6,7 @@ if [ -z "${__mal_core_included__}" ]; then __mal_core_included=true source $(dirname $0)/types.sh +source $(dirname $0)/reader.sh source $(dirname $0)/printer.sh # Exceptions/Errors @@ -87,6 +88,21 @@ println () { r="${__nil}"; } +readline () { + READLINE "${ANON["${1}"]}" && _string "${r}" || r="${__nil}" +} + +read_string () { + READ_STR "${ANON["${1}"]}" +} + +slurp () { + local lines + mapfile lines < "${ANON["${1}"]}" + local text="${lines[*]}"; text=${text//$'\n' /$'\n'} + _string "${text}" +} + # Function functions function? () { _function? "${1}" && r="${__true}" || r="${__false}"; } @@ -155,11 +171,6 @@ get () { [[ "${r}" ]] || r="${__nil}" } -_contains? () { - local obj="${ANON["${1}"]}" - #echo "_contains? ${1} ${2} -> \${${obj}[\"${2}\"]+isset}" - eval [[ "\${${obj}[\"${2}\"]+isset}" ]] -} contains? () { _contains? "${1}" "${ANON["${2}"]}" && r="${__true}" || r="${__false}"; } keys () { @@ -215,29 +226,6 @@ nth () { _nth "${1}" "${ANON["${2}"]}" } -first () { - local temp="${ANON["${1}"]}" - r="${temp%% *}" - [ "${r}" ] || r="${__nil}" -} - -# Creates a new vector/list of the everything after but the first -# element -rest () { - local temp="${ANON["${1}"]}" - _list - if [[ "${temp#* }" == "${temp}" ]]; then - ANON["${r}"]= - else - ANON["${r}"]="${temp#* }" - fi -} - -last () { - local temp="${ANON["${1}"]}" - r="${temp##* }" -} - empty? () { _empty? "${1}" && r="${__true}" || r="${__false}"; } count () { @@ -323,10 +311,14 @@ declare -A core_ns=( [true?]=true? [false?]=false? [symbol?]=symbol? + [pr-str]=pr_str [str]=str [prn]=prn [println]=println + [readline]=readline + [read-string]=read_string + [slurp]=slurp [<]=num_lt [<=]=num_lte [>]=num_gt @@ -353,8 +345,8 @@ declare -A core_ns=( [cons]=cons [concat]=concat [nth]=nth - [first]=first - [rest]=rest + [first]=_first + [rest]=_rest [empty?]=empty? [count]=count [conj]=conj diff --git a/bash/step2_eval.sh b/bash/step2_eval.sh index 0f03a79..4572223 100755 --- a/bash/step2_eval.sh +++ b/bash/step2_eval.sh @@ -4,11 +4,10 @@ INTERACTIVE=${INTERACTIVE-yes} source $(dirname $0)/reader.sh source $(dirname $0)/printer.sh -source $(dirname $0)/core.sh # READ: read and parse input READ () { - READLINE + [ "${1}" ] && r="${1}" || READLINE READ_STR "${r}" } @@ -56,8 +55,8 @@ EVAL () { EVAL_AST "${ast}" "${env}" [[ "${__ERROR}" ]] && return 1 local el="${r}" - first "${el}"; local f="${r}" - rest "${el}"; local args="${ANON["${r}"]}" + _first "${el}"; local f="${r}" + _rest "${el}"; local args="${ANON["${r}"]}" #echo "invoke: ${f} ${args}" eval ${f} ${args} } @@ -76,15 +75,20 @@ PRINT () { # REPL: read, eval, print, loop declare -A REPL_ENV REP () { - READ_STR "${1}" + READ "${1}" || return 1 EVAL "${r}" REPL_ENV PRINT "${r}" } -REPL_ENV["+"]=num_plus -REPL_ENV["-"]=num_minus -REPL_ENV["__STAR__"]=num_multiply -REPL_ENV["/"]=num_divide +plus () { r=$(( ${ANON["${1}"]} + ${ANON["${2}"]} )); _number "${r}"; } +minus () { r=$(( ${ANON["${1}"]} - ${ANON["${2}"]} )); _number "${r}"; } +multiply () { r=$(( ${ANON["${1}"]} * ${ANON["${2}"]} )); _number "${r}"; } +divide () { r=$(( ${ANON["${1}"]} / ${ANON["${2}"]} )); _number "${r}"; } + +REPL_ENV["+"]=plus +REPL_ENV["-"]=minus +REPL_ENV["__STAR__"]=multiply +REPL_ENV["/"]=divide if [[ -n "${INTERACTIVE}" ]]; then while true; do diff --git a/bash/step3_env.sh b/bash/step3_env.sh index 28106ee..f7a9e42 100755 --- a/bash/step3_env.sh +++ b/bash/step3_env.sh @@ -4,12 +4,11 @@ INTERACTIVE=${INTERACTIVE-yes} source $(dirname $0)/reader.sh source $(dirname $0)/printer.sh -source $(dirname $0)/core.sh source $(dirname $0)/env.sh # READ: read and parse input READ () { - READLINE + [ "${1}" ] && r="${1}" || READLINE READ_STR "${r}" } @@ -77,8 +76,8 @@ EVAL () { *) EVAL_AST "${ast}" "${env}" [[ "${__ERROR}" ]] && r= && return 1 local el="${r}" - first "${el}"; local f="${r}" - rest "${el}"; local args="${ANON["${r}"]}" + _first "${el}"; local f="${r}" + _rest "${el}"; local args="${ANON["${r}"]}" #echo "invoke: ${f} ${args}" eval ${f} ${args} return ;; @@ -100,16 +99,20 @@ PRINT () { ENV; REPL_ENV="${r}" REP () { r= - READ_STR "${1}" - EVAL "${r}" ${REPL_ENV} + READ "${1}" || return 1 + EVAL "${r}" "${REPL_ENV}" PRINT "${r}" } -_ref () { ENV_SET "${REPL_ENV}" "${1}" "${2}"; } -_ref "+" num_plus -_ref "-" num_minus -_ref "__STAR__" num_multiply -_ref "/" num_divide +plus () { r=$(( ${ANON["${1}"]} + ${ANON["${2}"]} )); _number "${r}"; } +minus () { r=$(( ${ANON["${1}"]} - ${ANON["${2}"]} )); _number "${r}"; } +multiply () { r=$(( ${ANON["${1}"]} * ${ANON["${2}"]} )); _number "${r}"; } +divide () { r=$(( ${ANON["${1}"]} / ${ANON["${2}"]} )); _number "${r}"; } + +ENV_SET "${REPL_ENV}" "+" plus +ENV_SET "${REPL_ENV}" "-" minus +ENV_SET "${REPL_ENV}" "__STAR__" multiply +ENV_SET "${REPL_ENV}" "/" divide if [[ -n "${INTERACTIVE}" ]]; then while true; do diff --git a/bash/step4_if_fn_do.sh b/bash/step4_if_fn_do.sh index d22ade6..a1b27ed 100755 --- a/bash/step4_if_fn_do.sh +++ b/bash/step4_if_fn_do.sh @@ -4,12 +4,12 @@ INTERACTIVE=${INTERACTIVE-yes} source $(dirname $0)/reader.sh source $(dirname $0)/printer.sh -source $(dirname $0)/core.sh source $(dirname $0)/env.sh +source $(dirname $0)/core.sh # READ: read and parse input READ () { - READLINE + [ "${1}" ] && r="${1}" || READLINE READ_STR "${r}" } @@ -74,10 +74,10 @@ EVAL () { done EVAL "${a2}" "${let_env}" return ;; - do) rest "${ast}" + do) _rest "${ast}" EVAL_AST "${r}" "${env}" [[ "${__ERROR}" ]] && r= && return 1 - last "${r}" + _last "${r}" return ;; if) EVAL "${a1}" "${env}" if [[ "${r}" == "${__false}" || "${r}" == "${__nil}" ]]; then @@ -99,8 +99,8 @@ EVAL () { *) EVAL_AST "${ast}" "${env}" [[ "${__ERROR}" ]] && r= && return 1 local el="${r}" - first "${el}"; local f="${ANON["${r}"]}" - rest "${el}"; local args="${ANON["${r}"]}" + _first "${el}"; local f="${ANON["${r}"]}" + _rest "${el}"; local args="${ANON["${r}"]}" #echo "invoke: ${f} ${args}" eval ${f} ${args} return ;; @@ -122,17 +122,16 @@ PRINT () { ENV; REPL_ENV="${r}" REP () { r= - READ_STR "${1}" - EVAL "${r}" ${REPL_ENV} + READ "${1}" || return 1 + EVAL "${r}" "${REPL_ENV}" PRINT "${r}" } +# core.sh: defined using bash _fref () { _function "${2} \"\${@}\""; ENV_SET "${REPL_ENV}" "${1}" "${r}"; } - -# Import types functions for n in "${!core_ns[@]}"; do _fref "${n}" "${core_ns["${n}"]}"; done -# Defined using the language itself +# core.mal: defined using the language itself REP "(def! not (fn* (a) (if a false true)))" if [[ -n "${INTERACTIVE}" ]]; then diff --git a/bash/step5_tco.sh b/bash/step5_tco.sh index 2d81d84..62446a7 100755 --- a/bash/step5_tco.sh +++ b/bash/step5_tco.sh @@ -4,12 +4,12 @@ INTERACTIVE=${INTERACTIVE-yes} source $(dirname $0)/reader.sh source $(dirname $0)/printer.sh -source $(dirname $0)/core.sh source $(dirname $0)/env.sh +source $(dirname $0)/core.sh # READ: read and parse input READ () { - READLINE + [ "${1}" ] && r="${1}" || READLINE READ_STR "${r}" } @@ -79,7 +79,7 @@ EVAL () { _slice "${ast}" 1 $(( ${r} - 2 )) EVAL_AST "${r}" "${env}" [[ "${__ERROR}" ]] && r= && return 1 - last "${ast}" + _last "${ast}" ast="${r}" # Continue loop ;; @@ -106,8 +106,8 @@ EVAL () { *) EVAL_AST "${ast}" "${env}" [[ "${__ERROR}" ]] && r= && return 1 local el="${r}" - first "${el}"; local f="${ANON["${r}"]}" - rest "${el}"; local args="${ANON["${r}"]}" + _first "${el}"; local f="${ANON["${r}"]}" + _rest "${el}"; local args="${ANON["${r}"]}" #echo "invoke: [${f}] ${args}" if [[ "${f//@/ }" != "${f}" ]]; then set -- ${f//@/ } @@ -139,17 +139,16 @@ PRINT () { ENV; REPL_ENV="${r}" REP () { r= - READ_STR "${1}" - EVAL "${r}" ${REPL_ENV} + READ "${1}" || return 1 + EVAL "${r}" "${REPL_ENV}" PRINT "${r}" } +# core.sh: defined using bash _fref () { _function "${2} \"\${@}\""; ENV_SET "${REPL_ENV}" "${1}" "${r}"; } - -# Import types functions for n in "${!core_ns[@]}"; do _fref "${n}" "${core_ns["${n}"]}"; done -# Defined using the language itself +# core.mal: defined using the language itself REP "(def! not (fn* (a) (if a false true)))" if [[ -n "${INTERACTIVE}" ]]; then diff --git a/bash/step6_file.sh b/bash/step6_file.sh index 1ebba64..b2a3dc8 100755 --- a/bash/step6_file.sh +++ b/bash/step6_file.sh @@ -4,12 +4,12 @@ INTERACTIVE=${INTERACTIVE-yes} source $(dirname $0)/reader.sh source $(dirname $0)/printer.sh -source $(dirname $0)/core.sh source $(dirname $0)/env.sh +source $(dirname $0)/core.sh # READ: read and parse input READ () { - READLINE + [ "${1}" ] && r="${1}" || READLINE READ_STR "${r}" } @@ -79,7 +79,7 @@ EVAL () { _slice "${ast}" 1 $(( ${r} - 2 )) EVAL_AST "${r}" "${env}" [[ "${__ERROR}" ]] && r= && return 1 - last "${ast}" + _last "${ast}" ast="${r}" # Continue loop ;; @@ -106,8 +106,8 @@ EVAL () { *) EVAL_AST "${ast}" "${env}" [[ "${__ERROR}" ]] && r= && return 1 local el="${r}" - first "${el}"; local f="${ANON["${r}"]}" - rest "${el}"; local args="${ANON["${r}"]}" + _first "${el}"; local f="${ANON["${r}"]}" + _rest "${el}"; local args="${ANON["${r}"]}" #echo "invoke: [${f}] ${args}" if [[ "${f//@/ }" != "${f}" ]]; then set -- ${f//@/ } @@ -139,29 +139,18 @@ PRINT () { ENV; REPL_ENV="${r}" REP () { r= - READ_STR "${1}" - EVAL "${r}" ${REPL_ENV} + READ "${1}" || return 1 + EVAL "${r}" "${REPL_ENV}" PRINT "${r}" } +# core.sh: defined using bash _fref () { _function "${2} \"\${@}\""; ENV_SET "${REPL_ENV}" "${1}" "${r}"; } - -# Import types functions for n in "${!core_ns[@]}"; do _fref "${n}" "${core_ns["${n}"]}"; done - -read_string () { READ_STR "${ANON["${1}"]}"; } -_fref "read-string" read_string _eval () { EVAL "${1}" "${REPL_ENV}"; } _fref "eval" _eval -slurp () { - local lines - mapfile lines < "${ANON["${1}"]}" - local text="${lines[*]}"; text=${text//$'\n' /$'\n'} - _string "${text}" -} -_fref "slurp" slurp -# Defined using the language itself +# core.mal: defined using the language itself REP "(def! not (fn* (a) (if a false true)))" REP "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))" diff --git a/bash/step7_quote.sh b/bash/step7_quote.sh index 5aa2991..b541d67 100755 --- a/bash/step7_quote.sh +++ b/bash/step7_quote.sh @@ -4,12 +4,12 @@ INTERACTIVE=${INTERACTIVE-yes} source $(dirname $0)/reader.sh source $(dirname $0)/printer.sh -source $(dirname $0)/core.sh source $(dirname $0)/env.sh +source $(dirname $0)/core.sh # READ: read and parse input READ () { - READLINE + [ "${1}" ] && r="${1}" || READLINE READ_STR "${r}" } @@ -36,7 +36,7 @@ QUASIQUOTE () { if [[ "${ANON["${a00}"]}" == "splice-unquote" ]]; then _symbol concat; local a="${r}" _nth "${a0}" 1; local b="${r}" - rest "${1}" + _rest "${1}" QUASIQUOTE "${r}"; local c="${r}" _list "${a}" "${b}" "${c}" return @@ -45,7 +45,7 @@ QUASIQUOTE () { fi _symbol cons; local a="${r}" QUASIQUOTE "${a0}"; local b="${r}" - rest "${1}" + _rest "${1}" QUASIQUOTE "${r}"; local c="${r}" _list "${a}" "${b}" "${c}" return @@ -124,7 +124,7 @@ EVAL () { _slice "${ast}" 1 $(( ${r} - 2 )) EVAL_AST "${r}" "${env}" [[ "${__ERROR}" ]] && r= && return 1 - last "${ast}" + _last "${ast}" ast="${r}" # Continue loop ;; @@ -151,8 +151,8 @@ EVAL () { *) EVAL_AST "${ast}" "${env}" [[ "${__ERROR}" ]] && r= && return 1 local el="${r}" - first "${el}"; local f="${ANON["${r}"]}" - rest "${el}"; local args="${ANON["${r}"]}" + _first "${el}"; local f="${ANON["${r}"]}" + _rest "${el}"; local args="${ANON["${r}"]}" #echo "invoke: [${f}] ${args}" if [[ "${f//@/ }" != "${f}" ]]; then set -- ${f//@/ } @@ -184,29 +184,18 @@ PRINT () { ENV; REPL_ENV="${r}" REP () { r= - READ_STR "${1}" + READ "${1}" || return 1 EVAL "${r}" "${REPL_ENV}" PRINT "${r}" } +# core.sh: defined using bash _fref () { _function "${2} \"\${@}\""; ENV_SET "${REPL_ENV}" "${1}" "${r}"; } - -# Import types functions for n in "${!core_ns[@]}"; do _fref "${n}" "${core_ns["${n}"]}"; done - -read_string () { READ_STR "${ANON["${1}"]}"; } -_fref "read-string" read_string _eval () { EVAL "${1}" "${REPL_ENV}"; } _fref "eval" _eval -slurp () { - local lines - mapfile lines < "${ANON["${1}"]}" - local text="${lines[*]}"; text=${text//$'\n' /$'\n'} - _string "${text}" -} -_fref "slurp" slurp -# Defined using the language itself +# core.mal: defined using the language itself REP "(def! not (fn* (a) (if a false true)))" REP "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))" diff --git a/bash/step8_macros.sh b/bash/step8_macros.sh index a32e7f9..0e58404 100755 --- a/bash/step8_macros.sh +++ b/bash/step8_macros.sh @@ -4,12 +4,12 @@ INTERACTIVE=${INTERACTIVE-yes} source $(dirname $0)/reader.sh source $(dirname $0)/printer.sh -source $(dirname $0)/core.sh source $(dirname $0)/env.sh +source $(dirname $0)/core.sh # READ: read and parse input READ () { - READLINE + [ "${1}" ] && r="${1}" || READLINE READ_STR "${r}" } @@ -36,7 +36,7 @@ QUASIQUOTE () { if [[ "${ANON["${a00}"]}" == "splice-unquote" ]]; then _symbol concat; local a="${r}" _nth "${a0}" 1; local b="${r}" - rest "${1}" + _rest "${1}" QUASIQUOTE "${r}"; local c="${r}" _list "${a}" "${b}" "${c}" return @@ -45,7 +45,7 @@ QUASIQUOTE () { fi _symbol cons; local a="${r}" QUASIQUOTE "${a0}"; local b="${r}" - rest "${1}" + _rest "${1}" QUASIQUOTE "${r}"; local c="${r}" _list "${a}" "${b}" "${c}" return @@ -68,7 +68,7 @@ MACROEXPAND () { while IS_MACRO_CALL "${ast}" "${env}"; do _nth "${ast}" 0; local a0="${r}" ENV_GET "${env}" "${ANON["${a0}"]}"; local mac="${ANON["${r}"]}" - rest "${ast}" + _rest "${ast}" ${mac%%@*} ${ANON["${r}"]} ast="${r}" done @@ -160,7 +160,7 @@ EVAL () { _slice "${ast}" 1 $(( ${r} - 2 )) EVAL_AST "${r}" "${env}" [[ "${__ERROR}" ]] && r= && return 1 - last "${ast}" + _last "${ast}" ast="${r}" # Continue loop ;; @@ -187,8 +187,8 @@ EVAL () { *) EVAL_AST "${ast}" "${env}" [[ "${__ERROR}" ]] && r= && return 1 local el="${r}" - first "${el}"; local f="${ANON["${r}"]}" - rest "${el}"; local args="${ANON["${r}"]}" + _first "${el}"; local f="${ANON["${r}"]}" + _rest "${el}"; local args="${ANON["${r}"]}" #echo "invoke: [${f}] ${args}" if [[ "${f//@/ }" != "${f}" ]]; then set -- ${f//@/ } @@ -220,31 +220,22 @@ PRINT () { ENV; REPL_ENV="${r}" REP () { r= - READ_STR "${1}" + READ "${1}" || return 1 EVAL "${r}" "${REPL_ENV}" PRINT "${r}" } +# core.sh: defined using bash _fref () { _function "${2} \"\${@}\""; ENV_SET "${REPL_ENV}" "${1}" "${r}"; } - -# Import types functions for n in "${!core_ns[@]}"; do _fref "${n}" "${core_ns["${n}"]}"; done - -read_string () { READ_STR "${ANON["${1}"]}"; } -_fref "read-string" read_string _eval () { EVAL "${1}" "${REPL_ENV}"; } _fref "eval" _eval -slurp () { - local lines - mapfile lines < "${ANON["${1}"]}" - local text="${lines[*]}"; text=${text//$'\n' /$'\n'} - _string "${text}" -} -_fref "slurp" slurp -# Defined using the language itself +# core.mal: defined using the language itself REP "(def! not (fn* (a) (if a false true)))" REP "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))" +REP "(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))" +REP "(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) \`(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))" if [[ "${1}" ]]; then echo "${@}" diff --git a/bash/step9_interop.sh b/bash/step9_interop.sh index dfa1e2c..c2b0931 100755 --- a/bash/step9_interop.sh +++ b/bash/step9_interop.sh @@ -4,12 +4,12 @@ INTERACTIVE=${INTERACTIVE-yes} source $(dirname $0)/reader.sh source $(dirname $0)/printer.sh -source $(dirname $0)/core.sh source $(dirname $0)/env.sh +source $(dirname $0)/core.sh # READ: read and parse input READ () { - READLINE + [ "${1}" ] && r="${1}" || READLINE READ_STR "${r}" } @@ -36,7 +36,7 @@ QUASIQUOTE () { if [[ "${ANON["${a00}"]}" == "splice-unquote" ]]; then _symbol concat; local a="${r}" _nth "${a0}" 1; local b="${r}" - rest "${1}" + _rest "${1}" QUASIQUOTE "${r}"; local c="${r}" _list "${a}" "${b}" "${c}" return @@ -45,7 +45,7 @@ QUASIQUOTE () { fi _symbol cons; local a="${r}" QUASIQUOTE "${a0}"; local b="${r}" - rest "${1}" + _rest "${1}" QUASIQUOTE "${r}"; local c="${r}" _list "${a}" "${b}" "${c}" return @@ -68,7 +68,7 @@ MACROEXPAND () { while IS_MACRO_CALL "${ast}" "${env}"; do _nth "${ast}" 0; local a0="${r}" ENV_GET "${env}" "${ANON["${a0}"]}"; local mac="${ANON["${r}"]}" - rest "${ast}" + _rest "${ast}" ${mac%%@*} ${ANON["${r}"]} ast="${r}" done @@ -169,7 +169,7 @@ EVAL () { _slice "${ast}" 1 $(( ${r} - 2 )) EVAL_AST "${r}" "${env}" [[ "${__ERROR}" ]] && r= && return 1 - last "${ast}" + _last "${ast}" ast="${r}" # Continue loop ;; @@ -196,8 +196,8 @@ EVAL () { *) EVAL_AST "${ast}" "${env}" [[ "${__ERROR}" ]] && r= && return 1 local el="${r}" - first "${el}"; local f="${ANON["${r}"]}" - rest "${el}"; local args="${ANON["${r}"]}" + _first "${el}"; local f="${ANON["${r}"]}" + _rest "${el}"; local args="${ANON["${r}"]}" #echo "invoke: [${f}] ${args}" if [[ "${f//@/ }" != "${f}" ]]; then set -- ${f//@/ } @@ -229,31 +229,22 @@ PRINT () { ENV; REPL_ENV="${r}" REP () { r= - READ_STR "${1}" + READ "${1}" || return 1 EVAL "${r}" "${REPL_ENV}" PRINT "${r}" } +# core.sh: defined using bash _fref () { _function "${2} \"\${@}\""; ENV_SET "${REPL_ENV}" "${1}" "${r}"; } - -# Import types functions for n in "${!core_ns[@]}"; do _fref "${n}" "${core_ns["${n}"]}"; done - -read_string () { READ_STR "${ANON["${1}"]}"; } -_fref "read-string" read_string _eval () { EVAL "${1}" "${REPL_ENV}"; } _fref "eval" _eval -slurp () { - local lines - mapfile lines < "${ANON["${1}"]}" - local text="${lines[*]}"; text=${text//$'\n' /$'\n'} - _string "${text}" -} -_fref "slurp" slurp -# Defined using the language itself +# core.mal: defined using the language itself REP "(def! not (fn* (a) (if a false true)))" REP "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))" +REP "(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))" +REP "(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) \`(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))" if [[ "${1}" ]]; then echo "${@}" diff --git a/bash/stepA_more.sh b/bash/stepA_more.sh index 605ca7f..d968942 100755 --- a/bash/stepA_more.sh +++ b/bash/stepA_more.sh @@ -4,12 +4,12 @@ INTERACTIVE=${INTERACTIVE-yes} source $(dirname $0)/reader.sh source $(dirname $0)/printer.sh -source $(dirname $0)/core.sh source $(dirname $0)/env.sh +source $(dirname $0)/core.sh # READ: read and parse input READ () { - READLINE + [ "${1}" ] && r="${1}" || READLINE READ_STR "${r}" } @@ -36,7 +36,7 @@ QUASIQUOTE () { if [[ "${ANON["${a00}"]}" == "splice-unquote" ]]; then _symbol concat; local a="${r}" _nth "${a0}" 1; local b="${r}" - rest "${1}" + _rest "${1}" QUASIQUOTE "${r}"; local c="${r}" _list "${a}" "${b}" "${c}" return @@ -45,7 +45,7 @@ QUASIQUOTE () { fi _symbol cons; local a="${r}" QUASIQUOTE "${a0}"; local b="${r}" - rest "${1}" + _rest "${1}" QUASIQUOTE "${r}"; local c="${r}" _list "${a}" "${b}" "${c}" return @@ -68,7 +68,7 @@ MACROEXPAND () { while IS_MACRO_CALL "${ast}" "${env}"; do _nth "${ast}" 0; local a0="${r}" ENV_GET "${env}" "${ANON["${a0}"]}"; local mac="${ANON["${r}"]}" - rest "${ast}" + _rest "${ast}" ${mac%%@*} ${ANON["${r}"]} ast="${r}" done @@ -184,7 +184,7 @@ EVAL () { _slice "${ast}" 1 $(( ${r} - 2 )) EVAL_AST "${r}" "${env}" [[ "${__ERROR}" ]] && r= && return 1 - last "${ast}" + _last "${ast}" ast="${r}" # Continue loop ;; @@ -211,8 +211,8 @@ EVAL () { *) EVAL_AST "${ast}" "${env}" [[ "${__ERROR}" ]] && r= && return 1 local el="${r}" - first "${el}"; local f="${ANON["${r}"]}" - rest "${el}"; local args="${ANON["${r}"]}" + _first "${el}"; local f="${ANON["${r}"]}" + _rest "${el}"; local args="${ANON["${r}"]}" #echo "invoke: [${f}] ${args}" if [[ "${f//@/ }" != "${f}" ]]; then set -- ${f//@/ } @@ -244,37 +244,22 @@ PRINT () { ENV; REPL_ENV="${r}" REP () { r= - READ_STR "${1}" + READ "${1}" || return 1 EVAL "${r}" "${REPL_ENV}" PRINT "${r}" } +# core.sh: defined using bash _fref () { _function "${2} \"\${@}\""; ENV_SET "${REPL_ENV}" "${1}" "${r}"; } - -# Import types functions for n in "${!core_ns[@]}"; do _fref "${n}" "${core_ns["${n}"]}"; done - -readline () { - READLINE "${ANON["${1}"]}" && _string "${r}" || r="${__nil}"; -} -_fref "readline" readline -read_string () { READ_STR "${ANON["${1}"]}"; } -_fref "read-string" read_string _eval () { EVAL "${1}" "${REPL_ENV}"; } _fref "eval" _eval -slurp () { - local lines - mapfile lines < "${ANON["${1}"]}" - local text="${lines[*]}"; text=${text//$'\n' /$'\n'} - _string "${text}" -} -_fref "slurp" slurp -# Defined using the language itself +# core.mal: defined using the language itself REP "(def! not (fn* (a) (if a false true)))" +REP "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))" REP "(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))" REP "(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) \`(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))" -REP "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))" if [[ "${1}" ]]; then echo "${@}" diff --git a/bash/types.sh b/bash/types.sh index 8e256be..6781492 100644 --- a/bash/types.sh +++ b/bash/types.sh @@ -185,6 +185,11 @@ _hash_map () { } _hash_map? () { [[ ${1} =~ ^hmap_ ]]; } +_contains? () { + local obj="${ANON["${1}"]}" + eval [[ "\${${obj}[\"${2}\"]+isset}" ]] +} + _copy_hash_map () { local orig_obj="${ANON["${1}"]}" _hash_map @@ -243,6 +248,30 @@ _nth () { r=${temp[${2}]} } +_first () { + local temp="${ANON["${1}"]}" + r="${temp%% *}" + [ "${r}" ] || r="${__nil}" +} + +_last () { + local temp="${ANON["${1}"]}" + r="${temp##* }" +} + +# Creates a new vector/list of the everything after but the first +# element +_rest () { + local temp="${ANON["${1}"]}" + _list + if [[ "${temp#* }" == "${temp}" ]]; then + ANON["${r}"]= + else + ANON["${r}"]="${temp#* }" + fi +} + + _empty? () { [[ -z "${ANON["${1}"]}" ]]; } # conj that mutates in place (and always appends) @@ -2,9 +2,12 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <sys/stat.h> +#include <fcntl.h> #include "types.h" #include "core.h" +#include "reader.h" #include "printer.h" // Errors/Exceptions @@ -80,17 +83,49 @@ MalVal *println(MalVal *args) { return &mal_nil; } +MalVal *mal_readline(MalVal *str) { + assert_type(str, MAL_STRING, "readline of non-string"); + char * line = _readline(str->val.string); + if (line) { return malval_new_string(line); } + else { return &mal_nil; } +} -// Number functions +MalVal *read_string(MalVal *str) { + assert_type(str, MAL_STRING, "read_string of non-string"); + return read_str(str->val.string); +} -#define WRAP_INTEGER_OP(name, op) \ - MalVal *int_ ## name(MalVal *a, MalVal *b) { \ - return malval_new_integer(a->val.intnum op b->val.intnum); \ +char *slurp_raw(char *path) { + char *data; + struct stat fst; + int fd = open(path, O_RDONLY), + sz; + if (fd < 0) { + abort("slurp failed to open '%s'", path); + } + if (fstat(fd, &fst) < 0) { + abort("slurp failed to stat '%s'", path); } -#define WRAP_INTEGER_CMP_OP(name, op) \ - MalVal *int_ ## name(MalVal *a, MalVal *b) { \ - return a->val.intnum op b->val.intnum ? &mal_true : &mal_false; \ + data = malloc(fst.st_size+1); + sz = read(fd, data, fst.st_size); + if (sz < fst.st_size) { + abort("slurp failed to read '%s'", path); } + data[sz] = '\0'; + return data; +} +MalVal *slurp(MalVal *path) { + assert_type(path, MAL_STRING, "slurp of non-string"); + char *data = slurp_raw(path->val.string); + if (!data || mal_error) { return NULL; } + return malval_new_string(data); +} + + + + +// Number functions + WRAP_INTEGER_OP(plus,+) WRAP_INTEGER_OP(minus,-) WRAP_INTEGER_OP(multiply,*) @@ -115,7 +150,6 @@ MalVal *vector_Q(MalVal *seq) { return _vector_Q(seq) ? &mal_true : &mal_false; // Hash map functions -MalVal *hash_map(MalVal *args) { return _hash_map(args); } MalVal *hash_map_Q(MalVal *seq) { return _hash_map_Q(seq) ? &mal_true : &mal_false; } MalVal *assoc(MalVal *args) { @@ -123,15 +157,15 @@ MalVal *assoc(MalVal *args) { "assoc called with non-sequential arguments"); assert(_count(args) >= 2, "assoc needs at least 2 arguments"); - GHashTable *htable = g_hash_table_copy(first(args)->val.hash_table); + GHashTable *htable = g_hash_table_copy(_first(args)->val.hash_table); MalVal *hm = malval_new_hash_map(htable); - return _assoc_BANG(hm, rest(args)); + return _assoc_BANG(hm, _rest(args)); } MalVal *dissoc(MalVal* args) { - GHashTable *htable = g_hash_table_copy(first(args)->val.hash_table); + GHashTable *htable = g_hash_table_copy(_first(args)->val.hash_table); MalVal *hm = malval_new_hash_map(htable); - return _dissoc_BANG(hm, rest(args)); + return _dissoc_BANG(hm, _rest(args)); } MalVal *keys(MalVal *obj) { @@ -249,19 +283,6 @@ MalVal *nth(MalVal *seq, MalVal *idx) { return _nth(seq, idx->val.intnum); } -MalVal *first(MalVal *seq) { - assert_type(seq, MAL_LIST|MAL_VECTOR, - "first called with non-sequential"); - if (_count(seq) == 0) { - return &mal_nil; - } - return g_array_index(seq->val.array, MalVal*, 0); -} - -MalVal *rest(MalVal *seq) { - return _slice(seq, 1, _count(seq)); -} - MalVal *empty_Q(MalVal *seq) { assert_type(seq, MAL_LIST|MAL_VECTOR, "empty? called with non-sequential"); @@ -296,20 +317,11 @@ MalVal *sconj(MalVal *args) { return malval_new_list(src_lst->type, new_arr); } -MalVal *last(MalVal *seq) { - assert_type(seq, MAL_LIST|MAL_VECTOR, - "last called with non-sequential"); - if (_count(seq) == 0) { - return &mal_nil; - } - return g_array_index(seq->val.array, MalVal*, _count(seq)-1); -} - MalVal *apply(MalVal *args) { assert_type(args, MAL_LIST|MAL_VECTOR, "apply called with non-sequential"); MalVal *f = _nth(args, 0); - MalVal *last_arg = last(args); + MalVal *last_arg = _last(args); assert_type(last_arg, MAL_LIST|MAL_VECTOR, "last argument to apply is non-sequential"); int i, len = _count(args) - 2 + _count(last_arg); @@ -410,7 +422,7 @@ MalVal *swap_BANG(MalVal *args) { -core_ns_entry core_ns[50] = { +core_ns_entry core_ns[53] = { {"=", (void*(*)(void*))equal_Q, 2}, {"throw", (void*(*)(void*))throw, 1}, {"nil?", (void*(*)(void*))nil_Q, 1}, @@ -418,10 +430,14 @@ core_ns_entry core_ns[50] = { {"false?", (void*(*)(void*))false_Q, 1}, {"symbol", (void*(*)(void*))symbol, 1}, {"symbol?", (void*(*)(void*))symbol_Q, 1}, + {"pr-str", (void*(*)(void*))pr_str, -1}, {"str", (void*(*)(void*))str, -1}, {"prn", (void*(*)(void*))prn, -1}, {"println", (void*(*)(void*))println, -1}, + {"readline", (void*(*)(void*))mal_readline, 1}, + {"read-string", (void*(*)(void*))read_string, 1}, + {"slurp", (void*(*)(void*))slurp, 1}, {"<", (void*(*)(void*))int_lt, 2}, {"<=", (void*(*)(void*))int_lte, 2}, {">", (void*(*)(void*))int_gt, 2}, @@ -435,7 +451,7 @@ core_ns_entry core_ns[50] = { {"list?", (void*(*)(void*))list_Q, 1}, {"vector", (void*(*)(void*))vector, -1}, {"vector?", (void*(*)(void*))vector_Q, 1}, - {"hash-map", (void*(*)(void*))hash_map, -1}, + {"hash-map", (void*(*)(void*))_hash_map, -1}, {"map?", (void*(*)(void*))hash_map_Q, 1}, {"assoc", (void*(*)(void*))assoc, -1}, {"dissoc", (void*(*)(void*))dissoc, -1}, @@ -448,9 +464,9 @@ core_ns_entry core_ns[50] = { {"cons", (void*(*)(void*))cons, 2}, {"concat", (void*(*)(void*))concat, -1}, {"nth", (void*(*)(void*))nth, 2}, - {"first", (void*(*)(void*))first, 1}, - {"rest", (void*(*)(void*))rest, 1}, - {"last", (void*(*)(void*))last, 1}, + {"first", (void*(*)(void*))_first, 1}, + {"rest", (void*(*)(void*))_rest, 1}, + {"last", (void*(*)(void*))_last, 1}, {"empty?", (void*(*)(void*))empty_Q, 1}, {"count", (void*(*)(void*))count, 1}, {"conj", (void*(*)(void*))sconj, -1}, @@ -3,20 +3,6 @@ #include <glib.h> -// These are just used by step2 and step3 before then core_ns environment is -// imported - -MalVal *int_plus(MalVal *a, MalVal *b); -MalVal *int_minus(MalVal *a, MalVal *b); -MalVal *int_multiply(MalVal *a, MalVal *b); -MalVal *int_divide(MalVal *a, MalVal *b); - -// Useful for step implementation -MalVal *first(MalVal *seq); -MalVal *rest(MalVal *seq); -MalVal *last(MalVal *seq); -MalVal *hash_map(MalVal *args); - // namespace of type functions typedef struct { char *name; @@ -24,6 +10,6 @@ typedef struct { int arg_cnt; } core_ns_entry; -extern core_ns_entry core_ns[50]; +extern core_ns_entry core_ns[53]; #endif diff --git a/c/step1_read_print.c b/c/step1_read_print.c index 77c75b9..d28439e 100644 --- a/c/step1_read_print.c +++ b/c/step1_read_print.c @@ -1,6 +1,7 @@ #include <stdlib.h> #include <stdio.h> #include <unistd.h> + #include "types.h" #include "readline.h" #include "reader.h" diff --git a/c/step2_eval.c b/c/step2_eval.c index 85746f8..5d24ff0 100644 --- a/c/step2_eval.c +++ b/c/step2_eval.c @@ -1,10 +1,10 @@ #include <stdlib.h> #include <stdio.h> #include <unistd.h> + #include "types.h" #include "readline.h" #include "reader.h" -#include "core.h" // Declarations MalVal *EVAL(MalVal *ast, GHashTable *env); @@ -54,7 +54,7 @@ MalVal *eval_ast(MalVal *ast, GHashTable *env) { MalVal *new_val = EVAL((MalVal *)value, env); g_array_append_val(seq->val.array, new_val); } - return hash_map(seq); + return _hash_map(seq); } else { //g_print("EVAL scalar: %s\n", _pr_str(ast,1)); return ast; @@ -62,8 +62,8 @@ MalVal *eval_ast(MalVal *ast, GHashTable *env) { } MalVal *EVAL(MalVal *ast, GHashTable *env) { - //g_print("EVAL: %s\n", _pr_str(ast,1)); if (!ast || mal_error) return NULL; + //g_print("EVAL: %s\n", _pr_str(ast,1)); if (ast->type != MAL_LIST) { return eval_ast(ast, env); } @@ -76,7 +76,7 @@ MalVal *EVAL(MalVal *ast, GHashTable *env) { assert_type(a0, MAL_SYMBOL, "Cannot invoke %s", _pr_str(a0,1)); MalVal *el = eval_ast(ast, env); if (!el || mal_error) { return NULL; } - MalVal *(*f)(void *, void*) = (MalVal *(*)(void*, void*))first(el); + MalVal *(*f)(void *, void*) = (MalVal *(*)(void*, void*))_first(el); //g_print("eval_invoke el: %s\n", _pr_str(el,1)); return f(_nth(el, 1), _nth(el, 2)); } @@ -109,9 +109,15 @@ MalVal *RE(GHashTable *env, char *prompt, char *str) { // Setup the initial REPL environment GHashTable *repl_env; + void init_repl_env() { repl_env = g_hash_table_new(g_str_hash, g_str_equal); + WRAP_INTEGER_OP(plus,+) + WRAP_INTEGER_OP(minus,-) + WRAP_INTEGER_OP(multiply,*) + WRAP_INTEGER_OP(divide,/) + g_hash_table_insert(repl_env, "+", int_plus); g_hash_table_insert(repl_env, "-", int_minus); g_hash_table_insert(repl_env, "*", int_multiply); diff --git a/c/step3_env.c b/c/step3_env.c index 4abf4d6..7c36b38 100644 --- a/c/step3_env.c +++ b/c/step3_env.c @@ -1,10 +1,10 @@ #include <stdlib.h> #include <stdio.h> #include <unistd.h> + #include "types.h" #include "readline.h" #include "reader.h" -#include "core.h" // Declarations MalVal *EVAL(MalVal *ast, Env *env); @@ -53,7 +53,7 @@ MalVal *eval_ast(MalVal *ast, Env *env) { MalVal *new_val = EVAL((MalVal *)value, env); g_array_append_val(seq->val.array, new_val); } - return hash_map(seq); + return _hash_map(seq); } else { //g_print("EVAL scalar: %s\n", _pr_str(ast,1)); return ast; @@ -61,8 +61,8 @@ MalVal *eval_ast(MalVal *ast, Env *env) { } MalVal *EVAL(MalVal *ast, Env *env) { - //g_print("EVAL: %s\n", _pr_str(ast,1)); if (!ast || mal_error) return NULL; + //g_print("EVAL: %s\n", _pr_str(ast,1)); if (ast->type != MAL_LIST) { return eval_ast(ast, env); } @@ -102,7 +102,7 @@ MalVal *EVAL(MalVal *ast, Env *env) { //g_print("eval apply\n"); MalVal *el = eval_ast(ast, env); if (!el || mal_error) { return NULL; } - MalVal *(*f)(void *, void*) = (MalVal *(*)(void*, void*))first(el); + MalVal *(*f)(void *, void*) = (MalVal *(*)(void*, void*))_first(el); return f(_nth(el, 1), _nth(el, 2)); } } @@ -138,6 +138,11 @@ Env *repl_env; void init_repl_env() { repl_env = new_env(NULL, NULL, NULL); + WRAP_INTEGER_OP(plus,+) + WRAP_INTEGER_OP(minus,-) + WRAP_INTEGER_OP(multiply,*) + WRAP_INTEGER_OP(divide,/) + env_set(repl_env, "+", (MalVal *)int_plus); env_set(repl_env, "-", (MalVal *)int_minus); env_set(repl_env, "*", (MalVal *)int_multiply); diff --git a/c/step4_if_fn_do.c b/c/step4_if_fn_do.c index 3662816..87e1241 100644 --- a/c/step4_if_fn_do.c +++ b/c/step4_if_fn_do.c @@ -1,6 +1,7 @@ #include <stdlib.h> #include <stdio.h> #include <unistd.h> + #include "types.h" #include "readline.h" #include "reader.h" @@ -53,7 +54,7 @@ MalVal *eval_ast(MalVal *ast, Env *env) { MalVal *new_val = EVAL((MalVal *)value, env); g_array_append_val(seq->val.array, new_val); } - return hash_map(seq); + return _hash_map(seq); } else { //g_print("EVAL scalar: %s\n", _pr_str(ast,1)); return ast; @@ -61,8 +62,8 @@ MalVal *eval_ast(MalVal *ast, Env *env) { } MalVal *EVAL(MalVal *ast, Env *env) { - //g_print("EVAL: %s\n", _pr_str(ast,1)); if (!ast || mal_error) return NULL; + //g_print("EVAL: %s\n", _pr_str(ast,1)); if (ast->type != MAL_LIST) { return eval_ast(ast, env); } @@ -102,8 +103,8 @@ MalVal *EVAL(MalVal *ast, Env *env) { } else if ((a0->type & MAL_SYMBOL) && strcmp("do", a0->val.string) == 0) { //g_print("eval apply do\n"); - MalVal *el = eval_ast(rest(ast), env); - return last(el); + MalVal *el = eval_ast(_rest(ast), env); + return _last(el); } else if ((a0->type & MAL_SYMBOL) && strcmp("if", a0->val.string) == 0) { //g_print("eval apply if\n"); @@ -136,8 +137,8 @@ MalVal *EVAL(MalVal *ast, Env *env) { //g_print("eval apply\n"); MalVal *el = eval_ast(ast, env); if (!el || mal_error) { return NULL; } - MalVal *f = first(el), - *args = rest(el); + MalVal *f = _first(el), + *args = _rest(el); assert_type(f, MAL_FUNCTION_C|MAL_FUNCTION_MAL, "cannot apply '%s'", _pr_str(f,1)); return _apply(f, args); @@ -173,18 +174,16 @@ MalVal *RE(Env *env, char *prompt, char *str) { Env *repl_env; void init_repl_env() { - void _ref(char *name, MalVal*(*func)(MalVal*), int arg_cnt) { - void *(*f)(void *) = (void*(*)(void*))func; - env_set(repl_env, name, malval_new_function(f, arg_cnt, NULL)); - } repl_env = new_env(NULL, NULL, NULL); + // core.c: defined using C int i; for(i=0; i< (sizeof(core_ns) / sizeof(core_ns[0])); i++) { - MalVal *(*f)(MalVal *) = (MalVal*(*)(MalVal*))core_ns[i].func; - _ref(core_ns[i].name, f, core_ns[i].arg_cnt); + env_set(repl_env, core_ns[i].name, + malval_new_function(core_ns[i].func, core_ns[i].arg_cnt)); } + // core.mal: defined using the language itself RE(repl_env, "", "(def! not (fn* (a) (if a false true)))"); } diff --git a/c/step5_tco.c b/c/step5_tco.c index 99d6826..6938e47 100644 --- a/c/step5_tco.c +++ b/c/step5_tco.c @@ -1,6 +1,7 @@ #include <stdlib.h> #include <stdio.h> #include <unistd.h> + #include "types.h" #include "readline.h" #include "reader.h" @@ -53,7 +54,7 @@ MalVal *eval_ast(MalVal *ast, Env *env) { MalVal *new_val = EVAL((MalVal *)value, env); g_array_append_val(seq->val.array, new_val); } - return hash_map(seq); + return _hash_map(seq); } else { //g_print("EVAL scalar: %s\n", _pr_str(ast,1)); return ast; @@ -62,93 +63,95 @@ MalVal *eval_ast(MalVal *ast, Env *env) { MalVal *EVAL(MalVal *ast, Env *env) { while (TRUE) { - //g_print("EVAL: %s\n", _pr_str(ast,1)); - if (!ast || mal_error) return NULL; - if (ast->type != MAL_LIST) { - return eval_ast(ast, env); + + if (!ast || mal_error) return NULL; + //g_print("EVAL: %s\n", _pr_str(ast,1)); + if (ast->type != MAL_LIST) { + return eval_ast(ast, env); + } + if (!ast || mal_error) return NULL; + + // apply list + //g_print("EVAL apply list: %s\n", _pr_str(ast,1)); + int i, len; + if (_count(ast) == 0) { return ast; } + MalVal *a0 = _nth(ast, 0); + if ((a0->type & MAL_SYMBOL) && + strcmp("def!", a0->val.string) == 0) { + //g_print("eval apply def!\n"); + MalVal *a1 = _nth(ast, 1), + *a2 = _nth(ast, 2); + MalVal *res = EVAL(a2, env); + env_set(env, a1->val.string, res); + return res; + } else if ((a0->type & MAL_SYMBOL) && + strcmp("let*", a0->val.string) == 0) { + //g_print("eval apply let*\n"); + MalVal *a1 = _nth(ast, 1), + *a2 = _nth(ast, 2), + *key, *val; + assert_type(a1, MAL_LIST|MAL_VECTOR, + "let* bindings must be list or vector"); + len = _count(a1); + assert((len % 2) == 0, "odd number of let* bindings forms"); + Env *let_env = new_env(env, NULL, NULL); + for(i=0; i<len; i+=2) { + key = g_array_index(a1->val.array, MalVal*, i); + val = g_array_index(a1->val.array, MalVal*, i+1); + assert_type(key, MAL_SYMBOL, "let* bind to non-symbol"); + env_set(let_env, key->val.string, EVAL(val, let_env)); } - if (!ast || mal_error) return NULL; - - // apply list - //g_print("EVAL apply list: %s\n", _pr_str(ast,1)); - int i, len; - if (_count(ast) == 0) { return ast; } - MalVal *a0 = _nth(ast, 0); - if ((a0->type & MAL_SYMBOL) && - strcmp("def!", a0->val.string) == 0) { - //g_print("eval apply def!\n"); - MalVal *a1 = _nth(ast, 1), - *a2 = _nth(ast, 2); - MalVal *res = EVAL(a2, env); - env_set(env, a1->val.string, res); - return res; - } else if ((a0->type & MAL_SYMBOL) && - strcmp("let*", a0->val.string) == 0) { - //g_print("eval apply let*\n"); - MalVal *a1 = _nth(ast, 1), - *a2 = _nth(ast, 2), - *key, *val; - assert_type(a1, MAL_LIST|MAL_VECTOR, - "let* bindings must be list or vector"); - len = _count(a1); - assert((len % 2) == 0, "odd number of let* bindings forms"); - Env *let_env = new_env(env, NULL, NULL); - for(i=0; i<len; i+=2) { - key = g_array_index(a1->val.array, MalVal*, i); - val = g_array_index(a1->val.array, MalVal*, i+1); - assert_type(key, MAL_SYMBOL, "let* bind to non-symbol"); - env_set(let_env, key->val.string, EVAL(val, let_env)); - } - return EVAL(a2, let_env); - } else if ((a0->type & MAL_SYMBOL) && - strcmp("do", a0->val.string) == 0) { - //g_print("eval apply do\n"); - eval_ast(_slice(ast, 1, _count(ast)-1), env); - ast = last(ast); - // Continue loop - } else if ((a0->type & MAL_SYMBOL) && - strcmp("if", a0->val.string) == 0) { - //g_print("eval apply if\n"); - MalVal *a1 = _nth(ast, 1); - MalVal *cond = EVAL(a1, env); - if (!cond || mal_error) return NULL; - if (cond->type & (MAL_FALSE|MAL_NIL)) { - // eval false slot form - ast = _nth(ast, 3); - if (!ast) { - return &mal_nil; - } - } else { - // eval true slot form - ast = _nth(ast, 2); + return EVAL(a2, let_env); + } else if ((a0->type & MAL_SYMBOL) && + strcmp("do", a0->val.string) == 0) { + //g_print("eval apply do\n"); + eval_ast(_slice(ast, 1, _count(ast)-1), env); + ast = _last(ast); + // Continue loop + } else if ((a0->type & MAL_SYMBOL) && + strcmp("if", a0->val.string) == 0) { + //g_print("eval apply if\n"); + MalVal *a1 = _nth(ast, 1); + MalVal *cond = EVAL(a1, env); + if (!cond || mal_error) return NULL; + if (cond->type & (MAL_FALSE|MAL_NIL)) { + // eval false slot form + ast = _nth(ast, 3); + if (!ast) { + return &mal_nil; } + } else { + // eval true slot form + ast = _nth(ast, 2); + } + // Continue loop + } else if ((a0->type & MAL_SYMBOL) && + strcmp("fn*", a0->val.string) == 0) { + //g_print("eval apply fn*\n"); + MalVal *mf = malval_new(MAL_FUNCTION_MAL, NULL); + mf->val.func.evaluator = EVAL; + mf->val.func.args = _nth(ast, 1); + mf->val.func.body = _nth(ast, 2); + mf->val.func.env = env; + return mf; + } else { + //g_print("eval apply\n"); + MalVal *el = eval_ast(ast, env); + if (!el || mal_error) { return NULL; } + MalVal *f = _first(el), + *args = _rest(el); + assert_type(f, MAL_FUNCTION_C|MAL_FUNCTION_MAL, + "cannot apply '%s'", _pr_str(f,1)); + if (f->type & MAL_FUNCTION_MAL) { + ast = f->val.func.body; + env = new_env(f->val.func.env, f->val.func.args, args); // Continue loop - } else if ((a0->type & MAL_SYMBOL) && - strcmp("fn*", a0->val.string) == 0) { - //g_print("eval apply fn*\n"); - MalVal *mf = malval_new(MAL_FUNCTION_MAL, NULL); - mf->val.func.evaluator = EVAL; - mf->val.func.args = _nth(ast, 1); - mf->val.func.body = _nth(ast, 2); - mf->val.func.env = env; - return mf; } else { - //g_print("eval apply\n"); - MalVal *el = eval_ast(ast, env); - if (!el || mal_error) { return NULL; } - MalVal *f = first(el), - *args = rest(el); - assert_type(f, MAL_FUNCTION_C|MAL_FUNCTION_MAL, - "cannot invoke '%s'", _pr_str(f,1)); - if (f->type & MAL_FUNCTION_MAL) { - ast = f->val.func.body; - env = new_env(f->val.func.env, f->val.func.args, args); - // Continue loop - } else { - return _apply(f, args); - } + return _apply(f, args); } } + + } // TCO while loop } // print @@ -180,18 +183,16 @@ MalVal *RE(Env *env, char *prompt, char *str) { Env *repl_env; void init_repl_env() { - void _ref(char *name, MalVal*(*func)(MalVal*), int arg_cnt) { - void *(*f)(void *) = (void*(*)(void*))func; - env_set(repl_env, name, malval_new_function(f, arg_cnt, NULL)); - } repl_env = new_env(NULL, NULL, NULL); + // core.c: defined using C int i; for(i=0; i< (sizeof(core_ns) / sizeof(core_ns[0])); i++) { - MalVal *(*f)(MalVal *) = (MalVal*(*)(MalVal*))core_ns[i].func; - _ref(core_ns[i].name, f, core_ns[i].arg_cnt); + env_set(repl_env, core_ns[i].name, + malval_new_function(core_ns[i].func, core_ns[i].arg_cnt)); } + // core.mal: defined using the language itself RE(repl_env, "", "(def! not (fn* (a) (if a false true)))"); } diff --git a/c/step6_file.c b/c/step6_file.c index acde758..ae48693 100644 --- a/c/step6_file.c +++ b/c/step6_file.c @@ -1,13 +1,11 @@ #include <stdlib.h> #include <stdio.h> #include <unistd.h> -#include <sys/stat.h> -#include <fcntl.h> + #include "types.h" #include "readline.h" #include "reader.h" #include "core.h" -#include "interop.h" // Declarations MalVal *EVAL(MalVal *ast, Env *env); @@ -56,7 +54,7 @@ MalVal *eval_ast(MalVal *ast, Env *env) { MalVal *new_val = EVAL((MalVal *)value, env); g_array_append_val(seq->val.array, new_val); } - return hash_map(seq); + return _hash_map(seq); } else { //g_print("EVAL scalar: %s\n", _pr_str(ast,1)); return ast; @@ -65,93 +63,95 @@ MalVal *eval_ast(MalVal *ast, Env *env) { MalVal *EVAL(MalVal *ast, Env *env) { while (TRUE) { - //g_print("EVAL: %s\n", _pr_str(ast,1)); - if (!ast || mal_error) return NULL; - if (ast->type != MAL_LIST) { - return eval_ast(ast, env); - } - if (!ast || mal_error) return NULL; - // apply list - //g_print("EVAL apply list: %s\n", _pr_str(ast,1)); - int i, len; - if (_count(ast) == 0) { return ast; } - MalVal *a0 = _nth(ast, 0); - if ((a0->type & MAL_SYMBOL) && - strcmp("def!", a0->val.string) == 0) { - //g_print("eval apply def!\n"); - MalVal *a1 = _nth(ast, 1), - *a2 = _nth(ast, 2); - MalVal *res = EVAL(a2, env); - env_set(env, a1->val.string, res); - return res; - } else if ((a0->type & MAL_SYMBOL) && - strcmp("let*", a0->val.string) == 0) { - //g_print("eval apply let*\n"); - MalVal *a1 = _nth(ast, 1), - *a2 = _nth(ast, 2), - *key, *val; - assert_type(a1, MAL_LIST|MAL_VECTOR, - "let* bindings must be list or vector"); - len = _count(a1); - assert((len % 2) == 0, "odd number of let* bindings forms"); - Env *let_env = new_env(env, NULL, NULL); - for(i=0; i<len; i+=2) { - key = g_array_index(a1->val.array, MalVal*, i); - val = g_array_index(a1->val.array, MalVal*, i+1); - assert_type(key, MAL_SYMBOL, "let* bind to non-symbol"); - env_set(let_env, key->val.string, EVAL(val, let_env)); - } - return EVAL(a2, let_env); - } else if ((a0->type & MAL_SYMBOL) && - strcmp("do", a0->val.string) == 0) { - //g_print("eval apply do\n"); - eval_ast(_slice(ast, 1, _count(ast)-1), env); - ast = last(ast); - // Continue loop - } else if ((a0->type & MAL_SYMBOL) && - strcmp("if", a0->val.string) == 0) { - //g_print("eval apply if\n"); - MalVal *a1 = _nth(ast, 1); - MalVal *cond = EVAL(a1, env); - if (!cond || mal_error) return NULL; - if (cond->type & (MAL_FALSE|MAL_NIL)) { - // eval false slot form - ast = _nth(ast, 3); - if (!ast) { - return &mal_nil; - } - } else { - // eval true slot form - ast = _nth(ast, 2); + if (!ast || mal_error) return NULL; + //g_print("EVAL: %s\n", _pr_str(ast,1)); + if (ast->type != MAL_LIST) { + return eval_ast(ast, env); + } + if (!ast || mal_error) return NULL; + + // apply list + //g_print("EVAL apply list: %s\n", _pr_str(ast,1)); + int i, len; + if (_count(ast) == 0) { return ast; } + MalVal *a0 = _nth(ast, 0); + if ((a0->type & MAL_SYMBOL) && + strcmp("def!", a0->val.string) == 0) { + //g_print("eval apply def!\n"); + MalVal *a1 = _nth(ast, 1), + *a2 = _nth(ast, 2); + MalVal *res = EVAL(a2, env); + env_set(env, a1->val.string, res); + return res; + } else if ((a0->type & MAL_SYMBOL) && + strcmp("let*", a0->val.string) == 0) { + //g_print("eval apply let*\n"); + MalVal *a1 = _nth(ast, 1), + *a2 = _nth(ast, 2), + *key, *val; + assert_type(a1, MAL_LIST|MAL_VECTOR, + "let* bindings must be list or vector"); + len = _count(a1); + assert((len % 2) == 0, "odd number of let* bindings forms"); + Env *let_env = new_env(env, NULL, NULL); + for(i=0; i<len; i+=2) { + key = g_array_index(a1->val.array, MalVal*, i); + val = g_array_index(a1->val.array, MalVal*, i+1); + assert_type(key, MAL_SYMBOL, "let* bind to non-symbol"); + env_set(let_env, key->val.string, EVAL(val, let_env)); + } + return EVAL(a2, let_env); + } else if ((a0->type & MAL_SYMBOL) && + strcmp("do", a0->val.string) == 0) { + //g_print("eval apply do\n"); + eval_ast(_slice(ast, 1, _count(ast)-1), env); + ast = _last(ast); + // Continue loop + } else if ((a0->type & MAL_SYMBOL) && + strcmp("if", a0->val.string) == 0) { + //g_print("eval apply if\n"); + MalVal *a1 = _nth(ast, 1); + MalVal *cond = EVAL(a1, env); + if (!cond || mal_error) return NULL; + if (cond->type & (MAL_FALSE|MAL_NIL)) { + // eval false slot form + ast = _nth(ast, 3); + if (!ast) { + return &mal_nil; } + } else { + // eval true slot form + ast = _nth(ast, 2); + } + // Continue loop + } else if ((a0->type & MAL_SYMBOL) && + strcmp("fn*", a0->val.string) == 0) { + //g_print("eval apply fn*\n"); + MalVal *mf = malval_new(MAL_FUNCTION_MAL, NULL); + mf->val.func.evaluator = EVAL; + mf->val.func.args = _nth(ast, 1); + mf->val.func.body = _nth(ast, 2); + mf->val.func.env = env; + return mf; + } else { + //g_print("eval apply\n"); + MalVal *el = eval_ast(ast, env); + if (!el || mal_error) { return NULL; } + MalVal *f = _first(el), + *args = _rest(el); + assert_type(f, MAL_FUNCTION_C|MAL_FUNCTION_MAL, + "cannot apply '%s'", _pr_str(f,1)); + if (f->type & MAL_FUNCTION_MAL) { + ast = f->val.func.body; + env = new_env(f->val.func.env, f->val.func.args, args); // Continue loop - } else if ((a0->type & MAL_SYMBOL) && - strcmp("fn*", a0->val.string) == 0) { - //g_print("eval apply fn*\n"); - MalVal *mf = malval_new(MAL_FUNCTION_MAL, NULL); - mf->val.func.evaluator = EVAL; - mf->val.func.args = _nth(ast, 1); - mf->val.func.body = _nth(ast, 2); - mf->val.func.env = env; - return mf; } else { - //g_print("eval apply\n"); - MalVal *el = eval_ast(ast, env); - if (!el || mal_error) { return NULL; } - MalVal *f = first(el), - *args = rest(el); - assert_type(f, MAL_FUNCTION_C|MAL_FUNCTION_MAL, - "cannot invoke '%s'", _pr_str(f,1)); - if (f->type & MAL_FUNCTION_MAL) { - ast = f->val.func.body; - env = new_env(f->val.func.env, f->val.func.args, args); - // Continue loop - } else { - return _apply(f, args); - } + return _apply(f, args); } } + + } // TCO while loop } // print @@ -182,58 +182,20 @@ MalVal *RE(Env *env, char *prompt, char *str) { // Setup the initial REPL environment Env *repl_env; -char *slurp_raw(char *path) { - char *data; - struct stat fst; - int fd = open(path, O_RDONLY), - sz; - if (fd < 0) { - abort("slurp failed to open '%s'", path); - } - if (fstat(fd, &fst) < 0) { - abort("slurp failed to stat '%s'", path); - } - data = malloc(fst.st_size+1); - sz = read(fd, data, fst.st_size); - if (sz < fst.st_size) { - abort("slurp failed to read '%s'", path); - } - data[sz] = '\0'; - return data; -} - void init_repl_env() { - void _ref(char *name, MalVal*(*func)(MalVal*), int arg_cnt) { - void *(*f)(void *) = (void*(*)(void*))func; - env_set(repl_env, name, malval_new_function(f, arg_cnt, NULL)); - } repl_env = new_env(NULL, NULL, NULL); + // core.c: defined using C int i; for(i=0; i< (sizeof(core_ns) / sizeof(core_ns[0])); i++) { - MalVal *(*f)(MalVal *) = (MalVal*(*)(MalVal*))core_ns[i].func; - _ref(core_ns[i].name, f, core_ns[i].arg_cnt); - } - - MalVal *read_string(MalVal *str) { - assert_type(str, MAL_STRING, "read_string of non-string"); - return read_str(str->val.string); - } - _ref("read-string", read_string, 1); - - MalVal *do_eval(MalVal *ast) { - return EVAL(ast, repl_env); + env_set(repl_env, core_ns[i].name, + malval_new_function(core_ns[i].func, core_ns[i].arg_cnt)); } - _ref("eval", do_eval, 1); - - MalVal *slurp(MalVal *path) { - assert_type(path, MAL_STRING, "slurp of non-string"); - char *data = slurp_raw(path->val.string); - if (!data || mal_error) { return NULL; } - return malval_new_string(data); - } - _ref("slurp", slurp, 1); + MalVal *do_eval(MalVal *ast) { return EVAL(ast, repl_env); } + env_set(repl_env, "eval", + malval_new_function((void*(*)(void *))do_eval, 1)); + // core.mal: defined using the language itself RE(repl_env, "", "(def! not (fn* (a) (if a false true)))"); RE(repl_env, "", "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))"); @@ -252,21 +214,22 @@ int main(int argc, char *argv[]) if (argc > 1) { char *cmd = g_strdup_printf("(load-file \"%s\")", argv[1]); RE(repl_env, "", cmd); - } else { - // REPL loop - for(;;) { - exp = RE(repl_env, prompt, NULL); - if (mal_error && strcmp("EOF", mal_error->val.string) == 0) { - return 0; - } - output = PRINT(exp); + return 0; + } - if (output) { - g_print("%s\n", output); - free(output); // Free output string - } + // REPL loop + for(;;) { + exp = RE(repl_env, prompt, NULL); + if (mal_error && strcmp("EOF", mal_error->val.string) == 0) { + return 0; + } + output = PRINT(exp); - //malval_free(exp); // Free evaluated expression + if (output) { + g_print("%s\n", output); + free(output); // Free output string } + + //malval_free(exp); // Free evaluated expression } } diff --git a/c/step7_quote.c b/c/step7_quote.c index 7da47ee..ac17955 100644 --- a/c/step7_quote.c +++ b/c/step7_quote.c @@ -1,13 +1,11 @@ #include <stdlib.h> #include <stdio.h> #include <unistd.h> -#include <sys/stat.h> -#include <fcntl.h> + #include "types.h" #include "readline.h" #include "reader.h" #include "core.h" -#include "interop.h" // Declarations MalVal *EVAL(MalVal *ast, Env *env); @@ -49,12 +47,12 @@ MalVal *quasiquote(MalVal *ast) { strcmp("splice-unquote", a00->val.string) == 0) { return _listX(3, malval_new_symbol("concat"), _nth(a0, 1), - quasiquote(rest(ast))); + quasiquote(_rest(ast))); } } return _listX(3, malval_new_symbol("cons"), quasiquote(a0), - quasiquote(rest(ast))); + quasiquote(_rest(ast))); } } @@ -83,7 +81,7 @@ MalVal *eval_ast(MalVal *ast, Env *env) { MalVal *new_val = EVAL((MalVal *)value, env); g_array_append_val(seq->val.array, new_val); } - return hash_map(seq); + return _hash_map(seq); } else { //g_print("EVAL scalar: %s\n", _pr_str(ast,1)); return ast; @@ -92,102 +90,104 @@ MalVal *eval_ast(MalVal *ast, Env *env) { MalVal *EVAL(MalVal *ast, Env *env) { while (TRUE) { - if (!ast || mal_error) return NULL; - //g_print("EVAL: %s\n", _pr_str(ast,1)); - if (ast->type != MAL_LIST) { - return eval_ast(ast, env); - } - if (!ast || mal_error) return NULL; - // apply list - //g_print("EVAL apply list: %s\n", _pr_str(ast,1)); - int i, len; - if (_count(ast) == 0) { return ast; } - MalVal *a0 = _nth(ast, 0); - if ((a0->type & MAL_SYMBOL) && - strcmp("def!", a0->val.string) == 0) { - //g_print("eval apply def!\n"); - MalVal *a1 = _nth(ast, 1), - *a2 = _nth(ast, 2); - MalVal *res = EVAL(a2, env); - env_set(env, a1->val.string, res); - return res; - } else if ((a0->type & MAL_SYMBOL) && - strcmp("let*", a0->val.string) == 0) { - //g_print("eval apply let*\n"); - MalVal *a1 = _nth(ast, 1), - *a2 = _nth(ast, 2), - *key, *val; - assert_type(a1, MAL_LIST|MAL_VECTOR, - "let* bindings must be list or vector"); - len = _count(a1); - assert((len % 2) == 0, "odd number of let* bindings forms"); - Env *let_env = new_env(env, NULL, NULL); - for(i=0; i<len; i+=2) { - key = g_array_index(a1->val.array, MalVal*, i); - val = g_array_index(a1->val.array, MalVal*, i+1); - assert_type(key, MAL_SYMBOL, "let* bind to non-symbol"); - env_set(let_env, key->val.string, EVAL(val, let_env)); - } - return EVAL(a2, let_env); - } else if ((a0->type & MAL_SYMBOL) && - strcmp("quote", a0->val.string) == 0) { - //g_print("eval apply quote\n"); - return _nth(ast, 1); - } else if ((a0->type & MAL_SYMBOL) && - strcmp("quasiquote", a0->val.string) == 0) { - //g_print("eval apply quasiquote\n"); - MalVal *a1 = _nth(ast, 1); - return EVAL(quasiquote(a1), env); - } else if ((a0->type & MAL_SYMBOL) && - strcmp("do", a0->val.string) == 0) { - //g_print("eval apply do\n"); - eval_ast(_slice(ast, 1, _count(ast)-1), env); - ast = last(ast); - // Continue loop - } else if ((a0->type & MAL_SYMBOL) && - strcmp("if", a0->val.string) == 0) { - //g_print("eval apply if\n"); - MalVal *a1 = _nth(ast, 1); - MalVal *cond = EVAL(a1, env); - if (!cond || mal_error) return NULL; - if (cond->type & (MAL_FALSE|MAL_NIL)) { - // eval false slot form - ast = _nth(ast, 3); - if (!ast) { - return &mal_nil; - } - } else { - // eval true slot form - ast = _nth(ast, 2); + if (!ast || mal_error) return NULL; + //g_print("EVAL: %s\n", _pr_str(ast,1)); + if (ast->type != MAL_LIST) { + return eval_ast(ast, env); + } + if (!ast || mal_error) return NULL; + + // apply list + //g_print("EVAL apply list: %s\n", _pr_str(ast,1)); + int i, len; + if (_count(ast) == 0) { return ast; } + MalVal *a0 = _nth(ast, 0); + if ((a0->type & MAL_SYMBOL) && + strcmp("def!", a0->val.string) == 0) { + //g_print("eval apply def!\n"); + MalVal *a1 = _nth(ast, 1), + *a2 = _nth(ast, 2); + MalVal *res = EVAL(a2, env); + env_set(env, a1->val.string, res); + return res; + } else if ((a0->type & MAL_SYMBOL) && + strcmp("let*", a0->val.string) == 0) { + //g_print("eval apply let*\n"); + MalVal *a1 = _nth(ast, 1), + *a2 = _nth(ast, 2), + *key, *val; + assert_type(a1, MAL_LIST|MAL_VECTOR, + "let* bindings must be list or vector"); + len = _count(a1); + assert((len % 2) == 0, "odd number of let* bindings forms"); + Env *let_env = new_env(env, NULL, NULL); + for(i=0; i<len; i+=2) { + key = g_array_index(a1->val.array, MalVal*, i); + val = g_array_index(a1->val.array, MalVal*, i+1); + assert_type(key, MAL_SYMBOL, "let* bind to non-symbol"); + env_set(let_env, key->val.string, EVAL(val, let_env)); + } + return EVAL(a2, let_env); + } else if ((a0->type & MAL_SYMBOL) && + strcmp("quote", a0->val.string) == 0) { + //g_print("eval apply quote\n"); + return _nth(ast, 1); + } else if ((a0->type & MAL_SYMBOL) && + strcmp("quasiquote", a0->val.string) == 0) { + //g_print("eval apply quasiquote\n"); + MalVal *a1 = _nth(ast, 1); + return EVAL(quasiquote(a1), env); + } else if ((a0->type & MAL_SYMBOL) && + strcmp("do", a0->val.string) == 0) { + //g_print("eval apply do\n"); + eval_ast(_slice(ast, 1, _count(ast)-1), env); + ast = _last(ast); + // Continue loop + } else if ((a0->type & MAL_SYMBOL) && + strcmp("if", a0->val.string) == 0) { + //g_print("eval apply if\n"); + MalVal *a1 = _nth(ast, 1); + MalVal *cond = EVAL(a1, env); + if (!cond || mal_error) return NULL; + if (cond->type & (MAL_FALSE|MAL_NIL)) { + // eval false slot form + ast = _nth(ast, 3); + if (!ast) { + return &mal_nil; } + } else { + // eval true slot form + ast = _nth(ast, 2); + } + // Continue loop + } else if ((a0->type & MAL_SYMBOL) && + strcmp("fn*", a0->val.string) == 0) { + //g_print("eval apply fn*\n"); + MalVal *mf = malval_new(MAL_FUNCTION_MAL, NULL); + mf->val.func.evaluator = EVAL; + mf->val.func.args = _nth(ast, 1); + mf->val.func.body = _nth(ast, 2); + mf->val.func.env = env; + return mf; + } else { + //g_print("eval apply\n"); + MalVal *el = eval_ast(ast, env); + if (!el || mal_error) { return NULL; } + MalVal *f = _first(el), + *args = _rest(el); + assert_type(f, MAL_FUNCTION_C|MAL_FUNCTION_MAL, + "cannot apply '%s'", _pr_str(f,1)); + if (f->type & MAL_FUNCTION_MAL) { + ast = f->val.func.body; + env = new_env(f->val.func.env, f->val.func.args, args); // Continue loop - } else if ((a0->type & MAL_SYMBOL) && - strcmp("fn*", a0->val.string) == 0) { - //g_print("eval apply fn*\n"); - MalVal *mf = malval_new(MAL_FUNCTION_MAL, NULL); - mf->val.func.evaluator = EVAL; - mf->val.func.args = _nth(ast, 1); - mf->val.func.body = _nth(ast, 2); - mf->val.func.env = env; - return mf; } else { - //g_print("eval apply\n"); - MalVal *el = eval_ast(ast, env); - if (!el || mal_error) { return NULL; } - MalVal *f = first(el), - *args = rest(el); - assert_type(f, MAL_FUNCTION_C|MAL_FUNCTION_MAL, - "cannot invoke '%s'", _pr_str(f,1)); - if (f->type & MAL_FUNCTION_MAL) { - ast = f->val.func.body; - env = new_env(f->val.func.env, f->val.func.args, args); - // Continue loop - } else { - return _apply(f, args); - } + return _apply(f, args); } } + + } // TCO while loop } // print @@ -218,58 +218,20 @@ MalVal *RE(Env *env, char *prompt, char *str) { // Setup the initial REPL environment Env *repl_env; -char *slurp_raw(char *path) { - char *data; - struct stat fst; - int fd = open(path, O_RDONLY), - sz; - if (fd < 0) { - abort("slurp failed to open '%s'", path); - } - if (fstat(fd, &fst) < 0) { - abort("slurp failed to stat '%s'", path); - } - data = malloc(fst.st_size+1); - sz = read(fd, data, fst.st_size); - if (sz < fst.st_size) { - abort("slurp failed to read '%s'", path); - } - data[sz] = '\0'; - return data; -} - void init_repl_env() { - void _ref(char *name, MalVal*(*func)(MalVal*), int arg_cnt) { - void *(*f)(void *) = (void*(*)(void*))func; - env_set(repl_env, name, malval_new_function(f, arg_cnt, NULL)); - } repl_env = new_env(NULL, NULL, NULL); + // core.c: defined using C int i; for(i=0; i< (sizeof(core_ns) / sizeof(core_ns[0])); i++) { - MalVal *(*f)(MalVal *) = (MalVal*(*)(MalVal*))core_ns[i].func; - _ref(core_ns[i].name, f, core_ns[i].arg_cnt); - } - - MalVal *read_string(MalVal *str) { - assert_type(str, MAL_STRING, "read_string of non-string"); - return read_str(str->val.string); - } - _ref("read-string", read_string, 1); - - MalVal *do_eval(MalVal *ast) { - return EVAL(ast, repl_env); + env_set(repl_env, core_ns[i].name, + malval_new_function(core_ns[i].func, core_ns[i].arg_cnt)); } - _ref("eval", do_eval, 1); - - MalVal *slurp(MalVal *path) { - assert_type(path, MAL_STRING, "slurp of non-string"); - char *data = slurp_raw(path->val.string); - if (!data || mal_error) { return NULL; } - return malval_new_string(data); - } - _ref("slurp", slurp, 1); + MalVal *do_eval(MalVal *ast) { return EVAL(ast, repl_env); } + env_set(repl_env, "eval", + malval_new_function((void*(*)(void *))do_eval, 1)); + // core.mal: defined using the language itself RE(repl_env, "", "(def! not (fn* (a) (if a false true)))"); RE(repl_env, "", "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))"); @@ -288,21 +250,22 @@ int main(int argc, char *argv[]) if (argc > 1) { char *cmd = g_strdup_printf("(load-file \"%s\")", argv[1]); RE(repl_env, "", cmd); - } else { - // REPL loop - for(;;) { - exp = RE(repl_env, prompt, NULL); - if (mal_error && strcmp("EOF", mal_error->val.string) == 0) { - return 0; - } - output = PRINT(exp); + return 0; + } - if (output) { - g_print("%s\n", output); - free(output); // Free output string - } + // REPL loop + for(;;) { + exp = RE(repl_env, prompt, NULL); + if (mal_error && strcmp("EOF", mal_error->val.string) == 0) { + return 0; + } + output = PRINT(exp); - //malval_free(exp); // Free evaluated expression + if (output) { + g_print("%s\n", output); + free(output); // Free output string } + + //malval_free(exp); // Free evaluated expression } } diff --git a/c/step8_macros.c b/c/step8_macros.c index eb715b1..93c83fa 100644 --- a/c/step8_macros.c +++ b/c/step8_macros.c @@ -1,13 +1,11 @@ #include <stdlib.h> #include <stdio.h> #include <unistd.h> -#include <sys/stat.h> -#include <fcntl.h> + #include "types.h" #include "readline.h" #include "reader.h" #include "core.h" -#include "interop.h" // Declarations MalVal *EVAL(MalVal *ast, Env *env); @@ -50,12 +48,12 @@ MalVal *quasiquote(MalVal *ast) { strcmp("splice-unquote", a00->val.string) == 0) { return _listX(3, malval_new_symbol("concat"), _nth(a0, 1), - quasiquote(rest(ast))); + quasiquote(_rest(ast))); } } return _listX(3, malval_new_symbol("cons"), quasiquote(a0), - quasiquote(rest(ast))); + quasiquote(_rest(ast))); } } @@ -73,7 +71,7 @@ MalVal *macroexpand(MalVal *ast, Env *env) { MalVal *a0 = _nth(ast, 0); MalVal *mac = env_get(env, a0->val.string); // TODO: this is weird and limits it to 20. FIXME - ast = _apply(mac, rest(ast)); + ast = _apply(mac, _rest(ast)); } return ast; } @@ -103,7 +101,7 @@ MalVal *eval_ast(MalVal *ast, Env *env) { MalVal *new_val = EVAL((MalVal *)value, env); g_array_append_val(seq->val.array, new_val); } - return hash_map(seq); + return _hash_map(seq); } else { //g_print("EVAL scalar: %s\n", _pr_str(ast,1)); return ast; @@ -112,121 +110,123 @@ MalVal *eval_ast(MalVal *ast, Env *env) { MalVal *EVAL(MalVal *ast, Env *env) { while (TRUE) { - if (!ast || mal_error) return NULL; - //g_print("EVAL: %s\n", _pr_str(ast,1)); - if (ast->type != MAL_LIST) { - return eval_ast(ast, env); - } - if (!ast || mal_error) return NULL; - // apply list - //g_print("EVAL apply list: %s\n", _pr_str(ast,1)); - ast = macroexpand(ast, env); - if (!ast || mal_error) return NULL; - if (ast->type != MAL_LIST) { return ast; } - if (_count(ast) == 0) { return ast; } + if (!ast || mal_error) return NULL; + //g_print("EVAL: %s\n", _pr_str(ast,1)); + if (ast->type != MAL_LIST) { + return eval_ast(ast, env); + } + if (!ast || mal_error) return NULL; - int i, len; - MalVal *a0 = _nth(ast, 0); - if ((a0->type & MAL_SYMBOL) && - strcmp("def!", a0->val.string) == 0) { - //g_print("eval apply def!\n"); - MalVal *a1 = _nth(ast, 1), - *a2 = _nth(ast, 2); - MalVal *res = EVAL(a2, env); - env_set(env, a1->val.string, res); - return res; - } else if ((a0->type & MAL_SYMBOL) && - strcmp("let*", a0->val.string) == 0) { - //g_print("eval apply let*\n"); - MalVal *a1 = _nth(ast, 1), - *a2 = _nth(ast, 2), - *key, *val; - assert_type(a1, MAL_LIST|MAL_VECTOR, - "let* bindings must be list or vector"); - len = _count(a1); - assert((len % 2) == 0, "odd number of let* bindings forms"); - Env *let_env = new_env(env, NULL, NULL); - for(i=0; i<len; i+=2) { - key = g_array_index(a1->val.array, MalVal*, i); - val = g_array_index(a1->val.array, MalVal*, i+1); - assert_type(key, MAL_SYMBOL, "let* bind to non-symbol"); - env_set(let_env, key->val.string, EVAL(val, let_env)); - } - return EVAL(a2, let_env); - } else if ((a0->type & MAL_SYMBOL) && - strcmp("quote", a0->val.string) == 0) { - //g_print("eval apply quote\n"); - return _nth(ast, 1); - } else if ((a0->type & MAL_SYMBOL) && - strcmp("quasiquote", a0->val.string) == 0) { - //g_print("eval apply quasiquote\n"); - MalVal *a1 = _nth(ast, 1); - return EVAL(quasiquote(a1), env); - } else if ((a0->type & MAL_SYMBOL) && - strcmp("defmacro!", a0->val.string) == 0) { - //g_print("eval apply defmacro!\n"); - MalVal *a1 = _nth(ast, 1), - *a2 = _nth(ast, 2); - MalVal *res = EVAL(a2, env); - res->ismacro = TRUE; - env_set(env, a1->val.string, res); - return res; - } else if ((a0->type & MAL_SYMBOL) && - strcmp("macroexpand", a0->val.string) == 0) { - //g_print("eval apply macroexpand\n"); - MalVal *a1 = _nth(ast, 1); - return macroexpand(a1, env); - } else if ((a0->type & MAL_SYMBOL) && - strcmp("do", a0->val.string) == 0) { - //g_print("eval apply do\n"); - eval_ast(_slice(ast, 1, _count(ast)-1), env); - ast = last(ast); - // Continue loop - } else if ((a0->type & MAL_SYMBOL) && - strcmp("if", a0->val.string) == 0) { - //g_print("eval apply if\n"); - MalVal *a1 = _nth(ast, 1); - MalVal *cond = EVAL(a1, env); - if (!cond || mal_error) return NULL; - if (cond->type & (MAL_FALSE|MAL_NIL)) { - // eval false slot form - ast = _nth(ast, 3); - if (!ast) { - return &mal_nil; - } - } else { - // eval true slot form - ast = _nth(ast, 2); + // apply list + //g_print("EVAL apply list: %s\n", _pr_str(ast,1)); + ast = macroexpand(ast, env); + if (!ast || mal_error) return NULL; + if (ast->type != MAL_LIST) { return ast; } + if (_count(ast) == 0) { return ast; } + + int i, len; + MalVal *a0 = _nth(ast, 0); + if ((a0->type & MAL_SYMBOL) && + strcmp("def!", a0->val.string) == 0) { + //g_print("eval apply def!\n"); + MalVal *a1 = _nth(ast, 1), + *a2 = _nth(ast, 2); + MalVal *res = EVAL(a2, env); + env_set(env, a1->val.string, res); + return res; + } else if ((a0->type & MAL_SYMBOL) && + strcmp("let*", a0->val.string) == 0) { + //g_print("eval apply let*\n"); + MalVal *a1 = _nth(ast, 1), + *a2 = _nth(ast, 2), + *key, *val; + assert_type(a1, MAL_LIST|MAL_VECTOR, + "let* bindings must be list or vector"); + len = _count(a1); + assert((len % 2) == 0, "odd number of let* bindings forms"); + Env *let_env = new_env(env, NULL, NULL); + for(i=0; i<len; i+=2) { + key = g_array_index(a1->val.array, MalVal*, i); + val = g_array_index(a1->val.array, MalVal*, i+1); + assert_type(key, MAL_SYMBOL, "let* bind to non-symbol"); + env_set(let_env, key->val.string, EVAL(val, let_env)); + } + return EVAL(a2, let_env); + } else if ((a0->type & MAL_SYMBOL) && + strcmp("quote", a0->val.string) == 0) { + //g_print("eval apply quote\n"); + return _nth(ast, 1); + } else if ((a0->type & MAL_SYMBOL) && + strcmp("quasiquote", a0->val.string) == 0) { + //g_print("eval apply quasiquote\n"); + MalVal *a1 = _nth(ast, 1); + return EVAL(quasiquote(a1), env); + } else if ((a0->type & MAL_SYMBOL) && + strcmp("defmacro!", a0->val.string) == 0) { + //g_print("eval apply defmacro!\n"); + MalVal *a1 = _nth(ast, 1), + *a2 = _nth(ast, 2); + MalVal *res = EVAL(a2, env); + res->ismacro = TRUE; + env_set(env, a1->val.string, res); + return res; + } else if ((a0->type & MAL_SYMBOL) && + strcmp("macroexpand", a0->val.string) == 0) { + //g_print("eval apply macroexpand\n"); + MalVal *a1 = _nth(ast, 1); + return macroexpand(a1, env); + } else if ((a0->type & MAL_SYMBOL) && + strcmp("do", a0->val.string) == 0) { + //g_print("eval apply do\n"); + eval_ast(_slice(ast, 1, _count(ast)-1), env); + ast = _last(ast); + // Continue loop + } else if ((a0->type & MAL_SYMBOL) && + strcmp("if", a0->val.string) == 0) { + //g_print("eval apply if\n"); + MalVal *a1 = _nth(ast, 1); + MalVal *cond = EVAL(a1, env); + if (!cond || mal_error) return NULL; + if (cond->type & (MAL_FALSE|MAL_NIL)) { + // eval false slot form + ast = _nth(ast, 3); + if (!ast) { + return &mal_nil; } + } else { + // eval true slot form + ast = _nth(ast, 2); + } + // Continue loop + } else if ((a0->type & MAL_SYMBOL) && + strcmp("fn*", a0->val.string) == 0) { + //g_print("eval apply fn*\n"); + MalVal *mf = malval_new(MAL_FUNCTION_MAL, NULL); + mf->ismacro = FALSE; + mf->val.func.evaluator = EVAL; + mf->val.func.args = _nth(ast, 1); + mf->val.func.body = _nth(ast, 2); + mf->val.func.env = env; + return mf; + } else { + //g_print("eval apply\n"); + MalVal *el = eval_ast(ast, env); + if (!el || mal_error) { return NULL; } + MalVal *f = _first(el), + *args = _rest(el); + assert_type(f, MAL_FUNCTION_C|MAL_FUNCTION_MAL, + "cannot apply '%s'", _pr_str(f,1)); + if (f->type & MAL_FUNCTION_MAL) { + ast = f->val.func.body; + env = new_env(f->val.func.env, f->val.func.args, args); // Continue loop - } else if ((a0->type & MAL_SYMBOL) && - strcmp("fn*", a0->val.string) == 0) { - //g_print("eval apply fn*\n"); - MalVal *mf = malval_new(MAL_FUNCTION_MAL, NULL); - mf->ismacro = FALSE; - mf->val.func.evaluator = EVAL; - mf->val.func.args = _nth(ast, 1); - mf->val.func.body = _nth(ast, 2); - mf->val.func.env = env; - return mf; } else { - //g_print("eval apply\n"); - MalVal *el = eval_ast(ast, env); - if (!el || mal_error) { return NULL; } - MalVal *f = first(el), - *args = rest(el); - assert_type(f, MAL_FUNCTION_C|MAL_FUNCTION_MAL, - "cannot invoke '%s'", _pr_str(f,1)); - if (f->type & MAL_FUNCTION_MAL) { - ast = f->val.func.body; - env = new_env(f->val.func.env, f->val.func.args, args); - // Continue loop - } else { - return _apply(f, args); - } + return _apply(f, args); } } + + } // TCO while loop } // print @@ -257,61 +257,25 @@ MalVal *RE(Env *env, char *prompt, char *str) { // Setup the initial REPL environment Env *repl_env; -char *slurp_raw(char *path) { - char *data; - struct stat fst; - int fd = open(path, O_RDONLY), - sz; - if (fd < 0) { - abort("slurp failed to open '%s'", path); - } - if (fstat(fd, &fst) < 0) { - abort("slurp failed to stat '%s'", path); - } - data = malloc(fst.st_size+1); - sz = read(fd, data, fst.st_size); - if (sz < fst.st_size) { - abort("slurp failed to read '%s'", path); - } - data[sz] = '\0'; - return data; -} - void init_repl_env() { - void _ref(char *name, MalVal*(*func)(MalVal*), int arg_cnt) { - void *(*f)(void *) = (void*(*)(void*))func; - env_set(repl_env, name, malval_new_function(f, arg_cnt, NULL)); - } repl_env = new_env(NULL, NULL, NULL); + // core.c: defined using C int i; for(i=0; i< (sizeof(core_ns) / sizeof(core_ns[0])); i++) { - MalVal *(*f)(MalVal *) = (MalVal*(*)(MalVal*))core_ns[i].func; - _ref(core_ns[i].name, f, core_ns[i].arg_cnt); - } - - MalVal *read_string(MalVal *str) { - assert_type(str, MAL_STRING, "read_string of non-string"); - return read_str(str->val.string); - } - _ref("read-string", read_string, 1); - - MalVal *do_eval(MalVal *ast) { - return EVAL(ast, repl_env); - } - _ref("eval", do_eval, 1); - - MalVal *slurp(MalVal *path) { - assert_type(path, MAL_STRING, "slurp of non-string"); - char *data = slurp_raw(path->val.string); - if (!data || mal_error) { return NULL; } - return malval_new_string(data); + env_set(repl_env, core_ns[i].name, + malval_new_function(core_ns[i].func, core_ns[i].arg_cnt)); } - _ref("slurp", slurp, 1); + MalVal *do_eval(MalVal *ast) { return EVAL(ast, repl_env); } + env_set(repl_env, "eval", + malval_new_function((void*(*)(void *))do_eval, 1)); + // core.mal: defined using the language itself RE(repl_env, "", "(def! not (fn* (a) (if a false true)))"); RE(repl_env, "", "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))"); + RE(repl_env, "", "(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))"); + RE(repl_env, "", "(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))"); } int main(int argc, char *argv[]) @@ -327,21 +291,22 @@ int main(int argc, char *argv[]) if (argc > 1) { char *cmd = g_strdup_printf("(load-file \"%s\")", argv[1]); RE(repl_env, "", cmd); - } else { - // REPL loop - for(;;) { - exp = RE(repl_env, prompt, NULL); - if (mal_error && strcmp("EOF", mal_error->val.string) == 0) { - return 0; - } - output = PRINT(exp); + return 0; + } - if (output) { - g_print("%s\n", output); - free(output); // Free output string - } + // REPL loop + for(;;) { + exp = RE(repl_env, prompt, NULL); + if (mal_error && strcmp("EOF", mal_error->val.string) == 0) { + return 0; + } + output = PRINT(exp); - //malval_free(exp); // Free evaluated expression + if (output) { + g_print("%s\n", output); + free(output); // Free output string } + + //malval_free(exp); // Free evaluated expression } } diff --git a/c/step9_interop.c b/c/step9_interop.c index dcd1526..743fb22 100644 --- a/c/step9_interop.c +++ b/c/step9_interop.c @@ -1,8 +1,7 @@ #include <stdlib.h> #include <stdio.h> #include <unistd.h> -#include <sys/stat.h> -#include <fcntl.h> + #include "types.h" #include "readline.h" #include "reader.h" @@ -50,12 +49,12 @@ MalVal *quasiquote(MalVal *ast) { strcmp("splice-unquote", a00->val.string) == 0) { return _listX(3, malval_new_symbol("concat"), _nth(a0, 1), - quasiquote(rest(ast))); + quasiquote(_rest(ast))); } } return _listX(3, malval_new_symbol("cons"), quasiquote(a0), - quasiquote(rest(ast))); + quasiquote(_rest(ast))); } } @@ -73,7 +72,7 @@ MalVal *macroexpand(MalVal *ast, Env *env) { MalVal *a0 = _nth(ast, 0); MalVal *mac = env_get(env, a0->val.string); // TODO: this is weird and limits it to 20. FIXME - ast = _apply(mac, rest(ast)); + ast = _apply(mac, _rest(ast)); } return ast; } @@ -103,7 +102,7 @@ MalVal *eval_ast(MalVal *ast, Env *env) { MalVal *new_val = EVAL((MalVal *)value, env); g_array_append_val(seq->val.array, new_val); } - return hash_map(seq); + return _hash_map(seq); } else { //g_print("EVAL scalar: %s\n", _pr_str(ast,1)); return ast; @@ -112,126 +111,128 @@ MalVal *eval_ast(MalVal *ast, Env *env) { MalVal *EVAL(MalVal *ast, Env *env) { while (TRUE) { - if (!ast || mal_error) return NULL; - //g_print("EVAL: %s\n", _pr_str(ast,1)); - if (ast->type != MAL_LIST) { - return eval_ast(ast, env); - } - if (!ast || mal_error) return NULL; - // apply list - //g_print("EVAL apply list: %s\n", _pr_str(ast,1)); - ast = macroexpand(ast, env); - if (!ast || mal_error) return NULL; - if (ast->type != MAL_LIST) { return ast; } - if (_count(ast) == 0) { return ast; } + if (!ast || mal_error) return NULL; + //g_print("EVAL: %s\n", _pr_str(ast,1)); + if (ast->type != MAL_LIST) { + return eval_ast(ast, env); + } + if (!ast || mal_error) return NULL; - int i, len; - MalVal *a0 = _nth(ast, 0); - if ((a0->type & MAL_SYMBOL) && - strcmp("def!", a0->val.string) == 0) { - //g_print("eval apply def!\n"); - MalVal *a1 = _nth(ast, 1), - *a2 = _nth(ast, 2); - MalVal *res = EVAL(a2, env); - env_set(env, a1->val.string, res); - return res; - } else if ((a0->type & MAL_SYMBOL) && - strcmp("let*", a0->val.string) == 0) { - //g_print("eval apply let*\n"); - MalVal *a1 = _nth(ast, 1), - *a2 = _nth(ast, 2), - *key, *val; - assert_type(a1, MAL_LIST|MAL_VECTOR, - "let* bindings must be list or vector"); - len = _count(a1); - assert((len % 2) == 0, "odd number of let* bindings forms"); - Env *let_env = new_env(env, NULL, NULL); - for(i=0; i<len; i+=2) { - key = g_array_index(a1->val.array, MalVal*, i); - val = g_array_index(a1->val.array, MalVal*, i+1); - assert_type(key, MAL_SYMBOL, "let* bind to non-symbol"); - env_set(let_env, key->val.string, EVAL(val, let_env)); - } - return EVAL(a2, let_env); - } else if ((a0->type & MAL_SYMBOL) && - strcmp("quote", a0->val.string) == 0) { - //g_print("eval apply quote\n"); - return _nth(ast, 1); - } else if ((a0->type & MAL_SYMBOL) && - strcmp("quasiquote", a0->val.string) == 0) { - //g_print("eval apply quasiquote\n"); - MalVal *a1 = _nth(ast, 1); - return EVAL(quasiquote(a1), env); - } else if ((a0->type & MAL_SYMBOL) && - strcmp("defmacro!", a0->val.string) == 0) { - //g_print("eval apply defmacro!\n"); - MalVal *a1 = _nth(ast, 1), - *a2 = _nth(ast, 2); - MalVal *res = EVAL(a2, env); - res->ismacro = TRUE; - env_set(env, a1->val.string, res); - return res; - } else if ((a0->type & MAL_SYMBOL) && - strcmp("macroexpand", a0->val.string) == 0) { - //g_print("eval apply macroexpand\n"); - MalVal *a1 = _nth(ast, 1); - return macroexpand(a1, env); - } else if ((a0->type & MAL_SYMBOL) && - strcmp(".", a0->val.string) == 0) { - //g_print("eval apply .\n"); - MalVal *el = eval_ast(_slice(ast, 1, _count(ast)), env); - return invoke_native(el); - } else if ((a0->type & MAL_SYMBOL) && - strcmp("do", a0->val.string) == 0) { - //g_print("eval apply do\n"); - eval_ast(_slice(ast, 1, _count(ast)-1), env); - ast = last(ast); - // Continue loop - } else if ((a0->type & MAL_SYMBOL) && - strcmp("if", a0->val.string) == 0) { - //g_print("eval apply if\n"); - MalVal *a1 = _nth(ast, 1); - MalVal *cond = EVAL(a1, env); - if (!cond || mal_error) return NULL; - if (cond->type & (MAL_FALSE|MAL_NIL)) { - // eval false slot form - ast = _nth(ast, 3); - if (!ast) { - return &mal_nil; - } - } else { - // eval true slot form - ast = _nth(ast, 2); + // apply list + //g_print("EVAL apply list: %s\n", _pr_str(ast,1)); + ast = macroexpand(ast, env); + if (!ast || mal_error) return NULL; + if (ast->type != MAL_LIST) { return ast; } + if (_count(ast) == 0) { return ast; } + + int i, len; + MalVal *a0 = _nth(ast, 0); + if ((a0->type & MAL_SYMBOL) && + strcmp("def!", a0->val.string) == 0) { + //g_print("eval apply def!\n"); + MalVal *a1 = _nth(ast, 1), + *a2 = _nth(ast, 2); + MalVal *res = EVAL(a2, env); + env_set(env, a1->val.string, res); + return res; + } else if ((a0->type & MAL_SYMBOL) && + strcmp("let*", a0->val.string) == 0) { + //g_print("eval apply let*\n"); + MalVal *a1 = _nth(ast, 1), + *a2 = _nth(ast, 2), + *key, *val; + assert_type(a1, MAL_LIST|MAL_VECTOR, + "let* bindings must be list or vector"); + len = _count(a1); + assert((len % 2) == 0, "odd number of let* bindings forms"); + Env *let_env = new_env(env, NULL, NULL); + for(i=0; i<len; i+=2) { + key = g_array_index(a1->val.array, MalVal*, i); + val = g_array_index(a1->val.array, MalVal*, i+1); + assert_type(key, MAL_SYMBOL, "let* bind to non-symbol"); + env_set(let_env, key->val.string, EVAL(val, let_env)); + } + return EVAL(a2, let_env); + } else if ((a0->type & MAL_SYMBOL) && + strcmp("quote", a0->val.string) == 0) { + //g_print("eval apply quote\n"); + return _nth(ast, 1); + } else if ((a0->type & MAL_SYMBOL) && + strcmp("quasiquote", a0->val.string) == 0) { + //g_print("eval apply quasiquote\n"); + MalVal *a1 = _nth(ast, 1); + return EVAL(quasiquote(a1), env); + } else if ((a0->type & MAL_SYMBOL) && + strcmp("defmacro!", a0->val.string) == 0) { + //g_print("eval apply defmacro!\n"); + MalVal *a1 = _nth(ast, 1), + *a2 = _nth(ast, 2); + MalVal *res = EVAL(a2, env); + res->ismacro = TRUE; + env_set(env, a1->val.string, res); + return res; + } else if ((a0->type & MAL_SYMBOL) && + strcmp("macroexpand", a0->val.string) == 0) { + //g_print("eval apply macroexpand\n"); + MalVal *a1 = _nth(ast, 1); + return macroexpand(a1, env); + } else if ((a0->type & MAL_SYMBOL) && + strcmp(".", a0->val.string) == 0) { + //g_print("eval apply .\n"); + MalVal *el = eval_ast(_slice(ast, 1, _count(ast)), env); + return invoke_native(el); + } else if ((a0->type & MAL_SYMBOL) && + strcmp("do", a0->val.string) == 0) { + //g_print("eval apply do\n"); + eval_ast(_slice(ast, 1, _count(ast)-1), env); + ast = _last(ast); + // Continue loop + } else if ((a0->type & MAL_SYMBOL) && + strcmp("if", a0->val.string) == 0) { + //g_print("eval apply if\n"); + MalVal *a1 = _nth(ast, 1); + MalVal *cond = EVAL(a1, env); + if (!cond || mal_error) return NULL; + if (cond->type & (MAL_FALSE|MAL_NIL)) { + // eval false slot form + ast = _nth(ast, 3); + if (!ast) { + return &mal_nil; } + } else { + // eval true slot form + ast = _nth(ast, 2); + } + // Continue loop + } else if ((a0->type & MAL_SYMBOL) && + strcmp("fn*", a0->val.string) == 0) { + //g_print("eval apply fn*\n"); + MalVal *mf = malval_new(MAL_FUNCTION_MAL, NULL); + mf->ismacro = FALSE; + mf->val.func.evaluator = EVAL; + mf->val.func.args = _nth(ast, 1); + mf->val.func.body = _nth(ast, 2); + mf->val.func.env = env; + return mf; + } else { + //g_print("eval apply\n"); + MalVal *el = eval_ast(ast, env); + if (!el || mal_error) { return NULL; } + MalVal *f = _first(el), + *args = _rest(el); + assert_type(f, MAL_FUNCTION_C|MAL_FUNCTION_MAL, + "cannot apply '%s'", _pr_str(f,1)); + if (f->type & MAL_FUNCTION_MAL) { + ast = f->val.func.body; + env = new_env(f->val.func.env, f->val.func.args, args); // Continue loop - } else if ((a0->type & MAL_SYMBOL) && - strcmp("fn*", a0->val.string) == 0) { - //g_print("eval apply fn*\n"); - MalVal *mf = malval_new(MAL_FUNCTION_MAL, NULL); - mf->ismacro = FALSE; - mf->val.func.evaluator = EVAL; - mf->val.func.args = _nth(ast, 1); - mf->val.func.body = _nth(ast, 2); - mf->val.func.env = env; - return mf; } else { - //g_print("eval apply\n"); - MalVal *el = eval_ast(ast, env); - if (!el || mal_error) { return NULL; } - MalVal *f = first(el), - *args = rest(el); - assert_type(f, MAL_FUNCTION_C|MAL_FUNCTION_MAL, - "cannot invoke '%s'", _pr_str(f,1)); - if (f->type & MAL_FUNCTION_MAL) { - ast = f->val.func.body; - env = new_env(f->val.func.env, f->val.func.args, args); - // Continue loop - } else { - return _apply(f, args); - } + return _apply(f, args); } } + + } // TCO while loop } // print @@ -262,61 +263,25 @@ MalVal *RE(Env *env, char *prompt, char *str) { // Setup the initial REPL environment Env *repl_env; -char *slurp_raw(char *path) { - char *data; - struct stat fst; - int fd = open(path, O_RDONLY), - sz; - if (fd < 0) { - abort("slurp failed to open '%s'", path); - } - if (fstat(fd, &fst) < 0) { - abort("slurp failed to stat '%s'", path); - } - data = malloc(fst.st_size+1); - sz = read(fd, data, fst.st_size); - if (sz < fst.st_size) { - abort("slurp failed to read '%s'", path); - } - data[sz] = '\0'; - return data; -} - void init_repl_env() { - void _ref(char *name, MalVal*(*func)(MalVal*), int arg_cnt) { - void *(*f)(void *) = (void*(*)(void*))func; - env_set(repl_env, name, malval_new_function(f, arg_cnt, NULL)); - } repl_env = new_env(NULL, NULL, NULL); + // core.c: defined using C int i; for(i=0; i< (sizeof(core_ns) / sizeof(core_ns[0])); i++) { - MalVal *(*f)(MalVal *) = (MalVal*(*)(MalVal*))core_ns[i].func; - _ref(core_ns[i].name, f, core_ns[i].arg_cnt); - } - - MalVal *read_string(MalVal *str) { - assert_type(str, MAL_STRING, "read_string of non-string"); - return read_str(str->val.string); - } - _ref("read-string", read_string, 1); - - MalVal *do_eval(MalVal *ast) { - return EVAL(ast, repl_env); - } - _ref("eval", do_eval, 1); - - MalVal *slurp(MalVal *path) { - assert_type(path, MAL_STRING, "slurp of non-string"); - char *data = slurp_raw(path->val.string); - if (!data || mal_error) { return NULL; } - return malval_new_string(data); + env_set(repl_env, core_ns[i].name, + malval_new_function(core_ns[i].func, core_ns[i].arg_cnt)); } - _ref("slurp", slurp, 1); + MalVal *do_eval(MalVal *ast) { return EVAL(ast, repl_env); } + env_set(repl_env, "eval", + malval_new_function((void*(*)(void *))do_eval, 1)); + // core.mal: defined using the language itself RE(repl_env, "", "(def! not (fn* (a) (if a false true)))"); RE(repl_env, "", "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))"); + RE(repl_env, "", "(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))"); + RE(repl_env, "", "(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))"); } int main(int argc, char *argv[]) @@ -332,21 +297,22 @@ int main(int argc, char *argv[]) if (argc > 1) { char *cmd = g_strdup_printf("(load-file \"%s\")", argv[1]); RE(repl_env, "", cmd); - } else { - // REPL loop - for(;;) { - exp = RE(repl_env, prompt, NULL); - if (mal_error && strcmp("EOF", mal_error->val.string) == 0) { - return 0; - } - output = PRINT(exp); + return 0; + } - if (output) { - g_print("%s\n", output); - free(output); // Free output string - } + // REPL loop + for(;;) { + exp = RE(repl_env, prompt, NULL); + if (mal_error && strcmp("EOF", mal_error->val.string) == 0) { + return 0; + } + output = PRINT(exp); - //malval_free(exp); // Free evaluated expression + if (output) { + g_print("%s\n", output); + free(output); // Free output string } + + //malval_free(exp); // Free evaluated expression } } diff --git a/c/stepA_more.c b/c/stepA_more.c index 4e4152c..82bf3db 100644 --- a/c/stepA_more.c +++ b/c/stepA_more.c @@ -1,8 +1,7 @@ #include <stdlib.h> #include <stdio.h> #include <unistd.h> -#include <sys/stat.h> -#include <fcntl.h> + #include "types.h" #include "readline.h" #include "reader.h" @@ -50,12 +49,12 @@ MalVal *quasiquote(MalVal *ast) { strcmp("splice-unquote", a00->val.string) == 0) { return _listX(3, malval_new_symbol("concat"), _nth(a0, 1), - quasiquote(rest(ast))); + quasiquote(_rest(ast))); } } return _listX(3, malval_new_symbol("cons"), quasiquote(a0), - quasiquote(rest(ast))); + quasiquote(_rest(ast))); } } @@ -73,7 +72,7 @@ MalVal *macroexpand(MalVal *ast, Env *env) { MalVal *a0 = _nth(ast, 0); MalVal *mac = env_get(env, a0->val.string); // TODO: this is weird and limits it to 20. FIXME - ast = _apply(mac, rest(ast)); + ast = _apply(mac, _rest(ast)); } return ast; } @@ -103,7 +102,7 @@ MalVal *eval_ast(MalVal *ast, Env *env) { MalVal *new_val = EVAL((MalVal *)value, env); g_array_append_val(seq->val.array, new_val); } - return hash_map(seq); + return _hash_map(seq); } else { //g_print("EVAL scalar: %s\n", _pr_str(ast,1)); return ast; @@ -112,147 +111,149 @@ MalVal *eval_ast(MalVal *ast, Env *env) { MalVal *EVAL(MalVal *ast, Env *env) { while (TRUE) { - if (!ast || mal_error) return NULL; - //g_print("EVAL: %s\n", _pr_str(ast,1)); - if (ast->type != MAL_LIST) { - return eval_ast(ast, env); - } - if (!ast || mal_error) return NULL; - // apply list - //g_print("EVAL apply list: %s\n", _pr_str(ast,1)); - ast = macroexpand(ast, env); - if (!ast || mal_error) return NULL; - if (ast->type != MAL_LIST) { return ast; } - if (_count(ast) == 0) { return ast; } + if (!ast || mal_error) return NULL; + //g_print("EVAL: %s\n", _pr_str(ast,1)); + if (ast->type != MAL_LIST) { + return eval_ast(ast, env); + } + if (!ast || mal_error) return NULL; - int i, len; - MalVal *a0 = _nth(ast, 0); - if ((a0->type & MAL_SYMBOL) && - strcmp("def!", a0->val.string) == 0) { - //g_print("eval apply def!\n"); - MalVal *a1 = _nth(ast, 1), - *a2 = _nth(ast, 2); - MalVal *res = EVAL(a2, env); - env_set(env, a1->val.string, res); - return res; - } else if ((a0->type & MAL_SYMBOL) && - strcmp("let*", a0->val.string) == 0) { - //g_print("eval apply let*\n"); - MalVal *a1 = _nth(ast, 1), - *a2 = _nth(ast, 2), - *key, *val; - assert_type(a1, MAL_LIST|MAL_VECTOR, - "let* bindings must be list or vector"); - len = _count(a1); - assert((len % 2) == 0, "odd number of let* bindings forms"); - Env *let_env = new_env(env, NULL, NULL); - for(i=0; i<len; i+=2) { - key = g_array_index(a1->val.array, MalVal*, i); - val = g_array_index(a1->val.array, MalVal*, i+1); - assert_type(key, MAL_SYMBOL, "let* bind to non-symbol"); - env_set(let_env, key->val.string, EVAL(val, let_env)); - } - return EVAL(a2, let_env); - } else if ((a0->type & MAL_SYMBOL) && - strcmp("quote", a0->val.string) == 0) { - //g_print("eval apply quote\n"); - return _nth(ast, 1); - } else if ((a0->type & MAL_SYMBOL) && - strcmp("quasiquote", a0->val.string) == 0) { - //g_print("eval apply quasiquote\n"); - MalVal *a1 = _nth(ast, 1); - return EVAL(quasiquote(a1), env); - } else if ((a0->type & MAL_SYMBOL) && - strcmp("defmacro!", a0->val.string) == 0) { - //g_print("eval apply defmacro!\n"); - MalVal *a1 = _nth(ast, 1), - *a2 = _nth(ast, 2); - MalVal *res = EVAL(a2, env); - res->ismacro = TRUE; - env_set(env, a1->val.string, res); + // apply list + //g_print("EVAL apply list: %s\n", _pr_str(ast,1)); + ast = macroexpand(ast, env); + if (!ast || mal_error) return NULL; + if (ast->type != MAL_LIST) { return ast; } + if (_count(ast) == 0) { return ast; } + + int i, len; + MalVal *a0 = _nth(ast, 0); + if ((a0->type & MAL_SYMBOL) && + strcmp("def!", a0->val.string) == 0) { + //g_print("eval apply def!\n"); + MalVal *a1 = _nth(ast, 1), + *a2 = _nth(ast, 2); + MalVal *res = EVAL(a2, env); + env_set(env, a1->val.string, res); + return res; + } else if ((a0->type & MAL_SYMBOL) && + strcmp("let*", a0->val.string) == 0) { + //g_print("eval apply let*\n"); + MalVal *a1 = _nth(ast, 1), + *a2 = _nth(ast, 2), + *key, *val; + assert_type(a1, MAL_LIST|MAL_VECTOR, + "let* bindings must be list or vector"); + len = _count(a1); + assert((len % 2) == 0, "odd number of let* bindings forms"); + Env *let_env = new_env(env, NULL, NULL); + for(i=0; i<len; i+=2) { + key = g_array_index(a1->val.array, MalVal*, i); + val = g_array_index(a1->val.array, MalVal*, i+1); + assert_type(key, MAL_SYMBOL, "let* bind to non-symbol"); + env_set(let_env, key->val.string, EVAL(val, let_env)); + } + return EVAL(a2, let_env); + } else if ((a0->type & MAL_SYMBOL) && + strcmp("quote", a0->val.string) == 0) { + //g_print("eval apply quote\n"); + return _nth(ast, 1); + } else if ((a0->type & MAL_SYMBOL) && + strcmp("quasiquote", a0->val.string) == 0) { + //g_print("eval apply quasiquote\n"); + MalVal *a1 = _nth(ast, 1); + return EVAL(quasiquote(a1), env); + } else if ((a0->type & MAL_SYMBOL) && + strcmp("defmacro!", a0->val.string) == 0) { + //g_print("eval apply defmacro!\n"); + MalVal *a1 = _nth(ast, 1), + *a2 = _nth(ast, 2); + MalVal *res = EVAL(a2, env); + res->ismacro = TRUE; + env_set(env, a1->val.string, res); + return res; + } else if ((a0->type & MAL_SYMBOL) && + strcmp("macroexpand", a0->val.string) == 0) { + //g_print("eval apply macroexpand\n"); + MalVal *a1 = _nth(ast, 1); + return macroexpand(a1, env); + } else if ((a0->type & MAL_SYMBOL) && + strcmp(".", a0->val.string) == 0) { + //g_print("eval apply .\n"); + MalVal *el = eval_ast(_slice(ast, 1, _count(ast)), env); + return invoke_native(el); + } else if ((a0->type & MAL_SYMBOL) && + strcmp("try*", a0->val.string) == 0) { + //g_print("eval apply try*\n"); + MalVal *a1 = _nth(ast, 1); + MalVal *a2 = _nth(ast, 2); + MalVal *res = EVAL(a1, env); + if (!mal_error) { return res; } + MalVal *a20 = _nth(a2, 0); + if (strcmp("catch*", a20->val.string) == 0) { + MalVal *a21 = _nth(a2, 1); + MalVal *a22 = _nth(a2, 2); + Env *catch_env = new_env(env, + _listX(1, a21), + _listX(1, mal_error)); + //malval_free(mal_error); + mal_error = NULL; + res = EVAL(a22, catch_env); return res; - } else if ((a0->type & MAL_SYMBOL) && - strcmp("macroexpand", a0->val.string) == 0) { - //g_print("eval apply macroexpand\n"); - MalVal *a1 = _nth(ast, 1); - return macroexpand(a1, env); - } else if ((a0->type & MAL_SYMBOL) && - strcmp(".", a0->val.string) == 0) { - //g_print("eval apply .\n"); - MalVal *el = eval_ast(_slice(ast, 1, _count(ast)), env); - return invoke_native(el); - } else if ((a0->type & MAL_SYMBOL) && - strcmp("try*", a0->val.string) == 0) { - //g_print("eval apply try*\n"); - MalVal *a1 = _nth(ast, 1); - MalVal *a2 = _nth(ast, 2); - MalVal *res = EVAL(a1, env); - if (!mal_error) { return res; } - MalVal *a20 = _nth(a2, 0); - if (strcmp("catch*", a20->val.string) == 0) { - MalVal *a21 = _nth(a2, 1); - MalVal *a22 = _nth(a2, 2); - Env *catch_env = new_env(env, - _listX(1, a21), - _listX(1, mal_error)); - //malval_free(mal_error); - mal_error = NULL; - res = EVAL(a22, catch_env); - return res; - } else { + } else { + return &mal_nil; + } + } else if ((a0->type & MAL_SYMBOL) && + strcmp("do", a0->val.string) == 0) { + //g_print("eval apply do\n"); + eval_ast(_slice(ast, 1, _count(ast)-1), env); + ast = _last(ast); + // Continue loop + } else if ((a0->type & MAL_SYMBOL) && + strcmp("if", a0->val.string) == 0) { + //g_print("eval apply if\n"); + MalVal *a1 = _nth(ast, 1); + MalVal *cond = EVAL(a1, env); + if (!cond || mal_error) return NULL; + if (cond->type & (MAL_FALSE|MAL_NIL)) { + // eval false slot form + ast = _nth(ast, 3); + if (!ast) { return &mal_nil; } - } else if ((a0->type & MAL_SYMBOL) && - strcmp("do", a0->val.string) == 0) { - //g_print("eval apply do\n"); - eval_ast(_slice(ast, 1, _count(ast)-1), env); - ast = last(ast); - // Continue loop - } else if ((a0->type & MAL_SYMBOL) && - strcmp("if", a0->val.string) == 0) { - //g_print("eval apply if\n"); - MalVal *a1 = _nth(ast, 1); - MalVal *cond = EVAL(a1, env); - if (!cond || mal_error) return NULL; - if (cond->type & (MAL_FALSE|MAL_NIL)) { - // eval false slot form - ast = _nth(ast, 3); - if (!ast) { - return &mal_nil; - } - } else { - // eval true slot form - ast = _nth(ast, 2); - } + } else { + // eval true slot form + ast = _nth(ast, 2); + } + // Continue loop + } else if ((a0->type & MAL_SYMBOL) && + strcmp("fn*", a0->val.string) == 0) { + //g_print("eval apply fn*\n"); + MalVal *mf = malval_new(MAL_FUNCTION_MAL, NULL); + mf->ismacro = FALSE; + mf->val.func.evaluator = EVAL; + mf->val.func.args = _nth(ast, 1); + mf->val.func.body = _nth(ast, 2); + mf->val.func.env = env; + return mf; + } else { + //g_print("eval apply\n"); + MalVal *el = eval_ast(ast, env); + if (!el || mal_error) { return NULL; } + MalVal *f = _first(el), + *args = _rest(el); + assert_type(f, MAL_FUNCTION_C|MAL_FUNCTION_MAL, + "cannot apply '%s'", _pr_str(f,1)); + if (f->type & MAL_FUNCTION_MAL) { + ast = f->val.func.body; + env = new_env(f->val.func.env, f->val.func.args, args); // Continue loop - } else if ((a0->type & MAL_SYMBOL) && - strcmp("fn*", a0->val.string) == 0) { - //g_print("eval apply fn*\n"); - MalVal *mf = malval_new(MAL_FUNCTION_MAL, NULL); - mf->ismacro = FALSE; - mf->val.func.evaluator = EVAL; - mf->val.func.args = _nth(ast, 1); - mf->val.func.body = _nth(ast, 2); - mf->val.func.env = env; - return mf; } else { - //g_print("eval apply\n"); - MalVal *el = eval_ast(ast, env); - if (!el || mal_error) { return NULL; } - MalVal *f = first(el), - *args = rest(el); - assert_type(f, MAL_FUNCTION_C|MAL_FUNCTION_MAL, - "cannot invoke '%s'", _pr_str(f,1)); - if (f->type & MAL_FUNCTION_MAL) { - ast = f->val.func.body; - env = new_env(f->val.func.env, f->val.func.args, args); - // Continue loop - } else { - return _apply(f, args); - } + return _apply(f, args); } } + + } // TCO while loop } // print @@ -283,71 +284,25 @@ MalVal *RE(Env *env, char *prompt, char *str) { // Setup the initial REPL environment Env *repl_env; -char *slurp_raw(char *path) { - char *data; - struct stat fst; - int fd = open(path, O_RDONLY), - sz; - if (fd < 0) { - abort("slurp failed to open '%s'", path); - } - if (fstat(fd, &fst) < 0) { - abort("slurp failed to stat '%s'", path); - } - data = malloc(fst.st_size+1); - sz = read(fd, data, fst.st_size); - if (sz < fst.st_size) { - abort("slurp failed to read '%s'", path); - } - data[sz] = '\0'; - return data; -} - void init_repl_env() { - void _ref(char *name, MalVal*(*func)(MalVal*), int arg_cnt) { - void *(*f)(void *) = (void*(*)(void*))func; - env_set(repl_env, name, malval_new_function(f, arg_cnt, NULL)); - } repl_env = new_env(NULL, NULL, NULL); + // core.c: defined using C int i; for(i=0; i< (sizeof(core_ns) / sizeof(core_ns[0])); i++) { - MalVal *(*f)(MalVal *) = (MalVal*(*)(MalVal*))core_ns[i].func; - _ref(core_ns[i].name, f, core_ns[i].arg_cnt); - } - - MalVal *readline(MalVal *str) { - assert_type(str, MAL_STRING, "readline of non-string"); - char * line = _readline(str->val.string); - if (line) { return malval_new_string(line); } - else { return &mal_nil; } - } - _ref("readline", readline, 1); - - MalVal *read_string(MalVal *str) { - assert_type(str, MAL_STRING, "read_string of non-string"); - return read_str(str->val.string); + env_set(repl_env, core_ns[i].name, + malval_new_function(core_ns[i].func, core_ns[i].arg_cnt)); } - _ref("read-string", read_string, 1); - - MalVal *do_eval(MalVal *ast) { - return EVAL(ast, repl_env); - } - _ref("eval", do_eval, 1); - - MalVal *slurp(MalVal *path) { - assert_type(path, MAL_STRING, "slurp of non-string"); - char *data = slurp_raw(path->val.string); - if (!data || mal_error) { return NULL; } - return malval_new_string(data); - } - _ref("slurp", slurp, 1); + MalVal *do_eval(MalVal *ast) { return EVAL(ast, repl_env); } + env_set(repl_env, "eval", + malval_new_function((void*(*)(void *))do_eval, 1)); + // core.mal: defined using the language itself RE(repl_env, "", "(def! not (fn* (a) (if a false true)))"); - RE(repl_env, "", "(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))"); - RE(repl_env, "", "(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))"); RE(repl_env, "", "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))"); + RE(repl_env, "", "(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))"); + RE(repl_env, "", "(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))"); } int main(int argc, char *argv[]) @@ -363,21 +318,22 @@ int main(int argc, char *argv[]) if (argc > 1) { char *cmd = g_strdup_printf("(load-file \"%s\")", argv[1]); RE(repl_env, "", cmd); - } else { - // REPL loop - for(;;) { - exp = RE(repl_env, prompt, NULL); - if (mal_error && strcmp("EOF", mal_error->val.string) == 0) { - return 0; - } - output = PRINT(exp); + return 0; + } - if (output) { - g_print("%s\n", output); - free(output); // Free output string - } + // REPL loop + for(;;) { + exp = RE(repl_env, prompt, NULL); + if (mal_error && strcmp("EOF", mal_error->val.string) == 0) { + return 0; + } + output = PRINT(exp); - //malval_free(exp); // Free evaluated expression + if (output) { + g_print("%s\n", output); + free(output); // Free output string } + + //malval_free(exp); // Free evaluated expression } } @@ -124,8 +124,8 @@ MalVal *malval_new_atom(MalVal *val) { } -MalVal *malval_new_function(void *(*func)(void *), int arg_cnt, MalVal* metadata) { - MalVal *mv = malval_new(MAL_FUNCTION_C, metadata); +MalVal *malval_new_function(void *(*func)(void *), int arg_cnt) { + MalVal *mv = malval_new(MAL_FUNCTION_C, NULL); mv->func_arg_cnt = arg_cnt; assert(mv->func_arg_cnt <= 20, "native function restricted to 20 args (%d given)", @@ -420,13 +420,37 @@ int _sequential_Q(MalVal *seq) { MalVal *_nth(MalVal *seq, int idx) { assert_type(seq, MAL_LIST|MAL_VECTOR, - "nth called with non-sequential"); + "_nth called with non-sequential"); if (idx >= _count(seq)) { return &mal_nil; } return g_array_index(seq->val.array, MalVal*, idx); } +MalVal *_first(MalVal *seq) { + assert_type(seq, MAL_LIST|MAL_VECTOR, + "_first called with non-sequential"); + if (_count(seq) == 0) { + return &mal_nil; + } + return g_array_index(seq->val.array, MalVal*, 0); +} + +MalVal *_last(MalVal *seq) { + assert_type(seq, MAL_LIST|MAL_VECTOR, + "_last called with non-sequential"); + if (_count(seq) == 0) { + return &mal_nil; + } + return g_array_index(seq->val.array, MalVal*, _count(seq)-1); +} + + +MalVal *_rest(MalVal *seq) { + return _slice(seq, 1, _count(seq)); +} + + MalVal *_map2(MalVal *(*func)(void*, void*), MalVal *lst, void *arg2) { MalVal *e, *el; assert_type(lst, MAL_LIST|MAL_VECTOR, @@ -133,8 +133,19 @@ MalVal *malval_new_symbol(char *val); MalVal *malval_new_list(MalType type, GArray *val); MalVal *malval_new_hash_map(GHashTable *val); MalVal *malval_new_atom(MalVal *val); -MalVal *malval_new_function(void *(*func)(void *), int arg_cnt, MalVal* metadata); +MalVal *malval_new_function(void *(*func)(void *), int arg_cnt); +// Numbers +#define WRAP_INTEGER_OP(name, op) \ + MalVal *int_ ## name(MalVal *a, MalVal *b) { \ + return malval_new_integer(a->val.intnum op b->val.intnum); \ + } +#define WRAP_INTEGER_CMP_OP(name, op) \ + MalVal *int_ ## name(MalVal *a, MalVal *b) { \ + return a->val.intnum op b->val.intnum ? &mal_true : &mal_false; \ + } + +// Collections MalVal *_listX(int count, ...); MalVal *_list(MalVal *args); MalVal *_vector(MalVal *args); @@ -148,6 +159,9 @@ char *_pr_str(MalVal *args, int print_readably); MalVal *_slice(MalVal *seq, int start, int end); MalVal *_nth(MalVal *seq, int idx); +MalVal *_first(MalVal *seq); +MalVal *_rest(MalVal *seq); +MalVal *_last(MalVal *seq); MalVal *_map2(MalVal *(*func)(void*, void*), MalVal *lst, void *arg2); diff --git a/clojure/src/core.clj b/clojure/src/core.clj index 27599c0..73d8fb4 100644 --- a/clojure/src/core.clj +++ b/clojure/src/core.clj @@ -1,4 +1,5 @@ -(ns core) +(ns core + (:require [readline])) ;; Errors/exceptions (defn mal_throw [obj] @@ -26,10 +27,14 @@ ['true? true?] ['false? false?] ['symbol? symbol?] + ['pr-str pr-str] ['str str] ['prn prn] ['println println] + ['readline readline/readline] + ['read-string reader/read-string] + ['slurp slurp] ['< <] ['<= <=] ['> >] diff --git a/clojure/src/step3_env.clj b/clojure/src/step3_env.clj index 7358b49..cb634ff 100644 --- a/clojure/src/step3_env.clj +++ b/clojure/src/step3_env.clj @@ -57,11 +57,10 @@ [strng] (PRINT (EVAL (READ strng) repl-env))) -(defn _ref [k,v] (env/env-set repl-env k v)) -(_ref '+ +) -(_ref '- -) -(_ref '* *) -(_ref '/ /) +(env/env-set repl-env '+ +) +(env/env-set repl-env '- -) +(env/env-set repl-env '* *) +(env/env-set repl-env '/ /) (defn -main [& args] diff --git a/clojure/src/step4_if_fn_do.clj b/clojure/src/step4_if_fn_do.clj index d332a14..49c8b03 100644 --- a/clojure/src/step4_if_fn_do.clj +++ b/clojure/src/step4_if_fn_do.clj @@ -73,12 +73,11 @@ [strng] (PRINT (EVAL (READ strng) repl-env))) -(defn _ref [k,v] (env/env-set repl-env k v)) +;; core.clj: defined using Clojure +(doseq [[k v] core/core_ns] (env/env-set repl-env k v)) +(env/env-set repl-env 'eval (fn [ast] (EVAL ast repl-env))) -;; Import types related functions -(doseq [[k v] core/core_ns] (_ref k v)) - -;; Defined using the language itself +;; core.mal: defined using the language itself (rep "(def! not (fn* [a] (if a false true)))") (defn -main [& args] diff --git a/clojure/src/step5_tco.clj b/clojure/src/step5_tco.clj index f4f0107..69e09bf 100644 --- a/clojure/src/step5_tco.clj +++ b/clojure/src/step5_tco.clj @@ -83,12 +83,11 @@ [strng] (PRINT (EVAL (READ strng) repl-env))) -(defn _ref [k,v] (env/env-set repl-env k v)) +;; core.clj: defined using Clojure +(doseq [[k v] core/core_ns] (env/env-set repl-env k v)) +(env/env-set repl-env 'eval (fn [ast] (EVAL ast repl-env))) -;; Import types related functions -(doseq [[k v] core/core_ns] (_ref k v)) - -;; Defined using the language itself +;; core.mal: defined using the language itself (rep "(def! not (fn* [a] (if a false true)))") (defn -main [& args] diff --git a/clojure/src/step6_file.clj b/clojure/src/step6_file.clj index 46bab16..2aeb1be 100644 --- a/clojure/src/step6_file.clj +++ b/clojure/src/step6_file.clj @@ -83,16 +83,11 @@ [strng] (PRINT (EVAL (READ strng) repl-env))) -(defn _ref [k,v] (env/env-set repl-env k v)) - -;; Import types related functions -(doseq [[k v] core/core_ns] (_ref k v)) - -;; Defined using the language itself -(_ref 'read-string reader/read-string) -(_ref 'eval (fn [ast] (EVAL ast repl-env))) -(_ref 'slurp slurp) +;; core.clj: defined using Clojure +(doseq [[k v] core/core_ns] (env/env-set repl-env k v)) +(env/env-set repl-env 'eval (fn [ast] (EVAL ast repl-env))) +;; core.mal: defined using the language itself (rep "(def! not (fn* [a] (if a false true)))") (rep "(def! load-file (fn* [f] (eval (read-string (str \"(do \" (slurp f) \")\")))))") diff --git a/clojure/src/step7_quote.clj b/clojure/src/step7_quote.clj index 5e58e42..774c61f 100644 --- a/clojure/src/step7_quote.clj +++ b/clojure/src/step7_quote.clj @@ -106,16 +106,11 @@ [strng] (PRINT (EVAL (READ strng) repl-env))) -(defn _ref [k,v] (env/env-set repl-env k v)) - -;; Import types related functions -(doseq [[k v] core/core_ns] (_ref k v)) - -;; Defined using the language itself -(_ref 'read-string reader/read-string) -(_ref 'eval (fn [ast] (EVAL ast repl-env))) -(_ref 'slurp slurp) +;; core.clj: defined using Clojure +(doseq [[k v] core/core_ns] (env/env-set repl-env k v)) +(env/env-set repl-env 'eval (fn [ast] (EVAL ast repl-env))) +;; core.mal: defined using the language itself (rep "(def! not (fn* [a] (if a false true)))") (rep "(def! load-file (fn* [f] (eval (read-string (str \"(do \" (slurp f) \")\")))))") diff --git a/clojure/src/step8_macros.clj b/clojure/src/step8_macros.clj index 605e9d6..723be17 100644 --- a/clojure/src/step8_macros.clj +++ b/clojure/src/step8_macros.clj @@ -132,18 +132,15 @@ [strng] (PRINT (EVAL (READ strng) repl-env))) -(defn _ref [k,v] (env/env-set repl-env k v)) - -;; Import types related functions -(doseq [[k v] core/core_ns] (_ref k v)) - -;; Defined using the language itself -(_ref 'read-string reader/read-string) -(_ref 'eval (fn [ast] (EVAL ast repl-env))) -(_ref 'slurp slurp) +;; core.clj: defined using Clojure +(doseq [[k v] core/core_ns] (env/env-set repl-env k v)) +(env/env-set repl-env 'eval (fn [ast] (EVAL ast repl-env))) +;; core.mal: defined using the language itself (rep "(def! not (fn* [a] (if a false true)))") (rep "(def! load-file (fn* [f] (eval (read-string (str \"(do \" (slurp f) \")\")))))") +(rep "(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))") +(rep "(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))") (defn -main [& args] (if args diff --git a/clojure/src/step9_interop.clj b/clojure/src/step9_interop.clj index 2fae3ca..40f2b5c 100644 --- a/clojure/src/step9_interop.clj +++ b/clojure/src/step9_interop.clj @@ -135,18 +135,15 @@ [strng] (PRINT (EVAL (READ strng) repl-env))) -(defn _ref [k,v] (env/env-set repl-env k v)) - -;; Import types related functions -(doseq [[k v] core/core_ns] (_ref k v)) - -;; Defined using the language itself -(_ref 'read-string reader/read-string) -(_ref 'eval (fn [ast] (EVAL ast repl-env))) -(_ref 'slurp slurp) +;; core.clj: defined using Clojure +(doseq [[k v] core/core_ns] (env/env-set repl-env k v)) +(env/env-set repl-env 'eval (fn [ast] (EVAL ast repl-env))) +;; core.mal: defined using the language itself (rep "(def! not (fn* [a] (if a false true)))") (rep "(def! load-file (fn* [f] (eval (read-string (str \"(do \" (slurp f) \")\")))))") +(rep "(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))") +(rep "(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))") (defn -main [& args] (if args diff --git a/clojure/src/stepA_more.clj b/clojure/src/stepA_more.clj index 30eb8a6..d45d86a 100644 --- a/clojure/src/stepA_more.clj +++ b/clojure/src/stepA_more.clj @@ -149,21 +149,15 @@ [strng] (PRINT (EVAL (READ strng) repl-env))) -(defn _ref [k,v] (env/env-set repl-env k v)) - -;; Import types related functions -(doseq [[k v] core/core_ns] (_ref k v)) - -;; Defined using the language itself -(_ref 'readline readline/readline) -(_ref 'read-string reader/read-string) -(_ref 'eval (fn [ast] (EVAL ast repl-env))) -(_ref 'slurp slurp) +;; core.clj: defined using Clojure +(doseq [[k v] core/core_ns] (env/env-set repl-env k v)) +(env/env-set repl-env 'eval (fn [ast] (EVAL ast repl-env))) +;; core.mal: defined using the language itself (rep "(def! not (fn* [a] (if a false true)))") +(rep "(def! load-file (fn* [f] (eval (read-string (str \"(do \" (slurp f) \")\")))))") (rep "(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))") (rep "(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))") -(rep "(def! load-file (fn* [f] (eval (read-string (str \"(do \" (slurp f) \")\")))))") (defn -main [& args] (if args @@ -1,4 +1,5 @@ using System; +using System.IO; using System.Collections.Generic; using MalVal = Mal.types.MalVal; using MalConstant = Mal.types.MalConstant; @@ -54,6 +55,21 @@ namespace Mal { return Nil; } ); + static public MalFunction mal_readline = new MalFunction( + a => { + var line = readline.Readline(((MalString)a[0]).getValue()); + if (line == null) { return types.Nil; } + else { return new MalString(line); } + } ); + + static public MalFunction read_string = new MalFunction( + a => reader.read_str(((MalString)a[0]).getValue())); + + static public MalFunction slurp = new MalFunction( + a => new MalString(File.ReadAllText( + ((MalString)a[0]).getValue()))); + + // List/Vector functions static public MalFunction list_Q = new MalFunction( a => a[0].GetType() == typeof(MalList) ? True : False); @@ -233,10 +249,14 @@ namespace Mal { {"true?", true_Q}, {"false?", false_Q}, {"symbol?", symbol_Q}, + {"pr-str", pr_str}, {"str", str}, {"prn", prn}, {"println", println}, + {"readline", mal_readline}, + {"read-string", read_string}, + {"slurp", slurp}, {"<", new MalFunction(a => (MalInteger)a[0] < (MalInteger)a[1])}, {"<=", new MalFunction(a => (MalInteger)a[0] <= (MalInteger)a[1])}, {">", new MalFunction(a => (MalInteger)a[0] > (MalInteger)a[1])}, diff --git a/cs/step1_read_print.cs b/cs/step1_read_print.cs index 92a8b65..62cd7ad 100644 --- a/cs/step1_read_print.cs +++ b/cs/step1_read_print.cs @@ -31,7 +31,6 @@ namespace Mal { if (args.Length > 0 && args[0] == "--raw") { Mal.readline.mode = Mal.readline.Mode.Raw; } - while (true) { string line; try { @@ -45,13 +44,10 @@ namespace Mal { Console.WriteLine(PRINT(RE(null, line))); } catch (Mal.types.MalContinue) { continue; - } catch (Mal.types.MalError e) { + } catch (Exception e) { Console.WriteLine("Error: " + e.Message); + Console.WriteLine(e.StackTrace); continue; - } catch (Mal.reader.ParseError e) { - Console.WriteLine(e.Message); - continue; - } } } diff --git a/cs/step2_eval.cs b/cs/step2_eval.cs index 09bf36d..dc467de 100644 --- a/cs/step2_eval.cs +++ b/cs/step2_eval.cs @@ -53,13 +53,13 @@ namespace Mal { // apply list MalList ast = (MalList)orig_ast; if (ast.size() == 0) { return ast; } - a0 = ast.nth(0); + a0 = ast[0]; if (!(a0 is MalSymbol)) { throw new Mal.types.MalError("attempt to apply on non-symbol '" + Mal.printer._pr_str(a0,true) + "'"); } var el = (MalList)eval_ast(ast, env); - var f = (MalFunction)el.nth(0); + var f = (MalFunction)el[0]; return f.apply(el.rest()); } @@ -92,10 +92,10 @@ namespace Mal { {"*", multiply}, {"/", divide}, }; + if (args.Length > 0 && args[0] == "--raw") { Mal.readline.mode = Mal.readline.Mode.Raw; } - while (true) { string line; try { @@ -109,14 +109,9 @@ namespace Mal { Console.WriteLine(PRINT(RE(repl_env, line))); } catch (Mal.types.MalContinue) { continue; - } catch (Mal.reader.ParseError e) { - Console.WriteLine(e.Message); - continue; - } catch (Mal.types.MalError e) { - Console.WriteLine("Error: " + e.Message); - continue; } catch (Exception e) { Console.WriteLine("Error: " + e.Message); + Console.WriteLine(e.StackTrace); continue; } } diff --git a/cs/step3_env.cs b/cs/step3_env.cs index 032fadd..ba859eb 100644 --- a/cs/step3_env.cs +++ b/cs/step3_env.cs @@ -55,7 +55,7 @@ namespace Mal { // apply list MalList ast = (MalList)orig_ast; if (ast.size() == 0) { return ast; } - a0 = ast.nth(0); + a0 = ast[0]; if (!(a0 is MalSymbol)) { throw new Mal.types.MalError("attempt to apply on non-symbol '" + Mal.printer._pr_str(a0,true) + "'"); @@ -63,26 +63,26 @@ namespace Mal { switch (((MalSymbol)a0).getName()) { case "def!": - a1 = ast.nth(1); - a2 = ast.nth(2); + a1 = ast[1]; + a2 = ast[2]; res = EVAL(a2, env); env.set(((MalSymbol)a1).getName(), res); return res; case "let*": - a1 = ast.nth(1); - a2 = ast.nth(2); + a1 = ast[1]; + a2 = ast[2]; MalSymbol key; MalVal val; Env let_env = new Env(env); for(int i=0; i<((MalList)a1).size(); i+=2) { - key = (MalSymbol)((MalList)a1).nth(i); - val = ((MalList)a1).nth(i+1); + key = (MalSymbol)((MalList)a1)[i]; + val = ((MalList)a1)[i+1]; let_env.set(key.getName(), EVAL(val, let_env)); } return EVAL(a2, let_env); default: el = (MalList)eval_ast(ast, env); - var f = (MalFunction)el.nth(0); + var f = (MalFunction)el[0]; return f.apply(el.rest()); } } @@ -96,9 +96,6 @@ namespace Mal { static MalVal RE(Env env, string str) { return EVAL(READ(str), env); } - public static Env _ref(Env env, string name, MalVal mv) { - return env.set(name, mv); - } static public MalFunction plus = new MalFunction( a => (MalInteger)a[0] + (MalInteger)a[1] ); @@ -114,10 +111,10 @@ namespace Mal { string prompt = "user> "; var repl_env = new Mal.env.Env(null); - _ref(repl_env, "+", plus); - _ref(repl_env, "-", minus); - _ref(repl_env, "*", multiply); - _ref(repl_env, "/", divide); + repl_env.set("+", plus); + repl_env.set("-", minus); + repl_env.set("*", multiply); + repl_env.set("/", divide); if (args.Length > 0 && args[0] == "--raw") { Mal.readline.mode = Mal.readline.Mode.Raw; @@ -135,14 +132,9 @@ namespace Mal { Console.WriteLine(PRINT(RE(repl_env, line))); } catch (Mal.types.MalContinue) { continue; - } catch (Mal.reader.ParseError e) { - Console.WriteLine(e.Message); - continue; - } catch (Mal.types.MalException e) { - Console.WriteLine("Error: " + e.getValue()); - continue; } catch (Exception e) { Console.WriteLine("Error: " + e.Message); + Console.WriteLine(e.StackTrace); continue; } } diff --git a/cs/step4_if_fn_do.cs b/cs/step4_if_fn_do.cs index d0ca882..2383c31 100644 --- a/cs/step4_if_fn_do.cs +++ b/cs/step4_if_fn_do.cs @@ -55,58 +55,58 @@ namespace Mal { // apply list MalList ast = (MalList)orig_ast; if (ast.size() == 0) { return ast; } - a0 = ast.nth(0); + a0 = ast[0]; String a0sym = a0 is MalSymbol ? ((MalSymbol)a0).getName() : "__<*fn*>__"; switch (a0sym) { case "def!": - a1 = ast.nth(1); - a2 = ast.nth(2); + a1 = ast[1]; + a2 = ast[2]; res = EVAL(a2, env); env.set(((MalSymbol)a1).getName(), res); return res; case "let*": - a1 = ast.nth(1); - a2 = ast.nth(2); + a1 = ast[1]; + a2 = ast[2]; MalSymbol key; MalVal val; Env let_env = new Env(env); for(int i=0; i<((MalList)a1).size(); i+=2) { - key = (MalSymbol)((MalList)a1).nth(i); - val = ((MalList)a1).nth(i+1); + key = (MalSymbol)((MalList)a1)[i]; + val = ((MalList)a1)[i+1]; let_env.set(key.getName(), EVAL(val, let_env)); } return EVAL(a2, let_env); case "do": el = (MalList)eval_ast(ast.rest(), env); - return el.nth(el.size()-1); + return el[el.size()-1]; case "if": - a1 = ast.nth(1); + a1 = ast[1]; MalVal cond = EVAL(a1, env); if (cond == Mal.types.Nil || cond == Mal.types.False) { // eval false slot form if (ast.size() > 3) { - a3 = ast.nth(3); + a3 = ast[3]; return EVAL(a3, env); } else { return Mal.types.Nil; } } else { // eval true slot form - a2 = ast.nth(2); + a2 = ast[2]; return EVAL(a2, env); } case "fn*": - MalList a1f = (MalList)ast.nth(1); - MalVal a2f = ast.nth(2); + MalList a1f = (MalList)ast[1]; + MalVal a2f = ast[2]; Env cur_env = env; return new MalFunction( args => EVAL(a2f, new Env(cur_env, a1f, args)) ); default: el = (MalList)eval_ast(ast, env); - var f = (MalFunction)el.nth(0); + var f = (MalFunction)el[0]; return f.apply(el.rest()); } } @@ -120,18 +120,17 @@ namespace Mal { static MalVal RE(Env env, string str) { return EVAL(READ(str), env); } - public static Env _ref(Env env, string name, MalVal mv) { - return env.set(name, mv); - } static void Main(string[] args) { string prompt = "user> "; - var repl_env = new Mal.env.Env(null); - foreach (var entry in Mal.core.ns) { - _ref(repl_env, entry.Key, entry.Value); + // core.cs: defined using C# + var repl_env = new env.Env(null); + foreach (var entry in core.ns) { + repl_env.set(entry.Key, entry.Value); } + // core.mal: defined using the language itself RE(repl_env, "(def! not (fn* (a) (if a false true)))"); if (args.Length > 0 && args[0] == "--raw") { @@ -150,14 +149,9 @@ namespace Mal { Console.WriteLine(PRINT(RE(repl_env, line))); } catch (Mal.types.MalContinue) { continue; - } catch (Mal.reader.ParseError e) { - Console.WriteLine(e.Message); - continue; - } catch (Mal.types.MalException e) { - Console.WriteLine("Error: " + e.getValue()); - continue; } catch (Exception e) { Console.WriteLine("Error: " + e.Message); + Console.WriteLine(e.StackTrace); continue; } } diff --git a/cs/step5_tco.cs b/cs/step5_tco.cs index 18b2fc9..b04d16e 100644 --- a/cs/step5_tco.cs +++ b/cs/step5_tco.cs @@ -13,7 +13,7 @@ using MalFunction = Mal.types.MalFunction; using Env = Mal.env.Env; namespace Mal { - class step4_if_fn_do { + class step5_tco { // read static MalVal READ(string str) { return reader.read_str(str); @@ -58,36 +58,36 @@ namespace Mal { // apply list MalList ast = (MalList)orig_ast; if (ast.size() == 0) { return ast; } - a0 = ast.nth(0); + a0 = ast[0]; String a0sym = a0 is MalSymbol ? ((MalSymbol)a0).getName() : "__<*fn*>__"; switch (a0sym) { case "def!": - a1 = ast.nth(1); - a2 = ast.nth(2); + a1 = ast[1]; + a2 = ast[2]; res = EVAL(a2, env); env.set(((MalSymbol)a1).getName(), res); return res; case "let*": - a1 = ast.nth(1); - a2 = ast.nth(2); + a1 = ast[1]; + a2 = ast[2]; MalSymbol key; MalVal val; Env let_env = new Env(env); for(int i=0; i<((MalList)a1).size(); i+=2) { - key = (MalSymbol)((MalList)a1).nth(i); - val = ((MalList)a1).nth(i+1); + key = (MalSymbol)((MalList)a1)[i]; + val = ((MalList)a1)[i+1]; let_env.set(key.getName(), EVAL(val, let_env)); } return EVAL(a2, let_env); case "do": eval_ast(ast.slice(1, ast.size()-1), env); - orig_ast = ast.nth(ast.size()-1); + orig_ast = ast[ast.size()-1]; break; case "if": - a1 = ast.nth(1); + a1 = ast[1]; MalVal cond = EVAL(a1, env); if (cond == Mal.types.Nil || cond == Mal.types.False) { // eval false slot form @@ -102,14 +102,14 @@ namespace Mal { } break; case "fn*": - MalList a1f = (MalList)ast.nth(1); - MalVal a2f = ast.nth(2); + MalList a1f = (MalList)ast[1]; + MalVal a2f = ast[2]; Env cur_env = env; return new MalFunction(a2f, env, a1f, args => EVAL(a2f, new Env(cur_env, a1f, args)) ); default: el = (MalList)eval_ast(ast, env); - var f = (MalFunction)el.nth(0); + var f = (MalFunction)el[0]; MalVal fnast = f.getAst(); if (fnast != null) { orig_ast = fnast; @@ -132,18 +132,17 @@ namespace Mal { static MalVal RE(Env env, string str) { return EVAL(READ(str), env); } - public static Env _ref(Env env, string name, MalVal mv) { - return env.set(name, mv); - } static void Main(string[] args) { string prompt = "user> "; - var repl_env = new Mal.env.Env(null); - foreach (var entry in Mal.core.ns) { - _ref(repl_env, entry.Key, entry.Value); + // core.cs: defined using C# + var repl_env = new env.Env(null); + foreach (var entry in core.ns) { + repl_env.set(entry.Key, entry.Value); } + // core.mal: defined using the language itself RE(repl_env, "(def! not (fn* (a) (if a false true)))"); if (args.Length > 0 && args[0] == "--raw") { @@ -162,14 +161,9 @@ namespace Mal { Console.WriteLine(PRINT(RE(repl_env, line))); } catch (Mal.types.MalContinue) { continue; - } catch (Mal.reader.ParseError e) { - Console.WriteLine(e.Message); - continue; - } catch (Mal.types.MalException e) { - Console.WriteLine("Error: " + e.getValue()); - continue; } catch (Exception e) { Console.WriteLine("Error: " + e.Message); + Console.WriteLine(e.StackTrace); continue; } } diff --git a/cs/step6_file.cs b/cs/step6_file.cs index b02cedb..cb2157e 100644 --- a/cs/step6_file.cs +++ b/cs/step6_file.cs @@ -4,7 +4,6 @@ using System.Collections; using System.Collections.Generic; using Mal; using MalVal = Mal.types.MalVal; -using MalString = Mal.types.MalString; using MalSymbol = Mal.types.MalSymbol; using MalInteger = Mal.types.MalInteger; using MalList = Mal.types.MalList; @@ -14,7 +13,7 @@ using MalFunction = Mal.types.MalFunction; using Env = Mal.env.Env; namespace Mal { - class step4_if_fn_do { + class step6_file { // read static MalVal READ(string str) { return reader.read_str(str); @@ -59,36 +58,36 @@ namespace Mal { // apply list MalList ast = (MalList)orig_ast; if (ast.size() == 0) { return ast; } - a0 = ast.nth(0); + a0 = ast[0]; String a0sym = a0 is MalSymbol ? ((MalSymbol)a0).getName() : "__<*fn*>__"; switch (a0sym) { case "def!": - a1 = ast.nth(1); - a2 = ast.nth(2); + a1 = ast[1]; + a2 = ast[2]; res = EVAL(a2, env); env.set(((MalSymbol)a1).getName(), res); return res; case "let*": - a1 = ast.nth(1); - a2 = ast.nth(2); + a1 = ast[1]; + a2 = ast[2]; MalSymbol key; MalVal val; Env let_env = new Env(env); for(int i=0; i<((MalList)a1).size(); i+=2) { - key = (MalSymbol)((MalList)a1).nth(i); - val = ((MalList)a1).nth(i+1); + key = (MalSymbol)((MalList)a1)[i]; + val = ((MalList)a1)[i+1]; let_env.set(key.getName(), EVAL(val, let_env)); } return EVAL(a2, let_env); case "do": eval_ast(ast.slice(1, ast.size()-1), env); - orig_ast = ast.nth(ast.size()-1); + orig_ast = ast[ast.size()-1]; break; case "if": - a1 = ast.nth(1); + a1 = ast[1]; MalVal cond = EVAL(a1, env); if (cond == Mal.types.Nil || cond == Mal.types.False) { // eval false slot form @@ -103,14 +102,14 @@ namespace Mal { } break; case "fn*": - MalList a1f = (MalList)ast.nth(1); - MalVal a2f = ast.nth(2); + MalList a1f = (MalList)ast[1]; + MalVal a2f = ast[2]; Env cur_env = env; return new MalFunction(a2f, env, a1f, args => EVAL(a2f, new Env(cur_env, a1f, args)) ); default: el = (MalList)eval_ast(ast, env); - var f = (MalFunction)el.nth(0); + var f = (MalFunction)el[0]; MalVal fnast = f.getAst(); if (fnast != null) { orig_ast = fnast; @@ -133,26 +132,18 @@ namespace Mal { static MalVal RE(Env env, string str) { return EVAL(READ(str), env); } - public static Env _ref(Env env, string name, MalVal mv) { - return env.set(name, mv); - } - static void Main(string[] args) { string prompt = "user> "; - var repl_env = new Mal.env.Env(null); - foreach (var entry in Mal.core.ns) { - _ref(repl_env, entry.Key, entry.Value); + // core.cs: defined using C# + var repl_env = new env.Env(null); + foreach (var entry in core.ns) { + repl_env.set(entry.Key, entry.Value); } - _ref(repl_env, "read-string", new MalFunction( - a => reader.read_str(((MalString)a[0]).getValue()))); - _ref(repl_env, "eval", new MalFunction( - a => EVAL(a[0], repl_env))); - _ref(repl_env, "slurp", new MalFunction( - a => new MalString(File.ReadAllText( - ((MalString)a[0]).getValue())))); + repl_env.set("eval", new MalFunction(a => EVAL(a[0], repl_env))); + // core.mal: defined using the language itself RE(repl_env, "(def! not (fn* (a) (if a false true)))"); RE(repl_env, "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))"); @@ -180,14 +171,9 @@ namespace Mal { Console.WriteLine(PRINT(RE(repl_env, line))); } catch (Mal.types.MalContinue) { continue; - } catch (Mal.reader.ParseError e) { - Console.WriteLine(e.Message); - continue; - } catch (Mal.types.MalException e) { - Console.WriteLine("Error: " + e.getValue()); - continue; } catch (Exception e) { Console.WriteLine("Error: " + e.Message); + Console.WriteLine(e.StackTrace); continue; } } diff --git a/cs/step7_quote.cs b/cs/step7_quote.cs index 61f638f..6f08abb 100644 --- a/cs/step7_quote.cs +++ b/cs/step7_quote.cs @@ -14,7 +14,7 @@ using MalFunction = Mal.types.MalFunction; using Env = Mal.env.Env; namespace Mal { - class step4_if_fn_do { + class step7_quote { // read static MalVal READ(string str) { return reader.read_str(str); @@ -164,26 +164,18 @@ namespace Mal { static MalVal RE(Env env, string str) { return EVAL(READ(str), env); } - public static Env _ref(Env env, string name, MalVal mv) { - return env.set(name, mv); - } - static void Main(string[] args) { string prompt = "user> "; - var repl_env = new Mal.env.Env(null); - foreach (var entry in Mal.core.ns) { - _ref(repl_env, entry.Key, entry.Value); + // core.cs: defined using C# + var repl_env = new env.Env(null); + foreach (var entry in core.ns) { + repl_env.set(entry.Key, entry.Value); } - _ref(repl_env, "read-string", new MalFunction( - a => reader.read_str(((MalString)a[0]).getValue()))); - _ref(repl_env, "eval", new MalFunction( - a => EVAL(a[0], repl_env))); - _ref(repl_env, "slurp", new MalFunction( - a => new MalString(File.ReadAllText( - ((MalString)a[0]).getValue())))); + repl_env.set("eval", new MalFunction(a => EVAL(a[0], repl_env))); + // core.mal: defined using the language itself RE(repl_env, "(def! not (fn* (a) (if a false true)))"); RE(repl_env, "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))"); @@ -211,12 +203,6 @@ namespace Mal { Console.WriteLine(PRINT(RE(repl_env, line))); } catch (Mal.types.MalContinue) { continue; - } catch (Mal.reader.ParseError e) { - Console.WriteLine(e.Message); - continue; - } catch (Mal.types.MalException e) { - Console.WriteLine("Error: " + e.getValue()); - continue; } catch (Exception e) { Console.WriteLine("Error: " + e.Message); Console.WriteLine(e.StackTrace); diff --git a/cs/step8_macros.cs b/cs/step8_macros.cs index fe085f9..caa8f0a 100644 --- a/cs/step8_macros.cs +++ b/cs/step8_macros.cs @@ -14,7 +14,7 @@ using MalFunction = Mal.types.MalFunction; using Env = Mal.env.Env; namespace Mal { - class step4_if_fn_do { + class step8_macros { // read static MalVal READ(string str) { return reader.read_str(str); @@ -201,28 +201,22 @@ namespace Mal { static MalVal RE(Env env, string str) { return EVAL(READ(str), env); } - public static Env _ref(Env env, string name, MalVal mv) { - return env.set(name, mv); - } - static void Main(string[] args) { string prompt = "user> "; - var repl_env = new Mal.env.Env(null); - foreach (var entry in Mal.core.ns) { - _ref(repl_env, entry.Key, entry.Value); + // core.cs: defined using C# + var repl_env = new env.Env(null); + foreach (var entry in core.ns) { + repl_env.set(entry.Key, entry.Value); } - _ref(repl_env, "read-string", new MalFunction( - a => reader.read_str(((MalString)a[0]).getValue()))); - _ref(repl_env, "eval", new MalFunction( - a => EVAL(a[0], repl_env))); - _ref(repl_env, "slurp", new MalFunction( - a => new MalString(File.ReadAllText( - ((MalString)a[0]).getValue())))); + repl_env.set("eval", new MalFunction(a => EVAL(a[0], repl_env))); + // core.mal: defined using the language itself RE(repl_env, "(def! not (fn* (a) (if a false true)))"); RE(repl_env, "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))"); + RE(repl_env, "(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))"); + RE(repl_env, "(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))"); int fileIdx = 0; if (args.Length > 0 && args[0] == "--raw") { @@ -248,12 +242,6 @@ namespace Mal { Console.WriteLine(PRINT(RE(repl_env, line))); } catch (Mal.types.MalContinue) { continue; - } catch (Mal.reader.ParseError e) { - Console.WriteLine(e.Message); - continue; - } catch (Mal.types.MalException e) { - Console.WriteLine("Error: " + e.getValue()); - continue; } catch (Exception e) { Console.WriteLine("Error: " + e.Message); Console.WriteLine(e.StackTrace); diff --git a/cs/stepA_more.cs b/cs/stepA_more.cs index 97b1e84..cbbc91f 100644 --- a/cs/stepA_more.cs +++ b/cs/stepA_more.cs @@ -3,7 +3,6 @@ using System.IO; using System.Collections; using System.Collections.Generic; using Mal; -using MalException = Mal.types.MalException; using MalVal = Mal.types.MalVal; using MalString = Mal.types.MalString; using MalSymbol = Mal.types.MalSymbol; @@ -161,8 +160,8 @@ namespace Mal { a2 = ast[2]; MalVal a20 = ((MalList)a2)[0]; if (((MalSymbol)a20).getName() == "catch*") { - if (e is MalException) { - exc = ((MalException)e).getValue(); + if (e is Mal.types.MalException) { + exc = ((Mal.types.MalException)e).getValue(); } else { exc = new MalString(e.StackTrace); } @@ -180,12 +179,12 @@ namespace Mal { case "if": a1 = ast[1]; MalVal cond = EVAL(a1, env); - if (cond == types.Nil || cond == types.False) { + if (cond == Mal.types.Nil || cond == Mal.types.False) { // eval false slot form if (ast.size() > 3) { orig_ast = ast[3]; } else { - return types.Nil; + return Mal.types.Nil; } } else { // eval true slot form @@ -223,40 +222,26 @@ namespace Mal { static MalVal RE(Env env, string str) { return EVAL(READ(str), env); } - public static Env _ref(Env env, string name, MalVal mv) { - return env.set(name, mv); - } - static void Main(string[] args) { string prompt = "user> "; + // core.cs: defined using C# var repl_env = new env.Env(null); foreach (var entry in core.ns) { - _ref(repl_env, entry.Key, entry.Value); + repl_env.set(entry.Key, entry.Value); } - _ref(repl_env, "readline", new MalFunction( - a => { - var line = readline.Readline(((MalString)a[0]).getValue()); - if (line == null) { return types.Nil; } - else { return new MalString(line); } - })); - _ref(repl_env, "read-string", new MalFunction( - a => reader.read_str(((MalString)a[0]).getValue()))); - _ref(repl_env, "eval", new MalFunction( - a => EVAL(a[0], repl_env))); - _ref(repl_env, "slurp", new MalFunction( - a => new MalString(File.ReadAllText( - ((MalString)a[0]).getValue())))); + repl_env.set("eval", new MalFunction(a => EVAL(a[0], repl_env))); + // core.mal: defined using the language itself RE(repl_env, "(def! not (fn* (a) (if a false true)))"); + RE(repl_env, "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))"); RE(repl_env, "(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))"); RE(repl_env, "(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))"); - RE(repl_env, "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))"); int fileIdx = 0; if (args.Length > 0 && args[0] == "--raw") { - readline.mode = readline.Mode.Raw; + Mal.readline.mode = Mal.readline.Mode.Raw; fileIdx = 1; } if (args.Length > fileIdx) { @@ -268,7 +253,7 @@ namespace Mal { while (true) { string line; try { - line = readline.Readline(prompt); + line = Mal.readline.Readline(prompt); if (line == null) { break; } } catch (IOException e) { Console.WriteLine("IOException: " + e.Message); @@ -276,12 +261,9 @@ namespace Mal { } try { Console.WriteLine(PRINT(RE(repl_env, line))); - } catch (types.MalContinue) { - continue; - } catch (reader.ParseError e) { - Console.WriteLine(e.Message); + } catch (Mal.types.MalContinue) { continue; - } catch (MalException e) { + } catch (Mal.types.MalException e) { Console.WriteLine("Error: " + printer._pr_str(e.getValue(), false)); continue; diff --git a/cs/types.cs b/cs/types.cs index 34985ad..b49adb6 100644 --- a/cs/types.cs +++ b/cs/types.cs @@ -19,10 +19,11 @@ namespace Mal { // Thrown by throw function public class MalException : MalThrowable { MalVal value; + //string Message; public MalException(MalVal value) { this.value = value; } - public MalException(string value) { + public MalException(string value) :base(value) { this.value = new MalString(value); } public MalVal getValue() { return value; } @@ -7,8 +7,7 @@ All: - gensym reader inside quasiquote - can let* and quasiquote be TCO'd ? - - more interop tests - - test metadata on builtin functions (i.e. +) + - per impl tests for step5_tco, step9_interop (if possible) - regular expression matching in runtest - Print full exception when test gets EOF from expect diff --git a/java/src/main/java/mal/core.java b/java/src/main/java/mal/core.java index 6ac4f8e..ea6787d 100644 --- a/java/src/main/java/mal/core.java +++ b/java/src/main/java/mal/core.java @@ -7,8 +7,14 @@ import java.util.Map; import java.util.HashMap; import com.google.common.collect.ImmutableMap; +import java.io.IOException; +import java.io.FileNotFoundException; +import java.util.Scanner; +import java.io.File; + import mal.types.*; import mal.printer; +import mal.readline; public class core { // Local references for convenience @@ -84,6 +90,41 @@ public class core { } }; + static MalFunction mal_readline = new MalFunction() { + public MalVal apply(MalList args) throws MalThrowable { + String prompt = ((MalString)args.nth(0)).getValue(); + try { + return new MalString(readline.readline(prompt)); + } catch (IOException e) { + throw new MalException(new MalString(e.getMessage())); + } catch (readline.EOFException e) { + throw new MalException(new MalString(e.getMessage())); + } + } + }; + + static MalFunction read_string = new MalFunction() { + public MalVal apply(MalList args) throws MalThrowable { + try { + return reader.read_str(((MalString)args.nth(0)).getValue()); + } catch (MalContinue c) { + return types.Nil; + } + } + }; + + static MalFunction slurp = new MalFunction() { + public MalVal apply(MalList args) throws MalThrowable { + String fname = ((MalString)args.nth(0)).getValue(); + try { + return new MalString( + new Scanner(new File(fname)).useDelimiter("\\Z").next()); + } catch (FileNotFoundException e) { + throw new MalError(e.getMessage()); + } + } + }; + // Number functions static MalFunction add = new MalFunction() { @@ -423,10 +464,14 @@ public class core { .put("true?", true_Q) .put("false?", false_Q) .put("symbol?", symbol_Q) + .put("pr-str", pr_str) .put("str", str) .put("prn", prn) .put("println", println) + .put("readline", mal_readline) + .put("read-string", read_string) + .put("slurp", slurp) .put("<", lt) .put("<=", lte) .put(">", gt) diff --git a/java/src/main/java/mal/step2_eval.java b/java/src/main/java/mal/step2_eval.java index 3d425db..cc2b785 100644 --- a/java/src/main/java/mal/step2_eval.java +++ b/java/src/main/java/mal/step2_eval.java @@ -77,42 +77,36 @@ public class step2_eval { return EVAL(READ(str), env); } - static interface ILambda { - public MalVal apply(MalList args); - } - static class plus implements ILambda { - public MalVal apply(MalList args) { - return ((MalInteger)args.nth(0)).add( - ((MalInteger)args.nth(1))); + static MalFunction add = new MalFunction() { + public MalVal apply(MalList a) throws MalThrowable { + return ((MalInteger)a.nth(0)).add((MalInteger)a.nth(1)); } - } - static class minus implements ILambda { - public MalVal apply(MalList args) { - return ((MalInteger)args.nth(0)).subtract( - ((MalInteger)args.nth(1))); + }; + static MalFunction subtract = new MalFunction() { + public MalVal apply(MalList a) throws MalThrowable { + return ((MalInteger)a.nth(0)).subtract((MalInteger)a.nth(1)); } - } - static class multiply implements ILambda { - public MalVal apply(MalList args) { - return ((MalInteger)args.nth(0)).multiply( - ((MalInteger)args.nth(1))); + }; + static MalFunction multiply = new MalFunction() { + public MalVal apply(MalList a) throws MalThrowable { + return ((MalInteger)a.nth(0)).multiply((MalInteger)a.nth(1)); } - } - static class divide implements ILambda { - public MalVal apply(MalList args) { - return ((MalInteger)args.nth(0)).divide( - ((MalInteger)args.nth(1))); + }; + static MalFunction divide = new MalFunction() { + public MalVal apply(MalList a) throws MalThrowable { + return ((MalInteger)a.nth(0)).divide((MalInteger)a.nth(1)); } - } + }; + public static void main(String[] args) throws MalThrowable { String prompt = "user> "; HashMap repl_env = new HashMap(); - repl_env.put("+", new plus()); - repl_env.put("-", new minus()); - repl_env.put("*", new multiply()); - repl_env.put("/", new divide()); + repl_env.put("+", add); + repl_env.put("-", subtract); + repl_env.put("*", multiply); + repl_env.put("/", divide); if (args.length > 0 && args[0].equals("--raw")) { readline.mode = readline.Mode.JAVA; @@ -132,12 +126,12 @@ public class step2_eval { System.out.println(PRINT(RE(repl_env, line))); } catch (MalContinue e) { continue; - } catch (MalError e) { - System.out.println("Error: " + e.getMessage()); - continue; } catch (reader.ParseError e) { System.out.println(e.getMessage()); continue; + } catch (MalError e) { + System.out.println("Error: " + e.getMessage()); + continue; } } } diff --git a/java/src/main/java/mal/step3_env.java b/java/src/main/java/mal/step3_env.java index 690469e..297453c 100644 --- a/java/src/main/java/mal/step3_env.java +++ b/java/src/main/java/mal/step3_env.java @@ -11,7 +11,6 @@ import mal.readline; import mal.reader; import mal.printer; import mal.env.Env; -import mal.core; public class step3_env { // read @@ -97,18 +96,37 @@ public class step3_env { public static MalVal RE(Env env, String str) throws MalThrowable { return EVAL(READ(str), env); } - public static Env _ref(Env env, String name, MalVal mv) { - return env.set(name, mv); - } + + static MalFunction add = new MalFunction() { + public MalVal apply(MalList a) throws MalThrowable { + return ((MalInteger)a.nth(0)).add((MalInteger)a.nth(1)); + } + }; + static MalFunction subtract = new MalFunction() { + public MalVal apply(MalList a) throws MalThrowable { + return ((MalInteger)a.nth(0)).subtract((MalInteger)a.nth(1)); + } + }; + static MalFunction multiply = new MalFunction() { + public MalVal apply(MalList a) throws MalThrowable { + return ((MalInteger)a.nth(0)).multiply((MalInteger)a.nth(1)); + } + }; + static MalFunction divide = new MalFunction() { + public MalVal apply(MalList a) throws MalThrowable { + return ((MalInteger)a.nth(0)).divide((MalInteger)a.nth(1)); + } + }; + public static void main(String[] args) throws MalThrowable { String prompt = "user> "; Env repl_env = new Env(null); - _ref(repl_env, "+", core.add); - _ref(repl_env, "-", core.subtract); - _ref(repl_env, "*", core.multiply); - _ref(repl_env, "/", core.divide); + repl_env.set("+", add); + repl_env.set("-", subtract); + repl_env.set("*", multiply); + repl_env.set("/", divide); if (args.length > 0 && args[0].equals("--raw")) { readline.mode = readline.Mode.JAVA; @@ -128,12 +146,12 @@ public class step3_env { System.out.println(PRINT(RE(repl_env, line))); } catch (MalContinue e) { continue; - } catch (MalError e) { - System.out.println("Error: " + e.getMessage()); - continue; } catch (reader.ParseError e) { System.out.println(e.getMessage()); continue; + } catch (MalError e) { + System.out.println("Error: " + e.getMessage()); + continue; } } } diff --git a/java/src/main/java/mal/step4_if_fn_do.java b/java/src/main/java/mal/step4_if_fn_do.java index 4d064cf..6e7997a 100644 --- a/java/src/main/java/mal/step4_if_fn_do.java +++ b/java/src/main/java/mal/step4_if_fn_do.java @@ -122,18 +122,18 @@ public class step4_if_fn_do { public static MalVal RE(Env env, String str) throws MalThrowable { return EVAL(READ(str), env); } - public static Env _ref(Env env, String name, MalVal mv) { - return env.set(name, mv); - } public static void main(String[] args) throws MalThrowable { String prompt = "user> "; Env repl_env = new Env(null); + + // core.java: defined using Java for (String key : core.ns.keySet()) { - _ref(repl_env, key, core.ns.get(key)); + repl_env.set(key, core.ns.get(key)); } + // core.mal: defined using the language itself RE(repl_env, "(def! not (fn* (a) (if a false true)))"); if (args.length > 0 && args[0].equals("--raw")) { diff --git a/java/src/main/java/mal/step5_tco.java b/java/src/main/java/mal/step5_tco.java index 9cd42d5..ad15d74 100644 --- a/java/src/main/java/mal/step5_tco.java +++ b/java/src/main/java/mal/step5_tco.java @@ -133,18 +133,18 @@ public class step5_tco { public static MalVal RE(Env env, String str) throws MalThrowable { return EVAL(READ(str), env); } - public static Env _ref(Env env, String name, MalVal mv) { - return env.set(name, mv); - } public static void main(String[] args) throws MalThrowable { String prompt = "user> "; Env repl_env = new Env(null); + + // core.java: defined using Java for (String key : core.ns.keySet()) { - _ref(repl_env, key, core.ns.get(key)); + repl_env.set(key, core.ns.get(key)); } + // core.mal: defined using the language itself RE(repl_env, "(def! not (fn* (a) (if a false true)))"); if (args.length > 0 && args[0].equals("--raw")) { diff --git a/java/src/main/java/mal/step6_file.java b/java/src/main/java/mal/step6_file.java index 6a4c981..446ffa0 100644 --- a/java/src/main/java/mal/step6_file.java +++ b/java/src/main/java/mal/step6_file.java @@ -1,10 +1,7 @@ package mal; import java.io.IOException; -import java.io.FileNotFoundException; -import java.util.Scanner; -import java.io.File; import java.util.List; import java.util.Map; import java.util.HashMap; @@ -136,46 +133,23 @@ public class step6_file { public static MalVal RE(Env env, String str) throws MalThrowable { return EVAL(READ(str), env); } - public static Env _ref(Env env, String name, MalVal mv) { - return env.set(name, mv); - } - public static String slurp(String fname) throws MalThrowable { - try { - return new Scanner(new File(fname)) - .useDelimiter("\\Z").next(); - } catch (FileNotFoundException e) { - throw new MalError(e.getMessage()); - } - } public static void main(String[] args) throws MalThrowable { String prompt = "user> "; final Env repl_env = new Env(null); + + // core.java: defined using Java for (String key : core.ns.keySet()) { - _ref(repl_env, key, core.ns.get(key)); + repl_env.set(key, core.ns.get(key)); } - _ref(repl_env, "read-string", new MalFunction() { - public MalVal apply(MalList args) throws MalThrowable { - try { - return reader.read_str(((MalString)args.nth(0)).getValue()); - } catch (MalContinue c) { - return types.Nil; - } - } - }); - _ref(repl_env, "eval", new MalFunction() { + repl_env.set("eval", new MalFunction() { public MalVal apply(MalList args) throws MalThrowable { return EVAL(args.nth(0), repl_env); } }); - _ref(repl_env, "slurp", new MalFunction() { - public MalVal apply(MalList args) throws MalThrowable { - String fname = ((MalString)args.nth(0)).getValue(); - return new MalString(slurp(fname)); - } - }); + // core.mal: defined using the language itself RE(repl_env, "(def! not (fn* (a) (if a false true)))"); RE(repl_env, "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))"); diff --git a/java/src/main/java/mal/step7_quote.java b/java/src/main/java/mal/step7_quote.java index c46b4f0..2f5a638 100644 --- a/java/src/main/java/mal/step7_quote.java +++ b/java/src/main/java/mal/step7_quote.java @@ -1,10 +1,7 @@ package mal; import java.io.IOException; -import java.io.FileNotFoundException; -import java.util.Scanner; -import java.io.File; import java.util.List; import java.util.Map; import java.util.HashMap; @@ -167,46 +164,23 @@ public class step7_quote { public static MalVal RE(Env env, String str) throws MalThrowable { return EVAL(READ(str), env); } - public static Env _ref(Env env, String name, MalVal mv) { - return env.set(name, mv); - } - public static String slurp(String fname) throws MalThrowable { - try { - return new Scanner(new File(fname)) - .useDelimiter("\\Z").next(); - } catch (FileNotFoundException e) { - throw new MalError(e.getMessage()); - } - } public static void main(String[] args) throws MalThrowable { String prompt = "user> "; final Env repl_env = new Env(null); + + // core.java: defined using Java for (String key : core.ns.keySet()) { - _ref(repl_env, key, core.ns.get(key)); + repl_env.set(key, core.ns.get(key)); } - _ref(repl_env, "read-string", new MalFunction() { - public MalVal apply(MalList args) throws MalThrowable { - try { - return reader.read_str(((MalString)args.nth(0)).getValue()); - } catch (MalContinue c) { - return types.Nil; - } - } - }); - _ref(repl_env, "eval", new MalFunction() { + repl_env.set("eval", new MalFunction() { public MalVal apply(MalList args) throws MalThrowable { return EVAL(args.nth(0), repl_env); } }); - _ref(repl_env, "slurp", new MalFunction() { - public MalVal apply(MalList args) throws MalThrowable { - String fname = ((MalString)args.nth(0)).getValue(); - return new MalString(slurp(fname)); - } - }); + // core.mal: defined using the language itself RE(repl_env, "(def! not (fn* (a) (if a false true)))"); RE(repl_env, "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))"); diff --git a/java/src/main/java/mal/step8_macros.java b/java/src/main/java/mal/step8_macros.java index bb99818..914c7d3 100644 --- a/java/src/main/java/mal/step8_macros.java +++ b/java/src/main/java/mal/step8_macros.java @@ -1,10 +1,7 @@ package mal; import java.io.IOException; -import java.io.FileNotFoundException; -import java.util.Scanner; -import java.io.File; import java.util.List; import java.util.Map; import java.util.HashMap; @@ -205,48 +202,27 @@ public class step8_macros { public static MalVal RE(Env env, String str) throws MalThrowable { return EVAL(READ(str), env); } - public static Env _ref(Env env, String name, MalVal mv) { - return env.set(name, mv); - } - public static String slurp(String fname) throws MalThrowable { - try { - return new Scanner(new File(fname)) - .useDelimiter("\\Z").next(); - } catch (FileNotFoundException e) { - throw new MalError(e.getMessage()); - } - } public static void main(String[] args) throws MalThrowable { String prompt = "user> "; final Env repl_env = new Env(null); + + // core.java: defined using Java for (String key : core.ns.keySet()) { - _ref(repl_env, key, core.ns.get(key)); + repl_env.set(key, core.ns.get(key)); } - _ref(repl_env, "read-string", new MalFunction() { - public MalVal apply(MalList args) throws MalThrowable { - try { - return reader.read_str(((MalString)args.nth(0)).getValue()); - } catch (MalContinue c) { - return types.Nil; - } - } - }); - _ref(repl_env, "eval", new MalFunction() { + repl_env.set("eval", new MalFunction() { public MalVal apply(MalList args) throws MalThrowable { return EVAL(args.nth(0), repl_env); } }); - _ref(repl_env, "slurp", new MalFunction() { - public MalVal apply(MalList args) throws MalThrowable { - String fname = ((MalString)args.nth(0)).getValue(); - return new MalString(slurp(fname)); - } - }); + // core.mal: defined using the language itself RE(repl_env, "(def! not (fn* (a) (if a false true)))"); RE(repl_env, "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))"); + RE(repl_env, "(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))"); + RE(repl_env, "(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))"); Integer fileIdx = 0; if (args.length > 0 && args[0].equals("--raw")) { diff --git a/java/src/main/java/mal/stepA_more.java b/java/src/main/java/mal/stepA_more.java index 76b8501..5ed5667 100644 --- a/java/src/main/java/mal/stepA_more.java +++ b/java/src/main/java/mal/stepA_more.java @@ -1,10 +1,7 @@ package mal; import java.io.IOException; -import java.io.FileNotFoundException; -import java.util.Scanner; -import java.io.File; import java.io.StringWriter; import java.io.PrintWriter; import java.util.List; @@ -231,62 +228,27 @@ public class stepA_more { public static MalVal RE(Env env, String str) throws MalThrowable { return EVAL(READ(str), env); } - public static Env _ref(Env env, String name, MalVal mv) { - return env.set(name, mv); - } - public static String slurp(String fname) throws MalThrowable { - try { - return new Scanner(new File(fname)) - .useDelimiter("\\Z").next(); - } catch (FileNotFoundException e) { - throw new MalError(e.getMessage()); - } - } public static void main(String[] args) throws MalThrowable { String prompt = "user> "; final Env repl_env = new Env(null); + + // core.java: defined using Java for (String key : core.ns.keySet()) { - _ref(repl_env, key, core.ns.get(key)); + repl_env.set(key, core.ns.get(key)); } - _ref(repl_env, "readline", new MalFunction() { - public MalVal apply(MalList args) throws MalThrowable { - String prompt = ((MalString)args.nth(0)).getValue(); - try { - return new MalString(readline.readline(prompt)); - } catch (IOException e) { - throw new MalException(new MalString(e.getMessage())); - } catch (readline.EOFException e) { - throw new MalException(new MalString(e.getMessage())); - } - } - }); - _ref(repl_env, "read-string", new MalFunction() { - public MalVal apply(MalList args) throws MalThrowable { - try { - return reader.read_str(((MalString)args.nth(0)).getValue()); - } catch (MalContinue c) { - return types.Nil; - } - } - }); - _ref(repl_env, "eval", new MalFunction() { + repl_env.set("eval", new MalFunction() { public MalVal apply(MalList args) throws MalThrowable { return EVAL(args.nth(0), repl_env); } }); - _ref(repl_env, "slurp", new MalFunction() { - public MalVal apply(MalList args) throws MalThrowable { - String fname = ((MalString)args.nth(0)).getValue(); - return new MalString(slurp(fname)); - } - }); + // core.mal: defined using the language itself RE(repl_env, "(def! not (fn* (a) (if a false true)))"); + RE(repl_env, "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))"); RE(repl_env, "(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))"); RE(repl_env, "(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))"); - RE(repl_env, "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))"); Integer fileIdx = 0; if (args.length > 0 && args[0].equals("--raw")) { @@ -4,6 +4,8 @@ if (typeof module === 'undefined') { var exports = core; } else { var types = require('./types'), + readline = require('./node_readline'), + reader = require('./reader'), printer = require('./printer'); } @@ -36,6 +38,10 @@ function println() { })); } +function slurp(f) { + return require('fs').readFileSync(f, 'utf-8'); +} + // Hash Map functions function assoc(src_hm) { @@ -144,10 +150,14 @@ var ns = {'type': types._obj_type, 'false?': types._false_Q, 'symbol': types._symbol, 'symbol?': types._symbol_Q, + 'pr-str': pr_str, 'str': str, 'prn': prn, 'println': println, + 'readline': readline.readline, + 'read-string': reader.read_str, + 'slurp': slurp, '<' : function(a,b){return a<b;}, '<=' : function(a,b){return a<=b;}, '>' : function(a,b){return a>b;}, diff --git a/js/step3_env.js b/js/step3_env.js index 86981d2..7dbefc4 100644 --- a/js/step3_env.js +++ b/js/step3_env.js @@ -69,12 +69,11 @@ function PRINT(exp) { // repl var repl_env = new Env(); var rep = function(str) { return PRINT(EVAL(READ(str), repl_env)); }; -_ref = function (k,v) { repl_env.set(k, v); } -_ref('+', function(a,b){return a+b;}); -_ref('-', function(a,b){return a-b;}); -_ref('*', function(a,b){return a*b;}); -_ref('/', function(a,b){return a/b;}); +repl_env.set('+', function(a,b){return a+b;}); +repl_env.set('-', function(a,b){return a-b;}); +repl_env.set('*', function(a,b){return a*b;}); +repl_env.set('/', function(a,b){return a/b;}); if (typeof require === 'undefined') { // Asynchronous browser mode diff --git a/js/step4_if_fn_do.js b/js/step4_if_fn_do.js index a80190e..7d679a9 100644 --- a/js/step4_if_fn_do.js +++ b/js/step4_if_fn_do.js @@ -84,12 +84,12 @@ function PRINT(exp) { // repl var repl_env = new Env(); var rep = function(str) { return PRINT(EVAL(READ(str), repl_env)); }; -_ref = function (k,v) { repl_env.set(k, v); } -// Import core functions +// core.js: defined using javascript for (var n in core.ns) { repl_env.set(n, core.ns[n]); } +repl_env.set('eval', function(ast) { return EVAL(ast, repl_env); }); -// Defined using the language itself +// core.mal: defined using the language itself rep("(def! not (fn* (a) (if a false true)))"); if (typeof require === 'undefined') { diff --git a/js/step5_tco.js b/js/step5_tco.js index dbeaa90..320807c 100644 --- a/js/step5_tco.js +++ b/js/step5_tco.js @@ -93,12 +93,12 @@ function PRINT(exp) { // repl var repl_env = new Env(); var rep = function(str) { return PRINT(EVAL(READ(str), repl_env)); }; -_ref = function (k,v) { repl_env.set(k, v); } -// Import core functions +// core.js: defined using javascript for (var n in core.ns) { repl_env.set(n, core.ns[n]); } +repl_env.set('eval', function(ast) { return EVAL(ast, repl_env); }); -// Defined using the language itself +// core.mal: defined using the language itself rep("(def! not (fn* (a) (if a false true)))"); if (typeof require === 'undefined') { diff --git a/js/step6_file.js b/js/step6_file.js index ee39501..aed1825 100644 --- a/js/step6_file.js +++ b/js/step6_file.js @@ -93,18 +93,12 @@ function PRINT(exp) { // repl var repl_env = new Env(); var rep = function(str) { return PRINT(EVAL(READ(str), repl_env)); }; -_ref = function (k,v) { repl_env.set(k, v); } -// Import core functions +// core.js: defined using javascript for (var n in core.ns) { repl_env.set(n, core.ns[n]); } +repl_env.set('eval', function(ast) { return EVAL(ast, repl_env); }); -_ref('read-string', reader.read_str); -_ref('eval', function(ast) { return EVAL(ast, repl_env); }); -_ref('slurp', function(f) { - return require('fs').readFileSync(f, 'utf-8'); -}); - -// Defined using the language itself +// core.mal: defined using the language itself rep("(def! not (fn* (a) (if a false true)))"); rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))"); diff --git a/js/step7_quote.js b/js/step7_quote.js index 6782647..a7f4535 100644 --- a/js/step7_quote.js +++ b/js/step7_quote.js @@ -117,18 +117,12 @@ function PRINT(exp) { // repl var repl_env = new Env(); var rep = function(str) { return PRINT(EVAL(READ(str), repl_env)); }; -_ref = function (k,v) { repl_env.set(k, v); } -// Import core functions +// core.js: defined using javascript for (var n in core.ns) { repl_env.set(n, core.ns[n]); } +repl_env.set('eval', function(ast) { return EVAL(ast, repl_env); }); -_ref('read-string', reader.read_str); -_ref('eval', function(ast) { return EVAL(ast, repl_env); }); -_ref('slurp', function(f) { - return require('fs').readFileSync(f, 'utf-8'); -}); - -// Defined using the language itself +// core.mal: defined using the language itself rep("(def! not (fn* (a) (if a false true)))"); rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))"); diff --git a/js/step8_macros.js b/js/step8_macros.js index f50bba4..1268a4d 100644 --- a/js/step8_macros.js +++ b/js/step8_macros.js @@ -141,20 +141,16 @@ function PRINT(exp) { // repl var repl_env = new Env(); var rep = function(str) { return PRINT(EVAL(READ(str), repl_env)); }; -_ref = function (k,v) { repl_env.set(k, v); } -// Import core functions +// core.js: defined using javascript for (var n in core.ns) { repl_env.set(n, core.ns[n]); } +repl_env.set('eval', function(ast) { return EVAL(ast, repl_env); }); -_ref('read-string', reader.read_str); -_ref('eval', function(ast) { return EVAL(ast, repl_env); }); -_ref('slurp', function(f) { - return require('fs').readFileSync(f, 'utf-8'); -}); - -// Defined using the language itself +// core.mal: defined using the language itself rep("(def! not (fn* (a) (if a false true)))"); rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))"); +rep("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))"); +rep("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))"); if (typeof process !== 'undefined' && process.argv.length > 2) { for (var i=2; i < process.argv.length; i++) { diff --git a/js/step9_interop.js b/js/step9_interop.js index 4987931..1fd07d4 100644 --- a/js/step9_interop.js +++ b/js/step9_interop.js @@ -147,20 +147,16 @@ function PRINT(exp) { // repl var repl_env = new Env(); var rep = function(str) { return PRINT(EVAL(READ(str), repl_env)); }; -_ref = function (k,v) { repl_env.set(k, v); } -// Import core functions +// core.js: defined using javascript for (var n in core.ns) { repl_env.set(n, core.ns[n]); } +repl_env.set('eval', function(ast) { return EVAL(ast, repl_env); }); -_ref('read-string', reader.read_str); -_ref('eval', function(ast) { return EVAL(ast, repl_env); }); -_ref('slurp', function(f) { - return require('fs').readFileSync(f, 'utf-8'); -}); - -// Defined using the language itself +// core.mal: defined using the language itself rep("(def! not (fn* (a) (if a false true)))"); rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))"); +rep("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))"); +rep("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))"); if (typeof process !== 'undefined' && process.argv.length > 2) { for (var i=2; i < process.argv.length; i++) { diff --git a/js/stepA_more.js b/js/stepA_more.js index a4fe21a..06eb43d 100644 --- a/js/stepA_more.js +++ b/js/stepA_more.js @@ -158,23 +158,16 @@ function PRINT(exp) { // repl var repl_env = new Env(); var rep = function(str) { return PRINT(EVAL(READ(str), repl_env)); }; -_ref = function (k,v) { repl_env.set(k, v); } -// Import core functions +// core.js: defined using javascript for (var n in core.ns) { repl_env.set(n, core.ns[n]); } +repl_env.set('eval', function(ast) { return EVAL(ast, repl_env); }); -_ref('readline', readline.readline) -_ref('read-string', reader.read_str); -_ref('eval', function(ast) { return EVAL(ast, repl_env); }); -_ref('slurp', function(f) { - return require('fs').readFileSync(f, 'utf-8'); -}); - -// Defined using the language itself +// core.mal: defined using the language itself rep("(def! not (fn* (a) (if a false true)))"); +rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))"); rep("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))"); rep("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))"); -rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))"); if (typeof process !== 'undefined' && process.argv.length > 2) { for (var i=2; i < process.argv.length; i++) { diff --git a/make/core.mk b/make/core.mk index fbea5ea..80f51ac 100644 --- a/make/core.mk +++ b/make/core.mk @@ -8,6 +8,8 @@ __mal_core_included := true _TOP_DIR := $(dir $(lastword $(MAKEFILE_LIST))) include $(_TOP_DIR)util.mk include $(_TOP_DIR)types.mk +include $(_TOP_DIR)readline.mk +include $(_TOP_DIR)reader.mk include $(_TOP_DIR)printer.mk @@ -49,12 +51,16 @@ number_divide = $(call _pnumber,$(call int_divide,$($(word 1,$(1))_value),$($(wo # String functions +string? = $(if $(call _string?,$(1)),$(__true),$(__false)) + pr_str = $(call _string,$(call _pr_str_mult,$(1),yes, )) str = $(call _string,$(call _pr_str_mult,$(1),,)) prn = $(info $(call _pr_str_mult,$(1),yes, )) println = $(info $(subst \n,$(NEWLINE),$(call _pr_str_mult,$(1),, ))) -string? = $(if $(call _string?,$(1)),$(__true),$(__false)) +readline= $(foreach res,$(call _string,$(call READLINE,"$(call str_decode,$($(1)_value))")),$(if $(READLINE_EOF),$(__nil),$(res))) +read_str= $(call READ_STR,$(1)) +slurp = $(call _string,$(call _read_file,$(call str_decode,$($(1)_value)))) subs = $(strip \ $(foreach start,$(call gmsl_plus,1,$(call int_decode,$($(word 2,$(1))_value))),\ @@ -205,10 +211,14 @@ core_ns = type obj_type \ symbol? symbol? \ function? function? \ string? string? \ + \ pr-str pr_str \ str str \ prn prn \ println println \ + readline readline \ + read-string read_str \ + slurp slurp \ subs subs \ number? number? \ < number_lt \ diff --git a/make/step3_env.mk b/make/step3_env.mk index 7c505cf..acc038d 100644 --- a/make/step3_env.mk +++ b/make/step3_env.mk @@ -86,11 +86,10 @@ REP = $(call PRINT,$(strip $(call EVAL,$(strip $(call READ,$(1))),$(REPL_ENV)))) REPL = $(info $(call REP,$(call READLINE,"user> ")))$(if $(READLINE_EOF),,$(call REPL)) # Setup the environment -_ref = $(eval REPL_ENV := $(call ENV_SET,$(REPL_ENV),$(1),$(if $(2),$(2),$(1)))) -$(call _ref,+,number_plus) -$(call _ref,-,number_subtract) -$(call _ref,*,number_multiply) -$(call _ref,/,number_divide) +REPL_ENV := $(call ENV_SET,$(REPL_ENV),+,number_plus) +REPL_ENV := $(call ENV_SET,$(REPL_ENV),-,number_subtract) +REPL_ENV := $(call ENV_SET,$(REPL_ENV),*,number_multiply) +REPL_ENV := $(call ENV_SET,$(REPL_ENV),/,number_divide) # Call the read-eval-print loop $(if $(strip $(INTERACTIVE)),$(call REPL)) diff --git a/make/step4_if_fn_do.mk b/make/step4_if_fn_do.mk index f45cfa9..13dcf1c 100644 --- a/make/step4_if_fn_do.mk +++ b/make/step4_if_fn_do.mk @@ -101,14 +101,12 @@ REPL_ENV := $(call ENV) REP = $(call PRINT,$(strip $(call EVAL,$(strip $(call READ,$(1))),$(REPL_ENV)))) REPL = $(info $(call REP,$(call READLINE,"user> ")))$(if $(READLINE_EOF),,$(call REPL)) -# Setup the environment +# core.mk: defined using Make _fref = $(eval REPL_ENV := $(call ENV_SET,$(REPL_ENV),$(1),$(call _function,$$(call $(2),$$1)))) - -# Import core namespace _import_core = $(if $(strip $(1)),$(call _fref,$(word 1,$(1)),$(word 2,$(1)))$(call _import_core,$(wordlist 3,$(words $(1)),$(1))),) $(call _import_core,$(core_ns)) -# Defined in terms of the language itself +# core.mal: defined in terms of the language itself $(call do,$(call REP, (def! not (fn* (a) (if a false true))) )) # Call the read-eval-print loop diff --git a/make/step6_file.mk b/make/step6_file.mk index c791b33..494cbb2 100644 --- a/make/step6_file.mk +++ b/make/step6_file.mk @@ -101,21 +101,13 @@ REPL_ENV := $(call ENV) REP = $(call PRINT,$(strip $(call EVAL,$(strip $(call READ,$(1))),$(REPL_ENV)))) REPL = $(info $(call REP,$(call READLINE,"user> ")))$(if $(READLINE_EOF),,$(call REPL)) -# Setup the environment -_ref = $(eval REPL_ENV := $(call ENV_SET,$(REPL_ENV),$(1),$(if $(2),$(2),$(1)))) +# core.mk: defined using Make _fref = $(eval REPL_ENV := $(call ENV_SET,$(REPL_ENV),$(1),$(call _function,$$(call $(2),$$1)))) - -# Import core namespace _import_core = $(if $(strip $(1)),$(call _fref,$(word 1,$(1)),$(word 2,$(1)))$(call _import_core,$(wordlist 3,$(words $(1)),$(1))),) $(call _import_core,$(core_ns)) +REPL_ENV := $(call ENV_SET,$(REPL_ENV),eval,$(call _function,$$(call EVAL,$$(1),$$(REPL_ENV)))) -$(call _ref,read-string,$(call _function,$$(call READ_STR,$$(1)))) -$(call _ref,eval,$(call _function,$$(call EVAL,$$(1),$$(REPL_ENV)))) - -_slurp = $(call _string,$(call _read_file,$(1))) -$(call _ref,slurp,$(call _function,$$(call _slurp,$$(call str_decode,$$($$(1)_value))))) - -# Defined in terms of the language itself +# core.mal: defined in terms of the language itself $(call do,$(call REP, (def! not (fn* (a) (if a false true))) )) $(call do,$(call REP, (def! load-file (fn* (f) (eval (read-string (str "(do " (slurp f) ")"))))) )) diff --git a/make/step7_quote.mk b/make/step7_quote.mk index 46e5130..af14e85 100644 --- a/make/step7_quote.mk +++ b/make/step7_quote.mk @@ -118,21 +118,13 @@ REPL_ENV := $(call ENV) REP = $(call PRINT,$(strip $(call EVAL,$(strip $(call READ,$(1))),$(REPL_ENV)))) REPL = $(info $(call REP,$(call READLINE,"user> ")))$(if $(READLINE_EOF),,$(call REPL)) -# Setup the environment -_ref = $(eval REPL_ENV := $(call ENV_SET,$(REPL_ENV),$(1),$(if $(2),$(2),$(1)))) +# core.mk: defined using Make _fref = $(eval REPL_ENV := $(call ENV_SET,$(REPL_ENV),$(1),$(call _function,$$(call $(2),$$1)))) - -# Import core namespace _import_core = $(if $(strip $(1)),$(call _fref,$(word 1,$(1)),$(word 2,$(1)))$(call _import_core,$(wordlist 3,$(words $(1)),$(1))),) $(call _import_core,$(core_ns)) +REPL_ENV := $(call ENV_SET,$(REPL_ENV),eval,$(call _function,$$(call EVAL,$$(1),$$(REPL_ENV)))) -$(call _ref,read-string,$(call _function,$$(call READ_STR,$$(1)))) -$(call _ref,eval,$(call _function,$$(call EVAL,$$(1),$$(REPL_ENV)))) - -_slurp = $(call _string,$(call _read_file,$(1))) -$(call _ref,slurp,$(call _function,$$(call _slurp,$$(call str_decode,$$($$(1)_value))))) - -# Defined in terms of the language itself +# core.mal: defined in terms of the language itself $(call do,$(call REP, (def! not (fn* (a) (if a false true))) )) $(call do,$(call REP, (def! load-file (fn* (f) (eval (read-string (str "(do " (slurp f) ")"))))) )) diff --git a/make/step8_macros.mk b/make/step8_macros.mk index fee4bb0..8ed5df8 100644 --- a/make/step8_macros.mk +++ b/make/step8_macros.mk @@ -141,23 +141,17 @@ REPL_ENV := $(call ENV) REP = $(call PRINT,$(strip $(call EVAL,$(strip $(call READ,$(1))),$(REPL_ENV)))) REPL = $(info $(call REP,$(call READLINE,"user> ")))$(if $(READLINE_EOF),,$(call REPL)) -# Setup the environment -_ref = $(eval REPL_ENV := $(call ENV_SET,$(REPL_ENV),$(1),$(if $(2),$(2),$(1)))) +# core.mk: defined using Make _fref = $(eval REPL_ENV := $(call ENV_SET,$(REPL_ENV),$(1),$(call _function,$$(call $(2),$$1)))) - -# Import core namespace _import_core = $(if $(strip $(1)),$(call _fref,$(word 1,$(1)),$(word 2,$(1)))$(call _import_core,$(wordlist 3,$(words $(1)),$(1))),) $(call _import_core,$(core_ns)) +REPL_ENV := $(call ENV_SET,$(REPL_ENV),eval,$(call _function,$$(call EVAL,$$(1),$$(REPL_ENV)))) -$(call _ref,read-string,$(call _function,$$(call READ_STR,$$(1)))) -$(call _ref,eval,$(call _function,$$(call EVAL,$$(1),$$(REPL_ENV)))) - -_slurp = $(call _string,$(call _read_file,$(1))) -$(call _ref,slurp,$(call _function,$$(call _slurp,$$(call str_decode,$$($$(1)_value))))) - -# Defined in terms of the language itself +# core.mal: defined in terms of the language itself $(call do,$(call REP, (def! not (fn* (a) (if a false true))) )) $(call do,$(call REP, (def! load-file (fn* (f) (eval (read-string (str "(do " (slurp f) ")"))))) )) +$(call do,$(call REP, (defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw "odd number of forms to cond")) (cons 'cond (rest (rest xs))))))) )) +$(call do,$(call REP, (defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs)))))))) )) # Load and eval any files specified on the command line $(if $(MAKECMDGOALS),\ diff --git a/make/step9_interop.mk b/make/step9_interop.mk index 4b7eaff..8c9220c 100644 --- a/make/step9_interop.mk +++ b/make/step9_interop.mk @@ -145,23 +145,17 @@ REPL_ENV := $(call ENV) REP = $(call PRINT,$(strip $(call EVAL,$(strip $(call READ,$(1))),$(REPL_ENV)))) REPL = $(info $(call REP,$(call READLINE,"user> ")))$(if $(READLINE_EOF),,$(call REPL)) -# Setup the environment -_ref = $(eval REPL_ENV := $(call ENV_SET,$(REPL_ENV),$(1),$(if $(2),$(2),$(1)))) +# core.mk: defined using Make _fref = $(eval REPL_ENV := $(call ENV_SET,$(REPL_ENV),$(1),$(call _function,$$(call $(2),$$1)))) - -# Import core namespace _import_core = $(if $(strip $(1)),$(call _fref,$(word 1,$(1)),$(word 2,$(1)))$(call _import_core,$(wordlist 3,$(words $(1)),$(1))),) $(call _import_core,$(core_ns)) +REPL_ENV := $(call ENV_SET,$(REPL_ENV),eval,$(call _function,$$(call EVAL,$$(1),$$(REPL_ENV)))) -$(call _ref,read-string,$(call _function,$$(call READ_STR,$$(1)))) -$(call _ref,eval,$(call _function,$$(call EVAL,$$(1),$$(REPL_ENV)))) - -_slurp = $(call _string,$(call _read_file,$(1))) -$(call _ref,slurp,$(call _function,$$(call _slurp,$$(call str_decode,$$($$(1)_value))))) - -# Defined in terms of the language itself +# core.mal: defined in terms of the language itself $(call do,$(call REP, (def! not (fn* (a) (if a false true))) )) $(call do,$(call REP, (def! load-file (fn* (f) (eval (read-string (str "(do " (slurp f) ")"))))) )) +$(call do,$(call REP, (defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw "odd number of forms to cond")) (cons 'cond (rest (rest xs))))))) )) +$(call do,$(call REP, (defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs)))))))) )) # Load and eval any files specified on the command line $(if $(MAKECMDGOALS),\ diff --git a/make/stepA_more.mk b/make/stepA_more.mk index 3cd0d2a..00ae252 100644 --- a/make/stepA_more.mk +++ b/make/stepA_more.mk @@ -160,26 +160,17 @@ REPL_ENV := $(call ENV) REP = $(call PRINT,$(strip $(call EVAL,$(strip $(call READ,$(1))),$(REPL_ENV)))) REPL = $(info $(call REP,$(call READLINE,"user> ")))$(if $(READLINE_EOF),,$(call REPL)) -# Setup the environment -_ref = $(eval REPL_ENV := $(call ENV_SET,$(REPL_ENV),$(1),$(if $(2),$(2),$(1)))) +# core.mk: defined using Make _fref = $(eval REPL_ENV := $(call ENV_SET,$(REPL_ENV),$(1),$(call _function,$$(call $(2),$$1)))) - -# Import core namespace _import_core = $(if $(strip $(1)),$(call _fref,$(word 1,$(1)),$(word 2,$(1)))$(call _import_core,$(wordlist 3,$(words $(1)),$(1))),) $(call _import_core,$(core_ns)) +REPL_ENV := $(call ENV_SET,$(REPL_ENV),eval,$(call _function,$$(call EVAL,$$(1),$$(REPL_ENV)))) -$(call _ref,readline,$(call _function,$$(foreach res,$$(call _string,$$(call READLINE,"$$(call str_decode,$$($$(1)_value))")),$$(if $$(READLINE_EOF),$$(__nil),$$(res))))) -$(call _ref,read-string,$(call _function,$$(call READ_STR,$$(1)))) -$(call _ref,eval,$(call _function,$$(call EVAL,$$(1),$$(REPL_ENV)))) - -_slurp = $(call _string,$(call _read_file,$(1))) -$(call _ref,slurp,$(call _function,$$(call _slurp,$$(call str_decode,$$($$(1)_value))))) - -# Defined in terms of the language itself +# core.mal: defined in terms of the language itself $(call do,$(call REP, (def! not (fn* (a) (if a false true))) )) +$(call do,$(call REP, (def! load-file (fn* (f) (eval (read-string (str "(do " (slurp f) ")"))))) )) $(call do,$(call REP, (defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw "odd number of forms to cond")) (cons 'cond (rest (rest xs))))))) )) $(call do,$(call REP, (defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs)))))))) )) -$(call do,$(call REP, (def! load-file (fn* (f) (eval (read-string (str "(do " (slurp f) ")"))))) )) # Load and eval any files specified on the command line $(if $(MAKECMDGOALS),\ diff --git a/mal/core.mal b/mal/core.mal index b616603..33b6a5e 100644 --- a/mal/core.mal +++ b/mal/core.mal @@ -5,10 +5,14 @@ ["true?" true?] ["false?" false?] ["symbol?" symbol?] + ["pr-str" pr-str] ["str" str] ["prn" prn] ["println" println] + ["readline" readline] + ["read-string" read-string] + ["slurp" slurp] ["<" <] ["<=" <=] [">" >] diff --git a/mal/step3_env.mal b/mal/step3_env.mal index a4f2c57..c812f17 100644 --- a/mal/step3_env.mal +++ b/mal/step3_env.mal @@ -60,11 +60,10 @@ (def! rep (fn* [strng] (PRINT (EVAL (READ strng) repl-env)))) -(def! _ref (fn* [k v] (env-set repl-env k v))) -(_ref "+" +) -(_ref "-" -) -(_ref "*" *) -(_ref "/" /) +(env-set repl-env "+" +) +(env-set repl-env "-" -) +(env-set repl-env "*" *) +(env-set repl-env "/" /) (def! -main (fn* [] (let* [line (readline "mal-user> ")] diff --git a/mal/step4_if_fn_do.mal b/mal/step4_if_fn_do.mal index 7107ac4..aa48112 100644 --- a/mal/step4_if_fn_do.mal +++ b/mal/step4_if_fn_do.mal @@ -57,7 +57,7 @@ (EVAL (nth ast 3) env) nil) (EVAL (nth ast 2) env))) - + (= 'fn* a0) (fn* [& args] (EVAL (nth ast 2) (new-env env (nth ast 1) args))) @@ -77,12 +77,10 @@ (def! rep (fn* [strng] (PRINT (EVAL (READ strng) repl-env)))) -(def! _ref (fn* [k v] (env-set repl-env k v))) - -;; Import core namespace functions -(map (fn* [data] (_ref (nth data 0) (nth data 1))) core_ns) +;; core.mal: defined directly using mal +(map (fn* [data] (env-set repl-env (nth data 0) (nth data 1))) core_ns) -;; Defined using the language itself +;; core.mal: defined using the new language itself (rep "(def! not (fn* [a] (if a false true)))") (def! -main (fn* [] diff --git a/mal/step6_file.mal b/mal/step6_file.mal index fa5991a..905f9fd 100644 --- a/mal/step6_file.mal +++ b/mal/step6_file.mal @@ -77,16 +77,11 @@ (def! rep (fn* [strng] (PRINT (EVAL (READ strng) repl-env)))) -(def! _ref (fn* [k v] (env-set repl-env k v))) - -;; Import core namespace functions -(map (fn* [data] (_ref (nth data 0) (nth data 1))) core_ns) - -;; Defined using the language itself -(_ref 'read-string read-string) -(_ref 'eval (fn* [ast] (EVAL ast repl-env))) -(_ref 'slurp slurp) +;; core.mal: defined directly using mal +(map (fn* [data] (env-set repl-env (nth data 0) (nth data 1))) core_ns) +(env-set repl-env 'eval (fn* [ast] (EVAL ast repl-env))) +;; core.mal: defined using the new language itself (rep "(def! not (fn* [a] (if a false true)))") (rep "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))") diff --git a/mal/step7_quote.mal b/mal/step7_quote.mal index 5195d38..c95db5d 100644 --- a/mal/step7_quote.mal +++ b/mal/step7_quote.mal @@ -105,16 +105,11 @@ (def! rep (fn* [strng] (PRINT (EVAL (READ strng) repl-env)))) -(def! _ref (fn* [k v] (env-set repl-env k v))) - -;; Import core namespace functions -(map (fn* [data] (_ref (nth data 0) (nth data 1))) core_ns) - -;; Defined using the language itself -(_ref 'read-string read-string) -(_ref 'eval (fn* [ast] (EVAL ast repl-env))) -(_ref 'slurp slurp) +;; core.mal: defined directly using mal +(map (fn* [data] (env-set repl-env (nth data 0) (nth data 1))) core_ns) +(env-set repl-env 'eval (fn* [ast] (EVAL ast repl-env))) +;; core.mal: defined using the new language itself (rep "(def! not (fn* [a] (if a false true)))") (rep "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))") diff --git a/mal/step8_macros.mal b/mal/step8_macros.mal index d2b2bd4..feec227 100644 --- a/mal/step8_macros.mal +++ b/mal/step8_macros.mal @@ -137,18 +137,15 @@ (def! rep (fn* [strng] (PRINT (EVAL (READ strng) repl-env)))) -(def! _ref (fn* [k v] (env-set repl-env k v))) - -;; Import core namespace functions -(map (fn* [data] (_ref (nth data 0) (nth data 1))) core_ns) - -;; Defined using the language itself -(_ref 'read-string read-string) -(_ref 'eval (fn* [ast] (EVAL ast repl-env))) -(_ref 'slurp slurp) +;; core.mal: defined directly using mal +(map (fn* [data] (env-set repl-env (nth data 0) (nth data 1))) core_ns) +(env-set repl-env 'eval (fn* [ast] (EVAL ast repl-env))) +;; core.mal: defined using the new language itself (rep "(def! not (fn* [a] (if a false true)))") (rep "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))") +(rep "(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))") +(rep "(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))") (def! -main (fn* [] (let* [line (readline "mal-user> ")] diff --git a/mal/stepA_more.mal b/mal/stepA_more.mal index 61eb029..b56a697 100644 --- a/mal/stepA_more.mal +++ b/mal/stepA_more.mal @@ -148,21 +148,15 @@ (def! rep (fn* [strng] (PRINT (EVAL (READ strng) repl-env)))) -(def! _ref (fn* [k v] (env-set repl-env k v))) - -;; Import core namespace functions -(map (fn* [data] (_ref (nth data 0) (nth data 1))) core_ns) - -;; Defined using the language itself -(_ref 'readline readline) -(_ref 'read-string read-string) -(_ref 'eval (fn* [ast] (EVAL ast repl-env))) -(_ref 'slurp slurp) +;; core.mal: defined directly using mal +(map (fn* [data] (env-set repl-env (nth data 0) (nth data 1))) core_ns) +(env-set repl-env 'eval (fn* [ast] (EVAL ast repl-env))) +;; core.mal: defined using the new language itself (rep "(def! not (fn* [a] (if a false true)))") +(rep "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))") (rep "(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))") (rep "(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))") -(rep "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))") (def! -main (fn* [] (let* [line (readline "mal-user> ")] diff --git a/php/core.php b/php/core.php index 16d34f8..39a22ac 100644 --- a/php/core.php +++ b/php/core.php @@ -1,6 +1,8 @@ <?php require_once 'types.php'; +require_once 'readline.php'; +require_once 'reader.php'; require_once 'printer.php'; // Error/Exception functions @@ -169,11 +171,15 @@ $core_ns = array( 'false?'=> function ($a) { return _false_Q($a); }, 'symbol'=> function () { return call_user_func_array('_symbol', func_get_args()); }, 'symbol?'=> function ($a) { return _symbol_Q($a); }, + 'string?'=> function ($a) { return _string_Q($a); }, 'pr-str'=> function () { return call_user_func_array('pr_str', func_get_args()); }, 'str'=> function () { return call_user_func_array('str', func_get_args()); }, 'prn'=> function () { return call_user_func_array('prn', func_get_args()); }, 'println'=>function () { return call_user_func_array('println', func_get_args()); }, + 'readline'=>function ($a) { return mal_readline($a); }, + 'read-string'=>function ($a) { return read_str($a); }, + 'slurp'=> function ($a) { return file_get_contents($a); }, '<'=> function ($a, $b) { return $a < $b; }, '<='=> function ($a, $b) { return $a <= $b; }, '>'=> function ($a, $b) { return $a > $b; }, diff --git a/php/step2_eval.php b/php/step2_eval.php index 0ef184a..1cec7f7 100644 --- a/php/step2_eval.php +++ b/php/step2_eval.php @@ -55,6 +55,7 @@ function rep($str) { global $repl_env; return MAL_PRINT(MAL_EVAL(READ($str), $repl_env)); } + $repl_env['+'] = function ($a, $b) { return intval($a + $b,10); }; $repl_env['-'] = function ($a, $b) { return intval($a - $b,10); }; $repl_env['*'] = function ($a, $b) { return intval($a * $b,10); }; diff --git a/php/step3_env.php b/php/step3_env.php index 83ced32..3c46b04 100644 --- a/php/step3_env.php +++ b/php/step3_env.php @@ -72,12 +72,11 @@ function rep($str) { global $repl_env; return MAL_PRINT(MAL_EVAL(READ($str), $repl_env)); } -function _ref($k, $v) { global $repl_env; $repl_env->set($k, $v); } -_ref('+', function ($a, $b) { return intval($a + $b,10); }); -_ref('-', function ($a, $b) { return intval($a - $b,10); }); -_ref('*', function ($a, $b) { return intval($a * $b,10); }); -_ref('/', function ($a, $b) { return intval($a / $b,10); }); +$repl_env->set('+', function ($a, $b) { return intval($a + $b,10); }); +$repl_env->set('-', function ($a, $b) { return intval($a - $b,10); }); +$repl_env->set('*', function ($a, $b) { return intval($a * $b,10); }); +$repl_env->set('/', function ($a, $b) { return intval($a / $b,10); }); do { try { diff --git a/php/step4_if_fn_do.php b/php/step4_if_fn_do.php index 25ca7c5..83734b1 100644 --- a/php/step4_if_fn_do.php +++ b/php/step4_if_fn_do.php @@ -90,11 +90,13 @@ function rep($str) { global $repl_env; return MAL_PRINT(MAL_EVAL(READ($str), $repl_env)); } -function _ref($k, $v) { global $repl_env; $repl_env->set($k, $v); } -// Import core functions -foreach ($core_ns as $k=>$v) { _ref($k, $v); } -// Defined using the language itself +// core.php: defined using PHP +foreach ($core_ns as $k=>$v) { + $repl_env->set($k, _function($v)); +} + +// core.mal: defined using the language itself rep("(def! not (fn* (a) (if a false true)))"); do { diff --git a/php/step5_tco.php b/php/step5_tco.php index cd4787e..31e2980 100644 --- a/php/step5_tco.php +++ b/php/step5_tco.php @@ -99,14 +99,13 @@ function rep($str) { global $repl_env; return MAL_PRINT(MAL_EVAL(READ($str), $repl_env)); } -function _ref($k, $v) { - global $repl_env; + +// core.php: defined using PHP +foreach ($core_ns as $k=>$v) { $repl_env->set($k, _function($v)); } -// Import core functions -foreach ($core_ns as $k=>$v) { _ref($k, $v); } -// Defined using the language itself +// core.mal: defined using the language itself rep("(def! not (fn* (a) (if a false true)))"); do { diff --git a/php/step6_file.php b/php/step6_file.php index 95b3982..0a632c0 100644 --- a/php/step6_file.php +++ b/php/step6_file.php @@ -99,22 +99,16 @@ function rep($str) { global $repl_env; return MAL_PRINT(MAL_EVAL(READ($str), $repl_env)); } -function _ref($k, $v) { - global $repl_env; + +// core.php: defined using PHP +foreach ($core_ns as $k=>$v) { $repl_env->set($k, _function($v)); } -// Import core functions -foreach ($core_ns as $k=>$v) { _ref($k, $v); } - -_ref('read-string', 'read_str'); -_ref('eval', function($ast) { +$repl_env->set('eval', _function(function($ast) { global $repl_env; return MAL_EVAL($ast, $repl_env); -}); -_ref('slurp', function($f) { - return file_get_contents($f); -}); +})); -// Defined using the language itself +// core.mal: defined using the language itself rep("(def! not (fn* (a) (if a false true)))"); rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))"); @@ -122,21 +116,22 @@ if (count($argv) > 1) { for ($i=1; $i < count($argv); $i++) { rep('(load-file "' . $argv[$i] . '")'); } -} else { - do { - try { - $line = mal_readline("user> "); - if ($line === NULL) { break; } - if ($line !== "") { - print(rep($line)); - } - } catch (BlankException $e) { - continue; - } catch (Exception $e) { - echo "Error: " . $e->getMessage() . "\n"; - echo $e->getTraceAsString() . "\n"; - } - } while (true); + exit(0); } +do { + try { + $line = mal_readline("user> "); + if ($line === NULL) { break; } + if ($line !== "") { + print(rep($line)); + } + } catch (BlankException $e) { + continue; + } catch (Exception $e) { + echo "Error: " . $e->getMessage() . "\n"; + echo $e->getTraceAsString() . "\n"; + } +} while (true); + ?> diff --git a/php/step7_quote.php b/php/step7_quote.php index 8c407c6..8296c1a 100644 --- a/php/step7_quote.php +++ b/php/step7_quote.php @@ -122,22 +122,16 @@ function rep($str) { global $repl_env; return MAL_PRINT(MAL_EVAL(READ($str), $repl_env)); } -function _ref($k, $v) { - global $repl_env; + +// core.php: defined using PHP +foreach ($core_ns as $k=>$v) { $repl_env->set($k, _function($v)); } -// Import core functions -foreach ($core_ns as $k=>$v) { _ref($k, $v); } - -_ref('read-string', 'read_str'); -_ref('eval', function($ast) { +$repl_env->set('eval', _function(function($ast) { global $repl_env; return MAL_EVAL($ast, $repl_env); -}); -_ref('slurp', function($f) { - return file_get_contents($f); -}); +})); -// Defined using the language itself +// core.mal: defined using the language itself rep("(def! not (fn* (a) (if a false true)))"); rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))"); @@ -145,21 +139,22 @@ if (count($argv) > 1) { for ($i=1; $i < count($argv); $i++) { rep('(load-file "' . $argv[$i] . '")'); } -} else { - do { - try { - $line = mal_readline("user> "); - if ($line === NULL) { break; } - if ($line !== "") { - print(rep($line)); - } - } catch (BlankException $e) { - continue; - } catch (Exception $e) { - echo "Error: " . $e->getMessage() . "\n"; - echo $e->getTraceAsString() . "\n"; - } - } while (true); + exit(0); } +do { + try { + $line = mal_readline("user> "); + if ($line === NULL) { break; } + if ($line !== "") { + print(rep($line)); + } + } catch (BlankException $e) { + continue; + } catch (Exception $e) { + echo "Error: " . $e->getMessage() . "\n"; + echo $e->getTraceAsString() . "\n"; + } +} while (true); + ?> diff --git a/php/step8_macros.php b/php/step8_macros.php index c6c7173..4723ffd 100644 --- a/php/step8_macros.php +++ b/php/step8_macros.php @@ -147,44 +147,41 @@ function rep($str) { global $repl_env; return MAL_PRINT(MAL_EVAL(READ($str), $repl_env)); } -function _ref($k, $v) { - global $repl_env; + +// core.php: defined using PHP +foreach ($core_ns as $k=>$v) { $repl_env->set($k, _function($v)); } -// Import core functions -foreach ($core_ns as $k=>$v) { _ref($k, $v); } - -_ref('read-string', 'read_str'); -_ref('eval', function($ast) { +$repl_env->set('eval', _function(function($ast) { global $repl_env; return MAL_EVAL($ast, $repl_env); -}); -_ref('slurp', function($f) { - return file_get_contents($f); -}); +})); -// Defined using the language itself +// core.mal: defined using the language itself rep("(def! not (fn* (a) (if a false true)))"); rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))"); +rep("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))"); +rep("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))"); if (count($argv) > 1) { for ($i=1; $i < count($argv); $i++) { rep('(load-file "' . $argv[$i] . '")'); } -} else { - do { - try { - $line = mal_readline("user> "); - if ($line === NULL) { break; } - if ($line !== "") { - print(rep($line)); - } - } catch (BlankException $e) { - continue; - } catch (Exception $e) { - echo "Error: " . $e->getMessage() . "\n"; - echo $e->getTraceAsString() . "\n"; - } - } while (true); + exit(0); } +do { + try { + $line = mal_readline("user> "); + if ($line === NULL) { break; } + if ($line !== "") { + print(rep($line)); + } + } catch (BlankException $e) { + continue; + } catch (Exception $e) { + echo "Error: " . $e->getMessage() . "\n"; + echo $e->getTraceAsString() . "\n"; + } +} while (true); + ?> diff --git a/php/step9_interop.php b/php/step9_interop.php index 3debdc4..a46864c 100644 --- a/php/step9_interop.php +++ b/php/step9_interop.php @@ -149,44 +149,41 @@ function rep($str) { global $repl_env; return MAL_PRINT(MAL_EVAL(READ($str), $repl_env)); } -function _ref($k, $v) { - global $repl_env; + +// core.php: defined using PHP +foreach ($core_ns as $k=>$v) { $repl_env->set($k, _function($v)); } -// Import core functions -foreach ($core_ns as $k=>$v) { _ref($k, $v); } - -_ref('read-string', 'read_str'); -_ref('eval', function($ast) { +$repl_env->set('eval', _function(function($ast) { global $repl_env; return MAL_EVAL($ast, $repl_env); -}); -_ref('slurp', function($f) { - return file_get_contents($f); -}); +})); -// Defined using the language itself +// core.mal: defined using the language itself rep("(def! not (fn* (a) (if a false true)))"); rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))"); +rep("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))"); +rep("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))"); if (count($argv) > 1) { for ($i=1; $i < count($argv); $i++) { rep('(load-file "' . $argv[$i] . '")'); } -} else { - do { - try { - $line = mal_readline("user> "); - if ($line === NULL) { break; } - if ($line !== "") { - print(rep($line)); - } - } catch (BlankException $e) { - continue; - } catch (Exception $e) { - echo "Error: " . $e->getMessage() . "\n"; - echo $e->getTraceAsString() . "\n"; - } - } while (true); + exit(0); } +do { + try { + $line = mal_readline("user> "); + if ($line === NULL) { break; } + if ($line !== "") { + print(rep($line)); + } + } catch (BlankException $e) { + continue; + } catch (Exception $e) { + echo "Error: " . $e->getMessage() . "\n"; + echo $e->getTraceAsString() . "\n"; + } +} while (true); + ?> diff --git a/php/stepA_more.php b/php/stepA_more.php index 7478ee8..c599986 100644 --- a/php/stepA_more.php +++ b/php/stepA_more.php @@ -167,47 +167,41 @@ function rep($str) { global $repl_env; return MAL_PRINT(MAL_EVAL(READ($str), $repl_env)); } -function _ref($k, $v) { - global $repl_env; + +// core.php: defined using PHP +foreach ($core_ns as $k=>$v) { $repl_env->set($k, _function($v)); } -// Import core functions -foreach ($core_ns as $k=>$v) { _ref($k, $v); } - -_ref('readline', 'mal_readline'); -_ref('read-string', 'read_str'); -_ref('eval', function($ast) { +$repl_env->set('eval', _function(function($ast) { global $repl_env; return MAL_EVAL($ast, $repl_env); -}); -_ref('slurp', function($f) { - return file_get_contents($f); -}); +})); -// Defined using the language itself +// core.mal: defined using the language itself rep("(def! not (fn* (a) (if a false true)))"); +rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))"); rep("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))"); rep("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))"); -rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))"); if (count($argv) > 1) { for ($i=1; $i < count($argv); $i++) { rep('(load-file "' . $argv[$i] . '")'); } -} else { - do { - try { - $line = mal_readline("user> "); - if ($line === NULL) { break; } - if ($line !== "") { - print(rep($line)); - } - } catch (BlankException $e) { - continue; - } catch (Exception $e) { - echo "Error: " . $e->getMessage() . "\n"; - echo $e->getTraceAsString() . "\n"; - } - } while (true); + exit(0); } +do { + try { + $line = mal_readline("user> "); + if ($line === NULL) { break; } + if ($line !== "") { + print(rep($line)); + } + } catch (BlankException $e) { + continue; + } catch (Exception $e) { + echo "Error: " . $e->getMessage() . "\n"; + echo $e->getTraceAsString() . "\n"; + } +} while (true); + ?> @@ -223,10 +223,14 @@ end } def (true?) { 0 _nth _true? } (false?) { 0 _nth _false? } (symbol?) { 0 _nth _symbol? } + (pr-str) { /data get ( ) true _pr_str_args } (str) { /data get () false _pr_str_args } (prn) { /data get ( ) true _pr_str_args print (\n) print null } (println) { /data get ( ) false _pr_str_args print (\n) print null } + (readline) { 0 _nth _readline not { pop null } if } + (read-string) { 0 _nth read_str } + (slurp) { 0 _nth (r) file dup bytesavailable string readstring pop } (<) { dup 0 _nth exch 1 _nth lt } (<=) { dup 0 _nth exch 1 _nth le } (>) { dup 0 _nth exch 1 _nth gt } diff --git a/ps/step3_env.ps b/ps/step3_env.ps index e94f92c..42eda2c 100644 --- a/ps/step3_env.ps +++ b/ps/step3_env.ps @@ -81,8 +81,8 @@ end } def /repl_env null null null env_new def /REP { READ repl_env EVAL PRINT } def -/_ref { repl_env 3 1 roll env_set pop } def +/_ref { repl_env 3 1 roll env_set pop } def (+) { dup 0 _nth exch 1 _nth add } _ref (-) { dup 0 _nth exch 1 _nth sub } _ref (*) { dup 0 _nth exch 1 _nth mul } _ref diff --git a/ps/step4_if_fn_do.ps b/ps/step4_if_fn_do.ps index 952661a..b01c594 100644 --- a/ps/step4_if_fn_do.ps +++ b/ps/step4_if_fn_do.ps @@ -111,10 +111,12 @@ end } def /RE { READ repl_env EVAL } def /REP { READ repl_env EVAL PRINT } def -/_ref { _function repl_env 3 1 roll env_set pop } def +% core.ps: defined using postscript +/_ref { _function repl_env 3 1 roll env_set pop } def core_ns { _ref } forall +% core.mal: defined using the language itself (\(def! not \(fn* \(a\) \(if a false true\)\)\)) RE pop { % loop diff --git a/ps/step5_tco.ps b/ps/step5_tco.ps index 802f3d3..50bcdbf 100644 --- a/ps/step5_tco.ps +++ b/ps/step5_tco.ps @@ -121,10 +121,12 @@ end } def /RE { READ repl_env EVAL } def /REP { READ repl_env EVAL PRINT } def -/_ref { _function repl_env 3 1 roll env_set pop } def +% core.ps: defined using postscript +/_ref { _function repl_env 3 1 roll env_set pop } def core_ns { _ref } forall +% core.mal: defined using the language itself (\(def! not \(fn* \(a\) \(if a false true\)\)\)) RE pop { % loop diff --git a/ps/step6_file.ps b/ps/step6_file.ps index fc12cc1..faa7101 100644 --- a/ps/step6_file.ps +++ b/ps/step6_file.ps @@ -121,15 +121,13 @@ end } def /RE { READ repl_env EVAL } def /REP { READ repl_env EVAL PRINT } def -/_ref { _function repl_env 3 1 roll env_set pop } def +% core.ps: defined using postscript +/_ref { _function repl_env 3 1 roll env_set pop } def core_ns { _ref } forall - -(read-string) { 0 _nth read_str } _ref (eval) { 0 _nth repl_env EVAL } _ref -/slurp { (r) file dup bytesavailable string readstring pop } def -(slurp) { 0 _nth slurp } _ref +% core.mal: defined using the language itself (\(def! not \(fn* \(a\) \(if a false true\)\)\)) RE pop (\(def! load-file \(fn* \(f\) \(eval \(read-string \(str "\(do " \(slurp f\) "\)"\)\)\)\)\)) RE pop diff --git a/ps/step7_quote.ps b/ps/step7_quote.ps index 6913bf6..f335683 100644 --- a/ps/step7_quote.ps +++ b/ps/step7_quote.ps @@ -153,15 +153,13 @@ end } def /RE { READ repl_env EVAL } def /REP { READ repl_env EVAL PRINT } def -/_ref { _function repl_env 3 1 roll env_set pop } def +% core.ps: defined using postscript +/_ref { _function repl_env 3 1 roll env_set pop } def core_ns { _ref } forall - -(read-string) { 0 _nth read_str } _ref (eval) { 0 _nth repl_env EVAL } _ref -/slurp { (r) file dup bytesavailable string readstring pop } def -(slurp) { 0 _nth slurp } _ref +% core.mal: defined using the language itself (\(def! not \(fn* \(a\) \(if a false true\)\)\)) RE pop (\(def! load-file \(fn* \(f\) \(eval \(read-string \(str "\(do " \(slurp f\) "\)"\)\)\)\)\)) RE pop diff --git a/ps/step8_macros.ps b/ps/step8_macros.ps index 286c1ab..87fafb1 100644 --- a/ps/step8_macros.ps +++ b/ps/step8_macros.ps @@ -195,17 +195,17 @@ end } def /RE { READ repl_env EVAL } def /REP { READ repl_env EVAL PRINT } def -/_ref { _function repl_env 3 1 roll env_set pop } def +% core.ps: defined using postscript +/_ref { _function repl_env 3 1 roll env_set pop } def core_ns { _ref } forall - -(read-string) { 0 _nth read_str } _ref (eval) { 0 _nth repl_env EVAL } _ref -/slurp { (r) file dup bytesavailable string readstring pop } def -(slurp) { 0 _nth slurp } _ref +% core.mal: defined using the language itself (\(def! not \(fn* \(a\) \(if a false true\)\)\)) RE pop (\(def! load-file \(fn* \(f\) \(eval \(read-string \(str "\(do " \(slurp f\) "\)"\)\)\)\)\)) RE pop +(\(defmacro! cond \(fn* \(& xs\) \(if \(> \(count xs\) 0\) \(list 'if \(first xs\) \(if \(> \(count xs\) 1\) \(nth xs 1\) \(throw "odd number of forms to cond"\)\) \(cons 'cond \(rest \(rest xs\)\)\)\)\)\)\)) RE pop +(\(defmacro! or \(fn* \(& xs\) \(if \(empty? xs\) nil \(if \(= 1 \(count xs\)\) \(first xs\) `\(let* \(or_FIXME ~\(first xs\)\) \(if or_FIXME or_FIXME \(or ~@\(rest xs\)\)\)\)\)\)\)\)) RE pop userdict /ARGUMENTS known { %if command line arguments ARGUMENTS length 0 gt { %if more than 0 arguments diff --git a/ps/step9_interop.ps b/ps/step9_interop.ps index f4380da..fbf51a5 100644 --- a/ps/step9_interop.ps +++ b/ps/step9_interop.ps @@ -209,17 +209,17 @@ end } def /RE { READ repl_env EVAL } def /REP { READ repl_env EVAL PRINT } def -/_ref { _function repl_env 3 1 roll env_set pop } def +% core.ps: defined using postscript +/_ref { _function repl_env 3 1 roll env_set pop } def core_ns { _ref } forall - -(read-string) { 0 _nth read_str } _ref (eval) { 0 _nth repl_env EVAL } _ref -/slurp { (r) file dup bytesavailable string readstring pop } def -(slurp) { 0 _nth slurp } _ref +% core.mal: defined using the language itself (\(def! not \(fn* \(a\) \(if a false true\)\)\)) RE pop (\(def! load-file \(fn* \(f\) \(eval \(read-string \(str "\(do " \(slurp f\) "\)"\)\)\)\)\)) RE pop +(\(defmacro! cond \(fn* \(& xs\) \(if \(> \(count xs\) 0\) \(list 'if \(first xs\) \(if \(> \(count xs\) 1\) \(nth xs 1\) \(throw "odd number of forms to cond"\)\) \(cons 'cond \(rest \(rest xs\)\)\)\)\)\)\)) RE pop +(\(defmacro! or \(fn* \(& xs\) \(if \(empty? xs\) nil \(if \(= 1 \(count xs\)\) \(first xs\) `\(let* \(or_FIXME ~\(first xs\)\) \(if or_FIXME or_FIXME \(or ~@\(rest xs\)\)\)\)\)\)\)\)) RE pop userdict /ARGUMENTS known { %if command line arguments ARGUMENTS length 0 gt { %if more than 0 arguments diff --git a/ps/stepA_more.ps b/ps/stepA_more.ps index 91a1fc7..1720b94 100644 --- a/ps/stepA_more.ps +++ b/ps/stepA_more.ps @@ -245,20 +245,17 @@ end } def /RE { READ repl_env EVAL } def /REP { READ repl_env EVAL PRINT } def -/_ref { _function repl_env 3 1 roll env_set pop } def +% core.ps: defined using postscript +/_ref { _function repl_env 3 1 roll env_set pop } def core_ns { _ref } forall - -(readline) { 0 _nth _readline not { pop null } if } _ref -(read-string) { 0 _nth read_str } _ref (eval) { 0 _nth repl_env EVAL } _ref -/slurp { (r) file dup bytesavailable string readstring pop } def -(slurp) { 0 _nth slurp } _ref +% core.mal: defined using the language itself (\(def! not \(fn* \(a\) \(if a false true\)\)\)) RE pop +(\(def! load-file \(fn* \(f\) \(eval \(read-string \(str "\(do " \(slurp f\) "\)"\)\)\)\)\)) RE pop (\(defmacro! cond \(fn* \(& xs\) \(if \(> \(count xs\) 0\) \(list 'if \(first xs\) \(if \(> \(count xs\) 1\) \(nth xs 1\) \(throw "odd number of forms to cond"\)\) \(cons 'cond \(rest \(rest xs\)\)\)\)\)\)\)) RE pop (\(defmacro! or \(fn* \(& xs\) \(if \(empty? xs\) nil \(if \(= 1 \(count xs\)\) \(first xs\) `\(let* \(or_FIXME ~\(first xs\)\) \(if or_FIXME or_FIXME \(or ~@\(rest xs\)\)\)\)\)\)\)\)) RE pop -(\(def! load-file \(fn* \(f\) \(eval \(read-string \(str "\(do " \(slurp f\) "\)"\)\)\)\)\)) RE pop userdict /ARGUMENTS known { %if command line arguments ARGUMENTS length 0 gt { %if more than 0 arguments diff --git a/python/core.py b/python/core.py index 20ac793..54737aa 100644 --- a/python/core.py +++ b/python/core.py @@ -3,6 +3,8 @@ from itertools import chain import mal_types as types from mal_types import List, Vector +import mal_readline +import reader import printer # Errors/Exceptions @@ -112,10 +114,14 @@ ns = { 'false?': types._false_Q, 'symbol': types._symbol, 'symbol?': types._symbol_Q, + 'pr-str': pr_str, 'str': do_str, 'prn': prn, 'println': println, + 'readline': lambda prompt: mal_readline.readline(prompt), + 'read-string': reader.read_str, + 'slurp': lambda file: open(file).read(), '<': lambda a,b: a<b, '<=': lambda a,b: a<=b, '>': lambda a,b: a>b, diff --git a/python/step3_env.py b/python/step3_env.py index f684274..372adc4 100644 --- a/python/step3_env.py +++ b/python/step3_env.py @@ -57,12 +57,11 @@ def PRINT(exp): repl_env = Env() def REP(str): return PRINT(EVAL(READ(str), repl_env)) -def _ref(k,v): repl_env.set(k, v) -_ref('+', lambda a,b: a+b) -_ref('-', lambda a,b: a-b) -_ref('*', lambda a,b: a*b) -_ref('/', lambda a,b: int(a/b)) +repl_env.set('+', lambda a,b: a+b) +repl_env.set('-', lambda a,b: a-b) +repl_env.set('*', lambda a,b: a*b) +repl_env.set('/', lambda a,b: int(a/b)) while True: try: diff --git a/python/step4_if_fn_do.py b/python/step4_if_fn_do.py index d9910f6..a06d10b 100644 --- a/python/step4_if_fn_do.py +++ b/python/step4_if_fn_do.py @@ -72,12 +72,11 @@ def PRINT(exp): repl_env = Env() def REP(str): return PRINT(EVAL(READ(str), repl_env)) -def _ref(k,v): repl_env.set(k, v) -# Import types functions -for name, val in core.ns.items(): _ref(name, val) +# core.py: defined using python +for k, v in core.ns.items(): repl_env.set(k, v) -# Defined using the language itself +# core.mal: defined using the language itself REP("(def! not (fn* (a) (if a false true)))") while True: diff --git a/python/step5_tco.py b/python/step5_tco.py index 633cbd8..7982073 100644 --- a/python/step5_tco.py +++ b/python/step5_tco.py @@ -79,12 +79,11 @@ def PRINT(exp): repl_env = Env() def REP(str): return PRINT(EVAL(READ(str), repl_env)) -def _ref(k,v): repl_env.set(k, v) -# Import types functions -for name, val in core.ns.items(): _ref(name, val) +# core.py: defined using python +for k, v in core.ns.items(): repl_env.set(k, v) -# Defined using the language itself +# core.mal: defined using the language itself REP("(def! not (fn* (a) (if a false true)))") while True: diff --git a/python/step6_file.py b/python/step6_file.py index 1a992cc..ba2d355 100644 --- a/python/step6_file.py +++ b/python/step6_file.py @@ -79,16 +79,12 @@ def PRINT(exp): repl_env = Env() def REP(str): return PRINT(EVAL(READ(str), repl_env)) -def _ref(k,v): repl_env.set(k, v) -# Import types functions -for name, val in core.ns.items(): _ref(name, val) +# core.py: defined using python +for k, v in core.ns.items(): repl_env.set(k, v) +repl_env.set('eval', lambda ast: EVAL(ast, repl_env)) -_ref('read-string', reader.read_str) -_ref('eval', lambda ast: EVAL(ast, repl_env)) -_ref('slurp', lambda file: open(file).read()) - -# Defined using the language itself +# core.mal: defined using the language itself REP("(def! not (fn* (a) (if a false true)))") REP("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))") diff --git a/python/step7_quote.py b/python/step7_quote.py index cae3c99..aefa421 100644 --- a/python/step7_quote.py +++ b/python/step7_quote.py @@ -101,16 +101,12 @@ def PRINT(exp): repl_env = Env() def REP(str): return PRINT(EVAL(READ(str), repl_env)) -def _ref(k,v): repl_env.set(k, v) -# Import types functions -for name, val in core.ns.items(): _ref(name, val) +# core.py: defined using python +for k, v in core.ns.items(): repl_env.set(k, v) +repl_env.set('eval', lambda ast: EVAL(ast, repl_env)) -_ref('read-string', reader.read_str) -_ref('eval', lambda ast: EVAL(ast, repl_env)) -_ref('slurp', lambda file: open(file).read()) - -# Defined using the language itself +# core.mal: defined using the language itself REP("(def! not (fn* (a) (if a false true)))") REP("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))") diff --git a/python/step8_macros.py b/python/step8_macros.py index adb4546..90aedf3 100644 --- a/python/step8_macros.py +++ b/python/step8_macros.py @@ -121,18 +121,16 @@ def PRINT(exp): repl_env = Env() def REP(str): return PRINT(EVAL(READ(str), repl_env)) -def _ref(k,v): repl_env.set(k, v) -# Import types functions -for name, val in core.ns.items(): _ref(name, val) +# core.py: defined using python +for k, v in core.ns.items(): repl_env.set(k, v) +repl_env.set('eval', lambda ast: EVAL(ast, repl_env)) -_ref('read-string', reader.read_str) -_ref('eval', lambda ast: EVAL(ast, repl_env)) -_ref('slurp', lambda file: open(file).read()) - -# Defined using the language itself +# core.mal: defined using the language itself REP("(def! not (fn* (a) (if a false true)))") REP("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))") +REP("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))") +REP("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))") if len(sys.argv) >= 2: REP('(load-file "' + sys.argv[1] + '")') diff --git a/python/step9_interop.py b/python/step9_interop.py index 1d8e55a..eae7837 100644 --- a/python/step9_interop.py +++ b/python/step9_interop.py @@ -130,18 +130,16 @@ def PRINT(exp): repl_env = Env() def REP(str): return PRINT(EVAL(READ(str), repl_env)) -def _ref(k,v): repl_env.set(k, v) -# Import types functions -for name, val in core.ns.items(): _ref(name, val) +# core.py: defined using python +for k, v in core.ns.items(): repl_env.set(k, v) +repl_env.set('eval', lambda ast: EVAL(ast, repl_env)) -_ref('read-string', reader.read_str) -_ref('eval', lambda ast: EVAL(ast, repl_env)) -_ref('slurp', lambda file: open(file).read()) - -# Defined using the language itself +# core.mal: defined using the language itself REP("(def! not (fn* (a) (if a false true)))") REP("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))") +REP("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))") +REP("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))") if len(sys.argv) >= 2: REP('(load-file "' + sys.argv[1] + '")') diff --git a/python/stepA_more.py b/python/stepA_more.py index dccce8e..fa79ec3 100644 --- a/python/stepA_more.py +++ b/python/stepA_more.py @@ -144,21 +144,16 @@ def PRINT(exp): repl_env = Env() def REP(str): return PRINT(EVAL(READ(str), repl_env)) -def _ref(k,v): repl_env.set(k, v) -# Import types functions -for name, val in core.ns.items(): _ref(name, val) +# core.py: defined using python +for k, v in core.ns.items(): repl_env.set(k, v) +repl_env.set('eval', lambda ast: EVAL(ast, repl_env)) -_ref('readline', lambda prompt: mal_readline.readline(prompt)) -_ref('read-string', reader.read_str) -_ref('eval', lambda ast: EVAL(ast, repl_env)) -_ref('slurp', lambda file: open(file).read()) - -# Defined using the language itself +# core.mal: defined using the language itself REP("(def! not (fn* (a) (if a false true)))") +REP("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))") REP("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))") REP("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))") -REP("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))") if len(sys.argv) >= 2: REP('(load-file "' + sys.argv[1] + '")') diff --git a/ruby/core.rb b/ruby/core.rb index 8823112..c7fc8c3 100644 --- a/ruby/core.rb +++ b/ruby/core.rb @@ -1,3 +1,7 @@ +require "readline" +require "reader" +require "printer" + $core_ns = { :"=" => lambda {|a,b| a == b}, :throw => lambda {|a| raise MalException.new(a), "Mal Exception"}, @@ -6,10 +10,14 @@ $core_ns = { :false? => lambda {|a| a == false}, :symbol? => lambda {|a| a.is_a? Symbol}, :symbol? => lambda {|a| a.is_a? Symbol}, + :"pr-str" => lambda {|*a| a.map {|e| _pr_str(e, true)}.join(" ")}, - :"str" => lambda {|*a| a.map {|e| _pr_str(e, false)}.join("")}, - :"prn" => lambda {|*a| puts(a.map {|e| _pr_str(e, true)}.join(" "))}, - :"println" => lambda {|*a| puts(a.map {|e| _pr_str(e, false)}.join(" "))}, + :str => lambda {|*a| a.map {|e| _pr_str(e, false)}.join("")}, + :prn => lambda {|*a| puts(a.map {|e| _pr_str(e, true)}.join(" "))}, + :println => lambda {|*a| puts(a.map {|e| _pr_str(e, false)}.join(" "))}, + :readline => lambda {|a| Readline.readline(a,true)}, + :"read-string" => lambda {|a| read_str(a)}, + :slurp => lambda {|a| File.read(a)}, :< => lambda {|a,b| a < b}, :<= => lambda {|a,b| a <= b}, :> => lambda {|a,b| a > b}, diff --git a/ruby/step3_env.rb b/ruby/step3_env.rb index ee80432..7266424 100644 --- a/ruby/step3_env.rb +++ b/ruby/step3_env.rb @@ -60,12 +60,11 @@ end # repl repl_env = Env.new REP = lambda {|str| PRINT(EVAL(READ(str), repl_env)) } -_ref = lambda {|k,v| repl_env.set(k, v) } -_ref[:+, lambda {|a,b| a + b}] -_ref[:-, lambda {|a,b| a - b}] -_ref[:*, lambda {|a,b| a * b}] -_ref[:/, lambda {|a,b| a / b}] +repl_env.set(:+, lambda {|a,b| a + b}) +repl_env.set(:-, lambda {|a,b| a - b}) +repl_env.set(:*, lambda {|a,b| a * b}) +repl_env.set(:/, lambda {|a,b| a / b}) while line = Readline.readline("user> ", true) begin diff --git a/ruby/step4_if_fn_do.rb b/ruby/step4_if_fn_do.rb index e99ed6c..559f81e 100644 --- a/ruby/step4_if_fn_do.rb +++ b/ruby/step4_if_fn_do.rb @@ -77,12 +77,12 @@ end repl_env = Env.new RE = lambda {|str| EVAL(READ(str), repl_env) } REP = lambda {|str| PRINT(EVAL(READ(str), repl_env)) } -_ref = lambda {|k,v| repl_env.set(k, v) } -# Import core functions -$core_ns.each &_ref +# core.rb: defined using ruby +$core_ns.each do |k,v| repl_env.set(k,v) end +repl_env.set(:eval, lambda {|ast| EVAL(ast, repl_env)}) -# Defined using the language itself +# core.mal: defined using the language itself RE["(def! not (fn* (a) (if a false true)))"] while line = Readline.readline("user> ", true) diff --git a/ruby/step5_tco.rb b/ruby/step5_tco.rb index 0b28a09..c6b5782 100644 --- a/ruby/step5_tco.rb +++ b/ruby/step5_tco.rb @@ -86,12 +86,12 @@ end repl_env = Env.new RE = lambda {|str| EVAL(READ(str), repl_env) } REP = lambda {|str| PRINT(EVAL(READ(str), repl_env)) } -_ref = lambda {|k,v| repl_env.set(k, v) } -# Import core functions -$core_ns.each &_ref +# core.rb: defined using ruby +$core_ns.each do |k,v| repl_env.set(k,v) end +repl_env.set(:eval, lambda {|ast| EVAL(ast, repl_env)}) -# Defined using the language itself +# core.mal: defined using the language itself RE["(def! not (fn* (a) (if a false true)))"] while line = Readline.readline("user> ", true) diff --git a/ruby/step6_file.rb b/ruby/step6_file.rb index 5425eac..6ca0d6d 100644 --- a/ruby/step6_file.rb +++ b/ruby/step6_file.rb @@ -86,16 +86,12 @@ end repl_env = Env.new RE = lambda {|str| EVAL(READ(str), repl_env) } REP = lambda {|str| PRINT(EVAL(READ(str), repl_env)) } -_ref = lambda {|k,v| repl_env.set(k, v) } -# Import core functions -$core_ns.each &_ref +# core.rb: defined using ruby +$core_ns.each do |k,v| repl_env.set(k,v) end +repl_env.set(:eval, lambda {|ast| EVAL(ast, repl_env)}) -_ref[:"read-string", lambda {|str| read_str str}] -_ref[:eval, lambda {|ast| EVAL(ast, repl_env)}] -_ref[:slurp, lambda {|f| File.read(f) }] - -# Defined using the language itself +# core.mal: defined using the language itself RE["(def! not (fn* (a) (if a false true)))"] RE["(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))"] diff --git a/ruby/step7_quote.rb b/ruby/step7_quote.rb index a39b0ce..78ef117 100644 --- a/ruby/step7_quote.rb +++ b/ruby/step7_quote.rb @@ -106,16 +106,12 @@ end repl_env = Env.new RE = lambda {|str| EVAL(READ(str), repl_env) } REP = lambda {|str| PRINT(EVAL(READ(str), repl_env)) } -_ref = lambda {|k,v| repl_env.set(k, v) } -# Import core functions -$core_ns.each &_ref +# core.rb: defined using ruby +$core_ns.each do |k,v| repl_env.set(k,v) end +repl_env.set(:eval, lambda {|ast| EVAL(ast, repl_env)}) -_ref[:"read-string", lambda {|str| read_str str}] -_ref[:eval, lambda {|ast| EVAL(ast, repl_env)}] -_ref[:slurp, lambda {|f| File.read(f) }] - -# Defined using the language itself +# core.mal: defined using the language itself RE["(def! not (fn* (a) (if a false true)))"] RE["(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))"] diff --git a/ruby/step8_macros.rb b/ruby/step8_macros.rb index 36f2a0d..db34159 100644 --- a/ruby/step8_macros.rb +++ b/ruby/step8_macros.rb @@ -131,18 +131,16 @@ end repl_env = Env.new RE = lambda {|str| EVAL(READ(str), repl_env) } REP = lambda {|str| PRINT(EVAL(READ(str), repl_env)) } -_ref = lambda {|k,v| repl_env.set(k, v) } -# Import core functions -$core_ns.each &_ref +# core.rb: defined using ruby +$core_ns.each do |k,v| repl_env.set(k,v) end +repl_env.set(:eval, lambda {|ast| EVAL(ast, repl_env)}) -_ref[:"read-string", lambda {|str| read_str str}] -_ref[:eval, lambda {|ast| EVAL(ast, repl_env)}] -_ref[:slurp, lambda {|f| File.read(f) }] - -# Defined using the language itself +# core.mal: defined using the language itself RE["(def! not (fn* (a) (if a false true)))"] RE["(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))"] +RE["(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))"] +RE["(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))"] if ARGV.size > 0 ARGV.each {|f| diff --git a/ruby/step9_interop.rb b/ruby/step9_interop.rb index 869a86d..ba35146 100644 --- a/ruby/step9_interop.rb +++ b/ruby/step9_interop.rb @@ -133,18 +133,16 @@ end repl_env = Env.new RE = lambda {|str| EVAL(READ(str), repl_env) } REP = lambda {|str| PRINT(EVAL(READ(str), repl_env)) } -_ref = lambda {|k,v| repl_env.set(k, v) } -# Import core functions -$core_ns.each &_ref +# core.rb: defined using ruby +$core_ns.each do |k,v| repl_env.set(k,v) end +repl_env.set(:eval, lambda {|ast| EVAL(ast, repl_env)}) -_ref[:"read-string", lambda {|str| read_str str}] -_ref[:eval, lambda {|ast| EVAL(ast, repl_env)}] -_ref[:slurp, lambda {|f| File.read(f) }] - -# Defined using the language itself +# core.mal: defined using the language itself RE["(def! not (fn* (a) (if a false true)))"] RE["(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))"] +RE["(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))"] +RE["(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))"] if ARGV.size > 0 ARGV.each {|f| diff --git a/ruby/stepA_more.rb b/ruby/stepA_more.rb index 974b3c7..42eac5b 100644 --- a/ruby/stepA_more.rb +++ b/ruby/stepA_more.rb @@ -148,21 +148,16 @@ end repl_env = Env.new RE = lambda {|str| EVAL(READ(str), repl_env) } REP = lambda {|str| PRINT(EVAL(READ(str), repl_env)) } -_ref = lambda {|k,v| repl_env.set(k, v) } -# Import core functions -$core_ns.each &_ref +# core.rb: defined using ruby +$core_ns.each do |k,v| repl_env.set(k,v) end +repl_env.set(:eval, lambda {|ast| EVAL(ast, repl_env)}) -_ref[:"readline", lambda {|prompt| Readline.readline(prompt,true)}] -_ref[:"read-string", lambda {|str| read_str str}] -_ref[:eval, lambda {|ast| EVAL(ast, repl_env)}] -_ref[:slurp, lambda {|f| File.read(f) }] - -# Defined using the language itself +# core.mal: defined using the language itself RE["(def! not (fn* (a) (if a false true)))"] +RE["(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))"] RE["(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))"] RE["(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))"] -RE["(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))"] if ARGV.size > 0 ARGV.each {|f| diff --git a/tests/step8_macros.mal b/tests/step8_macros.mal index 03128ca..023c3d5 100644 --- a/tests/step8_macros.mal +++ b/tests/step8_macros.mal @@ -146,7 +146,7 @@ (cond false 7 false 8 false 9) ;=>nil -;Testing all EVAL of non-default locations +;; Testing all EVAL of non-default locations (let* [x (or nil "yes")] x) ;=>"yes" diff --git a/tests/stepA_more.mal b/tests/stepA_more.mal index f2cc7eb..764c47f 100644 --- a/tests/stepA_more.mal +++ b/tests/stepA_more.mal @@ -313,21 +313,3 @@ (readline "mal-user> ") "hello" ;=>"\"hello\"" - -;; -;; Testing macros cond and or -(cond 1 2 3 4) -;=>2 -(cond false 2 3 4) -;=>4 -(cond false 2 false 4) -;=>nil - -(or) -;=>nil -(or 1) -;=>1 -(or 1 2) -;=>1 -(or nil 2) -;=>2 |
