]> E/V Lambda - evlambda.git/commitdiff
add implementation notes; revise user manual, tutorial, and reference manual main
authorRaphaël Van Dyck <raphael.vandyck@evlambda.org>
Mon, 16 Feb 2026 13:25:43 +0000 (14:25 +0100)
committerRaphaël Van Dyck <raphael.vandyck@evlambda.org>
Mon, 16 Feb 2026 13:25:43 +0000 (14:25 +0100)
12 files changed:
README
src/evaluator.js
src/ide.jsx
system-files/BIBLIOGRAPHY
system-files/IMPLEMENTATION-NOTES
system-files/LICENSE
system-files/REFERENCE-MANUAL
system-files/TUTORIAL
system-files/USER-MANUAL
system-files/all-caps.css
system-files/core.js
system-files/docgen-sample.evl [new file with mode: 0644]

diff --git a/README b/README
index bb057de86c5b7c5bd8fc2b9ced1a3d768fcdcefb..2719fbb741638c41f8a986d4c9c027ec102b2690 100644 (file)
--- a/README
+++ b/README
@@ -54,7 +54,29 @@ How to run the evaluator from the terminal
 ==========================================
 
 Execute the following command from <EVLAMBDA_HOME>:
-node system-files/core.js { -l <file> | -e <form> }*
-
-Example:
+node system-files/core.js { --plainrec | --cps | --oocps | --sboocps | --trampoline | --trampoliepp }? { -l <file> | -e <form> }*
+
+--plainrec: selects the plain recursive evaluator
+--cps: selects the continuation passing style evaluator
+--oocps: selects the object-oriented CPS evaluator
+--sboocps: selects the stack-based object-oriented CPS evaluator
+--trampoline: selects the trampoline evaluator
+--trampolinepp: selects the trampoline++ evaluator (DEFAULT)
+-l <file>: loads the EVL file
+-e <form>: evaluates the form
+
+Examples:
 node system-files/core.js -l system-files/mantle.evl -e '(test-loop 1000000)'
+node system-files/core.js --plainrec -l system-files/mantle.evl -e '(test-loop 1000000)'
+
+How to run the EVL to XML converter from the terminal
+=====================================================
+
+Execute the following command from <EVLAMBDA_HOME>:
+node system-files/core.js --convert <file>
+
+--convert <file>: converts the EVL file to XML
+
+Examples:
+node system-files/core.js --convert system-files/docgen-sample.evl
+node system-files/core.js --convert system-files/docgen-sample.evl | xsltproc system-files/evl2html.xslt -
index 50a739a9c0be91f7dc9199a68eae807eb3b0f814..264d251092dc989146247c1cd2b78fb2dfd83c9a 100644 (file)
@@ -6,15 +6,15 @@ import {
 } from './utilities.js';
 
 const FOUND_NO_FORM = 0;
-const COMPLETED_NORMALLY = 1;
-const COMPLETED_ABNORMALLY = 2;
+const SUCCESS = 1;
+const ERROR = 2;
 const ABORTED = 3;
 const TERMINATED = 4;
 
 const INITIALIZE = 0;
 const EVALUATE_FIRST_FORM = 1;
 const EVALUATE_ALL_FORMS = 2;
-const CONVERT_TO_XML = 3;
+const CONVERT_EVL_TO_XML = 3;
 
 export const evaluatorNames = new Map([
   ['plainrec', 'Plain Recursive'],
@@ -28,10 +28,10 @@ export const evaluatorNames = new Map([
 let evaluator = null;
 let jobId = 0;
 const jobs = new Map();
-const signalBuffer = new SharedArrayBuffer(1);
-const signalArray = new Uint8Array(signalBuffer);
+const abortSignalBuffer = new SharedArrayBuffer(1);
+const abortSignalArray = new Uint8Array(abortSignalBuffer);
 
-signalArray[0] = 0;
+abortSignalArray[0] = 0;
 
 // => {id, action, input}
 // <= {id, status, output}
@@ -66,7 +66,7 @@ export function createEvaluator(jsFile, selectedEvaluator, evlFiles, callback) {
       callback(event.data);
     }
   }
-  sendRequest(INITIALIZE, {signalBuffer, selectedEvaluator, evlFiles}, callback);
+  sendRequest(INITIALIZE, {abortSignalBuffer, selectedEvaluator, evlFiles}, callback);
 }
 
 export function evaluateFirstForm(text, callback) {
@@ -77,12 +77,12 @@ export function evaluateAllForms(text, callback) {
   sendRequest(EVALUATE_ALL_FORMS, text, callback);
 }
 
-export function convertToHTML(text, xsltString, cssURL, jsURL, windowId, callback) {
-  sendRequest(CONVERT_TO_XML, text, response => {
+export function convertEVLToHTML(text, xsltString, cssURL, jsURL, windowId, callback) {
+  sendRequest(CONVERT_EVL_TO_XML, text, response => {
     let result = null;
     try {
       switch (response.status) {
-        case COMPLETED_NORMALLY:
+        case SUCCESS:
           const parser = new DOMParser();
           const processor = new XSLTProcessor();
           const serializer = new XMLSerializer();
@@ -99,7 +99,7 @@ export function convertToHTML(text, xsltString, cssURL, jsURL, windowId, callbac
           //console.log(htmlString);
           result = htmlString;
           break;
-        case COMPLETED_ABNORMALLY:
+        case ERROR:
           result = errorPage(`ERROR: ${response.output}`, cssURL, jsURL, windowId);
           break;
         case ABORTED:
@@ -137,13 +137,13 @@ export function formatForListener(response) {
   switch (response.status) {
     case FOUND_NO_FORM:
       return null;
-    case COMPLETED_NORMALLY:
+    case SUCCESS:
       let text = '';
       for (const value of response.output) {
         text = text + value + '\n';
       }
       return text;
-    case COMPLETED_ABNORMALLY:
+    case ERROR:
       return `ERROR: ${response.output}\n`;
     case ABORTED:
       return 'ABORTED\n';
@@ -156,7 +156,7 @@ export function formatForMinibuffer(response) {
   switch (response.status) {
     case FOUND_NO_FORM:
       return 'FOUND NO FORM';
-    case COMPLETED_NORMALLY:
+    case SUCCESS:
       let text = '';
       let first = true;
       for (const value of response.output) {
@@ -168,7 +168,7 @@ export function formatForMinibuffer(response) {
         text = text + value.replaceAll('\n', '\u2424'); // SYMBOL FOR NEWLINE
       }
       return text;
-    case COMPLETED_ABNORMALLY:
+    case ERROR:
       return `ERROR: ${response.output}`;
     case ABORTED:
       return 'ABORTED';
@@ -178,5 +178,5 @@ export function formatForMinibuffer(response) {
 }
 
 export function abortEvaluation() {
-  signalArray[0] = 1;
+  abortSignalArray[0] = 1;
 }
index affa9c07d3bc055de937e84654997fad0b22c13f..30f874f9fc669117c2782289ef17b27b156d818c 100644 (file)
@@ -90,7 +90,7 @@ import {
   createEvaluator,
   evaluateFirstForm,
   evaluateAllForms,
-  convertToHTML,
+  convertEVLToHTML,
   formatForMinibuffer,
   abortEvaluation
 } from './evaluator.js';
@@ -227,7 +227,7 @@ class EVLBuffer extends FileBuffer {
     const jsURL = URL.createObjectURL(jsBlob);
     const state = this.transaction.state;
     const text = state.sliceDoc();
-    convertToHTML(text, xslt, cssURL, jsURL, window.id, html  => toggleHTMLModeCommand2(ide, window, html, cssURL, jsURL));
+    convertEVLToHTML(text, xslt, cssURL, jsURL, window.id, html  => toggleHTMLModeCommand2(ide, window, html, cssURL, jsURL));
   }
 }
 
@@ -1231,7 +1231,7 @@ init([
   'USER-MANUAL',
   'TUTORIAL',
   'REFERENCE-MANUAL',
-  //'IMPLEMENTATION-NOTES',
+  'IMPLEMENTATION-NOTES',
   'BIBLIOGRAPHY',
   'LICENSE',
   'all-caps.css',
index cdeec8d0e569770ed52892a0dc2098828ddb8ec5..3fc13c0240854ef5dd3cd03ff942da31d2a0afa0 100644 (file)
@@ -4,6 +4,7 @@
 <html>
   <head>
     <meta charset="utf-8">
+    <title>Bibliography</title>
     <link rel="stylesheet" href="___cssURL___"/>
     <script src="___jsURL___"></script>
     <script>const windowId = ___windowId___;</script>
index 785c0832d0d940c9577fabf912a422771b72932a..52784d7966da0ee70d3fd86197bb4e44782abe01 100644 (file)
 <html>
   <head>
     <meta charset="utf-8">
+    <title>Implementation Notes</title>
     <link rel="stylesheet" href="___cssURL___"/>
     <script src="___jsURL___"></script>
     <script>const windowId = ___windowId___;</script>
   </head>
   <body>
+    <div class="preamble">
+      $\DeclareMathOperator{\bindingmathop}{binding}$
+    </div>
+    <h1>Implementation Notes</h1>
+    <p>The implementation notes document the JavaScript file <code>/system/core.js</code>, which implements the &ldquo;core&rdquo; of the EVLambda programming language and the EVL to XML converter (one of the two components of the documentation generator, the other one being the XSLT stylesheet). The implementation notes and the JavaScript file have the same organization (the same sections, in the same order) and are meant to be read side by side.</p>
+    <p>The JavaScript file implements five interpreter-based evaluators:</p>
+    <ul>
+      <li>Plain Recursive (plainrec)</li>
+      <li>Continuation Passing Style (cps)</li>
+      <li>Object-Oriented CPS (oocps)</li>
+      <li>Stack-Based Object-Oriented CPS (sboocps)</li>
+      <li>Trampoline (trampoline)</li>
+      <li>Trampoline++ (trampolinepp)</li>
+    </ul>
+    <p>Only the Trampoline and Trampoline++ evaluators allow unbounded iterations through tail-recursive calls. The other evaluators are only useful as stepping stones to understand the Trampoline and Trampoline++ evaluators. The Trampoline++ evaluator is an optimized version of the Trampoline evaluator.</p>
+    <p>The JavaScript file can run inside the IDE (actually inside a web worker started from the IDE) or inside a Node.js runtime environment started from the command line.</p>
+    <p>Here are quick links to the sections:</p>
+    <ol>
+      <li><a href="#global-variables">Global Variables</a></li>
+      <li><a href="#interface-ide">Interface (IDE)</a></li>
+      <li><a href="#errors">Errors</a></li>
+      <li><a href="#tokenizer">Tokenizer</a></li>
+      <li><a href="#reader">Reader</a></li>
+      <li><a href="#evl-to-xml-converter">EVL to XML Converter</a></li>
+      <li><a href="#form-analyzer">Form Analyzer</a></li>
+      <li><a href="#scope-extent-combinations">Scope-Extent Combinations</a></li>
+      <li><a href="#namespaces">Namespaces</a></li>
+      <li><a href="#global-environment">Global Environment</a></li>
+      <li><a href="#lexical-and-dynamic-environments">Lexical and Dynamic Environments</a></li>
+      <li><a href="#pairing-parameters-with-arguments">Pairing Parameters with Arguments</a></li>
+      <li><a href="#generic-evaluator">Generic Evaluator</a></li>
+      <li><a href="#plainrec">Plain Recursive Evaluator</a></li>
+      <li><a href="#cps">Continuation Passing Style Evaluator</a></li>
+      <li><a href="#oocps">Object-Oriented CPS Evaluator</a></li>
+      <li><a href="#sboocps">Stack-Based Object-Oriented CPS Evaluator</a></li>
+      <li><a href="#trampoline">Trampoline Evaluator</a></li>
+      <li><a href="#trampolinepp">Trampoline++ Evaluator</a></li>
+      <li><a href="#primitive-function-definitions-1">Primitive Function Definitions (1)</a></li>
+      <li><a href="#bounce">Bounce</a></li>
+      <li><a href="#evaluation-request">Evaluation Request</a></li>
+      <li><a href="#result">Result</a></li>
+      <li><a href="#evlobjects">EVLObjects</a></li>
+      <li><a href="#evlobject">EVLObject</a></li>
+      <li><a href="#evlvoid">EVLVoid</a></li>
+      <li><a href="#evlboolean">EVLBoolean</a></li>
+      <li><a href="#evlnumber">EVLNumber</a></li>
+      <li><a href="#evlcharacter">EVLCharacter</a></li>
+      <li><a href="#evlstring">EVLString</a></li>
+      <li><a href="#evlsymbol">EVLSymbol</a></li>
+      <li><a href="#evlkeyword">EVLKeyword</a></li>
+      <li><a href="#evlvariable">EVLVariable</a></li>
+      <li><a href="#evllist">EVLList</a></li>
+      <li><a href="#evlemptylist">EVLEmptyList</a></li>
+      <li><a href="#evlcons">EVLCons</a></li>
+      <li><a href="#evlvector">EVLVector</a></li>
+      <li><a href="#evlfunction">EVLFunction</a></li>
+      <li><a href="#evlprimitivefunction">EVLPrimitiveFunction</a></li>
+      <li><a href="#evlclosure">EVLClosure</a></li>
+      <li><a href="#miscellaneous-primitive-functions">Miscellaneous Primitive Functions</a></li>
+      <li><a href="#primitive-function-definitions-2">Primitive Function Definitions (2)</a></li>
+      <li><a href="#interface-command-line">Interface (Command Line)</a></li>
+    </ol>
+    <h2 id="global-variables">Global Variables</h2>
+    <p>The constant <code>isRunningInsideNode</code> is true if the JavaScript file is running inside a Node.js runtime environment and false otherwise.</p>
+    <p>The variable <code>abortSignalArray</code> contains a shared array used by the IDE to abort the current evaluation without terminating the web worker.</p>
+    <p>The variable <code>selectedEvaluator</code> contains the name of the selected evaluator.</p>
+    <h2 id="interface-ide">Interface (IDE)</h2>
+    <p>This section implements the interface used by the IDE to control the evaluator and the EVL to XML converter.</p>
+    <p>The evaluator and the EVL to XML converter are running inside a web worker because running them directly inside the main thread would make the GUI unresponsive when they are running.</p>
+    <p>The main thread and the web worker thread communicate through messages and shared arrays.</p>
+    <h3>Messages</h3>
+    <p>The request messages sent by the main thread to the web worker thread are objects containing the following properties:</p>
+    <ul>
+      <li><code>id</code> (required): A unique integer id used to pair responses with requests.</li>
+      <li><code>action</code> (required): An integer specifying the requested action.</li>
+      <li><code>input</code> (optional): Some data.</li>
+    </ul>
+    <p>The response messages sent by the web worker thread to the main thread are objects containing the following properties:</p>
+    <ul>
+      <li><code>id</code> (required): A unique integer id used to pair responses with requests.</li>
+      <li><code>status</code> (required): An integer specifying the status of the processing of the requested action.</li>
+      <li><code>output</code> (optional): Some data.</li>
+    </ul>
+    <p>When the processing of the requested action fails because of an error, the response message contains the following properties:</p>
+    <ul>
+      <li><code>id</code>: A unique integer id used to pair responses with requests.</li>
+      <li><code>status</code>: The value of the constant <code>ERROR</code>.</li>
+      <li><code>output</code>: A string describing the error.</li>
+    </ul>
+    <p>When the processing of the requested action fails because of the abortion of an evaluation, the response message contains the following properties:</p>
+    <ul>
+      <li><code>id</code>: A unique integer id used to pair responses with requests.</li>
+      <li><code>status</code>: The value of the constant <code>ABORTED</code>.</li>
+    </ul>
+    <p>The following sections describe the messages currently implemented. The response messages <code>ERROR</code> and <code>ABORTED</code> are omitted from the descriptions. The property <code>id</code> is omitted from the descriptions.</p>
+    <h4><code>INITIALIZE</code></h4>
+    <p>This message is used to request the initialization of the web worker.</p>
+    <p>Request message:</p>
+    <ul>
+      <li><code>action</code>: The value of the constant <code>INITIALIZE</code>.</li>
+      <li><code>input</code>: An object containing the following properties:
+        <ul>
+          <li><code>abortSignalBuffer</code>: The shared buffer under the shared array.</li>
+          <li><code>selectedEvaluator</code>: The name of the selected evaluator.</li>
+          <li><code>evlFiles</code>: The contents of some EVLambda source files to load.</li>
+        </ul>
+      </li>
+    </ul>
+    <p>Response message when the processing of the requested action succeeds:</p>
+    <ul>
+      <li><code>status</code>: The value of the constant <code>SUCCESS</code>.</li>
+      <li><code>output</code>: An array containing the printable representations of the values of the last form of the last EVLambda source file.</li>
+    </ul>
+    <h4><code>EVALUATE_FIRST_FORM</code></h4>
+    <p>This message is used to request the evaluation of the first form contained in some input string.</p>
+    <p>Request message:</p>
+    <ul>
+      <li><code>action</code>: The value of the constant <code>EVALUATE_FIRST_FORM</code>.</li>
+      <li><code>input</code>: The input string.</li>
+    </ul>
+    <p>Response message when the processing of the requested action fails because the input string does not contain any forms:</p>
+    <ul>
+      <li><code>status</code>: The value of the constant <code>FOUND_NO_FORM</code>.</li>
+    </ul>
+    <p>Response message when the processing of the requested action succeeds:</p>
+    <ul>
+      <li><code>status</code>: The value of the constant <code>SUCCESS</code>.</li>
+      <li><code>output</code>: An array containing the printable representations of the values of the first form contained in the input string.</li>
+    </ul>
+    <h4><code>EVALUATE_ALL_FORMS</code></h4>
+    <p>This message is used to request the evaluation of all of the forms contained in some input string.</p>
+    <p>Request message:</p>
+    <ul>
+      <li><code>action</code>: The value of the constant <code>EVALUATE_ALL_FORMS</code>.</li>
+      <li><code>input</code>: The input string.</li>
+    </ul>
+    <p>Response message when the processing of the requested action succeeds:</p>
+    <ul>
+      <li><code>status</code>: The value of the constant <code>SUCCESS</code>.</li>
+      <li><code>output</code>: An array containing the printable representations of the values of the last form contained in the input string.</li>
+    </ul>
+    <h4><code>CONVERT_EVL_TO_XML</code></h4>
+    <p>This message is used to request the conversion of some input string from EVL to XML.</p>
+    <p>Request message:</p>
+    <ul>
+      <li><code>action</code>: The value of the constant <code>CONVERT_EVL_TO_XML</code>.</li>
+      <li><code>input</code>: The input string.</li>
+    </ul>
+    <p>Response message when the processing of the requested action succeeds:</p>
+    <ul>
+      <li><code>status</code>: The value of the constant <code>SUCCESS</code>.</li>
+      <li><code>output</code>: A string containing the result of the conversion.</li>
+    </ul>
+    <h3>Shared Arrays</h3>
+    <h4><code>abortSignalArray</code></h4>
+    <p>The shared array contained in the variable <code>abortSignalArray</code> is used by the IDE to abort the current evaluation without terminating the web worker. The shared array contains one boolean flag represented by an $8$-bit unsigned integer. The IDE requests the abortion of the current evaluation by setting the flag to true. The Trampoline and Trampoline++ evaluators implement the abort-evaluation functionality by regularly checking the value of the flag. The other evaluators do not implement the abort-evaluation functionality.</p>
+    <h2 id="errors">Errors</h2>
+    <p>This section defines some custom error types.</p>
+    <h2 id="tokenizer">Tokenizer</h2>
+    <p>For the most part, the tokenizer is implemented as described in the reference manual. The main difference is that the tokens are produced on demand instead of all at once before the parsing begins.</p>
+    <p>On each invocation, the tokenizer sets the following properties:</p>
+    <ul>
+      <li><code>whitespace</code>: The run of whitespace preceding the lexeme associated with the next token.</li>
+      <li><code>lexeme</code>: The lexeme associated with the next token.</li>
+      <li><code>category</code>: The category of the next token.</li>
+      <li><code>value</code>: The value of the next token or <code>null</code> if tokens of this category have no value.</li>
+    </ul>
+    <h2 id="reader">Reader</h2>
+    <p>The reader is a <a href="https://en.wikipedia.org/wiki/Recursive_descent_parser" target="_blank">recursive descent parser</a> getting its tokens from the tokenizer.</p>
+    <p>The read-time conditionalization facility is integrated into the reader.</p>
+    <p>The central function is the function <code>readObject</code>, which returns one of the following:</p>
+    <ul>
+      <li>The next object.</li>
+      <li>The value of the constant <code>DOT</code>.</li>
+      <li>The value of the constant <code>CLOSING_PARENTHESIS</code>.</li>
+      <li>The value of the constant <code>XML_END_TAG</code>.</li>
+      <li>The value of the constant <code>EOI</code>.</li>
+    </ul>
+    <p>The function <code>readObject</code> treats XML elements as comments and skips them. However, the objects contained inside XML elements are not completely invisible because a callback can be attached to the tokenizer that will be invoked on each object encountered while skipping XML elements.</p>
+    <p>Most of the other functions invoke the function <code>readObject</code> and use a <code>switch</code> statement to decide what to do depending on the result of the invocation.</p>
+    <p>The main function is the function <code>read</code>, which invokes the function <code>readObject</code> and, depending on the result of the invocation, does one of the following:</p>
+    <ul>
+      <li>If the function <code>readObject</code> returns the next object, then the function <code>read</code> returns that object.</li>
+      <li>If the function <code>readObject</code> returns <code>DOT</code>, <code>CLOSING_PARENTHESIS</code>, or <code>XML_END_TAG</code>, then the function <code>read</code> throws an exception.</li>
+      <li>If the function <code>readObject</code> returns <code>EOI</code>, then the function <code>read</code> returns <code>null</code> to signal the invoker that the end of the input has been reached.</li>
+    </ul>
+    <p>In order to evaluates all of the forms contained in an EVLambda source file, the function <code>read</code> is invoked in a loop. If the function <code>read</code> returns an object, then that object is evaluated and the loop continues. If the function <code>read</code> returns <code>null</code>, then the loop terminates.</p>
+    <p>When the source file is a plain EVLambda source file, the function <code>read</code> returns each of the forms in turn and all of the forms get evaluated.</p>
+    <p>When the source file is a documented EVLambda source file, the first invocation of the function <code>read</code> returns <code>null</code> (because the chapter element is treated as a comment and skipped) and nothing seems to get evaluated. Actually, this is not the case because a callback is attached to the tokenizer that will evaluate each object encountered while skipping the chapter element.</p>
+    <h2 id="evl-to-xml-converter">EVL to XML Converter</h2>
+    <p>The job of the EVL to XML converter is to produce an output identical to the input except that (1) the characters less-than, greater-than, and ampersand occurring inside the code have been escaped and (2) some tags have been added to better delimit the code from the surrounding documentation and the comments from the surrounding code.</p>
+    <p>The EVL to XML converter uses the following context-free grammar to model the input:</p>
+    <table class="ebnf">
+      <tr>
+        <td class="lhs">$\metavar{input}$</td>
+        <td class="def">$\Coloneq$</td>
+        <td class="rhs"><code>beginning-of-input {$\metavar{top-level-whitespace}$ $\metavar{construct}$}* $\metavar{top-level-whitespace}$ end-of-input</code></td>
+      </tr>
+      <tr>
+        <td class="lhs">$\metavar{construct}$</td>
+        <td class="def">$\Coloneq$</td>
+        <td class="rhs"><code>$\metavar{xml-construct}$ | $\metavar{evl-construct}$</code></td>
+      </tr>
+      <tr>
+        <td class="lhs">$\metavar{xml-construct}$</td>
+        <td class="def">$\Coloneq$</td>
+        <td class="rhs"><code>xml-start-tag {$\metavar{xml-whitespace}$ $\metavar{construct}$}* $\metavar{xml-whitespace}$ xml-end-tag | xml-empty-element-tag | xml-comment</code></td>
+      </tr>
+      <tr>
+        <td class="lhs">$\metavar{evl-construct}$</td>
+        <td class="def">$\Coloneq$</td>
+        <td class="rhs"><code>void | boolean | number | hash-string-construct | string | keyword | variable | quote | quasiquote | unquote | unquote-splicing | hash-plus | hash-minus | dot | {left-parenthesis | hash-left-parenthesis} {$\metavar{evl-whitespace}$ $\metavar{construct}$}* $\metavar{evl-whitespace}$ right-parenthesis</code></td>
+      </tr>
+    </table>
+    <p>The main differences between the context-free grammar used by the EVL to XML converter and the context-free grammar used by the parser are the following:</p>
+    <ul>
+      <li>New tokens have been added to represent the beginning and the end of the input.</li>
+      <li>New tokens have been added to represent runs of whitespace. Runs of whitespace can be empty and can occur in three different contexts: top-level context, XML context , and EVL context.</li>
+      <li>Hash-string constructs are not decomposed into characters.</li>
+      <li>The grammar recognizes the tokens introducing abbreviations but it does not recognize abbreviations as units.</li>
+      <li>The grammar recognizes the tokens introducing read-time conditionals but it does not recognize read-time conditionals as units.</li>
+      <li>The grammar recognizes the dot but it does not distinguish between proper lists and dotted lists.</li>
+      <li>The value of a token is the lexeme associated with the token.</li>
+    </ul>
+    <p>The tags are always added before or after a run of whitespace. The converter considers each of the runs of whitespace contained in the input and, for each of them, decides what to do depending on the following:</p>
+    <ul>
+      <li>Whether the run of whitespace occurs in top-level context, in XML context, or in EVL context.</li>
+      <li>Whether the token preceding the run of whitespace is <code>beginning-of-input</code>, an XML token, or an EVL token.</li>
+      <li>Whether the token following the run of whitespace is <code>end-of-input</code>, an XML token, or an EVL token.</li>
+    </ul>
+    <p>The XML tokens are the following tokens: <code>xml-start-tag</code>, <code>xml-end-tag</code>, <code>xml-empty-element-tag</code>, and <code>xml-comment</code>.</p>
+    <p>The EVL tokens are the end-of-line and end-of-last-line comments (which are treated as a unit) and the following tokens: <code>void</code>, <code>boolean</code>, <code>number</code>, <code>hash-string-construct</code>, <code>string</code>, <code>keyword</code>, <code>variable</code>, <code>quote</code>, <code>quasiquote</code>, <code>unquote</code>, <code>unquote-splicing</code>, <code>hash-plus</code>, <code>hash-minus</code>, <code>dot</code>, <code>left-parenthesis</code>, <code>hash-left-parenthesis</code>, and <code>right-parenthesis</code>.</p>
+    <p>The end-of-line and end-of-last-line comments are treated as EVL tokens because they are not delimited from the surrounding code by additional tags.</p>
+    <p>Tags are never added before or after the runs of whitespace occurring in top-level context.</p>
+    <p>The runs of whitespace occurring in XML context are processed as follows:</p>
+    <dl>
+      <dt><code>$\metavar{xmltok}$$\metavar{ws}$$\metavar{evltok}$</code> is transformed into</dt>
+      <dd><code>$\metavar{xmltok}$$\metavar{ws}$&lt;toplevelcode&gt;&lt;blockcode&gt;$\metavar{evltok}$</code></dd>
+      <dt><code>$\metavar{evltok}$$\metavar{ws}$$\metavar{evltok}$</code>, where $\metavar{ws}$ contains at least one blank line, is transformed into</dt>
+      <dd><code>$\metavar{evltok}$&lt;/blockcode&gt;&lt;/toplevelcode&gt;$\metavar{ws}$&lt;toplevelcode&gt;&lt;blockcode&gt;$\metavar{evltok}$</code></dd>
+      <dt><code>$\metavar{evltok}$$\metavar{ws}$$\metavar{evltok}$</code>, where $\metavar{ws}$ contains no blank lines, is transformed into</dt>
+      <dd><code>$\metavar{evltok}$$\metavar{ws}$$\metavar{evltok}$</code></dd>
+      <dt><code>$\metavar{evltok}$$\metavar{ws}$$\metavar{xmltok}$</code> is transformed into</dt>
+      <dd><code>$\metavar{evltok}$&lt;/toplevelcode&gt;&lt;/blockcode&gt;$\metavar{ws}$$\metavar{xmltok}$</code></dd>
+      <dt><code>$\metavar{xmltok}$$\metavar{ws}$$\metavar{xmltok}$</code> is transformed into</dt>
+      <dd><code>$\metavar{xmltok}$$\metavar{ws}$$\metavar{xmltok}$</code></dd>
+    </dl>
+    <p>The runs of whitespace occurring in EVL context are processed as follows:</p>
+    <dl>
+      <dt><code>$\metavar{evltok}$$\metavar{ws}$$\metavar{xmltok}$</code> is transformed into</dt>
+      <dd><code>$\metavar{evltok}$&lt;/blockcode&gt;&lt;indentation style="&hellip;"&gt;&lt;blockcomment&gt;$\metavar{ws}$$\metavar{xmltok}$</code></dd>
+      <dt><code>$\metavar{xmltok}$$\metavar{ws}$$\metavar{xmltok}$</code> is transformed into</dt>
+      <dd><code>$\metavar{xmltok}$$\metavar{ws}$$\metavar{xmltok}$</code></dd>
+      <dt><code>$\metavar{xmltok}$$\metavar{ws}$$\metavar{evltok}$</code> is transformed into</dt>
+      <dd><code>$\metavar{xmltok}$&lt;/blockcomment&gt;&lt;/indentation&gt;&lt;blockcode&gt;$\metavar{ws}$$\metavar{evltok}$</code></dd>
+      <dt><code>$\metavar{evltok}$$\metavar{ws}$$\metavar{evltok}$</code> is transformed into</dt>
+      <dd><code>$\metavar{evltok}$$\metavar{ws}$$\metavar{evltok}$</code></dd>
+    </dl>
+    <h2 id="form-analyzer">Form Analyzer</h2>
+    <p>The form analyzer analyzes a form without analyzing the subforms of the form. The subforms of the form are analyzed if and when they are evaluated. The classification part of the analysis is done by the evaluators themselves by checking whether the form is an empty-list, a cons, a variable, or something else and, if the form is a cons, by checking if the car of the cons is a special operator or something else.</p>
+    <h2 id="scope-extent-combinations">Scope-Extent Combinations</h2>
+    <p>The scope-extent combinations are identified using the members of the following enumeration:</p>
+    <ul>
+      <li><code>LEX_SCOPE</code>: lexical scope and indefinite extent (= lexical environment)</li>
+      <li><code>DYN_SCOPE</code>: indefinite scope and dynamic extent (= dynamic environment)</li>
+    </ul>
+    <h2 id="namespaces">Namespaces</h2>
+    <p>The namespaces are identified using the members of the following enumeration:</p>
+    <ul>
+      <li><code>VAL_NS</code>: value namespace</li>
+      <li><code>FUN_NS</code>: function namespace</li>
+    </ul>
+    <h2 id="global-environment">Global Environment</h2>
+    <p>Because the global environment is unique, any given variable can have, at any given time, at most one global binding in the value namespace and at most one global binding in the function namespace. An obvious way to implement the global environment is thus to store the global bindings for a variable directly on the JavaScript object representing the variable.</p>
+    <p>Variables are represented by instances of the JavaScript class <code>EVLVariable</code>. The property <code>value</code> of the instance representing a variable contains the value of the binding for the variable in the value namespace of the global environment if that binding exists and <code>null</code> otherwise.  The property <code>function</code> of the instance representing a variable contains the value of the binding for the variable in the function namespace of the global environment if that binding exists and <code>null</code> otherwise.</p>
+    <h2 id="lexical-and-dynamic-environments">Lexical and Dynamic Environments</h2>
+    <p>The user manual specifies the lexical and dynamic environments as follows:</p>
+    <ul>
+      <li>Each lexical or dynamic environment consists of two sets of bindings: one for the value namespace and one for the function namespace.</li>
+      <li>Each lexical or dynamic environment is the result of extending zero or more times in sequence an empty environment.</li>
+      <li>Extending a lexical or dynamic environment is achieved by copying the two sets of bindings and then deleting and adding some bindings from/to the copy of one of the two sets.</li>
+    </ul>
+    <p>A lexical or dynamic environment is represented not by the result of the sequence of extensions leading to the environment but by a recording of the sequence of extensions leading to the environment. A single extension is recorded into a data structure called a frame and a sequence of extensions is recorded into a chain of frames.</p>
+    <p>Let $\env$ be a lexical or dynamic environment and$$[(\ns_1,[\var_{1,1},\ldots,\var_{1,m_1}],[\obj_{1,1},\ldots,\obj_{1,m_1}]),\ldots,(\ns_n,[\var_{n,1},\ldots,\var_{n,m_n}],[\obj_{n,1},\ldots,\obj_{n,m_n}])]$$be the sequence of extensions leading to the environment. The extensions are applied from left to right and the first extension is applied to an empty environment. The $i$-th extension$$(\ns_i,[\var_{i,1},\ldots,\var_{i,m_i}],[\obj_{i,1},\ldots,\obj_{i,m_i}])$$extends the previous environment by binding, in the namespace $\ns_i$, the variable $\var_{i,j}$ to the object $\obj_{i,j}$ (for all $i$ from $1$ to $n$ and all $j$ from $1$ to $m_i$). We will denote by $\bindingmathop(i,j)$ the binding between $\var_{i,j}$ and $\obj_{i,j}$ created by the $i$-th extension.</p>
+    <p>The binding for the variable $\var$ in the namespace $\ns$ of the environment $\env$ can easily be located using the following lookup rule expressed in pseudocode:</p>
+    <blockquote>
+      Initialize $i$ to $n$.<br>
+      While $i>0$, do the following:
+      <blockquote>
+        If $\ns_i=\ns$ and there exists $j\in\{1,\ldots,m_i\}$ such that $\var_{i,j}=\var$ (if such $j$ exists, it is necessarily unique because the variables $\var_{i,1},\ldots,\var_{i,m_i}$ are distinct), then $\bindingmathop(i,j)$ is the binding for the variable $\var$ in the namespace $\ns$ of the environment $\env$. Otherwise, decrement $i$ by $1$.
+      </blockquote>
+      The variable $\var$ in unbound in the namespace $\ns$ of the environment $\env$.
+    </blockquote>
+    <p>By selecting the greatest $i$ satisfying the conditions, the lookup rule correctly handles the possibility of shadowing of a binding by another binding.</p>
+    <p>Lexical and dynamic environments are represented by instances of the classes <code>NullDefiniteEnv</code> and <code>Frame</code>, which are the two concrete subclasses of the abstract class <code>DefiniteEnv</code>.</p>
+    <p>For the sake of this document, lexical and dynamic environments are called definite because their scope or their extent is definite. The bindings of a lexical environment have lexical, and thus definite, scope. The bindings of a dynamic environment have dynamic, and thus definite, extent. By contrast, the bindings of the global environment have indefinite scope and indefinite extent.</p>
+    <p>Instances of <code>Frame</code>, which represent single extensions, have four properties:</p>
+    <ul>
+      <li><code>namespace</code>: <code>VAL_NS</code> or <code>FUN_NS</code></li>
+      <li><code>variables</code>: an array of distinct variables</li>
+      <li><code>values</code>: an array of objects</li>
+      <li><code>next</code>: an instance of <code>NullDefiniteEnv</code> or <code>Frame</code></li>
+    </ul>
+    <p>The two arrays <code>variables</code> and <code>values</code> must have the same length $m\ge0$. The array <code>variables</code> contains the variables $\var_1,\ldots,\var_m$ and the array <code>values</code> contains the objects $\obj_1,\ldots,\obj_m$. The property <code>next</code> is used to create chains of instances of <code>Frame</code>. Each chain is terminated by an instance of <code>NullDefiniteEnv</code>, which represents an empty environment.</p>
+    <h2 id="pairing-parameters-with-arguments">Pairing Parameters with Arguments</h2>
+    <p>The parameters are paired with the arguments by the following functions:</p>
+    <table class="plain">
+      <tr><th>Function</th><th>Function call</th><th>Rest parameter</th></tr>
+      <tr><td><code>pairPrimFunParametersNoApply</code></td><td>plain function call<br>multiple-value-call-form</td><td>N/A</td></tr>
+      <tr><td><code>pairPrimFunParametersApply</code></td><td>apply<br>multiple-value-apply-form</td><td>N/A</td></tr>
+      <tr><td><code>pairClosureParametersNoApplyNoRest</code></td><td>plain function call<br>multiple-value-call-form</td><td>no</td></tr>
+      <tr><td><code>pairClosureParametersNoApplyRest</code></td><td>plain function call<br>multiple-value-call-form</td><td>yes</td></tr>
+      <tr><td><code>pairClosureParametersApplyNoRest</code></td><td>apply<br>multiple-value-apply-form</td><td>no</td></tr>
+      <tr><td><code>pairClosureParametersApplyRest</code></td><td>apply<br>multiple-value-apply-form</td><td>yes</td></tr>
+    </table>
+    <p>The function <code>pairClosureParametersApplyRest</code> reuses conses from the last element of the spreadable sequence of objects when possible. To illustrate, let us consider the following function calls:</p>
+    <dl>
+      <dt><code>(apply (_vlambda a (list a)) $x=\code{1}$ $y=\code{2}$ $z=\code{'(3 4)}$)</code></dt>
+      <dd><code>a</code> is paired with <code>(cons $x$ (cons $y$ $z$))$\,=\,$(1 2 3 4)</code> ($2$ new conses and $2$ reused conses)</dd>
+      <dt><code>(apply (_vlambda (a . b) (list a b)) $x=\code{1}$ $y=\code{2}$ $z=\code{'(3 4)}$)</code></dt>
+      <dd><code>a</code> is paired with <code>$x$$\,=\,$1</code></dd>
+      <dd><code>b</code> is paired with <code>(cons $y$ $z$)$\,=\,$(2 3 4)</code> ($1$ new cons and $2$ reused conses)</dd>
+      <dt><code>(apply (_vlambda (a b . c) (list a b c)) $x=\code{1}$ $y=\code{2}$ $z=\code{'(3 4)}$)</code></dt>
+      <dd><code>a</code> is paired with <code>$x$$\,=\,$1</code></dd>
+      <dd><code>b</code> is paired with <code>$y$$\,=\,$2</code></dd>
+      <dd><code>c</code> is paired with <code>$z$$\,=\,$(3 4)</code> ($0$ new conses and $2$ reused conses)</dd>
+      <dt><code>(apply (_vlambda (a b c . d) (list a b c d)) $x=\code{1}$ $y=\code{2}$ $z=\code{'(3 4)}$)</code></dt>
+      <dd><code>a</code> is paired with <code>$x$$\,=\,$1</code></dd>
+      <dd><code>b</code> is paired with <code>$y$$\,=\,$2</code></dd>
+      <dd><code>c</code> is paired with <code>(car $z$)$\,=\,$3</code></dd>
+      <dd><code>d</code> is paired with <code>(cdr $z$)$\,=\,$(4)</code> ($0$ new conses and $1$ reused cons)</dd>
+      <dt><code>(apply (_vlambda (a b c d . e) (list a b c d e)) $x=\code{1}$ $y=\code{2}$ $z=\code{'(3 4)}$)</code></dt>
+      <dd><code>a</code> is paired with <code>$x$$\,=\,$1</code></dd>
+      <dd><code>b</code> is paired with <code>$y$$\,=\,$2</code></dd>
+      <dd><code>c</code> is paired with <code>(car $z$)$\,=\,$3</code></dd>
+      <dd><code>d</code> is paired with <code>(car (cdr $z$))$\,=\,$4</code></dd>
+      <dd><code>e</code> is paired with <code>(cdr (cdr $z$))$\,=\,$()</code> ($0$ new conses and $0$ reused conses)</dd>
+      <dt><code>(apply (_vlambda (a b c d e . f) (list a b c d e f)) $x=\code{1}$ $y=\code{2}$ $z=\code{'(3 4)}$)</code></dt>
+      <dd>ERROR: Too few arguments.</dd>
+    </dl>
+    <h2 id="generic-evaluator">Generic Evaluator</h2>
+    <p>The generic evaluator simply dispatches the form to evaluate to the selected evaluator.</p>
+    <h2 id="plainrec">Plain Recursive Evaluator</h2>
+    <h2 id="cps">Continuation Passing Style Evaluator</h2>
+    <h2 id="oocps">Object-Oriented CPS Evaluator</h2>
+    <h2 id="sboocps">Stack-Based Object-Oriented CPS Evaluator</h2>
+    <h2 id="trampoline">Trampoline Evaluator</h2>
+    <h2 id="trampolinepp">Trampoline++ Evaluator</h2>
+    <h2 id="primitive-function-definitions-1">Primitive Function Definitions (1)</h2>
+    <p>Defining a primitive function is a two-step process. First, the function <code>primitiveFunction</code> is used to add to the Map contained in the variable <code>primitiveFunctions</code> an entry mapping the name of the primitive function to a record containing the minimum number of arguments, the maximum number of arguments, and the JavaScript function implementing the primitive function. Then, a binding between the name of the primitive function and an instance of the JavaScript class EVLPrimitiveFunction is added to the function namespace of the global environment.</p>
+    <p>The first step occurs where it makes the most sense. For the primitive functions related to a primitive date type, the first step occurs inside the section defining the JavaScript class associated with the primitive data type.</p>
+    <p>The second step is postponed to the section <a href="#primitive-function-definitions-2">Primitive Function Definitions (2)</a> because it cannot happen before the JavaScript class EVLPrimitiveFunction has been defined.</p>
+    <p>The actual number of arguments is checked against the minimum and maximum numbers of arguments by the code implementing the invocation of primitive functions.</p>
+    <p>The types of the arguments are checked by the JavaScript functions implementing the primitive functions.</p>
+    <h2 id="bounce">Bounce</h2>
+    <p>A bounce is what is sent back to the trampoline. A bounce is either an evaluation request or a result.</p>
+    <h2 id="evaluation-request">Evaluation Request</h2>
+    <p>An evaluation request specifies the form to evaluate and the lexical environment with respect to which the form is to be evaluated. The dynamic environment with respect to which the form is to be evaluated is always the current dynamic environment.</p>
+    <h2 id="result">Result</h2>
+    <p>A result is either an instance of the class <code>EVLObjects</code> or an instance of a concrete subclass of the abstract class <code>EVLObject</code>.</p>
+    <h2 id="evlobjects">EVLObjects</h2>
+    <p>An instance of the class <code>EVLOjects</code> is a result consisting of a sequence of zero or more objects.</p>
+    <p>The method <code>primaryValue</code> returns <code>#v</code> if the result consists of a sequence of zero objects and the first object otherwise.</p>
+    <p>The method <code>allValues</code> returns an array containing all of the objects.</p>
+    <h2 id="evlobject">EVLObject</h2>
+    <p>An instance of a concrete subclass of the abstract class <code>EVLObject</code> is two things at once: an object and a result consisting of a single object.</p>
+    <p>The method <code>primaryValue</code> returns the single object.</p>
+    <p>The method <code>allValues</code> returns an array containing the single object.</p>
+    <p>The method <code>eql</code> implements the default behavior of the equality predicate <code>eql?</code> (that is, pointer equality). The method is overridden in the classes <code>EVLNumber</code>, <code>EVLCharacter</code>, and <code>EVLString</code>.</p>
+    <p>The method <code>toString</code> is overridden in some subclasses of the abstract class <code>EVLObject</code> to return the printable representation of <code>this</code>.</p>
+    <h2 id="evlvoid">EVLVoid</h2>
+    <p>An instance of the class <code>EVLVoid</code> is an object of type <code>void</code>.</p>
+    <h2 id="evlboolean">EVLBoolean</h2>
+    <p>An instance of the class <code>EVLBoolean</code> is an object of type <code>boolean</code>.</p>
+    <h2 id="evlnumber">EVLNumber</h2>
+    <p>An instance of the class <code>EVLNumber</code> is an object of type <code>number</code>.</p>
+    <h2 id="evlcharacter">EVLCharacter</h2>
+    <p>An instance of the class <code>EVLCharacter</code> is an object of type <code>character</code>.</p>
+    <h2 id="evlstring">EVLString</h2>
+    <p>An instance of the class <code>EVLString</code> is an object of type <code>string</code>.</p>
+    <h2 id="evlsymbol">EVLSymbol</h2>
+    <p>An instance of a concrete subclass of the abstract class <code>EVLSymbol</code> is an object of type <code>symbol</code>.</p>
+    <h2 id="evlkeyword">EVLKeyword</h2>
+    <p>An instance of the class <code>EVLKeyword</code> is an object of type <code>keyword</code>.</p>
+    <h2 id="evlvariable">EVLVariable</h2>
+    <p>An instance of the class <code>EVLVariable</code> is an object of type <code>variable</code>.</p>
+    <h2 id="evllist">EVLList</h2>
+    <p>An instance of a concrete subclass of the abstract class <code>EVLList</code> is an object of type <code>list</code>.</p>
+    <h2 id="evlemptylist">EVLEmptyList</h2>
+    <p>An instance of the class <code>EVLEmptyList</code> is an object of type <code>empty-list</code>.</p>
+    <h2 id="evlcons">EVLCons</h2>
+    <p>An instance of the class <code>EVLCons</code> is an object of type <code>cons</code>.</p>
+    <h2 id="evlvector">EVLVector</h2>
+    <p>An instance of the class <code>EVLVector</code> is an object of type <code>vector</code>.</p>
+    <h2 id="evlfunction">EVLFunction</h2>
+    <p>An instance of a concrete subclass of the abstract class <code>EVLFunction</code> is an object of type <code>function</code>.</p>
+    <h2 id="evlprimitivefunction">EVLPrimitiveFunction</h2>
+    <p>An instance of the class <code>EVLPrimitiveFunction</code> is an object of type <code>primitive-function</code>.</p>
+    <h2 id="evlclosure">EVLClosure</h2>
+    <p>An instance of the class <code>EVLClosure</code> is an object of type <code>closure</code>.</p>
+    <h2 id="miscellaneous-primitive-functions">Miscellaneous Primitive Functions</h2>
+    <p>This section implements the primitive functions <code>values</code>, <code>error</code>, and <code>now</code>.</p>
+    <h2 id="primitive-function-definitions-2">Primitive Function Definitions (2)</h2>
+    <p>The second steps of the primitive function definitions all occur in this section.</p>
+    <h2 id="interface-command-line">Interface (Command Line)</h2>
+    <p>This section implements the command-line interface used to control the evaluator and the EVL to XML converter.</p>
+    <p>The syntax of the command line to control the evaluator is as follows:</p>
+    <blockquote><code>node core.js { --plainrec | --cps | --oocps | --sboocps | --trampoline | --trampoliepp }? { -l $\metavar{file}$ | -e $\metavar{form}$ }*</code></blockquote>
+    <p>The options and arguments have the following meanings:</p>
+    <ul>
+      <li><code>--plainrec</code>: selects the plain recursive evaluator</li>
+      <li><code>--cps</code>: selects the continuation passing style evaluator</li>
+      <li><code>--oocps</code>: selects the object-oriented CPS evaluator</li>
+      <li><code>--sboocps</code>: selects the stack-based object-oriented CPS evaluator</li>
+      <li><code>--trampoline</code>: selects the trampoline evaluator</li>
+      <li><code>--trampolinepp</code>: selects the trampoline++ evaluator (DEFAULT)</li>
+      <li><code>-l $\metavar{file}$</code>: loads the EVL file</li>
+      <li><code>-e $\metavar{form}$</code>: evaluates the form</li>
+    </ul>
+    <p>The syntax of the command line to control the EVL to XML converter is as follows:</p>
+    <blockquote><code>node core.js --convert $\metavar{file}$</code></blockquote>
+    <p>The options and arguments have the following meanings:</p>
+    <ul>
+      <li><code>--convert $\metavar{file}$</code>: converts the EVL file to XML</li>
+    </ul>
   </body>
 </html>
index e6942e688e709adc1544d1cf8494f39e5dc5e434..118572be24c5d0c850df00da2aba7eff260ffbfb 100644 (file)
@@ -4,6 +4,7 @@
 <html>
   <head>
     <meta charset="utf-8">
+    <title>License</title>
     <link rel="stylesheet" href="___cssURL___"/>
     <script src="___jsURL___"></script>
     <script>const windowId = ___windowId___;</script>
index bed00c3204e77ef1d77095351cd0752791bdf4d0..71ab8ceb4e31fa83b904feb754f48be00baf156d 100644 (file)
@@ -4,6 +4,7 @@
 <html>
   <head>
     <meta charset="utf-8">
+    <title>Reference Manual</title>
     <link rel="stylesheet" href="___cssURL___"/>
     <script src="___jsURL___"></script>
     <script>const windowId = ___windowId___;</script>
     </ul>
     <p>Note that some whitespace is needed to separate the lexeme <span class="charseq"><span class="char">+</span></span> from the lexeme <span class="charseq"><span class="char">1</span><span class="char">2</span><span class="char">3</span></span> and the lexeme <span class="charseq"><span class="char">1</span><span class="char">2</span><span class="char">3</span></span> from the lexeme <span class="charseq"><span class="char">4</span><span class="char">5</span><span class="char">6</span></span> but no whitespace is needed to separate the lexeme <span class="charseq"><span class="char">(</span></span> from the lexeme <span class="charseq"><span class="char">+</span></span> or the lexeme <span class="charseq"><span class="char">4</span><span class="char">5</span><span class="char">6</span></span> from the lexeme <span class="charseq"><span class="char">)</span></span>.</p>
     <p><b>Step 2.2</b> A component of the reader called the parser converts the sequence of tokens from step 2.1 (minus the tokens of category <code>whitespace</code>, which are ignored by the parser) into a cons whose car is the variable from step 2.1 and whose cdr is a cons whose car is the first number from step 2.1 and whose cdr is a cons whose car is the second number from step 2.1 and whose cdr is the empty list. Together, those three conses represent the list <code>(+ 123 456)</code>.</p>
-    <p><b>Step 3</b> The evaluator evaluates the list <code>(+ 123 456)</code> to the number <code>579</code>. The evaluation of the top-level form <code>(+ 123 456)</code> entails the evaluation of other non-top-level forms. Each form must be classified in order to determine how it should be evaluated. The form <code>(+ 123 456)</code> is classified as a plain function call. The variable <code>+</code> is treated as an abbreviation for the form <code>(fref +)</code>. The form <code>(fref +)</code> is classified as an fref-form. The forms <code>123</code> and <code>456</code> are classified as self-evaluating objects. Because the global function <code>+</code> is a closure, its invocation entails the evaluation (and thus the classification) of other forms. The component of the evaluator responsible for classifying forms is called the syntax analyzer.</p>
+    <p><b>Step 3</b> The evaluator evaluates the list <code>(+ 123 456)</code> to the number <code>579</code>. The evaluation of the top-level form <code>(+ 123 456)</code> entails the evaluation of other non-top-level forms. Each form must be analyzed in order to determine how it should be evaluated. The form <code>(+ 123 456)</code> is analyzed as a plain function call. The variable <code>+</code> is treated as an abbreviation for the form <code>(fref +)</code>. The form <code>(fref +)</code> is analyzed as an fref-form. The forms <code>123</code> and <code>456</code> are analyzed as self-evaluating objects. Because the global function <code>+</code> is a closure, its invocation entails the evaluation (and thus the analysis) of other forms. The component of the evaluator responsible for analyzing forms is called the form analyzer.</p>
     <p><b>Step 4</b> The printer converts the number <code>579</code> into the sequence of characters <span class="charseq"><span class="char">5</span><span class="char">7</span><span class="char">9</span></span>. (The sequence of characters is the printable representation of the number.)</p>
     <p><b>Step 5</b> The sequence of characters <span class="charseq"><span class="char">5</span><span class="char">7</span><span class="char">9</span></span> is written into the listener buffer.</p>
     <p>EVLambda has three levels of syntax:</p>
     <ul>
       <li>The token level contains the rules used by the tokenizer to convert a sequence of characters into a sequence of tokens.</li>
       <li>The object level contains the rules used by the parser to convert a sequence of tokens into a sequence of objects.</li>
-      <li>The form level contains the rules used by the syntax analyzer to classify forms.</li>
+      <li>The form level contains the rules used by the form analyzer to analyze forms.</li>
     </ul>
     <p>Each level of syntax is described later in its own section.</p>
     <h4>EVLambda Source Files</h4>
     <p>References to named regular expressions can be used wherever regular expressions can be used. References to named regular expressions denoting classes of characters can also be used inside character classes. Circular definitions are not allowed.</p>
     <p>Except for the parts referencing named regular expressions, regular expressions are typeset in a monospaced typeface. Spaces can be added freely outside literal strings and character classes without modifying the meaning of a regular expression.</p>
     <h4>Extended Backus-Naur Form (EBNF)</h4>
-    <p>The parser and the syntax analyzer use a variant of the <a href="https://en.wikipedia.org/wiki/Extended_Backus%E2%80%93Naur_form" target="_blank">extended Backus-Naur form</a> (EBNF) notation to define various <a href="https://en.wikipedia.org/wiki/Context-free_grammar" target="_blank">context-free grammars</a>.</p>
+    <p>The parser and the form analyzer use a variant of the <a href="https://en.wikipedia.org/wiki/Extended_Backus%E2%80%93Naur_form" target="_blank">extended Backus-Naur form</a> (EBNF) notation to define various <a href="https://en.wikipedia.org/wiki/Context-free_grammar" target="_blank">context-free grammars</a>.</p>
     <p>The following table summarizes the syntax of the variant of the EBNF notation used in this document:</p>
     <table class="plain">
       <tr><th>Syntax</th><th>Meaning</th></tr>
       <li>Define a grammar $G$ whose sentences are also sentences of the pattern language.</li>
       <li>Declare that the objects belonging to $S$ are the objects matching at least one sentence of $G$.</li>
     </ul>
-    <p>The language specified by a grammar such as $G$ is called a template language. Template languages will be used to specify the forms recognized by the syntax analyzer, to specify template macro calls, to specify data structures, &hellip;</p>
+    <p>The language specified by a grammar such as $G$ is called a template language. Template languages will be used to specify the forms recognized by the form analyzer, to specify template macro calls, to specify data structures, &hellip;</p>
     <p>Here are the terminal symbols of the context-free grammar specifying the pattern language:</p>
     <ul>
       <li>The names of the types: $\object$, $\void$, $\boolean$, $\number$, $\character$, $\string$, $symbol$, $\keyword$, $\variable$, $\list$, $\emptylist$, $\cons$, $\vector$, $\function$, $\primitivefunction$, and $\closure$.</li>
     <p>By way of example, let us consider the following sequence of characters:</p>
     <blockquote><code>(1 . #+foo 2 #-foo 3)</code></blockquote>
     <p>If the symbol <code>foo</code> belongs to the value of the global variable <code>*features*</code>, then the sequence of characters is parsed as the dotted list <code>(1 . 2)</code>. Otherwise, the sequence of characters is parsed as the dotted list <code>(1 . 3)</code>.</p>
-    <h3>Syntax Analyzer</h3>
-    <p>The forms recognized by the syntax analyzer are specified by the template language specified by the following context-free grammar:</p>
+    <h3>Form Analyzer</h3>
+    <p>The forms recognized by the form analyzer are specified by the template language specified by the following context-free grammar:</p>
     <table class="ebnf">
       <tr>
         <td class="lhs">$\metavar{form}$</td>
     </table>
     <p>The variables <code>quasiquote</code>, <code>unquote</code>, and <code>unquote-splicing</code> are not special operators. The variable <code>quasiquote</code> is the name of a global macro and the variables <code>unquote</code> and <code>unquote-splicing</code> are variables having special meanings in the context of a call to the global macro <code>quasiquote</code>.</p>
     <p>A form consisting of a variable $\var$ is treated as an abbreviation. If it appears in operator position, then it is treated as an abbreviation for the special form <code>(fref $\var$)</code>. Otherwise, it is treated as an abbreviation for the special form <code>(vref $\var$)</code>.</p>
-    <p>Classifying a form is the first step in evaluating a form. If the classification fails, then the evaluation fails.</p>
-    <p>Some syntax analyzers classify forms without checking that the components that must be forms are actually forms. Syntax analyzers associated with interpreters usually function this way. With this kind of syntax analyzer, the components that must be forms are classified if and when they are evaluated.</p>
+    <p>Analyzing a form is the first step in evaluating a form. If the analysis fails, then the evaluation fails.</p>
+    <p>Some form analyzers analyze a form without analyzing the subforms of the form. With such form analyzers, the subforms of the form are analyzed if and when they are evaluated. Form analyzers associated with interpreters usually function this way.</p>
     <p>According to the template language, some objects can simultaneously be a special form, a macro call, and a plain function call. Additional rules are needed to disambiguate the situation.</p>
-    <p>The following rules used by the syntax analyzer are not expressed by the template language:</p>
+    <p>The following rules used by the form analyzer are not expressed by the template language:</p>
     <ul>
-      <li>A list whose first element is a special operator is never classified as a call. Either the list is successfully classified as a special form or the classification fails.</li>
-      <li>A list whose first element is a variable (other than a special operator) naming a macro according to the lookup rule used by <code>fref</code> is never classified as a plain function call. Either the list is successfully classified as a macro call or the classification fails.</li>
+      <li>A list whose first element is a special operator is never analyzed as a call. Either the list is successfully analyzed as a special form or the analysis fails.</li>
+      <li>A list whose first element is a variable (other than a special operator) naming a macro according to the lookup rule used by <code>fref</code> is never analyzed as a plain function call. Either the list is successfully analyzed as a macro call or the analysis fails.</li>
       <li>The variables of a parameter list must be distinct.</li>
     </ul>
     <h3>Documentation Generator</h3>
       <dt><code>(multiple-value-apply $\metavar{operator-form}$ $\metavar{operand-forms}$)</code></dt>
       <dd>The operator-form and the operand-forms are evaluated in sequence from left to right. If the primary value of the operator-form is not a function, then the evaluation of the multiple-value-apply-form completes abruptly with a reason of type <code>error</code>. <span class="bg">Otherwise, all the values of the operand-forms are collected into a sequence $\mlvar{seq}$ and the primary value of the operator-form is invoked on $\spread(\mlvar{seq})$. (The evaluation of the multiple-value-apply-form completes abruptly with a reason of type <code>error</code> if $\mlvar{seq}$ is not a spreadable sequence of objects.)</span> If the invocation completes abruptly for any reason, then the evaluation of the multiple-value-apply-form also completes abruptly for the same reason. Otherwise, if the invocation does not complete, then the evaluation of the multiple-value-apply-form does not complete either. Otherwise, the multiple-value-apply-form evaluates to the values of the invocation.</dd>
     </dl>
-    <p>A primitive function is invoked as described in the user manual. A closure is invoked as described in the user manual except for the way the parameters and the arguments are paired in order to extend the value namespace of the lexical environment captured by the closure (when the corresponding lambda abstraction is a _vlambda-form or an _mlambda-form), the function namespace of the lexical environment captured by the closure (when the corresponding lambda abstraction is an _flambda-form), or the value namespace of the current dynamic environment (when the corresponding lambda abstraction is a _dlambda-form). Let $\arg_1,\ldots,\arg_n$ be the arguments and $var_1,\ldots,\var_m$ be the required parameters. The way the parameters and the arguments are paired depends on the absence or presence of a rest parameter:</p>
+    <p>A primitive function is invoked as described in the user manual. A closure is invoked as described in the user manual except for the way the parameters are paired with the arguments in order to extend the value namespace of the lexical environment captured by the closure (when the corresponding lambda abstraction is a _vlambda-form or an _mlambda-form), the function namespace of the lexical environment captured by the closure (when the corresponding lambda abstraction is an _flambda-form), or the value namespace of the current dynamic environment (when the corresponding lambda abstraction is a _dlambda-form). Let $\arg_1,\ldots,\arg_n$ be the arguments and $var_1,\ldots,\var_m$ be the required parameters. The way the parameters are paired with the arguments depends on the absence or presence of a rest parameter:</p>
     <dl>
       <dt>Case 1, there is no rest parameter:</dt>
       <dd>If $n\neq m$, then the invocation completes abruptly with a reason of type <code>error</code>. Otherwise, the appropriate namespace of the appropriate environment is extended to bind $\var_i$ to $\arg_i$ for all $i$ from $1$ to $m$. (This is the behavior described in the user manual.)</dd>
       <dt>Case 2, there is a rest parameter $\var_{m+1}$:</dt>
-      <dd>If $n\lt m$, then the invocation completes abruptly with a reason of type <code>error</code>. Otherwise, the appropriate namespace of the appropriate environment is extended to bind $\var_i$ to $\arg_i$ for all $i$ from $1$ to $m$ and $\var_{m+1}$ to a proper list whose elements are $\arg_{m+1},\ldots,arg_{n}$. The conses used to build the proper list are not necessarily new conses. When the function call is an apply-form or a multiple-value-apply-form, the proper list actually reuses some or all of the conses constituting the last element of the spreadable sequence of objects.</dd>
+      <dd>If $n\lt m$, then the invocation completes abruptly with a reason of type <code>error</code>. Otherwise, the appropriate namespace of the appropriate environment is extended to bind $\var_i$ to $\arg_i$ for all $i$ from $1$ to $m$ and $\var_{m+1}$ to a proper list whose elements are $\arg_{m+1},\ldots,arg_{n}$. The conses used to build the proper list are not necessarily new conses. When the function call is an apply-form or a multiple-value-apply-form, the proper list and the last element of the spreadable sequence of objects can share conses.</dd>
     </dl>
     <p>Here are some examples, where the bindings are assumed to belong to the value namespace:</p>
     <table class="plain">
index fab0c2e5ef3f23ab33ad2e0a23cb9d11e36f4297..b514db7d8f48222587ea338df1ffd529abc20c69 100644 (file)
@@ -4,6 +4,7 @@
 <html>
   <head>
     <meta charset="utf-8">
+    <title>Tutorial</title>
     <link rel="stylesheet" href="___cssURL___"/>
     <script src="___jsURL___"></script>
     <script>const windowId = ___windowId___;</script>
     <pre class="repl">&gt; (not #t)<br>#f<br><br>&gt; (not #f)<br>#t</pre>
     <dl>
       <dt><code>(and $\metavar{test-forms}$)</code></dt>
-      <dd>The macro returns <code>#t</code> if all its arguments are <code>#t</code> and <code>#f</code> otherwise. The exact behavior of the macro is as follows: The macro evaluates the test-forms in sequence from left to right. If a test-form evaluates to an object that is not a boolean, then the following test-forms are not evaluated and the evaluation of the macro call completes abnormally.  If a test-form evaluates to <code>#f</code>, then the following test-forms are not evaluated and the macro call evaluates to <code>#f</code>. If all test-forms evaluate to <code>#t</code>, then the macro call evaluates to <code>#t</code>. Note that the previous condition is automatically satisfied if there are no test-forms.</dd>
+      <dd>The macro returns <code>#t</code> if all its arguments are <code>#t</code> and <code>#f</code> otherwise. The exact behavior of the macro is as follows: The macro evaluates the test-forms in sequence from left to right. If a test-form evaluates to an object that is not a boolean, then the following test-forms are not evaluated and the evaluation of the macro call completes abruptly with a reason of type <code>error</code>.  If a test-form evaluates to <code>#f</code>, then the following test-forms are not evaluated and the macro call evaluates to <code>#f</code>. If all test-forms evaluate to <code>#t</code>, then the macro call evaluates to <code>#t</code>. Note that the previous condition is automatically satisfied if there are no test-forms.</dd>
     </dl>
     <pre class="repl">&gt; (and)<br>#t<br><br>&gt; (and #t)<br>#t<br><br>&gt; (and #f)<br>#f<br><br>&gt; (and #t #t)<br>#t<br><br>&gt; (and #t #f)<br>#f<br><br>&gt; (and #f #t)<br>#f<br><br>&gt; (and #f #f)<br>#f</pre>
     <dl>
       <dt><code>(or $\metavar{test-forms}$)</code></dt>
-      <dd>The macro returns <code>#f</code> if all its arguments are <code>#f</code> and <code>#t</code> otherwise. The exact behavior of the macro is as follows: The macro evaluates the test-forms in sequence from left to right. If a test-form evaluates to an object that is not a boolean, then the following test-forms are not evaluated and the evaluation of the macro call completes abnormally.  If a test-form evaluates to <code>#t</code>, then the following test-forms are not evaluated and the macro call evaluates to <code>#t</code>. If all test-forms evaluate to <code>#f</code>, then the macro call evaluates to <code>#f</code>. Note that the previous condition is automatically satisfied if there are no test-forms.</dd>
+      <dd>The macro returns <code>#f</code> if all its arguments are <code>#f</code> and <code>#t</code> otherwise. The exact behavior of the macro is as follows: The macro evaluates the test-forms in sequence from left to right. If a test-form evaluates to an object that is not a boolean, then the following test-forms are not evaluated and the evaluation of the macro call completes abruptly with a reason of type <code>error</code>.  If a test-form evaluates to <code>#t</code>, then the following test-forms are not evaluated and the macro call evaluates to <code>#t</code>. If all test-forms evaluate to <code>#f</code>, then the macro call evaluates to <code>#f</code>. Note that the previous condition is automatically satisfied if there are no test-forms.</dd>
     </dl>
     <pre class="repl">&gt; (or)<br>#f<br><br>&gt; (or #t)<br>#t<br><br>&gt; (or #f)<br>#f<br><br>&gt; (or #t #t)<br>#t<br><br>&gt; (or #t #f)<br>#t<br><br>&gt; (or #f #t)<br>#t<br><br>&gt; (or #f #f)<br>#f</pre>
     <p>When we say that a macro does this and that, what we really mean is that the expansion of the macro does this and that. In the case of the macros <code>and</code> and <code>or</code>, it is their expansions that evaluate the test-forms in sequence from left to right, etc.</p>
-    <p>The macros <code>and</code> and <code>or</code> stop evaluating their operands as soon as they can determine that the result of the macro call is definitively true or definitively false. This short-circuiting behavior is possible only because <code>and</code> and <code>or</code> are macros. When a function call is evaluated, all the operands are always evaluated (unless the evaluation of the operator or the evaluation of one of the operands other than the last one completes abnormally or does not complete).</p>
+    <p>The macros <code>and</code> and <code>or</code> stop evaluating their operands as soon as they can determine that the result of the macro call is definitively true or definitively false. This short-circuiting behavior is possible only because <code>and</code> and <code>or</code> are macros. When a function call is evaluated, all the operands are always evaluated (unless the evaluation of the operator or the evaluation of one of the operands other than the last one completes abruptly or does not complete).</p>
     <p>Here are two expansions illustrating the implementations of the macros <code>and</code> and <code>or</code>:</p>
     <ul>
       <li><code>(and $\metavarn{test-form}{1}$ $\metavarn{test-form}{2}$ $\metavarn{test-form}{3}$)</code> expands into<br><code>(if $\metavarn{test-form}{1}$ (if $\metavarn{test-form}{2}$ (if $\metavarn{test-form}{3}$ #t #f) #f) #f)</code></li>
@@ -62,7 +63,7 @@
     <pre class="repl">&gt; (+)<br>0<br><br>&gt; (+ 1)<br>1<br><br>&gt; (+ 1 2)<br>3<br><br>&gt; (+ 1 2 3)<br>6</pre>
     <dl>
       <dt><code>(- $\number_1\ldots\number_n$)</code></dt>
-      <dd>If the function is invoked on zero numbers, then the invocation completes abnormally. If the function is invoked on one number, then the opposite of that number is returned. If the function is invoked on more than one number, then the result of subtracting those numbers from left to right is returned: the second number is subtracted from the first number, then the third number is subtracted from the partial result just computed, &hellip; The function is a closure built on top of the primitive function <code>_-</code>, which must be invoked on exactly two numbers.</dd>
+      <dd>If the function is invoked on zero numbers, then the invocation completes abruptly with a reason of type <code>error</code>. If the function is invoked on one number, then the opposite of that number is returned. If the function is invoked on more than one number, then the result of subtracting those numbers from left to right is returned: the second number is subtracted from the first number, then the third number is subtracted from the partial result just computed, &hellip; The function is a closure built on top of the primitive function <code>_-</code>, which must be invoked on exactly two numbers.</dd>
     </dl>
     <pre class="repl">&gt; (-)<br>ERROR: Expecting at least one number.<br><br>&gt; (- 1)<br>-1<br><br>&gt; (- 0 1)<br>-1<br><br>&gt; (- 0 1 2)<br>-3<br><br>&gt; (- 0 1 2 3)<br>-6</pre>
     <dl>
@@ -72,7 +73,7 @@
     <pre class="repl">&gt; (*)<br>1<br><br>&gt; (* 2)<br>2<br><br>&gt; (* 2 4)<br>8<br><br>&gt; (* 2 4 8)<br>64</pre>
     <dl>
       <dt><code>(/ $\number_1\ldots\number_n$)</code></dt>
-      <dd>If the function is invoked on zero numbers, then the invocation completes abnormally. If the function is invoked on one number, then the inverse of that number is returned. If the function is invoked on more than one number, then the result of dividing those numbers from left to right is returned: the first number is divided by the second number, then the partial result just computed is divided by the third number, &hellip; The function is a closure built on top of the primitive function <code>_/</code>, which must be invoked on exactly two numbers.</dd>
+      <dd>If the function is invoked on zero numbers, then the invocation completes abruptly with a reason of type <code>error</code>. If the function is invoked on one number, then the inverse of that number is returned. If the function is invoked on more than one number, then the result of dividing those numbers from left to right is returned: the first number is divided by the second number, then the partial result just computed is divided by the third number, &hellip; The function is a closure built on top of the primitive function <code>_/</code>, which must be invoked on exactly two numbers.</dd>
     </dl>
     <pre class="repl">&gt; (/)<br>ERROR: Expecting at least one number.<br><br>&gt; (/ 2)<br>0.5<br><br>&gt; (/ 1 2)<br>0.5<br><br>&gt; (/ 1 2 4)<br>0.125<br><br>&gt; (/ 1 2 4 8)<br>0.015625</pre>
     <h4>Comparison Operators</h4>
         <li>The form <code>(+ 1 2)</code> is evaluated with respect to $[]$ and $[]$.</li>
         <li>
           <ul>
-            <li>The form <code>(+ 1 2)</code> is classified as a plain function call.</li>
+            <li>The form <code>(+ 1 2)</code> is analyzed as a plain function call.</li>
             <li>The variable <code>+</code> is treated as an abbreviation for <code>(fref +)</code>.</li>
             <li>The form <code>(fref +)</code> is evaluated with respect to $[]$ and $[]$.</li>
             <li>
               <ul>
-                <li>The form <code>(fref +)</code> is classified as an fref-form.</li>
+                <li>The form <code>(fref +)</code> is analyzed as an fref-form.</li>
                 <li>The variable <code>+</code> is looked up.</li>
               </ul>
             </li>
             <li>The form <code>1</code> is evaluated with respect to $[]$ and $[]$.</li>
             <li>
               <ul>
-                <li>The form <code>1</code> is classified as a self-evaluating object.</li>
+                <li>The form <code>1</code> is analyzed as a self-evaluating object.</li>
               </ul>
             </li>
             <li>The form <code>1</code> evaluates to the number <code>1</code>.</li>
             <li>The form <code>2</code> is evaluated with respect to $[]$ and $[]$.</li>
             <li>
               <ul>
-                <li>The form <code>2</code> is classified as a self-evaluating object.</li>
+                <li>The form <code>2</code> is analyzed as a self-evaluating object.</li>
               </ul>
             </li>
             <li>The form <code>2</code> evaluates to the number <code>2</code>.</li>
                 <li>The form <code>(_+ x y)</code> is evaluated with respect to $[\vbinding{x}{1},\vbinding{y}{2}]$ and $[]$.</li>
                 <li>
                   <ul>
-                    <li>The form <code>(_+ x y)</code> is classified as a plain function call.</li>
+                    <li>The form <code>(_+ x y)</code> is analyzed as a plain function call.</li>
                     <li>The variable <code>_+</code> is treated as an abbreviation for <code>(fref _+)</code>.</li>
                     <li>The form <code>(fref _+)</code> is evaluated with respect to $[\vbinding{x}{1},\vbinding{y}{2}]$ and $[]$.</li>
                     <li>
                       <ul>
-                        <li>The form <code>(fref _+)</code> is classified as an fref-form.</li>
+                        <li>The form <code>(fref _+)</code> is analyzed as an fref-form.</li>
                         <li>The variable <code>_+</code> is looked up.</li>
                       </ul>
                     </li>
                     <li>The form <code>(vref x)</code> is evaluated with respect to $[\vbinding{x}{1},\vbinding{y}{2}]$ and $[]$.</li>
                     <li>
                       <ul>
-                        <li>The form <code>(vref x)</code> is classified as a vref-form.</li>
+                        <li>The form <code>(vref x)</code> is analyzed as a vref-form.</li>
                         <li>The variable <code>x</code> is looked up.</li>
                       </ul>
                     </li>
                     <li>The form <code>(vref y)</code> is evaluated with respect to $[\vbinding{x}{1},\vbinding{y}{2}]$ and $[]$.</li>
                     <li>
                       <ul>
-                        <li>The form <code>(vref y)</code> is classified as a vref-form.</li>
+                        <li>The form <code>(vref y)</code> is analyzed as a vref-form.</li>
                         <li>The variable <code>y</code> is looked up.</li>
                       </ul>
                     </li>
         <li>The form <code>(simple-vlet x 1 (+ x 2))</code> is evaluated with respect to $[]$ and $[]$.</li>
         <li>
           <ul>
-            <li>Because the variable <code>simple-vlet</code> names a macro according to the lookup rule used by <code>fref</code>, the form <code>(simple-vlet x 1 (+ x 2))</code> is classified as a macro call.</li>
+            <li>Because the variable <code>simple-vlet</code> names a macro according to the lookup rule used by <code>fref</code>, the form <code>(simple-vlet x 1 (+ x 2))</code> is analyzed as a macro call.</li>
             <li>The global macro <code>simple-vlet</code> is invoked on the variable <code>x</code>, the number <code>1</code>, and the list <code>(+ x 2)</code>.</li>
             <li>The global macro <code>simple-vlet</code> returns the list <code>((_vlambda (x) (+ x 2)) 1)</code>.</li>
             <li>The form <code>((_vlambda (x) (+ x 2)) 1)</code> is evaluated with respect to $[]$ and $[]$.</li>
             <li>
               <ul>
-                <li>The form <code>((_vlambda (x) (+ x 2)) 1)</code> is classified as a plain function call.</li>
+                <li>The form <code>((_vlambda (x) (+ x 2)) 1)</code> is analyzed as a plain function call.</li>
                 <li>The form <code>(_vlambda (x) (+ x 2))</code> is evaluated with respect to $[]$ and $[]$.</li>
                 <li>The form <code>(_vlambda (x) (+ x 2))</code> evaluates to a closure recording the following two pieces of information: the _vlambda-form <code>(_vlambda (x) (+ x 2))</code> and the lexical environment $[]$.</li>
                 <li>The form <code>1</code> is evaluated with respect to $[]$ and $[]$.</li>
         <li>The form <code>(simple-vlet x 1 (simple-vlet y 2 (+ x y)))</code> is evaluated with respect to $[]$ and $[]$.</li>
         <li>
           <ul>
-            <li>Because the variable <code>simple-vlet</code> names a macro according to the lookup rule used by <code>fref</code>, the form <code>(simple-vlet x 1 (simple-vlet y 2 (+ x y)))</code> is classified as a macro call.</li>
+            <li>Because the variable <code>simple-vlet</code> names a macro according to the lookup rule used by <code>fref</code>, the form <code>(simple-vlet x 1 (simple-vlet y 2 (+ x y)))</code> is analyzed as a macro call.</li>
             <li>The global macro <code>simple-vlet</code> is invoked on the variable <code>x</code>, the number <code>1</code>, and the list <code>(simple-vlet y 2 (+ x y))</code>.</li>
             <li>The global macro <code>simple-vlet</code> returns the list <code>((_vlambda (x) (simple-vlet y 2 (+ x y))) 1)</code>.</li>
             <li>The form <code>((_vlambda (x) (simple-vlet y 2 (+ x y))) 1)</code> is evaluated with respect to $[]$ and $[]$.</li>
             <li>
               <ul>
-                <li>The form <code>((_vlambda (x) (simple-vlet y 2 (+ x y))) 1)</code> is classified as a plain function call.</li>
+                <li>The form <code>((_vlambda (x) (simple-vlet y 2 (+ x y))) 1)</code> is analyzed as a plain function call.</li>
                 <li>The form <code>(_vlambda (x) (simple-vlet y 2 (+ x y)))</code> is evaluated with respect to $[]$ and $[]$.</li>
                 <li>The form <code>(_vlambda (x) (simple-vlet y 2 (+ x y)))</code> evaluates to a closure recording the following two pieces of information: the _vlambda-form <code>(_vlambda (x) (simple-vlet y 2 (+ x y)))</code> and the lexical environment $[]$.</li>
                 <li>The form <code>1</code> is evaluated with respect to $[]$ and $[]$.</li>
                     <li>The form <code>(simple-vlet y 2 (+ x y))</code> is evaluated with respect to $[\vbinding{x}{1}]$ and $[]$.</li>
                     <li>
                       <ul>
-                        <li>Because the variable <code>simple-vlet</code> names a macro according to the lookup rule used by <code>fref</code>, the form <code>(simple-vlet y 2 (+ x y))</code> is classified as a macro call.</li>
+                        <li>Because the variable <code>simple-vlet</code> names a macro according to the lookup rule used by <code>fref</code>, the form <code>(simple-vlet y 2 (+ x y))</code> is analyzed as a macro call.</li>
                         <li>The global macro <code>simple-vlet</code> is invoked on the variable <code>y</code>, the number <code>2</code>, and the list <code>(+ x y)</code>.</li>
                         <li>The global macro <code>simple-vlet</code> returns the list <code>((_vlambda (y) (+ x y)) 2)</code>.</li>
                         <li>The form <code>((_vlambda (y) (+ x y)) 2)</code> is evaluated with respect to $[\vbinding{x}{1}]$ and $[]$.</li>
                         <li>
                           <ul>
-                            <li>The form <code>((_vlambda (y) (+ x y)) 2)</code> is classified as a plain function call.</li>
+                            <li>The form <code>((_vlambda (y) (+ x y)) 2)</code> is analyzed as a plain function call.</li>
                             <li>The form <code>(_vlambda (y) (+ x y))</code> is evaluated with respect to $[\vbinding{x}{1}]$ and $[]$.</li>
                             <li>The form <code>(_vlambda (y) (+ x y))</code> evaluates to a closure recording the following two pieces of information: the _vlambda-form <code>(_vlambda (y) (+ x y))</code> and the lexical environment $[\vbinding{x}{1}]$.</li>
                             <li>The form <code>2</code> is evaluated with respect to $[\vbinding{x}{1}]$ and $[]$.</li>
                 <li>The form <code>(if (&gt;= x 0) x (- x)))</code> is evaluated with respect to $[\vbinding{x}{1}]$ and $[]$.</li>
                 <li>
                   <ul>
-                    <li>The form <code>(if (&gt;= x 0) x (- x)))</code> is classified as an if-form.</li>
+                    <li>The form <code>(if (&gt;= x 0) x (- x)))</code> is analyzed as an if-form.</li>
                     <li>The form <code>(&gt;= x 0)</code> is evaluated with respect to $[\vbinding{x}{1}]$ and $[]$.</li>
                     <li>
                       <ul>
                 <li>The form <code>(if (&gt;= x 0) x (- x)))</code> is evaluated with respect to $[\vbinding{x}{-1}]$ and $[]$.</li>
                 <li>
                   <ul>
-                    <li>The form <code>(if (&gt;= x 0) x (- x)))</code> is classified as an if-form.</li>
+                    <li>The form <code>(if (&gt;= x 0) x (- x)))</code> is analyzed as an if-form.</li>
                     <li>The form <code>(&gt;= x 0)</code> is evaluated with respect to $[\vbinding{x}{-1}]$ and $[]$.</li>
                     <li>
                       <ul>
index 7df7704f797534037e4bd9546db089e7586bbe1d..fdae29d20499197e571b00ac9d5b15b72f10ac6d 100644 (file)
@@ -4,6 +4,7 @@
 <html>
   <head>
     <meta charset="utf-8">
+    <title>User Manual</title>
     <link rel="stylesheet" href="___cssURL___"/>
     <script src="___jsURL___"></script>
     <script>const windowId = ___windowId___;</script>
     <ul>
       <li>The prompt is the greater-than sign printed at the beginning of a line to inform you that the listener buffer is waiting for a form to be typed in.</li>
       <li>If you press the Return or Enter key when (1) the cursor is not at the very end of the buffer or (2) the form is missing or incomplete, then a newline is simply inserted into the buffer and no evaluation takes place.</li>
-      <li>If the evaluation completes abruptly, then the reason is necessarily of type <code>error</code> and its payload (a message describing the error) is printed in place of the printable representations of the (nonexisting) resulting values.</li>
+      <li>If the evaluation completes abruptly, then the reason is necessarily of type <code>error</code> and its payload (an object of type <code>string</code> describing the error) is printed in place of the printable representations of the (nonexisting) resulting values.</li>
       <li>If the evaluation does not complete, then you must abort the evaluation or restart the evaluator in order to get a new prompt.</li>
     </ul>
-    <p>We will now illustrate some of the concepts introduced in the section <a href="#programming-language">Programming Language</a> by providing a commented transcript of a sequence of evaluations conducted in a listener buffer. If you want to reproduce the evaluations, be sure to start with a fresh Trampoline++ evaluator. To get a fresh Trampoline++ evaluator, restart the evaluator (using the Restart Evaluator&hellip; command from the Eval menu) with Trampoline++ selected as the evaluator type.</p>
+    <p>We will now illustrate some of the concepts introduced in the section <a href="#programming-language">Programming Language</a> by providing a commented transcript of a sequence of evaluations conducted in a listener buffer. If you want to reproduce the evaluations, be sure to start with a fresh Trampoline++ evaluator. To get a fresh Trampoline++ evaluator, restart the evaluator (using the Restart Evaluator&hellip; command from the Eval menu) with Trampoline++ selected as the evaluator name.</p>
     <p>The global functions used in the evaluations are listed below. For each function, a template function call and a description of the function's behavior are provided. The variable in operator position is the name of the function (i.e., the variable bound to the function in the function namespace of the global environment).</p>
     <dl>
       <dt><code>(car $\cons$)</code></dt>
     <ul>
       <li><code>/system/USER-MANUAL</code>: the user manual (this file)</li>
       <li><code>/system/TUTORIAL</code>: the tutorial</li>
-      <!--<li><code>/system/REFERENCE-MANUAL</code>: the reference manual</li>-->
-      <!--<li><code>/system/IMPLEMENTATION-NOTES</code>: the implementation notes</li>-->
+      <li><code>/system/REFERENCE-MANUAL</code>: the reference manual</li>
+      <li><code>/system/IMPLEMENTATION-NOTES</code>: the implementation notes</li>
       <li><code>/system/BIBLIOGRAPHY</code>: the bibliography</li>
       <li><code>/system/LICENSE</code>: the license</li>
       <li><code>/system/all-caps.css</code>: the CSS file styling the all-caps files</li>
       <li><code>/system/evl2html.xslt</code>: the XSLT file used to convert the EVLambda source files to HTML</li>
       <li><code>/system/evl2html.css</code>: the CSS file styling the EVLambda source files converted to HTML</li>
       <li><code>/system/evl2html.js</code>: the JavaScript file loaded by the EVLambda source files converted to HTML</li>
-      <li><code>/system/mantle.evl</code>: the EVLambda source file implementing the nonprimitive data types, the nonprimitive functions, and the macros constituting the &ldquo;mantle&rdquo; of the EVLambda programming language</li>
+      <li><code>/system/mantle.evl</code>: the EVLambda source file implementing the nonprimitive data types, the nonprimitive functions, the macros, etc., constituting the &ldquo;mantle&rdquo; of the EVLambda programming language</li>
       <li><code>Listener 1</code>: the initial listener</li>
     </ul>
     <h3>Help Menu</h3>
index e63b786c65caa4b32f012973b6b7235d1921c705..818800b0dd267b809d55638187044680975fad4f 100644 (file)
@@ -7,6 +7,12 @@ html {
   line-height: 1.4;
 }
 
+@media print {
+  html {
+    font-size: 12pt;
+  }
+}
+
 div.preamble {
   display: none;
 }
index bda2c3a8fd95b50a7254064dc647b63fa3e9f09c..1c1bcf4f56de2559a84a3ab7ed9fdcaf5f7f6e41 100644 (file)
@@ -1,25 +1,31 @@
 // SPDX-FileCopyrightText: Copyright (c) 2024-2025 Raphaël Van Dyck
 // SPDX-License-Identifier: BSD-3-Clause
 
-/*************/
-/* Interface */
-/*************/
+/********************/
+/* Global Variables */
+/********************/
+
+const isRunningInsideNode = (typeof process !== 'undefined') && (process.release.name === 'node');
+
+let abortSignalArray = null;
+let selectedEvaluator = null;
+
+/*******************/
+/* Interface (IDE) */
+/*******************/
 
 const FOUND_NO_FORM = 0;
-const COMPLETED_NORMALLY = 1;
-const COMPLETED_ABNORMALLY = 2;
+const SUCCESS = 1;
+const ERROR = 2;
 const ABORTED = 3;
 const TERMINATED = 4;
 
 const INITIALIZE = 0;
 const EVALUATE_FIRST_FORM = 1;
 const EVALUATE_ALL_FORMS = 2;
-const CONVERT_TO_XML = 3;
+const CONVERT_EVL_TO_XML = 3;
 
-let signalArray = null;
-let selectedEvaluator = null;
-
-if (typeof onmessage !== 'undefined') { // web worker
+if (!isRunningInsideNode) {
   onmessage = (event) => {
     const {id, action, input} = event.data;
     let response = null;
@@ -33,9 +39,11 @@ if (typeof onmessage !== 'undefined') { // web worker
       case EVALUATE_ALL_FORMS:
         response = evaluateAllForms(input);
         break;
-      case CONVERT_TO_XML:
-        response = convertToXML(input);
+      case CONVERT_EVL_TO_XML:
+        response = convertEVLToXML(input);
         break;
+      default:
+        throw new CannotHappen('onmessage');
     }
     if (response !== null) {
       postMessage({id: id, ...response});
@@ -47,23 +55,22 @@ function foundNoForm() {
   return {status: FOUND_NO_FORM};
 }
 
-function completedNormally(output) {
-  return {status: COMPLETED_NORMALLY, output: output};
+function success(output) {
+  return {status: SUCCESS, output: output};
 }
 
-function completedAbnormally(exception) {
+function abortedOrError(exception) {
   if (exception instanceof Aborted) {
     return {status: ABORTED};
   } else {
-    return {status: COMPLETED_ABNORMALLY, output: exception.message};
+    return {status: ERROR, output: exception.message};
   }
 }
 
 function initialize(input) {
-  signalArray = new Uint8Array(input.signalBuffer);
-  signalArray[0] = 0;
+  abortSignalArray = new Uint8Array(input.abortSignalBuffer);
   selectedEvaluator = input.selectedEvaluator;
-  GlobalEnv.set(VAL_NS, internVariable('*features*'), new EVLCons(internVariable(selectedEvaluator), EVLEmptyList.NIL));
+  initializeFeatureList([selectedEvaluator]);
   let lastResult = EVLVoid.VOID;
   for (const evlFile of input.evlFiles) {
     const tokenizer = new Tokenizer(evlFile);
@@ -73,7 +80,7 @@ function initialize(input) {
       try {
         object = read(tokenizer);
       } catch(exception) {
-        return completedAbnormally(exception);
+        return abortedOrError(exception);
       }
       if (object === null) {
         break;
@@ -81,26 +88,28 @@ function initialize(input) {
         try {
           lastResult = genericEval(object);
         } catch(exception) {
-          return completedAbnormally(exception);
+          return abortedOrError(exception);
         }
       }
     }
   }
   const output = lastResult.allValues().map(object => object.toString());
-  return completedNormally(output);
+  return success(output);
 }
 
 function evaluateFirstForm(text) {
-  signalArray[0] = 0;
+  if (abortSignalArray !== null) {
+    abortSignalArray[0] = 0;
+  }
   const tokenizer = new Tokenizer(text);
   let object = null;
   try {
     object = read(tokenizer);
   } catch(exception) {
-    if (exception instanceof TruncatedToken || exception instanceof UnexpectedEndOfFile) {
+    if (exception instanceof TruncatedToken || exception instanceof UnexpectedEndOfInput) {
       return foundNoForm();
     } else {
-      return completedAbnormally(exception);
+      return abortedOrError(exception);
     }
   }
   if (object === null) {
@@ -110,15 +119,17 @@ function evaluateFirstForm(text) {
     try {
       result = genericEval(object);
     } catch(exception) {
-      return completedAbnormally(exception);
+      return abortedOrError(exception);
     }
     const output = result.allValues().map(object => object.toString());
-    return completedNormally(output);
+    return success(output);
   }
 }
 
 function evaluateAllForms(text) {
-  signalArray[0] = 0;
+  if (abortSignalArray !== null) {
+    abortSignalArray[0] = 0;
+  }
   let lastResult = EVLVoid.VOID;
   const tokenizer = new Tokenizer(text);
   tokenizer.callback = object => lastResult = genericEval(object);
@@ -127,7 +138,7 @@ function evaluateAllForms(text) {
     try {
       object = read(tokenizer);
     } catch(exception) {
-      return completedAbnormally(exception);
+      return abortedOrError(exception);
     }
     if (object === null) {
       break;
@@ -135,24 +146,23 @@ function evaluateAllForms(text) {
       try {
         lastResult = genericEval(object);
       } catch(exception) {
-        return completedAbnormally(exception);
+        return abortedOrError(exception);
       }
     }
   }
   const output = lastResult.allValues().map(object => object.toString());
-  return completedNormally(output);
+  return success(output);
 }
 
-function convertToXML(text) {
-  signalArray[0] = 0;
+function convertEVLToXML(text) {
   const tokenizer = new Tokenizer(text, true);
   let xml = null;
   try {
-    xml = doConvertToXML(tokenizer);
+    xml = doConvertEVLToXML(tokenizer);
   } catch(exception) {
-    return completedAbnormally(exception);
+    return abortedOrError(exception);
   }
-  return completedNormally(xml);
+  return success(xml);
 }
 
 /**********/
@@ -187,17 +197,17 @@ class ReaderError extends Error {
   }
 }
 
-class ConverterError extends Error {
+class EVLToXMLConverterError extends Error {
   constructor(message) {
     super(message);
-    this.name = 'ConverterError';
+    this.name = 'EVLToXMLConverterError';
   }
 }
 
-class SyntaxAnalyzerError extends Error {
+class FormAnalyzerError extends Error {
   constructor(message) {
     super(message);
-    this.name = 'SyntaxAnalyzerError';
+    this.name = 'FormAnalyzerError';
   }
 }
 
@@ -219,7 +229,7 @@ class TruncatedToken extends TokenizerError {
   }
 }
 
-// token types
+// token categories
 const QUOTE = 0;
 const QUASIQUOTE = 1;
 const UNQUOTE = 2;
@@ -233,15 +243,16 @@ const HASH_MINUS = 9;
 const VOID = 10; // value is EVLVoid.VOID
 const BOOLEAN = 11; // value is EVLBoolean.TRUE or EVLBoolean.FALSE
 const CHARACTER = 12; // value is an EVLCharacter
-const XML_START_TAG = 13; // value is an XML element name (javascript string)
-const XML_END_TAG = 14; // value is an XML element name (javascript string)
-const XML_EMPTY_ELEMENT_TAG = 15; // value is an XML element name (javascript string)
+const XML_START_TAG = 13; // value is an XML element name
+const XML_END_TAG = 14; // value is an XML element name
+const XML_EMPTY_ELEMENT_TAG = 15; // value is an XML element name
 const XML_COMMENT = 16;
 const DOT = 17; // the dot of dotted lists
 const NUMBER = 18; // value is an EVLNumber
 const KEYWORD = 19; // value is an EVLKeyword
 const VARIABLE = 20; // value is an EVLVariable
-const EOF = 21;
+const BOI = 21; // beginning of input
+const EOI = 22; // end of input
 
 const codePointRegExp = /^[a-fA-F0-9]+$/;
 const numberRegExp = /^[+-]?[0-9]+(?:\.[0-9]+)?$/;
@@ -262,15 +273,17 @@ function isSurrogate(codeUnit) {
 
 function ensureCodePoint (charOrCodePoint) {
   if (typeof charOrCodePoint === "number") {
+    // charOrCodePoint is a JavaScript number
     return charOrCodePoint;
   } else {
+    // charOrCodePoint is a JavaScript string of one or two UTF-16 code units
     return charOrCodePoint.codePointAt(0);
   }
 }
 
 function isControlCharacter(charOrCodePoint) {
   const codePoint = ensureCodePoint(charOrCodePoint);
-  return (0x00 <= codePoint && codePoint <= 0x1F) || (0x7F <= codePoint && codePoint <= 0x9F);
+  return (0x0000 <= codePoint && codePoint <= 0x001F) || (0x007F <= codePoint && codePoint <= 0x009F);
 }
 
 function isNoncharacter(charOrCodePoint) {
@@ -281,7 +294,27 @@ function isNoncharacter(charOrCodePoint) {
 
 function isWhitespaceCharacter(charOrCodePoint) {
   // https://www.unicode.org/Public/UCD/latest/ucd/PropList.txt
-  // Pattern_White_Space
+  // Whitespace =
+  // 0009..000D   <control-0009>..<control-000D>
+  // 0020         SPACE
+  // 0085         <control-0085>
+  // 00A0         NO-BREAK SPACE
+  // 1680         OGHAM SPACE MARK
+  // 2000..200A   EN QUAD..HAIR SPACE
+  // 2028         LINE SEPARATOR
+  // 2029         PARAGRAPH SEPARATOR
+  // 202F         NARROW NO-BREAK SPACE
+  // 205F         MEDIUM MATHEMATICAL SPACE
+  // 3000         IDEOGRAPHIC SPACE
+  // https://www.unicode.org/L2/L2005/05012r-pattern.html
+  // Pattern_Whitespace = Whitespace + Left-to-Right Mark + Right-to-Left Mark -
+  // 00A0         NO-BREAK SPACE
+  // 1680         OGHAM SPACE MARK
+  // 180E         MONGOLIAN VOWEL SEPARATOR
+  // 2000..200A   EN QUAD..HAIR SPACE
+  // 202F         NARROW NO-BREAK SPACE
+  // 205F         MEDIUM MATHEMATICAL SPACE
+  // 3000         IDEOGRAPHIC SPACE
   const codePoint = ensureCodePoint(charOrCodePoint);
   return (
     codePoint === 0x0009 || // Horizontal Tab
@@ -322,15 +355,15 @@ function isDecimalDigit(charOrCodePoint) {
 }
 
 class Tokenizer {
-  constructor(text, convertToXML = false) {
+  constructor(text, convertEVLToXML = false) {
     this.text = text;
-    this.convertToXML = convertToXML;
+    this.convertEVLToXML = convertEVLToXML;
     this.position = 0;
-    this.xmlStack = []; // element: XML element name
+    this.xmlStack = []; // array of XML element names
     this.savedCodeUnits = '';
   }
   peekCharacter(position = this.position) {
-    let char = null; // one or two UTF-16 code units
+    let char = null; // JavaScript string of one or two UTF-16 code units
     const codeUnit = this.text.charCodeAt(position);
     if (isTrailingSurrogate(codeUnit)) {
       throw new TokenizerError('Lone surrogate.');
@@ -362,20 +395,20 @@ class Tokenizer {
     this.position += char.length;
   }
   nextToken() {
-    this.whitespace = ''; // whitespace preceding the lexeme
+    this.whitespace = '';
     this.lexeme = '';
     if (this.savedCodeUnits.length !== 0) {
-      this.type = CHARACTER;
+      this.category = CHARACTER;
       this.value = new EVLCharacter(this.savedCodeUnits.charAt(0));
       this.savedCodeUnits = this.savedCodeUnits.substring(1);
     } else {
-      this.type = null;
+      this.category = null;
       this.value = null;
       const pureXML = this.xmlStack.length !== 0 && !['chapter', 'section'].includes(this.xmlStack[this.xmlStack.length - 1]);
-      while (this.type === null) {
+      while (this.category === null) {
         this.skipWhitespace(pureXML);
         if (this.position === this.text.length) {
-          this.type = EOF;
+          this.category = EOI;
         } else {
           this.readToken(pureXML);
         }
@@ -383,7 +416,7 @@ class Tokenizer {
     }
   }
   skipWhitespace(pureXML) {
-    // When pure XML is true, XML character data is treated as whitespace.
+    // When pureXML is true, XML character data is treated as whitespace.
     while (true) {
       if (this.position === this.text.length) {
         break;
@@ -401,39 +434,39 @@ class Tokenizer {
     switch (char) {
       case "'":
         this.consumeCharacter(char);
-        this.type = QUOTE;
+        this.category = QUOTE;
         break;
       case '`':
         this.consumeCharacter(char);
-        this.type = QUASIQUOTE;
+        this.category = QUASIQUOTE;
         break;
       case ',':
         this.consumeCharacter(char);
         if (this.position === this.text.length) {
-          this.type = UNQUOTE;
+          this.category = UNQUOTE;
         } else {
           const char2 = this.peekCharacter();
           if (char2 === '@') {
             this.consumeCharacter(char2);
-            this.type = UNQUOTE_SPLICING;
+            this.category = UNQUOTE_SPLICING;
           } else {
-            this.type = UNQUOTE;
+            this.category = UNQUOTE;
           }
         }
         break;
       case '"':
         this.consumeCharacter(char);
         const string = readString(this);
-        this.type = STRING;
+        this.category = STRING;
         this.value = new EVLString(string);
         break;
       case '(':
         this.consumeCharacter(char);
-        this.type = OPENING_PARENTHESIS;
+        this.category = OPENING_PARENTHESIS;
         break;
       case ')':
         this.consumeCharacter(char);
-        this.type = CLOSING_PARENTHESIS;
+        this.category = CLOSING_PARENTHESIS;
         break;
       case '#':
         this.consumeCharacter(char);
@@ -450,15 +483,15 @@ class Tokenizer {
       default:
         const protoToken = readProtoToken(this);
         if (protoToken === '.') {
-          this.type = DOT;
+          this.category = DOT;
         } else if (numberRegExp.test(protoToken)) {
-          this.type = NUMBER;
+          this.category = NUMBER;
           this.value = new EVLNumber(Number.parseFloat(protoToken));
         } else if (keywordRegExp.test(protoToken)) {
-          this.type = KEYWORD;
+          this.category = KEYWORD;
           this.value = internKeyword(protoToken.substring(1));
         } else if (variableRegExp.test(protoToken)) {
-          this.type = VARIABLE;
+          this.category = VARIABLE;
           this.value = internVariable(protoToken);
         } else {
           throw new TokenizerError('Malformed proto-token.');
@@ -473,7 +506,7 @@ function escapeCharacters(chars, escapeCharacter) {
   let position = 0;
   const length = chars.length;
   while (position < length) {
-    let char = null; // one or two UTF-16 code units
+    let char = null; // JavaScript string of one or two UTF-16 code units
     const codeUnit = chars.charCodeAt(position);
     if (isTrailingSurrogate(codeUnit)) {
       escapedChars += unicodeEscape(codeUnit);
@@ -518,6 +551,7 @@ function unicodeEscape(charOrCodePoint) {
 }
 
 function readEscapeSequence(tokenizer) {
+  // reads {xyz}, returns xyz
   let chars = '';
   if (tokenizer.position === tokenizer.text.length) {
     throw new TruncatedToken('Truncated escape sequence.');
@@ -635,41 +669,41 @@ function readHashConstruct(tokenizer) {
   }
   switch (char) {
     case '(':
-      tokenizer.type = HASH_OPENING_PARENTHESIS;
+      tokenizer.category = HASH_OPENING_PARENTHESIS;
       break;
     case '+':
-      tokenizer.type = HASH_PLUS;
+      tokenizer.category = HASH_PLUS;
       break;
     case '-':
-      tokenizer.type = HASH_MINUS;
+      tokenizer.category = HASH_MINUS;
       break;
     case 'v':
-      tokenizer.type = VOID;
+      tokenizer.category = VOID;
       tokenizer.value = EVLVoid.VOID;
       break;
     case 't':
-      tokenizer.type = BOOLEAN;
+      tokenizer.category = BOOLEAN;
       tokenizer.value = EVLBoolean.TRUE;
       break;
     case 'f':
-      tokenizer.type = BOOLEAN;
+      tokenizer.category = BOOLEAN;
       tokenizer.value = EVLBoolean.FALSE;
       break;
     case '"':
       const string = readString(tokenizer);
-      if (tokenizer.convertToXML) {
-        tokenizer.type = CHARACTER;
-        tokenizer.value = new EVLCharacter(string);
+      if (tokenizer.convertEVLToXML) {
+        tokenizer.category = CHARACTER;
+        tokenizer.value = null; // the value is ignored by the EVL to XML converter
       } else if (arg !== '') {
         const index = Number.parseInt(arg);
         if (index < string.length) {
-          tokenizer.type = CHARACTER;
+          tokenizer.category = CHARACTER;
           tokenizer.value = new EVLCharacter(string.charAt(index));
         } else {
           throw new TokenizerError('Index out of bounds.');
         }
       } else if (string.length !== 0) {
-        tokenizer.type = CHARACTER;
+        tokenizer.category = CHARACTER;
         tokenizer.value = new EVLCharacter(string.charAt(0));
         tokenizer.savedCodeUnits = string.substring(1);
       }
@@ -735,7 +769,7 @@ function readXMLMarkup(tokenizer, consume) {
     tokenizer.lexeme = tokenizer.text.slice(tokenizer.position, position);
     tokenizer.position = position;
     if (isXMLComment) {
-      tokenizer.type = XML_COMMENT;
+      tokenizer.category = XML_COMMENT;
     } else if (isXMLEndTag) {
       if (tokenizer.xmlStack.length === 0) {
         throw new TokenizerError('Unexpected XML end tag.');
@@ -744,14 +778,14 @@ function readXMLMarkup(tokenizer, consume) {
         throw new TokenizerError('Unmatched XML tags.');
       }
       tokenizer.xmlStack.pop();
-      tokenizer.type = XML_END_TAG;
+      tokenizer.category = XML_END_TAG;
       tokenizer.value = name;
     } else if (isXMLEmptyElementTag) {
-      tokenizer.type = XML_EMPTY_ELEMENT_TAG;
+      tokenizer.category = XML_EMPTY_ELEMENT_TAG;
       tokenizer.value = name;
     } else {
       tokenizer.xmlStack.push(name);
-      tokenizer.type = XML_START_TAG;
+      tokenizer.category = XML_START_TAG;
       tokenizer.value = name;
     }
   }
@@ -842,10 +876,10 @@ class UnexpectedXMLEndTag extends ReaderError {
   }
 }
 
-class UnexpectedEndOfFile extends ReaderError {
+class UnexpectedEndOfInput extends ReaderError {
   constructor() {
-    super('Unexpected end-of-file.');
-    this.name = 'UnexpectedEndOfFile';
+    super('Unexpected end-of-input.');
+    this.name = 'UnexpectedEndOfInput';
   }
 }
 
@@ -858,7 +892,7 @@ function read(tokenizer) {
       throw new UnexpectedClosingParenthesis();
     case XML_END_TAG:
       throw new UnexpectedXMLEndTag();
-    case EOF:
+    case EOI:
       return null;
     default:
       return object;
@@ -866,11 +900,11 @@ function read(tokenizer) {
 }
 
 function readObject(tokenizer) {
-  // Returns DOT, CLOSING_PARENTHESIS, XML_END_TAG, EOF, or an object.
+  // Returns DOT, CLOSING_PARENTHESIS, XML_END_TAG, EOI, or an object.
   // XML elements are skipped because they are treated as comments.
   while (true) {
     tokenizer.nextToken();
-    switch (tokenizer.type) {
+    switch (tokenizer.category) {
       case VOID:
       case BOOLEAN:
       case NUMBER:
@@ -920,8 +954,8 @@ function readObject(tokenizer) {
         break; // skip
       case XML_COMMENT:
         break; // skip
-      case EOF:
-        return EOF;
+      case EOI:
+        return EOI;
       default:
         throw new CannotHappen('readObject');
     }
@@ -937,23 +971,24 @@ function readAbbreviation(tokenizer, variable) {
       throw new UnexpectedClosingParenthesis();
     case XML_END_TAG:
       throw new UnexpectedXMLEndTag();
-    case EOF:
-      throw new UnexpectedEndOfFile();
+    case EOI:
+      throw new UnexpectedEndOfInput();
     default:
       return new EVLCons(variable, new EVLCons(object, EVLEmptyList.NIL));
   }
 }
 
 function readReadTimeConditional(tokenizer, polarity) {
-  const featureExpression = readReadTimeConditionalFeatureExpression(tokenizer);
+  const featureExpression = readFeatureExpression(tokenizer);
+  const conditionalizedObject = readConditionalizedObject(tokenizer)
   if (evaluateFeatureExpression(featureExpression) === polarity) {
-    return readReadTimeConditionalObject(tokenizer);
+    return conditionalizedObject;
   } else {
-    return readReadTimeConditionalObject(tokenizer), null;
+    return null;
   }
 }
 
-function readReadTimeConditionalFeatureExpression(tokenizer) {
+function readFeatureExpression(tokenizer) {
   const object = readObject(tokenizer);
   switch (object) {
     case DOT:
@@ -962,14 +997,14 @@ function readReadTimeConditionalFeatureExpression(tokenizer) {
       throw new UnexpectedClosingParenthesis();
     case XML_END_TAG:
       throw new UnexpectedXMLEndTag();
-    case EOF:
-      throw new UnexpectedEndOfFile();
+    case EOI:
+      throw new UnexpectedEndOfInput();
     default:
       return object;
   }
 }
 
-function readReadTimeConditionalObject(tokenizer) {
+function readConditionalizedObject(tokenizer) {
   const object = readObject(tokenizer);
   switch (object) {
     case DOT:
@@ -978,8 +1013,8 @@ function readReadTimeConditionalObject(tokenizer) {
       throw new UnexpectedClosingParenthesis();
     case XML_END_TAG:
       throw new UnexpectedXMLEndTag();
-    case EOF:
-      throw new UnexpectedEndOfFile();
+    case EOI:
+      throw new UnexpectedEndOfInput();
     default:
       return object;
   }
@@ -1004,6 +1039,21 @@ function evaluateFeatureExpression(featureExpression) {
   }
 }
 
+function initializeFeatureList(features) {
+  let list = EVLEmptyList.NIL;
+  let lastCons = null;
+  for (const feature of features) {
+    const newCons = new EVLCons(internVariable(feature), EVLEmptyList.NIL);
+    if (lastCons === null) {
+      list = newCons;
+    } else {
+      lastCons.cdr = newCons;
+    }
+    lastCons = newCons;
+  }
+  GlobalEnv.set(VAL_NS, internVariable('*features*'), list);
+}
+
 function evaluateSymbolFeatureExpression(featureExpression) {
   let list = GlobalEnv.ref(VAL_NS, internVariable('*features*'));
   while (list !== EVLEmptyList.NIL) {
@@ -1072,8 +1122,8 @@ function readList(tokenizer) {
         break loop;
       case XML_END_TAG:
         throw new UnexpectedXMLEndTag();
-      case EOF:
-        throw new UnexpectedEndOfFile();
+      case EOI:
+        throw new UnexpectedEndOfInput();
       default:
         const newCons = new EVLCons(object, EVLEmptyList.NIL);
         if (lastCons === null) {
@@ -1100,8 +1150,8 @@ function readDottedList(tokenizer, list, lastCons) {
       throw new ReaderError('Malformed dotted list.');
     case XML_END_TAG:
       throw new UnexpectedXMLEndTag();
-    case EOF:
-      throw new UnexpectedEndOfFile();
+    case EOI:
+      throw new UnexpectedEndOfInput();
     default:
       lastCons.cdr = object;
       break
@@ -1114,8 +1164,8 @@ function readDottedList(tokenizer, list, lastCons) {
       return list;
     case XML_END_TAG:
       throw new UnexpectedXMLEndTag();
-    case EOF:
-      throw new UnexpectedEndOfFile();
+    case EOI:
+      throw new UnexpectedEndOfInput();
     default:
       throw new ReaderError('Malformed dotted list.');
   }
@@ -1132,8 +1182,8 @@ function readVector(tokenizer) {
         break loop;
       case XML_END_TAG:
         throw new UnexpectedXMLEndTag();
-      case EOF:
-        throw new UnexpectedEndOfFile();
+      case EOI:
+        throw new UnexpectedEndOfInput();
       default:
         elements.push(object);
         break;
@@ -1158,8 +1208,8 @@ function readXMLElement(tokenizer) {
         } else {
           throw new ReaderError('Unmatched XML tags.');
         }
-      case EOF:
-        throw new UnexpectedEndOfFile();
+      case EOI:
+        throw new UnexpectedEndOfInput();
       default:
         const callback = tokenizer.callback;
         if (callback !== undefined) {
@@ -1174,70 +1224,39 @@ function readXMLElement(tokenizer) {
 /* EVL to XML Converter */
 /************************/
 
-const TOPLEVEL = 100; // top level context
-const ABBREVIATION = 101; // abbreviation context
-const RTC1 = 102; // context between #+ or #- and feature expression
-const RTC2 = 103; // context between feature expression and object
-const SEQUENCE = 104; // list or vector context
+const XML_TOKEN = 100;
+const EVL_TOKEN = 101;
+const EOL_COMMENT = 102;
 
-const ABSTRACT_BOF = 0; // beginning-of-file abstract token
-const ABSTRACT_EVL = 1; // EVL abstract token
-const ABSTRACT_XML = 2; // XML abstract token
-const ABSTRACT_EOL_COMMENT = 3; // end-of-line comment
-const ABSTRACT_EOF = 4; // end-of-file abstract token
+const XML_CONTEXT = 0;
+const EVL_CONTEXT = 1;
 
-function doConvertToXML(tokenizer) {
+function doConvertEVLToXML(tokenizer) {
   let xml = '';
-  const contextStack = [TOPLEVEL]; // element: TOPLEVEL, ABBREVIATION, RTC1, RTC2, SEQUENCE, or XML element name
-  let previousAbstractToken = ABSTRACT_BOF;
-  let context = TOPLEVEL;
-  let abstractToken = null;
-  while ((abstractToken = abstractRead(tokenizer, contextStack)) !== ABSTRACT_EOF) {
-    if (context === TOPLEVEL) {
-      // BOF   <evl-object|xml-element>   <evl-object|xml-element>   EOF
-      //    ^^^                        ^^^                        ^^^
-      xml += tokenizer.whitespace; // whitespace is written as is
-    } else if ([ABBREVIATION, RTC1, RTC2, SEQUENCE].includes(context)) {
-      // '   <xml-element>   <xml-element>   <evl-object>
-      //  ^^^             ^^^             ^^^
-      // #+   <xml-element>   <xml-element>   <evl-object>   <xml-element>   <xml-element>   <evl-object>
-      //   ^^^             ^^^             ^^^
-      // #+   <xml-element>   <xml-element>   <evl-object>   <xml-element>   <xml-element>   <evl-object>
-      //                                                  ^^^             ^^^             ^^^
-      // (   <evl-object|xml-element>   <xml-element|xml-element>   )
-      //  ^^^                        ^^^                         ^^^
-      xml += convertEVL(previousAbstractToken, tokenizer.whitespace, abstractToken); // whitespace is converted by convertEVL
-    } else if (['chapter', 'section'].includes(context)) {
-      // <chapter>   <evl-object|xml-element>   <evl-object|xml-element>   </chapter>
-      //          ^^^                        ^^^                        ^^^
-      // <section>   <evl-object|xml-element>   <evl-object|xml-element>   </section>
-      //          ^^^                        ^^^                        ^^^
-      xml += convertXML(previousAbstractToken, tokenizer.whitespace, abstractToken); // whitespace is converted by convertXML
-    } else {
-      // <para>   <xml-element>   <xml-element>   </para>
-      //       ^^^             ^^^             ^^^
-      xml += tokenizer.whitespace; // whitespace (= character data) is written as is
-    }
-    if (abstractToken === ABSTRACT_EVL) {
-      xml += xmlEscape(tokenizer.lexeme); // lexeme is xml escaped
-    } else {
-      xml += tokenizer.lexeme; // lexeme is written as is
-    }
-    previousAbstractToken = abstractToken;
+  const contextStack = [];
+  let previousToken = BOI;
+  let context = contextStack[contextStack.length - 1];
+  let token = null;
+  while ((token = sketchyRead(tokenizer, contextStack)) !== EOI) {
+    if (context === XML_CONTEXT) {
+      xml += convertXMLWhitespace(previousToken, tokenizer.whitespace, token);
+    } else if (context = EVL_CONTEXT) {
+      xml += convertEVLWhitespace(previousToken, tokenizer.whitespace, token);
+    } else { // top-level context
+      xml += tokenizer.whitespace;
+    }
+    if (token === EVL_TOKEN) {
+      xml += xmlEscape(tokenizer.lexeme);
+    } else { // XML_TOKEN or EOL_COMMENT
+      xml += tokenizer.lexeme;
+    }
+    previousToken = token;
     context = contextStack[contextStack.length - 1];
   }
-  xml += tokenizer.whitespace; // whitespace is written as is
+  xml += tokenizer.whitespace;
   return xml;
 }
 
-// Example: BOF[1]<chapter>[2]([3]xxx[4])[5]</chapter>[6]EOF
-// whitespace [1] is processed in top level context
-// whitespace [2] is processed in chapter context
-// whitespace [3] is processed in sequence context
-// whitespace [4] is processed in sequence context
-// whitespace [5] is processed in chapter context
-// whitespace [6] is processed in top level context
-
 function xmlEscape(string) {
   return string.replace(/[<>&]/g, function (char) {
     switch (char) {
@@ -1251,147 +1270,111 @@ function xmlEscape(string) {
   });
 }
 
-function abstractRead(tokenizer, contextStack) {
+function sketchyRead(tokenizer, contextStack) {
   tokenizer.nextToken();
-  switch (tokenizer.type) {
+  switch (tokenizer.category) {
     case VOID:
     case BOOLEAN:
     case NUMBER:
-    case CHARACTER:
+    case CHARACTER: // full hash-string construct
     case STRING:
     case KEYWORD:
     case VARIABLE:
-      updateContextStackForEVLObject(contextStack);
-      return ABSTRACT_EVL;
     case QUOTE:
     case QUASIQUOTE:
     case UNQUOTE:
     case UNQUOTE_SPLICING:
-      contextStack.push(ABBREVIATION); // enter abbreviation context
-      return ABSTRACT_EVL;
     case HASH_PLUS:
     case HASH_MINUS:
-      contextStack.push(RTC1); // enter rtc1 context
-      return ABSTRACT_EVL;
+    case DOT:
+      return EVL_TOKEN;
     case OPENING_PARENTHESIS:
     case HASH_OPENING_PARENTHESIS:
-      contextStack.push(SEQUENCE); // enter sequence context
-      return ABSTRACT_EVL;
-    case DOT:
-      return ABSTRACT_EVL;
+      contextStack.push(EVL_CONTEXT);
+      return EVL_TOKEN;
     case CLOSING_PARENTHESIS:
-      if (contextStack[contextStack.length - 1] !== SEQUENCE) {
-        throw new ConverterError('Unexpected closing parenthesis.');
+      if (contextStack[contextStack.length - 1] !== EVL_CONTEXT) {
+        throw new EVLToXMLConverterError('Unexpected closing parenthesis.');
       }
-      contextStack.pop(); // exit sequence context
-      updateContextStackForEVLObject(contextStack);
-      return ABSTRACT_EVL;
+      contextStack.pop();
+      return EVL_TOKEN;
     case XML_START_TAG:
       if (tokenizer.value === 'comment') {
-        abstractReadEndOfLineComment(tokenizer, contextStack);
-        return ABSTRACT_EOL_COMMENT;
+        readEndOfLineComment(tokenizer);
+        return EOL_COMMENT;
       } else {
-        contextStack.push(tokenizer.value); // enter XML element name context
-        return ABSTRACT_XML;
+        contextStack.push(XML_CONTEXT);
+        return XML_TOKEN;
       }
     case XML_END_TAG:
-      if (typeof contextStack[contextStack.length - 1] !== 'string') {
-        throw new ConverterError('Unexpected XML end tag.');
-      }
-      if (contextStack[contextStack.length - 1] !== tokenizer.value) {
-        throw new ConverterError('Unmatched XML tags.');
+      if (contextStack[contextStack.length - 1] !== XML_CONTEXT) {
+        throw new EVLToXMLConverterError('Unexpected XML end tag.');
       }
-      contextStack.pop(); // exit XML element name context
-      return ABSTRACT_XML;
+      contextStack.pop();
+      return XML_TOKEN;
     case XML_EMPTY_ELEMENT_TAG:
-      return ABSTRACT_XML;
     case XML_COMMENT:
-      return ABSTRACT_XML;
-    case EOF:
-      if (contextStack[contextStack.length - 1] !== TOPLEVEL) {
-        throw new ConverterError('Unexpected end-of-file.');
+      return XML_TOKEN;
+    case EOI:
+      if (contextStack.length !== 0) {
+        throw new EVLToXMLConverterError('Unexpected end-of-input.');
       }
-      contextStack.pop(); // exit top level context
-      return ABSTRACT_EOF;
+      return EOI;
     default:
-      throw new CannotHappen('abstractRead');
+      throw new CannotHappen('sketchyRead');
   }
 }
 
-function updateContextStackForEVLObject(contextStack) {
-  while (true) {
-    switch (contextStack[contextStack.length - 1]) {
-      case ABBREVIATION:
-        contextStack.pop(); // exit abbreviation context
-        break;
-      case RTC1:
-        contextStack.pop(); // exit rtc1 context
-        contextStack.push(RTC2); // enter rtc2 context
-        return;
-      case RTC2:
-        contextStack.pop(); // exit rtc2 context
-        break;
-      default:
-        return;
-    }
-  }
-}
-
-function abstractReadEndOfLineComment(tokenizer) {
+function readEndOfLineComment(tokenizer) {
   const whitespace = tokenizer.whitespace;
   let lexeme = tokenizer.lexeme;
-  const contextStack = [tokenizer.value]; // local stack
+  const contextStack = [];
   while (true) {
     tokenizer.nextToken();
-    switch (tokenizer.type) {
+    switch (tokenizer.category) {
       case XML_START_TAG:
         lexeme += tokenizer.whitespace;
         lexeme += tokenizer.lexeme;
-        contextStack.push(tokenizer.value);
+        contextStack.push(XML_CONTEXT);
         break;
       case XML_END_TAG:
-        if (contextStack[contextStack.length - 1] !== tokenizer.value) {
-          throw new ConverterError('Unmatched XML tags.');
-        }
         lexeme += tokenizer.whitespace;
         lexeme += tokenizer.lexeme;
-        contextStack.pop();
         if (contextStack.length === 0) {
-          tokenizer.whitespace = whitespace; // whitespace before end-of-line comment
+          tokenizer.whitespace = whitespace; // run of whitespace before end-of-line comment
           tokenizer.lexeme = lexeme; // end-of-line comment
           return;
+        } else {
+          contextStack.pop();
+          break;
         }
-        break;
       case XML_EMPTY_ELEMENT_TAG:
-        lexeme += tokenizer.whitespace;
-        lexeme += tokenizer.lexeme;
-        break;
       case XML_COMMENT:
         lexeme += tokenizer.whitespace;
         lexeme += tokenizer.lexeme;
         break;
-      case EOF:
-        throw new ConverterError('Unexpected end-of-file.');
+      case EOI:
+        throw new EVLToXMLConverterError('Unexpected end-of-input.');
       default:
-        throw new CannotHappen('abstractReadEndOfLineComment');
+        throw new CannotHappen('readEndOfLineComment');
     }
   }
 }
 
-function isXMLAbstractToken(abstractToken) {
-  return abstractToken === ABSTRACT_XML;
+function isXMLToken(token) {
+  return token === XML_TOKEN;
 }
 
-function isEVLAbstractToken(abstractToken) {
-  return abstractToken === ABSTRACT_EVL || abstractToken === ABSTRACT_EOL_COMMENT;
+function isEVLToken(token) {
+  return token === EVL_TOKEN || token === EOL_COMMENT;
 }
 
-function convertXML(previousAbstractToken, whitespace, abstractToken) {
+function convertXMLWhitespace(previousToken, whitespace, token) {
   let xml = '';
-  if (isXMLAbstractToken(previousAbstractToken) && isEVLAbstractToken(abstractToken)) {
+  if (isXMLToken(previousToken) && isEVLToken(token)) {
     xml += whitespace;
     xml += '<toplevelcode><blockcode>';
-  } else if (isEVLAbstractToken(previousAbstractToken) && isEVLAbstractToken(abstractToken)) {
+  } else if (isEVLToken(previousToken) && isEVLToken(token)) {
     if (countNewlines(whitespace) >= 2) {
       xml += '</blockcode></toplevelcode>';
       xml += whitespace;
@@ -1399,7 +1382,7 @@ function convertXML(previousAbstractToken, whitespace, abstractToken) {
     } else {
       xml += whitespace;
     }
-  } else if (isEVLAbstractToken(previousAbstractToken) && isXMLAbstractToken(abstractToken)) {
+  } else if (isEVLToken(previousToken) && isXMLToken(token)) {
     xml += '</blockcode></toplevelcode>';
     xml += whitespace;
   } else {
@@ -1418,14 +1401,14 @@ function countNewlines(string) {
   return count;
 }
 
-function convertEVL(previousAbstractToken, whitespace, abstractToken) {
+function convertEVLWhitespace(previousToken, whitespace, token) {
   let xml = '';
-  if (isEVLAbstractToken(previousAbstractToken) && isXMLAbstractToken(abstractToken)) {
+  if (isEVLToken(previousToken) && isXMLToken(token)) {
     xml += '</blockcode><indentation style="margin-left: ';
     xml += countSpacesAfterFirstNewline(whitespace);
     xml += 'ch;"><blockcomment>';
     xml += whitespace;
-  } else if (isXMLAbstractToken(previousAbstractToken) && isEVLAbstractToken(abstractToken)) {
+  } else if (isXMLToken(previousToken) && isEVLToken(token)) {
     xml += '</blockcomment></indentation><blockcode>';
     xml += whitespace;
   } else {
@@ -1453,19 +1436,19 @@ function countSpacesAfterFirstNewline(string) {
   return count;
 }
 
-/*******************/
-/* Syntax Analyzer */
-/*******************/
+/*****************/
+/* Form Analyzer */
+/*****************/
 
-function syntaxAnalyzerError(formName) {
-  throw new SyntaxAnalyzerError(`Malformed ${formName} form.`);
+function formAnalyzerError(formName) {
+  throw new FormAnalyzerError(`Malformed ${formName} form.`);
 }
 
-function checkCons(object, formName) {
-  if (object instanceof EVLCons) {
+function checkVariable(object, formName) {
+  if (object instanceof EVLVariable) {
     return object;
   } else {
-    syntaxAnalyzerError(formName);
+    formAnalyzerError(formName);
   }
 }
 
@@ -1473,7 +1456,15 @@ function checkEmptyList(object, formName) {
   if (object instanceof EVLEmptyList) {
     return object;
   } else {
-    syntaxAnalyzerError(formName);
+    formAnalyzerError(formName);
+  }
+}
+
+function checkCons(object, formName) {
+  if (object instanceof EVLCons) {
+    return object;
+  } else {
+    formAnalyzerError(formName);
   }
 }
 
@@ -1483,7 +1474,7 @@ function checkProperList(object, formName) {
     if (list instanceof EVLCons) {
       list = list.cdr;
     } else {
-      syntaxAnalyzerError(formName);
+      formAnalyzerError(formName);
     }
   }
   return object;
@@ -1493,48 +1484,40 @@ function checkParameterList(object, formName) {
   if (object instanceof EVLVariable) {
     return [[object], true];
   } else {
-    const variables = [];
-    let variadic = false;
+    const parameters = [];
+    let rest = false;
     let list = object
     while (list !== EVLEmptyList.NIL) {
       if (list instanceof EVLCons) {
         if (list.car instanceof EVLVariable) {
-          variables.push(list.car);
+          parameters.push(list.car);
         } else {
-          syntaxAnalyzerError(formName);
+          formAnalyzerError(formName);
         }
         if (list.cdr instanceof EVLVariable) {
-          variables.push(list.cdr);
-          variadic = true;
+          parameters.push(list.cdr);
+          rest = true;
           break;
         } else {
           list = list.cdr;
         }
       } else {
-        syntaxAnalyzerError(formName);
+        formAnalyzerError(formName);
       }
     }
-    if (new Set(variables).size !== variables.length) {
-      syntaxAnalyzerError(formName);
+    if (new Set(parameters).size !== parameters.length) {
+      formAnalyzerError(formName);
     }
-    return [variables, variadic];
-  }
-}
-
-function checkVariable(object, formName) {
-  if (object instanceof EVLVariable) {
-    return object;
-  } else {
-    syntaxAnalyzerError(formName);
+    return [parameters, rest];
   }
 }
 
 function analyzeQuote(form) {
   let cons = form;
   cons = checkCons(cons.cdr, 'quote');
-  const object = cons.car;
+  const literal = cons.car;
   checkEmptyList(cons.cdr, 'quote');
-  return [object];
+  return [literal];
 }
 
 function analyzeProgn(form) {
@@ -1558,9 +1541,9 @@ function analyzeIf(form) {
 function analyzeLambda(form) {
   let cons = form;
   cons = checkCons(cons.cdr, '_lambda');
-  const [variables, variadic] = checkParameterList(cons.car, '_lambda');
+  const [parameters, rest] = checkParameterList(cons.car, '_lambda');
   const forms = checkProperList(cons.cdr, '_lambda');
-  return [variables, variadic, forms];
+  return [parameters, rest, forms];
 }
 
 function analyzeRef(form) {
@@ -1600,22 +1583,22 @@ function analyzeCatchErrors(form) {
   return [tryForm];
 }
 
-function analyzeApplication(mv, apply, form) {
+function analyzeCall(mv, apply, form) {
   let cons = form;
   if (mv || apply) {
-    cons = checkCons(cons.cdr, 'application');
+    cons = checkCons(cons.cdr, 'call');
   }
   const operator = cons.car;
-  const operands = checkProperList(cons.cdr, 'application');
+  const operands = checkProperList(cons.cdr, 'call');
   return [operator, operands];
 }
 
-/**********/
-/* Scopes */
-/**********/
+/*****************************/
+/* Scope-Extent Combinations */
+/*****************************/
 
-const LEX_SCOPE = 0; // lexical scope
-const DYN_SCOPE = 1; // dynamic scope
+const LEX_SCOPE = 0; // lexical scope and indefinite extent
+const DYN_SCOPE = 1; // indefinite scope and dynamic extent
 
 /**************/
 /* Namespaces */
@@ -1643,7 +1626,7 @@ class GlobalEnv {
         if (value !== null) {
           return value;
         } else {
-          throw new UnboundVariable(variable, 'VALUE');
+          throw new UnboundVariable(variable, 'value');
         }
       }
       case FUN_NS: {
@@ -1651,7 +1634,7 @@ class GlobalEnv {
         if (value !== null) {
           return value;
         } else {
-          throw new UnboundVariable(variable, 'FUNCTION');
+          throw new UnboundVariable(variable, 'function');
         }
       }
       default:
@@ -1685,14 +1668,14 @@ class GlobalEnv {
   }
 }
 
-/*********************/
-/* Local Environment */
-/*********************/
+/************************************/
+/* Lexical and Dynamic Environments */
+/************************************/
 
-class LocalEnv { // abstract class
+class DefiniteEnv { // abstract class
 }
 
-class NullLocalEnv extends LocalEnv {
+class NullDefiniteEnv extends DefiniteEnv {
   constructor() {
     super();
   }
@@ -1708,9 +1691,9 @@ class NullLocalEnv extends LocalEnv {
   }
 }
 
-const nullLocalEnv = new NullLocalEnv();
+const nullDefiniteEnv = new NullDefiniteEnv();
 
-class Frame extends LocalEnv {
+class Frame extends DefiniteEnv {
   constructor(namespace, variables, values, next) {
     super();
     this.namespace = namespace;
@@ -1751,9 +1734,9 @@ class Frame extends LocalEnv {
   }
 }
 
-/**********************************/
-/* Mapping Arguments to Variables */
-/**********************************/
+/*************************************/
+/* Pairing Parameters with Arguments */
+/*************************************/
 
 class TooFewArguments extends EvaluatorError {
   constructor() {
@@ -1769,96 +1752,104 @@ class TooManyArguments extends EvaluatorError {
   }
 }
 
-class MalformedSpreadableArgumentList extends EvaluatorError {
+class MalformedSpreadableSequenceOfObjects extends EvaluatorError {
   constructor() {
-    super('Malformed spreadable argument list.');
-    this.name = 'MalformedSpreadableArgumentList';
+    super('Malformed spreadable sequence of objects.');
+    this.name = 'MalformedSpreadableSequenceOfObjects';
   }
 }
 
-function mapPrimFunArgs(apply, args, arityMin, arityMax) {
+function pairPrimFunParameters(apply, args, arityMin, arityMax) {
   if (!apply) {
-    const nargs = args.length;
-    if (nargs < arityMin) {
-      throw new TooFewArguments();
-    }
-    if (arityMax !== null && nargs > arityMax) {
+    return pairPrimFunParametersNoApply(args, arityMin, arityMax);
+  } else {
+    return pairPrimFunParametersApply(args, arityMin, arityMax);
+  }
+}
+
+function pairPrimFunParametersNoApply(args, arityMin, arityMax) {
+  const nargs = args.length;
+  if (nargs < arityMin) {
+    throw new TooFewArguments();
+  }
+  if (arityMax !== null && nargs > arityMax) {
+    throw new TooManyArguments();
+  }
+  return args;
+}
+
+function pairPrimFunParametersApply(args, arityMin, arityMax) {
+  const nargs = args.length;
+  const spreadArgs = [];
+  let i = 0;
+  while (i < nargs - 1) {
+    if (arityMax === null || i < arityMax) {
+      spreadArgs.push(args[i]);
+      i++;
+    } else {
       throw new TooManyArguments();
     }
-    return args;
-  } else {
-    const nargs = args.length;
-    const spreadArgs = [];
-    let i = 0;
-    while (i < nargs - 1) {
+  }
+  if (nargs === 0 || !(args[nargs - 1] instanceof EVLList)) {
+    throw new MalformedSpreadableSequenceOfObjects();
+  }
+  let argList = args[nargs - 1];
+  while (argList !== EVLEmptyList.NIL) {
+    if (argList instanceof EVLCons) {
       if (arityMax === null || i < arityMax) {
-        spreadArgs.push(args[i]);
+        spreadArgs.push(argList.car);
         i++;
       } else {
         throw new TooManyArguments();
       }
+      argList = argList.cdr;
+    } else {
+      throw new MalformedSpreadableSequenceOfObjects();
     }
-    if (nargs === 0 || !(args[nargs - 1] instanceof EVLList)) {
-      throw new MalformedSpreadableArgumentList();
-    }
-    let argList = args[nargs - 1];
-    while (argList !== EVLEmptyList.NIL) {
-      if (argList instanceof EVLCons) {
-        if (arityMax === null || i < arityMax) {
-          spreadArgs.push(argList.car);
-          i++;
-        } else {
-          throw new TooManyArguments();
-        }
-        argList = argList.cdr;
-      } else {
-        throw new MalformedSpreadableArgumentList();
-      }
-    }
-    if (i < arityMin) {
-      throw new TooFewArguments();
-    }
-    return spreadArgs;
   }
+  if (i < arityMin) {
+    throw new TooFewArguments();
+  }
+  return spreadArgs;
 }
 
-function mapClosureArgs(apply, args, vars, variadic) {
+function pairClosureParameters(apply, args, parameters, rest) {
   if (!apply) {
-    if (!variadic) {
-      return mapClosureArgsForFixedArityCall(args, vars);
+    if (!rest) {
+      return pairClosureParametersNoApplyNoRest(args, parameters);
     } else {
-      return mapClosureArgsForVariableArityCall(args, vars);
+      return pairClosureParametersNoApplyRest(args, parameters);
     }
   } else {
-    if (!variadic) {
-      return mapClosureArgsForFixedArityApply(args, vars);
+    if (!rest) {
+      return pairClosureParametersApplyNoRest(args, parameters);
     } else {
-      return mapClosureArgsForVariableArityApply(args, vars);
+      return pairClosureParametersApplyRest(args, parameters);
     }
   }
 }
 
-function mapClosureArgsForFixedArityCall(args, vars) {
+function pairClosureParametersNoApplyNoRest(args, parameters) {
   const nargs = args.length;
-  const nvars = vars.length;
-  if (nargs < nvars) {
+  const nparameters = parameters.length;
+  if (nargs < nparameters) {
     throw new TooFewArguments();
   }
-  if (nargs > nvars) {
+  if (nargs > nparameters) {
     throw new TooManyArguments();
   }
   return args;
 }
 
-function mapClosureArgsForVariableArityCall(args, vars) {
+function pairClosureParametersNoApplyRest(args, parameters) {
   const nargs = args.length;
-  const nvars = vars.length;
-  const values = new Array(nvars);
+  const nparameters = parameters.length;
+  const values = new Array(nparameters);
   let list = EVLEmptyList.NIL;
   let lastCons = null;
   let i = 0;
   while (i < nargs) {
-    if (i < nvars - 1) {
+    if (i < nparameters - 1) {
       values[i] = args[i];
       i++;
     } else {
@@ -1872,20 +1863,20 @@ function mapClosureArgsForVariableArityCall(args, vars) {
       i++;
     }
   }
-  if (i < nvars - 1) {
+  if (i < nparameters - 1) {
     throw new TooFewArguments();
   }
-  values[nvars - 1] = list;
+  values[nparameters - 1] = list;
   return values;
 }
 
-function mapClosureArgsForFixedArityApply(args, vars) {
+function pairClosureParametersApplyNoRest(args, parameters) {
   const nargs = args.length;
-  const nvars = vars.length;
-  const values = new Array(nvars);
+  const nparameters = parameters.length;
+  const values = new Array(nparameters);
   let i = 0;
   while (i < nargs - 1) {
-    if (i < nvars) {
+    if (i < nparameters) {
       values[i] = args[i];
       i++;
     } else {
@@ -1893,12 +1884,12 @@ function mapClosureArgsForFixedArityApply(args, vars) {
     }
   }
   if (nargs === 0 || !(args[nargs - 1] instanceof EVLList)) {
-    throw new MalformedSpreadableArgumentList();
+    throw new MalformedSpreadableSequenceOfObjects();
   }
   let argList = args[nargs - 1];
   while (argList !== EVLEmptyList.NIL) {
     if (argList instanceof EVLCons) {
-      if (i < nvars) {
+      if (i < nparameters) {
         values[i] = argList.car;
         i++;
       } else {
@@ -1906,24 +1897,24 @@ function mapClosureArgsForFixedArityApply(args, vars) {
       }
       argList = argList.cdr;
     } else {
-      throw new MalformedSpreadableArgumentList();
+      throw new MalformedSpreadableSequenceOfObjects();
     }
   }
-  if (i < nvars) {
+  if (i < nparameters) {
     throw new TooFewArguments();
   }
   return values;
 }
 
-function mapClosureArgsForVariableArityApply(args, vars) {
+function pairClosureParametersApplyRest(args, parameters) {
   const nargs = args.length;
-  const nvars = vars.length;
-  const values = new Array(nvars);
+  const nparameters = parameters.length;
+  const values = new Array(nparameters);
   let list = EVLEmptyList.NIL;
   let lastCons = null;
   let i = 0;
   while (i < nargs - 1) {
-    if (i < nvars - 1) {
+    if (i < nparameters - 1) {
       values[i] = args[i];
       i++;
     } else {
@@ -1938,12 +1929,12 @@ function mapClosureArgsForVariableArityApply(args, vars) {
     }
   }
   if (nargs === 0 || !(args[nargs - 1] instanceof EVLList)) {
-    throw new MalformedSpreadableArgumentList();
+    throw new MalformedSpreadableSequenceOfObjects();
   }
   let argList = args[nargs - 1];
   while (argList !== EVLEmptyList.NIL) {
     if (argList instanceof EVLCons) {
-      if (i < nvars - 1) {
+      if (i < nparameters - 1) {
         values[i] = argList.car;
         i++;
       } else {
@@ -1956,13 +1947,13 @@ function mapClosureArgsForVariableArityApply(args, vars) {
       }
       argList = argList.cdr;
     } else {
-      throw new MalformedSpreadableArgumentList();
+      throw new MalformedSpreadableSequenceOfObjects();
     }
   }
-  if (i < nvars - 1) {
+  if (i < nparameters - 1) {
     throw new TooFewArguments();
   }
-  values[nvars - 1] = list;
+  values[nparameters - 1] = list;
   return values;
 }
 
@@ -1999,7 +1990,7 @@ function genericEval(form) {
 }
 
 function emptyListError() {
-  throw new EvaluatorError('The empty list is not a form.');
+  throw new EvaluatorError('The empty list does not evaluate.');
 }
 
 function ifTestFormError() {
@@ -2007,7 +1998,7 @@ function ifTestFormError() {
 }
 
 function forEachNotImplemented() {
-  throw new EvaluatorError('_for-each is not implemented.');
+  throw new EvaluatorError('The _for-each-form is not implemented.');
 }
 
 function forEachFunctionFormError() {
@@ -2018,7 +2009,7 @@ function forEachListFormError() {
   throw new EvaluatorError('The list-form does not evaluate to a proper list.');
 }
 
-function applicationOperatorFormError() {
+function callOperatorFormError() {
   throw new EvaluatorError('The operator-form does not evaluate to a function.');
 }
 
@@ -2027,7 +2018,7 @@ function applicationOperatorFormError() {
 /*****************************/
 
 function plainrecEval(form) {
-  return plainrecEvalForm(form, nullLocalEnv, nullLocalEnv);
+  return plainrecEvalForm(form, nullDefiniteEnv, nullDefiniteEnv);
 }
 
 function plainrecEvalForm(form, lenv, denv) {
@@ -2066,13 +2057,13 @@ function plainrecEvalForm(form, lenv, denv) {
       case _catchErrorsVariable:
         return plainrecEvalCatchErrors(form, lenv, denv);
       case applyVariable:
-        return plainrecEvalApplication(false, true, form, lenv, denv);
+        return plainrecEvalCall(false, true, form, lenv, denv);
       case multipleValueCallVariable:
-        return plainrecEvalApplication(true, false, form, lenv, denv);
+        return plainrecEvalCall(true, false, form, lenv, denv);
       case multipleValueApplyVariable:
-        return plainrecEvalApplication(true, true, form, lenv, denv);
+        return plainrecEvalCall(true, true, form, lenv, denv);
       default:
-        return plainrecEvalApplication(false, false, form, lenv, denv);
+        return plainrecEvalCall(false, false, form, lenv, denv);
     }
   } else if (form instanceof EVLVariable) {
     return lenv.ref(VAL_NS, form);
@@ -2082,8 +2073,8 @@ function plainrecEvalForm(form, lenv, denv) {
 }
 
 function plainrecEvalQuote(form, lenv, denv) {
-  const [object] = analyzeQuote(form);
-  return object;
+  const [literal] = analyzeQuote(form);
+  return literal;
 }
 
 function plainrecEvalProgn(form, lenv, denv) {
@@ -2116,8 +2107,8 @@ function plainrecEvalIf(form, lenv, denv) {
 }
 
 function plainrecEvalLambda(scope, namespace, macro, form, lenv, denv) {
-  const [variables, variadic, forms] = analyzeLambda(form);
-  return new EVLClosure(scope, namespace, macro, variables, variadic, forms, lenv);
+  const [parameters, rest, forms] = analyzeLambda(form);
+  return new EVLClosure(scope, namespace, macro, parameters, rest, forms, lenv);
 }
 
 function plainrecEvalRef(scope, namespace, form, lenv, denv) {
@@ -2155,8 +2146,8 @@ function plainrecEvalCatchErrors(form, lenv, denv) {
   return EVLVoid.VOID;
 }
 
-function plainrecEvalApplication(mv, apply, form, lenv, denv) {
-  const [operator, operands] = analyzeApplication(mv, apply, form);
+function plainrecEvalCall(mv, apply, form, lenv, denv) {
+  const [operator, operands] = analyzeCall(mv, apply, form);
   const fn = plainrecEvalOperator(operator, lenv, denv).primaryValue();
   const macro = operator instanceof EVLVariable && fn instanceof EVLClosure && fn.macro;
   const args = plainrecEvalOperands(mv, macro, operands, [], lenv, denv);
@@ -2192,13 +2183,13 @@ function plainrecEvalOperands(mv, macro, operands, args, lenv, denv) {
 
 function plainrecInvokeFun(apply, macro, fn, args, lenv, denv) {
   if (fn instanceof EVLPrimitiveFunction) {
-    const values = mapPrimFunArgs(apply, args, fn.arityMin, fn.arityMax);
+    const values = pairPrimFunParameters(apply, args, fn.arityMin, fn.arityMax);
     return fn.jsFunction(values);
   } else if (fn instanceof EVLClosure) {
-    const values = mapClosureArgs(apply, args, fn.variables, fn.variadic);
+    const values = pairClosureParameters(apply, args, fn.parameters, fn.rest);
     switch (fn.scope) {
       case LEX_SCOPE:
-        const elenv = new Frame(fn.namespace, fn.variables, values, fn.lenv);
+        const elenv = new Frame(fn.namespace, fn.parameters, values, fn.lenv);
         if (macro) {
           const expansion = plainrecEvalForms(fn.forms, elenv, denv).primaryValue();
           return plainrecEvalForm(expansion, lenv, denv);
@@ -2206,13 +2197,13 @@ function plainrecInvokeFun(apply, macro, fn, args, lenv, denv) {
           return plainrecEvalForms(fn.forms, elenv, denv);
         }
       case DYN_SCOPE:
-        const edenv = new Frame(fn.namespace, fn.variables, values, denv);
+        const edenv = new Frame(fn.namespace, fn.parameters, values, denv);
         return plainrecEvalForms(fn.forms, fn.lenv, edenv);
       default:
         throw new CannotHappen('plainrecInvokeFun');
     }
   } else {
-    applicationOperatorFormError();
+    callOperatorFormError();
   }
 }
 
@@ -2221,7 +2212,7 @@ function plainrecInvokeFun(apply, macro, fn, args, lenv, denv) {
 /****************************************/
 
 function cpsEval(form) {
-  return cpsEvalForm(form, nullLocalEnv, nullLocalEnv, cpsEndCont);
+  return cpsEvalForm(form, nullDefiniteEnv, nullDefiniteEnv, cpsEndCont);
 }
 
 function cpsEvalForm(form, lenv, denv, k) {
@@ -2260,13 +2251,13 @@ function cpsEvalForm(form, lenv, denv, k) {
       case _catchErrorsVariable:
         return cpsEvalCatchErrors(form, lenv, denv, k);
       case applyVariable:
-        return cpsEvalApplication(false, true, form, lenv, denv, k);
+        return cpsEvalCall(false, true, form, lenv, denv, k);
       case multipleValueCallVariable:
-        return cpsEvalApplication(true, false, form, lenv, denv, k);
+        return cpsEvalCall(true, false, form, lenv, denv, k);
       case multipleValueApplyVariable:
-        return cpsEvalApplication(true, true, form, lenv, denv, k);
+        return cpsEvalCall(true, true, form, lenv, denv, k);
       default:
-        return cpsEvalApplication(false, false, form, lenv, denv, k);
+        return cpsEvalCall(false, false, form, lenv, denv, k);
     }
   } else if (form instanceof EVLVariable) {
     return k(lenv.ref(VAL_NS, form));
@@ -2278,8 +2269,8 @@ function cpsEvalForm(form, lenv, denv, k) {
 const cpsEndCont = result => result;
 
 function cpsEvalQuote(form, lenv, denv, k) {
-  const [object] = analyzeQuote(form);
-  return k(object);
+  const [literal] = analyzeQuote(form);
+  return k(literal);
 }
 
 function cpsEvalProgn(form, lenv, denv, k) {
@@ -2321,8 +2312,8 @@ function cpsEvalIf(form, lenv, denv, k) {
 }
 
 function cpsEvalLambda(scope, namespace, macro, form, lenv, denv, k) {
-  const [variables, variadic, forms] = analyzeLambda(form);
-  return k(new EVLClosure(scope, namespace, macro, variables, variadic, forms, lenv));
+  const [parameters, rest, forms] = analyzeLambda(form);
+  return k(new EVLClosure(scope, namespace, macro, parameters, rest, forms, lenv));
 }
 
 function cpsEvalRef(scope, namespace, form, lenv, denv, k) {
@@ -2393,8 +2384,8 @@ function cpsEvalCatchErrors(form, lenv, denv, k) {
   return k(EVLVoid.VOID);
 }
 
-function cpsEvalApplication(mv, apply, form, lenv, denv, k) {
-  const [operator, operands] = analyzeApplication(mv, apply, form);
+function cpsEvalCall(mv, apply, form, lenv, denv, k) {
+  const [operator, operands] = analyzeCall(mv, apply, form);
   return cpsEvalOperator(
     operator, lenv, denv,
     result => { // OperatorCont
@@ -2443,13 +2434,13 @@ function cpsEvalOperands(mv, macro, operands, args, lenv, denv, k) {
 
 function cpsInvokeFun(apply, macro, fn, args, lenv, denv, k) {
   if (fn instanceof EVLPrimitiveFunction) {
-    const values = mapPrimFunArgs(apply, args, fn.arityMin, fn.arityMax);
+    const values = pairPrimFunParameters(apply, args, fn.arityMin, fn.arityMax);
     return k(fn.jsFunction(values));
   } else if (fn instanceof EVLClosure) {
-    const values = mapClosureArgs(apply, args, fn.variables, fn.variadic);
+    const values = pairClosureParameters(apply, args, fn.parameters, fn.rest);
     switch (fn.scope) {
       case LEX_SCOPE:
-        const elenv = new Frame(fn.namespace, fn.variables, values, fn.lenv);
+        const elenv = new Frame(fn.namespace, fn.parameters, values, fn.lenv);
         if (macro) {
           const expansion = cpsEvalForms(fn.forms, elenv, denv, cpsEndCont).primaryValue();
           return cpsEvalForm(expansion, lenv, denv, k);
@@ -2457,13 +2448,13 @@ function cpsInvokeFun(apply, macro, fn, args, lenv, denv, k) {
           return cpsEvalForms(fn.forms, elenv, denv, k);
         }
       case DYN_SCOPE:
-        const edenv = new Frame(fn.namespace, fn.variables, values, denv);
+        const edenv = new Frame(fn.namespace, fn.parameters, values, denv);
         return cpsEvalForms(fn.forms, fn.lenv, edenv, k);
       default:
         throw new CannotHappen('cpsInvokeFun');
     }
   } else {
-    applicationOperatorFormError();
+    callOperatorFormError();
   }
 }
 
@@ -2472,7 +2463,7 @@ function cpsInvokeFun(apply, macro, fn, args, lenv, denv, k) {
 /*********************************/
 
 function oocpsEval(form) {
-  return oocpsEvalForm(form, nullLocalEnv, nullLocalEnv, oocpsEndCont);
+  return oocpsEvalForm(form, nullDefiniteEnv, nullDefiniteEnv, oocpsEndCont);
 }
 
 function oocpsEvalForm(form, lenv, denv, k) {
@@ -2511,13 +2502,13 @@ function oocpsEvalForm(form, lenv, denv, k) {
       case _catchErrorsVariable:
         return oocpsEvalCatchErrors(form, lenv, denv, k);
       case applyVariable:
-        return oocpsEvalApplication(false, true, form, lenv, denv, k);
+        return oocpsEvalCall(false, true, form, lenv, denv, k);
       case multipleValueCallVariable:
-        return oocpsEvalApplication(true, false, form, lenv, denv, k);
+        return oocpsEvalCall(true, false, form, lenv, denv, k);
       case multipleValueApplyVariable:
-        return oocpsEvalApplication(true, true, form, lenv, denv, k);
+        return oocpsEvalCall(true, true, form, lenv, denv, k);
       default:
-        return oocpsEvalApplication(false, false, form, lenv, denv, k);
+        return oocpsEvalCall(false, false, form, lenv, denv, k);
     }
   } else if (form instanceof EVLVariable) {
     return k.invoke(lenv.ref(VAL_NS, form));
@@ -2546,8 +2537,8 @@ class OOCPSEndCont extends OOCPSCont {
 const oocpsEndCont = new OOCPSEndCont();
 
 function oocpsEvalQuote(form, lenv, denv, k) {
-  const [object] = analyzeQuote(form);
-  return k.invoke(object);
+  const [literal] = analyzeQuote(form);
+  return k.invoke(literal);
 }
 
 function oocpsEvalProgn(form, lenv, denv, k) {
@@ -2608,8 +2599,8 @@ class OOCPSIfTestFormCont extends OOCPSCont {
 }
 
 function oocpsEvalLambda(scope, namespace, macro, form, lenv, denv, k) {
-  const [variables, variadic, forms] = analyzeLambda(form);
-  return k.invoke(new EVLClosure(scope, namespace, macro, variables, variadic, forms, lenv));
+  const [parameters, rest, forms] = analyzeLambda(form);
+  return k.invoke(new EVLClosure(scope, namespace, macro, parameters, rest, forms, lenv));
 }
 
 function oocpsEvalRef(scope, namespace, form, lenv, denv, k) {
@@ -2709,8 +2700,8 @@ function oocpsEvalCatchErrors(form, lenv, denv, k) {
   return k.invoke(EVLVoid.VOID);
 }
 
-function oocpsEvalApplication(mv, apply, form, lenv, denv, k) {
-  const [operator, operands] = analyzeApplication(mv, apply, form);
+function oocpsEvalCall(mv, apply, form, lenv, denv, k) {
+  const [operator, operands] = analyzeCall(mv, apply, form);
   return oocpsEvalOperator(
     operator, lenv, denv,
     new OOCPSOperatorCont(mv, apply, operator, operands, lenv, denv, k)
@@ -2794,13 +2785,13 @@ class OOCPSOperandsCont extends OOCPSCont {
 
 function oocpsInvokeFun(apply, macro, fn, args, lenv, denv, k) {
   if (fn instanceof EVLPrimitiveFunction) {
-    const values = mapPrimFunArgs(apply, args, fn.arityMin, fn.arityMax);
+    const values = pairPrimFunParameters(apply, args, fn.arityMin, fn.arityMax);
     return k.invoke(fn.jsFunction(values));
   } else if (fn instanceof EVLClosure) {
-    const values = mapClosureArgs(apply, args, fn.variables, fn.variadic);
+    const values = pairClosureParameters(apply, args, fn.parameters, fn.rest);
     switch (fn.scope) {
       case LEX_SCOPE:
-        const elenv = new Frame(fn.namespace, fn.variables, values, fn.lenv);
+        const elenv = new Frame(fn.namespace, fn.parameters, values, fn.lenv);
         if (macro) {
           const expansion = oocpsEvalForms(fn.forms, elenv, denv, oocpsEndCont).primaryValue();
           return oocpsEvalForm(expansion, lenv, denv, k);
@@ -2808,13 +2799,13 @@ function oocpsInvokeFun(apply, macro, fn, args, lenv, denv, k) {
           return oocpsEvalForms(fn.forms, elenv, denv, k);
         }
       case DYN_SCOPE:
-        const edenv = new Frame(fn.namespace, fn.variables, values, denv);
+        const edenv = new Frame(fn.namespace, fn.parameters, values, denv);
         return oocpsEvalForms(fn.forms, fn.lenv, edenv, k);
       default:
         throw new CannotHappen('oocpsInvokeFun');
     }
   } else {
-    applicationOperatorFormError();
+    callOperatorFormError();
   }
 }
 
@@ -2825,7 +2816,7 @@ function oocpsInvokeFun(apply, macro, fn, args, lenv, denv, k) {
 function sboocpsEval(form) {
   const kStack = new SBOOCPSControlStack();
   kStack.push(sboocpsEndCont);
-  return sboocpsEvalForm(form, nullLocalEnv, kStack);
+  return sboocpsEvalForm(form, nullDefiniteEnv, kStack);
 }
 
 class SBOOCPSControlStack {
@@ -2913,13 +2904,13 @@ function sboocpsEvalForm(form, lenv, kStack) {
       case _catchErrorsVariable:
         return sboocpsEvalCatchErrors(form, lenv, kStack);
       case applyVariable:
-        return sboocpsEvalApplication(false, true, form, lenv, kStack);
+        return sboocpsEvalCall(false, true, form, lenv, kStack);
       case multipleValueCallVariable:
-        return sboocpsEvalApplication(true, false, form, lenv, kStack);
+        return sboocpsEvalCall(true, false, form, lenv, kStack);
       case multipleValueApplyVariable:
-        return sboocpsEvalApplication(true, true, form, lenv, kStack);
+        return sboocpsEvalCall(true, true, form, lenv, kStack);
       default:
-        return sboocpsEvalApplication(false, false, form, lenv, kStack);
+        return sboocpsEvalCall(false, false, form, lenv, kStack);
     }
   } else if (form instanceof EVLVariable) {
     return kStack.invokeCont(lenv.ref(VAL_NS, form));
@@ -2947,8 +2938,8 @@ class SBOOCPSEndCont extends SBOOCPSCont {
 const sboocpsEndCont = new SBOOCPSEndCont();
 
 function sboocpsEvalQuote(form, lenv, kStack) {
-  const [object] = analyzeQuote(form);
-  return kStack.invokeCont(object);
+  const [literal] = analyzeQuote(form);
+  return kStack.invokeCont(literal);
 }
 
 function sboocpsEvalProgn(form, lenv, kStack) {
@@ -3005,8 +2996,8 @@ class SBOOCPSIfTestFormCont extends SBOOCPSCont {
 }
 
 function sboocpsEvalLambda(scope, namespace, macro, form, lenv, kStack) {
-  const [variables, variadic, forms] = analyzeLambda(form);
-  return kStack.invokeCont(new EVLClosure(scope, namespace, macro, variables, variadic, forms, lenv));
+  const [parameters, rest, forms] = analyzeLambda(form);
+  return kStack.invokeCont(new EVLClosure(scope, namespace, macro, parameters, rest, forms, lenv));
 }
 
 function sboocpsEvalRef(scope, namespace, form, lenv, kStack) {
@@ -3104,8 +3095,8 @@ function sboocpsEvalCatchErrors(form, lenv, kStack) {
   return kStack.invokeCont(EVLVoid.VOID);
 }
 
-function sboocpsEvalApplication(mv, apply, form, lenv, kStack) {
-  const [operator, operands] = analyzeApplication(mv, apply, form);
+function sboocpsEvalCall(mv, apply, form, lenv, kStack) {
+  const [operator, operands] = analyzeCall(mv, apply, form);
   kStack.push(new SBOOCPSOperatorCont(mv, apply, operator, operands, lenv, kStack));
   return sboocpsEvalOperator(operator, lenv, kStack);
 }
@@ -3183,13 +3174,13 @@ class SBOOCPSOperandsCont extends SBOOCPSCont {
 
 function sboocpsInvokeFun(apply, macro, fn, args, lenv, kStack) {
   if (fn instanceof EVLPrimitiveFunction) {
-    const values = mapPrimFunArgs(apply, args, fn.arityMin, fn.arityMax);
+    const values = pairPrimFunParameters(apply, args, fn.arityMin, fn.arityMax);
     return kStack.invokeCont(fn.jsFunction(values));
   } else if (fn instanceof EVLClosure) {
-    const values = mapClosureArgs(apply, args, fn.variables, fn.variadic);
+    const values = pairClosureParameters(apply, args, fn.parameters, fn.rest);
     switch (fn.scope) {
       case LEX_SCOPE:
-        const elenv = new Frame(fn.namespace, fn.variables, values, fn.lenv);
+        const elenv = new Frame(fn.namespace, fn.parameters, values, fn.lenv);
         if (macro) {
           kStack.push(sboocpsEndCont);
           const expansion = sboocpsEvalForms(fn.forms, elenv, kStack).primaryValue();
@@ -3198,13 +3189,13 @@ function sboocpsInvokeFun(apply, macro, fn, args, lenv, kStack) {
           return sboocpsEvalForms(fn.forms, elenv, kStack);
         }
       case DYN_SCOPE:
-        kStack.push(new Frame(fn.namespace, fn.variables, values, undefined));
+        kStack.push(new Frame(fn.namespace, fn.parameters, values, undefined));
         return sboocpsEvalForms(fn.forms, fn.lenv, kStack);
       default:
         throw new CannotHappen('sboocpsInvokeFun');
     }
   } else {
-    applicationOperatorFormError();
+    callOperatorFormError();
   }
 }
 
@@ -3215,9 +3206,9 @@ function sboocpsInvokeFun(apply, macro, fn, args, lenv, kStack) {
 function trampolineEval(form) {
   const kStack = new TrampolineControlStack();
   kStack.push(trampolineEndCont);
-  let bounce = new EvalReq(form, nullLocalEnv);
+  let bounce = new EvalReq(form, nullDefiniteEnv);
   while (true) {
-    if (signalArray[0] === 1) {
+    if (abortSignalArray !== null && abortSignalArray[0] === 1) {
       throw new Aborted();
     }
     if (bounce instanceof EvalReq) {
@@ -3328,13 +3319,13 @@ function trampolineEvalForm(form, lenv, kStack) {
       case _catchErrorsVariable:
         return trampolineEvalCatchErrors(form, lenv, kStack);
       case applyVariable:
-        return trampolineEvalApplication(false, true, form, lenv, kStack);
+        return trampolineEvalCall(false, true, form, lenv, kStack);
       case multipleValueCallVariable:
-        return trampolineEvalApplication(true, false, form, lenv, kStack);
+        return trampolineEvalCall(true, false, form, lenv, kStack);
       case multipleValueApplyVariable:
-        return trampolineEvalApplication(true, true, form, lenv, kStack);
+        return trampolineEvalCall(true, true, form, lenv, kStack);
       default:
-        return trampolineEvalApplication(false, false, form, lenv, kStack);
+        return trampolineEvalCall(false, false, form, lenv, kStack);
     }
   } else if (form instanceof EVLVariable) {
     return lenv.ref(VAL_NS, form);
@@ -3359,8 +3350,8 @@ class TrampolineEndCont extends TrampolineCont {
 const trampolineEndCont = new TrampolineEndCont();
 
 function trampolineEvalQuote(form, lenv, kStack) {
-  const [object] = analyzeQuote(form);
-  return object;
+  const [literal] = analyzeQuote(form);
+  return literal;
 }
 
 function trampolineEvalProgn(form, lenv, kStack) {
@@ -3417,8 +3408,8 @@ class TrampolineIfTestFormCont extends TrampolineCont {
 }
 
 function trampolineEvalLambda(scope, namespace, macro, form, lenv, kStack) {
-  const [variables, variadic, forms] = analyzeLambda(form);
-  return new EVLClosure(scope, namespace, macro, variables, variadic, forms, lenv);
+  const [parameters, rest, forms] = analyzeLambda(form);
+  return new EVLClosure(scope, namespace, macro, parameters, rest, forms, lenv);
 }
 
 function trampolineEvalRef(scope, namespace, form, lenv, kStack) {
@@ -3480,8 +3471,8 @@ class TrampolineCatchErrorsTryFormCont extends TrampolineCont {
   }
 }
 
-function trampolineEvalApplication(mv, apply, form, lenv, kStack) {
-  const [operator, operands] = analyzeApplication(mv, apply, form);
+function trampolineEvalCall(mv, apply, form, lenv, kStack) {
+  const [operator, operands] = analyzeCall(mv, apply, form);
   kStack.push(new TrampolineOperatorCont(mv, apply, operator, operands, lenv, kStack));
   return trampolineEvalOperator(operator, lenv, kStack);
 }
@@ -3559,25 +3550,25 @@ class TrampolineOperandsCont extends TrampolineCont {
 
 function trampolineInvokeFun(apply, macro, fn, args, lenv, kStack) {
   if (fn instanceof EVLPrimitiveFunction) {
-    const values = mapPrimFunArgs(apply, args, fn.arityMin, fn.arityMax);
+    const values = pairPrimFunParameters(apply, args, fn.arityMin, fn.arityMax);
     return fn.jsFunction(values);
   } else if (fn instanceof EVLClosure) {
-    const values = mapClosureArgs(apply, args, fn.variables, fn.variadic);
+    const values = pairClosureParameters(apply, args, fn.parameters, fn.rest);
     switch (fn.scope) {
       case LEX_SCOPE:
-        const elenv = new Frame(fn.namespace, fn.variables, values, fn.lenv);
+        const elenv = new Frame(fn.namespace, fn.parameters, values, fn.lenv);
         if (macro) {
           kStack.push(new TrampolineMacroCont(lenv, kStack));
         }
         return trampolineEvalForms(fn.forms, elenv, kStack);
       case DYN_SCOPE:
-        kStack.push(new Frame(fn.namespace, fn.variables, values, undefined));
+        kStack.push(new Frame(fn.namespace, fn.parameters, values, undefined));
         return trampolineEvalForms(fn.forms, fn.lenv, kStack);
       default:
         throw new CannotHappen('trampolineInvokeFun');
     }
   } else {
-    applicationOperatorFormError();
+    callOperatorFormError();
   }
 }
 
@@ -3598,14 +3589,14 @@ class TrampolineMacroCont extends TrampolineCont {
 
 function trampolineppEval(form, lenv = null) {
   if (lenv === null) {
-    form = trampolineppPreprocessForm(form, nullLocalEnv);
-    lenv = nullLocalEnv;
+    form = trampolineppPreprocessForm(form, nullDefiniteEnv);
+    lenv = nullDefiniteEnv;
   }
   const kStack = new TrampolineppControlStack();
   kStack.push(trampolineppEndCont);
   let bounce = new EvalReq(form, lenv);
   while (true) {
-    if (signalArray[0] === 1) {
+    if (abortSignalArray !== null && abortSignalArray[0] === 1) {
       throw new Aborted();
     }
     if (bounce instanceof EvalReq) {
@@ -3716,13 +3707,13 @@ function trampolineppPreprocessForm(form, lenv) {
       case _catchErrorsVariable:
         return trampolineppPreprocessCatchErrors(form, lenv);
       case applyVariable:
-        return trampolineppPreprocessApplication(false, true, form, lenv);
+        return trampolineppPreprocessCall(false, true, form, lenv);
       case multipleValueCallVariable:
-        return trampolineppPreprocessApplication(true, false, form, lenv);
+        return trampolineppPreprocessCall(true, false, form, lenv);
       case multipleValueApplyVariable:
-        return trampolineppPreprocessApplication(true, true, form, lenv);
+        return trampolineppPreprocessCall(true, true, form, lenv);
       default:
-        return trampolineppPreprocessApplication(false, false, form, lenv);
+        return trampolineppPreprocessCall(false, false, form, lenv);
     }
   } else if (form instanceof EVLVariable) {
     return trampolineppPreprocessRef2(LEX_SCOPE, VAL_NS, form, lenv);
@@ -3761,18 +3752,18 @@ class TrampolineppForm { // abstract class
 }
 
 function trampolineppPreprocessQuote(form, lenv) {
-  const [object] = analyzeQuote(form);
-  return new TrampolineppQuote(object);
+  const [literal] = analyzeQuote(form);
+  return new TrampolineppQuote(literal);
 }
 
 class TrampolineppQuote extends TrampolineppForm {
-  constructor(object) {
+  constructor(literal) {
     super();
-    this.object = object;
+    this.literal = literal;
   }
   eval(lenv, kStack) {
-    const {object} = this;
-    return object;
+    const {literal} = this;
+    return literal;
   }
 }
 
@@ -3858,16 +3849,16 @@ class TrampolineppIfTestFormCont extends TrampolineppCont {
 }
 
 function trampolineppPreprocessLambda(scope, namespace, macro, form, lenv) {
-  const [variables, variadic, forms] = analyzeLambda(form);
+  const [parameters, rest, forms] = analyzeLambda(form);
   switch (scope) {
     case LEX_SCOPE: {
-      const elenv = new Frame(namespace, variables, new Array(variables.length).fill(null), lenv);
+      const elenv = new Frame(namespace, parameters, new Array(parameters.length).fill(null), lenv);
       const preprocessedForms = trampolineppPreprocessForms(forms, elenv);
-      return new TrampolineppLambda(scope, namespace, macro, variables, variadic, preprocessedForms);
+      return new TrampolineppLambda(scope, namespace, macro, parameters, rest, preprocessedForms);
     }
     case DYN_SCOPE: {
       const preprocessedForms = trampolineppPreprocessForms(forms, lenv);
-      return new TrampolineppLambda(scope, namespace, macro, variables, variadic, preprocessedForms);
+      return new TrampolineppLambda(scope, namespace, macro, parameters, rest, preprocessedForms);
     }
     default:
       throw new CannotHappen('trampolineppPreprocessLambda');
@@ -3875,18 +3866,18 @@ function trampolineppPreprocessLambda(scope, namespace, macro, form, lenv) {
 }
 
 class TrampolineppLambda extends TrampolineppForm {
-  constructor(scope, namespace, macro, variables, variadic, forms) {
+  constructor(scope, namespace, macro, parameters, rest, forms) {
     super();
     this.scope = scope;
     this.namespace = namespace;
     this.macro = macro;
-    this.variables = variables;
-    this.variadic = variadic;
+    this.parameters = parameters;
+    this.rest = rest;
     this.forms = forms;
   }
   eval(lenv, kStack) {
-    const {scope, namespace, macro, variables, variadic, forms} = this;
-    return new EVLClosure(scope, namespace, macro, variables, variadic, forms, lenv);
+    const {scope, namespace, macro, parameters, rest, forms} = this;
+    return new EVLClosure(scope, namespace, macro, parameters, rest, forms, lenv);
   }
 }
 
@@ -4174,32 +4165,32 @@ class TrampolineppCatchErrorsTryFormCont extends TrampolineppCont {
   }
 }
 
-function trampolineppPreprocessApplication(mv, apply, form, lenv) {
-  const [operator, operands] = analyzeApplication(mv, apply, form);
+function trampolineppPreprocessCall(mv, apply, form, lenv) {
+  const [operator, operands] = analyzeCall(mv, apply, form);
   if (operator instanceof EVLVariable) {
     const [i, j, fn] = lenv.preprocessorRef(FUN_NS, operator, 0);
     if (fn instanceof EVLClosure && fn.macro) {
-      const values = mapClosureArgs(false, listToArray(operands), fn.variables, fn.variadic);
-      const elenv = new Frame(fn.namespace, fn.variables, values, fn.lenv);
+      const values = pairClosureParameters(false, listToArray(operands), fn.parameters, fn.rest);
+      const elenv = new Frame(fn.namespace, fn.parameters, values, fn.lenv);
       const expansion = trampolineppEval(new TrampolineppProgn(fn.forms), elenv).primaryValue();
       return trampolineppPreprocessForm(expansion, lenv);
     } else {
       const preprocessedOperator = trampolineppPreprocessRef2(LEX_SCOPE, FUN_NS, operator, lenv);
       const preprocessedOperands = trampolineppPreprocessForms(operands, lenv);
-      return new TrampolineppApplication(mv, apply, preprocessedOperator, preprocessedOperands);
+      return new TrampolineppCall(mv, apply, preprocessedOperator, preprocessedOperands);
     }
   } else if (isMacroLet(operator, operands)) {
     const preprocessedOperands = trampolineppPreprocessForms(operands, lenv);
-    const [variables, variadic, forms] = analyzeLambda(operator);
-    const values = listToArray(preprocessedOperands).map(preprocessedOperand => preprocessedOperand.eval(nullLocalEnv, null));
-    const elenv = new Frame(FUN_NS, variables, values, lenv);
+    const [parameters, rest, forms] = analyzeLambda(operator);
+    const values = listToArray(preprocessedOperands).map(preprocessedOperand => preprocessedOperand.eval(nullDefiniteEnv, null));
+    const elenv = new Frame(FUN_NS, parameters, values, lenv);
     const preprocessedForms = trampolineppPreprocessForms(forms, elenv);
-    const preprocessedOperator = new TrampolineppLambda(LEX_SCOPE, FUN_NS, false, variables, variadic, preprocessedForms);
-    return new TrampolineppApplication(mv, apply, preprocessedOperator, preprocessedOperands);
+    const preprocessedOperator = new TrampolineppLambda(LEX_SCOPE, FUN_NS, false, parameters, rest, preprocessedForms);
+    return new TrampolineppCall(mv, apply, preprocessedOperator, preprocessedOperands);
   } else {
     const preprocessedOperator = trampolineppPreprocessForm(operator, lenv);
     const preprocessedOperands = trampolineppPreprocessForms(operands, lenv);
-    return new TrampolineppApplication(mv, apply, preprocessedOperator, preprocessedOperands);
+    return new TrampolineppCall(mv, apply, preprocessedOperator, preprocessedOperands);
   }
 }
 
@@ -4215,7 +4206,7 @@ function isMacroLet(operator, operands) {
   return true;
 }
 
-class TrampolineppApplication extends TrampolineppForm {
+class TrampolineppCall extends TrampolineppForm {
   constructor(mv, apply, operator, operands) {
     super();
     this.mv = mv;
@@ -4290,28 +4281,28 @@ class TrampolineppOperandsCont extends TrampolineppCont {
 
 function trampolineppInvokeFun(apply, fn, args, lenv, kStack) {
   if (fn instanceof EVLPrimitiveFunction) {
-    const values = mapPrimFunArgs(apply, args, fn.arityMin, fn.arityMax);
+    const values = pairPrimFunParameters(apply, args, fn.arityMin, fn.arityMax);
     return fn.jsFunction(values);
   } else if (fn instanceof EVLClosure) {
-    const values = mapClosureArgs(apply, args, fn.variables, fn.variadic);
+    const values = pairClosureParameters(apply, args, fn.parameters, fn.rest);
     switch (fn.scope) {
       case LEX_SCOPE:
-        const elenv = new Frame(fn.namespace, fn.variables, values, fn.lenv);
+        const elenv = new Frame(fn.namespace, fn.parameters, values, fn.lenv);
         return trampolineppEvalForms(fn.forms, elenv, kStack);
       case DYN_SCOPE:
-        kStack.push(new Frame(fn.namespace, fn.variables, values, undefined));
+        kStack.push(new Frame(fn.namespace, fn.parameters, values, undefined));
         return trampolineppEvalForms(fn.forms, fn.lenv, kStack);
       default:
         throw new CannotHappen('trampolineppInvokeFun');
     }
   } else {
-    applicationOperatorFormError();
+    callOperatorFormError();
   }
 }
 
-/**********************************/
-/* Primitive Function Definer (1) */
-/**********************************/
+/**************************************/
+/* Primitive Function Definitions (1) */
+/**************************************/
 
 const primitiveFunctions = new Map();
 
@@ -4425,6 +4416,7 @@ class EVLVoid extends EVLObject {
   }
 }
 
+// the only object of type void
 EVLVoid.VOID = new EVLVoid();
 
 function nullToVoid(x) {
@@ -4442,14 +4434,16 @@ primitiveFunction('void?', 1, 1, function(args) {
 class EVLBoolean extends EVLObject {
   constructor(jsValue) {
     super();
-    this.jsValue = jsValue; // javascript boolean
+    this.jsValue = jsValue; // JavaScript boolean
   }
   toString() {
     return this.jsValue ? '#t' : '#f';
   }
 }
 
+// the only object of type boolean representing true
 EVLBoolean.TRUE = new EVLBoolean(true);
+// the only object of type boolean representing false
 EVLBoolean.FALSE = new EVLBoolean(false);
 
 function evlBoolean(jsBoolean) {
@@ -4467,7 +4461,7 @@ primitiveFunction('boolean?', 1, 1, function(args) {
 class EVLNumber extends EVLObject {
   constructor(jsValue) {
     super();
-    this.jsValue = jsValue; // javascript number
+    this.jsValue = jsValue; // JavaScript number
   }
   eql(that) {
     if (that instanceof EVLNumber) {
@@ -4558,7 +4552,7 @@ primitiveFunction('>=', 2, 2, function(args) {
 class EVLCharacter extends EVLObject {
   constructor(jsValue) {
     super();
-    this.jsValue = jsValue; // javascript string of one UTF-16 code unit
+    this.jsValue = jsValue; // JavaScript string of one UTF-16 code unit
   }
   eql(that) {
     if (that instanceof EVLCharacter) {
@@ -4583,7 +4577,7 @@ primitiveFunction('character?', 1, 1, function(args) {
 class EVLString extends EVLObject {
   constructor(jsValue) {
     super();
-    this.jsValue = jsValue; // javascript string of zero or more UTF-16 code units
+    this.jsValue = jsValue; // JavaScript string
   }
   eql(that) {
     if (that instanceof EVLString) {
@@ -4608,7 +4602,7 @@ primitiveFunction('string?', 1, 1, function(args) {
 class EVLSymbol extends EVLObject { // abstract class
   constructor(name) {
     super();
-    this.name = name; // javascipt string of zero or more UTF-16 code units
+    this.name = name; // JavaScript string
   }
 }
 
@@ -4655,8 +4649,8 @@ primitiveFunction('make-keyword', 1, 1, function(args) {
 class EVLVariable extends EVLSymbol {
   constructor(name) {
     super(name);
-    this.value = null;
-    this.function = null;
+    this.value = null; // EVLObject or null
+    this.function = null; // EVLObject or null
   }
   toString() {
     return escapeCharacters(this.name, escapeProtoTokenCharacter);
@@ -4684,7 +4678,7 @@ const prognVariable = internVariable('progn');
 const ifVariable = internVariable('if');
 const _vlambdaVariable = internVariable('_vlambda');
 const _mlambdaVariable = internVariable('_mlambda');
-const mlambdaVariable = internVariable('mlambda');
+const mlambdaVariable = internVariable('mlambda'); // mlet
 const _flambdaVariable = internVariable('_flambda');
 const _dlambdaVariable = internVariable('_dlambda');
 const vrefVariable = internVariable('vref');
@@ -4794,6 +4788,7 @@ class EVLEmptyList extends EVLList {
   }
 }
 
+// the only object of type empty-list
 EVLEmptyList.NIL = new EVLEmptyList();
 
 primitiveFunction('empty-list?', 1, 1, function(args) {
@@ -4847,7 +4842,7 @@ primitiveFunction('set-cdr!', 2, 2, function(args) {
 class EVLVector extends EVLObject {
   constructor(elements) {
     super();
-    this.elements = elements; // javascript array of EVLObject or null elements
+    this.elements = elements; // JavaScript array of EVLObject's and/or null's
   }
   toString() {
     let string = '';
@@ -4893,7 +4888,7 @@ class EVLPrimitiveFunction extends EVLFunction {
     super();
     this.arityMin = arityMin;
     this.arityMax = arityMax;
-    this.jsFunction = jsFunction; // javascript function
+    this.jsFunction = jsFunction; // JavaScript function
   }
   toString() {
     return '#<primitive-function>';
@@ -4909,13 +4904,13 @@ primitiveFunction('primitive-function?', 1, 1, function(args) {
 /**************/
 
 class EVLClosure extends EVLFunction {
-  constructor(scope, namespace, macro, variables, variadic, forms, lenv) {
+  constructor(scope, namespace, macro, parameters, rest, forms, lenv) {
     super();
     this.scope = scope;
     this.namespace = namespace;
     this.macro = macro;
-    this.variables = variables;
-    this.variadic = variadic;
+    this.parameters = parameters;
+    this.rest = rest;
     this.forms = forms;
     this.lenv = lenv;
   }
@@ -4928,9 +4923,9 @@ primitiveFunction('closure?', 1, 1, function(args) {
   return evlBoolean(args[0] instanceof EVLClosure);
 });
 
-/*****************************/
-/* Other Primitive Functions */
-/*****************************/
+/*************************************/
+/* Miscellaneous Primitive Functions */
+/*************************************/
 
 primitiveFunction('values', 0, null, function(args) {
   return new EVLObjects(args);
@@ -4945,29 +4940,40 @@ primitiveFunction('now', 0, 0, function(args) {
   return new EVLNumber(Date.now());
 });
 
-/**********************************/
-/* Primitive Function Definer (2) */
-/**********************************/
+/**************************************/
+/* Primitive Function Definitions (2) */
+/**************************************/
 
 for (const [name, [arityMin, arityMax, jsFunction]] of primitiveFunctions) {
   GlobalEnv.set(FUN_NS, internVariable(name), new EVLPrimitiveFunction(arityMin, arityMax, jsFunction));
 }
 
-/********/
-/* Node */
-/********/
+/****************************/
+/* Interface (Command Line) */
+/****************************/
 
-if (typeof onmessage === 'undefined') { // node
+const evaluatorOptions = [
+  '--plainrec',
+  '--cps',
+  '--oocps',
+  '--sboocps',
+  '--trampoline',
+  '--trampolinepp'
+];
+
+if (isRunningInsideNode) {
   import('node:fs').then(fs => {
-    signalArray = [0];
-    selectedEvaluator = 'trampolinepp';
-    GlobalEnv.set(VAL_NS, internVariable('*features*'), new EVLCons(internVariable(selectedEvaluator), EVLEmptyList.NIL));
     const nargs = process.argv.length;
-    let n = 2;
+    let n = 2; // skip 'node' and 'core.js'
+    selectedEvaluator = 'trampolinepp';
+    if (n < nargs && evaluatorOptions.includes(process.argv[n])) {
+      selectedEvaluator = process.argv[n++].substring(2);
+    }
+    initializeFeatureList([selectedEvaluator]);
     while (n < nargs) {
       const arg = process.argv[n++];
       switch (arg) {
-        case '-l':
+        case '-l': {
           if (n === nargs) {
             usage();
           }
@@ -4975,13 +4981,24 @@ if (typeof onmessage === 'undefined') { // node
           const fileContents = fs.readFileSync(file, 'utf8');
           printToConsole(evaluateAllForms(fileContents));
           break;
-        case '-e':
+        }
+        case '-e': {
           if (n === nargs) {
             usage();
           }
           const form = process.argv[n++];
           printToConsole(evaluateFirstForm(form));
           break;
+        }
+        case '--convert': {
+          if (n === nargs) {
+            usage();
+          }
+          const file = process.argv[n++];
+          const fileContents = fs.readFileSync(file, 'utf8');
+          printToConsole(convertEVLToXML(fileContents));
+          break;
+        }
         default:
           usage();
       }
@@ -4990,16 +5007,25 @@ if (typeof onmessage === 'undefined') { // node
 }
 
 function usage() {
-  console.log('usage: -l <file> to load a file, -e <form> to evaluate a form');
+  console.log('usage:');
+  console.log('--plainrec: selects the plain recursive evaluator');
+  console.log('--cps: selects the continuation passing style evaluator');
+  console.log('--oocps: selects the object-oriented CPS evaluator');
+  console.log('--sboocps: selects the stack-based object-oriented CPS evaluator');
+  console.log('--trampoline: selects the trampoline evaluator');
+  console.log('--trampolinepp: selects the trampoline++ evaluator (DEFAULT)');
+  console.log('-l <file>: loads the EVL file');
+  console.log('-e <form>: evaluates the form');
+  console.log('--convert <file>: converts the EVL file to XML');
   process.exit();
 }
 
 function printToConsole(response) {
   switch (response.status) {
-    case COMPLETED_NORMALLY:
+    case SUCCESS:
       console.log(response.output);
       break;
-    case COMPLETED_ABNORMALLY:
+    case ERROR:
       console.log(response.output);
       process.exit();
   }
diff --git a/system-files/docgen-sample.evl b/system-files/docgen-sample.evl
new file mode 100644 (file)
index 0000000..dabe2f8
--- /dev/null
@@ -0,0 +1,37 @@
+<chapter>
+<title>Recursive Functions</title>
+<para>...para...</para>
+<para>...para...</para>
+<section>
+<title>Factorial Function</title>
+<para>...para...</para>
+<para>...para...</para>
+(fdef fact (n)
+  <para>...block...</para>
+  <para>...block...</para>
+  (if (= n 0)
+      1 <comment>...eol...</comment>
+    (* n (fact (- n 1))))) <comment>...eoll...</comment>
+
+(test 1 (fact 0))
+(test 120 (fact 5))
+(test 3628800 (fact 10))
+</section>
+<section>
+<title>Fibonacci Sequence</title>
+<para>...para...</para>
+<para>...para...</para>
+(fdef fib (n)
+  <para>...block...</para>
+  <para>...block...</para>
+  (if (= n 0)
+      0 <comment>...eol...</comment>
+    (if (= n 1)
+        1 <comment>...eol...</comment>
+      (+ (fib (- n 1)) (fib (- n 2)))))) <comment>...eoll...</comment>
+
+(test 0 (fib 0))
+(test 5 (fib 5))
+(test 55 (fib 10))
+</section>
+</chapter>