#!/bin/bash # If this file has already been sourced, just return test $(CALLABLE_LAMBDA_SH+true) && return declare -g CALLABLE_LAMBDA_SH=true source ${BASH_SOURCE%/*}/common.sh source ${BASH_SOURCE%/*}/variables.sh source ${BASH_SOURCE%/*}/variables.linkedlist.sh source ${BASH_SOURCE%/*}/specialforms.sh source ${BASH_SOURCE%/*}/evaluator.sh source ${BASH_SOURCE%/*}/callable.sh variable::type::define Lambda Function # # ============================================================ # # Lambda # # the underlying data is an list of [ ] # - the environment that the lambda was defined in # - token of a LinkedList of the argument names # - token to the code # (lambda (x y) # (if (x > y) (* x 2) (* y 3)) # Here, formal args == LinkedList : x y # code == LinkedList : if ... # (lambda (x) x) # Here, formal args == LinkedList : x # code = Identifier x # proc variable::Lambda::new { if [[ ${VARIABLES_DEBUG} == 1 ]] { stderr "variable::Lambda::new $(@)" ; } variable::new Lambda $(@) } # # args # $1 - the lambda token # $2 - the token to a linked list of arguments to pass when calling # proc variable::Lambda::call { if [[ ${VARIABLES_DEBUG} == 1 ]] { stderr "variable::Lambda::call $(@)" ; } declare lambdaToken="$(1)" declare passedArgs="$(2)" variable::type::instanceOfOrExit $(lambdaToken) Lambda variable::LinkedList::length $passedArgs ; declare passedArgCount="$(RESULT)" variable::Lambda::getEnv $lambdaToken ; declare env="$(RESULT)" variable::Lambda::getFormalArgs $lambdaToken ; declare formalArgs="$(RESULT)" variable::Lambda::getBody $lambdaToken ; declare body="$(RESULT)" variable::LinkedList::length $formalArgs ; declare formalArgCount="$(RESULT)" if [[ $passedArgCount -ne $formalArgCount ]] { stderr "lambda: passed arg count ($(passedArgCount)) != format arg count ($(formalArgCount))" exit 1 } environment::pushScope $env ; declare runningEnv="$(RESULT)" declare thisKeyToken declare thisValueToken while ! variable::LinkedList::isEmpty_c $(formalArgs) { variable::LinkedList::first $(formalArgs) ; setglobal thisKeyToken = $(RESULT) variable::LinkedList::first $(passedArgs) ; setglobal thisValueToken = $(RESULT) variable::LinkedList::rest $(formalArgs) ; setglobal formalArgs = $(RESULT) variable::LinkedList::rest $(passedArgs) ; setglobal passedArgs = $(RESULT) environment::setVariable $(runningEnv) $thisKeyToken $thisValueToken } variable::debug $body declare currentSexp currentResult while ! variable::LinkedList::isEmpty_c $(body) { variable::LinkedList::first $(body) ; setglobal currentSexp = $(RESULT) variable::LinkedList::rest $(body) ; setglobal body = $(RESULT) evaluator::eval $runningEnv $currentSexp setglobal currentResult = $(RESULT) } setglobal RESULT = $(currentResult) } proc variable::Lambda::getEnv { if [[ ${VARIABLES_DEBUG} == 1 ]] { stderr "variable::Lambda::getEnv $(@)" ; } declare lambdaToken="$(1)" variable::type::instanceOfOrExit $(lambdaToken) Lambda variable::value $lambdaToken declare -a data=(${RESULT}) setglobal RESULT = $(data[0]) } proc variable::Lambda::getFormalArgs { if [[ ${VARIABLES_DEBUG} == 1 ]] { stderr "variable::Lambda::getFormalArgs $(@)" ; } declare lambdaToken="$(1)" variable::type::instanceOfOrExit $(lambdaToken) Lambda variable::value $lambdaToken declare -a data=(${RESULT}) setglobal RESULT = $(data[1]) } proc variable::Lambda::getBody { if [[ ${VARIABLES_DEBUG} == 1 ]] { stderr "variable::Lambda::getBody $(@)" ; } declare lambdaToken="$(1)" variable::type::instanceOfOrExit $(lambdaToken) Lambda variable::value $lambdaToken declare -a data=(${RESULT}) setglobal RESULT = $(data[2]) } proc variable::Lambda::toSexp { declare token="$(1)" variable::Lambda::getEnv $token ; declare env=$RESULT variable::Lambda::getFormalArgs $token ; declare args=$RESULT variable::Lambda::getBody $token ; declare body=$RESULT variable::toSexp $args ; declare argsString=$RESULT variable::toSexp $body ; declare bodyString=$RESULT declare bodyLen ; sh-expr ' bodyLen = ${#bodyString} - 2 ' setglobal bodyString = $(bodyString:1:bodyLen) setglobal RESULT = ""(lambda $(argsString) $bodyString)"" } # # ====================================================== if test $0 != $BASH_SOURCE { return } source ${BASH_SOURCE%/*}/test.sh # # not using env # createTestEnv ; setglobal lambdaEnv = $(RESULT) variable::LinkedList::new ; setglobal lambdaArgs = $(RESULT) appendToList $lambdaArgs Identifier "x" Identifier "y" variable::LinkedList::new ; setglobal lambdaBody = $(RESULT) variable::LinkedList::new ; setglobal lambdaCode = $(RESULT) appendToList $lambdaCode Identifier '*' Identifier "x" Identifier "y" variable::LinkedList::append $lambdaBody $lambdaCode variable::Lambda::new "$lambdaEnv $lambdaArgs $lambdaBody" ; setglobal lambda = $(RESULT) variable::LinkedList::new ; setglobal callingArgs = $(RESULT) appendToList $callingArgs Integer 5 Integer 3 variable::Lambda::call $lambda $callingArgs variable::debug $(RESULT) ; \ assert::equals "Integer :: 15" $(RESULT) "((lambda (x y) (* x y) 5 3)" # # using env # createTestEnv ; setglobal lambdaEnv = $(RESULT) setInEnv $lambdaEnv "y" Integer 10 variable::LinkedList::new ; setglobal lambdaArgs = $(RESULT) appendToList $lambdaArgs Identifier "x" variable::LinkedList::new ; setglobal lambdaBody = $(RESULT) variable::LinkedList::new ; setglobal lambdaCode = $(RESULT) appendToList $lambdaCode Identifier '*' Identifier "x" Identifier "y" variable::LinkedList::append $lambdaBody $lambdaCode variable::Lambda::new "$lambdaEnv $lambdaArgs $lambdaBody" ; setglobal lambda = $(RESULT) variable::LinkedList::new ; setglobal callingArgs = $(RESULT) appendToList $callingArgs Integer 5 variable::Lambda::call $lambda $callingArgs variable::debug $(RESULT) ; \ assert::equals "Integer :: 50" $(RESULT) "env(y=10) ((lambda (x) (* x y) 5)" #variable::printMetadata #echo "typeof ${vCode}=$(variable::type_p $vCode)" assert::report if test $(1+isset) && test $1 == "debug" { variable::printMetadata }