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..6da4f8ea1 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.
@@ -274,6 +273,7 @@ function f(x) {
+
In this exercise we implement the method just described for interpreting
@@ -302,11 +302,11 @@ function f(x) {
Which place is better? Why?
-
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,19 +324,15 @@ 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 +362,7 @@ function f(x) {
v = b;
$\textit{statements}$
})($e_1$, $e_2$);
- })("*unassigned*", "*unassigned*");
+ })(unassigned, unassigned);
}
@@ -433,34 +429,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 +467,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?
+
@@ -510,7 +499,7 @@ add_binding_to_frame($\textit{name}$, $\textit{value}$, $\textit{frame}$)
the simultaneous scope rule for internal procedure definitions, and that
it is unreasonable to treat procedure names differently from other names.
Thus, she argues for the mechanism implemented in
- exercise. This would lead to
+ exercise. This would lead to
a being unassigned at the time that the
value for b is to be computed. Hence, in
Alyssas view the procedure should produce an error. Eva has a
@@ -534,17 +523,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 e03a8c6c4..e55b14bd5 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)
diff --git a/xml/chapter5/chapter5.xml b/xml/chapter5/chapter5.xml
index 5eef23352..a6998675d 100644
--- a/xml/chapter5/chapter5.xml
+++ b/xml/chapter5/chapter5.xml
@@ -26,7 +26,7 @@
% 2/24/96 use smalltt to make some stuff fit nicely in 5.2
% 2/23/96 use Stabular instead of point-size changes
% 2/22/96 new spec for epigraph
- %2/19/96 change exp-iter - -> expt-iter
+ % 2/19/96 change exp-iter - -> expt-iter
diff --git a/xml/chapter5/section1/section1.xml b/xml/chapter5/section1/section1.xml
index 05304a110..7b37c8266 100644
--- a/xml/chapter5/section1/section1.xml
+++ b/xml/chapter5/section1/section1.xml
@@ -1,156 +1,180 @@
-
- Designing Register Machines
-
-
-
-
- register machinedesign of|(
- register machinedata paths|(
- register machinecontroller|(
- data paths for register machine|(
- controller for register machine|(
- operationin register machine|(
-
-
- To design a register machine, we must design its data paths
- (registers and operations) and the controller that sequences
- these operations. To illustrate the design of a simple register
- machine, let us examine Euclids Algorithm, which is used to compute
-
- the greatest common divisor (GCD) of two integers. As we saw in
- Euclids Algorithm
- section, Euclids Algorithm can be carried out by an iterative
- process, as specified by the following
- procedurefunction:
-
-
-
+
+ Designing Register Machines
+
+
+
+
+ register machinedesign of|(
+ register machinedata paths|(
+ register machinecontroller|(
+ data paths for register machine|(
+ controller for register machine|(
+ operationin register machine|(
+
+
+ To design a register machine, we must design its data paths
+ (registers and operations) and the controller that sequences
+ these operations. To illustrate the design of a simple register
+ machine, let us examine Euclids Algorithm, which is used to compute
+
+ the greatest common divisor (GCD) of two integers. As we saw in
+ Euclids Algorithm
+ section, Euclids Algorithm can be
+ carried out by an iterative process, as specified by the following
+
+ procedure:
+ function:
+
+
+ gcd_example
+
(define (gcd a b)
(if (= b 0)
a
(gcd b (remainder a b))))
-
-
+
+
function gcd(a, b) {
return b === 0 ? a : gcd(b, a % b);
}
-
-
-
-
-
- A machine to carry out this algorithm must keep track of two numbers,
- $a$ and $b$, so let us assume that these numbers are stored in two
- registers with those names. The basic operations required are testing
- whether the contents of register b is zero and computing the
- remainder of the contents of register a divided by the contents
- of register b. The remainder operation is a complex process,
- but assume for the moment that we have a primitive device that
- computes remainders. On each cycle of the GCD algorithm, the contents
- of register a must be replaced by the contents of register b, and the contents of b must be replaced by the remainder of
- the old contents of a divided by the old contents of b.
- It would be convenient if these replacements could be done
- simultaneously, but in our model of register machines we will assume
- that only one register can be assigned a new value at each step. To
- accomplish the replacements, our machine will use a third
- temporary register, which we call t. (First the remainder
- will be placed in t, then the contents of b will be placed
- in a, and finally the remainder stored in t will be placed
- in b.)
-
-
-
- data paths for register machinedata-path diagram
- register machinedata-path diagram
- We can illustrate the registers and operations required for this
- machine by using the data-path diagram shown in
- figure. In this
- diagram, the registers (a, b, and t) are represented
- by rectangles. Each way to assign a value to a register is
- indicated by an arrow with an X behind the head, pointing from
- the source of data to the register. We can think of the X as a
- button that, when pushed, allows the value at the source to flow
- into the designated register. The label next to each button is the
- name we will use to refer to the button. The names are arbitrary, and
- can be chosen to have mnemonic value (for example, a<-b denotes
- pushing the button that assigns the contents of register b to
- register a). The source of data for a register can be another
- register (as in the a<-b assignment), an operation result (as in
- the t<-r assignment), or a constant (a built-in value that
- cannot be changed, represented in a data-path diagram by a triangle
- containing the constant).
-
-
-
- An operation that computes a value from constants and the contents
- of registers is represented in a data-path diagram by a trapezoid
- containing a name for the operation. For example, the box marked rem in figure represents an
- operation that computes the remainder of the contents of the
- registers a and b to which it is attached. Arrows
- (without buttons) point from the input registers and constants to the
- box, and arrows connect the operations output value to registers.
- A test is represented by a circle containing a name for the test. For
- example, our GCD machine has an operation that
- tests whether the contents of register
- b is zero. A test also has arrows from its input
- test operation in register machine
- register machinetest operation
- registers and constants, but it has no output
- arrows; its value is used by the controller rather than by the data
- paths. Overall, the data-path diagram shows the registers and
- operations that are required for the machine and how they must be
- connected. If we view the arrows as wires and the X buttons as
- switches, the data-path diagram is very like the wiring diagram for a
- machine that could be constructed from electrical components.
-
-
-
-
-
Data paths for a GCD machine.
-
-
-
-
- register machinecontroller diagram
- controller for register machinecontroller diagram
- In order for the data paths to actually compute GCDs, the buttons must
- be pushed in the correct sequence. We will describe this sequence in
- terms of a controller diagram, as illustrated in
- figure. The elements of the controller
- diagram indicate how the
- data-path components should be operated. The rectangular boxes in the
- controller diagram identify data-path buttons to be pushed, and the
- arrows describe the sequencing from one step to the next. The diamond
- in the diagram represents a decision. One of the two sequencing
- arrows will be followed, depending on the value of the data-path test
- identified in the diamond. We can interpret the controller in terms
- of a physical analogy: Think of the diagram as a maze in which a
- marble is rolling. When the marble rolls into a box, it pushes the
- data-path button that is named by the box. When the marble rolls into
- a decision node (such as the test for b$\, =0$), it leaves the
- node on the path determined by the result of the indicated test.
- Taken together, the data paths and the controller completely describe
- a machine for computing GCDs. We start the controller (the rolling
- marble) at the place marked start, after placing numbers in
- registers a and b. When the controller reaches done, we will find the value of the GCD in register a.
-
-
-
-
-
-
Controller for a GCD machine.
-
-
-
-
-
- Design a register machine to compute factorials using the iterative
- algorithm specified by the following
- procedurefunction. Draw data-path and
- controller diagrams for this machine.
-
-
-
+
+
+
+
+
+ A machine to carry out this algorithm must keep track of two numbers,
+ $a$ and $b$, so let
+ us assume that these numbers are stored in two registers with those names.
+ The basic operations required are testing whether the contents of register
+ b is zero and computing the remainder of the
+ contents of register a divided by the contents
+ of register b. The remainder operation is a
+ complex process,
+ but assume for the moment that we have a primitive device that
+ computes remainders. On each cycle of the GCD algorithm, the contents
+ of register a must be replaced by the contents
+ of register b, and the contents of
+ b must be replaced by the remainder of
+ the old contents of a divided by the old
+ contents of b. It would be convenient if these
+ replacements could be done simultaneously, but in our model of register
+ machines we will assume that only one register can be assigned a new value
+ at each step. To accomplish the replacements, our machine will use a third
+ temporary register, which we call
+ t. (First the remainder
+ will be placed in t, then the contents of
+ b will be placed in
+ a, and finally the remainder stored in
+ t will be placed in
+ b.)
+
+
+
+ data paths for register machine
+ data-path diagram
+ register machinedata-path diagram
+ We can illustrate the registers and operations required for this
+ machine by using the data-path diagram shown in
+ figure. In this
+ diagram, the registers (a,
+ b, and t) are
+ represented by rectangles. Each way to assign a value to a register is
+ indicated by an arrow with an X behind the
+ head, pointing from the source of data to the register. We can think of
+ the X as a button that, when pushed, allows
+ the value at the source to flow into the designated register.
+ The label next to each button is the name we will use to refer to the
+ button. The names are arbitrary, and can be chosen to have mnemonic value
+ (for example, a<-b denotes pushing the
+ button that assigns the contents of register b
+ to register a). The source of data for a
+ register can be another register (as in the
+ a<-b assignment), an operation result (as in
+ the t<-r assignment), or a constant
+ (a built-in value that cannot be changed, represented in a data-path
+ diagram by a triangle containing the constant).
+
+
+
+ An operation that computes a value from constants and the contents
+ of registers is represented in a data-path diagram by a trapezoid
+ containing a name for the operation. For example, the box marked
+ rem in
+ figure represents an operation that
+ computes the remainder of the contents of the registers
+ a and b to which
+ it is attached. Arrows (without buttons) point from the input registers and
+ constants to the box, and arrows connect the operations output value
+ to registers. A test is represented by a circle containing a name for the
+ test. For example, our GCD machine has an operation that tests whether the
+ contents of register b is zero. A test also
+ has arrows from its input
+ test operation in register machine
+ register machinetest operation
+ registers and constants, but it has no output
+ arrows; its value is used by the controller rather than by the data
+ paths. Overall, the data-path diagram shows the registers and
+ operations that are required for the machine and how they must be
+ connected. If we view the arrows as wires and the
+ X buttons as switches, the data-path diagram
+ is very like the wiring diagram for a machine that could be constructed
+ from electrical components.
+
+
+
+
Data paths for a GCD machine.
+
+
+
+
+ register machinecontroller diagram
+ controller for register machine
+ controller diagram
+ In order for the data paths to actually compute GCDs, the buttons must
+ be pushed in the correct sequence. We will describe this sequence in
+ terms of a controller diagram, as illustrated in
+ figure. The elements of the
+ controller diagram indicate how the data-path components should be operated.
+ The rectangular boxes in the controller diagram identify data-path buttons
+ to be pushed, and the arrows describe the sequencing from one step to the
+ next. The diamond in the diagram represents a decision. One of the two
+ sequencing arrows will be followed, depending on the value of the data-path
+ test identified in the diamond. We can interpret the controller in terms
+ of a physical analogy: Think of the diagram as a maze in which a marble is
+ rolling. When the marble rolls into a box, it pushes the data-path button
+ that is named by the box. When the marble rolls into a decision node (such
+ as the test for
+ b$\, =0$), it leaves
+ the node on the path determined by the result of the indicated test.
+ Taken together, the data paths and the controller completely describe
+ a machine for computing GCDs. We start the controller (the rolling
+ marble) at the place marked start, after
+ placing numbers in registers a and
+ b. When the controller reaches
+ done, we will find the value of the GCD in
+ register a.
+
+
+
+
+
Controller for a GCD machine.
+
+
+
+
+
+
+ Design a register machine to compute factorials using the iterative
+ algorithm specified by the following
+
+ procedure.
+ function.
+
+ Draw data-path and
+ controller diagrams for this machine.
+
+ factorial_example
+
(define (factorial n)
(define (iter product counter)
(if (> counter n)
@@ -158,8 +182,8 @@ function gcd(a, b) {
(iter (* counter product)
(+ counter 1))))
(iter 1 1))
-
-
+
+
function factorial(n) {
function iter(product, counter) {
return counter > n
@@ -169,28 +193,28 @@ function factorial(n) {
}
return iter(1, 1);
}
-
-
-
- data paths for register machine|)
- controller for register machine|)
- register machinedata paths|)
- register machinecontroller|)
- operationin register machine|)
+
+
+
+ data paths for register machine|)
+ controller for register machine|)
+ register machinedata paths|)
+ register machinecontroller|)
+ operationin register machine|)
-
- &subsection5.1.1;
+
+ &subsection5.1.1;
-
- &subsection5.1.2;
+
+ &subsection5.1.2;
-
- &subsection5.1.3;
+
+ &subsection5.1.3;
-
- &subsection5.1.4;
+
+ &subsection5.1.4;
-
- &subsection5.1.5;
+
+ &subsection5.1.5;
-
+
diff --git a/xml/chapter5/section1/subsection1.xml b/xml/chapter5/section1/subsection1.xml
index 783b84714..fbc7f5ce4 100644
--- a/xml/chapter5/section1/subsection1.xml
+++ b/xml/chapter5/section1/subsection1.xml
@@ -1,79 +1,95 @@
-
-
- A Language for Describing Register Machines
-
+
+
+ A Language for Describing Register Machines
+
-
- register machinelanguage for describing|(
+
+ register machinelanguage for describing|(
-
- Data-path and controller diagrams are adequate for representing simple
- machines such as GCD, but they are unwieldy for describing large
- machines such as a LispJavaScript interpreter. To make it possible to deal with complex machines, we will create a language that presents, in textual
- form, all the information given by the data-path and controller
- diagrams. We will start with a notation that directly mirrors the diagrams.
-
-
-
- We define the data paths of a machine by describing the registers and
- the operations. To describe a register, we give it a name
- and specify the buttons that control assignment to it. We give each
- of these buttons a name and specify the source of the data that enters
- the register under the buttons control. (The source is a register, a
- constant, or an operation.)
- To describe an operation, we give
- it a name and specify its inputs (registers or constants).
-
+
+ Data-path and controller diagrams are adequate for representing simple
+ machines such as GCD, but they are unwieldy for describing large
+ machines such as a
+
+ Lisp
+ JavaScript
+
+ interpreter. To make it possible to deal with complex machines, we will
+ create a language that presents, in textual form, all the information given
+ by the data-path and controller diagrams. We will start with a notation
+ that directly mirrors the diagrams.
+
+
+
+ We define the data paths of a machine by describing the registers and
+ the operations. To describe a register, we give it a name
+ and specify the buttons that control assignment to it. We give each
+ of these buttons a name and specify the source of the data that enters
+ the register under the buttons control. (The source is a register,
+ a constant, or an operation.) To describe an operation, we give
+ it a name and specify its inputs (registers or constants).
+
-
- We define the controller of a machine as a sequence of
- register-machine languageinstructions
-
- instructions together with
- register-machine languagelabel
- register-machine languageentry point
- labels that identify entry
- points in the sequence. An instruction is one of the following:
-
-
The name of a data-path button to push to assign a value to
- a register. (This corresponds to a box in the controller diagram.)
+
+ We define the controller of a machine as a sequence of
+ register-machine languageinstructions
+
+ instructions together with
+ register-machine languagelabel
+ register-machine languageentry point
+ labels that identify entry
+ points in the sequence. An instruction is one of the following:
+
+
+ The name of a data-path button to push to assign a value to
+ a register. (This corresponds to a box in the controller diagram.)
+ register-machine language
+ test@test
+
+
+
+ A test instruction, that performs a
+ specified test.
+ register-machine language
+ branch@branch
+
+ register-machine language
+ label@label
+
+
+
+ A conditional branch (branch instruction)
+ to a location indicated by a controller label, based on the result of
+ the previous test. (The test and branch together correspond to a
+ diamond in the controller diagram.) If the test is false, the
+ controller should continue with the next instruction in the sequence.
+ Otherwise, the controller should continue with the instruction after
+ the label.
+ register-machine language
+ goto@goto
+
+
+
+ An unconditional branch
+
+ (goto
+ (go_to
+
+ instruction) naming a
+ controller label at which to continue execution.
+
+
+
- register-machine languagetest@test
-
-
-
A test instruction, that performs a specified test.
+
+ The machine starts at the beginning of the controller instruction
+ sequence and stops when execution reaches the end of the sequence.
+ Except when a branch changes the flow of control, instructions are
+ executed in the order in which they are listed.
- register-machine languagebranch@branch
-
- register-machine languagelabel@label
-
-
-
A conditional branch (branch instruction) to a
- location indicated by a controller label, based on the result of the
- previous test. (The test and branch together correspond to a diamond
- in the controller diagram.) If the test is false, the controller
- should continue with the next instruction in the sequence. Otherwise,
- the controller should continue with the instruction after the label.
-
- register-machine languagegoto@goto
-
-
-
An unconditional branch (gotogo_to instruction) naming a
- controller label at which to continue execution.
-
-
-
-
-
-
- The machine starts at the beginning of the controller instruction
- sequence and stops when execution reaches the end of the sequence.
- Except when a branch changes the flow of control, instructions are
- executed in the order in which they are listed.
-
-
-
-
+
+
+
(data-paths
(registers
((name a)
@@ -98,8 +114,8 @@
(b<-t) ; button push
(goto (label test-b)) ; unconditional branch
gcd-done) ; label
-
-
+
+
data_paths(
registers(
list(
@@ -124,63 +140,75 @@ controller(
"t<-r", // button push
"a<-b", // button push
"b<-t", // button push
- go_to(label("test-b"))), // unconditional branch
+ go_to(label("test-b"))), // unconditional branch
"gcd-done"); // label
-
-
-
A specification of the GCD machine.
-
-
-
+
+
+
A specification of the GCD machine.
+
+
+
-
- Figure shows the GCD machine described in
- this way. This example only hints at the generality of these
- descriptions, since the GCD machine is a very simple case: Each
- register has only one button, and each button and test is used only
- once in the controller.
-
+
+ Figure shows the GCD machine
+ described in this way. This example only hints at the generality of these
+ descriptions, since the GCD machine is a very simple case: Each register has
+ only one button, and each button and test is used only once in the
+ controller.
+
-
- Unfortunately, it is difficult to read such a description. In order
- to understand the controller instructions we must constantly refer
- back to the definitions of the button names and the operation names,
- and to understand what the buttons do we may have to refer to the
- definitions of the operation names. We will thus transform our
- notation to combine the information from the data-path and controller
- descriptions so that we see it all together.
-
+
+ Unfortunately, it is difficult to read such a description. In order
+ to understand the controller instructions we must constantly refer
+ back to the definitions of the button names and the operation names,
+ and to understand what the buttons do we may have to refer to the
+ definitions of the operation names. We will thus transform our
+ notation to combine the information from the data-path and controller
+ descriptions so that we see it all together.
+
-
- To obtain this form of description, we will replace the arbitrary
- button and operation names by the definitions of their behavior. That
- is, instead of saying (in the controller) Push button t<-r
- and separately saying (in the data paths) Button t<-r assigns
- the value of the rem operation to register t and The
- rem operations inputs are the contents of registers
- register-machine languageassign@assign
+
+ To obtain this form of description, we will replace the arbitrary
+ button and operation names by the definitions of their behavior. That
+ is, instead of saying (in the controller) Push button
+ t<-r and separately saying (in the
+ data paths) Button t<-r assigns the
+ value of the rem operation to register
+ t and The
+ rem operations inputs are the contents
+ of registers
+ register-machine language
+ assign@assign
- register-machine languageop@op
+ register-machine language
+ op@op
- register-machine languagereg@reg
+ register-machine language
+ reg@reg
- a and b, we will say (in the controller) Push the
- button that assigns to register t the value of the rem
- operation on the contents of registers a and b.
- Similarly, instead of saying (in the controller) Perform the = test and separately saying (in the data paths) The = test operates on the contents of register b and the
- constant 0, we will say Perform the = test on the
- register-machine languageconst@const
+ a and b,
+ we will say (in the controller) Push the button that assigns to
+ register t the value of the
+ rem operation on the contents of registers
+ a and b.
+ Similarly, instead of saying (in the controller) Perform the
+ = test and separately saying (in the
+ data paths) The = test operates on the
+ contents of register b and the
+ constant 0, we will say Perform the
+ = test on the
+ register-machine language
+ const@const
- contents of register b and the constant 0. We will omit the
- data-path description, leaving only the controller sequence. Thus,
- the GCD machine is described as follows:
-
-
-
-
+
(controller
test-b
(test (op =) (reg b) (const 0))
@@ -190,8 +218,8 @@ controller(
(assign b (reg t))
(goto (label test-b))
gcd-done)
-
-
+
+
controller(
"test-b",
list(test(list(op("="), reg(b), constant(0))),
@@ -201,16 +229,15 @@ controller(
assign("b", reg(t)),
go_to(label("test-b"))),
"gcd-done");
-
+
-
+
-
- This form of description is easier to read than the kind illustrated
- in Figure, but it also has disadvantages:
-
-
-
It is more verbose for large machines,
+
+ This form of description is easier to read than the kind illustrated
+ in Figure, but it also has disadvantages:
+
+
It is more verbose for large machines,
because complete descriptions of the data-path elements are repeated
whenever the elements are mentioned in the controller instruction
sequence. (This is not a problem in the GCD example, because each
@@ -218,111 +245,162 @@ controller(
data-path descriptions obscures the actual data-path structure of the
machine; it is not obvious for a large machine how many registers,
operations, and buttons there are and how they are interconnected.
+
+
+ Because the controller instructions in a machine definition look like
+
+ Lisp
+ JavaScript
+
+ expressions, it is easy to forget that they are
+ not arbitrary
+
+ Lisp.
+ JavaScript.
+
+ expressions. They can notate only legal machine operations. For
+ example, operations can operate directly only on constants and the
+ contents of registers, not on the results of other operations.
+
+
+
-
-
Because the controller instructions in a machine definition
- look like LispJavaScript expressions, it is easy to forget that they are
- not arbitrary LispJavaScript expressions. They can notate only legal machine
- operations. For example, operations can operate directly only on
- constants and the contents of registers, not on the results of other
- operations.
-
-
-
-
+
+ In spite of these disadvantages, we will use this register-machine
+ language throughout this chapter, because we will be more concerned with
+ understanding controllers than with understanding the elements and
+ connections in data paths. We should keep in mind,
+ however, that data-path design is crucial in designing real machines.
+
-
- In spite of these disadvantages, we will use this register-machine
- language throughout this chapter, because we will be more concerned with
- understanding controllers than with understanding the elements and
- connections in data paths. We should keep in mind,
- however, that data-path design is crucial in designing real machines.
-
+
+
+ Use the register-machine language to describe the iterative factorial
+ machine of exercise.
+
+
-
-
- Use the register-machine language to describe
- the iterative factorial machine of exercise.
-
-
+
+ Actions
+
-
- Actions
-
+ actions, in register machine|(
+ register machineactions|(
- actions, in register machine|(
- register machineactions|(
-
-
- Let us modify the GCD machine so that we can type in the numbers
- whose GCD we want and get the answer printed at our terminal. We will
- not discuss how to make a machine that can read and print, but will
- assume (as we do when we use readprompt and display in
- SchemeJavaScript) that
- they are available as primitive operations.This assumption
- glosses over a great deal of complexity. Usually a large portion of
- the implementation of a LispJavaScript system is dedicated to making reading
+
+ Let us modify the GCD machine so that we can type in the numbers
+ whose GCD we want and get the answer printed at our terminal. We will
+ not discuss how to make a machine that can read and print, but will
+ assume (as we do when we use
+
+ read
+ prompt
+
+ and display in
+
+ Scheme)
+ JavaScript)
+
+ that they are available as primitive
+ operations.This assumption glosses over a
+ great deal of complexity. Usually a large portion of the implementation of
+ a Lisp
+
+ system
+ system, for example,
+
+ is dedicated to making reading
and printing work.
-
+
-
-
- ReadThe operation read is like the operations we have been using in that it
- produces a value that can be stored in a register. But read
- does not take inputs from any registers; its value depends on
- something that happens outside the parts of the machine we are
- designing. We will allow our machines operations to have such
- behavior, and thus will draw and notate the use of read just as
- we do any other operation that computes a value.
-
+
+
+
+ Read
+ The operation prompt
+
+
+ is like the operations we have been using in that it produces a value that
+ can be stored in a register. But
+
+ read
+
+
+ prompt
+
+
+ does not take inputs from any registers; its value depends on
+ something that happens outside the parts of the machine we are
+ designing. We will allow our machines operations to have such
+ behavior, and thus will draw and notate the use of
+
+ read
+
+
+ prompt
+
+
+ just as we do any other operation that computes a value.
+
-
-
- PrintThe operation print, on the other hand, differs from the operations we have
- been using in a fundamental way: It does not produce an output value
- to be stored in a register. Though it has an effect, this effect is
- not on a part of the machine we are designing. We will refer to this
- kind of operation as an action. We will represent an action in
- a data-path diagram just as we represent an operation that computes a
- valueas a trapezoid that contains the name of the action.
- Arrows point to the action box from any inputs (registers or
- constants). We also associate a button with the action. Pushing the
- button makes the action happen. To make a controller push an action
- register-machine languageperform@perform
-
- button we use a new kind of instruction called perform. Thus,
- the action of printing the contents of register a is represented
- in a controller sequence by the instruction
-
-
+
+
+
+ Print,
+ The operation display,
+
+
+ on the other hand, differs from the operations we have
+ been using in a fundamental way: It does not produce an output value
+ to be stored in a register. Though it has an effect, this effect is
+ not on a part of the machine we are designing. We will refer to this
+ kind of operation as an action. We will represent an action in
+ a data-path diagram just as we represent an operation that computes a
+ valueas a trapezoid that contains the name of the action.
+ Arrows point to the action box from any inputs (registers or
+ constants). We also associate a button with the action. Pushing the
+ button makes the action happen. To make a controller push an action
+ register-machine language
+ perform@perform
+
+ button we use a new kind of instruction called
+ perform. Thus,
+ the action of printing the contents of register
+ a is represented
+ in a controller sequence by the instruction
+
+
(perform (op print) (reg a))
-
-
+
+
perform(list(op("print"), reg("a")))
-
-
-
-
-
-
- Figure shows the data paths and controller for
- the new GCD machine. Instead of having the machine stop after
- printing the answer, we have made it start over, so that it repeatedly
- reads a pair of numbers, computes their GCD, and prints the result.
- This structure is like the driver loops we used in the interpreters of
- chapter4.
+
+
+
-
-
-
-
-
-
+
+
+
+ Figure
+
+
+ Figure
+
+
+ shows the data paths and controller for
+ the new GCD machine. Instead of having the machine stop after
+ printing the answer, we have made it start over, so that it repeatedly
+ reads a pair of numbers, computes their GCD, and prints the result.
+ This structure is like the driver loops we used in the interpreters of
+ chapter4.
+
+
+
+
+
+
(controller
gcd-loop
(assign a (op read))
@@ -337,17 +415,17 @@ perform(list(op("print"), reg("a")))
gcd-done
(perform (op print) (reg a))
(goto (label gcd-loop)))
+
+
+
A GCD machine that reads inputs and prints results.
+
+
-
-
A GCD machine that reads inputs and prints results.
A GCD machine that reads inputs and prints results.
+
+
+
+
+ register machinelanguage for describing|)
+ actions, in register machine|)
+ register machineactions|)
+
+
diff --git a/xml/chapter5/section1/subsection2.xml b/xml/chapter5/section1/subsection2.xml
index f03b3fd97..ab819a715 100644
--- a/xml/chapter5/section1/subsection2.xml
+++ b/xml/chapter5/section1/subsection2.xml
@@ -1,82 +1,93 @@
-
-
- Abstraction in Machine Design
-
+
+
+ Abstraction in Machine Design
+
- abstractionregister@in register-machine design|(
+ abstraction
+ register@in register-machine design|(
-
- We will often define a machine to include primitive operations that are
- actually very complex. For example, in sections and
- we will treat SchemeJavaScripts environment
- manipulations as primitive. Such abstraction is valuable because it
- allows us to ignore the details of parts of a machine so that we can
- concentrate on other aspects of the design. The fact that we have
- swept a lot of complexity under the rug, however, does not mean that a
- machine design is unrealistic. We can always replace the complex
- primitives by simpler primitive operations.
-
+
+ We will often define a machine to include primitive
+ operations that are actually very complex. For example, in
+ sections and
+ we will treat
+
+ Scheme
+ JavaScripts
+
+ environment manipulations as primitive. Such abstraction is valuable
+ because it allows us to ignore the details of parts of a machine so that we
+ can concentrate on other aspects of the design. The fact that we have
+ swept a lot of complexity under the rug, however, does not mean that a
+ machine design is unrealistic. We can always replace the complex
+ primitives by simpler primitive operations.
+
-
- Consider the GCD machine. The machine has an instruction that computes
- the remainder of the contents of registers a and b and
- assigns the result to register t. If we want to construct the
- GCD machine without using a primitive remainder operation,
- we must specify how to compute remainders in terms of simpler
- operations, such as subtraction. Indeed, we can write a
- Scheme procedureJavaScript function
- that finds remainders in this way:
-
-
-
+
+ Consider the GCD machine. The machine has an instruction that computes
+ the remainder of the contents of registers a
+ and b and assigns the result to register
+ t. If we want to construct the GCD machine
+ without using a primitive remainder operation, we must specify how to
+ compute remainders in terms of simpler operations, such as subtraction.
+ Indeed, we can write a
+
+ Scheme procedure
+ JavaScript function
+
+ that finds remainders in this way:
+
+ remainder_example
+
+remainder(29, 5);
+
+
+
+ remainder
+ remainder_example
+
(define (remainder n d)
(if (< n d)
n
(remainder (- n d) d)))
-
-
+
+
function remainder(n, d) {
return n < d
? n
: remainder(n - d, d);
}
-
-
-
-
-
- We can thus replace the remainder operation in the GCD machines
- data paths with a subtraction operation and a comparison test.
- Figure shows the data paths and controller
- for the elaborated machine.
- The instruction
-
-
-
-
-
Data paths and controller for the elaborated GCD machine.
-
+
+
+
-
-
+
+ We can thus replace the remainder operation in the GCD machines
+ data paths with a subtraction operation and a comparison test.
+ Figure shows the data paths and
+ controller for the elaborated machine. The instruction
+
+
+
+
+ Data paths and controller for the elaborated GCD machine.
+
+
+
+
(assign t (op rem) (reg a) (reg b))
-
-
+
+
assign("t", list(op("rem"), reg("a"), reg("b")))
-
-
-
- in the GCD controller definition is replaced by a sequence of
- instructions that contains a loop, as shown in
- figure.
-
-
-
-
-
+
+
+ in the GCD controller definition is replaced by a sequence of
+ instructions that contains a loop, as shown in
+ figure.
+
+
+
+
(controller
test-b
(test (op =) (reg b) (const 0))
@@ -92,8 +103,8 @@ assign("t", list(op("rem"), reg("a"), reg("b")))
(assign b (reg t))
(goto (label test-b))
gcd-done)
-
-
+
+
controller(
"test-b",
list(test(list(op("="), reg("b"), constant(0))),
@@ -109,21 +120,20 @@ controller(
assign("b", reg("t")),
go_to(label("test-b"))),
"gcd-done");
-
-
-
Controller instruction sequence for the GCD machine in
- figure.
-
-
-
+
+
+
Controller instruction sequence for the GCD machine in
+ figure.
+
+
+
-
-
- Design a machine to compute square roots using Newtons method, as
- described in section:
-
-
-
+
+
+ Design a machine to compute square roots using Newtons method, as
+ described in section:
+
+
(define (sqrt x)
(define (good-enough? guess)
(< (abs (- (square guess) x)) 0.001))
@@ -134,32 +144,65 @@ controller(
guess
(sqrt-iter (improve guess))))
(sqrt-iter 1.0))
-
-
+
+
+function square(x) {
+ return x * x;
+}
+
+function average(x,y) {
+ return (x + y) / 2;
+}
+
function sqrt(x) {
- function good_enough(guess, x) {
- return abs(square(guess) - x) < 0.001;
+ function good_enough(guess) {
+ return math_abs(square(guess) - x) < 0.001;
}
- function improve(guess, x) {
+ function improve(guess) {
return average(guess, x / guess);
}
- function sqrt_iter(guess, x) {
- return good_enough(guess, x)
+ function sqrt_iter(guess) {
+ return good_enough(guess)
? guess
- : sqrt_iter(improve(guess, x), x);
+ : sqrt_iter(improve(guess));
}
return sqrt_iter(1.0);
}
-
-
- Begin by assuming that good-enough?good_enough and improve operations
- are available as primitives. Then show how to expand these in terms
- of arithmetic operations. Describe each version of the sqrt
- machine design by drawing a data-path diagram and writing a controller
- definition in the register-machine language.
-
-
- abstractionregister@in register-machine design|)
-
-
+display(sqrt(4)); // Should display close to 2
+display(sqrt(9)); // Should display close to 3
+display(sqrt(100)); // Should display close to 10
+// Change 0.001 above to tune precision
+
+
+function sqrt(x) {
+ function good_enough(guess) {
+ return math_abs(square(guess) - x) < 0.001;
+ }
+ function improve(guess) {
+ return average(guess, x / guess);
+ }
+ function sqrt_iter(guess) {
+ return good_enough(guess)
+ ? guess
+ : sqrt_iter(improve(guess));
+ }
+ return sqrt_iter(1.0);
+}
+
+
+ Begin by assuming that
+
+ good-enough?
+ good_enough
+
+ and improve operations are available as
+ primitives. Then show how to expand these in terms of arithmetic
+ operations. Describe each version of the sqrt
+ machine design by drawing a data-path diagram and writing a controller
+ definition in the register-machine language.
+
+
+ abstraction
+ register@in register-machine design|)
+
diff --git a/xml/chapter5/section1/subsection3.xml b/xml/chapter5/section1/subsection3.xml
index ed3989db3..6e0ab05e9 100644
--- a/xml/chapter5/section1/subsection3.xml
+++ b/xml/chapter5/section1/subsection3.xml
@@ -1,29 +1,31 @@
-
-
- Subroutines
-
+
+
+ Subroutines
+
-
- register machinesubroutine|(
- subroutine in register machine|(
+
+ register machinesubroutine|(
+ subroutine in register machine|(
-
- When designing a machine to perform a computation, we would often
- prefer to arrange for components to be shared by different parts of
- the computation rather than duplicate the components. Consider a
- machine that includes two GCD computationsone that finds the GCD of
- the contents of registers a and b and one that finds the
- GCD of the contents of registers c and d. We might start
- by assuming we have a primitive gcd operation, then expand the
- two instances of gcd in terms of more primitive operations.
- Figure shows just the GCD portions of the
- resulting machines data paths, without showing how they connect to
- the rest of the machine. The figure also shows the corresponding
- portions of the machines controller sequence.
-
-
-
-
+
+ When designing a machine to perform a computation, we would often
+ prefer to arrange for components to be shared by different parts of
+ the computation rather than duplicate the components. Consider a
+ machine that includes two GCD computationsone that finds the GCD of
+ the contents of registers a and
+ b and one that finds the
+ GCD of the contents of registers c and
+ d. We might start
+ by assuming we have a primitive gcd operation,
+ then expand the two instances of gcd in terms
+ of more primitive operations. Figure
+ shows just the GCD portions of the resulting machines data paths,
+ without showing how they connect to the rest of the machine. The figure
+ also shows the corresponding portions of the machines controller
+ sequence.
+
+
+
gcd-1
(test (op =) (reg b) (const 0))
(branch (label after-gcd-1))
@@ -41,8 +43,8 @@
(assign d (reg s))
(goto (label gcd-2))
after-gcd-2
-
-
+
+
list(...,
"gcd-1",
test(list(op("="), reg("b"), constant(0))),
@@ -62,52 +64,54 @@ list(...,
go_to(label("gcd-2")),
"after-gcd-2",
...
-
-
-
-
Portions of the data paths and controller sequence for
- a machine with two GCD computations.
-
-
-
-
-
- This machine has two remainder operation boxes and two boxes for
- testing equality. If the duplicated components are complicated, as is the
- remainder box, this will not be an economical way to build the
- machine. We can avoid duplicating the data-path components by using
- the same components for both GCD computations, provided that doing so
- will not affect the rest of the larger machines computation. If the
- values in registers a and b are not needed by the time the
- controller gets to gcd-2 (or if these values can be moved to
- other registers for safekeeping), we can change the machine so that
- it uses registers a and b, rather than registers c
- and d, in computing the second GCD as well as the first. If we
- do this, we obtain the controller sequence shown in
- figure.
-
+
+
+
Portions of the data paths and controller sequence for
+ a machine with two GCD computations.
+
+
+
-
- We have removed the duplicate data-path components
- (so that the data paths are again as in figure),
- but the controller
- now has two GCD sequences that differ only in their entry-point
- labels. It would be better to replace these two sequences by branches
- to a single sequencea gcdsubroutineat the end of
- which we branch back to the correct place in the main instruction
- sequence. We can accomplish this as follows: Before branching to gcd, we place a distinguishing value (such as 0 or1) into a special
- register,
-
- continue. At the end of the gcd subroutine we
- return either to after-gcd-1 or to after-gcd-2, depending
- on the value of the continue register.
- Figure shows the relevant portion of the
- resulting controller sequence, which includes only a single copy of the
- gcd instructions.
+
+ This machine has two remainder operation boxes and two boxes for
+ testing equality. If the duplicated components are complicated, as is the
+ remainder box, this will not be an economical way to build the
+ machine. We can avoid duplicating the data-path components by using
+ the same components for both GCD computations, provided that doing so
+ will not affect the rest of the larger machines computation. If the
+ values in registers a and
+ b are not needed by the time the
+ controller gets to gcd-2 (or if these values
+ can be moved to other registers for safekeeping), we can change the machine
+ so that it uses registers a and
+ b, rather than registers
+ c and d, in
+ computing the second GCD as well as the first. If we do this, we obtain the
+ controller sequence shown in
+ figure.
+
-
-
-
+
+ We have removed the duplicate data-path components (so that the data paths
+ are again as in figure), but the
+ controller now has two GCD sequences that differ only in their entry-point
+ labels. It would be better to replace these two sequences by branches to a
+ single sequencea gcd
+ subroutineat the end of which we branch back to the
+ correct place in the main instruction sequence. We can accomplish this as
+ follows: Before branching to gcd, we place a
+ distinguishing value (such as 0 or1) into a special register,
+
+ continue. At the end of the
+ gcd subroutine we return either to
+ after-gcd-1 or to after-gcd-2, depending
+ on the value of the continue register.
+ Figure shows the relevant portion
+ of the resulting controller sequence, which includes only a single copy of
+ the gcd instructions.
+
+
+
gcd-1
(test (op =) (reg b) (const 0))
(branch (label after-gcd-1))
@@ -125,8 +129,8 @@ list(...,
(assign b (reg t))
(goto (label gcd-2))
after-gcd-2
-
-
+
+
list(...,
"gcd-1",
test(list(op("="), reg("b"), constant(0))),
@@ -146,17 +150,16 @@ list(...,
go_to(label("gcd-2")),
"after-gcd-2",
...
-
-
-
Portions of the controller sequence for a machine that
- uses the same data-path components for two different GCD
- computations.
-
-
-
-
-
-
+
+
+
Portions of the controller sequence for a machine that
+ uses the same data-path components for two different GCD
+ computations.
+
+
+
+
+
gcd
(test (op =) (reg b) (const 0))
(branch (label gcd-done))
@@ -169,18 +172,18 @@ list(...,
(branch (label after-gcd-1))
(goto (label after-gcd-2))
$\vdots$
- ;; Before branching to gcd from the first place where
+ ;; Before branching to gcd from the first place where;; it is needed, we place $0$ in the continue register
(assign continue (const 0))
(goto (label gcd))
after-gcd-1
$\vdots$
- ;; Before the second use of gcd, we place $1$ in the continue register
+ ;; Before the second use of gcd, we place $1$ in the continue register
(assign continue (const 1))
(goto (label gcd))
after-gcd-2
-
-
+
+
list(...,
"gcd",
test(list(op("="), reg("b"), constant(0))),
@@ -206,16 +209,18 @@ list(...,
go_to(label("gcd")),
"after-gcd-2",
...)
-
-
-
Using a continue register to avoid
- the duplicate controller sequence in figure.
-
-
-
-
-
-
+
+
+
+ Using a continue register to avoid
+ the duplicate controller sequence in
+ figure.
+
Assigning labels to the continue register simplifies
- and generalizes the strategy shown in figure.
-
-
-
-
-
- This is a reasonable approach for handling small problems, but it
- would be awkward if there were many instances of GCD computations in
- the controller sequence. To decide where to continue executing after
- the gcd subroutine, we would need tests in the data paths and
- branch instructions in the controller for all the places that use gcd.
- A more powerful method for implementing subroutines is to have
- the continue register hold the label of the entry point in the
- controller sequence at which execution should continue when the subroutine is finished.
- Implementing this strategy requires a new
- kind of connection between the data paths and the controller of a
- register machine: There must be a way to assign to a register a label
- in the controller sequence in such a way that this value can be fetched
- from the register and used to continue execution at the designated
- entry point.
-
+
+
+
+ Assigning labels to the continue register
+ simplifies and generalizes the strategy shown in
+ figure.
+
+
+
+
-
-
-
- To reflect this ability, we will extend the assign
- instruction of the register-machine language to allow a register to be
- assigned as value a label from the controller sequence (as a special
- kind of constant). We will also extend the gotogo_to instruction to
- allow execution to continue at the entry point described by the
- contents of a register rather than only at an entry point described by
- a constant label. Using these new constructs we can terminate the
- gcd subroutine with a branch to the location stored in the continue register. This leads to the controller sequence shown in
- figure.
-
+
+ This is a reasonable approach for handling small problems, but it would be
+ awkward if there were many instances of GCD computations in the controller
+ sequence. To decide where to continue executing after the
+ gcd subroutine, we would need tests in the data
+ paths and branch instructions in the controller for all the places that use
+ gcd. A more powerful method for implementing
+ subroutines is to have the continue register
+ hold the label of the entry point in the controller sequence at which
+ execution should continue when the subroutine is finished. Implementing this
+ strategy requires a new kind of connection between the data paths and the
+ controller of a register machine: There must be a way to assign to a
+ register a label in the controller sequence in such a way that this value
+ can be fetched from the register and used to continue execution at the
+ designated entry point.
+
-
- A machine with more than one subroutine could use multiple
- continuation registers (e.g., gcd-continue, factorial-continue) or we could have all subroutines share a single
- continue register. Sharing is more economical, but we must be
- careful if we have a subroutine (sub1) that calls another
- subroutine (sub2). Unless sub1 saves the contents of continue in some other register before setting up continue for
- the call to sub2, sub1 will not know where to go when it
- is finished. The mechanism developed in the next section to handle
- recursion also provides a better solution to this problem of nested
- subroutine calls.
- register machinesubroutine|)
- subroutine in register machine|)
-
+
+
+
+ To reflect this ability, we will extend the
+ assign
+ instruction of the register-machine language to allow a register to be
+ assigned as value a label from the controller sequence (as a special
+ kind of constant). We will also extend the
+
+ goto
+ go_to
+
+ instruction to allow execution to continue at the entry point described by
+ the contents of a register rather than only at an entry point described by
+ a constant label. Using these new constructs we can terminate the
+ gcd subroutine with a branch to the location
+ stored in the continue register. This leads
+ to the controller sequence shown in
+ figure.
+
-
+
+ A machine with more than one subroutine could use multiple
+ continuation registers (e.g., gcd-continue,
+ factorial-continue) or we could have all
+ subroutines share a single
+ continue register. Sharing is more economical,
+ but we must be careful if we have a subroutine
+ (sub1) that calls another subroutine
+ (sub2). Unless
+ sub1 saves the contents of
+ continue in some other register before setting
+ up continue for the call to
+ sub2, sub1 will
+ not know where to go when it is finished. The mechanism developed in the
+ next section to handle recursion also provides a better solution to this
+ problem of nested subroutine calls.
+ register machinesubroutine|)
+ subroutine in register machine|)
+
+
diff --git a/xml/chapter5/section1/subsection4.xml b/xml/chapter5/section1/subsection4.xml
index 60cb6ed52..55fa7cf92 100644
--- a/xml/chapter5/section1/subsection4.xml
+++ b/xml/chapter5/section1/subsection4.xml
@@ -1,235 +1,319 @@
-
-
- Using a Stack to Implement Recursion
-
+
+
+ Using a Stack to Implement Recursion
+
-
- stackrecursion@for recursion in register machine|(
- register machinestack|(
- recursive processregister machine for|(
+
+ stack
+ recursion@for recursion in register machine|(
+ register machinestack|(
+ recursive processregister machine for|(
-
- iterative processregister machine for
- With the ideas illustrated so far, we can implement any iterative
- process by specifying a register machine that has a register
- corresponding to each state variable of the process. The machine
- repeatedly executes a controller loop, changing the contents
- of the registers, until some termination condition is satisfied. At
- each point in the controller sequence, the state of the machine
- (representing the state of the iterative process) is completely
- determined by the contents of the registers (the values of the state
- variables).
-
+
+ iterative processregister machine for
+ With the ideas illustrated so far, we can implement any iterative
+ process by specifying a register machine that has a register
+ corresponding to each state variable of the process. The machine
+ repeatedly executes a controller loop, changing the contents
+ of the registers, until some termination condition is satisfied. At
+ each point in the controller sequence, the state of the machine
+ (representing the state of the iterative process) is completely
+ determined by the contents of the registers (the values of the state
+ variables).
+
-
- recursive processiterative process vs.
- iterative processrecursive process vs.
-
- Implementing recursive processes, however, requires an additional
- mechanism. Consider the following recursive method for computing
- factorials, which we first examined in
- section:
-
-
-
+
+ recursive processiterative process vs.
+ iterative processrecursive process vs.
+
+ Implementing recursive processes, however, requires an additional
+ mechanism. Consider the following recursive method for computing
+ factorials, which we first examined in
+ section:
+
+ factorial_5_1_4
+ factorial_example
+
(define (factorial n)
(if (= n 1)
1
(* (factorial (- n 1)) n)))
-
-
+
+
function factorial(n) {
return n === 1
? 1
: n * factorial(n - 1);
}
-
-
-
-
-
- As we see from the
- procedurefunction, computing $n!$ requires computing
- $(n-1)!$. Our GCD machine, modeled on the
- procedurefunction
+
+
+
-
-
+
+ As we see from the
+
+ procedure,
+ function,
+
+ computing $n!$ requires computing
+ $(n-1)!$. Our GCD machine, modeled on the
+
+ procedure
+ function
+
+
+ gcd_5_1_4_example
+ 4
+
+(gcd 20 12)
+
+
+gcd(20, 12);
+
+
+
+ gcd_5_1_4
+ gcd_5_1_4_example
+
(define (gcd a b)
(if (= b 0)
a
(gcd b (remainder a b))))
-
-
+
+
function gcd(a, b) {
return b === 0 ? a : gcd(b, a % b);
}
-
-
+
+
- similarly had to compute another GCD. But there is an important
- difference between the gcd
- procedurefunction, which reduces the original
- computation to a new GCD computation, and factorial, which
- requires computing another factorial as a subproblem. In GCD, the
- answer to the new GCD computation is the answer to the original
- problem. To compute the next GCD, we simply place the new arguments
- in the input registers of the GCD machine and reuse the
- machines data paths by executing the same controller sequence. When
- the machine is finished solving the final GCD problem, it has
- completed the entire computation.
-
+ similarly had to compute another GCD. But there is an important
+ difference between the gcd
+
+ procedure,
+ function,
+
+ which reduces the original computation to a new GCD computation, and
+ factorial, which requires computing another
+ factorial as a subproblem. In GCD, the answer to the new GCD computation is
+ the answer to the original problem. To compute the next GCD, we simply
+ place the new arguments in the input registers of the GCD machine and reuse
+ the machines data paths by executing the same controller sequence.
+ When the machine is finished solving the final GCD problem, it has completed
+ the entire computation.
+
-
- In the case of factorial (or any recursive process) the answer to the
- new factorial subproblem is not the answer to the original problem.
- The value obtained for $(n-1)!$ must be multiplied by $n$ to get the
- final answer. If we try to imitate the GCD design, and solve
- the factorial subproblem by decrementing the n register and
- rerunning the factorial machine, we will no longer have available the
- old value of n by which to multiply the result. We thus need a
- second factorial machine to work on the subproblem. This second
- factorial computation itself has a factorial subproblem, which
- requires a third factorial machine, and so on. Since each factorial
- machine contains another factorial machine within it, the total
- machine contains an infinite nest of similar machines and hence cannot
- be constructed from a fixed, finite number of parts.
-
+
+ In the case of factorial (or any recursive process) the answer to the
+ new factorial subproblem is not the answer to the original problem.
+ The value obtained for $(n-1)!$ must be
+ multiplied by $n$ to get the final answer. If
+ we try to imitate the GCD design, and solve the factorial subproblem by
+ decrementing the n register and rerunning the
+ factorial machine, we will no longer have available the old value of
+ n by which to multiply the result. We thus
+ need a second factorial machine to work on the subproblem. This second
+ factorial computation itself has a factorial subproblem, which
+ requires a third factorial machine, and so on. Since each factorial
+ machine contains another factorial machine within it, the total
+ machine contains an infinite nest of similar machines and hence cannot
+ be constructed from a fixed, finite number of parts.
+
-
- Nevertheless, we can implement the factorial process as a register
- machine if we can arrange to use the same components for each nested
- instance of the machine. Specifically, the machine that computes $n!$
- should use the same components to work on the subproblem of computing
- $(n-1)!$, on the subproblem for $(n-2)!$, and so on. This is
- plausible because, although the factorial process dictates that an
- unbounded number of copies of the same machine are needed to perform a
- computation, only one of these copies needs to be active at any given
- time. When the machine encounters a recursive subproblem, it can
- suspend work on the main problem, reuse the same physical parts to
- work on the subproblem, then continue the suspended computation.
-
+
+ Nevertheless, we can implement the factorial process as a register
+ machine if we can arrange to use the same components for each nested
+ instance of the machine. Specifically, the machine that computes
+ $n!$
+ should use the same components to work on the subproblem of computing
+ $(n-1)!$, on the subproblem for
+ $(n-2)!$, and so on. This is
+ plausible because, although the factorial process dictates that an
+ unbounded number of copies of the same machine are needed to perform a
+ computation, only one of these copies needs to be active at any given
+ time. When the machine encounters a recursive subproblem, it can
+ suspend work on the main problem, reuse the same physical parts to
+ work on the subproblem, then continue the suspended computation.
+
-
- In the subproblem, the contents of the registers will be different
- than they were in the main problem. (In this case the n register
- is decremented.) In order to be able to continue the suspended
- computation, the machine must save the contents of any registers that
- will be needed after the subproblem is solved so that these can be
- restored to continue the suspended computation. In the case of
- factorial, we will save the old value of n, to be restored when
- we are finished computing the factorial of the decremented n
- register.One might argue that we dont need to save the old
- n; after we decrement it and solve the subproblem, we could
- simply increment it to recover the old value. Although this strategy
- works for factorial, it cannot work in general, since the old value of
- a register cannot always be computed from the new one.
-
+
+ In the subproblem, the contents of the registers will be different
+ than they were in the main problem. (In this case the
+ n register is decremented.) In order to be
+ able to continue the suspended computation, the machine must save the
+ contents of any registers that will be needed after the subproblem is
+ solved so that these can be restored to continue the suspended computation.
+ In the case of factorial, we will save the old value of
+ n, to be restored when we are finished
+ computing the factorial of the decremented n
+ register.One might argue that we dont need to save the old
+ n; after we decrement it and solve the
+ subproblem, we could simply increment it to recover the old value. Although
+ this strategy works for factorial, it cannot work in general, since the old
+ value of a register cannot always be computed from the new one.
+
-
- Since there is no a priori limit on the depth of nested
- recursive calls, we may need to save an arbitrary number of register
- values. These values must be restored in the reverse of the order in
- which they were saved, since in a nest of recursions the last
- subproblem to be entered is the first to be finished. This dictates
- the use of a stack, or last in, first out data structure, to
- save register values. We can extend the register-machine language to
- include a stack by adding two kinds of instructions: Values are placed
- register-machine languagesave@save
-
- register-machine languagerestore@restore
-
- on the stack using a save instruction and restored from the
- stack using a restore instruction. After a sequence of values
- has been saved on the stack, a sequence of restores will
- retrieve these values in reverse order.In
- section we will see how to implement a
- stack in terms of more primitive operations.
-
+
+ Since there is no a priori limit on the depth of nested
+ recursive calls, we may need to save an arbitrary number of register
+ values. These values must be restored in the reverse of the order in
+ which they were saved, since in a nest of recursions the last
+ subproblem to be entered is the first to be finished. This dictates
+ the use of a stack, or last in, first out data
+ structure, to save register values. We can extend the register-machine
+ language to include a stack by adding two kinds of instructions: Values are
+ placed
+ register-machine language
+ save@save
+
+ register-machine language
+ restore@restore
+
+ on the stack using a save instruction and
+ restored from the stack using a restore
+ instruction. After a sequence of values has been
+ saved on the stack, a sequence of
+ restores will retrieve these values in reverse
+ order.In section we
+ will see how to implement a stack in terms of more primitive
+ operations.
+
-
- With the aid of the stack, we can reuse a single copy of the factorial
- machines data paths for each factorial subproblem. There is a
- similar design issue in reusing the controller sequence that operates
- the data paths. To reexecute the factorial computation, the
- controller cannot simply loop back to the beginning, as with
- an iterative process, because after solving the $(n-1)!$ subproblem
- the machine must still multiply the result by $n$. The controller
- must suspend its computation of $n!$, solve the $(n-1)!$ subproblem,
- then continue its computation of $n!$. This view of the factorial
- computation suggests the use of the subroutine mechanism described in
- section, which has the controller use a
-
- continue register to transfer to the part of the sequence that
- solves a subproblem and then continue where it left off on the main
- problem. We can thus make a factorial subroutine that returns to the
- entry point stored in the continue register. Around each subroutine
- call, register, since each level of the factorial computation will use
- the same continue register. That is, the factorial subroutine
- must put a new value in continue when it calls itself for a
- subproblem, but it will need the old value in order to return to the
- place that called it to solve a subproblem.
-
+
+ With the aid of the stack, we can reuse a single copy of the factorial
+ machines data paths for each factorial subproblem. There is a
+ similar design issue in reusing the controller sequence that operates
+ the data paths. To reexecute the factorial computation, the
+ controller cannot simply loop back to the beginning, as with
+ an iterative process, because after solving the
+ $(n-1)!$ subproblem
+ the machine must still multiply the result by
+ $n$. The controller
+ must suspend its computation of $n!$, solve the
+ $(n-1)!$ subproblem,
+ then continue its computation of $n!$. This
+ view of the factorial computation suggests the use of the subroutine
+ mechanism described in section, which
+ has the controller use a
+
+ continue register to transfer to the part of
+ the sequence that solves a subproblem and then continue where it left off
+ on the main problem. We can thus make a factorial subroutine that returns
+ to the entry point stored in the continue
+ register. Around each subroutine call, register, since each
+ level of the factorial computation will use the same
+ continue register. That is, the factorial
+ subroutine must put a new value in continue
+ when it calls itself for a subproblem, but it will need the old value in
+ order to return to the place that called it to solve a subproblem.
+
-
- Figure shows the data paths and controller for
- a machine that implements the recursive factorial
- procedurefunction.
- The machine has a stack and three registers, called n, val, and continue. To simplify the data-path diagram, we have
- not named the register-assignment buttons, only the stack-operation
- buttons (sc and sn to save registers, rc and rn to restore registers). To operate the machine, we put in register
- n the number whose factorial we wish to compute and start the
- machine. When the machine reaches fact-done, the computation is
- finished and the answer will be found in the val register. In
- the controller sequence, n and continue are saved before
- each recursive call and restored upon return from the call. Returning
- from a call is accomplished by branching to the location stored in
- continue. Continue is initialized when the machine starts
- so that the last return will go to fact-done. The val
- register, which holds the result of the factorial computation, is not
- saved before the recursive call, because the old contents of val
- is not useful after the subroutine returns. Only the new value, which
- is the value produced by the subcomputation, is needed.
-
-
+
+
+
+ Figure
+
+
+ Figure
+
+
+ shows the data paths and controller for
+ a machine that implements the recursive
+ factorial
+
+ procedure.
+ function.
+
+ The machine has a stack and three registers, called
+ n, val, and
+ continue. To simplify the data-path diagram,
+ we have not named the register-assignment buttons, only the stack-operation
+ buttons (sc and sn
+ to save registers, rc and
+ rn to restore registers). To operate the
+ machine, we put in register n the number whose
+ factorial we wish to compute and start the machine. When the machine
+ reaches fact-done, the computation is finished
+ and the answer will be found in the val
+ register. In the controller sequence, n and
+ continue are saved before each recursive call
+ and restored upon return from the call. Returning from a call is
+ accomplished by branching to the location stored in
+ continue.
+
+ Continue
+
+ The register
+ continue
+
+
+ is initialized when the machine starts so that the last return will go to
+ fact-done. The
+ val
+ register, which holds the result of the factorial computation, is not
+ saved before the recursive call, because the old contents of
+ val is not useful after the subroutine returns.
+ Only the new value, which is the value produced by the subcomputation, is
+ needed.
+
+
-
- Although in principle the factorial computation requires an infinite
- machine, the machine in Figure is actually
- finite except for the stack, which is potentially unbounded. Any
- particular physical implementation of a stack, however, will be of
- finite size, and this will limit the depth of recursive calls that can
- be handled by the machine. This implementation of factorial
- illustrates the general strategy for realizing recursive algorithms as
- ordinary register machines augmented by stacks. When a recursive
- subproblem is encountered, we save on the stack the registers whose
- current values will be required after the subproblem is solved, solve
- the recursive subproblem, then restore the saved registers and
- continue execution on the main problem. The continue register
- must always be saved. Whether there are other registers that need to
- be saved depends on the particular machine, since not all recursive
- computations need the original values of registers that are modified
- during solution of the subproblem (see exercise).
-
+
+ Although in principle the factorial computation requires an infinite
+ machine, the machine in
+
+
+ figure
+
+
+ figure
+
+
+ is actually finite except for the stack, which is potentially unbounded. Any
+ particular physical implementation of a stack, however, will be of finite
+ size, and this will limit the depth of recursive calls that can be handled
+ by the machine. This implementation of factorial illustrates the general
+ strategy for realizing recursive algorithms as ordinary register machines
+ augmented by stacks. When a recursive subproblem is encountered, we save on
+ the stack the registers whose current values will be required after the
+ subproblem is solved, solve the recursive subproblem, then restore the saved
+ registers and continue execution on the main problem. The
+ continue register must always be saved.
+ Whether there are other registers that need to be saved depends on the
+ particular machine, since not all recursive computations need the original
+ values of registers that are modified during solution of the subproblem
+ (see exercise).
+
+
+ A double recursion
+
-
- A double recursion
-
-
-
-
- Let us examine a more complex recursive process, the tree-recursive
- computation of the Fibonacci numbers, which we introduced in
- section:
-
-
+
+
+ Let us examine a more complex recursive process, the tree-recursive
+ computation of the Fibonacci numbers, which we introduced in
+ section:
+
+ fib_5_1_4_example
+ 8
+
+(fib 6)
+
+
+fib(6);
+
+
+
+ fib_5_1_4
+ fib_5_1_4_example
+
(define (fib n)
(if (< n 2)
n
(+ (fib (- n 1)) (fib (- n 2)))))
-
-
+
+
function fib(n) {
return n === 0
? 0
@@ -237,43 +321,47 @@ function fib(n) {
? 1
: fib(n - 1) + fib(n - 2);
}
-
-
-
-
-
- Just as with factorial, we can implement the recursive Fibonacci
- computation as a register machine with registers n, val,
- and continue. The machine is more complex than the one for
- factorial, because there are two places in the controller sequence
- where we need to perform recursive callsonce to compute Fib$(n-1)$
- and once to compute Fib$(n-2)$. To set up for each of these calls, we
- save the registers whose values will be needed later, set the n
- register to the number whose Fib we need to compute recursively ($n-1$
- or $n-2$), and assign to continue the entry point in the main sequence
- to which to return (afterfib-n-1 or afterfib-n-2,
- respectively). We then go to fib-loop. When we return from the
- recursive call, the answer is in val.
- Figure shows the controller sequence for this
- machine.
+
+
+
-
-
-
-
-
+
+ Just as with factorial, we can implement the recursive Fibonacci
+ computation as a register machine with registers
+ n, val,
+ and continue. The machine is more complex than
+ the one for factorial, because there are two places in the controller
+ sequence where we need to perform recursive callsonce to compute
+ Fib$(n-1)$ and once to compute
+ Fib$(n-2)$. To set up for each of these calls,
+ we save the registers whose values will be needed later, set the
+ n
+ register to the number whose Fib we need to compute recursively
+ ($n-1$ or $n-2$), and
+ assign to continue the entry point in the main
+ sequence to which to return (afterfib-n-1 or
+ afterfib-n-2, respectively). We then go to
+ fib-loop. When we return from the
+ recursive call, the answer is in val.
+ Figure shows the controller sequence
+ for this machine.
+
+
+
+
+
(controller
- (assign continue (label fact-done)) ; set up final return address
+ (assign continue (label fact-done)) ; set up final return address
fact-loop
(test (op =) (reg n) (const 1))
(branch (label base-case))
- ;; Set up for the recursive call by saving n and continue.
- ;; Set up continue so that the computation will continue
- ;; at after-fact when the subroutine returns.
+ ;; Set up for the recursive call by saving n and continue.
+ ;; Set up continue so that the computation will continue
+ ;; at after-fact when the subroutine returns.
(save continue)
(save n)
(assign n (op -) (reg n) (const 1))
@@ -288,18 +376,18 @@ function fib(n) {
(assign val (const 1)) ; ^base case: $1!=1$^
(goto (reg continue)) ; ^return to caller^
fact-done)
-
-
-
A recursive factorial machine.
-
-
-
-
-
-
-
-
-
+
+
+
A recursive factorial machine.
+
+
+
+
+
+
+
+
+
controller(
list(
assign("continue", label("fact-done")), // set up final return address
@@ -324,22 +412,18 @@ controller(
assign("val", constant(1)), // base case: val = 1
go_to(reg("continue")), // return to caller
"fact-done"));
-
-
-
Controller for a machine to compute Fibonacci numbers.
-
-
-
-
-
- Specify register machines that implement each of the following
- proceduresfunctions. For each machine, write a controller instruction sequence
- and draw a diagram showing the data paths.
-
-
- Recursive exponentiation:
+
+
+
Controller for a machine to compute Fibonacci numbers.
+
+
+
-
-
-
+
+
+ Specify register machines that implement each of the following
+
+ procedures.
+ functions.
+
+ For each machine, write a controller instruction sequence
+ and draw a diagram showing the data paths.
+
+
+ Recursive exponentiation:
+
+
+ expt_5_1_4
+ expt_5_1_4_example
+
(define (expt b n)
(if (= n 0)
1
(* b (expt b (- n 1)))))
-
-
+
+
function expt(b, n) {
return n === 0
? 1
: b * expt(b, n - 1);
}
-
-
-
-
-
-
-
- Hand-simulate the factorial and Fibonacci machines, using some
- nontrivial input (requiring execution of at least one recursive call).
- Show the contents of the stack at each significant point in the
- execution.
-
-
+
+
+
+
+
-
- Ben Bitdiddle observes that the Fibonacci machines controller sequence
- has an extra save and an extra restore, which can be
- removed to make a faster machine. Where are these instructions?
-
- stackrecursion@for recursion in register machine|)
- register machinestack|)
- recursive processregister machine for|)
+
+ Hand-simulate the factorial and Fibonacci machines, using some
+ nontrivial input (requiring execution of at least one recursive call).
+ Show the contents of the stack at each significant point in the
+ execution.
+
+
-
+
+ Ben Bitdiddle observes that the Fibonacci machines controller sequence
+ has an extra save and an extra
+ restore, which can be removed to make a faster
+ machine. Where are these instructions?
+
+ stack
+ recursion@for recursion in register machine|)
+ register machinestack|)
+ recursive processregister machine for|)
+
diff --git a/xml/chapter5/section1/subsection5.xml b/xml/chapter5/section1/subsection5.xml
index 62314a1b1..cad94505e 100644
--- a/xml/chapter5/section1/subsection5.xml
+++ b/xml/chapter5/section1/subsection5.xml
@@ -1,32 +1,53 @@
-
-
- Instruction Summary
-
-
-
- register-machine languageinstructions
-
-
- register-machine languagereg@reg
- register-machine languageconst@const
- A controller instruction in our register-machine language
- has one of the following forms, where each
- $\textit{input}_i$ is either (reg ^register-name^)reg($\textit{register-name}$)
- or (const ^constant-value^)constant($\textit{constant-value}$).
-
-
-
- These instructions were introduced in
- section:
- register-machine languageassign@assign
- register-machine languageop@op
- register-machine languageperform@perform
- register-machine languagetest@test
- register-machine languagebranch@branch
- register-machine languagelabel@label
- register-machine languagegoto@goto
-
-
+
+
+ Instruction Summary
+
+
+
+ register-machine languageinstructions
+
+
+ register-machine language
+ reg@reg
+ register-machine language
+ const@const
+ A controller instruction in our register-machine language
+ has one of the following forms, where each
+ $\textit{input}_i$ is either
+
+ (reg ^register-name^)
+
+ reg($\textit{register-name}$)
+
+
+ or
+
+ (const ^constant-value^).
+
+ constant($\textit{constant-value}$).
+
+
+
+
+
+ These instructions were introduced in
+ section:
+ register-machine language
+ assign@assign
+ register-machine language
+ op@op
+ register-machine language
+ perform@perform
+ register-machine language
+ test@test
+ register-machine language
+ branch@branch
+ register-machine language
+ label@label
+ register-machine language
+ goto@goto
+
+
(assign ^register-name^ (reg ^register-name^))
(assign ^register-name^ (const ^constant-value^))
@@ -40,8 +61,8 @@
(branch (label ^label-name^))
(goto (label ^label-name^))
-
-
+
+
assign($\textit{register-name}$, reg($\textit{register-name}$))
assign($\textit{register-name}$, constant($\textit{constant-value}$))
@@ -55,58 +76,87 @@ test(list(op($\textit{operation-name}$), $\textit{input}_{1}$, $\ldots$, $\texti
branch(label($\textit{label-name}$))
go_to(label($\textit{label-name}$))
-
-
-
-
-
- The use of registers to hold labels was introduced in
- section:
-
-
+
+
+
+
+
+ The use of registers to hold labels was introduced in
+ section:
+
+
(assign ^register-name^ (label ^label-name^))
(goto (reg ^register-name^))
-
-
+
+
assign($\textit{register-name}$, label($\textit{label-name}$))
go_to(reg($\textit{register-name}$))
-
-
-
-
-
- Instructions to use the stack were introduced in
- section:
- register-machine languagesave@save
- register-machine languagerestore@restore
-
-
+
+
+
+
+
+ Instructions to use the stack were introduced in
+ section:
+ register-machine language
+ save@save
+ register-machine language
+ restore@restore
+
+
(save ^register-name^)
(restore ^register-name^)
-
-
+
+
save($\textit{register-name}$)
restore($\textit{register-name}$)
-
-
-
-
-
- register-machine languageconst@const
-
- constant@constant, specifying in register machine
- The only kind of constant-value we have seen so far is a number,
- but later we will use strings, symbols, and lists.
- For example,
- (const"abc")constant("abc") is the string "abc",
- (constabc) is the symbol abc,
- (const(a b c))constant(list(a, b, c)) is the list (a b c)list(a, b, c),
- and (const())constant(null) is the empty list.
- register machinedesign of|)
-
-
-
+
+
+
+
+
+ register-machine language
+ const@const
+
+ constant@constant, specifying in register machine
+ The only kind of constant-value we have seen so
+ far is a number, but later we will use strings, symbols, and lists.
+ For example,
+
+
+ (constabc) is the symbol
+ abc,
+
+
+ constant("abc")
+ is the string "abc",
+
+
+
+
+ (const(a b c))
+
+
+ constant(list(a, b, c))
+
+
+ is the list
+
+ (a b c),
+ list(a, b, c),
+
+
+ and
+
+ (const())
+
+ constant(null)
+
+ is the empty list.
+ register machinedesign of|)
+
+
diff --git a/xml/chapter5/section2/section2.xml b/xml/chapter5/section2/section2.xml
index 8e07d1fe3..5c65dae7e 100644
--- a/xml/chapter5/section2/section2.xml
+++ b/xml/chapter5/section2/section2.xml
@@ -1,104 +1,130 @@
- A Register-Machine Simulator
-
+ A Register-Machine Simulator
-
- register machinesimulator|(
- register-machine simulator|(
+
-
- In order to gain a good understanding of the design of register
- machines, we must test the machines we design to see if they perform
- as expected. One way to test a design is to hand-simulate the
- operation of the controller, as in exercise. But this is
- extremely tedious for all but the simplest machines. In this section
- we construct a simulator for machines described in the
- register-machine language. The simulator is a SchemeSource program with
- four interface
- proceduresfunctions. The first uses a description of a register
- machine to construct a model of the machine (a data structure whose
- parts correspond to the parts of the machine to be simulated), and the
- other three allow us to simulate the machine by manipulating the
- model:
+
+ register machinesimulator|(
+ register-machine simulator|(
-
-
-
+
+ In order to gain a good understanding of the design of register machines,
+ we must test the machines we design to see if they perform as expected.
+ One way to test a design is to hand-simulate the operation of the
+ controller, as in exercise. But this is
+ extremely tedious for all but the simplest machines. In this section we
+ construct a simulator for machines described in the register-machine
+ language. The simulator is a
- (make-machine register-names operations controller)
-
- make_machine(
- $\textit{register-names}\texttt{,}$
- $\textit{operations}\texttt{,}$
- $\textit{controller}$
- );
-
+ Scheme
+ JavaScript
-
-
- constructs and returns a model of the machine with the given
- registers, operations, and controller.
-
-
-
+ program with
+ four interface
- (set_register_contents machine-model register-name value)
-
- set_register_contents(
- $\textit{machine-model}\texttt{,}$
- $\textit{register-name}\texttt{,}$
- $\textit{value}$
- );
-
+ procedures.
+ functions.
-
-
- stores a value in a simulated register in the given machine.
-
-
-
+ The first uses a description of a register
+ machine to construct a model of the machine (a data structure whose
+ parts correspond to the parts of the machine to be simulated), and the
+ other three allow us to simulate the machine by manipulating the
+ model:
+
+
+
+
+ (make-machine register-names operations controller)
+
+ make_machine(
+ $\textit{register-names}\texttt{,}$
+ $\textit{operations}\texttt{,}$
+ $\textit{controller}$
+ );
+
+
+
+ constructs and returns a model of the machine with the given
+ registers, operations, and controller.
+
+
+
+
+ (set_register_contents machine-model register-name value)
+
+ set_register_contents(
+ $\textit{machine-model}\texttt{,}$
+ $\textit{register-name}\texttt{,}$
+ $\textit{value}$
+ );
+
+
+
+ stores a value in a simulated register in the given machine.
+
+
+
+
+ (get-register-contents machine-model, register-name)
+
+ get_register_contents(
+ $\textit{machine-model}\texttt{,}$
+ $\textit{register-name}$
+ );
+
+
+
+ returns the contents of a simulated register in the given machine.
+
+
+
+
+ (start machine-model)
+
+ start(
+ $\textit{machine-model}$
+ );
+
+
+
+ simulates the execution of the given machine, starting from the
+ beginning of the controller sequence and stopping when it reaches the
+ end of the sequence.
+
+
+
+
+
+ As an example of how these
- (get-register-contents machine-model, register-name)
-
- get_register_contents(
- $\textit{machine-model}\texttt{,}$
- $\textit{register-name}$
- );
-
+ procedures
+ functions
-
-
- returns the contents of a simulated register in the given machine.
-
-
-
+ are used, we can define
- (start machine-model)
-
- start(
- $\textit{machine-model}$
- );
+ gcd-machine
+ gcd_machine
-
-
- simulates the execution of the given
- machine, starting from the beginning of the controller sequence and
- stopping when it reaches the end of the sequence.
-
-
-
-
-
- As an example of how these
- proceduresfunctions
- are used, we can define
- gcd-machinegcd_machine() to be a model of the GCD machine
- of section as follows:
-
-
-
+ to be a model of the GCD machine
+ of section as follows:
+
+ gcd_machine_example
+ gcd_machine
+ start
+
+set_register_contents(gcd_machine, "a", 206);
+set_register_contents(gcd_machine, "b", 40);
+start(gcd_machine);
+get_register_contents(gcd_machine, "a");
+
+
+
+ gcd_machine
+ make_machine
+ gcd_machine_example
+
(define gcd-machine
(make-machine
@@ -112,113 +138,143 @@
(assign b (reg t))
(goto (label test-b))
gcd-done)))
-
-
-function gcd_machine() {
- return make_machine(list("a", "b", "t"),
- list(list("rem", binary_function((a, b) => a % b)),
- list("=", binary_function((a, b) => a === b))),
- list("test-b",
- test(op("="), reg("b"), constant(0)),
- branch(label("gcd-done")),
- assign("t", list(op("rem"), reg("a"), reg("b"))),
- assign("a", list(reg("b"))),
- assign("b", list(reg("t"))),
- go_to(label("test-b")),
- "gcd-done"));
-}
-
-
- The first argument to make-machinemake_machine is a list of register names.
- The next argument is a table (a list of two-element lists) that pairs
- each operation name with a Scheme
- procedurefunction
- that implements the operation
- (that is, produces the same output value given the same input values).
- The last argument specifies the controller as a list of labels and
- machine instructions, as in section.
-
+
+
+const gcd_machine =
+ make_machine(
+ list("a", "b", "t"),
+ list(list("rem", (a, b) => a % b),
+ list("=", (a, b) => a === b)),
+ list("test-b",
+ test(op("="), reg("b"), constant(0)),
+ branch(label("gcd-done")),
+ assign("t", list(op("rem"), reg("a"), reg("b"))),
+ assign("a", list(reg("b"))),
+ assign("b", list(reg("t"))),
+ go_to(label("test-b")),
+ "gcd-done"));
+
+
+ The first argument to
+
+ make-machine
+ make_machine
+
+
+ is a list of register names. The next argument is a table (a list of
+ two-element lists) that pairs each operation name with a Scheme
+
+ procedure
+ function
+
+ that implements the operation (that is, produces the same output value
+ given the same input values). The last argument specifies the controller
+ as a list of labels and machine instructions, as in
+ section.
+
-
- To compute GCDs with this machine, we set the
- input registers, start the machine, and examine the result when the
- simulation terminates:
-
-
- (set-register-contents! gcd-machine 'a 206)
-
-
- done
-
-
-set_register_contents(gcd_machine, "a", 206);
+
+ To compute GCDs with this machine, we set the input registers, start the
+ machine, and examine the result when the simulation terminates:
+
+ set_register_contents_a
+ gcd_machine
+ start
+
+ (set-register-contents! gcd-machine 'a 206)
+
+
+ done
+
+
+set_register_contents(gcd_machine, "a", 206);
+
+
"done"
-
-
-
-
-
+
+
+
+ set_register_contents_b
+ set_register_contents_a
+
(set-register-contents! gcd-machine 'b 40)
-
-
+
+
done
-
-
+
+
set_register_contents(gcd_machine, "b", 40);
+
+
"done"
-
-
-
-
-
+
+
+
+ start_gcd_machine
+ set_register_contents_b
+
(start gcd-machine)
-
-
+
+
done
-
-
+
+
start(gcd_machine);
+
+
"done"
-
-
-
-
-
+
+
+
+ get_register_contents_a
+ start_gcd_machine
+
(get-register-contents gcd-machine 'a)
-
-
+
+
2
-
-
+
+
get_register_contents(gcd_machine, "a");
+
+
2
-
-
-
+
+
+
-
- This computation will run much more slowly than a gcdgcd
- procedurefunction
- written in Scheme, because we will simulate low-level machine
- instructions, such as assignassign, by much more complex operations.
-
+
+ This computation will run much more slowly than a
+ gcd
+
+ procedure
+ function
+
+ written in
+
+ Scheme,
+ JavaScript,
+
+ because we will simulate low-level machine instructions, such as
+ assign, by much more complex operations.
+
-
- Use the simulator to test the machines you designed in
- exercise.
-
-
+
+ Use the simulator to test the machines you designed in
+ exercise.
+
+
-
- &subsection5.2.1;
+
+ &subsection5.2.1;
-
- &subsection5.2.2;
+
+ &subsection5.2.2;
-
- &subsection5.2.3;
+
+ &subsection5.2.3;
-
- &subsection5.2.4;
+
+ &subsection5.2.4;
-
+
diff --git a/xml/chapter5/section2/subsection1.xml b/xml/chapter5/section2/subsection1.xml
index 325f2a2af..c1d98ab07 100644
--- a/xml/chapter5/section2/subsection1.xml
+++ b/xml/chapter5/section2/subsection1.xml
@@ -1,38 +1,67 @@
-
-
- The Machine Model
-
-
-
-
-
- The machine model generated by make-machinemake_machine is represented as a
- procedurefunction
- with local state using the message-passing techniques
- developed in chapter3. To build this model, make-machinemake_machine
- begins by calling the
- procedurefunction
- make-new-machinemake_new_machine to construct
- the parts of the machine model that are common to all register
- machines. This basic machine model constructed by make-new-machinemake_new_machine is essentially a container for some registers and a
- stack, together with an execution mechanism that processes the controller
- instructions one by one.
-
-
-
- Make-machineMake_machine then extends this basic model (by sending it
- messages) to include the registers, operations, and controller of the
- particular machine being defined. First it allocates a register in
- the new machine for each of the supplied register names and installs
- the designated operations in the machine. Then it uses an
- assembler
-
- assembler (described below in section) to
- transform the controller list into instructions for the new machine
- and installs these as the machines instruction sequence. Make-machineMake_machine returns as its value the modified machine model.
-
-
-
+
+
+ The Machine Model
+
+
+
+
+
+ The machine model generated by make-machinemake_machine is represented as a
+ procedurefunction
+ with local state using the message-passing techniques
+ developed in chapter3. To build this model, make-machinemake_machine
+ begins by calling the
+ procedurefunction
+ make-new-machinemake_new_machine to construct
+ the parts of the machine model that are common to all register
+ machines. This basic machine model constructed by make-new-machinemake_new_machine is essentially a container for some registers and a
+ stack, together with an execution mechanism that processes the controller
+ instructions one by one.
+
+
+
+ Make-machineMake_machine then extends this basic model (by sending it
+ messages) to include the registers, operations, and controller of the
+ particular machine being defined. First it allocates a register in
+ the new machine for each of the supplied register names and installs
+ the designated operations in the machine. Then it uses an
+ assembler
+
+ assembler (described below in section) to
+ transform the controller list into instructions for the new machine
+ and installs these as the machines instruction sequence. Make-machineMake_machine returns as its value the modified machine model.
+
+
+
+ gcd_machine_complete_example
+ make_machine
+ start
+
+const gcd_machine =
+ make_machine(
+ list("a", "b", "t"),
+ list(list("rem", (a, b) => a % b),
+ list("=", (a, b) => a === b)),
+ list("test-b",
+ test(op("="), reg("b"), constant(0)),
+ branch(label("gcd-done")),
+ assign("t", list(op("rem"), reg("a"), reg("b"))),
+ assign("a", list(reg("b"))),
+ assign("b", list(reg("t"))),
+ go_to(label("test-b")),
+ "gcd-done"));
+set_register_contents(gcd_machine, "a", 206);
+set_register_contents(gcd_machine, "b", 40);
+start(gcd_machine);
+get_register_contents(gcd_machine, "a");
+
+
+
+ make_machine
+ make_new_machine
+ assemble
+ gcd_machine_complete_example
+
(define (make-machine register-names ops controller-text)
(let ((machine (make-new-machine)))
@@ -43,38 +72,40 @@
((machine 'install-instruction-sequence)
(assemble controller-text machine))
machine))
-
-
+
+
function make_machine(register_names, ops, controller_text) {
const machine = make_new_machine();
-
+
for_each(register_name => machine("allocate_register")(register_name), register_names);
machine("install_operations")(ops);
machine("install_instruction_sequence")(assemble(controller_text, machine));
return machine;
}
-
-
-
-
-
- Registers
-
-
- register(s)representing
-
-
- We will represent a register as a
- procedurefunction
- with local state, as in
- chapter3. The
- procedurefunction
- make-registermake_register creates a register that
- holds a value that can be accessed or changed:
-
-
-
+
+
+
+
+
+ Registers
+
+
+ register(s)representing
+
+
+ We will represent a register as a
+ procedurefunction
+ with local state, as in
+ chapter3. The
+ procedurefunction
+ make-registermake_register creates a register that
+ holds a value that can be accessed or changed:
+
+
+ make_register
+ gcd_machine_complete_example
+
(define (make-register name)
(let ((contents '*unassigned*))
@@ -85,8 +116,8 @@ function make_machine(register_names, ops, controller_text) {
(else
(error "Unknown request - - REGISTER" message))))
dispatch))
-
-
+
+
function make_register(name) {
let contents = "*unassigned*";
@@ -106,17 +137,21 @@ function make_register(name) {
return dispatch;
}
-
-
-
-
-
- The following
- proceduresfunctions
- are used to access registers:
-
-
-
+
+
+
+
+
+ The following
+
+ procedures
+ functions
+
+ are used to access registers:
+
+ get_contents
+ gcd_machine_complete_example
+
(define (get-contents register)
(register 'get))
@@ -124,8 +159,8 @@ function make_register(name) {
(define (set-contents! register value)
((register 'set) value))
-
-
+
+
function get_contents(register) {
return register("get");
}
@@ -133,28 +168,30 @@ function get_contents(register) {
function set_contents(register, value) {
return register("set")(value);
}
-
-
-
-
-
- The stack
-
-
- stackrepresenting
-
-
- We can also represent a stack as a
- procedurefunction
- with local state. The
- procedurefunction
- make-stackmake_stack creates a stack whose local state consists
- of a list of the items on the stack. A stack accepts requests to pushpush an
- item onto the stack, to poppop the top item off the stack
- and return it, and to initializeinitialize the stack to empty.
-
-
-
+
+
+
+
+
+ The stack
+
+
+ stackrepresenting
+
+
+ We can also represent a stack as a
+ procedurefunction
+ with local state. The
+ procedurefunction
+ make-stackmake_stack creates a stack whose local state consists
+ of a list of the items on the stack. A stack accepts requests to pushpush an
+ item onto the stack, to poppop the top item off the stack
+ and return it, and to initializeinitialize the stack to empty.
+
+
+ make_stack
+ gcd_machine_complete_example
+
(define (make-stack)
(let ((s '()))
@@ -176,8 +213,8 @@ function set_contents(register, value) {
(else (error "Unknown request - - STACK"
message))))
dispatch))
-
-
+
+
function make_stack() {
let stack = null;
@@ -214,17 +251,19 @@ function make_stack() {
return dispatch;
}
-
-
-
-
-
- The following
- proceduresfunctions
- are used to access stacks:
-
-
-
+
+
+
+
+
+ The following
+ proceduresfunctions
+ are used to access stacks:
+
+
+ pop
+ gcd_machine_complete_example
+
(define (pop stack)
(stack 'pop))
@@ -232,8 +271,8 @@ function make_stack() {
(define (push stack value)
((stack 'push) value))
-
-
+
+
function pop(stack) {
return stack("pop");
}
@@ -241,78 +280,78 @@ function pop(stack) {
function push(stack, value) {
return stack("push")(value);
}
-
-
-
-
-
- The basic machine
-
-
-
- The make-new-machinemake_new_machine
- procedurefunction, shown in
- figure, constructs an object whose local
- state consists of a stack, an initially empty instruction sequence, a
- list of operations that initially contains an operation to
-
- initialize
- the stack, and a
- register table, in simulator
- register table that initially contains two
-
-
- registers, named flagflag and pcpc
- program counter
- (for program counter).
- The internal
- procedurefunction
- allocate_register adds new entries to the
- register table, and the internal
- procedurefunction
- lookup-registerlookup_register looks
- up registers in the table.
-
-
-
- The flagflag register is used to control branching in the simulated
- machine. TestTest instructions set the contents of flagflag to
- the result of the test (true or false). BranchBranch instructions
- decide whether or not to branch by examining the contents of flagflag.
-
-
-
- The pcpc register determines the sequencing of instructions as
- the machine runs. This sequencing is implemented by the internal
- procedurefunction
- executeexecute.
- In the simulation model, each machine instruction is a data structure
- that includes a
- procedurefunction
- of no arguments, called the
- instruction execution
+
+
+
+
+
+ The basic machine
+
+
+
+ The make-new-machinemake_new_machine
+ procedurefunction, shown in
+ figure, constructs an object whose local
+ state consists of a stack, an initially empty instruction sequence, a
+ list of operations that initially contains an operation to
+
+ initialize
+ the stack, and a
+ register table, in simulator
+ register table that initially contains two
+
+
+ registers, named flagflag and pcpc
+ program counter
+ (for program counter).
+ The internal
+ procedurefunction
+ allocate_register adds new entries to the
+ register table, and the internal
+ procedurefunction
+ lookup-registerlookup_register looks
+ up registers in the table.
+
+
+
+ The flagflag register is used to control branching in the simulated
+ machine. TestTest instructions set the contents of flagflag to
+ the result of the test (true or false). BranchBranch instructions
+ decide whether or not to branch by examining the contents of flagflag.
+
+
+
+ The pcpc register determines the sequencing of instructions as
+ the machine runs. This sequencing is implemented by the internal
+ procedurefunction
+ executeexecute.
+ In the simulation model, each machine instruction is a data structure
+ that includes a
+ procedurefunction
+ of no arguments, called the
+ instruction execution
procedurefunction
- execution
+ execution
procedurefunctionin register-machine simulator
- instruction
+ instruction
execution
procedurefunction, such that calling this
- procedurefunction
- simulates
- executing the instruction. As the simulation runs, pcpc points to
- the place in the instruction sequence beginning with the next
- instruction to be executed.
-
- ExecuteExecute gets that instruction,
- executes it by calling the instruction execution
- procedurefunction, and
- repeats this cycle until there are no more instructions to execute
- (i.e., until pcpc points to the end of the instruction sequence).
-
-
-
-
-
+ procedurefunction
+ simulates
+ executing the instruction. As the simulation runs, pcpc points to
+ the place in the instruction sequence beginning with the next
+ instruction to be executed.
+
+ ExecuteExecute gets that instruction,
+ executes it by calling the instruction execution
+ procedurefunction, and
+ repeats this cycle until there are no more instructions to execute
+ (i.e., until pcpc points to the end of the instruction sequence).
+
+
+
+
+
(define (make-new-machine)
(let ((pc (make-register 'pc))
@@ -357,23 +396,33 @@ function push(stack, value) {
((eq? message 'operations) the-ops)
(else (error "Unknown request - - MACHINE" message))))
dispatch)))
-
-
-
-
-
-
-
+
+
+
+
+
+
+ make_new_machine
+ make_stack
+ make_register
+ assoc
+ get_contents
+ make_instruction
+ gcd_machine_complete_example
+
function make_new_machine() {
const pc = make_register("pc");
const flag = make_register("flag");
const stack = make_stack();
let the_instruction_sequence = null;
- let the_ops = list(list("initialize_stack", () => stack("initialize")));
+ let the_ops = list(list("initialize_stack",
+ () => stack("initialize")));
let register_table = list(list("pc", pc), list("flag", flag));
+
function allocate_register(name) {
if (assoc(name, register_table) === undefined) {
- register_table = pair(list(name, make_register(name)), register_table);
+ register_table = pair(list(name, make_register(name)),
+ register_table);
} else {
error(name, "Multiply defined register: ");
}
@@ -381,6 +430,7 @@ function make_new_machine() {
}
function lookup_register(name) {
const val = assoc(name, register_table);
+
return val === undefined
? error(name, "Unknown register:")
: head(tail(val));
@@ -395,69 +445,107 @@ function make_new_machine() {
return execute();
}
}
- function dispatch(message) {
- return message === "start"
- ? () => { set_contents(pc, the_instruction_sequence);
- return execute(); }
- : message === "install_instruction_sequence"
- ? seq => { the_instruction_sequence = seq; }
- : message === "allocate_register"
- ? allocate_register
- : message === "get_register"
- ? lookup_register
- : message === "install_operations"
- ? ops => { the_ops = append(the_ops, ops); }
- : message === "stack"
- ? stack
- : message === "operations"
- ? the_ops
- : error(message, "Unknown request: MACHINE");
- }
- return dispatch;
+ return message =>
+ message === "start"
+ ? () => { set_contents(pc, the_instruction_sequence);
+ return execute(); }
+ : message === "install_instruction_sequence"
+ ? seq => { the_instruction_sequence = seq; }
+ : message === "allocate_register"
+ ? allocate_register
+ : message === "get_register"
+ ? lookup_register
+ : message === "install_operations"
+ ? ops => { the_ops = append(the_ops, ops); }
+ : message === "stack"
+ ? stack
+ : message === "operations"
+ ? the_ops
+ : error(message, "Unknown request: machine");
}
-
-
-
The make_new_machine function,
- which implements the basic machine model.
-
-
-
-
-
-
-
- As part of its operation, each instruction execution
- procedurefunction
- modifies pcpc to indicate the next instruction to be executed.
- BranchBranch and gotogo_to instructions change pcpc to point to
- the new destination. All other instructions simply advance pcpc,
- making it point to the next instruction in the sequence. Observe that
- each call to executeexecute calls executeexecute again, but this does
- not produce an infinite loop because running the instruction execution
- procedurefunction
- changes the contents of pcpc.
-
-
-
- Make-new-machineMake_new_machine returns a
- dispatchdispatch
- procedurefunction
- that implements message-passing
- access to the internal state. Notice that starting the machine is
- accomplished by setting pcpc to the beginning of the instruction
- sequence and calling executeexecute.
-
-
-
- For convenience, we provide an alternate procedural interface to a
- machines startstart operation,
- as well as
- proceduresfunctions
- to set and examine register contents,
- as specified at the beginning of section:
-
-
-
+
+
+
+ The make_new_machine function,
+ which implements the basic machine model.
+
+
+
+
+
+
+
+
+ As part of its operation, each instruction execution
+
+ procedure
+ function
+
+ modifies
+ pc to indicate the next instruction to be
+ executed.
+
+
+ Branch and
+ goto instructions
+
+
+ The instructions
+ branch
+ and
+ go_to
+
+
+ change pc to point to the new destination.
+ All other instructions simply advance pc,
+ making it point to the next instruction in the sequence. Observe that
+ each call to execute calls
+ execute again, but this does not produce an
+ infinite loop because running the instruction execution
+
+ procedure
+ function
+
+ changes the contents of pc.
+
+
+
+
+ Make-new-machine
+
+ The function
+ make_new_machine
+
+
+ returns a
+
+ dispatch
+ dispatch
+
+
+ procedure
+ function
+
+ that implements message-passing access to the internal state. Notice that
+ starting the machine is accomplished by setting
+ pc to the beginning of the instruction sequence
+ and calling execute.
+
+
+
+ For convenience, we provide an alternate procedural interface to a
+ machines start operation,
+ as well as
+
+ procedures
+ functions
+
+ to set and examine register contents, as specified at the beginning of
+ section:
+
+ start
+ gcd_machine_complete_example
+
(define (start machine)
(machine 'start))
@@ -470,44 +558,50 @@ function make_new_machine() {
(define (set-register-contents! machine register-name value)
(set-contents! (get-register machine register-name) value)
'done)
-
-
+
+
function start(machine) {
return machine("start")();
}
-
function get_register_contents(machine, register_name) {
return get_contents(get_register(machine, register_name));
}
-
function set_register_contents(machine, register_name, value) {
set_contents(get_register(machine, register_name), value);
return "done";
}
-
-
-
-
-
- These
- proceduresfunctions
- (and many
- proceduresfunctions
- in sections
- and ) use the following to look up the register with a
- given name in a given machine:
-
-
+
+
+
+
+
+ These
+
+ procedures
+ functions
+
+ (and many
+
+ procedures
+ functions
+
+ in sections and )
+ use the following to look up the register with a given name in a given
+ machine:
+
+ get_register
+ gcd_machine_complete_example
+
(define (get-register machine reg-name)
((machine 'get-register) reg-name))
-
-
+
+
function get_register(machine, reg_name) {
return machine("get_register")(reg_name);
}
-
-
-
+
+
+
-
+
diff --git a/xml/chapter5/section2/subsection2.xml b/xml/chapter5/section2/subsection2.xml
index 179b5bd79..029fbf613 100644
--- a/xml/chapter5/section2/subsection2.xml
+++ b/xml/chapter5/section2/subsection2.xml
@@ -1,105 +1,181 @@
-
-
- The Assembler
-
+
+
+ The Assembler
+
-
- assembler|(
+
+ assembler|(
-
- The assembler transforms the sequence of controller expressions for a
- machine into a corresponding list of machine instructions, each with
- its execution
- procedurefunction. Overall, the assembler is much like the
- evaluators we studied in chapter4there is an input language (in
- this case, the register-machine language) and we must perform an
- appropriate action for each type of expression in the language.
-
+
+ The assembler transforms the sequence of controller expressions for a
+ machine into a corresponding list of machine instructions, each with
+ its execution
+
+ procedure.
+ function.
+
+ Overall, the assembler is much like the evaluators we studied in
+ chapter4there is an input language (in this case, the
+ register-machine language) and we must perform an appropriate action for
+ each type of expression in the language.
+
-
- syntactic analysis, separated from executionin register-machine
- simulator
- The technique of producing an execution
- procedurefunction
- for each instruction
- is just what we used in section to speed
- up the evaluator by separating analysis from runtime execution. As we
- saw in chapter4, much useful analysis of Scheme expressions could be
- performed without knowing the actual values of variables. Here,
- analogously, much useful analysis of register-machine-language
- expressions can be performed without knowing the actual contents of
- machine registers. For example, we can replace references to
- registers by pointers to the register objects, and we can
- replace references to labels by pointers to the place in the
- instruction sequence that the label designates.
-
+
+ syntactic analysis, separated from execution
+ in register-machine simulator
+ The technique of producing an execution
+
+ procedure
+ function
+
+ for each instruction is just what we used in
+ section to speed
+ up the evaluator by separating analysis from runtime execution. As we
+ saw in chapter4, much useful analysis of Scheme expressions could
+ be performed without knowing the actual values of variables. Here,
+ analogously, much useful analysis of register-machine-language
+ expressions can be performed without knowing the actual contents of
+ machine registers. For example, we can replace references to
+ registers by pointers to the register objects, and we can
+ replace references to labels by pointers to the place in the
+ instruction sequence that the label designates.
+
-
- Before it can generate the instruction execution
- proceduresfunctions, the
- assembler must know what all the labels refer to, so it begins by
- scanning the controller text to separate the labels from the
- instructions. As it scans the text, it constructs both a list of
- instructions and a table that associates each label with a pointer
- into that list. Then the assembler augments the instruction list by
- inserting the execution
- procedurefunction
- for each instruction.
-
+
+ Before it can generate the instruction execution
+
+ procedures,
+ functions,
+ the assembler must know what all the labels refer to, so it begins by
+ scanning the controller text to separate the labels from the
+ instructions. As it scans the text, it constructs both a list of
+ instructions and a table that associates each label with a pointer
+ into that list. Then the assembler augments the instruction list by
+ inserting the execution
+
+ procedure
+ function
+
+ for each instruction.
+
-
- The assembleassemble
- procedurefunction
- is the main entry to the assembler.
- It takes the controller text and the machine model as arguments and
- returns the instruction sequence to be stored in the model.
- AssembleAssemble calls
- extract-labelsextract_labels to build the initial instruction list
- and label table from the supplied controller text. The second argument
- to extract-labelsextract_labels is a
- procedurefunction
- to be called to process these results:
- This
- procedurefunction
- uses update-insts!update_insts to generate the instruction execution
- proceduresfunctions
- and insert them into the instruction list,
- and returns the modified list.
-
-
+
+ The assemble
+
+ procedure
+ function
+
+ is the main entry to the assembler. It takes the controller text and the
+ machine model as arguments and returns the instruction sequence to be stored
+ in the model.
+
+ Assemble
+
+ The function
+ Assemble
+
+
+ calls
+
+ extract-labels
+
+ extract_labels
+
+
+ to build the initial instruction list and label table from the supplied
+ controller text. The second argument
+ to
+
+ extract-labels
+
+ extract_labels
+
+
+ is a
+
+ procedure
+ function
+
+ to be called to process these results: This
+
+ procedure
+ function
+
+ uses
+
+ update-insts!
+
+ update_insts
+
+
+ to generate the instruction execution
+
+ procedures
+ functions
+
+ and insert them into the instruction list, and returns the modified list.
+
+ assemble
+ update_insts
+ extract_labels
+ gcd_machine_complete_example
+
(define (assemble controller-text machine)
(extract-labels controller-text
(lambda (insts labels)
(update-insts! insts labels machine)
insts)))
-
-
+
+
function assemble(controller_text, machine) {
- function receive(insts, labels) {
- update_insts(insts, labels, machine);
- return insts;
- }
-
- return extract_labels(controller_text, receive);
+return extract_labels(
+ controller_text,
+ (insts, labels) => {
+ update_insts(insts, labels, machine);
+ return insts;
+ });
}
-
-
-
-
-
- Extract-labelsExtract_labels
- takes as arguments a list texttext (the sequence of controller
- instruction expressions) and a receivereceive
- procedurefunction. ReceiveReceive
- will be called with two values: (1) a list instsinsts of instruction
- data structures, each containing an instruction from texttext; and
- (2) a table called labelslabels, which associates each label from
- texttext
- with the position in the list instsinsts that the label designates.
+
+
+
-
-
+
+
+ Extract-labels
+
+ The function
+ extract_labels
+
+
+ takes as arguments a list
+ text (the sequence of controller
+ instruction expressions) and a
+
+ receive
+ receive
+
+
+ procedure.
+ function.
+
+
+ Receive
+ The function
+ receive
+
+
+ will be called with two values: (1) a list
+ insts of instruction data structures, each
+ containing an instruction from text; and
+ (2) a table called labels, which associates each
+ label from text with the position in the list
+ insts that the label designates.
+
+ extract_labels
+ make_label_entry
+ gcd_machine_complete_example
+
(define (extract-labels text receive)
(if (null? text)
@@ -115,8 +191,8 @@ function assemble(controller_text, machine) {
(receive (cons (make-instruction next-inst)
insts)
labels)))))))
-
-
+
+
function extract_labels(text, receive) {
function helper(insts, labels) {
const next_inst = head(text);
@@ -130,25 +206,48 @@ function extract_labels(text, receive) {
? receive(null, null)
: extract_labels(tail(text), helper);
}
-
-
-
+
+
+
-
- Extract-labelsExtract_labels works by sequentially scanning the elements of
- the texttext and accumulating the
- instsinsts and the labelslabels.
- If an element is a symbol (and thus a label) an appropriate entry is
- added to the labelslabels table. Otherwise the element is accumulated
- onto the instsinsts list.
-
- Using the receivereceive
- procedurefunction
- here is a way to get extract-labelsextract_labels to effectively return two valueslabelslabels and
- instsinstswithout explicitly making a compound data structure to
- hold them. An alternative implementation, which returns an explicit
- pair of values, is
-
+
+
+ Extract-labels
+ The function
+ extract_labels
+
+
+ works by sequentially scanning the elements of the
+ text and accumulating the
+ insts and the
+ labels. If an element is a symbol (and thus a
+ label) an appropriate entry is added to the
+ labels table. Otherwise the element is
+ accumulated onto the insts
+ list.
+
+ Using the
+ receive
+
+ procedure
+ function
+
+ here is a way to get
+
+ extract-labels
+
+ extract_labels
+
+
+ to effectively return two
+ valueslabels and
+ instswithout explicitly making a
+ compound data structure to hold them. An alternative implementation, which
+ returns an explicit pair of values, is
+
+ extract_labels_alternative
+ gcd_machine_complete_example
(define (extract-labels text)
@@ -181,8 +280,10 @@ function extract_labels(text, receive) {
}
- which would be called by assembleassemble as follows:
-
+ which would be called by assemble as follows:
+
+ assemble_alternative
+ gcd_machine_complete_example
(define (assemble controller-text machine)
@@ -205,25 +306,43 @@ function assemble_alternative(controller_text, machine) {
returning multiple values
- procedurefunctionreturning multiple values
+ procedure
+ function
+ returning multiple valuescontinuationin register-machine simulator
- You can consider our use of receivereceive as demonstrating an elegant
- way to return multiple values, or simply an excuse to show off a
- programming trick. An argument like receivereceive that is the next
- procedurefunction
+ You can consider our use of receive as
+ demonstrating an elegant way to return multiple values, or simply an excuse
+ to show off a programming trick. An argument like
+ receive that is the next
+
+ procedure
+ function
+
to be invoked is called a continuation. Recall that we
also used continuations to implement the backtracking control
- structure in the ambamb evaluator in section.
-
-
-
- Update-insts!Update_insts modifies the instruction list, which initially
- contains only the text of the instructions, to include the
- corresponding execution
- proceduresfunctions:
+ structure in the amb evaluator in
+ section.
+
-
-
+
+
+ Update-insts!
+
+ The function
+ update_insts
+
+ modifies the instruction list, which initially contains only the text of the
+ instructions, to include the corresponding execution
+
+ procedures:
+ functions:
+
+
+ update_insts
+ get_register
+ make_execution_function
+ gcd_machine_complete_example
+
(define (update-insts! insts labels machine)
(let ((pc (get-register machine 'pc))
@@ -238,8 +357,8 @@ function assemble_alternative(controller_text, machine) {
(instruction-text inst) labels machine
pc flag stack ops)))
insts)))
-
-
+
+
function update_insts(insts, labels, machine) {
const pc = get_register(machine, "pc");
const flag = get_register(machine, "flag");
@@ -258,20 +377,40 @@ function update_insts(insts, labels, machine) {
ops)),
insts);
}
-
-
-
+
+
+
-
- The machine instruction data structure simply pairs the
- instruction text with the corresponding execution
- procedurefunction.
- The execution
- procedurefunction
- is not yet available when extract-labelsextract_labels
- constructs the instruction, and is inserted later by update-insts!update_insts.
-
-
+
+ The machine instruction data structure simply pairs the
+ instruction text with the corresponding execution
+
+ procedure.
+ function.
+
+ The execution
+
+ procedure
+ function
+
+ is not yet available when
+
+ extract-labels
+
+ extract_labels
+
+
+ constructs the instruction, and is inserted later by
+
+ update-insts!.
+
+ update_insts.
+
+
+
+ make_instruction
+ gcd_machine_complete_example
+
(define (make-instruction text)
(cons text '()))
@@ -287,8 +426,8 @@ function update_insts(insts, labels, machine) {
(define (set-instruction-execution-proc! inst proc)
(set-cdr! inst proc))
-
-
+
+
function make_instruction(text) {
return pair(text, null);
}
@@ -304,40 +443,45 @@ function instruction_execution_proc(inst) {
function set_instruction_execution_proc(inst, proc) {
set_tail(inst, proc);
}
-
-
-
+
+
+
-
- The instruction text is not used by our simulator, but it is handy to keep
- around for debugging (see exercise).
-
+
+ The instruction text is not used by our simulator, but it is handy to keep
+ around for debugging (see
+ exercise).
+
-
- Elements of the label table are pairs:
-
-
+
+ Elements of the label table are pairs:
+
+ make_label_entry
+ gcd_machine_complete_example
+
(define (make-label-entry label-name insts)
(cons label-name insts))
-
-
+
+
function make_label_entry(label_name, insts) {
return pair(label_name, insts);
}
-
-
- Entries will be looked up in the table with
-
-
+
+
+ Entries will be looked up in the table with
+
+ lookup_label
+ gcd_machine_complete_example
+
(define (lookup-label labels label-name)
(let ((val (assoc label-name labels)))
(if val
(cdr val)
(error "Undefined label - - ASSEMBLE" label-name))))
-
-
+
+
function lookup_label(labels, label_name) {
const val = assoc(label_name, labels);
@@ -345,15 +489,15 @@ function lookup_label(labels, label_name) {
? error(label_name, "Undefined label: ASSEMBLE")
: tail(val);
}
-
-
-
+
+
+
-
- The following register-machine code is ambiguous, because the label
- herehere is defined more than once:
-
-
+
+ The following register-machine code is ambiguous, because the label
+ here is defined more than once:
+
+
start
(goto (label here))
here
@@ -363,26 +507,44 @@ function lookup_label(labels, label_name) {
(assign a (const 4))
(goto (label there))
there
-
-
- "start"
+
+
+"start",
go_to(label(here)),
- "here",
+"here",
assign("a", list(const(3))),
go_to(label(there)),
- "here",
+"here",
assign("a", list(const(4))),
go_to(label(there)),
- "there",
-
-
+"there",
+
+
+ With the simulator as written, what will the contents of register
+
+ a
+ "a"
+
+ be when control reaches
+
+ there?
+ "there"?
+
+ Modify the
+
+ extract-labels
+
+ extract_labels
+
+
+
+ procedure
+ function
+
+ so that the assembler will signal an error if the same label
+ name is used to indicate two different locations.
+
+ assembler|)
+
- With the simulator as written, what will the contents of register aa
- be when control reaches therethere? Modify the extract-labelsextract_labels
- procedurefunction
- so that the assembler will signal an error if the same label
- name is used to indicate two different locations.
-
- assembler|)
-
diff --git a/xml/chapter5/section2/subsection3.xml b/xml/chapter5/section2/subsection3.xml
index 392d62aea..9ec398d6d 100644
--- a/xml/chapter5/section2/subsection3.xml
+++ b/xml/chapter5/section2/subsection3.xml
@@ -1,27 +1,55 @@
-
-
- Generating Execution
- ProceduresFunctions
- for Instructions
-
-
-
- execution
- procedurefunctionin register-machine simulator|(
-
-
- The assembler calls make-execution-proceduremake_execution_function to
- generate the execution
- procedurefunction
- for an instruction.
- Like the analyzeanalyze
- procedurefunction
- in the evaluator of
- section, this dispatches on the type of
- instruction to generate the appropriate execution
- procedurefunction.
-
-
+
+
+ Generating Execution
+
+ Procedures
+ Functions
+
+ for Instructions
+
+
+
+ execution
+ procedure
+ function
+ in register-machine simulator|(
+
+
+ The assembler calls
+
+ make-execution-procedure
+
+ make_execution_function
+
+
+ to generate the execution
+
+ procedure
+ function
+
+ for an instruction.
+ Like the analyze
+
+ procedure
+ function
+
+ in the evaluator of section,
+ this dispatches on the type of instruction to generate the appropriate
+ execution
+
+ procedure.
+ function.
+
+
+ make_execution_function
+ make_assign
+ make_test
+ make_branch_5
+ make_go_to
+ make_save
+ make_perform
+ gcd_machine_complete_example
+
(define (make-execution-procedure inst labels machine
pc flag stack ops)
@@ -41,11 +69,11 @@
(make-perform inst machine labels ops pc))
(else (error "Unknown instruction type - - ASSEMBLE"
inst))))
-
-
-function make_execution_function(inst, labels, machine, pc, flag, stack, ops) {
+
+
+function make_execution_function(inst, labels, machine,
+ pc, flag, stack, ops) {
const x = head(inst);
-
return x === "assign"
? make_assign(inst, machine, labels, ops, pc)
: x === "test"
@@ -53,46 +81,76 @@ function make_execution_function(inst, labels, machine, pc, flag, stack, ops) {
: x === "branch"
? make_branch(inst, machine, labels, flag, pc)
: x === "go_to"
- ? make_goto(inst, machine, labels, pc)
+ ? make_go_to(inst, machine, labels, pc)
: x === "save"
? make_save(inst, machine, stack, pc)
: x === "restore"
? make_restore(inst, machine, stack, pc)
: x === "perform"
? make_perform(inst, machine, labels, ops, pc)
- : error(inst, "Unknown instruction type: ASSEMBLE");
-}
-
-
-
-
-
- For each type of instruction in the register-machine language, there
- is a generator that builds an appropriate execution
- procedurefunction. The
- details of these
- proceduresfunctions
- determine both the syntax and meaning of
- the individual instructions in the register-machine language.
- We use data abstraction to isolate the detailed syntax of
- register-machine expressions from the general execution mechanism, as
- we did for evaluators in section,
- by using syntax
- proceduresfunctions
- to extract and classify the parts of an instruction.
-
-
-
- AssignAssign instructions
-
-
-
-
- The make-assignmake_assign
- procedurefunction
- handles assignassign instructions:
-
-
+ : error(inst, "Unknown instruction type in assemble:");
+}
+
+
+
+
+
+ For each type of instruction in the register-machine language, there
+ is a generator that builds an appropriate execution
+
+ procedure.
+ function.
+ The details of these
+
+ procedures
+ functions
+
+ determine both the syntax and meaning of the individual instructions in the
+ register-machine language. We use data abstraction to isolate the detailed
+ syntax of register-machine expressions from the general execution mechanism,
+ as we did for evaluators in
+ section, by using syntax
+
+ procedures
+ functions
+
+ to extract and classify the parts of an instruction.
+
+
+
+
+
+ Assign instructions
+ The instruction assign
+
+
+
+
+
+
+
+ The
+
+ make-assign
+ make_assign
+
+
+ procedure
+ function
+
+ handles
+
+ assign
+ assign
+
+ instructions:
+
+ make_assign
+ assign_reg_name
+ make_operation_exp
+ assign_constructor
+ gcd_machine_complete_example
+
(define (make-assign inst machine labels operations pc)
(let ((target
@@ -104,33 +162,39 @@ function make_execution_function(inst, labels, machine, pc, flag, stack, ops) {
value-exp machine labels operations)
(make-primitive-exp
(car value-exp) machine labels))))
- (lambda () ; execution procedure for assign
+ (lambda () ; execution procedure for assign
(set-contents! target (value-proc))
(advance-pc pc)))))
-
-
+
+
function make_assign(inst, machine, labels, operations, pc) {
const target = get_register(machine, assign_reg_name(inst));
const value_exp = assign_value_exp(inst);
- const value_fun = is_operation_exp(value_exp)
- ? make_operation_exp(value_exp, machine, labels, operations)
- : make_primitive_exp(head(value_exp), machine, labels);
-
- function perform_make_assign() {
+ const value_fun =
+ is_operation_exp(value_exp)
+ ? make_operation_exp(value_exp, machine, labels, operations)
+ : make_primitive_exp(head(value_exp), machine, labels);
+ return () => {
set_contents(target, value_fun());
advance_pc(pc);
- }
-
- return perform_make_assign;
+ };
}
-
-
- Make-assignMake_assign extracts the target register name (the
- second element of the instruction) and the value expression
- (the rest of the list that forms the instruction)
- from the assignassign instruction using the selectors
-
-
+
+
+
+ Make-assign
+
+ The function
+ make_assign
+
+
+ extracts the target register name (the second element of the instruction)
+ and the value expression (the rest of the list that forms the instruction)
+ from the assign instruction using the selectors
+
+ assign_reg_name
+ gcd_machine_complete_example
+
(define (assign-reg-name assign-instruction)
(cadr assign-instruction))
@@ -138,8 +202,8 @@ function make_assign(inst, machine, labels, operations, pc) {
(define (assign-value-exp assign-instruction)
(cddr assign-instruction))
-
-
+
+
function assign_reg_name(assign_instruction) {
return head(tail(assign_instruction));
}
@@ -147,81 +211,187 @@ function assign_reg_name(assign_instruction) {
function assign_value_exp(assign_instruction) {
return tail(tail(assign_instruction));
}
-
-
-
-
-
- The register name is looked up with get-registerget_register to produce the
- target register object. The value expression is passed to make-operation-expmake_operation_exp if the value is the result of an operation, and to
- make-primitive-expmake_primitive_exp otherwise. These
- proceduresfunctions
- (shown below)
- parse the value expression and produce an execution
- procedurefunction
- for the
- value. This is a
- procedurefunction
- of no arguments, called
-
- value-procvalue_fun,
- which will be evaluated during the simulation to produce the actual
- value to be assigned to the register. Notice that the work of looking
- up the register name and parsing the value expression is performed
- just once, at assembly time, not every time the instruction is
- simulated. This saving of work is the reason we use execution
- syntactic analysis, separated from executionin register-machine
- simulator
- proceduresfunctions, and corresponds directly to the saving in work we obtained
- by separating program analysis from execution in the evaluator of
- section.
-
-
-
- The result returned by make-assignmake_assign is the execution
- procedurefunction
- for the assignassign instruction. When this
- procedurefunction
- is
- called (by the machine models executeexecute
- procedurefunction),
- it sets the contents of the target register to the result
- obtained by executing value-procvalue_fun. Then it advances
- the pcpc to the next instruction by running the
- procedurefunction
-
-
+
+
+ The function assign is the matching
+ constructor for assign instructions.
+
+ assign_constructor
+ gcd_machine_complete_example
+
+function assign(register_name, source) {
+ const a = append(list("assign", register_name), source);
+ return a;
+}
+
+
+
+
+
+ The register name is looked up with
+
+ get-register
+
+ get_register
+
+
+ to produce the target register object. The value expression is passed to
+
+ make-operation-exp
+ make_operation_exp
+
+
+ if the value is the result of an operation, and to
+
+ make-primitive-exp
+
+ make_primitive_exp
+
+
+ otherwise. These
+
+ procedures
+ functions
+
+ (shown below) parse the value expression and produce an execution
+
+ procedure
+ function
+
+ for the value. This is a
+
+ procedure
+ function
+
+ of no arguments, called
+
+
+ value-proc,
+
+ value_fun,
+
+ which will be evaluated during the simulation to produce the actual
+ value to be assigned to the register. Notice that the work of looking
+ up the register name and parsing the value expression is performed
+ just once, at assembly time, not every time the instruction is
+ simulated. This saving of work is the reason we use execution
+ syntactic analysis, separated from execution
+ in register-machine simulator
+
+ procedures,
+ functions,
+
+ and corresponds directly to the saving in work we obtained by separating
+ program analysis from execution in the evaluator of
+ section.
+
+
+
+ The result returned by
+
+ make-assign
+ make_assign
+
+ is the execution
+
+ procedure
+ function
+
+ for the assign instruction. When this
+
+ procedure
+ function
+
+ is called (by the machine models execute
+
+ procedure),
+ function),
+
+ it sets the contents of the target register to the result obtained by
+ executing
+
+ value-proc.
+ value_fun.
+
+ Then it advances the pc to the next instruction
+ by running the
+
+ procedure
+ function
+
+
+ advance_pc
+ gcd_machine_complete_example
+
(define (advance-pc pc)
(set-contents! pc (cdr (get-contents pc))))
-
-
+
+
function advance_pc(pc) {
set_contents(pc, tail(get_contents(pc)));
-
-}
-
-
- Advance-pcAdvance_pc is the normal termination for all instructions except
- branchbranch and gotogo_to.
-
-
-
- TestTest, branchbranch, and gotogo_to instructions
-
-
-
-
- Make-testMake_test handles testtest instructions in a similar way. It
- extracts the expression that specifies the condition to be tested and
- generates an execution
- procedurefunction
- for it. At simulation time, the
- procedurefunction
- for the condition is called, the result is assigned to the
- flagflag register, and the pcpc is advanced:
-
-
+}
+
+
+
+ Advance-pc
+
+ The function
+ advance_pc
+
+
+ is the normal termination for all instructions except
+ branch and goto.
+
+
+
+
+
+
+ Test,
+ branch, and
+ goto
+ instructions
+
+
+ The instructions
+ test,
+ branch, and
+ goto
+
+
+
+
+
+
+
+
+ Make-test
+
+ The function
+ make_test
+
+
+ handles test instructions in a similar way.
+ It extracts the expression that specifies the condition to be tested and
+ generates an execution
+
+ procedure
+ function
+
+ for it. At simulation time, the
+
+ procedure
+ function
+
+ for the condition is called, the result is assigned to the
+ flag register, and the
+ pc is advanced:
+
+ make_test
+ advance_pc
+ gcd_machine_complete_example
+
(define (make-test inst machine labels operations flag pc)
(let ((condition (test-condition inst)))
@@ -237,49 +407,61 @@ function advance_pc(pc) {
(define (test-condition test-instruction)
(cdr test-instruction))
-
-
+
+
function make_test(inst, machine, labels, operations, flag, pc) {
const condition = test_condition(inst);
-
if (is_operation_exp(condition)) {
- const condition_fun = make_operation_exp(condition, machine, labels, operations);
-
- function perform_make_test() {
+ const condition_fun = make_operation_exp(condition,
+ machine, labels, operations);
+ return () => {
set_contents(flag, condition_fun());
advance_pc(pc);
- }
-
- return perform_make_test;
+ };
} else {
- error(inst, "Bad TEST instruction: ASSEMBLE");
+ error(inst, "Bad test instruction in assemble:");
}
}
+function test(op, lhs, rhs) {
+ return list("test", op, lhs, rhs);
+}
function test_condition(test_instruction) {
return tail(test_instruction);
}
-
-
-
-
-
-
- The execution
- procedurefunction
- for a branchbranch instruction checks the
- contents of the flagflag register and either sets the contents of
- the pcpc to the branch destination (if the branch is taken) or
- else just advances the pcpc (if the branch is not taken). Notice
- that the indicated destination in a branchbranch instruction must be a
- label, and the make-branchmake-branch
- procedurefunction
- enforces this. Notice
- also that the label is looked up at assembly time, not each time the
- branchbranch instruction is simulated.
-
-
-
+
+
+
+
+
+
+ The execution
+
+ procedure
+ function
+
+ for a branch instruction checks the contents of
+ the flag register and either sets the contents
+ of the pc to the branch destination (if the
+ branch is taken) or else just advances the pc
+ (if the branch is not taken). Notice that the indicated destination in a
+ branch instruction must be a label, and the
+
+ make-branch
+ make_branch
+
+
+
+ procedure
+ function
+
+ enforces this. Notice also that the label is looked up at assembly time,
+ not each time the branch instruction is
+ simulated.
+
+ make_branch_5
+ gcd_machine_complete_example
+
(define (make-branch inst machine labels flag pc)
(let ((dest (branch-dest inst)))
@@ -295,45 +477,50 @@ function test_condition(test_instruction) {
(define (branch-dest branch-instruction)
(cadr branch-instruction))
-
-
+
+
function make_branch(inst, machine, labels, flag, pc) {
const dest = branch_dest(inst);
-
if (is_label_exp(dest)) {
const insts = lookup_label(labels, label_exp_label(dest));
-
- function perform_make_branch() {
- if (get_contents(flag)) {
- set_contents(pc, insts);
-
- } else {
- advance_pc(pc);
- }
- }
-
- return perform_make_branch;
-
+ return () => {
+ if (get_contents(flag)) {
+ set_contents(pc, insts);
+ } else {
+ advance_pc(pc);
+ }
+ };
} else {
- error(inst, "Bad BRANCH instruction: ASSEMBLE");
+ error(inst, "Bad branch instruction in assemble:");
}
}
+function branch(label) {
+ return list("branch", label);
+}
function branch_dest(branch_instruction) {
return head(tail(branch_instruction));
}
-
-
-
-
-
-
- A gotogo_to instruction is similar to a branch, except that the
- destination may be specified either as a label or as a register, and
- there is no condition to checkthe pcpc is always set to the
- new destination.
-
-
+
+
+
+
+
+
+ A
+
+ goto
+ go_to
+
+ instruction is similar to a branch, except that the destination may be
+ specified either as a label or as a register, and there is no condition to
+ checkthe pc is always set to the
+ new destination.
+
+ make_go_to
+ is_register_exp
+ gcd_machine_complete_example
+
(define (make-goto inst machine labels pc)
(let ((dest (goto-dest inst)))
@@ -354,40 +541,45 @@ function branch_dest(branch_instruction) {
(define (goto-dest goto-instruction)
(cadr goto-instruction))
-
-
-function make_goto(inst, machine, labels, pc) {
+
+
+function make_go_to(inst, machine, labels, pc) {
const dest = goto_dest(inst);
-
if (is_label_exp(dest)) {
const insts = lookup_label(labels, label_exp_label(dest));
return () => set_contents(pc, insts);
-
} else if (is_register_exp(dest)) {
const reg = get_register(machine, register_exp_reg(dest));
return () => set_contents(pc, get_contents(reg));
-
} else {
- error(inst, "Bad GOTO instruction: ASSEMBLE");
+ error(inst, "Bad go_to instruction in assemble");
}
}
+function go_to(label) {
+ return list("go_to", label);
+}
function goto_dest(goto_instruction) {
return head(tail(goto_instruction));
}
-
-
-
-
-
- Other instructions
-
-
-
- The stack instructions savesave and restorerestore simply use the
- stack with the designated register and advance the pcpc:
-
-
+
+
+
+
+
+ Other instructions
+
+
+
+ The stack instructions
+ save and restore
+ simply use the stack with the designated register and advance the
+ pc:
+
+ make_save
+ pop
+ gcd_machine_complete_example
+
(define (make-save inst machine stack pc)
@@ -409,48 +601,61 @@ function goto_dest(goto_instruction) {
(define (stack-inst-reg-name stack-instruction)
(cadr stack-instruction))
-
-
+
+
function make_save(inst, machine, stack, pc) {
const reg = get_register(machine, stack_inst_reg_name(inst));
-
- function perform_make_save() {
- push(stack, get_contents(reg));
- advance_pc(pc);
- }
-
- return perform_make_save;
+ return () => {
+ push(stack, get_contents(reg));
+ advance_pc(pc);
+ };
}
function make_restore(inst, machine, stack, pc) {
const reg = get_register(machine, stack_inst_reg_name(inst));
-
- function perform_make_restore() {
- set_contents(reg, pop(stack));
- advance_pc(pc);
- }
-
- return perform_make_restore;
+ return () => {
+ set_contents(reg, pop(stack));
+ advance_pc(pc);
+ };
}
+function save(reg) {
+ return list("save", reg);
+}
+function restore(reg) {
+ return list("restore", reg);
+}
function stack_inst_reg_name(stack_instruction) {
return head(tail(stack_instruction));
}
-
-
-
-
-
-
- The final instruction type, handled by make-performmake_perform, generates
- an execution
- procedurefunction
- for the action to be performed. At simulation
- time, the action
- procedurefunction
- is executed and the pcpc advanced.
-
-
+
+
+
+
+
+
+ The final instruction type, handled by
+
+ make-perform,
+
+ make_perform,
+
+ generates an execution
+
+ procedure
+ function
+
+ for the action to be performed. At simulation time, the action
+
+ procedure
+ function
+
+ is executed and the pc advanced.
+
+ make_perform
+ is_operation_exp
+ gcd_machine_complete_example
+
(define (make-perform inst machine labels operations pc)
(let ((action (perform-action inst)))
@@ -465,47 +670,74 @@ function stack_inst_reg_name(stack_instruction) {
(define (perform-action inst) (cdr inst))
-
-
+
+
function make_perform(inst, machine, labels, operations, pc) {
const action = perform_action(inst);
-
if (is_operation_exp(action)) {
- const action_fun = make_operation_exp(action, machine, labels, operations);
- return () => { action_fun(); advance_pc(pc); }
-
+ const action_fun = make_operation_exp(action, machine,
+ labels, operations);
+ return () => {
+ action_fun(); advance_pc(pc);
+ };
} else {
- error(inst, "Bad PERFORM instruction: ASSEMBLE");
+ error(inst, "Bad perform instruction in assemble");
}
}
function perform_action(inst) {
return tail(inst);
}
-
-
-
-
-
- Execution
- proceduresfunctions
+
+
+
+
+
+ Execution
+
+ procedures
+ functions
+
for subexpressions
-
-
-
-
-
-
- The value of a regreg, labellabel, or constconstant expression
- may be needed for assignment to a register (make-assignmake_assign) or for input to
- an operation (make-operation-expmake_operation_exp, below). The following
- procedurefunction
- generates execution
- proceduresfunctions
- to produce values for these expressions
- during the simulation:
-
-
+
+
+
+
+
+
+ The value of a reg,
+ label, or
+
+ const
+ constant
+
+ expression may be needed for assignment to a register
+
+ (make-assign)
+ (make_assign)
+
+ or for input to an operation
+
+ (make-operation-exp,
+
+ (make_operation_exp,
+
+ below). The following
+
+ procedure
+ function
+
+ generates execution
+
+ procedures
+ functions
+
+ to produce values for these expressions during the simulation:
+
+ make_primitive_exp
+ lookup_label
+ gcd_machine_complete_example
+
(define (make-primitive-exp exp machine labels)
(cond ((constant-exp? exp)
@@ -522,34 +754,39 @@ function perform_action(inst) {
(lambda () (get-contents r))))
(else
(error "Unknown expression type - - ASSEMBLE" exp))))
-
-
+
+
function make_primitive_exp(exp, machine, labels) {
if (is_constant_exp(exp)) {
const c = constant_exp_value(exp);
return () => c;
-
} else if (is_label_exp(exp)) {
const insts = lookup_label(labels, label_exp_label(exp));
return () => insts;
-
} else if (is_register_exp(exp)) {
const r = get_register(machine, register_exp_reg(exp));
return () => get_contents(r);
-
} else {
- error(exp, "Unknown expression type: ASSEMBLE");
+ error(exp, "Unknown expression type in assemble:");
}
}
-
-
-
-
-
- The syntax of regreg, labellabel, and constconstant expressions
- is determined by
-
-
+
+
+
+
+
+ The syntax of
+ reg, label, and
+
+ const
+ constant
+
+ expressions is determined by
+
+ is_register_exp
+ tagged_list
+ gcd_machine_complete_example
+
(define (register-exp? exp) (tagged-list? exp 'reg))
@@ -567,49 +804,82 @@ function make_primitive_exp(exp, machine, labels) {
(define (label-exp-label exp) (cadr exp))
-
-
+
+
+function reg(name) {
+ return list("reg", name);
+}
function is_register_exp(exp) {
return is_tagged_list(exp, "reg");
}
-
function register_exp_reg(exp) {
return head(tail(exp));
}
+function constant(value) {
+ return list("constant", value);
+}
function is_constant_exp(exp) {
return is_tagged_list(exp, "constant");
}
-
function constant_exp_value(exp) {
return head(tail(exp));
}
+function label(name) {
+ return list("label", name);
+}
function is_label_exp(exp) {
return is_tagged_list(exp, "label");
}
-
function label_exp_label(exp) {
return head(tail(exp));
}
-
-
-
-
-
-
- AssignAssign, performperform, and testtest instructions
- may include the application of a machine operation (specified by
- an opop expression) to some operands (specified by regreg
- and constconstant expressions).
- The following
- procedurefunction
- produces an execution
- procedurefunction
- for an operation expressiona list containing the operation and
- operand expressions from the instruction:
-
-
+
+
+
+
+
+
+
+
+ Assign,
+ perform, and
+ test
+ instructions
+
+
+ Instructions
+ assign,
+ perform, and
+ test
+
+
+ may include the application of a machine operation (specified by
+ an op expression) to some operands (specified
+ by reg and
+
+ const
+ constant
+
+ expressions). The following
+
+ procedure
+ function
+
+ produces an execution
+
+ procedure
+ function
+
+ for an operation expressiona list containing the
+ operation and operand expressions from the instruction:
+
+ make_operation_exp
+ lookup_prim
+ make_primitive_exp
+ gcd_machine_complete_example
+
(define (make-operation-exp exp machine labels operations)
(let ((op (lookup-prim (operation-exp-op exp) operations))
@@ -619,27 +889,27 @@ function label_exp_label(exp) {
(operation-exp-operands exp))))
(lambda ()
(apply op (map (lambda (p) (p)) aprocs)))))
-
-
+
+
function make_operation_exp(exp, machine, labels, operations) {
const op = lookup_prim(operation_exp_op(exp), operations);
const aprocs = map(e => make_primitive_exp(e, machine, labels),
operation_exp_operands(exp));
- function perform_make_operation_exp() {
- return op(map(p => p(), aprocs));
- }
-
- return perform_make_operation_exp;
+ return () => apply_in_underlying_javascript(
+ op, map(p => p(), aprocs));
}
-
-
-
-
-
- The syntax of operation expressions is determined by
-
-
+
+
+
+
+
+ The syntax of operation expressions is determined by
+
+ is_operation_exp
+ tagged_list
+ gcd_machine_complete_example
+
(define (operation-exp? exp)
(and (pair? exp) (tagged-list? (car exp) 'op)))
@@ -651,10 +921,13 @@ function make_operation_exp(exp, machine, labels, operations) {
(define (operation-exp-operands operation-exp)
(cdr operation-exp))
-
-
+
+
+function op(name) {
+ return list("op", name);
+}
function is_operation_exp(exp) {
- return is_pair(exp) && is_tagged_list(head(exp), "op");
+ return is_pair(exp) && is_tagged_list(head(exp), "op");
}
function operation_exp_op(operation_exp) {
@@ -664,158 +937,247 @@ function operation_exp_op(operation_exp) {
function operation_exp_operands(operation_exp) {
return tail(operation_exp);
}
-
-
-
-
-
- Observe that the treatment of operation expressions is very much like
- the treatment of
- procedurefunction
- applications by the analyze-applicationanalyze_application
- procedurefunction
- in the evaluator of
- section in that we generate an execution
- procedurefunction
- for each operand. At simulation time, we call the
- operand
- proceduresfunctions
- and apply the Scheme
- procedurefunction
- that simulates
- the operation to the resulting values.
- The simulation
- procedurefunction
- is found by looking up the operation name in
- the operation table for the machine:
-
-
+
+
+
+
+
+ Observe that the treatment of operation expressions is very much like
+ the treatment of
+
+ procedure
+ function
+
+ applications by the
+
+ analyze-application
+
+ analyze_application
+
+
+
+ procedure
+ function
+
+ in the evaluator of section in
+ that we generate an execution
+
+ procedure
+ function
+
+ for each operand. At simulation time, we call the operand
+
+ procedures
+ functions
+
+ and apply the Scheme
+
+ procedure
+ function
+
+ that simulates the operation to the resulting values. The simulation
+
+ procedure
+ function
+
+ is found by looking up the operation name in the operation table for the
+ machine:
+
+ lookup_prim
+ gcd_machine_complete_example
+
(define (lookup-prim symbol operations)
(let ((val (assoc symbol operations)))
(if val
(cadr val)
(error "Unknown operation - - ASSEMBLE" symbol))))
-
-
+
+
function lookup_prim(symbol, operations) {
const val = assoc(symbol, operations);
-
return val === undefined
- ? error(symbol, "Unknown operation: ASSEMBLE")
+ ? error(symbol, "Unknown operation in assemble:")
: head(tail(val));
}
-
-
-
-
-
- The treatment of machine operations above permits them to operate
- on labels as well as on constants and the contents of registers.
- Modify the expression-processing
- proceduresfunctions
- to enforce the condition
- that operations can be used only with registers and constants.
-
-
-
- Design a new syntax for register-machine instructions and modify the
- simulator to use your new syntax. Can you implement your new
- syntax without changing any part of the simulator except the
- syntax
- proceduresfunctions
- in this section?
-
-
-
-
-
-
- When we introduced savesave and restorerestore in
- section, we didnt specify what would happen
- if you tried to restore a register that was not the last one saved, as
- in the sequence
-
-
-
+
+
+
+
+
+ The treatment of machine operations above permits them to operate
+ on labels as well as on constants and the contents of registers.
+ Modify the expression-processing
+
+ procedures
+ functions
+
+ to enforce the condition that operations can be used only with registers
+ and constants.
+
+
+
+ Design a new syntax for register-machine instructions and modify the
+ simulator to use your new syntax. Can you implement your new
+ syntax without changing any part of the simulator except the
+ syntax
+
+ procedures
+ functions
+
+ in this section?
+
+
+
+
+
+
+ When we introduced save and
+ restore in
+ section, we didnt specify
+ what would happen if you tried to restore a register that was not the last
+ one saved, as in the sequence
+
+
(save y)
(save x)
(restore y)
-
-
+
+
save(y);
save(x);
restore(y);
-
-
- There are several reasonable possibilities for the meaning of restorerestore:
-
-
- (restore y)restore(y) puts into yy the last value saved on the
- stack, regardless of what register that value came from. This is the
- way our simulator behaves. Show how to take advantage of this
- behavior to eliminate one instruction from the Fibonacci machine of
- section (figure).
-
-
- (restore y)restore(y) puts into yy the last value saved on the
- stack, but only if that value was saved from yy; otherwise, it
- signals an error. Modify the simulator to behave this way. You will
- have to change savesave to put the register name on the stack along
- with the value.
-
-
- (restore y)restore(y) puts into yy the last value saved from yy regardless of what other registers were saved after yy and not
- restored. Modify the simulator to behave this way. You will have to
- associate a separate stack with each register. You should make the
- initialize-stackinitialize_stack operation initialize all the register stacks.
-
-
-
-
-
-
- The simulator can be used to help determine the data paths required
- for implementing a machine with a given controller. Extend
- the assembler to store the following information in the machine model:
-
-
a list of all instructions, with duplicates removed, sorted by
- instruction type (assignassign, gotogo_to, and so on);
-
-
-
a list (without duplicates) of the registers used to hold entry
- points (these are the registers referenced by gotogo_to
- instructions);
-
-
-
a list (without duplicates) of the registers that are savesaved
- or restorerestored;
-
-
-
for each register, a list (without duplicates) of the sources from
- which it is assigned (for example, the sources for register valval
- in the factorial machine of Figure are
- (const 1)constant(1) and ((op *) (reg n) (reg val))op("*", reg("n"), reg("val"))).
-
-
-
-
- Extend the
- message-passing interface to the machine to provide access to this new
- information. To test your analyzer, define the Fibonacci machine from
- figure and examine the lists you constructed.
-
-
-
-
- Modify the simulator so that it uses the controller sequence to
- determine what registers the machine has rather than requiring a list
- of registers as an argument to make-machinemake_machine. Instead of
- pre-allocating the registers in make-machinemake_machine, you can allocate
- them one at a time when they are first seen during assembly of the
- instructions.
-
- execution
- procedurefunctionin register-machine simulator|)
-
-
+
+
+ There are several reasonable possibilities for the meaning of
+ restore:
+
+
+
+ (restore y)
+ restore(y)
+
+
+ puts into >y the last value saved on the
+ stack, regardless of what register that value came from. This is the
+ way our simulator behaves. Show how to take advantage of this
+ behavior to eliminate one instruction from the Fibonacci machine of
+ section
+ (figure).
+
+
+
+ (restore y)
+ restore(y)
+
+
+ puts into y the last value saved on the
+ stack, but only if that value was saved from
+ y; otherwise, it signals an error. Modify
+ the simulator to behave this way. You will have to change
+ save to put the register name on the stack
+ along with the value.
+
+
+
+ (restore y)
+ restore(y)
+
+
+ puts into y the last value saved from
+ y regardless of what other registers were
+ saved after y and not restored. Modify the
+ simulator to behave this way. You will have to associate a separate
+ stack with each register. You should make the
+
+ initialize-stack
+
+ initialize_stack
+
+
+ operation initialize all the register stacks.
+
+
+
+
+
+
+ The simulator can be used to help determine the data paths required
+ for implementing a machine with a given controller. Extend
+ the assembler to store the following information in the machine model:
+
+
+ a list of all instructions, with duplicates removed, sorted by
+ instruction type
+ (assign,
+
+ goto,
+ go_to,
+
+ and so on);
+
+
+ a list (without duplicates) of the registers used to hold entry points
+ (these are the registers referenced by
+
+ goto
+ go_to
+
+ instructions);
+
+
+ a list (without duplicates) of the registers that are
+ saved
+ or restored;
+
+
+ for each register, a list (without duplicates) of the sources from
+ which it is assigned (for example, the sources for register
+ val in the factorial machine of
+ figure are
+
+ (const 1)
+
+ constant(1)
+
+
+ and
+
+
+ ((op *) (reg n) (reg val))).
+
+
+ op("*", reg("n"), reg("val"))).
+
+
+
+
+ Extend the message-passing interface to the machine to provide access to
+ this new information. To test your analyzer, define the Fibonacci machine
+ from figure and examine the lists you
+ constructed.
+
+
+
+
+ Modify the simulator so that it uses the controller sequence to determine
+ what registers the machine has rather than requiring a list of registers as
+ an argument to
+
+ make-machine.
+ make_machine.
+
+ Instead of pre-allocating the registers in
+
+ make-machine,
+
+ make_machine,
+
+ you can allocate them one at a time when they are first seen during assembly
+ of the instructions.
+
+ execution
+ procedure
+ function
+ in register-machine simulator|)
+
diff --git a/xml/chapter5/section2/subsection4.xml b/xml/chapter5/section2/subsection4.xml
index 7f52c3888..0526fd487 100644
--- a/xml/chapter5/section2/subsection4.xml
+++ b/xml/chapter5/section2/subsection4.xml
@@ -1,43 +1,61 @@
-
-
- Monitoring Machine Performance
-
+
+
+ Monitoring Machine Performance
+
-
- register machinemonitoring performance|(
+
+ register machinemonitoring performance|(
-
- simulationmonitor@for monitoring performance of register machine
- Simulation is useful not only for verifying the correctness of a
- proposed machine design but also for measuring the machines
- performance. For example, we can install in our simulation program a
- meter that measures the number of stack operations used in a
- computation. To do this, we modify our simulated stack to keep track
- of the number of times registers are saved on the stack and the
- maximum depth reached by the stack, and add a message to the stacks
- interface that prints the statistics, as shown below.
- We also add an operation to the basic machine model to print the
- stack statistics, by initializing the-opsthe_ops in make-new-machinemake_new_machine to
-
-
+
+ simulation
+ monitor@for monitoring performance of register machine
+
+ Simulation is useful not only for verifying the correctness of a
+ proposed machine design but also for measuring the machines
+ performance. For example, we can install in our simulation program a
+ meter that measures the number of stack operations used in a
+ computation. To do this, we modify our simulated stack to keep track
+ of the number of times registers are saved on the stack and the
+ maximum depth reached by the stack, and add a message to the stacks
+ interface that prints the statistics, as shown below.
+ We also add an operation to the basic machine model to print the
+ stack statistics, by initializing
+
+ the-ops
+ the_ops
+
+ in
+
+ make-new-machine
+
+ make_new_machine
+
+ to
+
+
(list (list 'initialize-stack
(lambda () (stack 'initialize)))
(list 'print-stack-statistics
(lambda () (stack 'print-statistics))))
-
-
+
+
list(list("initialize-stack", () => stack("initialize")),
list("print-stack-statistics", () => stack("print-statistics")));
-
-
-
+
+
+
-
- Here is the new version of make-stackmake_stack:
-
-
+
+ Here is the new version of
+
+ make-stack:
+
+ make_stack:
+
+
+
(define (make-stack)
(let ((s '())
@@ -75,21 +93,19 @@ list(list("initialize-stack", () => stack("initialize")),
(else
(error "Unknown request - - STACK" message))))
dispatch))
-
-
+
+
function make_stack() {
let s = null;
let number_pushes = 0;
let max_depth = 0;
let current_depth = 0;
-
function push(x) {
s = pair(x, s);
number_pushes = number_pushes + 1;
current_depth = current_depth + 1;
max_depth = math_max(current_depth, math_max);
}
-
function pop() {
if (is_null(s)) {
error("Empty stack: POP");
@@ -98,11 +114,9 @@ function make_stack() {
const top = head(s);
s = tail(s);
current_depth = current_depth - 1;
-
return top;
}
}
-
function initialize() {
s = null;
number_pushes = 0;
@@ -111,13 +125,11 @@ function make_stack() {
return "done";
}
-
function print_statistics() {
display(accumulate((b, a) => stringify(a) + b,
- list("\n", "total-pushes = ", number_pushes,
- "\n", "maximum-depth = ", max_depth)));
+ list("\n", "total-pushes = ", number_pushes,
+ "\n", "maximum-depth = ", max_depth)));
}
-
function dispatch(message) {
return message === "push"
? push
@@ -129,152 +141,208 @@ function make_stack() {
? print_statistics()
: error(message, "Unknown request: STACK");
}
-
return dispatch;
}
-
-
-
+
+
+
-
- Exercises through
- describe other useful monitoring and debugging features that can be
- added to the register-machine simulator.
-
-
-
-
- Measure the number of pushes and the maximum stack depth required to
- compute $n!$ for various small values of $n$ using the factorial
- machine shown in Figure. From your data
- determine formulas in terms of $n$ for the total number of push
- operations and the maximum stack depth used in computing $n!$ for any
- $n > 1$. Note that each of these is a linear function of $n$ and is
- thus determined by two constants. In order to get the statistics
- printed, you will have to augment the factorial machine with instructions to
- initialize the stack and print the statistics.
- You may want to also modify the
- machine so that it repeatedly reads a value for $n$, computes the
- factorial, and prints the result (as we did for the GCD machine in
- figure), so that you will not have to repeatedly
- invoke get-register-contentsget_register_contents, set-register-contents!set_register_contents, and
- startstart.
-
-
+
+ Exercises
+ through
+ describe other useful monitoring and debugging features that can be
+ added to the register-machine simulator.
+
+
+
+
+ Measure the number of pushes and the maximum stack depth required to
+ compute $n!$ for various small values of
+ $n$ using the factorial
+ machine shown in Figure. From your
+ data determine formulas in terms of $n$ for the
+ total number of push operations and the maximum stack depth used in
+ computing $n!$ for any
+ $n > 1$. Note that each of these is a linear
+ function of $n$ and is thus determined by two
+ constants. In order to get the statistics printed, you will have to augment
+ the factorial machine with instructions to initialize the stack and print
+ the statistics. You may want to also modify the machine so that it
+ repeatedly reads a value for $n$, computes the
+ factorial, and prints the result (as we did for the GCD machine in
+ figure), so that you will not have to
+ repeatedly invoke
+
+ get-register-contents,
+ get_register_contents,
+
+
+
+ set-register-contents!,
+
+ set_register_contents,
+
+
+ and
+
+ start.
+ start.
+
+
+
-
- Add
- instruction counting
- instruction counting to the register machine simulation.
- That is, have the machine model keep track of the number of
- instructions executed. Extend the machine models interface to accept
- a new message that prints the value of the instruction count and
- resets the count to zero.
-
-
+
+ Add
+ instruction counting
+ instruction counting to the register machine simulation.
+ That is, have the machine model keep track of the number of
+ instructions executed. Extend the machine models interface to
+ accept a new message that prints the value of the instruction count and
+ resets the count to zero.
+
+
-
- Augment the simulator to provide for
- instruction tracing
- tracinginstruction execution
- instruction tracing.
- That is, before each instruction is executed, the simulator should print
- the text of the instruction. Make the machine model accept trace-ontrace_on and
- trace-offtrace_off messages to turn tracing on and off.
-
-
+
+ Augment the simulator to provide for
+ instruction tracing
+ tracinginstruction execution
+ instruction tracing.
+ That is, before each instruction is executed, the simulator should print
+ the text of the instruction. Make the machine model accept
+
+ trace-on
+
+ trace_on
+
+
+ and
+
+ trace-off
+ trace_off
+
+ messages to turn tracing on and off.
+
+
-
- Extend the instruction tracing of
- exercise so that before
- printing an instruction, the simulator prints any labels that
- immediately precede that instruction in the controller sequence. Be
- careful to do this in a way that does not interfere with instruction
- counting (exercise).
- You will have to make the simulator retain the necessary label information.
-
+
+ Extend the instruction tracing of
+ exercise so that
+ before printing an instruction, the simulator prints any labels that
+ immediately precede that instruction in the controller sequence. Be
+ careful to do this in a way that does not interfere with instruction
+ counting (exercise).
+ You will have to make the simulator retain the necessary label information.
+
-
- register(s)tracing
- tracingregister assignment
- Modify the make-registermake_register
- procedurefunction
- of
- section so that registers can be traced.
- Registers should accept messages that turn tracing on and off. When a
- register is traced, assigning a value to the register should print the
- name of the register, the old contents of the register, and the new
- contents being assigned. Extend the interface to the machine model
- to permit you to turn tracing on and off for designated machine registers.
-
+
+ register(s)tracing
+ tracingregister assignment
+ Modify the
+
+ make-register
+ make_register
+
+
+ procedure
+ function
+
+ of section so that registers can be
+ traced. Registers should accept messages that turn tracing on and off. When
+ a register is traced, assigning a value to the register should print the
+ name of the register, the old contents of the register, and the new contents
+ being assigned. Extend the interface to the machine model to permit you to
+ turn tracing on and off for designated machine registers.
+
-
- Alyssa P. Hacker wants a
- breakpoint
- breakpoint feature in the simulator to
- help her debug her machine designs. You have been hired to install
- this feature for her. She wants to be able to specify a place in the
- controller sequence where the simulator will stop and allow her to
- examine the state of the machine. You are to implement a
- procedurefunction
-
-
-
+
+
+ Alyssa P. Hacker wants a
+ breakpoint
+ breakpoint feature in the simulator to help her debug her machine
+ designs. You have been hired to install this feature for her. She wants to
+ be able to specify a place in the controller sequence where the simulator
+ will stop and allow her to examine the state of the machine. You are to
+ implement a
+
+ procedure
+ function
+
+
+
(set-breakpoint $machine$ $label$ $n$)
-
-
+
+
set_breakpoint($machine$, $label$, $n$);
-
-
- that sets a breakpoint just before the $n$th instruction after the
- given label. For example,
-
-
-
+
+
+ that sets a breakpoint just before the $n$th
+ instruction after the given label. For example,
+
+
(set-breakpoint gcd-machine 'test-b 4)
-
-
+
+
set_breakpoint(gcd_machine, "test-b", 4);
-
-
- installs a breakpoint in gcd-machinegcd_machine just before the
- assignment to register aa. When the simulator reaches the
- breakpoint it should print the label and the offset of the breakpoint
- and stop executing instructions. Alyssa can then use get-register-contentsget_register_contents and set-register-contents!set_register_contents to manipulate
- the state of the simulated machine. She should then be able to
- continue execution by saying
-
-
-
+
+
+ installs a breakpoint in
+
+ gcd-machine
+
+ gcd_machine
+
+
+ just before the assignment to register
+
+ a.
+ "a".
+
+ When the simulator reaches the breakpoint it should print the label and the
+ offset of the breakpoint and stop executing instructions. Alyssa can then
+ use
+
+ get-register-contents
+
+ get_register_contents
+
+
+ and
+
+ set-register-contents!
+
+ set_register_contents
+
+
+ to manipulate the state of the simulated machine. She should then be able
+ to continue execution by saying
+
+
(proceed-machine $machine$)
-
-
+
+
proceed_machine($machine$);
-
-
- She should also be able to remove a specific breakpoint by means of
-
-
-
+
+
+ She should also be able to remove a specific breakpoint by means of
+
+
(cancel-breakpoint $machine$ $label$ $n$)
-
-
+
+
cancel_breakpoint($machine$, $label$, $n$);
-
-
- or to remove all breakpoints by means of
-
-
-
+
+
+ or to remove all breakpoints by means of
+
+
(cancel-all-breakpoints $machine$)
-
-
+
+
cancel_all_breakpoints($machine$);
-
-
-
- register machinesimulator|)
- register-machine simulator|)
- register machinemonitoring performance|)
-
-
+
+
+
+ register machinesimulator|)
+ register-machine simulator|)
+ register machinemonitoring performance|)
+
diff --git a/xml/chapter5/section3/subsection1.xml b/xml/chapter5/section3/subsection1.xml
index eea8e08da..986ddc023 100644
--- a/xml/chapter5/section3/subsection1.xml
+++ b/xml/chapter5/section3/subsection1.xml
@@ -30,7 +30,7 @@
To model computer memory, we use a new kind of data
structure called a
vector (data structure)
- vector(in JavaScript, a vector is essentially just a wrapper around an array). Abstractly, a vector is a compound
+ vector(in JavaScript, a vector is essentially just a wrapper around an array). Abstractly, a vector is a compound
data object whose individual elements can be accessed by means of an
integer index in an amount of time that is independent of the
index.We could represent memory as lists of items.
@@ -48,7 +48,7 @@
proceduresfunctions
(those marked ns are not in the IEEE Scheme standard)}[vector-ref@vector-ref]
-->
-
(vector-ref vector n)vector_ref($\textit{vector}, \textit{n}$) returns the $n$th
+
(vector-ref vector n)vector_ref(vector, n) returns the $n$th
element of the vector.
@@ -57,7 +57,7 @@
(those marked ns are not in the IEEE Scheme standard)}[vector-set!@vector-set!]
-->
-
(vector-set! vector n value)vector_set(vector, n, value) sets
+
(vector-set! vector n value)vector_set(vector, n, value) sets
the $n$th element of the vector to the designated value.
@@ -153,7 +153,7 @@
-
+
Box-and-pointer and memory-vector representations
of the list list(list(1, 2), 3, 4).
@@ -251,8 +251,8 @@
(assign ^reg$_{1}$^ (op cdr) (reg ^reg$_{2}$^))
-assign(reg$_{1}$,list(op("head"), reg$_{2}$))
-assign(reg$_{1}$,list(op("tail"), reg$_{2}$))
+assign(reg1, list(op("head"), reg2))
+assign(reg1, list(op("tail"), reg2))
@@ -264,8 +264,8 @@ assign(reg$_{1}$,list(op("tail"), reg$_{
(assign ^reg$_{1}$^ (op vector-ref) (reg the-cdrs) (reg ^reg$_{2}$^))
-assign(reg$_{1}$,list(op("vector_ref"), reg("the_heads"), reg$_{2}$))
-assign(reg$_{1}$,list(op("vector_ref"), reg("the_tails"), reg$_{2}$))
+assign(reg1, list(op("vector_ref"), reg("the_heads"), reg2))
+assign(reg1, list(op("vector_ref"), reg("the_tails"), reg2))
@@ -280,8 +280,8 @@ assign(reg$_{1}$,list(op("vector_ref"), reg("the_tail
(perform (op set-cdr!) (reg ^reg$_{1}$^) (reg ^reg$_{2}$^))
-perform(list(op("set_head"), reg($\textit{reg}_{1}$), reg($\textit{reg}_{2}$)))
-perform(list(op("set_tail"), reg($\textit{reg}_{1}$), reg($\textit{reg}_{2}$)))
+perform(list(op("set_head"), reg(reg1), reg(reg2)))
+perform(list(op("set_tail"), reg(reg1), reg(reg2)))
are implemented as
@@ -293,8 +293,8 @@ perform(list(op("set_tail"), reg($\textit{reg}_{1}$),
(op vector-set!) (reg the-cdrs) (reg ^reg$_{1}$^) (reg ^reg$_{2}$^))
-perform(op("vector_set"), list(reg("the_heads"), reg($\textit{reg}_{1}$), reg($\textit{reg}_{2}$)))
-perform(op("vector_set"), list((reg("the_tails"), reg($\textit{reg}_{1}$), reg($\textit{reg}_{2}$)))
+perform(op("vector_set"), list(reg("the_heads"), reg(reg1), reg(reg2)))
+perform(op("vector_set"), list((reg("the_tails"), reg(reg1), reg(reg2)))
@@ -322,7 +322,7 @@ perform(op("vector_set"), list((reg("the_tails"), reg($\textit{reg}
(assign ^reg$_{1}$^ (op cons) (reg ^reg$_{2}$^) (reg ^reg$_{3}$^))
-assign($\textit{reg}_{1}$, list(op("pair"), reg($\textit{reg}_{2}$), reg($\textit{reg}_{3}$))
+assign(reg1, list(op("pair"), reg(reg2), reg(reg3)))
is implemented as the following sequence of vector
@@ -339,10 +339,10 @@ assign($\textit{reg}_{1}$, list(op("pair"), reg(
-perform(op("vector_set"), list(reg("the_heads"), reg("free"), reg($\textit{reg}_{2}$))),
-perform(op("vector_set"), list(reg("the_tails"), reg("free"), reg($\textit{reg}_{3}$))),
-assign($\textit{reg}_{1}$, reg("free")),
-assign("free", list(op("+"), reg("free"), cons(1)))
+perform(op("vector_set"), list(reg("the_heads"), reg("free"), reg(reg2))),
+perform(op("vector_set"), list(reg("the_tails"), reg("free"), reg(reg3))),
+assign(reg1, reg("free")),
+assign("free", list(op("+"), reg("free"), constant(1)))
@@ -355,7 +355,7 @@ assign("free", list(op("+"), reg("free"), cons(1)))
(op eq?) (reg ^reg$_{1}$^) (reg ^reg$_{2}$^)
-list(op("==="), reg($\textit{reg}_{1}$), reg($\textit{reg}_{2}$))
+list(op("==="), reg(reg1), reg(reg2))
simply tests the equality of all fields in the registers, and
@@ -382,7 +382,7 @@ list(op("==="), reg($\textit{reg}_{1}$), reg(
-assign("the_stack", list(op("pair"), reg($\textit{reg}$), reg("the-stack")))
+assign("the_stack", list(op("pair"), reg(reg1), reg("the_stack")))
@@ -404,7 +404,7 @@ assign("the_stack", list(op("tail"), reg("the_stack")))
(assign the-stack (const ()))
-assign("the_stack", cons(null))
+assign("the_stack", constant(null))
diff --git a/xml/chapter5/section3/subsection2.xml b/xml/chapter5/section3/subsection2.xml
index 1dcec6996..5c04a1027 100644
--- a/xml/chapter5/section3/subsection2.xml
+++ b/xml/chapter5/section3/subsection2.xml
@@ -80,7 +80,7 @@ accumulate((x, y) => x + y, 0, filter(is_odd, enumerate_interval(0, n)))
continue processing; new pairs will be allocated in the new working
memory (which was the old free memory). When this is full, we can
copy the useful pairs into the new free memory (which was the old
- working memory).
This idea was invented and first implemented
+ working memory).This idea was invented and first implemented
Minsky, Marvin Lee
by Minsky, as part of the implementation of
LispDEC@on DEC PDP-1
@@ -100,8 +100,7 @@ accumulate((x, y) => x + y, 0, filter(is_odd, enumerate_interval(0, n)))
Hewitt, Lieberman, and Moon (see Lieberman and Hewitt
1983) to take advantage of the fact that some structure is more volatile
and other structure is more permanent.
-
-
+
An alternative commonly used garbage-collection technique is the
mark-sweep garbage collectorgarbage collectormark-sweep
@@ -113,8 +112,7 @@ accumulate((x, y) => x + y, 0, filter(is_odd, enumerate_interval(0, n)))
Allen, John
discussion of the mark-sweep method can be found in
Allen 1978.
-
-
+
The Minsky-Fenichel-Yochelson algorithm is the dominant algorithm in
use for large-memory systems because it examines only the useful part
of memory. This is in contrast to mark-sweep, in which the sweep
@@ -128,7 +126,7 @@ accumulate((x, y) => x + y, 0, filter(is_odd, enumerate_interval(0, n)))
compressed out. This can be an extremely important performance
consideration in machines with virtual memory, in which accesses to
widely separated memory addresses may require extra paging
- operations.
+ operations.
@@ -184,7 +182,7 @@ accumulate((x, y) => x + y, 0, filter(is_odd, enumerate_interval(0, n)))
-
+
Reconfiguration of memory by the garbage-collection process.
@@ -294,7 +292,7 @@ accumulate((x, y) => x + y, 0, filter(is_odd, enumerate_interval(0, n)))
there are any more objects to be scanned. We do this by testing
whether the scan pointer is coincident with the free
pointer. If the pointers are equal, then all accessible objects have
- been relocated, and we branch to gc-flip, which cleans things up
+ been relocated, and we branch to gc-flipgc_flip, which cleans things up
so that we can continue the interrupted computation. If there are
still pairs to be scanned, we call the relocate subroutine to relocate
the carhead of the next pair (by placing the carhead pointer in old). The relocate-continuerelocate_continue register is set up so that the
@@ -351,7 +349,7 @@ accumulate((x, y) => x + y, 0, filter(is_odd, enumerate_interval(0, n)))
"update_tail",
perform(list(op("vector_set"), reg("new_tails"), reg("scan"), reg("new"))),
- assign("scan", list(op("+"), reg("scan"), cons(1))),
+ assign("scan", list(op("+"), reg("scan"), constant(1))),
go_to(label("gc_loop")),
@@ -449,7 +447,7 @@ accumulate((x, y) => x + y, 0, filter(is_odd, enumerate_interval(0, n)))
branch(label("already_moved")),
assign("new", reg("free")), // new location for pair
// Update "free" pointer.
- assign("free", list((op("+"), reg(free), cons(1))),
+ assign("free", list((op("+"), reg("free"), constant(1))),
// Copy the head and tail to new memory
perform(list(op("vector_set"),
reg("new_heads"), reg("new"), reg("oldhr"))),
@@ -458,7 +456,7 @@ accumulate((x, y) => x + y, 0, filter(is_odd, enumerate_interval(0, n)))
reg("new_tails"), reg("new"), reg("oldhr"))),
// Construct the broken heart
perform(list(op("vector_set"),
- reg("the_heads"), reg("old"), cons("broken_heart"))),
+ reg("the_heads"), reg("old"), constant("broken_heart"))),
perform(list(op("vector_set"),
reg("the_tails"), reg("old"), reg("new"))),
go_to(reg("relocate_continue")),
diff --git a/xml/chapter5/section4/section4.xml b/xml/chapter5/section4/section4.xml
index 401fd8f32..b729eb2c6 100644
--- a/xml/chapter5/section4/section4.xml
+++ b/xml/chapter5/section4/section4.xml
@@ -1,4 +1,4 @@
-
+ The Explicit-Control Evaluator
@@ -52,7 +52,7 @@
operations to be used in our register machine. We described the
metacircular evaluator in terms of abstract syntax, using
proceduresfunctions
- such as quoted? and make-procedure. In implementing the
+ such as quoted?is_self_evaluating and make-proceduremake_compound_function. In implementing the
register machine, we could expand these
proceduresfunctions
into sequences of
diff --git a/xml/chapter5/section4/subsection1.xml b/xml/chapter5/section4/subsection1.xml
index a3765f565..8cb3a0c45 100644
--- a/xml/chapter5/section4/subsection1.xml
+++ b/xml/chapter5/section4/subsection1.xml
@@ -22,7 +22,7 @@
it could have been written in a data-directed style (and in a real
system it probably would have been) to avoid the need to perform
sequential tests and to facilitate the definition of new expression
- types. A machine designed to run LispJavaScript would probably include a dispatch-on-type instruction that would efficiently execute such
+ types. A machine designed to run LispJavaScript would probably include a dispatch-on-typedispatch_on_type instruction that would efficiently execute such
data-directed dispatches.
@@ -51,26 +51,30 @@
- "eval_dispatch",
- test(op("is_self_evaluating"), reg("exp")),
- branch(label("ev_self_eval")),
- test(op("is_variable"), reg("exp")),
- branch(label("ev_variable")),
- test(op("is_quoted"), reg("exp")),
- branch(label("ev_quoted")), /// FIXME
- test(op("is_assignment"), reg("exp")),
- branch(label("ev_assignment")),
- test(op("is_definition"), reg("exp")),
- branch(label("ev_definition")),
- test(op("is_conditional_statement"), reg("exp")),
- branch(label("ev_if")),
- test(op("is_function_expression"), reg("exp")),
- branch(label("ev_lambda")),
- test(op("is_block"), reg("exp")),
- branch(label("ev_begin")),
- test(op("is_application"), reg("exp")),
- branch(label("ev_application")),
- go_to(label("unknown_expression_type")),
+"eval_dispatch",
+ test(op("is_self_evaluating"), reg("exp")),
+ branch(label("ev_self_eval")),
+ test(op("is_name"), reg("exp")),
+ branch(label("ev_name")),
+
+ test(op("is_variable_declaration"), reg("exp")),
+ branch(label("ev_variable_declaration")),
+ test(op("is_constant_declaration"), reg("exp")),
+ branch(label("ev_constant_declaration")),
+ test(op("is_assignment"), reg("exp")),
+ branch(label("ev_assignment")),
+
+ test(op("is_conditional_expression"), reg("exp")),
+ branch(label("ev_if")),
+ test(op("is_function_definition"), reg("exp")),
+ branch(label("ev_lambda")),
+ test(op("is_sequence"), reg("exp")),
+ branch(label("ev_seq")),
+ test(op("is_block"), reg("exp")),
+ branch(label("ev_block")),
+ test(op("is_application"), reg("exp")),
+ branch(label("ev_application")),
+ go_to(label("unknown_expression_type")),
@@ -111,23 +115,20 @@
(goto (reg continue))
-
"ev_self_eval",
- assign("val", reg("exp")),
+ assign("val", list(reg("exp"))),
go_to(reg("continue")),
-
-"ev_variable",
- assign("val", op("lookup_variable_value"), reg("exp"), reg("env")),
+
+"ev_name",
+ assign("val", list(op("lookup_name_value"), reg("exp"), reg("env"))),
go_to(reg("continue")),
-
-"ev_quoted",
- assign("val", op("text_of_quotation"), reg("exp")),
- go_to(reg("continue")),
-
+
"ev_lambda",
- assign("unev", op("lambda_parameters"), reg("exp")),
- assign("exp", op("lambda_body"), reg("exp")),
- assign("val", op("make_procedure"), reg("unev"), reg("exp"), reg("env")),
+ assign("unev", list(op("function_definition_parameters"), reg("exp"))),
+ assign("exp", list(op("function_definition_body"), reg("exp"))),
+ assign("val",
+ list(op("make_compound_function"),
+ reg("unev"), reg("exp"), reg("env"))),
go_to(reg("continue")),
@@ -193,10 +194,10 @@
to produce a
procedurefunction, which will later be applied to the evaluated
operands. To evaluate the operator, we move it to the exp
- register and go to eval-dispatch. The environment in the env register is already the correct one in which to evaluate the
+ register and go to eval-dispatcheval_dispatch. The environment in the env register is already the correct one in which to evaluate the
operator. However, we save env because we will need it later to
evaluate the operands. We also extract the operands into unev
- and save this on the stack. We set up continue so that eval-dispatch will resume at ev-appl-did-operator after the
+ and save this on the stack. We set up continue so that eval-dispatcheval_dispatch will resume at ev-appl-did-operatorev_appl_did_operator after the
operator has been evaluated. First, however, we save the old value of
continue, which tells the controller where to continue after the
application.
@@ -214,15 +215,14 @@
(goto (label eval-dispatch))
-
- "ev_application",
- save("continue"),
- save("env"),
- assign("unev", op("operands"), reg("exp")),
- save("unev"),
- assign("exp", op("operator"), reg("exp")),
- assign("continue", label("ev_appl_did_operator")),
- go_to(label("eval_dispatch")),
+"ev_application",
+ save("continue"),
+ save("env"),
+ assign("unev", list(op("operands"), reg("exp"))),
+ save("unev"),
+ assign("exp", list(op("operator"), reg("exp"))),
+ assign("continue", label("ev_appl_did_operator")),
+ go_to(label("eval_dispatch")),
@@ -235,7 +235,7 @@
the unevaluated operands and the environment. We initialize argl to an empty list. Then we assign to the proc register the
procedurefunction
that was produced by evaluating the operator. If there are
- no operands, we go directly to apply-dispatch. Otherwise we
+ no operands, we go directly to apply-dispatchapply_dispatch. Otherwise we
save proc on the stack and start the argument-evaluation
loop:We add to the evaluator data-structure
proceduresfunctions
@@ -294,14 +294,14 @@ function is_last_operand(ops) {
(save proc)
- "ev_appl_did_operator",
- restore("unev"), // the operands
- restore("env"),
- assign("argl", op("empty_arglist")),
- assign("fun", reg("val")), // the operator
- test(op("has_no_operands"), reg("unev")),
- branch(label("apply_dispatch")),
- save("fun"),
+"ev_appl_did_operator",
+ restore("unev"), // the operands
+ restore("env"),
+ assign("argl", list(op("empty_arglist"))),
+ assign("fun", list(reg("val"))), // the operator
+ test(op("has_no_operands"), reg("unev")),
+ branch(label("apply_dispatch")),
+ save("fun"),
@@ -330,15 +330,15 @@ function is_last_operand(ops) {
(goto (label eval-dispatch))
- "ev_appl_operand_loop",
- save("argl"),
- assign("exp", (op("first_operand"), reg("unev")),
- test(op("is_last_operand"), reg("unev")),
- branch(label("ev_appl_last_arg")),
- save("env"),
- save("unev"),
- assign("continue", (label("ev_appl_accumulate_arg"))),
- go_to(label("eval_dispatch")),
+"ev_appl_operand_loop",
+ save("argl"),
+ assign("exp", list(op("first_operand"), reg("unev"))),
+ test(op("is_last_operand"), reg("unev")),
+ branch(label("ev_appl_last_arg")),
+ save("env"),
+ save("unev"),
+ assign("continue", label("ev_appl_accumulate_arg")),
+ go_to(label("eval_dispatch")),
@@ -359,13 +359,13 @@ function is_last_operand(ops) {
(goto (label ev-appl-operand-loop))
- "ev_appl_accumulate_arg",
- restore("unev"),
- restore("env"),
- restore("argl"),
- assign("argl", op("adjoin_arg"), reg("val"), reg("argl")),
- assign("unev", op("rest_operands"), reg("unev")),
- go_to(label("ev_appl_operand_loop")),
+"ev_appl_accumulate_arg",
+ restore("unev"),
+ restore("env"),
+ restore("argl"),
+ assign("argl", list(op("adjoin_arg"), reg("val"), reg("argl"))),
+ assign("unev", list(op("rest_operands"), reg("unev"))),
+ go_to(label("ev_appl_operand_loop")),
@@ -373,9 +373,9 @@ function is_last_operand(ops) {
Evaluation of the last argument is handled differently. There is no
need to save the environment or the list of unevaluated operands
- before going to eval-dispatch,
+ before going to eval-dispatcheval_dispatch,
since they will not be required after the last operand is evaluated.
- Thus, we return from the evaluation to a special entry point ev-appl-accum-last-arg, which restores the argument list, accumulates
+ Thus, we return from the evaluation to a special entry point ev-appl-accum-last-argev_appl_accum_last_arg, which restores the argument list, accumulates
the new argument, restores the saved
procedurefunction, and goes off to
perform the
@@ -390,7 +390,7 @@ function is_last_operand(ops) {
initializing argl until after evaluating the first operand, so
as to avoid saving argl in this case. The compiler in
section performs this optimization. (Compare
- the construct-arglist
+ the construct-arglistconstruct_arglistprocedurefunction
of
section.)
@@ -408,14 +408,14 @@ function is_last_operand(ops) {
"ev_appl_last_arg",
- assign("continue", label("ev_appl_accum_last_arg")),
- go_to(label("eval_dispatch")),
-
+ assign("continue", label("ev_appl_accum_last_arg")),
+ go_to(label("eval_dispatch")),
+
"ev_appl_accum_last_arg",
- restore(argl),
- assign("argl", op("adjoin_arg"), reg("val"), reg("argl")),
- restore("fun"),
- go_to(label("apply_dispatch")),
+ restore("argl"),
+ assign("argl", list(op("adjoin_arg"), reg("val"), reg("argl"))),
+ restore("fun"),
+ go_to(label("apply_dispatch")),
@@ -432,10 +432,10 @@ function is_last_operand(ops) {
determined by the order of evaluation of the arguments to conspair
in the
procedurefunction
- list-of-values of section
- (see exercise). Because the first\?operand
- selector (used in ev-appl-operand-loop to extract successive operands
- from unev) is implemented as carhead and the rest-operands selector is implemented as cdrtail, the
+ list-of-valueslist_of_values of section
+ (see exercise). Because the first-operandfirst_operand
+ selector (used in ev-appl-operand-loopev_appl_operand_loop to extract successive operands
+ from unev) is implemented as carhead and the rest-operandsrest_operands selector is implemented as cdrtail, the
explicit-control evaluator will evaluate the operands of a combination
in left-to-right order.
explicit-control evaluator for Schemeoperand evaluation|)
@@ -448,14 +448,14 @@ function is_last_operand(ops) {
- The entry point apply-dispatch corresponds to the apply
+ The entry point apply-dispatchapply_dispatch corresponds to the applyprocedurefunction
- of the metacircular evaluator. By the time we get to apply-dispatch, the proc register contains the
+ of the metacircular evaluator. By the time we get to apply-dispatchapply_dispatch, the proc register contains the
procedurefunction
to
apply and argl contains the list of evaluated arguments to which
it must be applied. The saved value of continue (originally
- passed to eval-dispatch and saved at ev-application),
+ passed to eval-dispatcheval_dispatch and saved at ev-applicationev_application),
which tells where to return with the result of the
procedurefunction
application, is on the stack. When the application is complete, the
@@ -477,13 +477,12 @@ function is_last_operand(ops) {
(goto (label unknown-procedure-type))
-
- "apply_dispatch",
- test(op("is_primitive_procedure"), reg("fun")),
- branch(label("primitive_apply")),
- test(op("is_compound_procedure"), reg("fun")),
- branch(label("compound_apply")),
- go_to(label("unknown_procedure_type")),
+"apply_dispatch",
+ test(op("is_primitive_function"), reg("fun")),
+ branch(label("primitive_apply")),
+ test(op("is_compound_function"), reg("fun")),
+ branch(label("compound_apply")),
+ go_to(label("unknown_procedure_type")),
@@ -511,13 +510,13 @@ function is_last_operand(ops) {
Since we are interested in the structure of
the evaluation process rather than the details of the primitives, we
- will instead just use an apply-primitive-procedure operation
+ will instead just use an apply-primitive-procedureapply_primitive_procedure operation
that applies the
procedurefunction
in proc to the arguments in argl. For the purpose of simulating the evaluator with the simulator
of section we use the
procedurefunction
- apply-primitive-procedure, which calls on the underlying Scheme
+ apply-primitive-procedureapply_primitive_procedure, which calls on the underlying Scheme
system to perform the application, just as we did for the metacircular
evaluator in section. After computing the
value of the primitive application, we restore continue and go
@@ -534,11 +533,13 @@ function is_last_operand(ops) {
(goto (reg continue))
-
- "primitive_apply",
- assign("val", op("apply_primitive_procedure"), reg("fun"), reg("argl")),
- restore("continue"),
- go_to(reg("continue")),
+"primitive_apply",
+ assign(
+ "val",
+ list(op("apply_primitive_function"), reg("fun"), reg("argl"))
+ ),
+ restore("continue"),
+ go_to(reg("continue")),
@@ -554,7 +555,7 @@ function is_last_operand(ops) {
procedurefunction, and evaluate in this
extended environment the sequence of expressions that forms the body
of the
- procedurefunction. Ev-sequence, described below in
+ procedurefunction. Ev-sequenceEv_sequence, described below in
section, handles the evaluation
of the sequence.
@@ -570,19 +571,32 @@ function is_last_operand(ops) {
(goto (label ev-sequence))
-
- "compound_apply",
- assign("unev", op("procedure_parameters"), reg("fun")),
- assign("env", op("procedure_environment") reg("fun")),
- assign("env", op("extend_environment"), reg("unev"), reg("argl"), reg("env")),
- assign("unev", op("procedure_body"), reg("fun")),
- go_to(label("ev_sequence")),
+"compound_apply",
+ assign("unev", list(op("function_parameters"), reg("fun"))), // params
+ // FIXME: A QUICK HACK HERE, UNSURE WHY IT'S NEEDED
+ assign("unev", list(op("all_names_of_names"), reg("unev"))), // params destructured
+
+ assign("temp", list(op("function_body"), reg("fun"))), // body
+ assign("temp", list(op("local_names"), reg("unev"))), // locals
+
+ assign("unev", list(op("insert_all"), reg("unev"), reg("temp"))), //names
+ assign("temp", list(op("get_temp_block_values"), reg("temp"))), // temp_values
+
+ assign("temp", list(op("append"), reg("argl"), reg("temp"))), // values
+ assign("env", list(op("function_environment"), reg("fun"))),
+
+ assign(
+ "env",
+ list(op("extend_environment"), reg("unev"), reg("temp"), reg("env"))
+ ),
+ assign("unev", list(op("function_body"), reg("fun"))),
+ go_to(label("ev_sequence")),
- Compound-apply is the only place in the interpreter where the
+ Compound-applyCompound_apply is the only place in the interpreter where the
env register is ever assigned a new value. Just as in the
metacircular evaluator, the new environment is constructed from the
environment carried by the
diff --git a/xml/chapter5/section4/subsection2.xml b/xml/chapter5/section4/subsection2.xml
index ac9e912c6..5b8c8f88f 100644
--- a/xml/chapter5/section4/subsection2.xml
+++ b/xml/chapter5/section4/subsection2.xml
@@ -7,7 +7,7 @@
explicit-control evaluator for Schemesequences of expressions|(
- The portion of the explicit-control evaluator at ev-sequenceev-sequence is
+ The portion of the explicit-control evaluator at ev-sequenceev_sequence is
analogous to the metacircular evaluators eval-sequenceeval_sequenceprocedurefunction. It
handles sequences of expressions in
@@ -86,23 +86,28 @@
(goto (label eval-dispatch))
-
- "ev_sequence",
- assign(exp(op("first_exp"), reg("unev"))),
- test(op("is_last_exp"), reg("unev")),
- branch(label("ev_sequence_last_exp")),
- save("unev"),
- save("env"),
- assign("continue", label("ev_sequence_continue")),
- go_to(label("eval_dispatch")),
- "ev_sequence_continue",
- restore("env"),
- restore("unev"),
- assign("unev", op("rest_exps"), reg("unev")),
- go_to(label("ev_sequence")),
- "ev_sequence_last_exp",
- restore("continue"),
- go_to(label("eval_dispatch")),
+"ev_seq", //FIXME: not part of original book -- explain why needed
+ save("continue"),
+ assign("unev", list(op("sequence_statements"), reg("exp"))),
+
+"ev_sequence",
+ assign("exp", list(op("first_statement"), reg("unev"))),
+ test(op("is_last_statement"), reg("unev")),
+ branch(label("ev_sequence_last_exp")),
+ save("unev"),
+ save("env"),
+ assign("continue", label("ev_sequence_continue")),
+ go_to(label("eval_dispatch")),
+
+"ev_sequence_continue",
+ restore("env"),
+ restore("unev"),
+ assign("unev", list(op("rest_statements"), reg("unev"))),
+ go_to(label("ev_sequence")),
+
+"ev_sequence_last_exp",
+ restore("continue"),
+ go_to(label("eval_dispatch")),
@@ -127,11 +132,11 @@
x)))
- function sqrt_iter(guess, x) {
- return is_good_enough(guess, x)
- ? guess
- : sqrt_iter(improve(guess, x), x);
- }
+function sqrt_iter(guess, x) {
+ return is_good_enough(guess, x)
+ ? guess
+ : sqrt_iter(improve(guess, x), x);
+}
is an iterative process. Even though the
@@ -201,9 +206,9 @@
(define (no-more-exps? seq) (null? seq))
- function has_no_more_exps(seq) {
- return is_null(seq);
- }
+function has_no_more_exps(seq) {
+ return is_null(seq);
+}
@@ -229,23 +234,23 @@
(goto (reg continue))
-
- "ev_sequence",
- test(op("has_no_more_exps"), reg("unev")),
- branch(label("ev_sequence_end")),
- assign(exp(op("first_exp"), reg("unev")),
- save("unev"),
- save("env"),
- assign(continue(label("ev_sequence_continue"))),
- go_to(label("eval_dispatch")),
- "ev_sequence_continue",
- restore("env"),
- restore("unev"),
- assign("unev", op("rest_exps"), reg("unev")),
- go_to(label("ev_sequence")),
- "ev_sequence_end",
- restore("continue"),
- go_to(reg("continue")),
+
+"ev_sequence",
+ test(op("has_no_more_exps"), reg("unev")),
+ branch(label("ev_sequence_end")),
+ assign(exp(op("first_exp"), reg("unev")),
+ save("unev"),
+ save("env"),
+ assign(continue(label("ev_sequence_continue"))),
+ go_to(label("eval_dispatch")),
+"ev_sequence_continue",
+ restore("env"),
+ restore("unev"),
+ assign("unev", op("rest_exps"), reg("unev")),
+ go_to(label("ev_sequence")),
+"ev_sequence_end",
+ restore("continue"),
+ go_to(reg("continue")),
@@ -278,10 +283,10 @@
(count (+ n 1)))
- function count(n) {
- display(n, "\n");
- count(n + 1);
- }
+function count(n) {
+ display(n, "\n");
+ count(n + 1);
+}
Without tail recursion, such a
diff --git a/xml/chapter5/section4/subsection3.xml b/xml/chapter5/section4/subsection3.xml
index c711911ef..3c1b5190e 100644
--- a/xml/chapter5/section4/subsection3.xml
+++ b/xml/chapter5/section4/subsection3.xml
@@ -32,14 +32,14 @@
(goto (label eval-dispatch)) ; evaluate the predicate
-
- "ev_if",
- save("exp"), // save expression for later
- save("env"),
- save("continue"),
- assign("continue", label("ev_if_decide")),
- assign("exp", op("if_predicate"), reg("exp")),
- go_to(label("eval_dispatch")), // evaluate the predicate
+
+"ev_if",
+ save("exp"), // save expression for later
+ save("env"),
+ save("continue"),
+ assign("continue", label("ev_if_decide")),
+ assign("exp", list(op("conditional_pred"), reg("exp"))),
+ go_to(label("eval_dispatch")), // evaluate the predicate
@@ -68,20 +68,20 @@
(goto (label eval-dispatch))
- "ev_if_decide",
- restore("continue"),
- restore("env"),
- restore("exp"),
- test(op("is_true"), reg("val")),
- branch(label("ev_if_consequent")),
-
- "ev_if_alternative",
- assign("exp", op("if_alternative"), reg("exp")),
- go_to(label("eval_dispatch")),
-
- "ev_if_consequent",
- assign("exp", op("if_consequent"), reg("exp")),
- go_to(label("eval_dispatch")),
+"ev_if_decide",
+ restore("continue"),
+ restore("env"),
+ restore("exp"),
+ test(op("is_true"), reg("val")),
+ branch(label("ev_if_consequent")),
+
+"ev_if_alternative",
+ assign("exp", list(op("conditional_alt"), reg("exp"))),
+ go_to(label("eval_dispatch")),
+
+"ev_if_consequent",
+ assign("exp", list(op("conditional_cons"), reg("exp"))),
+ go_to(label("eval_dispatch")),
@@ -118,23 +118,25 @@
(goto (reg continue))
-
- "ev_assignment",
- assign("unev", op("assignment_variable"), reg("exp")),
- save("unev"), // save variable for later
- assign("exp", op("assignment_value"), reg("exp")),
- save("env"),
- save("continue"),
- assign("continue", label("ev_assignment_1")),
- go_to(label("eval_dispatch")), // evaluate the assignment value
-
- "ev_assignment_1",
- restore("continue"),
- restore("env"),
- restore("unev"),
- perform(op("set_variable_value"), reg("unev"), reg("val"), reg("env")),
- assign("val", const("ok")),
- go_to(reg("continue")),
+
+"ev_assignment",
+ assign("unev", list(op("assignment_name"), reg("exp"))),
+ save("unev"), // save variable for later
+ assign("exp", list(op("assignment_value"), reg("exp"))),
+ save("env"),
+ save("continue"),
+ assign("continue", label("ev_assignment_1")),
+ go_to(label("eval_dispatch")), // evaluate the assignment value
+
+"ev_assignment_1",
+ restore("continue"),
+ restore("env"),
+ restore("unev"),
+ perform(
+ list(op("assign_name_value"), reg("unev"), reg("val"), reg("env"))
+ ),
+ assign("val", list(constant("ok"))),
+ go_to(reg("continue")),
@@ -163,23 +165,23 @@
(goto (reg continue))
-
- "ev-definition",
- assign("unev", op("definition-variable"), reg("exp")),
- save("unev"), // save variable for later
- assign("exp", op("definition-value"), reg("exp")),
- save("env"),
- save("continue"),
- assign("continue", label("ev-definition-1")),
- go_to(label("eval-dispatch")), // evaluate the definition value
-
- "ev-definition-1",
- restore("continue"),
- restore("env"),
- restore("unev"),
- perform(op("define-variable"), reg("unev"), reg("val"), reg("env")),
- assign("val", const("ok")),
- go_to(reg("continue")),
+
+"ev_variable_declaration",
+ assign("unev", list(op("variable_declaration_name"), reg("exp"))),
+ save("unev"), // save variable for later
+ assign("exp", list(op("variable_declaration_value"), reg("exp"))),
+ save("env"),
+ save("continue"),
+ assign("continue", label("ev_variable_declaration_1")),
+ go_to(label("eval_dispatch")), // evaluate the declaration value
+
+"ev_variable_declaration_1",
+ restore("continue"),
+ restore("env"),
+ restore("unev"),
+ perform(list(op("declare_value"), reg("unev"), reg("val"), reg("env"))),
+ assign("val", list(constant("ok"))),
+ go_to(reg("continue")),
@@ -205,7 +207,7 @@
Implement cond as a new basic special form without
reducing it to if. You will have to construct a loop that tests
the predicates of successive cond clauses until you find one
- that is true, and then use ev-sequence to evaluate the actions
+ that is true, and then use ev-sequenceev_sequence to evaluate the actions
of the clause.
diff --git a/xml/chapter5/section4/subsection4.xml b/xml/chapter5/section4/subsection4.xml
index c80b95b70..8d3402d83 100644
--- a/xml/chapter5/section4/subsection4.xml
+++ b/xml/chapter5/section4/subsection4.xml
@@ -89,19 +89,21 @@ function get_global_environment() {
promptsexplicit-control evaluator
-
- "read_eval_print_loop",
- perform(op("initialize_stack")),
- perform(op("prompt_for_input"), const("/// EC_Eval input:")),
- assign("exp", op("read")),
- assign("env", op("get_global_environment")),
- assign("continue", label("print_result")),
- go_to(label("eval_dispatch")),
-
- "print_result",
- perform(op("announce_output"), const(";;; EC_Eval value:")),
- perform(op("user_print"), reg("val")),
- go_to(label("read_eval_print_loop")),
+
+/// FIXME: missing from PR39
+"read_eval_print_loop",
+ perform(op("initialize_stack")),
+ perform(op("prompt_for_input"), const("/// EC_Eval input:")),
+ assign("exp", op("read")),
+ assign("env", op("get_global_environment")),
+ assign("continue", label("print_result")),
+ go_to(label("eval_dispatch")),
+
+
+"print_result",
+ perform(op("announce_output"), const(";;; EC_Eval value:")),
+ perform(op("user_print"), reg("val")),
+ go_to(label("read_eval_print_loop")),
@@ -137,21 +139,21 @@ function get_global_environment() {
(goto (label read-eval-print-loop))
-
- "unknown_expression_type",
- assign("val", const("unknown_expression_type_error")),
- go_to(label("signal_error")),
-
-
- "unknown_procedure_type",
- restore("continue"), /// clean up stack (from apply_dispatch)
- assign(val(const("unknown_procedure_type_error"))),
- go_to(label("signal_error")),
-
-
- "signal_error",
- perform(op("user_print"), reg("val")),
- go_to(label("read_eval_print_loop")),
+
+"unknown_expression_type",
+ assign("val", list(constant("unknown_expression_type_error"))),
+ go_to(label("signal_error")),
+
+
+"unknown_procedure_type",
+ restore("continue"), /// clean up stack (from apply_dispatch)
+ assign("val", list(constant("unknown_procedure_type_error"))),
+ go_to(label("signal_error")),
+
+
+"signal_error",
+ perform(list(op("user_print"), reg("val"))),
+ go_to(label("evaluator_done")),
@@ -186,7 +188,7 @@ function get_global_environment() {
function eceval() {
- return make_machine(list("exp", "env", "val", "proc", "argl", "continue", "unev"),
+ return make_machine(list("exp", "env", "val", "proc", "argl", "continue", "unev", "temp"), // FIXME: remove temp?
eceval_operations,
list(read_eval_print_loop,
... /* entire machine controller as given above */
@@ -244,6 +246,1702 @@ const eceval_operations =
;;; EC-Eval value:
(a b c d e f)
+
+24
+
+
+/*
+ Explicit control evaluator for Source §1 sublanguage
+
+ This is an "explicit control evaluator", that is, a register machine that acts as a meta-circular evaluator.
+ It is described in 5.4 of SICP JS (https://sicp.comp.nus.edu.sg/chapters/109#top).
+
+ An example usage:
+ ```
+ set_program_to_run("1 + 3 * 4;"); // This parses the program and loads it into the register machine
+ start(m); // This runs the register machine
+ get_register_contents(m, "val"); // The output is in the register named "val"
+ ```
+
+ The Source §1 evaluator skeleton code is in lines 20-558
+ The skeleton code for a register machine is in lines 560-1160
+ The register machine is in the function "evaluator_machine" (lines 1162-1671)
+*/
+
+/*
+ Evaluator for language with booleans, conditionals,
+ sequences, functions, constants, variables and blocks
+ This is an evaluator for a language that lets you declare
+ functions, variables and constants, apply functions, and
+ carry out simple arithmetic calculations, boolean operations.
+ The covered Source §1 sublanguage is:
+ stmt ::= const name = expr ;
+ | let name = expr ;
+ | function name(params) block
+ | expr ;
+ | stmt stmt
+ | name = expr ;
+ | block
+ block ::= { stmt }
+ expr ::= expr ? expr : expr
+ | expr binop expr
+ | unop expr
+ | name
+ | number
+ | expr(expr, expr, ...)
+ binop ::= + | - | * | / | % | < | > | <= | >=
+ | === | !== | && | ||
+ unop ::= ! | -
+*/
+
+/* CONSTANTS: NUMBERS, STRINGS, TRUE, FALSE */
+
+// constants (numbers, strings, booleans)
+// are considered "self_evaluating". This means, they
+// represent themselves in the syntax tree
+
+function is_self_evaluating(stmt) {
+ return is_number(stmt) || is_string(stmt) || is_boolean(stmt);
+}
+
+// all other statements and expressions are
+// tagged lists. Their tag tells us what
+// kind of statement/expression they are
+
+function is_tagged_list(stmt, the_tag) {
+ return is_pair(stmt) && head(stmt) === the_tag;
+}
+
+/* NAMES */
+
+// In this evaluator, the operators are referred
+// to as "names" in expressions.
+
+// Names are tagged with "name".
+// In this evaluator, typical names
+// are
+// list("name", "+")
+// list("name", "factorial")
+// list("name", "n")
+
+function is_name(stmt) {
+ return is_tagged_list(stmt, "name");
+}
+function name_of_name(stmt) {
+ return head(tail(stmt));
+}
+
+/* CONSTANT DECLARATIONS */
+
+// constant declarations are tagged with "constant_declaration"
+// and have "name" and "value" properties
+
+function is_constant_declaration(stmt) {
+ return is_tagged_list(stmt, "constant_declaration");
+}
+function constant_declaration_name(stmt) {
+ return head(tail(head(tail(stmt))));
+}
+function constant_declaration_value(stmt) {
+ return head(tail(tail(stmt)));
+}
+
+/* VARIABLE DECLARATIONS */
+
+function is_variable_declaration(stmt) {
+ return is_tagged_list(stmt, "variable_declaration");
+}
+function variable_declaration_name(stmt) {
+ return head(tail(head(tail(stmt))));
+}
+function variable_declaration_value(stmt) {
+ return head(tail(tail(stmt)));
+}
+
+/* CONDITIONAL EXPRESSIONS */
+
+// conditional expressions are tagged
+// with "conditional_expression"
+
+function is_conditional_expression(stmt) {
+ return is_tagged_list(stmt, "conditional_expression");
+}
+function cond_expr_pred(stmt) {
+ return list_ref(stmt, 1);
+}
+function cond_expr_cons(stmt) {
+ return list_ref(stmt, 2);
+}
+function cond_expr_alt(stmt) {
+ return list_ref(stmt, 3);
+}
+function is_true(x) {
+ return x === true;
+}
+
+/* FUNCTION DEFINITION EXPRESSIONS */
+
+// function definitions are tagged with "function_definition"
+// have a list of "parameters" and a "body" statement
+
+function is_function_definition(stmt) {
+ return is_tagged_list(stmt, "function_definition");
+}
+function function_definition_parameters(stmt) {
+ return head(tail(stmt));
+}
+function function_definition_body(stmt) {
+ return head(tail(tail(stmt)));
+}
+
+// compound function values keep track of parameters, body
+// and environment, in a list tagged as "compound_function"
+
+function make_compound_function(parameters, body, env) {
+ return list("compound_function", parameters, body, env);
+}
+function is_compound_function(f) {
+ return is_tagged_list(f, "compound_function");
+}
+function function_parameters(f) {
+ return list_ref(f, 1);
+}
+function function_body(f) {
+ return list_ref(f, 2);
+}
+function function_environment(f) {
+ return list_ref(f, 3);
+}
+
+// evaluating a function definition expression
+// results in a function value. Note that the
+// current environment is stored as the function
+// value's environment
+
+function eval_function_definition(stmt, env) {
+ return make_compound_function(
+ map(name_of_name, function_definition_parameters(stmt)),
+ function_definition_body(stmt),
+ env
+ );
+}
+
+/* SEQUENCES */
+
+// sequences of statements are just represented
+// by tagged lists of statements by the parser.
+
+function is_sequence(stmt) {
+ return is_tagged_list(stmt, "sequence");
+}
+function make_sequence(stmts) {
+ return list("sequence", stmts);
+}
+function sequence_statements(stmt) {
+ return head(tail(stmt));
+}
+function is_empty_sequence(stmts) {
+ return is_null(stmts);
+}
+function is_last_statement(stmts) {
+ return is_null(tail(stmts));
+}
+function first_statement(stmts) {
+ return head(stmts);
+}
+function rest_statements(stmts) {
+ return tail(stmts);
+}
+
+/* FUNCTION APPLICATION */
+
+// The core of our evaluator is formed by the
+// implementation of function applications.
+// Applications are tagged with "application"
+// and have "operator" and "operands"
+
+function is_application(stmt) {
+ return is_tagged_list(stmt, "application");
+}
+function operator(stmt) {
+ return head(tail(stmt));
+}
+function operands(stmt) {
+ return head(tail(tail(stmt)));
+}
+function no_operands(ops) {
+ return is_null(ops);
+}
+function first_operand(ops) {
+ return head(ops);
+}
+function rest_operands(ops) {
+ return tail(ops);
+}
+
+// primitive functions are tagged with "primitive"
+// and come with a Source function "implementation"
+
+function make_primitive_function(impl) {
+ return list("primitive", impl);
+}
+function is_primitive_function(fun) {
+ return is_tagged_list(fun, "primitive");
+}
+function primitive_implementation(fun) {
+ return list_ref(fun, 1);
+}
+
+/* APPLY */
+
+// apply_in_underlying_javascript allows us
+// to make use of JavaScript's primitive functions
+// in order to access operators such as addition
+
+function apply_primitive_function(fun, argument_list) {
+ return apply_in_underlying_javascript(
+ primitive_implementation(fun),
+ argument_list
+ );
+}
+
+// We use a nullary function as temporary value for names whose
+// declaration has not yet been evaluated. The purpose of the
+// function definition is purely to create a unique identity;
+// the function will never be applied and its return value
+// (null) is irrelevant.
+const no_value_yet = () => null;
+
+// The function local_names collects all names declared in the
+// body statements. For a name to be included in the list of
+// local_names, it needs to be declared outside of any other
+// block or function.
+
+function insert_all(xs, ys) {
+ return is_null(xs)
+ ? ys
+ : is_null(member(head(xs), ys))
+ ? pair(head(xs), insert_all(tail(xs), ys))
+ : error(head(xs), "multiple declarations of: ");
+}
+
+function local_names(stmt) {
+ if (is_sequence(stmt)) {
+ const stmts = sequence_statements(stmt);
+ return is_empty_sequence(stmts)
+ ? null
+ : insert_all(
+ local_names(first_statement(stmts)),
+ local_names(make_sequence(rest_statements(stmts)))
+ );
+ } else {
+ return is_constant_declaration(stmt)
+ ? list(constant_declaration_name(stmt))
+ : is_variable_declaration(stmt)
+ ? list(variable_declaration_name(stmt))
+ : null;
+ }
+}
+
+/* RETURN STATEMENTS */
+
+// functions return the value that results from
+// evaluating their expression
+
+function is_return_statement(stmt) {
+ return is_tagged_list(stmt, "return_statement");
+}
+function return_statement_expression(stmt) {
+ return head(tail(stmt));
+}
+
+function make_return_value(content) {
+ return list("return_value", content);
+}
+function is_return_value(value) {
+ return is_tagged_list(value, "return_value");
+}
+function return_value_content(value) {
+ return head(tail(value));
+}
+
+/* ASSIGNMENT */
+
+function is_assignment(stmt) {
+ return is_tagged_list(stmt, "assignment");
+}
+function assignment_name(stmt) {
+ return head(tail(head(tail(stmt))));
+}
+function assignment_value(stmt) {
+ return head(tail(tail(stmt)));
+}
+
+/* BLOCKS */
+
+// blocks are tagged with "block"
+function is_block(stmt) {
+ return is_tagged_list(stmt, "block");
+}
+function make_block(stmt) {
+ return list("block", stmt);
+}
+function block_body(stmt) {
+ return head(tail(stmt));
+}
+
+/* ENVIRONMENTS */
+
+// frames are pairs with a list of names as head
+// an a list of pairs as tail (values). Each value
+// pair has the proper value as head and a flag
+// as tail, which indicates whether assignment
+// is allowed for the corresponding name
+
+function make_frame(names, values) {
+ return pair(names, values);
+}
+function frame_names(frame) {
+ return head(frame);
+}
+function frame_values(frame) {
+ return tail(frame);
+}
+
+// The first frame in an environment is the
+// "innermost" frame. The tail operation
+// takes you to the "enclosing" environment
+
+function first_frame(env) {
+ return head(env);
+}
+function enclosing_environment(env) {
+ return tail(env);
+}
+function enclose_by(frame, env) {
+ return pair(frame, env);
+}
+function is_empty_environment(env) {
+ return is_null(env);
+}
+
+// set_name_value is used for let and const to give
+// the initial value to the name in the first
+// (innermost) frame of the given environment
+
+function set_name_value(name, val, env) {
+ function scan(names, vals) {
+ return is_null(names)
+ ? error("internal error: name not found")
+ : name === head(names)
+ ? set_head(head(vals), val)
+ : scan(tail(names), tail(vals));
+ }
+ const frame = first_frame(env);
+ return scan(frame_names(frame), frame_values(frame));
+}
+
+// name lookup proceeds from the innermost
+// frame and continues to look in enclosing
+// environments until the name is found
+
+function lookup_name_value(name, env) {
+ function env_loop(env) {
+ function scan(names, vals) {
+ return is_null(names)
+ ? env_loop(enclosing_environment(env))
+ : name === head(names)
+ ? head(head(vals))
+ : scan(tail(names), tail(vals));
+ }
+ if (is_empty_environment(env)) {
+ error(name, "Unbound name: ");
+ } else {
+ const frame = first_frame(env);
+ const value = scan(frame_names(frame), frame_values(frame));
+ if (value === no_value_yet) {
+ error(name, "Name used before declaration: ");
+ } else {
+ return value;
+ }
+ }
+ }
+ return env_loop(env);
+}
+
+// to assign a name to a new value in a specified environment,
+// we scan for the name, just as in lookup_name_value, and
+// change the corresponding value when we find it,
+// provided it is tagged as mutable
+
+function assign_name_value(name, val, env) {
+ function env_loop(env) {
+ function scan(names, vals) {
+ return is_null(names)
+ ? env_loop(enclosing_environment(env))
+ : name === head(names)
+ ? tail(head(vals))
+ ? set_head(head(vals), val)
+ : error("no assignment " + "to constants allowed")
+ : scan(tail(names), tail(vals));
+ }
+ if (is_empty_environment(env)) {
+ error(name, "Unbound name in assignment: ");
+ } else {
+ const frame = first_frame(env);
+ return scan(frame_names(frame), frame_values(frame));
+ }
+ }
+ return env_loop(env);
+}
+
+// applying a compound function to parameters will
+// lead to the creation of a new environment, with
+// respect to which the body of the function needs
+// to be evaluated
+// (also used for blocks)
+
+function extend_environment(names, vals, base_env) {
+ if (length(names) === length(vals)) {
+ return enclose_by(
+ make_frame(
+ names,
+ map((x) => pair(x, true), vals)
+ ),
+ base_env
+ );
+ } else if (length(names) < length(vals)) {
+ error(
+ "Too many arguments supplied: " +
+ stringify(names) +
+ ", " +
+ stringify(vals)
+ );
+ } else {
+ error(
+ "Too few arguments supplied: " + stringify(names) + ", " + stringify(vals)
+ );
+ }
+}
+
+/* THE GLOBAL ENVIRONMENT */
+
+const the_empty_environment = null;
+
+// the minus operation is overloaded to
+// support both binary minus and unary minus
+
+function minus(x, y) {
+ if (is_number(x) && is_number(y)) {
+ return x - y;
+ } else {
+ return -x;
+ }
+}
+
+// the global environment has bindings for all
+// primitive functions, including the operators
+
+const primitive_functions = list(
+ list("display", display),
+ list("error", error),
+ list("+", (x, y) => x + y),
+ list("-", (x, y) => minus(x, y)),
+ list("*", (x, y) => x * y),
+ list("/", (x, y) => x / y),
+ list("%", (x, y) => x % y),
+ list("===", (x, y) => x === y),
+ list("!==", (x, y) => x !== y),
+ list("<", (x, y) => x < y),
+ list("<=", (x, y) => x <= y),
+ list(">", (x, y) => x > y),
+ list(">=", (x, y) => x >= y),
+ list("!", (x) => !x)
+);
+
+// the global environment also has bindings for all
+// primitive non-function values, such as undefined and
+// math_PI
+
+const primitive_constants = list(
+ list("undefined", undefined),
+ list("math_PI", math_PI)
+);
+
+// setup_environment makes an environment that has
+// one single frame, and adds a binding of all names
+// listed as primitive_functions and primitive_values.
+// The values of primitive functions are "primitive"
+// objects, see line 281 how such functions are applied
+
+function setup_environment() {
+ const primitive_function_names = map((f) => head(f), primitive_functions);
+ const primitive_function_values = map(
+ (f) => make_primitive_function(head(tail(f))),
+ primitive_functions
+ );
+ const primitive_constant_names = map((f) => head(f), primitive_constants);
+ const primitive_constant_values = map(
+ (f) => head(tail(f)),
+ primitive_constants
+ );
+ return extend_environment(
+ append(primitive_function_names, primitive_constant_names),
+ append(primitive_function_values, primitive_constant_values),
+ the_empty_environment
+ );
+}
+
+function get_global_environment() {
+ return the_global_environment;
+}
+
+function user_print(object) {
+ return is_compound_function(object)
+ ? "function" +
+ stringify(function_parameters(object)) +
+ stringify(function_body(object)) +
+ ""
+ : object;
+}
+
+// END EVALUATOR SKELETON
+
+// Global registers
+let the_heads = [];
+let the_tails = [];
+let free = 0;
+
+const POINTER_TAG = 800;
+const NUMBER_TAG = 801;
+const STRING_TAG = 802;
+const NULL_TAG = -1;
+
+// Implementation is with arrays...
+function head_(n) {
+ return vector_ref(the_heads, n);
+}
+
+function tail_(n) {
+ return vector_ref(the_tails, n);
+}
+
+function vector_ref(xs, index) {
+ return xs[index];
+}
+
+function vector_set(xs, index, value) {
+ xs[index] = value;
+ display(xs);
+ return undefined;
+}
+
+function pair_(a, b) {
+ // perform(list(op("vector_set"), reg("the_heads"), reg("free"), a));
+ // perform(list(op("vector_set"), reg("the_tails"), reg("free"), b));
+ // assign("free", list(op("+"), reg("free"), constant(1)));
+ // display(is_number(a), "is_number");
+
+ const ret = free;
+ vector_set(the_heads, free, a);
+ vector_set(the_tails, free, b);
+
+ free = free + 1;
+ return ret;
+}
+
+function assoc(key, records) {
+ return is_null(records)
+ ? undefined
+ : equal(key, head(head(records)))
+ ? head(records)
+ : assoc(key, tail(records));
+}
+
+function get_contents(register) {
+ return register("get");
+}
+
+function set_contents(register, value) {
+ return register("set")(value);
+}
+
+function op(name) {
+ return list("op", name);
+}
+
+function reg(name) {
+ return list("reg", name);
+}
+
+function label(name) {
+ return list("label", name);
+}
+
+function constant(value) {
+ return list("constant", value);
+}
+
+function branch(label) {
+ return list("branch", label);
+}
+
+function assign(register_name, source) {
+ const a = append(list("assign", register_name), source);
+ return a;
+}
+
+function perform(args) {
+ const a = append(list("perform"), args);
+ return a;
+}
+
+// Made an n-ary function helper...
+function nary_function(f) {
+ // f is n-ary
+ return (arg_list) => apply_in_underlying_javascript(f, arg_list);
+}
+
+function save(value) {
+ return list("save", value);
+}
+
+function restore(value) {
+ return list("restore", value);
+}
+
+function go_to(label) {
+ return list("go_to", label);
+}
+
+function test(op, lhs) {
+ return list("test", op, lhs, constant(true));
+}
+
+function binary_function(f) {
+ // f is binary
+ return (arg_list) =>
+ length(arg_list) === 2
+ ? apply_in_underlying_javascript(f, arg_list)
+ : error(
+ arg_list,
+ "Incorrect number of arguments passed to binary function "
+ );
+}
+
+function make_machine(register_names, ops, controller_text) {
+ const machine = make_new_machine();
+
+ map((reg_name) => machine("allocate_register")(reg_name), register_names);
+ machine("install_operations")(ops);
+ machine("install_instruction_sequence")(assemble(controller_text, machine));
+
+ return machine;
+}
+
+function make_register(name) {
+ let contents = "*unassigned*";
+
+ function dispatch(message) {
+ if (message === "get") {
+ return contents;
+ } else {
+ if (message === "set") {
+ return (value) => {
+ contents = value;
+ };
+ } else {
+ error(message, "Unknown request: REGISTER");
+ }
+ }
+ }
+
+ return dispatch;
+}
+
+function debug(reg) {
+ display(reg);
+}
+
+function make_stack() {
+ let stack = null;
+
+ function push(x) {
+ stack = pair(x, stack);
+ return "done";
+ }
+
+ function pop() {
+ if (is_null(stack)) {
+ error("Empty stack: POP");
+ } else {
+ const top = head(stack);
+ stack = tail(stack);
+ return top;
+ }
+ }
+
+ function initialize() {
+ stack = null;
+ return "done";
+ }
+
+ function dispatch(message) {
+ return message === "push"
+ ? push
+ : message === "pop"
+ ? pop()
+ : message === "initialize"
+ ? initialize()
+ : error("Unknown request: STACK", message);
+ }
+
+ return dispatch;
+}
+
+function pop(stack) {
+ return stack("pop");
+}
+
+function push(stack, value) {
+ return stack("push")(value);
+}
+
+function make_new_machine() {
+ const pc = make_register("pc");
+ const flag = make_register("flag");
+ const stack = make_stack();
+ let the_instruction_sequence = null;
+ let the_ops = list(list("initialize_stack", () => stack("initialize")));
+ let register_table = list(list("pc", pc), list("flag", flag));
+
+ function allocate_register(name) {
+ if (assoc(name, register_table) === undefined) {
+ register_table = pair(list(name, make_register(name)), register_table);
+ } else {
+ error(name, "Multiply defined register: ");
+ }
+
+ return "register_allocated";
+ }
+
+ function lookup_register(name) {
+ const val = assoc(name, register_table);
+
+ return val === undefined
+ ? error(name, "Unknown register:")
+ : head(tail(val));
+ }
+
+ function execute() {
+ const insts = get_contents(pc);
+
+ if (is_null(insts)) {
+ return "done";
+ } else {
+ const proc = instruction_execution_proc(head(insts));
+ proc();
+ return execute();
+ }
+ }
+
+ function dispatch(message) {
+ return message === "start"
+ ? () => {
+ set_contents(pc, the_instruction_sequence);
+ return execute();
+ }
+ : message === "install_instruction_sequence"
+ ? (seq) => {
+ the_instruction_sequence = seq;
+ }
+ : message === "allocate_register"
+ ? allocate_register
+ : message === "get_register"
+ ? lookup_register
+ : message === "install_operations"
+ ? (ops) => {
+ the_ops = append(the_ops, ops);
+ }
+ : message === "stack"
+ ? stack
+ : message === "operations"
+ ? the_ops
+ : error(message, "Unknown request: MACHINE");
+ }
+
+ return dispatch;
+}
+
+function start(machine) {
+ return machine("start")();
+}
+
+function get_register_contents(machine, register_name) {
+ return get_contents(get_register(machine, register_name));
+}
+
+function set_register_contents(machine, register_name, value) {
+ set_contents(get_register(machine, register_name), value);
+ return "done";
+}
+
+function get_register(machine, reg_name) {
+ return machine("get_register")(reg_name);
+}
+
+function assemble(controller_text, machine) {
+ function receive(insts, labels) {
+ update_insts(insts, labels, machine);
+ return insts;
+ }
+
+ return extract_labels(controller_text, receive);
+}
+
+function extract_labels(text, receive) {
+ function helper(insts, labels) {
+ /// FIXME: rename to something useful
+ const next_inst = head(text);
+
+ return is_string(next_inst)
+ ? receive(insts, pair(make_label_entry(next_inst, insts), labels))
+ : receive(pair(make_instruction(next_inst), insts), labels);
+ }
+
+ return text === undefined || is_null(text)
+ ? receive(null, null)
+ : extract_labels(tail(text), helper);
+}
+
+function update_insts(insts, labels, machine) {
+ const pc = get_register(machine, "pc");
+ const flag = get_register(machine, "flag");
+ const stack = machine("stack");
+ const ops = machine("operations");
+
+ const set_iep = set_instruction_execution_proc;
+ const make_ep = make_execution_procedure;
+ return map(
+ (i) =>
+ set_iep(
+ i,
+ make_ep(instruction_text(i), labels, machine, pc, flag, stack, ops)
+ ),
+ insts
+ );
+}
+
+function make_instruction(text) {
+ return pair(text, null);
+}
+
+function instruction_text(inst) {
+ return head(inst);
+}
+
+function instruction_execution_proc(inst) {
+ return tail(inst);
+}
+
+function set_instruction_execution_proc(inst, proc) {
+ set_tail(inst, proc);
+}
+
+function make_label_entry(label_name, insts) {
+ return pair(label_name, insts);
+}
+
+function lookup_label(labels, label_name) {
+ const val = assoc(label_name, labels);
+
+ return val === undefined
+ ? error(label_name, "Undefined label: ASSEMBLE")
+ : tail(val);
+}
+
+function make_execution_procedure(inst, labels, machine, pc, flag, stack, ops) {
+ const x = head(inst);
+
+ return x === "assign"
+ ? make_assign(inst, machine, labels, ops, pc)
+ : x === "test"
+ ? make_test(inst, machine, labels, ops, flag, pc)
+ : x === "branch"
+ ? make_branch(inst, machine, labels, flag, pc)
+ : x === "go_to"
+ ? make_goto(inst, machine, labels, pc)
+ : x === "save"
+ ? make_save(inst, machine, stack, pc)
+ : x === "restore"
+ ? make_restore(inst, machine, stack, pc)
+ : x === "perform"
+ ? make_perform(inst, machine, labels, ops, pc)
+ : error(inst, "Unknown instruction type: ASSEMBLE");
+}
+
+function make_assign(inst, machine, labels, operations, pc) {
+ const target = get_register(machine, assign_reg_name(inst));
+ const value_exp = assign_value_exp(inst);
+
+ // FIX FOR LABEL
+ const value_proc = is_operation_exp(value_exp)
+ ? make_operation_exp(value_exp, machine, labels, operations)
+ : // fix for label...
+ is_label_exp(value_exp)
+ ? make_primitive_exp(value_exp, machine, labels)
+ : make_primitive_exp(head(value_exp), machine, labels);
+
+ function perform_make_assign() {
+ set_contents(target, value_proc());
+ advance_pc(pc);
+ }
+
+ return perform_make_assign;
+}
+
+function assign_reg_name(assign_instruction) {
+ return head(tail(assign_instruction));
+}
+
+function assign_value_exp(assign_instruction) {
+ return tail(tail(assign_instruction));
+}
+
+function advance_pc(pc) {
+ set_contents(pc, tail(get_contents(pc)));
+}
+
+function make_test(inst, machine, labels, operations, flag, pc) {
+ const condition = test_condition(inst);
+
+ if (is_operation_exp(condition)) {
+ const condition_proc = make_operation_exp(
+ condition,
+ machine,
+ labels,
+ operations
+ );
+
+ function perform_make_test() {
+ set_contents(flag, condition_proc());
+ advance_pc(pc);
+ }
+
+ return perform_make_test;
+ } else {
+ error(inst, "Bad TEST instruction: ASSEMBLE");
+ }
+}
+
+function test_condition(test_instruction) {
+ return tail(test_instruction);
+}
+
+function make_branch(inst, machine, labels, flag, pc) {
+ const dest = branch_dest(inst);
+
+ if (is_label_exp(dest)) {
+ const insts = lookup_label(labels, label_exp_label(dest));
+
+ function perform_make_branch() {
+ if (get_contents(flag)) {
+ set_contents(pc, insts);
+ } else {
+ advance_pc(pc);
+ }
+ }
+
+ return perform_make_branch;
+ } else if (is_register_exp(dest)) {
+ const reg = get_register(machine, register_exp_reg(dest));
+
+ function perform_make_branch() {
+ if (get_contents(flag)) {
+ set_contents(pc, get_contents(reg));
+ } else {
+ advance_pc(pc);
+ }
+ }
+
+ return perform_make_branch;
+ } else {
+ error(inst, "Bad BRANCH instruction: ASSEMBLE");
+ }
+}
+
+function branch_dest(branch_instruction) {
+ return head(tail(branch_instruction));
+}
+
+function make_goto(inst, machine, labels, pc) {
+ const dest = goto_dest(inst);
+
+ if (is_label_exp(dest)) {
+ const insts = lookup_label(labels, label_exp_label(dest));
+ return () => set_contents(pc, insts);
+ } else if (is_register_exp(dest)) {
+ const reg = get_register(machine, register_exp_reg(dest));
+ return () => set_contents(pc, get_contents(reg));
+ } else {
+ error(inst, "Bad GOTO instruction: ASSEMBLE");
+ }
+}
+
+function goto_dest(goto_instruction) {
+ return head(tail(goto_instruction));
+}
+
+function make_save(inst, machine, stack, pc) {
+ const reg = get_register(machine, stack_inst_reg_name(inst));
+
+ function perform_make_save() {
+ push(stack, get_contents(reg));
+ advance_pc(pc);
+ }
+
+ return perform_make_save;
+}
+
+function make_restore(inst, machine, stack, pc) {
+ const reg = get_register(machine, stack_inst_reg_name(inst));
+
+ function perform_make_restore() {
+ set_contents(reg, pop(stack));
+ advance_pc(pc);
+ }
+
+ return perform_make_restore;
+}
+
+function stack_inst_reg_name(stack_instruction) {
+ return head(tail(stack_instruction));
+}
+
+function make_perform(inst, machine, labels, operations, pc) {
+ const action = perform_action(inst);
+
+ if (is_operation_exp(action)) {
+ const action_proc = make_operation_exp(action, machine, labels, operations);
+ return () => {
+ action_proc();
+ advance_pc(pc);
+ };
+ } else {
+ error(inst, "Bad PERFORM instruction: ASSEMBLE");
+ }
+}
+
+function perform_action(inst) {
+ return tail(inst);
+}
+
+function make_primitive_exp(exp, machine, labels) {
+ if (is_constant_exp(exp)) {
+ const c = constant_exp_value(exp);
+ return () => c;
+ } else if (is_label_exp(exp)) {
+ const insts = lookup_label(labels, label_exp_label(exp));
+ return () => insts;
+ } else if (is_register_exp(exp)) {
+ const r = get_register(machine, register_exp_reg(exp));
+ return () => get_contents(r);
+ } else {
+ error(exp, "Unknown expression type: ASSEMBLE");
+ }
+}
+
+function is_register_exp(exp) {
+ return is_tagged_list(exp, "reg");
+}
+
+function register_exp_reg(exp) {
+ return head(tail(exp));
+}
+
+function is_constant_exp(exp) {
+ return is_tagged_list(exp, "constant");
+}
+
+function constant_exp_value(exp) {
+ return head(tail(exp));
+}
+
+function is_label_exp(exp) {
+ return is_tagged_list(exp, "label");
+}
+
+function label_exp_label(exp) {
+ return head(tail(exp));
+}
+
+function make_operation_exp(exp, machine, labels, operations) {
+ const op = lookup_prim(operation_exp_op(exp), operations);
+ const aprocs = map(
+ (e) => make_primitive_exp(e, machine, labels),
+ operation_exp_operands(exp)
+ );
+
+ function perform_make_operation_exp() {
+ return op(map((p) => p(), aprocs));
+ }
+
+ return perform_make_operation_exp;
+}
+
+function is_operation_exp(exp) {
+ return is_pair(exp) && is_tagged_list(head(exp), "op");
+}
+
+function operation_exp_op(operation_exp) {
+ return head(tail(head(operation_exp)));
+}
+
+function operation_exp_operands(operation_exp) {
+ return tail(operation_exp);
+}
+
+function lookup_prim(symbol, operations) {
+ const val = assoc(symbol, operations);
+
+ return val === undefined
+ ? error(symbol, "Unknown operation: ASSEMBLE")
+ : head(tail(val));
+}
+
+function eceval() {
+ return make_machine(
+ list(
+ "exp",
+ "env",
+ "val",
+ "continue",
+ "fun",
+ "argl",
+ "unev",
+ "temp" // FIXME: TOBIAS -- not mentioned in 5.4
+ ),
+ list(
+ // basic functions
+ list(
+ "rem",
+ binary_function((a, b) => a % b)
+ ),
+ list(
+ "=",
+ binary_function((a, b) => a === b)
+ ),
+ list(
+ "+",
+ binary_function((a, b) => a + b)
+ ),
+ list(
+ "===",
+ binary_function((a, b) => a === b)
+ ),
+ // operands
+ list("operands", nary_function(operands)),
+ list("operator", nary_function(operator)),
+ list("has_no_operands", nary_function(no_operands)),
+ list("first_operand", nary_function(first_operand)),
+ list(
+ "is_last_operand",
+ nary_function((a) => is_null(tail(a)))
+ ),
+ list("rest_operands", nary_function(rest_operands)),
+
+ //arg
+ list(
+ "empty_arglist",
+ nary_function((_) => list())
+ ),
+ list(
+ "adjoin_arg",
+ nary_function((val, argl) => append(argl, list(val)))
+ ),
+
+ // exp (sequence)
+ list("first_statement", nary_function(first_statement)),
+ list("rest_statements", nary_function(rest_statements)),
+ list("is_last_statement", nary_function(is_last_statement)),
+ list("sequence_statements", nary_function(sequence_statements)),
+
+ // eval functions from meta-circular evaluator
+ list("is_self_evaluating", nary_function(is_self_evaluating)),
+ list("is_name", nary_function(is_name)),
+ list("name_of_name", nary_function(name_of_name)),
+ list(
+ "all_names_of_names",
+ nary_function((names) => map(name_of_name, names))
+ ),
+ list("is_assignment", nary_function(is_assignment)),
+ list("assignment_name", nary_function(assignment_name)),
+ list("assignment_value", nary_function(assignment_value)),
+ list("assign_name_value", nary_function(assign_name_value)),
+ list("is_constant_declaration", nary_function(is_constant_declaration)),
+ list(
+ "constant_declaration_name",
+ nary_function(constant_declaration_name)
+ ),
+ list(
+ "constant_declaration_value",
+ nary_function(constant_declaration_value)
+ ),
+ list("is_variable_declaration", nary_function(is_variable_declaration)),
+ list(
+ "variable_declaration_name",
+ nary_function(variable_declaration_name)
+ ),
+ list(
+ "variable_declaration_value",
+ nary_function(variable_declaration_value)
+ ),
+ list("declare_value", nary_function(set_name_value)),
+ list("is_function_definition", nary_function(is_function_definition)),
+ list(
+ "function_definition_parameters",
+ nary_function(function_definition_parameters)
+ ),
+ list("function_definition_body", nary_function(function_definition_body)),
+ list("is_return_statement", nary_function(is_return_statement)),
+ list(
+ "is_conditional_expression",
+ nary_function(is_conditional_expression)
+ ),
+ list("conditional_pred", nary_function(cond_expr_pred)),
+ list("conditional_cons", nary_function(cond_expr_cons)),
+ list("conditional_alt", nary_function(cond_expr_alt)),
+
+ list("is_sequence", nary_function(is_sequence)),
+ list("make_sequence", nary_function(make_sequence)),
+ list("is_block", nary_function(is_block)),
+ list("block_body", nary_function(block_body)),
+ list("local_names", nary_function(local_names)),
+ list(
+ "get_temp_block_values",
+ nary_function((locals) => map((x) => no_value_yet, locals))
+ ),
+ list("is_application", nary_function(is_application)),
+ list("is_primitive_function", nary_function(is_primitive_function)),
+ list("apply_primitive_function", nary_function(apply_primitive_function)),
+ list("is_compound_function", nary_function(is_compound_function)),
+ list("function_parameters", nary_function(function_parameters)),
+ list("function_environment", nary_function(function_environment)),
+ list("function_body", nary_function(function_body)),
+ list("insert_all", nary_function(insert_all)),
+ list("extend_environment", nary_function(extend_environment)),
+ list("make_compound_function", nary_function(make_compound_function)),
+
+ list(
+ "lookup_name_value",
+ nary_function((stmt, env) => lookup_name_value(name_of_name(stmt), env))
+ ),
+ list("get_global_environment", nary_function(get_global_environment)),
+
+ // generic helpers
+ list("is_true", nary_function(is_true)),
+ list("is_null", nary_function(is_null)),
+ list(
+ "is_pair",
+ nary_function((a) => is_pair(a))
+ ),
+ list(
+ "is_number",
+ nary_function((a) => is_number(a))
+ ),
+ list(
+ "append",
+ nary_function((xs, ys) => append(xs, ys))
+ ),
+
+ list("vector_ref", binary_function(vector_ref)),
+ list("vector_set", nary_function(vector_set)),
+ list("pair", nary_function(pair_)),
+
+ list("user_print", nary_function(user_print)),
+ list("display", nary_function(display))
+ ),
+ list(
+ assign("continue", label("evaluator_done")),
+ assign("env", list(op("get_global_environment"))),
+ "ev_begin",
+
+ // This corresponds to the 'evaluate' function
+ // The workhorse of our evaluator is the evaluate function.
+ // It dispatches on the kind of statement at hand, and
+ // invokes the appropriate implementations of their
+ // evaluation process, as described above, always using
+ // a current environment
+ // See the source code at bottom of file for refrence
+ "eval_dispatch",
+ test(op("is_self_evaluating"), reg("exp")), // <-- TOBIAS need to make "true" default because not in the book
+ branch(label("ev_self_eval")),
+ test(op("is_name"), reg("exp")),
+ branch(label("ev_name")),
+
+ // for now, treat let/const the same
+ test(op("is_variable_declaration"), reg("exp")),
+ branch(label("ev_variable_declaration")),
+ test(op("is_constant_declaration"), reg("exp")),
+ branch(label("ev_constant_declaration")),
+ test(op("is_assignment"), reg("exp")),
+ branch(label("ev_assignment")),
+
+ test(op("is_conditional_expression"), reg("exp")),
+ branch(label("ev_if")),
+ test(op("is_function_definition"), reg("exp")),
+ branch(label("ev_lambda")),
+ test(op("is_sequence"), reg("exp")),
+ branch(label("ev_seq")),
+ test(op("is_block"), reg("exp")),
+ branch(label("ev_block")),
+ test(op("is_application"), reg("exp")),
+ branch(label("ev_application")),
+ go_to(label("unknown_expression_type")),
+
+ "ev_self_eval",
+ assign("val", list(reg("exp"))),
+ go_to(reg("continue")),
+
+ "ev_name",
+ assign("val", list(op("lookup_name_value"), reg("exp"), reg("env"))),
+ go_to(reg("continue")),
+
+ "ev_lambda",
+ assign("unev", list(op("function_definition_parameters"), reg("exp"))),
+ assign("exp", list(op("function_definition_body"), reg("exp"))),
+ assign(
+ "val",
+ list(op("make_compound_function"), reg("unev"), reg("exp"), reg("env"))
+ ),
+ go_to(reg("continue")),
+
+ "ev_application",
+ save("continue"),
+ save("env"),
+ assign("unev", list(op("operands"), reg("exp"))),
+ save("unev"),
+ assign("exp", list(op("operator"), reg("exp"))),
+ assign("continue", label("ev_appl_did_operator")),
+ go_to(label("eval_dispatch")),
+
+ "ev_appl_did_operator",
+ restore("unev"), // the operands
+ restore("env"),
+ assign("argl", list(op("empty_arglist"))),
+ assign("fun", list(reg("val"))), // the operator
+ test(op("has_no_operands"), reg("unev")),
+ branch(label("apply_dispatch")),
+ save("fun"),
+
+ "ev_appl_operand_loop",
+ save("argl"),
+ assign("exp", list(op("first_operand"), reg("unev"))),
+ test(op("is_last_operand"), reg("unev")),
+ branch(label("ev_appl_last_arg")),
+ save("env"),
+ save("unev"),
+ assign("continue", label("ev_appl_accumulate_arg")),
+ go_to(label("eval_dispatch")),
+
+ "ev_appl_accumulate_arg",
+ restore("unev"),
+ restore("env"),
+ restore("argl"),
+ assign("argl", list(op("adjoin_arg"), reg("val"), reg("argl"))),
+ assign("unev", list(op("rest_operands"), reg("unev"))),
+ go_to(label("ev_appl_operand_loop")),
+
+ "ev_appl_last_arg",
+ assign("continue", label("ev_appl_accum_last_arg")),
+ go_to(label("eval_dispatch")),
+
+ "ev_appl_accum_last_arg",
+ restore("argl"),
+ assign("argl", list(op("adjoin_arg"), reg("val"), reg("argl"))),
+ restore("fun"),
+ go_to(label("apply_dispatch")),
+
+ // function application needs to distinguish between
+ // primitive functions (which are evaluated using the
+ // underlying JavaScript), and compound functions.
+ // An application of the latter needs to evaluate the
+ // body of the function value with respect to an
+ // environment that results from extending the function
+ // object's environment by a binding of the function
+ // parameters to the arguments and of local names to
+ // the special value no_value_yet
+
+ // 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 = local_names(body);
+ // const names = insert_all(function_parameters(fun), locals);
+ // const temp_values = map((x) => no_value_yet, locals);
+ // const values = append(args, temp_values);
+ // const result = evaluate(
+ // body,
+ // extend_environment(names, values, function_environment(fun))
+ // );
+ // if (is_return_value(result)) {
+ // return return_value_content(result);
+ // } else {
+ // return undefined;
+ // }
+ // } else {
+ // error(fun, "Unknown function type in apply");
+ // }
+ // }
+ "apply_dispatch",
+ test(op("is_primitive_function"), reg("fun")),
+ branch(label("primitive_apply")),
+ test(op("is_compound_function"), reg("fun")),
+ branch(label("compound_apply")),
+ go_to(label("unknown_procedure_type")),
+
+ "primitive_apply",
+ assign(
+ "val",
+ list(op("apply_primitive_function"), reg("fun"), reg("argl"))
+ ),
+ restore("continue"),
+ go_to(reg("continue")),
+
+ "compound_apply",
+ assign("unev", list(op("function_parameters"), reg("fun"))), // params
+ // A QUICK HACK HERE, UNSURE WHY IT'S NEEDED
+ assign("unev", list(op("all_names_of_names"), reg("unev"))), // params destructured
+
+ assign("temp", list(op("function_body"), reg("fun"))), // body
+ assign("temp", list(op("local_names"), reg("unev"))), // locals
+
+ assign("unev", list(op("insert_all"), reg("unev"), reg("temp"))), //names
+ assign("temp", list(op("get_temp_block_values"), reg("temp"))), // temp_values
+
+ assign("temp", list(op("append"), reg("argl"), reg("temp"))), // values
+ assign("env", list(op("function_environment"), reg("fun"))),
+
+ assign(
+ "env",
+ list(op("extend_environment"), reg("unev"), reg("temp"), reg("env"))
+ ),
+ assign("unev", list(op("function_body"), reg("fun"))),
+ go_to(label("ev_sequence")),
+
+ // to evaluate a sequence, we need to evaluate
+ // its statements one after the other, and return
+ // the value of the last statement.
+ // An exception to this rule is when a return
+ // statement is encountered. In that case, the
+ // remaining statements are ignored and the
+ // return value is the value of the sequence.
+
+ // function eval_sequence(stmts, env) {
+ // if (is_empty_sequence(stmts)) {
+ // return undefined;
+ // } else if (is_last_statement(stmts)) {
+ // return evaluate(first_statement(stmts), env);
+ // } else {
+ // const first_stmt_value = evaluate(first_statement(stmts), env);
+ // if (is_return_value(first_stmt_value)) {
+ // return first_stmt_value;
+ // } else {
+ // return eval_sequence(rest_statements(stmts), env);
+ // }
+ // }
+ // }
+ "ev_seq",
+ save("continue"),
+ assign("unev", list(op("sequence_statements"), reg("exp"))),
+
+ "ev_sequence",
+ assign("exp", list(op("first_statement"), reg("unev"))),
+ test(op("is_last_statement"), reg("unev")),
+ branch(label("ev_sequence_last_exp")),
+ save("unev"),
+ save("env"),
+ assign("continue", label("ev_sequence_continue")),
+ go_to(label("eval_dispatch")),
+
+ "ev_sequence_continue",
+ restore("env"),
+ restore("unev"),
+ assign("unev", list(op("rest_statements"), reg("unev"))),
+ go_to(label("ev_sequence")),
+
+ "ev_sequence_last_exp",
+ restore("continue"),
+ go_to(label("eval_dispatch")),
+
+ // evaluation of blocks evaluates the body of the block
+ // with respect to the current environment extended by
+ // a binding of all local names to the special value
+ // no_value_yet
+
+ // function eval_block(stmt, env) {
+ // const body = block_body(stmt);
+ // const locals = local_names(body);
+ // const temp_values = map((x) => no_value_yet, locals);
+ // return evaluate(body, extend_environment(locals, temp_values, env));
+ // }
+ "ev_block",
+ save("continue"),
+ assign("exp", list(op("block_body"), reg("exp"))),
+ assign("val", list(op("local_names"), reg("exp"))),
+ assign("temp", list(op("get_temp_block_values"), reg("val"))),
+ assign(
+ "env",
+ list(op("extend_environment"), reg("val"), reg("temp"), reg("env"))
+ ),
+ go_to(label("eval_dispatch")),
+
+ // the meta-circular evaluation of conditional expressions
+ // evaluates the predicate and then the appropriate
+ // branch, depending on whether the predicate evaluates to
+ // true or not
+
+ // function eval_conditional_expression(stmt, env) {
+ // return is_true(evaluate(cond_expr_pred(stmt), env))
+ // ? evaluate(cond_expr_cons(stmt), env)
+ // : evaluate(cond_expr_alt(stmt), env);
+ // }
+ "ev_if",
+ save("exp"), // save expression for later
+ save("env"),
+ save("continue"),
+ assign("continue", label("ev_if_decide")),
+ assign("exp", list(op("conditional_pred"), reg("exp"))),
+ go_to(label("eval_dispatch")), // evaluate the predicate
+
+ "ev_if_decide",
+ restore("continue"),
+ restore("env"),
+ restore("exp"),
+ test(op("is_true"), reg("val")),
+ branch(label("ev_if_consequent")),
+
+ "ev_if_alternative",
+ assign("exp", list(op("conditional_alt"), reg("exp"))),
+ go_to(label("eval_dispatch")),
+
+ "ev_if_consequent",
+ assign("exp", list(op("conditional_cons"), reg("exp"))),
+ go_to(label("eval_dispatch")),
+
+ // function eval_assignment(stmt, env) {
+ // const value = evaluate(assignment_value(stmt), env);
+ // assign_name_value(assignment_name(stmt), value, env);
+ // return value;
+ // }
+ "ev_assignment",
+ assign("unev", list(op("assignment_name"), reg("exp"))),
+ save("unev"), // save variable for later
+ assign("exp", list(op("assignment_value"), reg("exp"))),
+ save("env"),
+ save("continue"),
+ assign("continue", label("ev_assignment_1")),
+ go_to(label("eval_dispatch")), // evaluate the assignment value
+
+ "ev_assignment_1",
+ restore("continue"),
+ restore("env"),
+ restore("unev"),
+ perform(
+ list(op("assign_name_value"), reg("unev"), reg("val"), reg("env"))
+ ),
+ assign("val", list(constant("ok"))),
+ go_to(reg("continue")),
+
+ // evaluation of a constant declaration evaluates
+ // the right-hand expression and binds the
+ // name to the resulting value in the
+ // first (innermost) frame
+
+ // function eval_constant_declaration(stmt, env) {
+ // set_name_value(
+ // constant_declaration_name(stmt),
+ // evaluate(constant_declaration_value(stmt), env),
+ // env
+ // );
+ // }
+ "ev_variable_declaration",
+ assign("unev", list(op("variable_declaration_name"), reg("exp"))),
+ save("unev"), // save variable for later
+ assign("exp", list(op("variable_declaration_value"), reg("exp"))),
+ save("env"),
+ save("continue"),
+ assign("continue", label("ev_variable_declaration_1")),
+ go_to(label("eval_dispatch")), // evaluate the declaration value
+
+ "ev_variable_declaration_1",
+ restore("continue"),
+ restore("env"),
+ restore("unev"),
+ perform(list(op("declare_value"), reg("unev"), reg("val"), reg("env"))),
+ assign("val", list(constant("ok"))),
+ go_to(reg("continue")),
+
+ "ev_constant_declaration",
+ assign("unev", list(op("constant_declaration_name"), reg("exp"))),
+ save("unev"), // save constant for later
+ assign("exp", list(op("constant_declaration_value"), reg("exp"))),
+ save("env"),
+ save("continue"),
+ assign("continue", label("ev_constant_declaration_1")),
+ go_to(label("eval_dispatch")), // evaluate the declaration value
+
+ "ev_constant_declaration_1",
+ restore("continue"),
+ restore("env"),
+ restore("unev"),
+ perform(list(op("declare_value"), reg("unev"), reg("val"), reg("env"))),
+ assign("val", list(constant("ok"))),
+ go_to(reg("continue")),
+
+ // Error handling
+ "unknown_expression_type",
+ assign("val", list(constant("unknown_expression_type_error"))),
+ go_to(label("signal_error")),
+
+ "unknown_procedure_type",
+ restore("continue"), /// clean up stack (from apply_dispatch)
+ assign("val", list(constant("unknown_procedure_type_error"))),
+ go_to(label("signal_error")),
+
+ "signal_error",
+ perform(list(op("user_print"), reg("val"))),
+ go_to(label("evaluator_done")),
+
+ "evaluator_done"
+ )
+ );
+}
+
+const m = eceval();
+set_register_contents(m, "exp", "1;");
+
+function set_program_to_run(str) {
+ const parsed = parse(str);
+ // wrap program in block to create
+ // program environment
+ const program_block = make_block(parsed);
+
+ set_register_contents(m, "exp", program_block);
+}
+
+set_program_to_run(
+ "function factorial(n) { return n === 1 ? 1 : n * factorial(n - 1);} factorial(4);"
+);
+
+const the_global_environment = setup_environment();
+
+start(m);
+
+get_register_contents(m, "val");
+
+
const the_global_environment = setup_environment();
@@ -291,11 +1989,11 @@ start(eceval);
^$\ldots$^ ; same as before
-
- "print_result",
- perform(op("print_stack_statistics")), // added instruction
- perform(op("announce_output"), const("/// EC-Eval value:")),
- /* ... same as before ... */
+
+"print_result",
+ perform(op("print_stack_statistics")), // added instruction
+ perform(op("announce_output"), constant("/// EC-Eval value:")),
+ /* ... same as before ... */
@@ -370,7 +2068,7 @@ factorial(5);
function factorial(n) {
function iter(product, counter, max_count) {
- return counter > max_count
+ return counter > max_count
? product
: fact_iter(counter * product,
counter + 1,
@@ -527,7 +2225,7 @@ function fib(n) {
access an unbound variable, could be caught by changing the lookup
operation to make it return a distinguished condition code, which cannot
be a possible value of any user variable. The evaluator can test
- for this condition code and then do what is necessary to go to signal-error. Find all of the places in the evaluator where such a
+ for this condition code and then do what is necessary to go to signal-errorsignal_error. Find all of the places in the evaluator where such a
change is necessary and fix them. This is lots of work.
@@ -543,8 +2241,8 @@ function fib(n) {
for this in our register-machine simulator by making each primitive
procedurefunction
check for applicability and returning an appropriate distinguished
- condition code on failure. Then the primitive-apply code in the
- evaluator can check for the condition code and go to signal-error if necessary. Build this structure and make it work.
+ condition code on failure. Then the primitive-applyprimitive_apply code in the
+ evaluator can check for the condition code and go to signal-errorsignal_error if necessary. Build this structure and make it work.
This is a major project.
diff --git a/xml/chapter5/section5/subsection1.xml b/xml/chapter5/section5/subsection1.xml
index 123fa9ce3..155c099bb 100644
--- a/xml/chapter5/section5/subsection1.xml
+++ b/xml/chapter5/section5/subsection1.xml
@@ -85,7 +85,7 @@
function compile(exp, target, linkage) {
- return is_self-evaluating(exp)
+ return is_self_evaluating(exp)
? compile_self_evaluating(exp, target, linkage)
: is_quoted(exp)
? compile_quoted(exp, target, linkage)
diff --git a/xml/chapter5/section5/subsection2.xml b/xml/chapter5/section5/subsection2.xml
index dfae3d71c..025b628b0 100644
--- a/xml/chapter5/section5/subsection2.xml
+++ b/xml/chapter5/section5/subsection2.xml
@@ -345,7 +345,7 @@ function compile_definition(exp, target, linkage) {
function new_label_number() {
label_counter = label_counter + 1;
- return label-counter;
+ return label_counter;
}
character stringsprimitive functions for
diff --git a/xml/chapter5/section5/subsection3.xml b/xml/chapter5/section5/subsection3.xml
index 999c8ae8e..ae007b2ff 100644
--- a/xml/chapter5/section5/subsection3.xml
+++ b/xml/chapter5/section5/subsection3.xml
@@ -250,7 +250,7 @@ function code_to_get_rest_args(operand_codes) {
procedurefunction
in the
meta-circular evaluator of section or the
- apply-dispatch entry point in the explicit-control evaluator of
+ apply-dispatchapply_dispatch entry point in the explicit-control evaluator of
section. It checks whether the
procedurefunction
to be applied is a primitive
@@ -278,11 +278,11 @@ function code_to_get_rest_args(operand_codes) {
after-call
- test(op("primitive-procedure"), reg("proc")),
+ test(op("primitive_procedure"), reg("proc")),
branch(label("primitive_branch")),
-"compiled-branch",
+"compiled_branch",
$\langle \textit{code to apply compiled procedure with given target and appropriate linkage} \rangle$
-"primitive-branch",
+"primitive_branch",
assign($\langle\textit{target}\rangle$,
list(op("apply_primitive_procedure"),
reg("proc"),
@@ -332,9 +332,9 @@ $\langle\textit{linkage}\rangle$
function compile_procedure_call(target, linkage) {
- const primitive_branch = make_label("primitive-branch");
- const compiled_branch = make_label("compiled-branch");
- const after_call = make_label("after-call");
+ const primitive_branch = make_label("primitive_branch");
+ const compiled_branch = make_label("compiled_branch");
+ const after_call = make_label("after_call");
const compiled_linkage = linkage === "next" ? after_call : linkage;
@@ -649,7 +649,7 @@ function compile_procedure_call(target, linkage) {
function compile_proc_appl(target, linkage) {
- if (target === "val" && linkage !== "return") {
+ if (target === "val" && linkage !== "return") {
return make_instruction_sequence(
list("proc"),
all_regs,
@@ -657,7 +657,7 @@ function compile_proc_appl(target, linkage) {
assign("continue", list(label(linkage))),
assign("val", list(op("compiled_procedure_entry"), reg("proc"))),
go_to(reg("val"))));
- } else if (target !== "val" && linkage !== "return") {
+ } else if (target !== "val" && linkage !== "return") {
const proc_return = make_label("proc_return");
return make_instruction_sequence(
@@ -670,14 +670,14 @@ function compile_proc_appl(target, linkage) {
proc_return,
assign(target, list(reg("val"))),
go_to(label(linkage))));
- } else if (target === "val" && linkage === "return") {
+ } else if (target === "val" && linkage === "return") {
return make_instruction_sequence(
list("proc", "continue"),
all_regs,
list(
assign("val", list(op("compiled_procedure_entry"), reg("proc"))),
go_to(reg("val"))));
- } else if (target !== "val" && linkage === "return") {
+ } else if (target !== "val" && linkage === "return") {
error(target, "return linkage, target not val - - COMPILE");
}
}
diff --git a/xml/chapter5/section5/subsection4.xml b/xml/chapter5/section5/subsection4.xml
index 5eedc5c22..177da6257 100644
--- a/xml/chapter5/section5/subsection4.xml
+++ b/xml/chapter5/section5/subsection4.xml
@@ -242,7 +242,7 @@ function preserving(regs, seq1, seq2) {
} else {
const first_reg = head(regs);
- if (need_register(seq2, first_reg) && modifies_register(seq1, first_reg)) {
+ if (need_register(seq2, first_reg) && modifies_register(seq1, first_reg)) {
return preserving(
tail(regs),
make_instruction_sequence(
@@ -301,7 +301,7 @@ function tack_on_instruction_sequence(seq, body_seq) {
Compile-ifCompile_if and compile-procedure-callcompile_procedure_call use a special
- combiner called parallel-instruction-sequences to append the two
+ combiner called parallel-instruction-sequencesparallel_instruction_sequences to append the two
alternative branches that follow a test. The two branches will never be
executed sequentially; for any particular evaluation of the test, one
branch or the other will be entered. Because of this, the registers needed by the second branch are still needed by the combined sequence,
diff --git a/xml/chapter5/section5/subsection5.xml b/xml/chapter5/section5/subsection5.xml
index 938c7cf28..61ed4565c 100644
--- a/xml/chapter5/section5/subsection5.xml
+++ b/xml/chapter5/section5/subsection5.xml
@@ -144,7 +144,7 @@ assign("val", constant(ok));
reg("env"))),
$\langle \textit{compilation of procedure body} \rangle$
"after_lambda1",
- perform(op("define-variable"),
+ perform(op("define_variable"),
constant("factorial"),
reg("val"),
reg("env")),
@@ -356,7 +356,7 @@ function factorial_alt(n) {
function factorial(n) {
function iter(product, counter) {
- return counter > n
+ return counter > n
? product
: iter(product * counter, counter + 1);
}
@@ -655,7 +655,7 @@ function factorial(n) {
(assign val (op +) (reg val) (const 1))
-assign("val", op("lookup-variable-value"), constant("a"), reg("env")),
+assign("val", op("lookup_variable_value"), constant("a"), reg("env")),
assign("val", op("+"), reg("val"), constant(1)),
diff --git a/xml/chapter5/section5/subsection6.xml b/xml/chapter5/section5/subsection6.xml
index abe55782f..fb0feda03 100644
--- a/xml/chapter5/section5/subsection6.xml
+++ b/xml/chapter5/section5/subsection6.xml
@@ -70,7 +70,8 @@ let y = 4;
structure that parallels the lexical structure of the program in which
the expression appears.This is not true if we allow
internal definitions, unless we scan them out.
- See exercise.
+ See exercise.
+
Thus, the compiler can know, when it analyzes the
above expression, that each time the
procedurefunction
@@ -168,15 +169,16 @@ let y = 4;
scanning out internal definitionscompiler@in compilercompiler for Schemescanning out internal definitions
required if we implement the scanning method to eliminate internal
- definitions (exercise). We will need
+ definitions (exercise).
+ We will need
to eliminate these definitions in order for lexical addressing to
work. Also write a
procedurefunctionlexical-address-set!lexical_address_set that
implements the operation that changes the value of the variable at a
specified lexical address.
-
+
@@ -243,7 +245,7 @@ let y = 4;
Using find-variablefind_variable from exercise,
rewrite compile-variablecompile_variable and compile-assignmentcompile_assignment to output
- lexical-address instructions. In cases where find-variable
+ lexical-address instructions. In cases where find-variablefind_variable
returns not-foundnot_found (that is, where the variable is not in the
compile-time environment), you should have the code generators use the
evaluator operations, as before, to search for the binding.
@@ -253,7 +255,8 @@ let y = 4;
environment.Lexical addresses cannot be used to access variables in the global
environment, because these names can be defined and redefined
interactively at any time. With internal definitions scanned out, as
- in exercise, the only definitions the
+ in exercise,
+ the only definitions the
compiler sees are those at top level, which act on the global
environment. Compilation of a definition does not cause the defined
name to be entered in the compile-time environment.
@@ -280,8 +283,8 @@ let y = 4;
the compiler to perform the same transformation before it compiles a
procedurefunction
body.
-
+
diff --git a/xml/chapter5/section5/subsection7.xml b/xml/chapter5/section5/subsection7.xml
index 14aaa12ee..6c24bc787 100644
--- a/xml/chapter5/section5/subsection7.xml
+++ b/xml/chapter5/section5/subsection7.xml
@@ -16,7 +16,7 @@
a
procedurefunction
- compile-and-go that compiles a Scheme expression, loads the
+ compile-and-gocompile_and_go that compiles a Scheme expression, loads the
resulting object code into the evaluator machine,
and causes the machine to run the code in the
evaluator global environment, print the result, and
@@ -167,7 +167,7 @@ function start_eceval() {
branch(label("external_entry")), // branches if flag is set
"read_eval_print_loop",
-perform(op("initialize-stack")),
+perform(op("initialize_stack")),
$\langle\ldots\rangle$
@@ -277,7 +277,7 @@ function user_print(object) {
function compile_and_go(expression) {
const instructions = assemble(statements(compile(expression, "val", "return")), "eceval");
- the-global-environment = setup-environment();
+ the_global_environment = setup_environment();
set_register_contents("eceval", "val", instructions);
set_register_contents("eceval", "flag", true);
return start("eceval");