Our evaluator for Lisp JavaScript will be implemented as a Lisp JavaScript program. It may seem circular to think about evaluating Lisp JavaScript programs using an evaluator that is itself implemented in Lisp. JavaScript. However, evaluation is a process, so it is appropriate to describe the evaluation process using Lisp, JavaScript, which, after all, is our tool for describing processes.[1] An evaluator that is written in the same language that it evaluates is said to be metacircular.
The metacircular evaluator is essentially a Scheme JavaScript formulation of the environment model of evaluation described in section 3.2. Recall that the model has two basic parts: Recall that the model specifies the evaluation of function application in two basic steps:
Original | JavaScript | |
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 evaluate a function application, evaluate the subexpressions and then apply the value of the function subexpression to the values of the argument subexpressions. |
These two rules describe the essence of the evaluation process, a basic cycle in which statements and expressions to be evaluated in environments are reduced to procedures functions to be applied to arguments, which in turn are reduced to new statements and expressions to be evaluated in new environments, and so on, until we get down to symbols, names, whose values are looked up in the environment, and to primitive procedures, operators and primitive functions, which are applied directly (see figure 4.2).[2] This evaluation cycle will be embodied by the interplay between the two critical procedures functions in the evaluator, eval evaluate and apply, which are described in section 4.1.1 (see figure 4.2).
The implementation of the evaluator will depend upon procedures that define the syntax of the expressions to be evaluated. We will use data abstraction to make the evaluator independent of the representation of the language. For example, rather than committing to a choice that an assignment is to be represented by a list beginning with the symbol set! we use an abstract predicate assignment? to test for an assignment, and we use abstract selectors assignment-variable and assignment-value to access the parts of an assignment. Implementation of expressions will be described in detail in section 4.1.2. The implementation of the evaluator will depend upon functions that define the syntax of the statements and expressions to be evaluated. We will use data abstraction to make the evaluator independent of the representation of the language. For example, rather than committing to a choice that an assignment is to be represented by a string beginning with a name followed by =, we use an abstract predicate is_assignment to test for an assignment, and we use abstract selectors assignment_symbol and assignment_value_expression to access the parts of an assignment. The data abstraction layers presented in section 4.1.2 will allow the evaluator to remain independent of concrete syntactic issues, such as the keywords of the interpreted language, and of the choice of data structures that represent the program components. There are also operations, described in section 4.1.3, that specify the representation of procedures functions and environments. For example, make-procedure make_function constructs compound procedures, functions, lookup-variable-value lookup_symbol_value accesses the values of variables, names, and apply-primitive-procedure apply_primitive_function applies a primitive procedure function to a given list of arguments.