#!/bin/bash # If this file has already been sourced, just return test $(CALLABLE_LAMBDA_SH+true)TODO && 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 $(@)" ; }TODO declare lambdaToken = $(1)TODO declare passedArgs = $(2) variable::type::instanceOfOrExit $(lambdaToken) Lambda variable::LinkedList::length $passedArgsTODO ; declare passedArgCount = $(RESULT) variable::Lambda::getEnv $lambdaTokenTODO ; declare env = $(RESULT) variable::Lambda::getFormalArgs $lambdaTokenTODO ; declare formalArgs = $(RESULT) variable::Lambda::getBody $lambdaTokenTODO ; declare body = $(RESULT) variable::LinkedList::length $formalArgsTODO ; declare formalArgCount = $(RESULT) if [[ $passedArgCount -ne $formalArgCount ]] { stderr "lambda: passed arg count ($(passedArgCount)) != format arg count ($(formalArgCount))" exit 1 } environment::pushScope $envTODO ; declare runningEnv = $(RESULT)TODO declare thisKeyToken = ''TODO 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 $bodyTODO 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 $(@)" ; }TODO declare lambdaToken = $(1) variable::type::instanceOfOrExit $(lambdaToken) Lambda variable::value $lambdaTokenTODO declare -a data = '('${RESULT}) setglobal RESULT = $(data[0]) } proc variable::Lambda::getFormalArgs { if [[ ${VARIABLES_DEBUG} == 1 ]] { stderr "variable::Lambda::getFormalArgs $(@)" ; }TODO declare lambdaToken = $(1) variable::type::instanceOfOrExit $(lambdaToken) Lambda variable::value $lambdaTokenTODO declare -a data = '('${RESULT}) setglobal RESULT = $(data[1]) } proc variable::Lambda::getBody { if [[ ${VARIABLES_DEBUG} == 1 ]] { stderr "variable::Lambda::getBody $(@)" ; }TODO declare lambdaToken = $(1) variable::type::instanceOfOrExit $(lambdaToken) Lambda variable::value $lambdaTokenTODO declare -a data = '('${RESULT}) setglobal RESULT = $(data[2]) } proc variable::Lambda::toSexpTODO { declare token = $(1) variable::Lambda::getEnv $tokenTODO ; declare env = $RESULT variable::Lambda::getFormalArgs $tokenTODO ; declare args = $RESULT variable::Lambda::getBody $tokenTODO ; declare body = $RESULT variable::toSexp $argsTODO ; declare argsString = $RESULT variable::toSexp $bodyTODO ; declare bodyString = $RESULTTODO 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 }