#!/bin/bash # If this file has already been sourced, just return test $(SPECIALFORMS_LET_SH+true) && return declare -g SPECIALFORMS_LET_SH=true source ${BASH_SOURCE%/*}/common.sh source ${BASH_SOURCE%/*}/test.sh source ${BASH_SOURCE%/*}/specialforms.sh # # LET # # The inits are evaluated in the current environment (in some unspecified order), # the variables are bound to fresh locations holding the results, the expressions # are evaluated sequentially in the extended environment, and the value of the # last expression is returned. Each binding of a variable has the expressions as # its region. # # (let ((x 2) (y 3)) # (* x y)) # proc evaluator::specialforms::let { declare env="$(1)" declare functionName="$(2)" # let declare args="$(3)" # list of , ,..., variable::LinkedList::length $args ; declare -i length="$(RESULT)" if [[ $length < 2 ]] { stderr "usage: (let .. )" exit 1 } variable::LinkedList::first $args ; declare bindings=$(RESULT) variable::LinkedList::rest $args ; declare expressions=$(RESULT) # set values environment::pushScope $env ; declare runningEnv="$(RESULT)" declare thisBinding thisKey thisValue thisResult while ! variable::LinkedList::isEmpty_c $(bindings) { variable::LinkedList::first $(bindings) ; setglobal thisBinding = $(RESULT) variable::LinkedList::rest $(bindings) ; setglobal bindings = $(RESULT) variable::LinkedList::length $(thisBinding) if [[ "${RESULT}" != 2 ]] { stderr "let binding must have exactly 2 elements" } variable::LinkedList::index $(thisBinding) 0 ; setglobal thisKey = $(RESULT) variable::LinkedList::index $(thisBinding) 1 ; setglobal thisValue = $(RESULT) evaluator::eval $env $thisValue environment::setVariable $(runningEnv) $thisKey $RESULT } # 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 $runningEnv $currentSexp setglobal currentResult = $(RESULT) } setglobal RESULT = $(currentResult) } # # ====================================================== if test $0 != $BASH_SOURCE { return } assert::report if test $(1+isset) && test $1 == "debug" { variable::printMetadata }