#!/bin/bash # If this file has already been sourced, just return test $(SPECIALFORMS_LAMBDA_SH+true) && return declare -g SPECIALFORMS_LAMBDA_SH=true source ${BASH_SOURCE%/*}/common.sh source ${BASH_SOURCE%/*}/test.sh source ${BASH_SOURCE%/*}/specialforms.sh source ${BASH_SOURCE%/*}/callable.lambda.sh # # LAMBDA # (lambda formals expression expression ..._) # # A lambda expression evaluates to a procedure. The environment in effect when # the lambda expression is evaluated is remembered as part of the procedure; it # is called the closing environment. When the procedure is later called with # some arguments, the closing environment is extended by binding the variables # in the formal parameter list to fresh locations, and the locations are filled # with the arguments according to rules about to be given. The new environment # created by this process is referred to as the invocation environment. # # Once the invocation environment has been constructed, the expressions in the # body of the lambda expression are evaluated sequentially in it. This means # that the region of the variables bound by the lambda expression is all of the # expressions in the body. The result of evaluating the last expression in the # body is returned as the result of the procedure call. # # # proc evaluator::specialforms::lambda { declare env="$(1)" declare functionName="$(2)" # if declare args="$(3)" # list of , ,..., variable::LinkedList::length $args ; declare -i length="$(RESULT)" if [[ $length < 2 ]] { stderr "usage: (if condition true-branch false-branch)" exit 1 } variable::LinkedList::first $args ; declare formalArgs=$(RESULT) variable::LinkedList::rest $args ; declare expressions=$(RESULT) variable::Lambda::new "$(env) $(formalArgs) $(expressions)" setglobal RESULT = $(RESULT) } # # ====================================================== if test $0 != $BASH_SOURCE { return } # # no args # (lambda () 1) # createTestEnv ; setglobal env = $(RESULT) variable::LinkedList::new ; setglobal command = $(RESULT) appendToList $command Identifier "lambda" variable::LinkedList::new ; variable::LinkedList::append $command $RESULT appendToList $command Integer 1 evaluator::eval $env $command ; setglobal lambda = $(RESULT) variable::toSexp $(lambda) ; \ assert::equals "(lambda () 1)" $(RESULT) "(lambda () 1)" variable::Lambda::getEnv $lambda ; \ assert::equals $env $RESULT " = " # # with args # (lambda (a b c) 1) # createTestEnv ; setglobal env = $(RESULT) variable::LinkedList::new ; setglobal command = $(RESULT) appendToList $command Identifier "lambda" variable::LinkedList::new ; setglobal argList = $(RESULT) appendToList $(argList) Identifier a Identifier b Identifier c variable::LinkedList::append $command $argList appendToList $command Integer 1 evaluator::eval $env $command ; setglobal lambda = $(RESULT) variable::toSexp $(lambda) ; \ assert::equals "(lambda (a b c) 1)" $(RESULT) "(lambda (a b c) 1)" # # multiple commands # (lambda () x y) # createTestEnv ; setglobal env = $(RESULT) variable::LinkedList::new ; setglobal command = $(RESULT) appendToList $command Identifier "lambda" variable::LinkedList::new ; variable::LinkedList::append $command $RESULT appendToList $command Identifier x Identifier y evaluator::eval $env $command ; setglobal lambda = $(RESULT) variable::toSexp $(lambda) ; \ assert::equals "(lambda () x y)" $(RESULT) "(lambda () x y)" # # multiple commands as lists # (lambda () (+ 1 2) (+ 2 3)) # createTestEnv ; setglobal env = $(RESULT) variable::LinkedList::new ; setglobal command = $(RESULT) appendToList $command Identifier "lambda" variable::LinkedList::new ; variable::LinkedList::append $command $RESULT variable::LinkedList::new ; setglobal sc1 = $(RESULT) appendToList $sc1 Identifier + Integer 1 Integer 2 variable::LinkedList::append $command $sc1 variable::LinkedList::new ; setglobal sc2 = $(RESULT) appendToList $sc2 Identifier + Integer 2 Integer 3 variable::LinkedList::append $command $sc2 evaluator::eval $env $command ; setglobal lambda = $(RESULT) variable::toSexp $(lambda) ; \ assert::equals "(lambda () (+ 1 2) (+ 2 3))" $(RESULT) "(lambda () (+ 1 2) (+ 2 3))" assert::report if test $(1+isset) && test $1 == "debug" { variable::printMetadata }