From: Raphaël Van Dyck Date: Mon, 4 Nov 2024 19:24:10 +0000 (+0100) Subject: revise user manual X-Git-Url: http://evlambda.org/gitweb/gitweb.cgi?a=commitdiff_plain;h=HEAD;p=evlambda.git revise user manual --- diff --git a/.gitignore b/.gitignore index f6eaa48..ff457f0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ deploy +scratch.org /ide/ /node_modules/ /src/lezer/evlambda.js diff --git a/system-files/BIBLIOGRAPHY b/system-files/BIBLIOGRAPHY index 619ae9e..d0f8713 100644 --- a/system-files/BIBLIOGRAPHY +++ b/system-files/BIBLIOGRAPHY @@ -9,10 +9,13 @@ +

Bibliography

Harold Abelson and Gerald Jay Sussman, Structure and Interpretation of Computer Programs, second edition, 1996, MIT Press

Tim Bray, Jean Paoli, C. M. Sperberg-McQueen, Eve Maler, François Yergeau (Editors), Extensible Markup Language (XML) 1.0, fifth edition, 2008, https://www.w3.org/TR/2008/REC-xml-20081126/

R. Kent Dybvig, The Scheme Programming Language, fourth edition, 2009, MIT Press

Daniel P. Friedman and Mitchell Wand, Essentials of Programming Languages, third edition, 2008, MIT Press

+

Paul Graham, On Lisp: Advanced Techniques for Common Lisp, 1994, Prentice Hall

+

Paul Graham, ANSI Common Lisp, 1996, Prentice Hall

Peter Norvig, Paradigms of Artificial Intelligence Programming: Case Studies in Common Lisp, 1992, Morgan Kaufmann Publishers

Kent Pitman, Common Lisp HyperSpec, 2005, https://www.lispworks.com/documentation/HyperSpec/Front/index.htm

Christian Queinnec, Lisp in Small Pieces, 2003, Cambridge University Press

diff --git a/system-files/LICENSE b/system-files/LICENSE index bdf022b..b8c1b04 100644 --- a/system-files/LICENSE +++ b/system-files/LICENSE @@ -9,6 +9,7 @@ +

License

Copyright © 2024 Raphaël Van Dyck

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

    diff --git a/system-files/USER-MANUAL b/system-files/USER-MANUAL index df723eb..589a5d0 100644 --- a/system-files/USER-MANUAL +++ b/system-files/USER-MANUAL @@ -16,8 +16,8 @@

    Programming Language

    This section provides an overview of the programming language. Some of the concepts introduced in this section will be illustrated in the section Listener Buffers. An introduction to writing programs can be found in the tutorial. A detailed account of the programming language can be found in the reference manual.

    The programming language, which is called EVLambda like the project, is heavily inspired by the programming languages Scheme and Common Lisp. The bibliography contains many references covering and/or using those two programming languages.

    -

    In EVLambda, there is no difference of nature between code and data. Both code and data are represented by objects and it is the context that determines if an object, in a given occurrence, must be treated as code or as data. The word object is used here in the broad sense of data structure without any reference to object-oriented programming.

    -

    Objects are patterns of bits inside the computer's memory. To facilitate communicating about objects, reading and writing code and data, etc., objects have associated sequences of characters that can be used to represent them.

    +

    In EVLambda, there is no difference of nature between code and data. Both code and data are represented by objects and it is the context of its occurrence that determines if an object must be treated as code or as data. The word object is used here in the broad sense of data structure without any reference to object-oriented programming.

    +

    As we will see below, objects are patterns of bits inside the computer's memory. To facilitate communicating about objects, reading and writing code and data, etc., objects have associated sequences of characters that can be used to represent them.

    Most objects have at least one readable representation. A readable representation of an object is a sequence of characters that can be used to represent the object in input operations. The reader is the component of the programming language responsible for converting a readable representation into the corresponding object.

    All objects have exactly one printable representation. The printable representation of an object is a sequence of characters that can be used to represent the object in output operations. When an object has exactly one readable representation, its printable representation is identical to its readable representation. When an object has more than one readable representation, its printable representation is identical to one of its readable representations chosen to be the standard way to represent the object. When an object has no readable representations, its printable representation is a sequence of characters revealing its type. The printer is the component of the programming language responsible for converting an object into its printable representation.

    Objects are organized into classes called types and types are organized into a hierarchy of types. The type at the top of the hierarchy is called the root type. The types at the bottom of the hierarchy are called the leaf types. If type A is located below type B in the hierarchy, then type A is called a subtype of type B and any object that belongs to type A also belongs to type B. The term “data type” is sometimes used in place of the term “type” even though a (data) type is a class of objects and an object can be a piece of code or a piece of data.

    @@ -30,11 +30,11 @@
    boolean
    Booleans represent truth values. There are exactly two objects of type boolean: #t (representing true) and #f (representing false).
    number
    -
    Numbers represent mathematical numbers: 123, -123, 123.456, -123.456, … The representation of a mathematical number by an object of type number can be exact or approximate.
    +
    Numbers represent mathematical numbers: 123, -123, 123.456, -123.456, … The representation of a mathematical number by an object of type number can be exact or approximate.
    character
    Characters represent Unicode characters: #\a, #\b, #\c, … Most of the characters used in the world have a corresponding Unicode character.
    string
    -
    Strings represent sequences of Unicode characters: "abc", …
    +
    Strings represent indexed sequences of Unicode characters: "abc", …
    keyword
    Keywords can, among other uses, represent named values: :red, :green, :blue, …
    variable
    @@ -42,82 +42,113 @@
    empty-list
    There is exactly one object of type empty-list. Its readable representation is () and its purpose is to represent empty lists of objects.
    cons
    -
    A cons is a pair of objects. The first element is called the car of the cons and the second element is called the cdr of the cons. Conses are the building blocks of many data structures. In particular, conses can be chained together to represent non-empty lists of objects. A non-empty list is represented by a cons whose car is the first element of the list and whose cdr is the sublist of the list obtained by omitting its first element. The non-empty list (1 2 3), for example, is represented by a chain of three conses: a first cons whose car is the number 1 and whose cdr is the second cons, a second cons whose car is the number 2 and whose cdr is the third cons, and a third cons whose car is the number 3 and whose cdr is the empty list.
    +
    A cons is a pair of objects. The first element is called the car of the cons and the second element is called the cdr of the cons. Conses are the building blocks of many data structures. In particular, conses can be chained together to represent non-empty lists of objects. A non-empty list of objects is represented by a cons whose car is the first element of the list and whose cdr is the sublist of the list obtained by omitting its first element. For example, the list (1 2 3) is represented by a chain of three conses: a first cons whose car is the number 1 and whose cdr is the second cons, a second cons whose car is the number 2 and whose cdr is the third cons, and a third cons whose car is the number 3 and whose cdr is the empty list.
    vector
    Vectors represent indexed sequences of objects: #(), #(1 2 3), …
    primitive-function
    -
    Primitive functions are built-in input/output mappings implemented in JavaScript. Primitive functions have no readable representations and their printable representation is #<primitive-function>.
    +
    Primitive functions are input/output mappings implemented in a programming language other than EVLambda. Primitive functions have no readable representations and their printable representation is #<primitive-function>.
    closure
    Closures are input/output mappings implemented in EVLambda. Some closures are tagged as being a macro. Macros are code to code mappings used to create new language constructs. Closures have no readable representations and their printable representation is #<closure>.
    -

    Objects treated as code are called forms. A form is executed by being submitted to a component of the programming language called the evaluator. The evaluation of a form has three possible outcomes:

    +

    Variables name objects through the use of namespaces, bindings, environments, and lookup rules:

    -

    The primary value of a form whose evaluation completed normally is defined as follows: If the result consists of one or more objects, then the primary value of the form is the first object. Otherwise, the primary value of the form is #v.

    -

    Irrespective of its outcome, the evaluation of a form can also have side effects.

    -

    A side effect can be loosely defined as a change observable beyond some boundary. Examples of side effects are:

    +

    A variable that is associated with an object through a binding belonging to namespace X of environment Y is said to be bound to the object in namespace X of environment Y. A variable that is not associated with an object through a binding belonging to namespace X of environment Y is said to be unbound in namespace X of environment Y.

    +

    Objects, bindings, and environments are represented by non-overlapping patterns of bits located inside a region of the computer's memory called the heap. Each object, binding, and environment is uniquely identified by the address of the pattern of bits that represents it. Like objects, bindings, and environments, addresses are also represented by patterns of bits. A reference to an object, binding, or environment is an instance of the pattern of bits that represents the address of the pattern of bits that represents the object, binding, or environment. By abuse of language, we often confuse a reference to an object, binding, or environment with the referenced object, binding, or environment itself.

    +

    An object, binding, or environment references another object, binding, or environment by embedding into its representation a reference to that other object, binding, or environment:

    + +

    The references embedded inside the representation of an object, binding, or environment can be thought of as occupying memory locations denoted by the object, binding, or environment. By abuse of language, we often say that a memory location contains an object, binding, or environment when in reality the memory location contains a reference to the object, binding, or environment.

    +

    Multiple objects, bindings, or environments can reference a common object, binding, or environment, leading to the sharing of the common object, binding, or environment. An object, binding, or environment can reference itself directly (XX) or indirectly (XY→…→X), leading to the existence of a cycle.

    +

    Objects of type void, boolean, number, character, string, keyword, symbol, empty-list, primitive-function, or closure are immutable and cannot be altered. Objects of type cons or vector, bindings, and environments are mutable and can be altered in the following ways:

    + +

    Replacing an object by another object can be thought of as replacing the object (reference) contained in a memory location by another object (reference).

    +

    The life cycle of an object, binding, or environment consists of the following events: a creation (which consists of an allocation followed by an initialization) followed by any number of alterations followed by a destruction (which consists of a deallocation). Alterations are possible only if the object, binding, or environment is mutable.

    +

    The destruction of an object, binding, or environment occurs automatically if and when the object, binding, or environment becomes unreachable. The rules used to determine if an object, binding, or environment is reachable are as follows (the concepts of global environment and control stack will be introduced later in this section):

    -

    A consequence of the existence of side effects is that the repeated evaluations of the same form do not necessarily have the same outcome.

    -

    It was said above that variables can be used to name objects. This is accomplished through the use of namespaces, bindings, environments, and lookup rules:

    +

    The garbage collector is the component of the programming language responsible for the automatic destruction of unreachable objects, bindings, and environments.

    +

    Objects treated as code are called forms. A form is executed by being submitted to a component of the programming language called the evaluator. (The form is said to be evaluated.) The evaluation of a form has three possible outcomes:

    -

    The purpose of an environment is to implement a non-ambiguous mapping from variables to memory locations. Without the use of namespaces, an environment would not be allowed to contain different bindings for the same variable. With the use of namespaces, an environment is allowed to contain different bindings for the same variable as long as those bindings belong to different namespaces.

    -

    The association between a variable and an object through a binding is indirect. A binding is fundamentally an association between a variable and a memory location and the object that is associated with the variable through the binding is the object that happens to be stored in the memory location. And because the programming language provides operations to change the object stored in the memory location of a binding, it is possible for the object that is associated with the variable through the binding to change over time.

    -

    Changing the object stored in the memory location of a binding is called altering the binding. Altering a binding is an example of side effect. Each time a new binding is created, the memory location is drawn from a pool of fresh (unused) memory locations. Consequently, memory locations are not shared among bindings and altering a binding has no effect on other bindings.

    -

    A variable that is associated with a memory location through a binding belonging to namespace X of environment Y is said to be bound to the memory location in namespace X of environment Y. By abuse of language, the variable is also often said to be bound to the object contained in the memory location in namespace X of environment Y. A variable that is not associated with a memory location through a binding belonging to namespace X of environment Y is said to be unbound in namespace X of environment Y.

    -

    Each evaluation is done with respect to three environments: the global environment, the current lexical environment, and the current dynamic environment. The reference manual will introduce the concepts of scope and extent. The different environments draw their name from the scope and extent of the bindings they contain:

    +

    The primary value of a form whose evaluation completed normally is defined as follows: If the result consists of one or more objects, then the primary value of the form is the first object. Otherwise, the primary value of the form is #v.

    +

    Execution of EVLambda code is achieved through interpretation or compilation.

    +

    An interpreter for a language X is a program capable of directly executing code written in language X. Language X is called the source language of the interpreter. A compiler for a language X is a program capable of translating code written in language X into code written in a language Y. Language X is called the source language of the compiler and language Y is called the target language of the compiler. Code handed to an interpreter or compiler is called source code. A file containing source code is called a source file. Code produced by a compiler is called compiled code. A file containing compiled code is called a compiled file.

    +

    An interpreter-based EVLambda evaluator executes EVLambda code by submitting the EVLambda code to its embedded EVLambda interpreter. A compiler-based EVLambda evaluator executes EVLambda code by first submitting the EVLambda code to its embedded EVLambda compiler and then arranging for the compiled code to be executed. Interpretation of EVLambda code and execution of compiled code occur at a time called run time. Compilation of EVLambda code occurs at a time called compile time.

    +

    Each evaluation is done with respect to three environments: a global environment, a lexical environment, and a dynamic environment. The reference manual will introduce the concepts of scope and extent. The different environments draw their names from the scope and extent of their bindings:

    -

    Global environments and lexical environments are partitioned into two namespaces: the value namespace and the function namespace. Dynamic environments only have one namespace: the value namespace. The function namespace correspond to contexts requiring a function. The value namespace correspond to all other contexts. Although the programming language does not enforce any restriction on the type of the object stored in the memory location of a binding, function namespaces should only contain bindings between variables and functions.

    -

    The global environment is unique for a given instance of the evaluator. It is created when the evaluator starts and it continues to exist until the evaluator stops. The global environment of an evaluator that has just started contains a set of predefined bindings. Each primitive function, for example, is accessible through a predefined binding. As forms are evaluated, side effects can change the global environment in the following ways:

    +

    The fact that a global/lexical/dynamic environment contains bindings with such scope and such extent is a consequence of the evaluation rules stated later in this section. It is thus not necessary to know the concepts of scope and extent to start writing programs in EVLambda. Knowing the evaluation rules should be enough.

    +

    Evaluations are all done with respect to the same global environment. That environment, referred to as “the global environment”, is created when the evaluator starts and continues to exist until the evaluator stops. The global environment of an evaluator that has just started contains a set of predefined bindings, most of which providing access to a primitive function. As forms are evaluated, the global environment can change in the following ways:

    A global variable is a binding between a variable and an object (of any type) in the value namespace of the global environment. A global function is a binding between a variable and a function other than a macro in the function namespace of the global environment. A global macro is a binding between a variable and a macro in the function namespace of the global environment. Global variables, global functions, and global macros are usually created and altered using language constructs called global definitions.

    -

    The lexical and dynamic environments are not unique. Different forms can be evaluated with respect to different lexical and dynamic environments. The lexical and dynamic environments with respect to which a form is evaluated are called the current lexical and dynamic environments. In contrast to global environments, bindings are never added to or removed from a lexical or dynamic environment after the environment has been created.

    +

    Evaluations are not all done with respect to the same lexical and dynamic environments. The lexical environment and the dynamic environments with respect to which a form is evaluated are referred to as “the current lexical environment” and “the current dynamic environment”, respectively.

    Forms submitted to the evaluator through a listener buffer, through the Evaluate Form command, or through the Load Buffer command are evaluated with respect to an initial lexical environment and an initial dynamic environment that are both empty.

    -

    The current lexical and dynamic environments are not always empty. A consequence of the evaluation rules stated later in this section is that each form is evaluated with respect to (1) a lexical environment that is either the initial empty lexical environment or the result of extending, once or multiple times in sequence, the initial empty lexical environment and (2) a dynamic environment that is either the initial empty dynamic environment or the result of extending, once or multiple times in sequence, the initial empty dynamic environment.

    +

    A consequence of the evaluation rules stated later in this section is that each form is evaluated with respect to (1) a lexical environment that is either the initial empty lexical environment or the result of extending, once or multiple times in sequence, the initial empty lexical environment and (2) a dynamic environment that is either the initial empty dynamic environment or the result of extending, once or multiple times in sequence, the initial empty dynamic environment.

    Let env be an environment, ns be a namespace of the environment, n be a nonnegative integer, var1, …, varn be a sequence of n distinct variables, and obj1, …, objn be a sequence of n objects. The environment extending the environment env to bind, in the namespace ns, the variable vari to the object obji (for all i from 1 to n) is the environment obtained as follows:

    1. Copy the environment env in such a way that (1) the environment env and its copy are distinct (steps 2 and 3 below must have no effect on the environment env) and (2) the environment env and its copy contain the exact same bindings (the bindings are not copied but shared between the two environments).
    2. For all i from 1 to n, delete from the copy of the namespace ns the binding for the variable vari, if it exists.
    3. -
    4. For all i from 1 to n, add to the copy of the namespace ns a binding between the variable vari and a fresh memory location containing the object obji.
    5. +
    6. For all i from 1 to n, add to the copy of the namespace ns a new binding between the variable vari and the object obji.

    A binding deleted from the environment in step 2 is said to be shadowed by the binding for the same variable added to the environment in step 3. The extended environment is non-empty unless the environment being extended is empty and n is equal to zero.

    -

    Memory locations named by variables are accessed using three pairs of operations. In each pair, one operation is used to get (read) the object stored in the memory location and one operation is used to change (write) the object stored in the memory location. Each pair uses a specific lookup rule to determine the memory location named by the variable.

    -

    The operations vref and vset! limit their search to the value namespace and are primarily interested in bindings with a lexical scope. They use the following lookup rule:

    +

    Two additional consequences of the evaluation rules stated later in this section are that (1) the function namespace of a dynamic environment is always empty and (2) bindings are never added to or deleted from a lexical or dynamic environment after the environment has been created.

    +

    Together, the global environment, the current lexical environment, and the current dynamic environment can contain up to five bindings for any given variable:

    -

    The operations fref and fset! limit their search to the function namespace and are primarily interested in bindings with a lexical scope. They use the following lookup rule:

    +

    Three pairs of operations are provided to get and set the object associated with a variable through a binding. Each pair uses a specific lookup rule to select one of the five aforementioned bindings. In each pair, one operation is used to get the object associated with the variable through the selected binding (the operation fails if the lookup rule fails to select a binding) and one operation is used to set the object associated with the variable through the selected binding (a new binding is added to the global environment if the lookup rule fails to select a binding).

    +

    The operations vref and vset! use the following lookup rule:

    -

    The operations dref and dset! limit their search to the value namespace and are primarily interested in bindings with a dynamic extent. They use the following lookup rule:

    +

    The operations fref and fset! use the following lookup rule:

    -

    A consequence of those two-step lookup rules is that a binding in the current lexical or dynamic environment will effectively shadow a binding for the same variable in the same namespace of the global environment.

    -

    When a form is submitted to the evaluator, the evaluator analyzes the form to determine how to evaluate it. If the form is the empty list, then the evaluation completes abnormally. Otherwise, if the form is neither a variable nor a cons, then the result of the evaluation is the form itself. (Objects that are neither the empty list, nor a variable, nor a cons are said to be self-evaluating.) Otherwise, if the form is a variable var, then the variable is treated as an abbreviation for either (vref var) or (fref var), depending on the context. Otherwise, the form is necessarily a cons and the evaluation completes abnormally unless the form is a non-empty list matching one of the following patterns:

    +

    The operations dref and dset! use the following lookup rule:

    + +

    A consequence of the lookup rules is that a binding in the value/function namespace of the current lexical/dynamic environment will effectively shadow a binding for the same variable in the value/function namespace of the global environment.

    +

    When a form is submitted to the evaluator, the evaluator analyzes the form to determine how to evaluate it. If the form is the empty list, then the evaluation completes abnormally. Otherwise, if the form is neither a variable nor a cons, then the result of the evaluation is the form itself. (Objects that are neither the empty list, nor a variable, nor a cons are said to be self-evaluating.) Otherwise, if the form is a variable var, then the variable is treated as an abbreviation for either (vref var) or (fref var), depending on the context of its occurrence. Otherwise, the form is necessarily a cons and the evaluation completes abnormally unless the form matches one of the following patterns:

    1. (quote <literal>)
    2. (progn <forms>)
    3. @@ -153,7 +184,8 @@

      The first sixteen patterns have priority over pattern 17: If a form matches one of the first sixteen patterns, then pattern 17 is ignored.

      A form matching one of the first sixteen patterns is called a special form and the variables quote, progn, if, _vlambda, _mlambda, _flambda, _dlambda, vref, vset!, fref, fset!, dref, dset!, apply, multiple-value-call, and multiple-value-apply are called special operators. A form matching pattern 17 is either a macro call or a function call. If <operator> matches a variable naming a macro according to the lookup rule used by fref, then the form is a macro call. Otherwise, the form is a function call.

      -

      The rule regarding forms consisting of a variable is the following: If a variable var occurs in operator position, then the variable is treated as an abbreviation for (fref var) and the function namespace is used. Otherwise, the variable is treated as an abbreviation for (vref var) and the value namespace is used. The function call (f x), for example, would be treated as an abbreviation for ((fref f) (vref x)). The full forms (fref var) and (vref var) can always be used to force the use of one of the namespaces.

      +

      Forms consisting of a variable and special forms matching pattern 8 (vref), 10 (fref), or 12 (dref) are called variable references. Special forms matching pattern 9 (vset!), 11 (fset!), or 13 (dset!) are called variable assignments.

      +

      The rule regarding forms consisting of a variable is the following: If a variable var occurs in operator position, then the variable is treated as an abbreviation for (fref var) and the function namespace is used. Otherwise, the variable is treated as an abbreviation for (vref var) and the value namespace is used. For example, the function call (f x) would be treated as an abbreviation for ((fref f) (vref x)). The full forms (fref var) and (vref var) can always be used to force the use of a specific namespace.

      As we will see momentarily, the evaluation of a form containing subforms entails the evaluation of some or all of the subforms. When a subform is evaluated, the following rules apply:

      • The subform is evaluated with respect to the same lexical and dynamic environments as the containing form. (But see below how closures are invoked.)
      • @@ -163,28 +195,28 @@

        Special forms and calls are evaluated as follows:

        (quote <literal>)
        -
        The quote-form evaluates to the unevaluated literal. Using a quote-form, any object can be treated as data. For any object obj, (quote obj) can be abbreviated to 'obj.
        +
        The quote-form evaluates to the unevaluated literal. Using a quote-form, any object can be treated as data. For any object obj, (quote obj) can be abbreviated to 'obj.
        (progn <forms>)
        The forms are evaluated in sequence from left to right. If there is at least one form, then the progn-form evaluates to the values of the last form. Otherwise, the progn-form evaluates to #v.
        (if <test-form> <then-form> <else-form>)
        -
        The test-form is evaluated. Let primval be the primary value of the test-form. If primval is not a boolean, then the evaluation of the if-form completes abnormally. If primval is the boolean #t, then the then-form is evaluated and the if-form evaluates to the values of the then-form. If primval is the boolean #f, then the else-form is evaluated and the if-form evaluates to the values of the else-form.
        +
        The test-form is evaluated. Let primval be the primary value of the test-form. If primval is not a boolean, then the evaluation of the if-form completes abnormally. If primval is the boolean #t, then the then-form is evaluated and the if-form evaluates to the values of the then-form. If primval is the boolean #f, then the else-form is evaluated and the if-form evaluates to the values of the else-form.
        (_vlambda <parameter-list> <body>)
        (_mlambda <parameter-list> <body>)
        (_flambda <parameter-list> <body>)
        (_dlambda <parameter-list> <body>)
        A lambda-form evaluates to a closure recording the following two pieces of information: the lambda-form and the current lexical environment. A closure resulting from the evaluation of an _mlambda-form is tagged as being a macro.
        (vref <variable>)
        -
        If the variable is bound to an object in the value namespace of the current lexical environment, then the vref-form evaluates to that object. Otherwise, if the variable is bound to an object in the value namespace of the global environment, then the vref-form evaluates to that object. Otherwise, the evaluation of the vref-form completes abnormally.
        +
        If there exists a binding for the variable in the value namespace of the current lexical environment, then the vref-form evaluates to the object associated with the variable through that binding. Otherwise, if there exists a binding for the variable in the value namespace of the global environment, then the vref-form evaluates to the object associated with the variable through that binding. Otherwise, the evaluation of the vref-form completes abnormally.
        (vset! <variable> <value-form>)
        -
        The value-form is evaluated. Let primval be the primary value of the value-form. If the variable is bound to a memory location in the value namespace of the current lexical environment, then the object stored in that memory location is replaced by primval. Otherwise, if the variable is bound to a memory location in the value namespace of the global environment, then the object stored in that memory location is replaced by primval. Otherwise, a binding between the variable and a fresh memory location containing primval is added to the value namespace of the global environment. In all three cases, the vset-form evaluates to primval.
        +
        The value-form is evaluated. Let primval be the primary value of the value-form. If there exists a binding for the variable in the value namespace of the current lexical environment, then the object associated with the variable through that binding is replaced by primval. Otherwise, if there exists a binding for the variable in the value namespace of the global environment, then the object associated with the variable through that binding is replaced by primval. Otherwise, a new binding between the variable and primval is added to the value namespace of the global environment. In all three cases, the vset-form evaluates to primval.
        (fref <variable>)
        -
        If the variable is bound to an object in the function namespace of the current lexical environment, then the fref-form evaluates to that object. Otherwise, if the variable is bound to an object in the function namespace of the global environment, then the fref-form evaluates to that object. Otherwise, the evaluation of the fref-form completes abnormally.
        +
        If there exists a binding for the variable in the function namespace of the current lexical environment, then the fref-form evaluates to the object associated with the variable through that binding. Otherwise, if there exists a binding for the variable in the function namespace of the global environment, then the fref-form evaluates to the object associated with the variable through that binding. Otherwise, the evaluation of the fref-form completes abnormally.
        (fset! <variable> <value-form>)
        -
        The value-form is evaluated. Let primval be the primary value of the value-form. If the variable is bound to a memory location in the function namespace of the current lexical environment, then the object stored in that memory location is replaced by primval. Otherwise, if the variable is bound to a memory location in the function namespace of the global environment, then the object stored in that memory location is replaced by primval. Otherwise, a binding between the variable and a fresh memory location containing primval is added to the function namespace of the global environment. In all three cases, the fset-form evaluates to primval.
        +
        The value-form is evaluated. Let primval be the primary value of the value-form. If there exists a binding for the variable in the function namespace of the current lexical environment, then the object associated with the variable through that binding is replaced by primval. Otherwise, if there exists a binding for the variable in the function namespace of the global environment, then the object associated with the variable through that binding is replaced by primval. Otherwise, a new binding between the variable and primval is added to the function namespace of the global environment. In all three cases, the fset-form evaluates to primval.
        (dref <variable>)
        -
        If the variable is bound to an object in the value namespace of the current dynamic environment, then the dref-form evaluates to that object. Otherwise, if the variable is bound to an object in the value namespace of the global environment, then the dref-form evaluates to that object. Otherwise, the evaluation of the dref-form completes abnormally.
        +
        If there exists a binding for the variable in the value namespace of the current dynamic environment, then the dref-form evaluates to the object associated with the variable through that binding. Otherwise, if there exists a binding for the variable in the value namespace of the global environment, then the dref-form evaluates to the object associated with the variable through that binding. Otherwise, the evaluation of the dref-form completes abnormally.
        (dset! <variable> <value-form>)
        -
        The value-form is evaluated. Let primval be the primary value of the value-form. If the variable is bound to a memory location in the value namespace of the current dynamic environment, then the object stored in that memory location is replaced by primval. Otherwise, if the variable is bound to a memory location in the value namespace of the global environment, then the object stored in that memory location is replaced by primval. Otherwise, a binding between the variable and a fresh memory location containing primval is added to the value namespace of the global environment. In all three cases, the dset-form evaluates to primval.
        +
        The value-form is evaluated. Let primval be the primary value of the value-form. If there exists a binding for the variable in the value namespace of the current dynamic environment, then the object associated with the variable through that binding is replaced by primval. Otherwise, if there exists a binding for the variable in the value namespace of the global environment, then the object associated with the variable through that binding is replaced by primval. Otherwise, a new binding between the variable and primval is added to the value namespace of the global environment. In all three cases, the dset-form evaluates to primval.
        (apply <operator> <operands>)
        (multiple-value-call <operator> <operands>)
        (multiple-value-apply <operator> <operands>)
        @@ -201,10 +233,10 @@
      • The invocation can get caught in an infinite loop and never complete.

      The primary value of an invocation that completed normally is defined as follows: If the output consists of one or more objects, then the primary value of the invocation is the first object. Otherwise, the primary value of the invocation is #v.

      -

      Irrespective of its outcome, the invocation of a function can also have side effects.

      -

      Primitive functions are invoked as follows:

      -
      A primitive function is implemented by a JavaScript function accepting as input (an encoding of) the arguments of the invocation of the primitive function and producing as output (an encoding of) the values of the invocation of the primitive function. The JavaScript function is invoked on the arguments. If the invocation of the JavaScript function completes abnormally, then the invocation of the primitive function also completes abnormally. Otherwise, if the invocation of the JavaScript function does not complete, then the invocation of the primitive function does not complete either. Otherwise, the primitive function returns the values returned by the JavaScript function.
      -

      Closures are invoked as follows:

      +

      The verbs “accept” and “return” are often used to describe the input/output mapping implemented by a function. For example, we could describe a function by saying that the function accepts two numbers and returns the sum of their squares.

      +

      A primitive function is invoked as follows:

      +
      Let's assume that, as is the case with all the evaluators currently available, the primitive function is implemented by a JavaScript function accepting (an encoding of) the arguments of the invocation of the primitive function and returning (an encoding of) the values of the invocation of the primitive function. The JavaScript function is invoked on the arguments. If the invocation of the JavaScript function completes abnormally, then the invocation of the primitive function also completes abnormally. Otherwise, if the invocation of the JavaScript function does not complete, then the invocation of the primitive function does not complete either. Otherwise, the primitive function returns the values returned by the JavaScript function.
      +

      A closure is invoked as follows:

      If the number of variables in the parameter list of the lambda-form recorded by the closure and the number of arguments are different, then the invocation completes abnormally. (As we will see in the reference manual, it is actually possible to create closures accepting a variable number of arguments.) Otherwise, let var1, …, varn be the variables composing the parameter list of the lambda-form recorded by the closure, arg1, …, argn be the arguments, and lexenv and dynenv be the following environments:

        @@ -214,6 +246,20 @@

      The objects composing the body of the lambda-form recorded by the closure are evaluated with respect to lexenv and dynenv as if they were part of a progn-form. If the evaluation of the progn-form completes abnormally, then the invocation also completes abnormally. Otherwise, if the evaluation of the progn-form does not complete, then the invocation does not complete either. Otherwise, the closure returns the values of the progn-form.

      +

      The evaluator uses a data structure called a control stack to coordinate its activities. Each time a form is submitted to the evaluator through a listener buffer, through the Evaluate Form command, or through the Load Buffer command, a new control stack is created that will be used throughout the evaluation of the form.

      +

      An evaluation/invocation that completes normally produces a result/output consisting of zero or more objects. The production of that result/output, which only occurs if the evaluation/invocation completes normally, is the primary effect of the evaluation/invocation. In addition to or in place of its primary effect, an evaluation/invocation can also have secondary effects called side effects. Examples of side effects are:

      +
        +
      • The abnormal completion of the evaluation/invocation.
      • +
      • The noncompletion of the evaluation/invocation.
      • +
      • The consumption of time.
      • +
      • The consumption of memory.
      • +
      • The alteration of a cons, vector, or binding.
      • +
      • The addition of a binding to the global environment.
      • +
      • The deletion of a binding from the global environment.
      • +
      • The transfer of information from the outside world to the form/function (an input operation).
      • +
      • The transfer of information from the form/function to the outside world (an output operation).
      • +
      +

      A consequence of the existence of side effects is that the repeated evaluations of the same form or the repeated invocations of the same function on the same arguments do not necessarily have the same outcome and, if they complete normally, do not necessarily produce the same result/output.

      Integrated Development Environment

      The integrated development environment (IDE) is a web application that can run either from the EVLambda web server (online mode) or from a web server running on your machine (offline mode). The code running in the web browser is exactly the same in both modes but the behavior of the IDE is slightly different because the backends have different capabilities.

      The IDE's graphical user interface consists of a menu bar at the top left, an info bar at the top right, a minibuffer at the bottom, and a set of windows in the main area. Each window consists of a contents area and a status bar. At any given time, a window displays the contents of a buffer, of which there are two types: the file buffers and the listener buffers.

      @@ -236,90 +282,94 @@
    4. If the evaluation does not complete, then you must abort the evaluation or restart the evaluator in order to get a new prompt.
    5. We will now illustrate some of the concepts introduced in the section Programming Language 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… command from the Eval menu) and select Trampoline++ as the evaluator type.

      -

      Here are the global functions that will be used in the evaluations. For each function, a template function call and a description of the values returned by the function 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).

      +

      Here are the global functions that will be used in the evaluations. For each function, a template function call and a description of the function 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).

      (car cons)
      The function car returns the first element of its argument, which must be of type cons.
      (cdr cons)
      The function cdr returns the second element of its argument, which must be of type cons.
      -
      (list obj1objn)
      -
      The function list collects its arguments into a list: when invoked on the arguments obj1, …, objn, the function returns a list whose elements are obj1, …, objn.
      -
      (+ num1numn)
      +
      (list object1objectn)
      +
      The function list collects its arguments into a list: when invoked on the arguments object1, …, objectn, the function returns a list whose elements are object1, …, objectn.
      +
      (+ number1numbern)
      The function + returns the sum of its arguments, which must be of type number.
      -
      (* num1numn)
      +
      (* number1numbern)
      The function * returns the product of its arguments, which must be of type number.
      -
      (values obj1objn)
      -
      The function values converts its arguments into values: when invoked on the arguments obj1, …, objn, the function returns the values obj1, …, objn.
      +
      (values object1objectn)
      +
      The function values converts its arguments into values: when invoked on the arguments object1, …, objectn, the function returns the values object1, …, objectn.
      -

      Here are the global macros that will be used in the evaluations. For each macro, a template macro call and a description of what the macro does are provided. The variable in operator position is the name of the macro (i.e., the variable bound to the macro in the function namespace of the global environment).

      +

      Here are the global macros that will be used in the evaluations. For each macro, a template macro call and a description of the macro are provided. The variable in operator position is the name of the macro (i.e., the variable bound to the macro in the function namespace of the global environment).

      (vdef <variable> <value-form>)
      The purpose of the macro vdef is to define a global variable by ensuring that the variable is bound in the value namespace of the global environment to the primary value of the value-form. The macro call evaluates to the variable.
      (fdef <variable> <parameter-list> <body>)
      The purpose of the macro fdef is to define a global function by ensuring that the variable is bound in the function namespace of the global environment to the closure resulting from the evaluation of the _vlambda-form (_vlambda <parameter-list> <body>). The macro call evaluates to the variable.
      (loop <forms>)
      -
      The purpose of the macro loop is to create an infinite loop repeatedly evaluating the forms in sequence from left to right. The macro call (loop), for example, endlessly does nothing. The evaluation of the macro call normally does not complete but there are ways to exit an infinite loop.
      +
      The purpose of the macro loop is to create an infinite loop repeatedly evaluating the forms in sequence from left to right. For example, the macro call (loop) endlessly does nothing. The evaluation of the macro call normally does not complete but there are ways to exit an infinite loop.

      And here is the commented transcript, where each box contains a form and its values. The character ⏎ marks the places where the Return or Enter key should be pressed.

      -
      > (+ 1 2)⏎
      3
      +
      > (+ 1 2)⏎
      3

      The evaluation produces a result consisting of the sum of the two numbers 1 and 2. Because numbers are self-evaluating, quoting the numbers is not necessary.

      -
      > (+ '1 '2)⏎
      3
      +
      > (+ '1 '2)⏎
      3

      The evaluation produces the same result if the numbers are quoted. Quoting self-evaluating objects is unidiomatic, though.

      -
      > (car '(1 2 3))⏎
      1
      +
      > (car '(1 2 3))⏎
      1

      The evaluation produces a result consisting of the first element of the list (1 2 3). Because lists are not self-evaluating, quoting the list is necessary.

      -
      > (car (1 2 3))⏎
      ERROR: The operator form does not evaluate to a function.
      +
      > (car (1 2 3))⏎
      ERROR: The operator form does not evaluate to a function.

      The evaluation completes abnormally if the list is not quoted. The reason is as follows: The evaluator treats the list (1 2 3) as a function call and the operator, the number 1, does not evaluate to a function.

      -
      > (cdr '(1 2 3))⏎
      (2 3)
      +
      > (cdr '(1 2 3))⏎
      (2 3)

      The evaluation produces a result consisting of the sublist of the list (1 2 3) obtained by omitting its first element.

      -
      > (car (cdr '(1 2 3)))⏎
      2
      +
      > (car (cdr '(1 2 3)))⏎
      2

      The evaluation produces a result consisting of the second element of the list (1 2 3).

      -
      > (cdr (cdr '(1 2 3)))⏎
      (3)
      +
      > (cdr (cdr '(1 2 3)))⏎
      (3)

      The evaluation produces a result consisting of the sublist of the list (1 2 3) obtained by omitting its first two elements.

      -
      > (car (cdr (cdr '(1 2 3))))⏎
      3
      +
      > (car (cdr (cdr '(1 2 3))))⏎
      3

      The evaluation produces a result consisting of the third element of the list (1 2 3).

      -
      > (cdr (cdr (cdr '(1 2 3))))⏎
      ()
      +
      > (cdr (cdr (cdr '(1 2 3))))⏎
      ()

      The evaluation produces a result consisting of the sublist of the list (1 2 3) obtained by omitting its first three elements.

      -
      > (car (cdr (cdr (cdr '(1 2 3)))))⏎
      ERROR: The 1st argument is not of type EVLCons.
      +
      > (car (cdr (cdr (cdr '(1 2 3)))))⏎
      ERROR: The 1st argument is not of type EVLCons.

      The evaluation completes abnormally because the empty list is not a cons.

      -
      > (disk-area 2)⏎
      ERROR: The variable 'disk-area' is unbound in the FUNCTION namespace.
      +
      > (disk-area 2)⏎
      ERROR: The variable 'disk-area' is unbound in the FUNCTION namespace.

      The evaluation completes abnormally because the global function disk-area is undefined.

      -
      > (fdef disk-area (r) (* 3.14 r r))⏎
      disk-area
      -

      The evaluation produces a result consisting of the variable disk-area. More importantly, the evaluation has the side effect of defining the global function disk-area. The function accepts as input the radius of a disk and produces as output the area of the disk computed using 3.14 as the value of pi. When the function is invoked, its body is evaluated with respect to a lexical environment binding, in its value namespace, the variable r to the argument of the invocation (that is, the radius of the disk).

      -
      > (disk-area 2)⏎
      12.56
      +
      > (fdef disk-area (r) (* 3.14 r r))⏎
      disk-area
      +

      The evaluation produces a result consisting of the variable disk-area. More importantly, the evaluation has the side effect of defining the global function disk-area. The function accepts the radius of a disk and returns the area of the disk computed using 3.14 as the value of pi. When the function is invoked, its body is evaluated with respect to a lexical environment binding, in its value namespace, the variable r to the argument of the invocation (that is, the radius of the disk).

      +
      > (disk-area 2)⏎
      12.56

      The evaluation produces the expected result.

      -
      > (fdef disk-area (r) (* 3.1415 r r))⏎
      disk-area
      +
      > (fdef disk-area (r) (* 3.1415 r r))⏎
      disk-area

      The evaluation has the side effect of redefining the global function disk-area to compute the area of the disk using 3.1415 as the value of pi.

      -
      > (disk-area 2)⏎
      12.566
      +
      > (disk-area 2)⏎
      12.566

      The evaluation produces the expected result.

      -
      > (fdef disk-area (r) (* *pi* r r))⏎
      disk-area
      +
      > (fdef disk-area (r) (* *pi* r r))⏎
      disk-area

      The evaluation has the side effect of redefining the global function disk-area to compute the area of the disk using the value of the global variable *pi* as the value of pi. It is customary for a global variable to have a name starting and ending with an asterisk.

      -
      > (disk-area 2)⏎
      ERROR: The variable '*pi*' is unbound in the VALUE namespace.
      +
      > (disk-area 2)⏎
      ERROR: The variable '*pi*' is unbound in the VALUE namespace.

      The evaluation completes abnormally because the global variable *pi* is undefined.

      -
      > (vdef *pi* 3.141592)⏎
      *pi*
      -

      The evaluation produces a result consisting of the variable *pi*. More importantly, the evaluation has the side effect of defining the global variable *pi*. The variable has the value 3.141592.

      -
      > (disk-area 2)⏎
      12.566368
      +
      > (vdef *pi* 3.141592)⏎
      *pi*
      +

      The evaluation produces a result consisting of the variable *pi*. More importantly, the evaluation has the side effect of defining the global variable *pi*.

      +
      > *pi*⏎
      3.141592
      +

      The global variable *pi* has the value 3.141592.

      +
      > (disk-area 2)⏎
      12.566368

      The evaluation produces the expected result.

      -
      > (vdef *pi* 3.14159265)⏎
      *pi*
      -

      The evaluation has the side effect of redefining the global variable *pi* to have the value 3.14159265.

      -
      > (disk-area 2)⏎
      12.5663706
      +
      > (vdef *pi* 3.14159265)⏎
      *pi*
      +

      The evaluation has the side effect of redefining the global variable *pi*.

      +
      > *pi*⏎
      3.14159265
      +

      The global variable *pi* has the value 3.14159265.

      +
      > (disk-area 2)⏎
      12.5663706

      The evaluation produces the expected result.

      -
      > (values)⏎
      +
      > (values)⏎

      The evaluation produces a result consisting of zero values.

      -
      > (values 1)⏎
      1
      +
      > (values 1)⏎
      1

      The evaluation produces a result consisting of one value: 1.

      -
      > 1⏎
      1
      +
      > 1⏎
      1

      Producing a result consisting of one value is the default behavior so using values in this case is unnecessary and unidiomatic.

      -
      > (values 1 2)⏎
      1
      2
      +
      > (values 1 2)⏎
      1
      2

      The evaluation produces a result consisting of two values: 1 and 2.

      -
      > (list (values) (values 1) 1 (values 1 2))⏎
      (#v 1 1 1)
      -

      The forms (values), (values 1), 1, and (values 1 2) have the primary values #v, 1, 1, and 1, respectively.

      -
      > (loop)⏎
      ABORTED
      +
      > (list (values) (values 1) 1 (values 1 2))⏎
      (#v 1 1 1)
      +

      The primary values of the forms (values), (values 1), 1, and (values 1 2) are #v, 1, 1, and 1, respectively.

      +
      > (loop)⏎
      ABORTED

      The evaluation is caught in an infinite loop. You can use the Abort Evaluation command from the Eval menu to stop the evaluation and get a new prompt.

      -
      > (disk-area 2)⏎
      12.5663706
      +
      > (disk-area 2)⏎
      12.5663706

      Aborting an evaluation has no effect on the global definitions.

      -
      > (loop)⏎
      TERMINATED
      +
      > (loop)⏎
      TERMINATED

      The evaluation is caught in an infinite loop. You can use the Restart Evaluator… command from the Eval menu to stop the evaluation and get a new prompt.

      -
      > (disk-area 2)⏎
      ERROR: The variable 'disk-area' is unbound in the FUNCTION namespace.
      +
      > (disk-area 2)⏎
      ERROR: The variable 'disk-area' is unbound in the FUNCTION namespace.

      Restarting the evaluator erases all global definitions.

      Menu Bar

      File Menu

      diff --git a/system-files/all-caps.css b/system-files/all-caps.css index 7597748..d71d442 100644 --- a/system-files/all-caps.css +++ b/system-files/all-caps.css @@ -7,7 +7,7 @@ html { line-height: 1.4; } -pre.tr { +pre.repl { margin-left: 2em; padding: 10px; border-radius: 10px; diff --git a/system-files/mantle.evl b/system-files/mantle.evl index 481b5a3..02fa237 100644 --- a/system-files/mantle.evl +++ b/system-files/mantle.evl @@ -941,12 +941,12 @@ (<= <number> <number>) (> <number> <number>) (>= <number> <number>) -(test #t (= 0 0)) -(test #f (/= 0 0)) -(test #f (< 0 0)) -(test #t (<= 0 0)) -(test #f (> 0 0)) -(test #t (>= 0 0)) +(test '(#f #t #f) (list (= -1 0) (= 0 0) (= 1 0))) +(test '(#t #f #t) (list (/= -1 0) (/= 0 0) (/= 1 0))) +(test '(#t #f #f) (list (< -1 0) (< 0 0) (< 1 0))) +(test '(#t #t #f) (list (<= -1 0) (<= 0 0) (<= 1 0))) +(test '(#f #f #t) (list (> -1 0) (> 0 0) (> 1 0))) +(test '(#f #t #t) (list (>= -1 0) (>= 0 0) (>= 1 0)))
      Primitive Datatype <code>character</code>