diff --git a/xml/chapter1/section2/subsection1.xml b/xml/chapter1/section2/subsection1.xml
index a48335d97..3b0c60503 100644
--- a/xml/chapter1/section2/subsection1.xml
+++ b/xml/chapter1/section2/subsection1.xml
@@ -165,7 +165,6 @@ function factorial(n) {
We avoided doing this here so as to minimize the number of things to
think about at
once.
-
factorial_iterative_definitionfactorial_example
diff --git a/xml/chapter1/section3/subsection2.xml b/xml/chapter1/section3/subsection2.xml
index 38126e809..a4e3e15c8 100644
--- a/xml/chapter1/section3/subsection2.xml
+++ b/xml/chapter1/section3/subsection2.xml
@@ -749,10 +749,10 @@ function f(x, y) {
Names that are declared with const
- directly inside of function declarations have the surrounding function as
+ inside of a block have the body of the immediately surrounding block as
their scope.
- Note that a name declared in a function using
+ Note that a name declared in a block using
const cannot be used before the
declaration fully is evaluated, not even in the right-hand expression
of the declaration itself, and regardless whether the same name is
diff --git a/xml/chapter1/section3/subsection3.xml b/xml/chapter1/section3/subsection3.xml
index f9a9e567c..15b301ebe 100644
--- a/xml/chapter1/section3/subsection3.xml
+++ b/xml/chapter1/section3/subsection3.xml
@@ -85,7 +85,7 @@
procedure that implements this strategy:function that implements this strategy:Note that we
slightly extend the syntax of conditional statements described in
- section by allowing another conditional
+ section by admitting another conditional
statement in place of the block following
else.
diff --git a/xml/chapter3/section2/subsection1.xml b/xml/chapter3/section2/subsection1.xml
index ff2cdfc24..b79180de4 100644
--- a/xml/chapter3/section2/subsection1.xml
+++ b/xml/chapter3/section2/subsection1.xml
@@ -213,8 +213,8 @@ const square = x => x * x;
function and
let
create declarations by adding bindings to frames.
- For declarations at the toplevel of the program, outside of any block
- or function body, we introduce a program environment,
+ For declarations at the toplevel of the program, outside of any block,
+ we introduce a program environment,
consisting of a single framethe program
framedirectly inside the global environment. To reduce
clutter, after this figure, we will not display the global environment
@@ -479,10 +479,7 @@ const square = x => x * x;
simple function
application can be summarized by two
- rules:
- In section, we shall generalize
- the first rule to be able to handle functions whose bodies declare local
- names.
+ rules:
A
diff --git a/xml/chapter3/section2/subsection4.xml b/xml/chapter3/section2/subsection4.xml
index 2da44bdfc..d9fc50372 100644
--- a/xml/chapter3/section2/subsection4.xml
+++ b/xml/chapter3/section2/subsection4.xml
@@ -21,9 +21,9 @@
Section describes the application
- of simple functions according to the substitution model, but fails to
- handle the case where the body of the function being applied contains
- constant, function or variable declarations, as in the functions
+ of simple functions according to the environment model, but fails to
+ handle proper blocks that contain contains constant, function or
+ variable declarations, as the bodies of the functions
new_withdraw and
make_account
of section,
@@ -31,16 +31,16 @@
of section
and in both versions of the factorial function
of section.
- Our treatment of the declaration of local names within function bodies
+ Our treatment of the declaration of local names within blocks
is similar to the treatment of declarations of global names, as for
example the name square in
section. We explained
that the names declared in the program are added to the program
frame. More precisely, before the program gets evaluated, we identify
- the names that are declared in the program at toplevel. These names
- are all added to the program frame, and then the program gets evaluated
- with respect to the program environment. Initially, before the program
- runs, these names refer to a special value
+ the names that are declared in the program at toplevel (outside of any
+ block). These names are all added to the program frame, and then the
+ program gets evaluated with respect to the program environment.
+ Initially, before the program runs, these names refer to a special value
$\textit{unassigned}$, and any attempt to
access the value of a name that refers to
$\textit{unassigned}$ leads to an error.
@@ -48,50 +48,16 @@
in section.
- Similar to names that are declared at toplevel, we simply add the
- names declared locally in a function body to the relevant
- environment frame
- before we evaluate the body. The relevant environment frame
- here is of course the new frame that binds the function's parameters
- to the values of the arguments. This leads to the final, definitive
- version of the first of the two rules in
- section that summarize
- the environment model of function application. The second rule
- is copied here for completeness; it hasnt changed.
-
-
- A function object is applied to a set of arguments by constructing a
- frame, in which we create variable bindings for the parameters of the
- function to the arguments of the call, and constant/variable
- bindings for the local constants/variable declarations in the body
- of the function, and then evaluating the body in the context of
- the new environment constructed. The new frame has as its enclosing
- environment the environment part of the function object being
- applied. The local constant/variable names intially refer to
- the value $\textit{unassigned}$, before
- the body is evaluated.
- function
- creating with lambda
-
-
-
- A function is created by evaluating a
- lambda expression relative to a given environment. The resulting
- function object is a pair consisting of the text of the lambda
- expression and a pointer to the environment in which the function
- was created.
-
-
- We apply the same idea to names that are declared within blocks, such
- as the consequent or alternative block of conditional statements, which
- we introduced in section: A block
- is evaluated by extending the current environment with a new frame.
- The constant/variable names declared within the block intially refer
- to the value $\textit{unassigned}$, before
- the body of the block is evaluated.Equipped with a deeper
- understanding of the scope of names, we can now explain why the program
- in footnote of chapter 1 goes
- wrong.
+ In order to evaluate a block in a given environment, we extend the
+ environment by a new frame that contains all names declared locally
+ (outside of nested blocks) in the block body. These names intially refer
+ to the value $\textit{unassigned}$, when the
+ evaluation of the body commences. The evaluation of local constant
+ and variable declarations then re-assign the names to the left of the
+ equal sign, as if the declaration was an assignment.Equipped
+ with a deeper understanding of the scope of names, we can now explain
+ why the program in footnote of chapter 1
+ goes wrong.
@@ -225,21 +191,21 @@ function sqrt(x) {
program
environment, in which the parameter x is bound
-
+ to 2.
+
- to 2.
+ The body of sqrt was then evaluated in E1. Since the first expression
+ in the body of sqrt is
- to 2, and in which the locally declared names
- good_enough,
- improve and
- sqrt_iter are bound to
- the value $\textit{unassigned}$.
+ The body of sqrt was then evaluated in
+ E1. That body in this case is a block with local
+ function declarations and therefore we extended E1 with a new frame for
+ those declarations, resulting in the new environment E2. The body
+ of the block was then evaluated in E2. Since the first statement
+ in the body is
-
- The body of sqrt was then evaluated in
- E1. Since the first expression in the body of
- sqrt is
+
abs_definitionsquare_definition
@@ -253,38 +219,31 @@ function good_enough(guess) {
}
- evaluating this expression defined the
-
- procedure
- function
-
-
- good-enough?
- good_enough
-
- in the environmentE1.
- Tobe more precise, the
- symbol
-
- good-enough?
- good_enough
-
-
- was added to the first frame of E1, bound to a
-
- procedure
- function
-
+ evaluating this expression defined the procedure
+ good-enough?
+ in the environmentE1.
+
+
+ evaluating this declaration created the function
+ good_enough
+ in the environmentE2.
+
+
+
+
+ Tobe more precise, the symbol
+ good-enough? was added to the first frame
+ of E1, bound to a procedure
object whose associated environment is E1.
Tobe more precise, the value
$\textit{unassigned}$
for the symbol good_enough
- in the first frame of E1 is bound to a function
- object whose associated environment is E1.
+ in the first frame of E2 was replaced by a function
+ object whose associated environment is E2.
Similarly,
@@ -295,10 +254,10 @@ function good_enough(guess) {
were defined as
- procedures
- functions
+ procedures in E1.
+ functions in E2.
- in E1. For conciseness,
+ For conciseness,
figure
@@ -331,18 +290,28 @@ function good_enough(guess) {
sqrt_iter(1.0)
- was evaluated, still in environment E1. So the
+ was evaluated, still in environment
+
+ E1.
+ E2.
+
+ So the
procedurefunction
object bound to
- sqrt-iter
- sqrt_iter
+ sqrt-iter
+ in E1 was called with 1 as an argument. This created an environment E2
+ in which
+
+ sqrt_iter
+ in E2 was called with 1 as an argument. This created an environment E3
+ in which
+
- in E1 was called with 1 as an argument. This created an environment E2 in
- which guess, the parameter of
+ guess, the parameter of
sqrt-iter,sqrt_iter,
@@ -358,17 +327,29 @@ function good_enough(guess) {
good-enough?good_enough
- with the value of guess (from E2) as the
- argument for
+ with the value of guess
- good-enough?.
- good_enough.
+
+ (from E2) as the argument for
+ good-enough?.
+
+
+ (from E3) as the argument for
+ good_enough.
+
- This set up another environment, E3, in which
- guess (the parameter of
+ This set up another environment,
- good-enough?)
- good_enough)
+
+ E3, in which
+ guess (the parameter of
+ good-enough?)
+
+
+ E4, in which
+ guess (the parameter of
+ good_enough)
+
is bound to 1. Although
@@ -381,22 +362,23 @@ function good_enough(guess) {
good_enough
both have a parameter named guess, these are two
- distinct local variables located in different frames. Also, E2 and E3 both
- have E1 as their enclosing environment, because the
-
- sqrt-iter
- sqrt_iter
-
- and
-
- good-enough?
- good_enough
-
-
- procedures
- functions
-
- both have E1 as their environment part. One consequence of this is that the
+ distinct local variables located in different frames.
+
+
+ Also, E2 and E3 both have E1 as their enclosing environment, because the
+ sqrt-iter and
+ good-enough? procedures
+ both have E1 as their environment part.
+
+
+ Also, E3 and E4 both have E2 as their enclosing environment, because the
+ sqrt_iter
+ and
+ good_enough
+ both have E2 as their environment part.
+
+
+ One consequence of this is that the
symbol x that appears in the body of
good-enough?
@@ -440,10 +422,10 @@ function good_enough(guess) {
names will be bound in the frame that the
- procedure
- function
+ procedure creates when it is run,
+ block creates when it is evaluated,
- creates when it is run, rather than being bound in the
+ rather than being bound in the
globalprogram
@@ -461,14 +443,14 @@ function good_enough(guess) {
procedure,function,
- simply by using parameter names as free variables. This is because the
- body of the local
+ simply by using parameter names as free variables. This is
+ because the body of the local
procedurefunction
- is evaluated in an environment that is subordinate to the evaluation
- environment for the enclosing
+ is evaluated in an environment that is subordinate to the
+ evaluation environment for the enclosing
procedure.function.
diff --git a/xml/chapter4/section1/section1.xml b/xml/chapter4/section1/section1.xml
index 76182054d..eba2091ec 100644
--- a/xml/chapter4/section1/section1.xml
+++ b/xml/chapter4/section1/section1.xml
@@ -71,55 +71,68 @@
Recall that the model has two basic parts:
- The ultimate refinement of the model in
- section specifies the
+ Recall that the model specifies the
evaluation of function application in two basic steps:
-
-
-
-
-
+
+
+
+
+
To evaluate a combination (a compound expression other than a
special form), evaluate the subexpressions and then apply the value
of the operator subexpression to the values of the operand
subexpressions.
-
-
- To apply a compound procedure to a set of arguments, evaluate the
- body of the procedure in a new environment. To construct this
- environment, extend the environment part of the procedure object by a
- frame in which the formal parameters of the procedure are bound to
- the arguments to which the procedure is applied.
-
-
-
-
-
-
-
-
+
+
To evaluate a function application, evaluate the function
subexpression and the argument subexpressions, and then apply the
value of the function subexpression to the values of the argument
subexpressions.
-
-
- To apply a function to a set of arguments, evaluate the body of the
- function in a new environment. To construct this environment,
- extend the environment part of the function object by a frame in
- which the formal parameters of the function are bound to the
- arguments to which the function is applied, and in which the names
- of constants and variables declared in the body of the function are
- bound to the value $\textit{unassigned}$.
-
-
-
-
-
+
+
+
+
+ To apply a compound
+
+ procedure
+ function
+
+ to a set of arguments, evaluate the
+ body of the
+
+ procedure
+ function
+
+ in a new environment. To construct this
+ environment, extend the environment part of the
+
+ procedure
+ function
+
+ object by a
+ frame in which the
+
+ formal
+
+ parameters of the
+
+ procedure
+ function
+
+ are bound to
+ the arguments to which the
+
+ procedure
+ function
+
+ is applied.
+
+
+
diff --git a/xml/chapter4/section1/subsection1.xml b/xml/chapter4/section1/subsection1.xml
index 1a08c20ab..d2da38769 100644
--- a/xml/chapter4/section1/subsection1.xml
+++ b/xml/chapter4/section1/subsection1.xml
@@ -278,7 +278,7 @@
The operator symbol is the name of the function being applied, and
the operands are the arguments. Thus
evaluate does not need any
- rules for operators.
+ rules for operators combinations.
For a function application,
@@ -357,7 +357,7 @@ function evaluate(stmt, env) {
? eval_return(stmt, env)
: is_application(stmt)
? apply(evaluate(function_expression(stmt), env),
- map(arg => evaluate(arg, env), args(stmt)))
+ list_of_values(args(stmt), env))
: error(stmt, "Unknown syntax in evaluate:");
}
@@ -483,35 +483,27 @@ evaluate(my_program, the_empty_environment);
expressions that make up the body of the procedure.statements that make up the body of the function.
-
-
- The environment for the evaluation of the body of a compound procedure
- is constructed by extending the base environment carried by the
- procedure to include a frame that binds the parameters of the procedure
- to the arguments to which the procedure is to be applied.
-
-
- The environment for the evaluation of the body of a compound function
- is constructed by extending the base environment carried by the function
- to include a frame that binds the parameters of the function to the
- arguments to which the function is to be applied, and the body's local
- names to a special value unassigned.
- Any name declared with const
- or let will refer to this value
- until its declaration gets evaluated. To make sure that the value of
- unassigned is different from any
- other value in the interpreter, we declare it as follows:
-
- unassigned
-
-const unassigned = () => null;
-
-
- The purpose of the lambda expression is purely to create a unique
- identity; the function will never be applied and its return value
- (here null) is irrelevant.
-
-
+ The environment for the evaluation of the body of a compound
+
+ procedure
+ function
+
+ is constructed by extending the base environment carried by the
+
+ procedure
+ function
+
+ to include a frame that binds the parameters of the
+
+ procedure
+ function
+
+ to the arguments to which the
+
+ procedure
+ function
+
+ is to be applied.
Here is the definition of apply:
apply
@@ -537,14 +529,11 @@ function apply(fun, args) {
if (is_primitive_function(fun)) {
return apply_primitive_function(fun, args);
} else if (is_compound_function(fun)) {
- const body = function_body(fun);
- const locals = scan_out_declarations(body);
- const symbols = append(function_parameters(fun), locals);
- const unassigneds = map(_ => unassigned, locals);
- const values = append(args, unassigneds);
- const result = evaluate(body,
- extend_environment(symbols, values,
- function_environment(fun)));
+ const result = evaluate(function_body(fun),
+ extend_environment(
+ function_parameters(fun),
+ args,
+ function_environment(fun)));
return is_return_value(result)
? return_value_content(result)
: undefined;
@@ -554,6 +543,11 @@ function apply(fun, args) {
}
+
+ The name arguments
+ is reserved in JavaScript's strict mode. We chose
+ args instead.
+ apply_examplefunctions_4_1_1
@@ -567,46 +561,7 @@ apply(plus, list(1, 2));
-
- The function scan_out_declarations
- collects the list of all names declared in the body statements. For a
- name to be included in the list, it needs to be declared outside of any
- other block or function.
-
- scan_out_declarations
- scan_out_declarations_example
- [ 'x', [ 'y', null ] ]
-
-function scan_out_declarations(stmt) {
- if (is_sequence(stmt)) {
- const stmts = sequence_statements(stmt);
- return is_empty_sequence(stmts)
- ? null
- : append(scan_out_declarations(first_statement(stmts)),
- scan_out_declarations(make_sequence(
- rest_statements(stmts))));
- } else {
- return is_constant_declaration(stmt)
- ? list(constant_declaration_symbol(stmt))
- : is_variable_declaration(stmt)
- ? list(variable_declaration_symbol(stmt))
- : null;
- }
-}
-
-
-
-
- scan_out_declarations_example
- functions_4_1_1
- functions_4_1_2
- functions_4_1_3
- functions_4_1_4
-
-scan_out_declarations(parse("const x = 1; let y = 2;"));
-
-
In order to return a value, JavaScript functions need to evaluate a
return statement. If a function terminates without return, the value
undefined is returned. Thus, if
@@ -617,75 +572,145 @@ scan_out_declarations(parse("const x = 1; let y = 2;"));
-
-
-
-
- Procedure arguments
-
+
+
+
+ Procedure
+ Function
+
+ arguments
+
+
-
- When eval processes a procedure
- application, it uses list-of-values
- to produce the list of arguments to which the procedure is to be applied.
- List-of-values takes as an argument the
- operands of the combination. It evaluates each operand and returns a
- list of the corresponding values:We could have simplified the
- application? clause in
- eval by using
- map (and stipulating that
- operands returns a list) rather than writing
- an explicit list-of-values procedure. We
- chose not to use map here to emphasize the
- fact that the
- metacircular evaluator for Schemehigher-order
- procedures
- functions in
- higher-order procedures
- functions
- in metacircular evaluator
- evaluator can be implemented without any use of higher-order
- procedures (and thus could be written in a language that doesnt
- have higher-order procedures), even though the language that it supports
- will include higher-order procedures.
-
-
- list_of_values
- list_of_values_example
-
+
+ When
+
+ eval
+ evaluate
+
+ processes a
+
+ procedure
+ function
+
+ application, it uses
+
+ list-of-values
+ list_of_values
+
+
+ to produce the list of arguments to which the
+
+ procedure
+ function
+
+ is to be applied.
+
+ List-of-values
+ The function
+ list_of_values
+
+
+ takes as an argument the
+
+ operands of the combination.
+ arguments of the application.
+
+ It evaluates each
+
+ operand
+ argument
+
+ and returns a
+ list of the corresponding values:We could have simplified the
+
+ application?
+
+ is_applicationfunction
+
+
+ clause in
+
+ eval
+ evaluate
+
+ by using
+ map (and stipulating that
+
+ operands
+ args
+
+ returns a list) rather than writing
+ an explicit
+
+ list-of-values procedure.
+
+ list_of_values
+ function.
+
+
+ We chose not to use map here to emphasize the
+ fact that the
+ metacircular evaluator for Schemehigher-order
+ procedures
+ functions in
+ higher-order procedures
+ functions
+ in metacircular evaluator
+ evaluator can be implemented without any use of higher-order
+
+ procedures
+ functions
+
+ (and thus could be written in a language that doesnt
+ have higher-order
+
+ procedures),
+ functions),
+
+ even though the language that it supports
+ will include higher-order
+
+ procedures.
+ functions.
+
+
+
+ list_of_values
+ list_of_values_example
+
(define (list-of-values exps env)
(if (no-operands? exps)
\'()
(cons (eval (first-operand exps) env)
(list-of-values (rest-operands exps) env))))
-
-
-
- list_of_values_example
- functions_4_1_1
- functions_4_1_2
- functions_4_1_3
- functions_4_1_4
-
+
+
+function list_of_values(exps, env) {
+ return no_args(exps)
+ ? null
+ : pair(evaluate(first_arg(exps), env),
+ list_of_values(rest_args(exps), env));
+}
+
+
+
+ list_of_values_example
+ functions_4_1_1
+ functions_4_1_2
+ functions_4_1_3
+ functions_4_1_4
+
(define the-global-environment (setup-environment))
(eval (read) the-global-environment)
-
-
+
+
const my_addition_expression = parse("1 + 2;");
list_of_values(list(1, my_addition_expression, 7),
the_global_environment);
-
-
-
-
-
-
- list_of_values
-
-
-
-
-
+
+
+
@@ -820,9 +845,9 @@ eval_conditional_expression(my_cond_expr, the_empty_environment);
The function eval_sequence
- is used by eval
- to evaluate a sequence of statements at the toplevel, in a function body
- or in a block. It takes as arguments a sequence of statements and an
+ is used by evaluate
+ to evaluate a sequence of statements at the toplevel, in a block.
+ It takes as arguments a sequence of statements and an
environment, and evaluates the statements in the order in which they
occur. The value returned is the value of the final statement, except
if the result of evaluating any statement in the sequence yields
@@ -881,10 +906,22 @@ eval_sequence(my_sequence, the_empty_environment);
The evaluation of block statements evaluates the body of the
block with respect to an environment that extends the current
environment with a binding of the local names of the block
- body to unassigned.
+ body to a special value
+ unassigned.
- eval_block
+ list_of_unassignedunassigned
+
+function list_of_unassigned(names) {
+ return is_null(names)
+ ? null
+ : pair(unassigned, list_of_unassigned(tail(names)));
+}
+
+
+
+ eval_block
+ list_of_unassignedscan_out_declarationseval_block_example42
@@ -892,13 +929,61 @@ eval_sequence(my_sequence, the_empty_environment);
function eval_block(stmt, env) {
const body = block_body(stmt);
const locals = scan_out_declarations(body);
- const unassigneds = map(_ => unassigned, locals);
+ const unassigneds = list_of_unassigned(locals);
return evaluate(body,
extend_environment(locals, unassigneds, env));
}
-
+ The function scan_out_declarations
+ collects the list of all names declared in the body statements. For a
+ name to be included in the list, it needs to be declared outside of any
+ other block.
+
+ scan_out_declarations
+ scan_out_declarations_example
+ [ 'x', [ 'y', null ] ]
+
+function scan_out_declarations(stmt) {
+ if (is_sequence(stmt)) {
+ const stmts = sequence_statements(stmt);
+ return is_empty_sequence(stmts)
+ ? null
+ : append(scan_out_declarations(first_statement(stmts)),
+ scan_out_declarations(make_sequence(
+ rest_statements(stmts))));
+ } else {
+ return is_constant_declaration(stmt)
+ ? list(constant_declaration_symbol(stmt))
+ : is_variable_declaration(stmt)
+ ? list(variable_declaration_symbol(stmt))
+ : null;
+ }
+}
+
+
+
+ scan_out_declarations_example
+ functions_4_1_1
+ functions_4_1_2
+ functions_4_1_3
+ functions_4_1_4
+
+scan_out_declarations(parse("const x = 1; let y = 2;"));
+
+
+ To make sure that the value of
+ unassigned is different from any
+ other value in the interpreter, we declare it as follows:
+
+ unassigned
+
+const unassigned = () => null;
+
+
+ The purpose of the lambda expression is purely to create a unique
+ identity; the function will never be applied and its return value
+ (here null) is irrelevant.
eval_block_examplefunctions_4_1_1
@@ -1175,7 +1260,7 @@ evaluate(my_program, the_global_environment);
function parse_and_evaluate(input) {
const program = parse(input);
const locals = scan_out_declarations(program);
- const unassigneds = map(x => unassigned, locals);
+ const unassigneds = list_of_unassigned(locals);
const program_env = extend_environment(locals, unassigneds,
the_global_environment);
return evaluate(program, program_env);
diff --git a/xml/chapter4/section1/subsection2.xml b/xml/chapter4/section1/subsection2.xml
index 16dad9ff7..6dc3695fb 100644
--- a/xml/chapter4/section1/subsection2.xml
+++ b/xml/chapter4/section1/subsection2.xml
@@ -517,6 +517,8 @@ display(variable_declaration_value(my_declaration_statement));
// FIXME: replace "function_definition" with "lambda"
// after this issue is handled:
// https://github.com/source-academy/js-slang/issues/634
+// FIXME: remove the block from lambda_body once the
+// parser wraps the body in a block whenever needed.
function is_lambda(stmt) {
return is_tagged_list(stmt, "function_definition");
}
@@ -524,7 +526,7 @@ function lambda_parameters(stmt) {
return map(symbol_of_name, head(tail(stmt)));
}
function lambda_body(stmt) {
- return head(tail(tail(stmt)));
+ return make_block(head(tail(tail(stmt))));
}
diff --git a/xml/chapter4/section1/subsection3.xml b/xml/chapter4/section1/subsection3.xml
index 6d1c00820..a5e0f2afa 100644
--- a/xml/chapter4/section1/subsection3.xml
+++ b/xml/chapter4/section1/subsection3.xml
@@ -616,7 +616,6 @@ tail(head(extend_environment(list("x", "y", "z"),
error.
lookup_variable_value
- unassignedlookup_variable_value_example1
diff --git a/xml/chapter4/section1/subsection4.xml b/xml/chapter4/section1/subsection4.xml
index b1dc0fe4f..8bfcc790b 100644
--- a/xml/chapter4/section1/subsection4.xml
+++ b/xml/chapter4/section1/subsection4.xml
@@ -671,7 +671,7 @@ function driver_loop(env) {
} else {
const program = parse(input);
const locals = scan_out_declarations(program);
- const unassigneds = map(x => unassigned, locals);
+ const unassigneds = list_of_unassigned(locals);
const program_env = extend_environment(locals, unassigneds, env);
const output = evaluate(program, program_env);
user_print(output_prompt, output);
@@ -692,7 +692,7 @@ function driver_loop(env) {
input_prompt + "\n" + input + "\n");
const program = parse(input);
const locals = scan_out_declarations(program);
- const unassigneds = map(x => unassigned, locals);
+ const unassigneds = list_of_unassigned(locals);
const program_env = extend_environment(locals, unassigneds, env);
const output = evaluate(program, program_env);
user_print(output_prompt, output);
diff --git a/xml/chapter4/section1/subsection6.xml b/xml/chapter4/section1/subsection6.xml
index 259672bf6..c5bbc262b 100644
--- a/xml/chapter4/section1/subsection6.xml
+++ b/xml/chapter4/section1/subsection6.xml
@@ -167,9 +167,9 @@
Our environment model of evaluation
(section)
and our metacircular evaluator
- (section) evaluate function
- bodies and blocks by extending an environment with bindings for the
- local names that occur in the body of the function or block. Initially,
+ (section) evaluate
+ blocks by extending an environment with bindings for the
+ local names that occur in the body of the block. Initially,
the names refer to the special value
$\textit{unassigned}$, but evaluation of the
declaration statements assign them to their proper values. Correct
@@ -208,7 +208,7 @@ function f(x) {
should refer to the function is_odd
that is declared after is_even.
The scope of the name is_odd is the
- entire body of f, not just the portion of
+ body block of f, not just the portion of
the body of f starting at the point where
the declaration of is_odd
occurs. Indeed, when we consider that
@@ -221,19 +221,20 @@ function f(x) {
is_even and
is_odd
were being added to the environment simultaneously. More generally, in
- block structure, the scope of a local name is the entire function
- body in which the declaration is evaluated.
+ block structure, the scope of a local name is the entire block
+ in which the declaration is evaluated.
- This is achieved in section by
- the function scan_out_declarations,
- and binding them to the value
- unassigned, whenever the function,
- here f, is applied. We can make
- this treatment of local names explicit by transforming them into
+ The evaluator in section
+ finds all locally declared names of a block using
+ scan_out_declarations,
+ and binds them to the value
+ unassigned, whenever the block is
+ evaluated. For lambda expressions with local declarations, we can achieve
+ the same effect by transforming their bodies into
immediately invoked lambda expressions
- (see section). For example, the
- lambda expression
+ (see section). For example,
+ the lambda expression
($\textit{vars}$) => {
@@ -256,9 +257,7 @@ function f(x) {
where unassigned
- is as defined in section,
- which causes looking up a name to signal an error if an attempt is made
- to use the value of the not-yet-assigned name.
+ is as defined in section.
@@ -306,7 +305,8 @@ function f(x) {
In this exercise we implement the method just described for interpreting
- internal declarations as syntactic sugar for immediately invoked
+ internal declarations of lambda expressions
+ as syntactic sugar for immediately invoked
functions that carry out assignments.
@@ -324,13 +324,10 @@ function f(x) {
Write a function
transform_lambda
- that transforms any lambda expression as shown above. Analogously,
- write a function transform_block
- that transforms any block in a similar manner.
+ that transforms any lambda expression as shown above.
- Install transform_lambda and
- transform_block
+ Install transform_lambda
in the interpreter by modifying the function
evaluate
in an appropriate way.
@@ -366,7 +363,7 @@ function f(x) {
v = b;
$\textit{statements}$
})($e_1$, $e_2$);
- })("*unassigned*", "*unassigned*");
+ })(unassigned, unassigned);
}
@@ -433,34 +430,22 @@ function solve(f, y0, dt) {
- Our implementation of lambda expressions and blocks in
+ Our implementation of blocks in
section imposes a
- runtime burden on every function application: It needs to scan
- the function body for locally declared names. We shall devise
- a simpler mechanism in this exercise. In it, we fall back on
- the penultimate specification of the environment model of
- function application, given in
- section. Recall that
- in this specification, a function object is applied to a set of
- arguments by constructing a frame, in which we create variable bindings
- of the parameters of the function to the arguments of the call, and then
- evaluating the body of the function in the context of the new
- environment constructed. (The ultimate specification adds
- bindings for the local names, and is given in
- section.) Using the
- penultimate specification, we can achieve the desired result in
- many cases by changing the evaluation of constant and variable
+ runtime burden: It needs to scan
+ the body of the block for locally declared names. We shall devise
+ a simpler mechanism in this exercise. We can achieve the desired result
+ in many cases by changing the evaluation of constant and variable
declaration such that they force into the innermost
frame of the given environment a binding of the declared name to
the result of evaluating the expression on the right hand side of the
declaration.
- Simplify the declaration of
- apply in
- section to adhere
- to the penultimate specification of function application, given in
- section.
+ Simplify the declaration of
+ eval_block in
+ section to
+ ignore local declarations.
Declare a function
@@ -483,6 +468,11 @@ add_binding_to_frame($\textit{name}$, $\textit{value}$, $\textit{frame}$)
add_binding_to_frame instead of
set_name_value.
+
+ Can you find programs that behave differently with this treatment
+ of local declarations, compared to the implementation
+ in section?
+
@@ -534,17 +524,19 @@ add_binding_to_frame($\textit{name}$, $\textit{value}$, $\textit{frame}$)
Draw diagrams of the environment in effect when evaluating the
- $\textit{statement}$ in the
+ $\textit{statements}$ in the
function in the text, comparing how this will be structured when
declarations are interpreted sequentially as described in
exercise
- with how it is structured if declarations are scanned out as described in
- section.
- Why is there an extra frame in the latter case? Explain why this
- difference in environment structure can never make a difference in the
- behavior of a correct program. Design a way to make the interpreter
- implement the simultaneous scope rule for internal
- declarations without constructing the extra frame.
+ with how it is structured if declarations are scanned out as described
+ in section.
+ Why is there an extra frame in the latter case?
+ JavaScript forbids the re-declaration of parameters
+ as local names in the body block of any function. With this restriction,
+ can you achieve the scoping for local names
+ in lambda expressions of
+ section,
+ without constructing the extra frame?
diff --git a/xml/chapter4/section1/subsection7.xml b/xml/chapter4/section1/subsection7.xml
index 1b50610ae..a681a1e4a 100644
--- a/xml/chapter4/section1/subsection7.xml
+++ b/xml/chapter4/section1/subsection7.xml
@@ -198,6 +198,8 @@ analyze(parse("{ const x = 1; x + 1; }"))
analyze
+ headline_4_1_1
+ list_of_unassignedfunctions_4_1_2functions_4_1_3functions_4_1_4
@@ -535,15 +537,6 @@ function analyze_conditional_expression(stmt) {
may be applied many times.
-
-
- The scanning-out of local declarations is also only
- done once, and their bindings to the
- $\textit{unassigned}$ value are
- installed in the environment of the function object, once
- the execution function of the lambda expression is called.
-
- analyze_lambda_exampleanalyze
@@ -569,12 +562,9 @@ list_ref(analyze_lambda(parse("x => x;"))
function analyze_lambda(stmt) {
const parameters = lambda_parameters(stmt);
const body = lambda_body(stmt);
- const locals = scan_out_declarations(body);
- const unassigneds = map(_ => unassigned, locals);
const bfun = analyze(body);
return env =>
- make_function(parameters, bfun,
- extend_environment(locals, unassigneds, env));
+ make_function(parameters, bfun, env);
}
@@ -677,79 +667,93 @@ function analyze_sequence(stmts) {
-
-
- For return statements, we analyze the return expression and
- apply the resulting execution function in the execution function
- for the return statement.
-
- analyze_return_statement_example
- analyze
-
+
+
+
+
+
+ For return statements, we analyze the return expression and
+ apply the resulting execution function in the execution function
+ for the return statement.
+
+ analyze_return_statement_example
+ analyze
+
analyze_return_statement(list_ref(parse("() => x + 1;"), 2))
(extend_environment(list("x"), list(6), the_global_environment));
-
-
-
- analyze_return_statement
- analyze_return_statement_example
- [ 'return_value', [ 7, null ] ]
-
+
+
+
+ analyze_return_statement
+ analyze_return_statement_example
+ [ 'return_value', [ 7, null ] ]
+
function analyze_return_statement(stmt) {
const rfun = analyze(return_expression(stmt));
return env => make_return_value(rfun(env));
}
-
-
-
+
+
+
-
- Just like the bodies of lambda expressions, the bodies of
- blocks are scanned only once for local declarations, and
- their bindings are installed in the environment, once
- the execution function for the block is called.
-
- analyze_block_example
- analyze
-
+
+ The bodies of
+ blocks are scanned only once for local declarations, and
+ their bindings are installed in the environment, once
+ the execution function for the block is called.
+
+ analyze_block_example
+ analyze
+
analyze_block(parse("{ const x = 4; x; }"))
(the_global_environment);
-
-
-
- analyze_block
- analyze_block_example
- 4
-
+
+
+
+ analyze_block
+ list_of_unassigned
+ analyze_block_example
+ 4
+
function analyze_block(stmt) {
const body = block_body(stmt);
const locals = scan_out_declarations(body);
- const unassigneds = map(_ => unassigned, locals);
+ const unassigneds = list_of_unassigned(locals);
const bfun = analyze(body);
return env => bfun(extend_environment(locals, unassigneds, env));
}
-
-
-
-
+
+
+
+
+
- To analyze an application, we analyze the operator and operands and
- construct an execution
+ To analyze an application, we analyze the
- procedure
- function
+ operator and operands
+ function expression and arguments
- that calls the operator execution
+ and construct an execution
procedurefunction
+ that calls the
+
+ operator execution function
+ execution function of the function expression
+
(to obtain the actual
procedurefunction
- to be applied) and the operand execution
+ to be applied) and the
+
+ operand
+ argument
+
+ execution
proceduresfunctions
@@ -883,20 +887,8 @@ function execute_application(fun, args) {
analyzing evaluator
conditional statements
- Eva Lu Ator ponders over the treatment of locally
- declared names in the function
- analyze_lambda, drawing
- environment model diagrams. Alyssa P. Hacker notices
- and asks her what's wrong. Eva complains that she is getting
- different diagrams than the ones obtained from following
- the evaluator in section.
- Eva agrees, but says: Don't worry. Any program that runs properly
- in the original evaluator will properly in the syntactic analysis
- evaluator. Is Eva right? Try to draw the diagrams that Eva
- is talking about. How about the other way around: Are there programs
- that run properly in the syntactic analysis evaluator,
- but not in the original one? If yes, can you change the syntactic
- analysis evaluator such that it behaves exactly like the original one.
+ Extend the evaluator in this section to support while loops.
+ (See exercise.)
@@ -1051,7 +1043,7 @@ function analyze_sequence(stmts) {
function parse_and_evaluate(input) {
const program = parse(input);
const locals = scan_out_declarations(program);
- const unassigneds = map(x => unassigned, locals);
+ const unassigneds = list_of_unassigned(locals);
const program_env = extend_environment(locals, unassigneds,
the_global_environment);
return evaluate(program, program_env);
diff --git a/xml/chapter4/section2/subsection2.xml b/xml/chapter4/section2/subsection2.xml
index f579d5c2e..5c3ed6d70 100644
--- a/xml/chapter4/section2/subsection2.xml
+++ b/xml/chapter4/section2/subsection2.xml
@@ -383,12 +383,8 @@ function apply(fun, args, env) {
map(arg => actual_value(arg, env), args));
} else if (is_compound_function(fun)) {
const body = function_body(fun);
- const locals = scan_out_declarations(body);
- const symbols = append(function_parameters(fun), locals);
- const unassigneds = map(_ => unassigned, locals);
- // following line changed
- const values = append(map(arg => delay_it(arg, env), args),
- unassigneds);
+ const symbols = function_parameters(fun);
+ const values = map(arg => delay_it(arg, env), args);
const result = evaluate(body,
extend_environment(symbols, values,
function_environment(fun)));
@@ -610,7 +606,7 @@ function driver_loop(env) {
} else {
const program = parse(input);
const locals = scan_out_declarations(program);
- const unassigneds = map(x => unassigned, locals);
+ const unassigneds = list_of_unassigned(locals);
const program_env = extend_environment(locals, unassigneds, env);
const output = actual_value(program, program_env);
user_print(output_prompt, output);
@@ -631,7 +627,7 @@ function driver_loop(env) {
input_prompt + "\n" + input + "\n");
const program = parse(input);
const locals = scan_out_declarations(program);
- const unassigneds = map(x => unassigned, locals);
+ const unassigneds = list_of_unassigned(locals);
const program_env = extend_environment(locals, unassigneds, env);
const output = actual_value(program, program_env);
user_print(output_prompt, output);
@@ -1506,7 +1502,7 @@ function f(a, b, c, d) {
function parse_and_evaluate(input) {
const program = parse(input);
const locals = scan_out_declarations(program);
- const unassigneds = map(x => unassigned, locals);
+ const unassigneds = list_of_unassigned(locals);
const program_env = extend_environment(locals, unassigneds,
the_global_environment);
return actual_value(program, program_env);
diff --git a/xml/chapter4/section3/subsection3.xml b/xml/chapter4/section3/subsection3.xml
index fffd5c0e8..34062ff82 100644
--- a/xml/chapter4/section3/subsection3.xml
+++ b/xml/chapter4/section3/subsection3.xml
@@ -639,12 +639,9 @@ function analyze_name(stmt) {
function analyze_lambda(stmt) {
const parameters = lambda_parameters(stmt);
const body = lambda_body(stmt);
- const locals = scan_out_declarations(body);
- const unassigneds = map(_ => unassigned, locals);
const bfun = analyze(body);
return (env, succeed, fail) =>
- succeed(make_function(parameters, bfun,
- extend_environment(locals, unassigneds, env)),
+ succeed(make_function(parameters, bfun, env),
fail);
}
@@ -1067,12 +1064,13 @@ function analyze_return_statement(stmt) {
failure continuations.
analyze_block_amb
+ list_of_unassignedall_solutions_test_4
function analyze_block(stmt) {
const body = block_body(stmt);
const locals = scan_out_declarations(body);
- const unassigneds = map(_ => unassigned, locals);
+ const unassigneds = list_of_unassigned(locals);
const bfun = analyze(body);
return (env, succeed, fail) =>
bfun(extend_environment(locals, unassigneds, env),
diff --git a/xml/chapter4/section4/subsection4.xml b/xml/chapter4/section4/subsection4.xml
index 7f6fc4c4d..ee48c7bcb 100644
--- a/xml/chapter4/section4/subsection4.xml
+++ b/xml/chapter4/section4/subsection4.xml
@@ -3151,11 +3151,11 @@ function pretty(string, args, arg_pretty) {
function pretty_term(arg) {
return is_null(arg)
? "null"
- : is_list(arg) &&
- (is_null(tail(arg)) || head(tail(arg)) !== "?")
- ? (head(arg) === "?"
- ? contract_question_mark(arg)
- : pretty("list", arg, pretty_term))
+ : is_list(arg) && head(arg) === "?"
+ ? contract_question_mark(arg)
+ : is_list(arg) &&
+ (is_null(tail(arg)) || head(tail(arg)) !== "?")
+ ? pretty("list", arg, pretty_term))
: is_pair(arg)
? pretty("pair", list(head(arg), tail(arg)), pretty_term)
: is_string(arg)