#!/bin/bash # If this file has already been sourced, just return test $(EVALUATOR_SH+true)TODO && return declare -g EVALUATOR_SH = 'true' source ${BASH_SOURCE%/*}/common.sh source ${BASH_SOURCE%/*}/variables.sh source ${BASH_SOURCE%/*}/callable.sh source ${BASH_SOURCE%/*}/specialforms.sh source ${BASH_SOURCE%/*}/specialforms.lambda.sh source ${BASH_SOURCE%/*}/environment.sh sourceTODO ${BASH_SOURCE%/*}/evaluator.functions.builtin.sh declare -g EVALUATOR_DEBUG = '0' # We declare this so that we have a variable we can use over an over, # rather than creating a new variable each time we need a result # declare -g EVALUATOR_VARIABLE="EVAL_RESULT" # variable::new -name "${EVALUATOR_VARIABLE}" Nil "" # # eval # proc evaluator::eval { if [[ ${EVALUATOR_DEBUG} == 1 ]] { stderr "evaluator::eval $(@)" ; }TODO declare envToken = $(1)TODO declare exprToken = $(2) if variable::type::instanceOf $(exprToken) Atom { variable::clone $(exprToken) setglobal RESULT = $(RESULT) return } elif variable::type::instanceOf $(exprToken) Callable { variable::clone $(exprToken) setglobal RESULT = $(RESULT) return } elif variable::type::instanceOf $(exprToken) Identifier { environment::getValue $(envToken) $(exprToken) setglobal RESULT = $(RESULT) return } elif variable::type::instanceOf $(exprToken) LinkedList { evaluator::eval_list $(@) setglobal RESULT = $(RESULT) return } else { stderr "evaluator::eval / Unhandled type for token [$exprToken]" variable::printMetadata exit 1 } stderr "should never get here" exit 1 } proc evaluator::evalFromLinkedList { if [[ ${EVALUATOR_DEBUG} == 1 ]] { stderr "evaluator::eval $(@)" ; }TODO declare envToken = $(1)TODO declare expressions = $(2)TODO ;# A LinkedList of expressions # evaluate expressions declare currentSexp = '', currentResult = '' while ! variable::LinkedList::isEmpty_c $(expressions) { variable::LinkedList::first $(expressions) ; setglobal currentSexp = $(RESULT) variable::LinkedList::rest $(expressions) ; setglobal expressions = $(RESULT) evaluator::eval $envToken $currentSexp setglobal currentResult = $(RESULT) } setglobal RESULT = $(currentResult) return 0 } proc evaluator::eval_list { if [[ ${EVALUATOR_DEBUG} == 1 ]] { stderr "evaluator::eval_list $(@)" ; }TODO declare envToken = $(1)TODO declare listToken = $(2) if ! variable::type::instanceOf $listToken LinkedList { stderr "evaluator::eval_list / must be a list" exit 1 } variable::LinkedList::length $(listToken) if [[ "${RESULT}" -eq 0 ]] { variable::new Nil "" setglobal RESULT = $(RESULT) return } variable::LinkedList::first $(listToken) evaluator::eval $(envToken) $(RESULT)TODO ; declare headItem = $(RESULT) variable::type $(headItem)TODO ; declare headType = $(RESULT) variable::value $(headItem)TODO ; declare headValue = $(RESULT) # variable::toSexp $listToken # variable::debug $headItem variable::LinkedList::rest $listTokenTODO ; declare rest = $(RESULT) match $(headType) { with BuiltinFunction evaluator::call_builtinFunction $(envToken) $(headItem) $(rest) setglobal RESULT = $(RESULT) with BuiltinMacro # Lookup the identifier in the environment and return it's value evaluator::call_builtinMacro $(envToken) $(headItem) $(rest) setglobal RESULT = $(RESULT) with SpecialForm # Lookup the identifier in the environment and return it's value evaluator::call_specialForm $(envToken) $(headItem) $(rest) setglobal RESULT = $(RESULT) with Lambda evaluator::call_lambda $(envToken) $(headItem) $(rest) setglobal RESULT = $(RESULT) with Macro stderr "evaluator::eval_list / evaluator::eval_list not implemented yet" exit 1 with * stderr "evaluator::eval_list / type [$(headType)] not valid" exit 1 } } proc evaluator::call_builtinFunction { if [[ ${EVALUATOR_DEBUG} == 1 ]] { stderr "evaluator::call_builtinFunction($(#@)) $(@)" ; }TODO declare env = $(1)TODO declare functionToken = $(2)TODO declare argsToken = $(3) variable::value $(functionToken)TODO ; declare functionName = $(RESULT) if ! functionExists $functionName { stderr "The builtin function [$(functionName)] does not exist" exit 1 } eval $(functionName) $(env) $(functionName) $(argsToken) setglobal RESULT = $RESULT } proc evaluator::call_lambda { if [[ ${EVALUATOR_DEBUG} == 1 ]] { stderr "evaluator::call_lambda $(@)" ; }TODO declare env = $(1)TODO declare lambdaToken = $(2)TODO declare argsToken = $(3) variable::LinkedList::newTODO ; declare passedArgs = $(RESULT) while ! variable::LinkedList::isEmpty_c $(argsToken) { variable::LinkedList::first $(argsToken) evaluator::eval $(env) $(RESULT) variable::LinkedList::append $passedArgs $(RESULT) variable::LinkedList::rest $(argsToken); setglobal argsToken = $(RESULT) } variable::Lambda::call $lambdaToken $passedArgs setglobal RESULT = $(RESULT) } proc evaluator::call_builtinMacro { if [[ ${EVALUATOR_DEBUG} == 1 ]] { stderr "evaluator::call_builtinMacro $(@)" ; } variable::set $(EVALUATOR_VARIABLE) nil nilTODO declare env = $(1)TODO declare identifier = $(2)TODO declare -a values = '('${3}) match $(identifier) { with "if" if [[ ${EVALUATOR_DEBUG} == 1 ]] { stderr "evaluator::call_identifier in 'if'" ; } stderr "[if] not implemented yet" with * stderr "evaluator::call_identifier / Not implemented [$(identifier)]" } } proc evaluator::call_specialForm { if [[ ${EVALUATOR_DEBUG} == 1 ]] { stderr "evaluator::call_specialForm($(#@)) $(@)" ; }TODO declare env = $(1)TODO declare functionToken = $(2)TODO declare argsToken = $(3) variable::value $(functionToken)TODO ; declare functionName = $(RESULT) if ! functionExists $functionName { stderr "The builtin function [$(functionName)] does not exist" exit 1 } eval $(functionName) $(env) $(functionName) $(argsToken) setglobal RESULT = $(RESULT) } proc evaluator::setup_builtinTODO { declare env = $(1)TODO declare type = $(2)TODO declare identifier = $(3)TODO declare functionName = $(4) variable::new $(type) $(functionName)TODO declare t1 = $(RESULT) variable::new String $(identifier)TODO declare t2 = $(RESULT) environment::setVariable $(env) $(t2) $(t1) } proc evaluator::setup_builtinsTODO { declare env = $(1) evaluator::setup_builtin $(env) BuiltinFunction "+" "evaluator::functions::builtin::add" evaluator::setup_builtin $(env) BuiltinFunction "-" "evaluator::functions::builtin::subtract" evaluator::setup_builtin $(env) BuiltinFunction "*" "evaluator::functions::builtin::multiply" evaluator::setup_builtin $(env) BuiltinFunction "/" "evaluator::functions::builtin::divide" evaluator::setup_builtin $(env) BuiltinFunction "=" "evaluator::functions::builtin::equals" evaluator::setup_builtin $(env) BuiltinFunction "<" "evaluator::functions::builtin::lessthan" evaluator::setup_builtin $(env) BuiltinFunction ">" "evaluator::functions::builtin::greaterthan" evaluator::setup_builtin $(env) BuiltinFunction "<=" "evaluator::functions::builtin::lessthanorequal" evaluator::setup_builtin $(env) BuiltinFunction ">=" "evaluator::functions::builtin::greaterthanorequal" evaluator::setup_builtin $(env) SpecialForm "if" "evaluator::specialforms::if" evaluator::setup_builtin $(env) SpecialForm "lambda" "evaluator::specialforms::lambda" evaluator::setup_builtin $(env) SpecialForm "let" "evaluator::specialforms::let" evaluator::setup_builtin $(env) SpecialForm 'let*' "evaluator::specialforms::letstar" environment::pushScope $(env) } # ====================================================== if test $0 != $BASH_SOURCE { return } proc createTestEnv { environment::new evaluator::setup_builtins $(RESULT) } proc setInEnvTODO { declare env = $(1)TODO declare name = $(2)TODO declare type = $(3)TODO declare value = $(4) variable::new Identifier $(name)TODO ; declare nameToken = $(RESULT) variable::new $(type) $(value)TODO ; declare valueToken = $(RESULT) environment::setVariable $(env) $(nameToken) $(valueToken) variable::new Identifier $(name) } proc appendToListTODO { declare listToken = $(1)TODO declare -a items = '('TODO "${@:2}") declare -i size = ''TODO declare -i max_index = ''TODO declare currentType = ''TODO declare currentValue = '' sh-expr ' size=${#items[@]}, max_index=size-1 ' if sh-expr 'size % 2 != 0' { stderr "appendToList: number of items to add to list not even" exit 1 }TODO for ((i=0; i<=max_index; i=i+2)); do currentType="${items[${i}]}" currentValue="${items[((i+1))]}" variable::new "${currentType}" "${currentValue}" variable::LinkedList::append "${listToken}" "${RESULT}" done } declare env = '' # Atom, return the value createTestEnv ; setglobal env = $(RESULT) variable::new Boolean true ; setglobal valueToken = $(RESULT) evaluator::eval $(env) $valueToken variable::debug $(RESULT) ; \ assert::equals "Boolean :: true" $(RESULT) "atom/boolean" # # Identifier/Variable # createTestEnv ; setglobal env = $(RESULT) setInEnv $(env) v Integer 4 ; setglobal token = $(RESULT) evaluator::eval $(env) $(token) variable::debug $(RESULT) ; \ assert::equals "Integer :: 4" $(RESULT) "identifier evaluation" # + createTestEnv ; setglobal env = $(RESULT) variable::LinkedList::new ; setglobal vCode = $(RESULT) appendToList $vCode Identifier '+' Integer 5 Integer 2 evaluator::eval $(env) $vCode variable::debug $(RESULT) ; \ assert::equals "Integer :: 7" $(RESULT) "(+ 5 2)" # - createTestEnv ; setglobal env = $(RESULT) variable::LinkedList::new ; setglobal vCode = $(RESULT) appendToList $vCode Identifier '-' Integer 5 Integer 2 evaluator::eval $(env) $vCode variable::debug $(RESULT) ; \ assert::equals "Integer :: 3" $(RESULT) "(- 5 2)" # * createTestEnv ; setglobal env = $(RESULT) variable::LinkedList::new ; setglobal vCode = $(RESULT) appendToList $vCode Identifier '*' Integer 5 Integer 2 evaluator::eval $(env) $vCode variable::debug $(RESULT) ; \ assert::equals "Integer :: 10" $(RESULT) "(* 5 2)" # / createTestEnv ; setglobal env = $(RESULT) variable::LinkedList::new ; setglobal vCode = $(RESULT) appendToList $vCode Identifier '/' Integer 6 Integer 2 evaluator::eval $(env) $vCode variable::debug $(RESULT) ; \ assert::equals "Integer :: 3" $(RESULT) "(/ 6 2)" # = / true createTestEnv ; setglobal env = $(RESULT) variable::LinkedList::new ; setglobal vCode = $(RESULT) appendToList $vCode Identifier '=' Integer 2 Integer 2 evaluator::eval $(env) $vCode variable::debug $(RESULT) ; \ assert::equals "Boolean :: true" $(RESULT) "(= 2 2)" # = / false createTestEnv ; setglobal env = $(RESULT) variable::LinkedList::new ; setglobal vCode = $(RESULT) appendToList $vCode Identifier '=' Integer 2 Integer 3 evaluator::eval $(env) $vCode variable::debug $(RESULT) ; \ assert::equals "Boolean :: false" $(RESULT) "(= 2 3)" # # variable as argument # createTestEnv ; setglobal env = $(RESULT) setInEnv $(env) u Integer 4 ; setglobal token = $(RESULT) setInEnv $(env) v Integer 4 ; setglobal token = $(RESULT) setInEnv $(env) w Integer 2 ; setglobal token = $(RESULT) # variable as argument / + / first variable::new LinkedList ; setglobal vCode = $(RESULT) appendToList $vCode Identifier '+' Identifier 'v' Integer 2 evaluator::eval $(env) $(vCode) variable::debug $(RESULT) ; \ assert::equals "Integer :: 6" $(RESULT) "(+ 2)" # variable as argument / + / second variable::new LinkedList ; setglobal vCode = $(RESULT) appendToList $vCode Identifier '+' Integer 2 Identifier 'v' evaluator::eval $(env) $(vCode) variable::debug $(RESULT) ; \ assert::equals "Integer :: 6" $(RESULT) "(+ 2 )" # variable as argument / - / first variable::new LinkedList ; setglobal vCode = $(RESULT) appendToList $vCode Identifier '-' Identifier 'v' Integer 2 evaluator::eval $(env) $(vCode) variable::debug $(RESULT) ; \ assert::equals "Integer :: 2" $(RESULT) "(- 2)" # variable as argument / - / second variable::new LinkedList ; setglobal vCode = $(RESULT) appendToList $vCode Identifier '-' Integer 2 Identifier 'v' evaluator::eval $(env) $(vCode) variable::debug $(RESULT) ; \ assert::equals "Integer :: -2" $(RESULT) "(- 2 )" # variable as argument / * / first variable::new LinkedList ; setglobal vCode = $(RESULT) appendToList $vCode Identifier '*' Identifier 'v' Integer 2 evaluator::eval $(env) $(vCode) variable::debug $(RESULT) ; \ assert::equals "Integer :: 8" $(RESULT) "(* 2)" # variable as argument / * / second variable::new LinkedList ; setglobal vCode = $(RESULT) appendToList $vCode Identifier '*' Integer 2 Identifier 'v' evaluator::eval $(env) $(vCode) variable::debug $(RESULT) ; \ assert::equals "Integer :: 8" $(RESULT) "(* 2 )" # variable as argument / / / first variable::new LinkedList ; setglobal vCode = $(RESULT) appendToList $vCode Identifier '/' Identifier 'v' Integer 2 evaluator::eval $(env) $(vCode) variable::debug $(RESULT) ; \ assert::equals "Integer :: 2" $(RESULT) "(/ 2)" # variable as argument / + / second variable::new LinkedList ; setglobal vCode = $(RESULT) appendToList $vCode Identifier '/' Integer 12 Identifier 'v' evaluator::eval $(env) $(vCode) variable::debug $(RESULT) ; \ assert::equals "Integer :: 3" $(RESULT) "(/ 12 )" # variable as argument / = / first variable::new LinkedList ; setglobal vCode = $(RESULT) appendToList $vCode Identifier '=' Identifier 'v' Integer 2 evaluator::eval $(env) $(vCode) variable::debug $(RESULT) ; \ assert::equals "Boolean :: false" $(RESULT) "(= 2)" # variable as argument / + / second variable::new LinkedList ; setglobal vCode = $(RESULT) appendToList $vCode Identifier '=' Integer 4 Identifier 'v' evaluator::eval $(env) $(vCode) variable::debug $(RESULT) ; \ assert::equals "Boolean :: true" $(RESULT) "(= 4 )" # # Sub expressions # createTestEnv ; setglobal env = $(RESULT) setInEnv $(env) u Integer 4 ; setglobal token = $(RESULT) setInEnv $(env) v Integer 4 ; setglobal token = $(RESULT) setInEnv $(env) w Integer 2 ; setglobal token = $(RESULT) variable::new LinkedList ; setglobal slistOne = $(RESULT) appendToList $slistOne Identifier + Integer 4 Identifier v variable::new LinkedList ; setglobal slistTwo = $(RESULT) appendToList $slistTwo Identifier - Integer 4 Identifier w variable::new LinkedList ; setglobal vCode = $(RESULT) appendToList $vCode Identifier '*' variable::LinkedList::append $vCode $slistTwo variable::LinkedList::append $vCode $slistOne evaluator::eval $(env) $(vCode) variable::debug $(RESULT) ; \ assert::equals "Integer :: 16" $(RESULT) "(* (+ 4 ) (- 4 ))" # # lambda # createTestEnv ; setglobal lambdaEnv = $(RESULT) setInEnv $lambdaEnv "y" Integer 10 variable::LinkedList::new ; setglobal lambdaArgs = $(RESULT) appendToList $lambdaArgs Identifier "x" variable::LinkedList::new ; setglobal lambdaExpression = $(RESULT) appendToList $lambdaExpression Identifier '*' Identifier "x" Identifier "y" variable::LinkedList::new ; setglobal lambdaCode = $(RESULT) variable::LinkedList::append $lambdaCode $lambdaExpression variable::Lambda::new "$lambdaEnv $lambdaArgs $lambdaCode" ; setglobal lambda = $(RESULT) createTestEnv ; setglobal env = $(RESULT) setInEnv $(env) "a" Integer 5 variable::new LinkedList ; setglobal vCode = $(RESULT) variable::LinkedList::append $vCode $lambda appendToList $vCode Identifier "a" evaluator::eval $(env) $(vCode) variable::debug $(RESULT) ; \ assert::equals "Integer :: 50" $(RESULT) "env(a=5) ((lambda[env(y=10)] (x) (* y x)) a)" #variable::printMetadata #echo "typeof ${vCode}=$(variable::type_p $vCode)" assert::report if test $(1+isset) && test $1 == "debug" { variable::printMetadata }