Original | JavaScript | |
In JavaScript's strict mode—our preferred way to run JavaScript programs—the name eval cannot be declared in user programs. It is reserved for a related but quite different pre-declared function. We opt for the complete word evaluate as a replacement.
Eval The function evaluate takes as arguments a program component—a statement or expression[1]—and an environment. and an environment. It classifies the expression component and directs its evaluation. Eval The function evaluate is structured as a case analysis of the syntactic type of the expression component to be evaluated. In order to keep the procedure function general, we express the determination of the type of an expression a component abstractly, making no commitment to any particular representation for the various types of expressions. components. Each type of expression component has a predicate syntax predicate that tests for it and an abstract means for selecting its parts. This abstract syntax makes it easy to see how we can change the syntax of the language by using the same evaluator, but with a different collection of syntax procedures. functions.
Original | JavaScript | |
|
|
For clarity, eval evaluate has been implemented as a case analysis using cond. conditional expressions. The disadvantage of this is that our procedure function handles only a few distinguishable types of statements and expressions, and no new ones can be defined without editing the definition of eval. declaration of evaluate. In most Lisp interpreter implementations, dispatching on the type of an expression a component is done in a data-directed style. This allows a user to add new types of expressions that eval components that evaluate can distinguish, without modifying the definition of eval declaration of evaluate itself. (See exercise 4.6.)
The representation of names is handled by the syntax abstractions. Internally, the evaluator uses strings to represent names, and we refer to such strings as symbols. The function symbol_of_name used in evaluate extracts from a name the symbol by which it is represented.
Apply The function apply takes two arguments, a procedure function and a list of arguments to which the procedure function should be applied. Apply The function apply classifies procedures functions into two kinds: It calls apply-primitive-procedure apply_primitive_function to apply primitives; it applies compound procedures functions by sequentially evaluating the expressions that make up the body of the procedure. by evaluating the block that makes up the body of the function. 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 declaration of apply:
Original | JavaScript |
(define (apply procedure arguments) (cond ((primitive-procedure? procedure) (apply-primitive-procedure procedure arguments)) ((compound-procedure? procedure) (eval-sequence (procedure-body procedure) (extend-environment (procedure-parameters procedure) arguments (procedure-environment procedure)))) (else (error "Unknown procedure type - - APPLY" procedure)))) | function apply(fun, args) { if (is_primitive_function(fun)) { return apply_primitive_function(fun, args); } else if (is_compound_function(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; } else { error(fun, "unknown function type -- apply"); } } |
Original | JavaScript | |
In order to return a value, a JavaScript function needs to evaluate a return statement. If a function terminates without evaluating a return statement, the value undefined is returned. To distinguish the two cases, the evaluation of a return statement will wrap the result of evaluating its return expression into a return value. If the evaluation of the function body yields such a return value, the content of the return value is retrieved; otherwise the value undefined is returned.[2] |
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. argument expressions of the application. It evaluates each operand argument expression and returns a list of the corresponding values:[3][4]
Original | JavaScript |
(define (list-of-values exps env) (if (no-operands? exps) '() (cons (eval (first-operand exps) env) (list-of-values (rest-operands exps) env)))) | function list_of_values(exps, env) { return map(arg => evaluate(arg, env), exps); } |
Eval-if The function eval_conditional evaluates the predicate part of an if expression a conditional component in the given environment. If the result is true, eval-if evaluates the consequent, otherwise it evaluates the alternative: the consequent is evaluated, otherwise the alternative is evaluated:
Original | JavaScript |
(define (eval-if exp env) (if (true? (eval (if-predicate exp) env)) (eval (if-consequent exp) env) (eval (if-alternative exp) env))) | function eval_conditional(component, env) { return is_truthy(evaluate(conditional_predicate(component), env)) ? evaluate(conditional_consequent(component), env) : evaluate(conditional_alternative(component), env); } |
The use of true? is_truthy in eval-if eval_conditional highlights the issue of the connection between an implemented language and an implementation language. The if-predicate conditional_predicate is evaluated in the language being implemented and thus yields a value in that language. The interpreter predicate true? is_truthy translates that value into a value that can be tested by the if conditional expression in the implementation language: The metacircular representation of truth might not be the same as that of the underlying SchemeJavaScript.[5]
Original | JavaScript | |
Eval-sequence is used by apply to evaluate the sequence of expressions in a procedure body and by eval to evaluate the sequence of expressions in a begin expression. It takes as arguments a sequence of expressions and an environment, and evaluates the expressions in the order in which they occur. The value returned is the value of the final expression. (define (eval-sequence exps env) (cond ((last-exp? exps) (eval (first-exp exps) env)) (else (eval (first-exp exps) env) (eval-sequence (rest-exps exps) env)))) |
The function eval_sequence is used by evaluate to evaluate a sequence of statements at the top level or 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 that if the evaluation of any statement in the sequence yields a return value, that value is returned and the subsequent statements are ignored.[6] 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); } } } |
Original | JavaScript | |
The function eval_block handles
blocks. The variables and constants (including functions)
declared in the block have the whole block as their scope and thus
are The function eval_return_statement is used to evaluate return statements. As seen in apply and the evaluation of sequences, the result of evaluation of a return statement needs to be identifiable so that the evaluation of a function body can return immediately, even if there are statements after the return statement. For this purpose, the evaluation of a return statement wraps the result of evaluating the return expression in a return value object.[7] function eval_return_statement(component, env) { return make_return_value(evaluate(return_expression(component), env)); } |
The following procedure function eval_assignment handles assignments to variables. names. (To simplify the presentation of our evaluator, we are allowing assignment not just to variables but also—erroneously—to constants. Exercise 4.16 explains how we could distinguish constants from variables and prevent assignment to constants.) It calls eval to find the value to be assigned and transmits the variable and the resulting value to set-variable-value! to be installed in the designated environment. The function eval_assignment calls evaluate on the value expression to find the value to be assigned and calls assignment_symbol to retrieve the symbol that represents the name from the assignment. The function eval_assignment transmits the symbol and the value to assign_symbol_value to be installed in the designated environment. The evaluation of an assignment returns the value that was assigned.
Original | JavaScript |
(define (eval-assignment exp env) (set-variable-value! (assignment-variable exp) (eval (assignment-value exp) env) env) 'ok) | function eval_assignment(component, env) { const value = evaluate(assignment_value_expression(component), env); assign_symbol_value(assignment_symbol(component), value, env); return value; } |
Original | JavaScript | |
Definitions of variables are handled in a similar manner.[8] (define (eval-definition exp env) (define-variable! (definition-variable exp) (eval (definition-value exp) env) env) 'ok) We have chosen here to return the symbol ok as the value of an assignment or a definition.[9] |
Constant and variable declarations are both recognized by the is_declaration syntax predicate. They are treated in a manner similar to assignments, because eval_block has already bound their symbols to "*unassigned*" in the current environment. Their evaluation replaces "*unassigned*" with the result of evaluating the value expression. function eval_declaration(component, env) { assign_symbol_value( declaration_symbol(component), evaluate(declaration_value_expression(component), env), env); return undefined; } The result of evaluating the body of a function is determined by return statements, and therefore the return value undefined in eval_declaration only matters when the declaration occurs at the top level, outside of any function body. Here we use the return value undefined to simplify the presentation; exercise 4.13 describes the real result of evaluating top-level components in JavaScript. |
Original | JavaScript | |
There is currently no solution available for this exercise. This textbook adaptation is a community effort. Do consider contributing by providing a solution for this exercise, using a Pull Request in Github.
|
There is currently no solution available for this exercise. This textbook adaptation is a community effort. Do consider contributing by providing a solution for this exercise, using a Pull Request in Github.
|