Merge branch 'develop'
This commit is contained in:
commit
544f222edc
5
.gitignore
vendored
5
.gitignore
vendored
|
@ -1,6 +1,7 @@
|
|||
/target
|
||||
/classes
|
||||
/checkouts
|
||||
resources/scratch/
|
||||
profiles.clj
|
||||
pom.xml
|
||||
pom.xml.asc
|
||||
|
@ -16,3 +17,7 @@ pom.xml.asc
|
|||
.clj-kondo/
|
||||
.lsp/
|
||||
resources/scratch.lsp
|
||||
Sysout*.lsp
|
||||
*.pdf
|
||||
|
||||
src/beowulf/scratch.clj
|
||||
|
|
12
CHANGELOG.md
12
CHANGELOG.md
|
@ -1,7 +1,17 @@
|
|||
# Change Log
|
||||
All notable changes to this project will be documented in this file. This change log follows the conventions of [keepachangelog.com](http://keepachangelog.com/).
|
||||
|
||||
## [0.2.1] - 2023-03-??
|
||||
## [0.3.0] - 2023-04-10
|
||||
|
||||
### Changed
|
||||
|
||||
- Added property lists in the exact format used by Lisp 1.5;
|
||||
- Added `ASSOC`, `EFFACE`, `MAPLIST`, `PROG`, and other functions;
|
||||
- Where there are both interpreted (`EXPR`) and Clojure (`SUBR`) implementations of the same function, the `EXPR` is preferred (it is planned to make this configurable if/when there is a working compiler);
|
||||
- More error messages/diagnostics are now printed in Old English (it is planned to implement internationalisation so that you can switch to messages you actually understand);
|
||||
- Documentation improvements.
|
||||
|
||||
## [0.2.1] - 2023-03-30
|
||||
|
||||
### Changed
|
||||
- this is fundamentally a working Lisp. The reader reads S-Expressions fully and M-Expressions at least partially. It is not (yet) a feature complete Lisp 1.5.
|
||||
|
|
277
LICENSE
277
LICENSE
|
@ -1,277 +0,0 @@
|
|||
Eclipse Public License - v 2.0
|
||||
|
||||
THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE
|
||||
PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION
|
||||
OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
|
||||
|
||||
1. DEFINITIONS
|
||||
|
||||
"Contribution" means:
|
||||
|
||||
a) in the case of the initial Contributor, the initial content
|
||||
Distributed under this Agreement, and
|
||||
|
||||
b) in the case of each subsequent Contributor:
|
||||
i) changes to the Program, and
|
||||
ii) additions to the Program;
|
||||
where such changes and/or additions to the Program originate from
|
||||
and are Distributed by that particular Contributor. A Contribution
|
||||
"originates" from a Contributor if it was added to the Program by
|
||||
such Contributor itself or anyone acting on such Contributor's behalf.
|
||||
Contributions do not include changes or additions to the Program that
|
||||
are not Modified Works.
|
||||
|
||||
"Contributor" means any person or entity that Distributes the Program.
|
||||
|
||||
"Licensed Patents" mean patent claims licensable by a Contributor which
|
||||
are necessarily infringed by the use or sale of its Contribution alone
|
||||
or when combined with the Program.
|
||||
|
||||
"Program" means the Contributions Distributed in accordance with this
|
||||
Agreement.
|
||||
|
||||
"Recipient" means anyone who receives the Program under this Agreement
|
||||
or any Secondary License (as applicable), including Contributors.
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source Code or other
|
||||
form, that is based on (or derived from) the Program and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship.
|
||||
|
||||
"Modified Works" shall mean any work in Source Code or other form that
|
||||
results from an addition to, deletion from, or modification of the
|
||||
contents of the Program, including, for purposes of clarity any new file
|
||||
in Source Code form that contains any contents of the Program. Modified
|
||||
Works shall not include works that contain only declarations,
|
||||
interfaces, types, classes, structures, or files of the Program solely
|
||||
in each case in order to link to, bind by name, or subclass the Program
|
||||
or Modified Works thereof.
|
||||
|
||||
"Distribute" means the acts of a) distributing or b) making available
|
||||
in any manner that enables the transfer of a copy.
|
||||
|
||||
"Source Code" means the form of a Program preferred for making
|
||||
modifications, including but not limited to software source code,
|
||||
documentation source, and configuration files.
|
||||
|
||||
"Secondary License" means either the GNU General Public License,
|
||||
Version 2.0, or any later versions of that license, including any
|
||||
exceptions or additional permissions as identified by the initial
|
||||
Contributor.
|
||||
|
||||
2. GRANT OF RIGHTS
|
||||
|
||||
a) Subject to the terms of this Agreement, each Contributor hereby
|
||||
grants Recipient a non-exclusive, worldwide, royalty-free copyright
|
||||
license to reproduce, prepare Derivative Works of, publicly display,
|
||||
publicly perform, Distribute and sublicense the Contribution of such
|
||||
Contributor, if any, and such Derivative Works.
|
||||
|
||||
b) Subject to the terms of this Agreement, each Contributor hereby
|
||||
grants Recipient a non-exclusive, worldwide, royalty-free patent
|
||||
license under Licensed Patents to make, use, sell, offer to sell,
|
||||
import and otherwise transfer the Contribution of such Contributor,
|
||||
if any, in Source Code or other form. This patent license shall
|
||||
apply to the combination of the Contribution and the Program if, at
|
||||
the time the Contribution is added by the Contributor, such addition
|
||||
of the Contribution causes such combination to be covered by the
|
||||
Licensed Patents. The patent license shall not apply to any other
|
||||
combinations which include the Contribution. No hardware per se is
|
||||
licensed hereunder.
|
||||
|
||||
c) Recipient understands that although each Contributor grants the
|
||||
licenses to its Contributions set forth herein, no assurances are
|
||||
provided by any Contributor that the Program does not infringe the
|
||||
patent or other intellectual property rights of any other entity.
|
||||
Each Contributor disclaims any liability to Recipient for claims
|
||||
brought by any other entity based on infringement of intellectual
|
||||
property rights or otherwise. As a condition to exercising the
|
||||
rights and licenses granted hereunder, each Recipient hereby
|
||||
assumes sole responsibility to secure any other intellectual
|
||||
property rights needed, if any. For example, if a third party
|
||||
patent license is required to allow Recipient to Distribute the
|
||||
Program, it is Recipient's responsibility to acquire that license
|
||||
before distributing the Program.
|
||||
|
||||
d) Each Contributor represents that to its knowledge it has
|
||||
sufficient copyright rights in its Contribution, if any, to grant
|
||||
the copyright license set forth in this Agreement.
|
||||
|
||||
e) Notwithstanding the terms of any Secondary License, no
|
||||
Contributor makes additional grants to any Recipient (other than
|
||||
those set forth in this Agreement) as a result of such Recipient's
|
||||
receipt of the Program under the terms of a Secondary License
|
||||
(if permitted under the terms of Section 3).
|
||||
|
||||
3. REQUIREMENTS
|
||||
|
||||
3.1 If a Contributor Distributes the Program in any form, then:
|
||||
|
||||
a) the Program must also be made available as Source Code, in
|
||||
accordance with section 3.2, and the Contributor must accompany
|
||||
the Program with a statement that the Source Code for the Program
|
||||
is available under this Agreement, and informs Recipients how to
|
||||
obtain it in a reasonable manner on or through a medium customarily
|
||||
used for software exchange; and
|
||||
|
||||
b) the Contributor may Distribute the Program under a license
|
||||
different than this Agreement, provided that such license:
|
||||
i) effectively disclaims on behalf of all other Contributors all
|
||||
warranties and conditions, express and implied, including
|
||||
warranties or conditions of title and non-infringement, and
|
||||
implied warranties or conditions of merchantability and fitness
|
||||
for a particular purpose;
|
||||
|
||||
ii) effectively excludes on behalf of all other Contributors all
|
||||
liability for damages, including direct, indirect, special,
|
||||
incidental and consequential damages, such as lost profits;
|
||||
|
||||
iii) does not attempt to limit or alter the recipients' rights
|
||||
in the Source Code under section 3.2; and
|
||||
|
||||
iv) requires any subsequent distribution of the Program by any
|
||||
party to be under a license that satisfies the requirements
|
||||
of this section 3.
|
||||
|
||||
3.2 When the Program is Distributed as Source Code:
|
||||
|
||||
a) it must be made available under this Agreement, or if the
|
||||
Program (i) is combined with other material in a separate file or
|
||||
files made available under a Secondary License, and (ii) the initial
|
||||
Contributor attached to the Source Code the notice described in
|
||||
Exhibit A of this Agreement, then the Program may be made available
|
||||
under the terms of such Secondary Licenses, and
|
||||
|
||||
b) a copy of this Agreement must be included with each copy of
|
||||
the Program.
|
||||
|
||||
3.3 Contributors may not remove or alter any copyright, patent,
|
||||
trademark, attribution notices, disclaimers of warranty, or limitations
|
||||
of liability ("notices") contained within the Program from any copy of
|
||||
the Program which they Distribute, provided that Contributors may add
|
||||
their own appropriate notices.
|
||||
|
||||
4. COMMERCIAL DISTRIBUTION
|
||||
|
||||
Commercial distributors of software may accept certain responsibilities
|
||||
with respect to end users, business partners and the like. While this
|
||||
license is intended to facilitate the commercial use of the Program,
|
||||
the Contributor who includes the Program in a commercial product
|
||||
offering should do so in a manner which does not create potential
|
||||
liability for other Contributors. Therefore, if a Contributor includes
|
||||
the Program in a commercial product offering, such Contributor
|
||||
("Commercial Contributor") hereby agrees to defend and indemnify every
|
||||
other Contributor ("Indemnified Contributor") against any losses,
|
||||
damages and costs (collectively "Losses") arising from claims, lawsuits
|
||||
and other legal actions brought by a third party against the Indemnified
|
||||
Contributor to the extent caused by the acts or omissions of such
|
||||
Commercial Contributor in connection with its distribution of the Program
|
||||
in a commercial product offering. The obligations in this section do not
|
||||
apply to any claims or Losses relating to any actual or alleged
|
||||
intellectual property infringement. In order to qualify, an Indemnified
|
||||
Contributor must: a) promptly notify the Commercial Contributor in
|
||||
writing of such claim, and b) allow the Commercial Contributor to control,
|
||||
and cooperate with the Commercial Contributor in, the defense and any
|
||||
related settlement negotiations. The Indemnified Contributor may
|
||||
participate in any such claim at its own expense.
|
||||
|
||||
For example, a Contributor might include the Program in a commercial
|
||||
product offering, Product X. That Contributor is then a Commercial
|
||||
Contributor. If that Commercial Contributor then makes performance
|
||||
claims, or offers warranties related to Product X, those performance
|
||||
claims and warranties are such Commercial Contributor's responsibility
|
||||
alone. Under this section, the Commercial Contributor would have to
|
||||
defend claims against the other Contributors related to those performance
|
||||
claims and warranties, and if a court requires any other Contributor to
|
||||
pay any damages as a result, the Commercial Contributor must pay
|
||||
those damages.
|
||||
|
||||
5. NO WARRANTY
|
||||
|
||||
EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT
|
||||
PERMITTED BY APPLICABLE LAW, THE PROGRAM IS PROVIDED ON AN "AS IS"
|
||||
BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR
|
||||
IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF
|
||||
TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR
|
||||
PURPOSE. Each Recipient is solely responsible for determining the
|
||||
appropriateness of using and distributing the Program and assumes all
|
||||
risks associated with its exercise of rights under this Agreement,
|
||||
including but not limited to the risks and costs of program errors,
|
||||
compliance with applicable laws, damage to or loss of data, programs
|
||||
or equipment, and unavailability or interruption of operations.
|
||||
|
||||
6. DISCLAIMER OF LIABILITY
|
||||
|
||||
EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT
|
||||
PERMITTED BY APPLICABLE LAW, NEITHER RECIPIENT NOR ANY CONTRIBUTORS
|
||||
SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST
|
||||
PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE
|
||||
EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
7. GENERAL
|
||||
|
||||
If any provision of this Agreement is invalid or unenforceable under
|
||||
applicable law, it shall not affect the validity or enforceability of
|
||||
the remainder of the terms of this Agreement, and without further
|
||||
action by the parties hereto, such provision shall be reformed to the
|
||||
minimum extent necessary to make such provision valid and enforceable.
|
||||
|
||||
If Recipient institutes patent litigation against any entity
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that the
|
||||
Program itself (excluding combinations of the Program with other software
|
||||
or hardware) infringes such Recipient's patent(s), then such Recipient's
|
||||
rights granted under Section 2(b) shall terminate as of the date such
|
||||
litigation is filed.
|
||||
|
||||
All Recipient's rights under this Agreement shall terminate if it
|
||||
fails to comply with any of the material terms or conditions of this
|
||||
Agreement and does not cure such failure in a reasonable period of
|
||||
time after becoming aware of such noncompliance. If all Recipient's
|
||||
rights under this Agreement terminate, Recipient agrees to cease use
|
||||
and distribution of the Program as soon as reasonably practicable.
|
||||
However, Recipient's obligations under this Agreement and any licenses
|
||||
granted by Recipient relating to the Program shall continue and survive.
|
||||
|
||||
Everyone is permitted to copy and distribute copies of this Agreement,
|
||||
but in order to avoid inconsistency the Agreement is copyrighted and
|
||||
may only be modified in the following manner. The Agreement Steward
|
||||
reserves the right to publish new versions (including revisions) of
|
||||
this Agreement from time to time. No one other than the Agreement
|
||||
Steward has the right to modify this Agreement. The Eclipse Foundation
|
||||
is the initial Agreement Steward. The Eclipse Foundation may assign the
|
||||
responsibility to serve as the Agreement Steward to a suitable separate
|
||||
entity. Each new version of the Agreement will be given a distinguishing
|
||||
version number. The Program (including Contributions) may always be
|
||||
Distributed subject to the version of the Agreement under which it was
|
||||
received. In addition, after a new version of the Agreement is published,
|
||||
Contributor may elect to Distribute the Program (including its
|
||||
Contributions) under the new version.
|
||||
|
||||
Except as expressly stated in Sections 2(a) and 2(b) above, Recipient
|
||||
receives no rights or licenses to the intellectual property of any
|
||||
Contributor under this Agreement, whether expressly, by implication,
|
||||
estoppel or otherwise. All rights in the Program not expressly granted
|
||||
under this Agreement are reserved. Nothing in this Agreement is intended
|
||||
to be enforceable by any entity that is not a Contributor or Recipient.
|
||||
No third-party beneficiary rights are created under this Agreement.
|
||||
|
||||
Exhibit A - Form of Secondary Licenses Notice
|
||||
|
||||
"This Source Code may also be made available under the following
|
||||
Secondary Licenses when the conditions for such availability set forth
|
||||
in the Eclipse Public License, v. 2.0 are satisfied: {name license(s),
|
||||
version(s), and exceptions or additional permissions here}."
|
||||
|
||||
Simply including a copy of this Agreement, including this Exhibit A
|
||||
is not sufficient to license the Source Code under Secondary Licenses.
|
||||
|
||||
If it is not possible or desirable to put the notice in a particular
|
||||
file, then You may include the notice in a location (such as a LICENSE
|
||||
file in a relevant directory) where a recipient would be likely to
|
||||
look for such a notice.
|
||||
|
||||
You may add additional accurate notices of copyright ownership.
|
333
LICENSE.md
Normal file
333
LICENSE.md
Normal file
|
@ -0,0 +1,333 @@
|
|||
### GNU GENERAL PUBLIC LICENSE
|
||||
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
### Preamble
|
||||
|
||||
The licenses for most software are designed to take away your freedom
|
||||
to share and change it. By contrast, the GNU General Public License is
|
||||
intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if
|
||||
you distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on,
|
||||
we want its recipients to know that what they have is not the
|
||||
original, so that any problems introduced by others will not reflect
|
||||
on the original authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at
|
||||
all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
### TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
**0.** This License applies to any program or other work which
|
||||
contains a notice placed by the copyright holder saying it may be
|
||||
distributed under the terms of this General Public License. The
|
||||
"Program", below, refers to any such program or work, and a "work
|
||||
based on the Program" means either the Program or any derivative work
|
||||
under copyright law: that is to say, a work containing the Program or
|
||||
a portion of it, either verbatim or with modifications and/or
|
||||
translated into another language. (Hereinafter, translation is
|
||||
included without limitation in the term "modification".) Each licensee
|
||||
is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the Program
|
||||
(independent of having been made by running the Program). Whether that
|
||||
is true depends on what the Program does.
|
||||
|
||||
**1.** You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a
|
||||
fee.
|
||||
|
||||
**2.** You may modify your copy or copies of the Program or any
|
||||
portion of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
**a)** You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
**b)** You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any part
|
||||
thereof, to be licensed as a whole at no charge to all third parties
|
||||
under the terms of this License.
|
||||
|
||||
**c)** If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such interactive
|
||||
use in the most ordinary way, to print or display an announcement
|
||||
including an appropriate copyright notice and a notice that there is
|
||||
no warranty (or else, saying that you provide a warranty) and that
|
||||
users may redistribute the program under these conditions, and telling
|
||||
the user how to view a copy of this License. (Exception: if the
|
||||
Program itself is interactive but does not normally print such an
|
||||
announcement, your work based on the Program is not required to print
|
||||
an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote
|
||||
it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
**3.** You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
**a)** Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections 1
|
||||
and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
**b)** Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your cost of
|
||||
physically performing source distribution, a complete machine-readable
|
||||
copy of the corresponding source code, to be distributed under the
|
||||
terms of Sections 1 and 2 above on a medium customarily used for
|
||||
software interchange; or,
|
||||
|
||||
**c)** Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is allowed
|
||||
only for noncommercial distribution and only if you received the
|
||||
program in object code or executable form with such an offer, in
|
||||
accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
**4.** You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt otherwise
|
||||
to copy, modify, sublicense or distribute the Program is void, and
|
||||
will automatically terminate your rights under this License. However,
|
||||
parties who have received copies, or rights, from you under this
|
||||
License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
**5.** You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
**6.** Each time you redistribute the Program (or any work based on
|
||||
the Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
**7.** If, as a consequence of a court judgment or allegation of
|
||||
patent infringement or for any other reason (not limited to patent
|
||||
issues), conditions are imposed on you (whether by court order,
|
||||
agreement or otherwise) that contradict the conditions of this
|
||||
License, they do not excuse you from the conditions of this License.
|
||||
If you cannot distribute so as to satisfy simultaneously your
|
||||
obligations under this License and any other pertinent obligations,
|
||||
then as a consequence you may not distribute the Program at all. For
|
||||
example, if a patent license would not permit royalty-free
|
||||
redistribution of the Program by all those who receive copies directly
|
||||
or indirectly through you, then the only way you could satisfy both it
|
||||
and this License would be to refrain entirely from distribution of the
|
||||
Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
**8.** If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
**9.** The Free Software Foundation may publish revised and/or new
|
||||
versions of the General Public License from time to time. Such new
|
||||
versions will be similar in spirit to the present version, but may
|
||||
differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and
|
||||
"any later version", you have the option of following the terms and
|
||||
conditions either of that version or of any later version published by
|
||||
the Free Software Foundation. If the Program does not specify a
|
||||
version number of this License, you may choose any version ever
|
||||
published by the Free Software Foundation.
|
||||
|
||||
**10.** If you wish to incorporate parts of the Program into other
|
||||
free programs whose distribution conditions are different, write to
|
||||
the author to ask for permission. For software which is copyrighted by
|
||||
the Free Software Foundation, write to the Free Software Foundation;
|
||||
we sometimes make exceptions for this. Our decision will be guided by
|
||||
the two goals of preserving the free status of all derivatives of our
|
||||
free software and of promoting the sharing and reuse of software
|
||||
generally.
|
||||
|
||||
**NO WARRANTY**
|
||||
|
||||
**11.** BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO
|
||||
WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
||||
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
|
||||
OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY
|
||||
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
|
||||
PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME
|
||||
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
**12.** IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
|
||||
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
|
||||
AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU
|
||||
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
|
||||
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
|
||||
PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
|
||||
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
|
||||
FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF
|
||||
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
DAMAGES.
|
||||
|
||||
### END OF TERMS AND CONDITIONS
|
||||
|
||||
### How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these
|
||||
terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest to
|
||||
attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
|
||||
|
||||
Also add information on how to contact you by electronic and paper
|
||||
mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details
|
||||
type `show w'. This is free software, and you are welcome
|
||||
to redistribute it under certain conditions; type `show c'
|
||||
for details.
|
||||
|
||||
The hypothetical commands \`show w' and \`show c' should show the
|
||||
appropriate parts of the General Public License. Of course, the
|
||||
commands you use may be called something other than \`show w' and
|
||||
\`show c'; they could even be mouse-clicks or menu items--whatever
|
||||
suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or
|
||||
your school, if any, to sign a "copyright disclaimer" for the program,
|
||||
if necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright
|
||||
interest in the program `Gnomovision'
|
||||
(which makes passes at compilers) written
|
||||
by James Hacker.
|
||||
|
||||
signature of Ty Coon, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
282
README.md
282
README.md
|
@ -1,6 +1,37 @@
|
|||
# beowulf
|
||||
|
||||
LISP 1.5 is to all Lisp dialects as Beowulf is to Emglish literature.
|
||||
## Þý liste cræfte spræc
|
||||
|
||||
LISP 1.5 is to all Lisp dialects as Beowulf is to English literature.
|
||||
|
||||

|
||||
|
||||
## Contents
|
||||
* [What this is](#what-this-is)
|
||||
+ [Status](#status)
|
||||
+ [BUT WHY?!!?!](#but-why-----)
|
||||
+ [Project Target](#project-target)
|
||||
+ [Invoking](#invoking)
|
||||
+ [Building and Invoking](#building-and-invoking)
|
||||
+ [Reader macros](#reader-macros)
|
||||
+ [Functions and symbols implemented](#functions-and-symbols-implemented)
|
||||
+ [Architectural plan](#architectural-plan)
|
||||
- [resources/lisp1.5.lsp](#resources-lisp15lsp)
|
||||
- [beowulf/boostrap.clj](#beowulf-boostrapclj)
|
||||
- [beowulf/host.clj](#beowulf-hostclj)
|
||||
- [beowulf/read.clj](#beowulf-readclj)
|
||||
+ [Commentary](#commentary)
|
||||
* [Installation](#installation)
|
||||
+ [Input/output](#input-output)
|
||||
- [SYSOUT](#sysout)
|
||||
- [SYSIN](#sysin)
|
||||
* [Learning Lisp 1.5](#learning-lisp-15)
|
||||
* [Other Lisp 1.5 resources](#other-lisp-15-resources)
|
||||
+ [Other implmentations](#other-implementations)
|
||||
+ [History resources](#history-resources)
|
||||
* [License](#license)
|
||||
|
||||
<small><i><a href='http://ecotrust-canada.github.io/markdown-toc/'>Table of contents generated with markdown-toc</a></i></small>
|
||||
|
||||
## What this is
|
||||
|
||||
|
@ -9,24 +40,44 @@ objective is to build a complete and accurate implementation of Lisp 1.5
|
|||
as described in the manual, with, in so far as is possible, exactly the
|
||||
same bahaviour - except as documented below.
|
||||
|
||||
### BUT WHY?!!?!
|
||||
|
||||
Because.
|
||||
|
||||
Because Lisp is the only computer language worth learning, and if a thing
|
||||
is worth learning, it's worth learning properly; which means going back to
|
||||
the beginning and trying to understand that.
|
||||
|
||||
Because there is, so far as I know, no working implementation of Lisp 1.5
|
||||
for modern machines.
|
||||
|
||||
Because I'm barking mad, and this is therapy.
|
||||
|
||||
### Status
|
||||
|
||||
Boots to REPL, but few functions yet available.
|
||||
Working Lisp interpreter, but some key features not yet implemented.
|
||||
|
||||
* [Project website](https://simon-brooke.github.io/beowulf/).
|
||||
* [Source code documentation](https://simon-brooke.github.io/beowulf/docs/codox/index.html).
|
||||
* [Test Coverage Report](https://simon-brooke.github.io/beowulf/docs/cloverage/index.html)
|
||||
|
||||
### Project Target
|
||||
|
||||
### Building and Invoking
|
||||
The project target is to be able to run the [Wang algorithm for the propositional calculus](https://www.softwarepreservation.org/projects/LISP/book/LISP%201.5%20Programmers%20Manual.pdf#page=52) given in chapter 8 of the *Lisp 1.5 Programmer's Manual*. When that runs, the project is as far as I am concerned feature complete. I may keep tinkering with it after that and I'll certainly accept pull requests which are in the spirit of the project (i.e. making Beowulf more usable, and/or implementing parts of Lisp 1.5 which I have not implemented), but this isn't intended to be a new language for doing real work; it's an
|
||||
educational and archaeological project, not serious engineering.
|
||||
|
||||
Build with
|
||||
Some `readline`-like functionality would be really useful, but my attempt to
|
||||
integrate [JLine](https://github.com/jline/jline3) has not (yet) been successful.
|
||||
|
||||
lein uberjar
|
||||
An in-core structure editor would be an extremely nice thing, and I may well
|
||||
implement one.
|
||||
|
||||
You are of course welcome to fork the project and do whatever you like with it!
|
||||
|
||||
### Invoking
|
||||
|
||||
Invoke with
|
||||
|
||||
java -jar target/uberjar/beowulf-0.2.1-SNAPSHOT-standalone.jar --help
|
||||
java -jar target/uberjar/beowulf-0.3.0-SNAPSHOT-standalone.jar --help
|
||||
|
||||
(Obviously, check your version number)
|
||||
|
||||
|
@ -35,71 +86,136 @@ Command line arguments as follows:
|
|||
```
|
||||
-h, --help Print this message
|
||||
-p PROMPT, --prompt PROMPT Set the REPL prompt to PROMPT
|
||||
-r INITFILE, --read INITFILE Read Lisp functions from the file INITFILE
|
||||
-s, --strict Strictly interpret the Lisp 1.5 language, without extensions.
|
||||
-r INITFILE, --read SYSOUTFILE Read Lisp sysout from the file SYSOUTFILE
|
||||
(defaults to `resources/lisp1.5.lsp`)
|
||||
-s, --strict Strictly interpret the Lisp 1.5 language,
|
||||
without extensions.
|
||||
```
|
||||
|
||||
To end a session, type `STOP` at the command prompt.
|
||||
|
||||
### Building and Invoking
|
||||
|
||||
Build with
|
||||
|
||||
lein uberjar
|
||||
|
||||
|
||||
### Reader macros
|
||||
|
||||
Currently `SETQ` and `DEFUN` are implemented as reader macros, sort of. It would
|
||||
now be possible to reimplement them as `FEXPRs` and so the reader macro functionality will probably go away.
|
||||
|
||||
### Functions and symbols implemented
|
||||
|
||||
The following functions and symbols are implemented:
|
||||
| Function | Type | Signature | Implementation | Documentation |
|
||||
|--------------|----------------|------------------|----------------|----------------------|
|
||||
| NIL | Lisp variable | ? | | see manual pages <a href='#page22'>22</a>, <a href='#page69'>69</a> |
|
||||
| T | Lisp variable | ? | | see manual pages <a href='#page22'>22</a>, <a href='#page69'>69</a> |
|
||||
| F | Lisp variable | ? | | see manual pages <a href='#page22'>22</a>, <a href='#page69'>69</a> |
|
||||
| ADD1 | Host lambda function | ? | | ? |
|
||||
| AND | Host lambda function | ? | PREDICATE | `T` if and only if none of my `args` evaluate to either `F` or `NIL`, else `F`. In `beowulf.host` principally because I don't yet feel confident to define varargs functions in Lisp. |
|
||||
| APPEND | Lisp lambda function | ? | | see manual pages <a href='#page11'>11</a>, <a href='#page61'>61</a> |
|
||||
| APPLY | Host lambda function | ? | | Apply this `function` to these `arguments` in this `environment` and return the result. For bootstrapping, at least, a version of APPLY written in Clojure. All args are assumed to be symbols or `beowulf.cons-cell/ConsCell` objects. See page 13 of the Lisp 1.5 Programmers Manual. |
|
||||
| ASSOC | Lisp lambda function, Host lambda function | ? | ? | If a is an association list such as the one formed by PAIRLIS in the above example, then assoc will produce the first pair whose first term is x. Thus it is a table searching function. All args are assumed to be `beowulf.cons-cell/ConsCell` objects. See page 12 of the Lisp 1.5 Programmers Manual. **NOTE THAT** this function is overridden by an implementation in Lisp, but is currently still present for bootstrapping. |
|
||||
| ATOM | Host lambda function | ? | PREDICATE | Returns `T` if and only if the argument `x` is bound to an atom; else `F`. It is not clear to me from the documentation whether `(ATOM 7)` should return `T` or `F`. I'm going to assume `T`. |
|
||||
| CAR | Host lambda function | ? | | Return the item indicated by the first pointer of a pair. NIL is treated specially: the CAR of NIL is NIL. |
|
||||
| CAAAAR | Lisp lambda function | ? | ? | ? |
|
||||
| CAAADR | Lisp lambda function | ? | ? | ? |
|
||||
| CAAAR | Lisp lambda function | ? | ? | ? |
|
||||
| CAADAR | Lisp lambda function | ? | ? | ? |
|
||||
| CAADDR | Lisp lambda function | ? | ? | ? |
|
||||
| CAADR | Lisp lambda function | ? | ? | ? |
|
||||
| CAAR | Lisp lambda function | ? | ? | ? |
|
||||
| CADAAR | Lisp lambda function | ? | ? | ? |
|
||||
| CADADR | Lisp lambda function | ? | ? | ? |
|
||||
| CADAR | Lisp lambda function | ? | ? | ? |
|
||||
| CADDAR | Lisp lambda function | ? | ? | ? |
|
||||
| CADDDR | Lisp lambda function | ? | ? | ? |
|
||||
| CADDR | Lisp lambda function | ? | ? | ? |
|
||||
| CADR | Lisp lambda function | ? | ? | ? |
|
||||
| CDAAAR | Lisp lambda function | ? | ? | ? |
|
||||
| CDAADR | Lisp lambda function | ? | ? | ? |
|
||||
| CDAAR | Lisp lambda function | ? | ? | ? |
|
||||
| CDADAR | Lisp lambda function | ? | ? | ? |
|
||||
| CDADDR | Lisp lambda function | ? | ? | ? |
|
||||
| CDADR | Lisp lambda function | ? | ? | ? |
|
||||
| CDAR | Lisp lambda function | ? | ? | ? |
|
||||
| CDDAAR | Lisp lambda function | ? | ? | ? |
|
||||
| CDDADR | Lisp lambda function | ? | ? | ? |
|
||||
| CDDAR | Lisp lambda function | ? | ? | ? |
|
||||
| CDDDAR | Lisp lambda function | ? | ? | ? |
|
||||
| CDDDDR | Lisp lambda function | ? | ? | ? |
|
||||
| CDDDR | Lisp lambda function | ? | ? | ? |
|
||||
| CDDR | Lisp lambda function | ? | ? | ? |
|
||||
| CDR | Host lambda function | ? | | Return the item indicated by the second pointer of a pair. NIL is treated specially: the CDR of NIL is NIL. |
|
||||
| CONS | Host lambda function | ? | | Construct a new instance of cons cell with this `car` and `cdr`. |
|
||||
| CONSP | Host lambda function | ? | ? | Return `T` if object `o` is a cons cell, else `F`. **NOTE THAT** this is an extension function, not available in strct mode. I believe that Lisp 1.5 did not have any mechanism for testing whether an argument was, or was not, a cons cell. |
|
||||
| COPY | Lisp lambda function | ? | | see manual pages <a href='#page62'>62</a> |
|
||||
| DEFINE | Host lambda function | ? | PSEUDO-FUNCTION | Bootstrap-only version of `DEFINE` which, post boostrap, can be overwritten in LISP. The single argument to `DEFINE` should be an association list of symbols to lambda functions. See page 58 of the manual. |
|
||||
| DIFFERENCE | Host lambda function | ? | | ? |
|
||||
| DIVIDE | Lisp lambda function | ? | | see manual pages <a href='#page26'>26</a>, <a href='#page64'>64</a> |
|
||||
| DOC | Host lambda function | ? | ? | Open the page for this `symbol` in the Lisp 1.5 manual, if known, in the default web browser. **NOTE THAT** this is an extension function, not available in strct mode. |
|
||||
| EFFACE | Lisp lambda function | ? | PSEUDO-FUNCTION | see manual pages <a href='#page63'>63</a> |
|
||||
| ERROR | Host lambda function | ? | PSEUDO-FUNCTION | Throw an error |
|
||||
| EQ | Host lambda function | ? | PREDICATE | Returns `T` if and only if both `x` and `y` are bound to the same atom, else `NIL`. |
|
||||
| EQUAL | Host lambda function | ? | PREDICATE | This is a predicate that is true if its two arguments are identical S-expressions, and false if they are different. (The elementary predicate `EQ` is defined only for atomic arguments.) The definition of `EQUAL` is an example of a conditional expression inside a conditional expression. NOTE: returns `F` on failure, not `NIL` |
|
||||
| EVAL | Host lambda function | ? | | Evaluate this `expr` and return the result. If `environment` is not passed, it defaults to the current value of the global object list. The `depth` argument is part of the tracing system and should not be set by user code. All args are assumed to be numbers, symbols or `beowulf.cons-cell/ConsCell` objects. However, if called with just a single arg, `expr`, I'll assume it's being called from the Clojure REPL and will coerce the `expr` to `ConsCell`. |
|
||||
| FACTORIAL | Lisp lambda function | ? | ? | ? |
|
||||
| FIXP | Host lambda function | ? | PREDICATE | ? |
|
||||
| GENSYM | Host lambda function | ? | | Generate a unique symbol. |
|
||||
| GET | Host lambda function | ? | | From the manual: '`get` is somewhat like `prop`; however its value is car of the rest of the list if the `indicator` is found, and NIL otherwise.' It's clear that `GET` is expected to be defined in terms of `PROP`, but we can't implement `PROP` here because we lack `EVAL`; and we can't have `EVAL` here because both it and `APPLY` depends on `GET`. OK, It's worse than that: the statement of the definition of `GET` (and of) `PROP` on page 59 says that the first argument to each must be a list; But the in the definition of `ASSOC` on page 70, when `GET` is called its first argument is always an atom. Since it's `ASSOC` and `EVAL` which I need to make work, I'm going to assume that page 59 is wrong. |
|
||||
| GREATERP | Host lambda function | ? | PREDICATE | ? |
|
||||
| INTEROP | Host lambda function | ? | ? | ? |
|
||||
| INTERSECTION | Lisp lambda function | ? | ? | ? |
|
||||
| LENGTH | Lisp lambda function | ? | | see manual pages <a href='#page62'>62</a> |
|
||||
| LESSP | Host lambda function | ? | PREDICATE | ? |
|
||||
| MAPLIST | Lisp lambda function | ? | FUNCTIONAL | see manual pages <a href='#page20'>20</a>, <a href='#page21'>21</a>, <a href='#page63'>63</a> |
|
||||
| MEMBER | Lisp lambda function | ? | PREDICATE | see manual pages <a href='#page11'>11</a>, <a href='#page62'>62</a> |
|
||||
| MINUSP | Lisp lambda function | ? | PREDICATE | see manual pages <a href='#page26'>26</a>, <a href='#page64'>64</a> |
|
||||
| NOT | Lisp lambda function | ? | PREDICATE | see manual pages <a href='#page21'>21</a>, <a href='#page23'>23</a>, <a href='#page58'>58</a> |
|
||||
| NULL | Lisp lambda function | ? | PREDICATE | see manual pages <a href='#page11'>11</a>, <a href='#page57'>57</a> |
|
||||
| NUMBERP | Host lambda function | ? | PREDICATE | ? |
|
||||
| OBLIST | Host lambda function | ? | | Return a list of the symbols currently bound on the object list. **NOTE THAT** in the Lisp 1.5 manual, footnote at the bottom of page 69, it implies that an argument can be passed but I'm not sure of the semantics of this. |
|
||||
| ONEP | Lisp lambda function | ? | PREDICATE | see manual pages <a href='#page26'>26</a>, <a href='#page64'>64</a> |
|
||||
| OR | Host lambda function | ? | PREDICATE | `T` if and only if at least one of my `args` evaluates to something other than either `F` or `NIL`, else `F`. In `beowulf.host` principally because I don't yet feel confident to define varargs functions in Lisp. |
|
||||
| PAIR | Lisp lambda function | ? | | see manual pages <a href='#page60'>60</a> |
|
||||
| PAIRLIS | Lisp lambda function, Host lambda function | ? | ? | This function gives the list of pairs of corresponding elements of the lists `x` and `y`, and APPENDs this to the list `a`. The resultant list of pairs, which is like a table with two columns, is called an association list. Eessentially, it builds the environment on the stack, implementing shallow binding. All args are assumed to be `beowulf.cons-cell/ConsCell` objects. See page 12 of the Lisp 1.5 Programmers Manual. **NOTE THAT** this function is overridden by an implementation in Lisp, but is currently still present for bootstrapping. |
|
||||
| PLUS | Host lambda function | ? | | ? |
|
||||
| PRETTY | | ? | ? | ? |
|
||||
| PRINT | | ? | PSEUDO-FUNCTION | see manual pages <a href='#page65'>65</a>, <a href='#page84'>84</a> |
|
||||
| PROG | Host nlambda function | ? | | The accursed `PROG` feature. See page 71 of the manual. Lisp 1.5 introduced `PROG`, and most Lisps have been stuck with it ever since. It introduces imperative programming into what should be a pure functional language, and consequently it's going to be a pig to implement. Broadly, `PROG` is a variadic pseudo function called as a `FEXPR` (or possibly an `FSUBR`, although I'm not presently sure that would even work.) The arguments, which are unevaluated, are a list of forms, the first of which is expected to be a list of symbols which will be treated as names of variables within the program, and the rest of which (the 'program body') are either lists or symbols. Lists are treated as Lisp expressions which may be evaulated in turn. Symbols are treated as targets for the `GO` statement. **GO:** A `GO` statement takes the form of `(GO target)`, where `target` should be one of the symbols which occur at top level among that particular invocation of `PROG`s arguments. A `GO` statement may occur at top level in a PROG, or in a clause of a `COND` statement in a `PROG`, but not in a function called from the `PROG` statement. When a `GO` statement is evaluated, execution should transfer immediately to the expression which is the argument list immediately following the symbol which is its target. If the target is not found, an error with the code `A6` should be thrown. **RETURN:** A `RETURN` statement takes the form `(RETURN value)`, where `value` is any value. Following the evaluation of a `RETURN` statement, the `PROG` should immediately exit without executing any further expressions, returning the value. **SET and SETQ:** In addition to the above, if a `SET` or `SETQ` expression is encountered in any expression within the `PROG` body, it should affect not the global object list but instead only the local variables of the program. **COND:** In **strict** mode, when in normal execution, a `COND` statement none of whose clauses match should not return `NIL` but should throw an error with the code `A3`... *except* that inside a `PROG` body, it should not do so. *sigh*. **Flow of control:** Apart from the exceptions specified above, expressions in the program body are evaluated sequentially. If execution reaches the end of the program body, `NIL` is returned. Got all that? Good. |
|
||||
| PROP | Lisp lambda function | ? | FUNCTIONAL | see manual pages <a href='#page59'>59</a> |
|
||||
| QUOTE | Lisp lambda function | ? | | see manual pages <a href='#page10'>10</a>, <a href='#page22'>22</a>, <a href='#page71'>71</a> |
|
||||
| QUOTIENT | Host lambda function | ? | | I'm not certain from the documentation whether Lisp 1.5 `QUOTIENT` returned the integer part of the quotient, or a realnum representing the whole quotient. I am for now implementing the latter. |
|
||||
| RANGE | Lisp lambda function | ? | ? | ? |
|
||||
| READ | Host lambda function | ? | PSEUDO-FUNCTION | An implementation of a Lisp reader sufficient for bootstrapping; not necessarily the final Lisp reader. `input` should be either a string representation of a LISP expression, or else an input stream. A single form will be read. |
|
||||
| REMAINDER | Host lambda function | ? | | ? |
|
||||
| REPEAT | Lisp lambda function | ? | ? | ? |
|
||||
| RPLACA | Host lambda function | ? | PSEUDO-FUNCTION | Replace the CAR pointer of this `cell` with this `value`. Dangerous, should really not exist, but does in Lisp 1.5 (and was important for some performance hacks in early Lisps) |
|
||||
| RPLACD | Host lambda function | ? | PSEUDO-FUNCTION | Replace the CDR pointer of this `cell` with this `value`. Dangerous, should really not exist, but does in Lisp 1.5 (and was important for some performance hacks in early Lisps) |
|
||||
| SEARCH | Lisp lambda function | ? | FUNCTIONAL | see manual pages <a href='#page63'>63</a> |
|
||||
| SET | Host lambda function | ? | PSEUDO-FUNCTION | Implementation of SET in Clojure. Add to the `oblist` a binding of the value of `var` to the value of `val`. NOTE WELL: this is not SETQ! |
|
||||
| SUB1 | Lisp lambda function, Host lambda function | ? | | ? |
|
||||
| SUB2 | Lisp lambda function | ? | ? | ? |
|
||||
| SUBLIS | Lisp lambda function | ? | | see manual pages <a href='#page12'>12</a>, <a href='#page61'>61</a> |
|
||||
| SUBST | Lisp lambda function | ? | | see manual pages <a href='#page11'>11</a>, <a href='#page61'>61</a> |
|
||||
| SYSIN | Host lambda function | ? | ? | Read the contents of the file at this `filename` into the object list. If the file is not a valid Beowulf sysout file, this will probably corrupt the system, you have been warned. File paths will be considered relative to the filepath set when starting Lisp. It is intended that sysout files can be read both from resources within the jar file, and from the file system. If a named file exists in both the file system and the resources, the file system will be preferred. **NOTE THAT** if the provided `filename` does not end with `.lsp` (which, if you're writing it from the Lisp REPL, it won't), the extension `.lsp` will be appended. **NOTE THAT** this is an extension function, not available in strct mode. |
|
||||
| SYSOUT | Host lambda function | ? | ? | Dump the current content of the object list to file. If no `filepath` is specified, a file name will be constructed of the symbol `Sysout` and the current date. File paths will be considered relative to the filepath set when starting Lisp. **NOTE THAT** this is an extension function, not available in strct mode. |
|
||||
| TERPRI | | ? | PSEUDO-FUNCTION | see manual pages <a href='#page65'>65</a>, <a href='#page84'>84</a> |
|
||||
| TIMES | Host lambda function | ? | | ? |
|
||||
| TRACE | Host lambda function | ? | PSEUDO-FUNCTION | Add this `s` to the set of symbols currently being traced. If `s` is not a symbol or sequence of symbols, does nothing. |
|
||||
| UNION | Lisp lambda function | ? | ? | ? |
|
||||
| UNTRACE | Host lambda function | ? | PSEUDO-FUNCTION | Remove this `s` from the set of symbols currently being traced. If `s` is not a symbol or sequence of symbols, does nothing. |
|
||||
| ZEROP | Lisp lambda function | ? | PREDICATE | see manual pages <a href='#page26'>26</a>, <a href='#page64'>64</a> |
|
||||
|
||||
| Symbol | Type | Signature | Documentation |
|
||||
|--------|------|-----------|---------------|
|
||||
| NIL | ? | null | ? |
|
||||
| T | ? | null | ? |
|
||||
| F | ? | null | ? |
|
||||
| ADD1 | Host function | ([x]) | ? |
|
||||
| AND | Host function | ([& args]) | `T` if and only if none of my `args` evaluate to either `F` or `NIL`, else `F`. In `beowulf.host` principally because I don't yet feel confident to define varargs functions in Lisp. |
|
||||
| APPEND | Host function | ([x y]) | Append the the elements of `y` to the elements of `x`. All args are assumed to be `beowulf.cons-cell/ConsCell` objects. See page 11 of the Lisp 1.5 Programmers Manual. |
|
||||
| APPLY | Host function | ([function args environment depth]) | Apply this `function` to these `arguments` in this `environment` and return the result. For bootstrapping, at least, a version of APPLY written in Clojure. All args are assumed to be symbols or `beowulf.cons-cell/ConsCell` objects. See page 13 of the Lisp 1.5 Programmers Manual. |
|
||||
| ATOM | Host function | ([x]) | Returns `T` if and only if the argument `x` is bound to an atom; else `F`. It is not clear to me from the documentation whether `(ATOM 7)` should return `T` or `F`. I'm going to assume `T`. |
|
||||
| CAR | ? | null | ? |
|
||||
| CDR | ? | null | ? |
|
||||
| CONS | ? | null | ? |
|
||||
| COPY | Lisp function | (X) | ? |
|
||||
| DEFINE | Host function | ([args]) | Bootstrap-only version of `DEFINE` which, post boostrap, can be overwritten in LISP. The single argument to `DEFINE` should be an assoc list which should be nconc'ed onto the front of the oblist. Broadly, (SETQ OBLIST (NCONC ARG1 OBLIST)) |
|
||||
| DIFFERENCE | Host function | ([x y]) | ? |
|
||||
| DIVIDE | Lisp function | (X Y) | ? |
|
||||
| ERROR | Host function | ([& args]) | Throw an error |
|
||||
| EQ | Host function | ([x y]) | Returns `T` if and only if both `x` and `y` are bound to the same atom, else `NIL`. |
|
||||
| EQUAL | Host function | ([x y]) | This is a predicate that is true if its two arguments are identical S-expressions, and false if they are different. (The elementary predicate `EQ` is defined only for atomic arguments.) The definition of `EQUAL` is an example of a conditional expression inside a conditional expression. NOTE: returns `F` on failure, not `NIL` |
|
||||
| EVAL | Host function | ([expr] [expr env depth]) | Evaluate this `expr` and return the result. If `environment` is not passed, it defaults to the current value of the global object list. The `depth` argument is part of the tracing system and should not be set by user code. All args are assumed to be numbers, symbols or `beowulf.cons-cell/ConsCell` objects. |
|
||||
| FIXP | Host function | ([x]) | ? |
|
||||
| GENSYM | Host function | ([]) | Generate a unique symbol. |
|
||||
| GET | Lisp function | (X Y) | ? |
|
||||
| GREATERP | Host function | ([x y]) | ? |
|
||||
| INTEROP | Host function | ([fn-symbol args]) | Clojure (or other host environment) interoperation API. `fn-symbol` is expected to be either 1. a symbol bound in the host environment to a function; or 2. a sequence (list) of symbols forming a qualified path name bound to a function. Lower case characters cannot normally be represented in Lisp 1.5, so both the upper case and lower case variants of `fn-symbol` will be tried. If the function you're looking for has a mixed case name, that is not currently accessible. `args` is expected to be a Lisp 1.5 list of arguments to be passed to that function. Return value must be something acceptable to Lisp 1.5, so either a symbol, a number, or a Lisp 1.5 list. If `fn-symbol` is not found (even when cast to lower case), or is not a function, or the value returned cannot be represented in Lisp 1.5, an exception is thrown with `:cause` bound to `:interop` and `:detail` set to a value representing the actual problem. |
|
||||
| INTERSECTION | Lisp function | (X Y) | ? |
|
||||
| LENGTH | Lisp function | (L) | ? |
|
||||
| LESSP | Host function | ([x y]) | ? |
|
||||
| MEMBER | Lisp function | (A X) | ? |
|
||||
| MINUSP | Lisp function | (X) | ? |
|
||||
| NULL | Lisp function | (X) | ? |
|
||||
| NUMBERP | Host function | ([x]) | ? |
|
||||
| OBLIST | Host function | ([]) | Return a list of the symbols currently bound on the object list. **NOTE THAT** in the Lisp 1.5 manual, footnote at the bottom of page 69, it implies that an argument can be passed but I'm not sure of the semantics of this. |
|
||||
| ONEP | Lisp function | (X) | ? |
|
||||
| PAIR | Lisp function | (X Y) | ? |
|
||||
| PLUS | Host function | ([& args]) | ? |
|
||||
| PRETTY | ? | null | ? |
|
||||
| PRINT | ? | null | ? |
|
||||
| PROP | Lisp function | (X Y U) | ? |
|
||||
| QUOTIENT | Host function | ([x y]) | I'm not certain from the documentation whether Lisp 1.5 `QUOTIENT` returned the integer part of the quotient, or a realnum representing the whole quotient. I am for now implementing the latter. |
|
||||
| READ | Host function | ([] [input]) | An implementation of a Lisp reader sufficient for bootstrapping; not necessarily the final Lisp reader. `input` should be either a string representation of a LISP expression, or else an input stream. A single form will be read. |
|
||||
| REMAINDER | Host function | ([x y]) | ? |
|
||||
| REPEAT | Lisp function | (N X) | ? |
|
||||
| RPLACA | Host function | ([cell value]) | Replace the CAR pointer of this `cell` with this `value`. Dangerous, should really not exist, but does in Lisp 1.5 (and was important for some performance hacks in early Lisps) |
|
||||
| RPLACD | Host function | ([cell value]) | Replace the CDR pointer of this `cell` with this `value`. Dangerous, should really not exist, but does in Lisp 1.5 (and was important for some performance hacks in early Lisps) |
|
||||
| SET | Host function | ([symbol val]) | Implementation of SET in Clojure. Add to the `oblist` a binding of the value of `var` to the value of `val`. NOTE WELL: this is not SETQ! |
|
||||
| SUB1 | Lisp function | (N) | ? |
|
||||
| SYSIN | Host function | ([filename]) | Read the contents of the file at this `filename` into the object list. If the file is not a valid Beowulf sysout file, this will probably corrupt the system, you have been warned. File paths will be considered relative to the filepath set when starting Lisp. It is intended that sysout files can be read both from resources within the jar file, and from the file system. If a named file exists in both the file system and the resources, the file system will be preferred. **NOTE THAT** if the provided `filename` does not end with `.lsp` (which, if you're writing it from the Lisp REPL, it won't), the extension `.lsp` will be appended. |
|
||||
| SYSOUT | Host function | ([] [filepath]) | Dump the current content of the object list to file. If no `filepath` is specified, a file name will be constructed of the symbol `Sysout` and the current date. File paths will be considered relative to the filepath set when starting Lisp. |
|
||||
| TERPRI | ? | null | ? |
|
||||
| TIMES | Host function | ([& args]) | ? |
|
||||
| TRACE | ? | null | ? |
|
||||
| UNTRACE | ? | null | ? |
|
||||
| ZEROP | Lisp function | (N) | ? |
|
||||
Functions described as 'Lisp function' above are defined in the default
|
||||
sysout file, `resources/lisp1.5.lsp`, which will be loaded by default unless
|
||||
you specify another initfile on the command line.
|
||||
|
||||
Functions described as 'Host function' are implemented in Clojure, but if you're
|
||||
brave you can redefine them in Lisp and the Lisp definitions will take precedence
|
||||
over the Clojure implementations.
|
||||
|
||||
### Architectural plan
|
||||
|
||||
|
@ -153,19 +269,6 @@ Intended deviations from the behaviour of the real Lisp reader are as follows:
|
|||
a comment, as most modern Lisps do; but I do not believe Lisp 1.5 had
|
||||
this feature.
|
||||
|
||||
### BUT WHY?!!?!
|
||||
|
||||
Because.
|
||||
|
||||
Because Lisp is the only computer language worth learning, and if a thing
|
||||
is worth learning, it's worth learning properly; which means going back to
|
||||
the beginning and trying to understand that.
|
||||
|
||||
Because there is, so far as I know, no working implementation of Lisp 1.5
|
||||
for modern machines.
|
||||
|
||||
Because I'm barking mad, and this is therapy.
|
||||
|
||||
### Commentary
|
||||
|
||||
What's surprised me in working on this is how much more polished Lisp 1.5 is
|
||||
|
@ -183,11 +286,19 @@ but this is software which is almost sixty years old).
|
|||
|
||||
## Installation
|
||||
|
||||
At present, clone the source and build it using
|
||||
Download the latest [release 'uberjar'](https://github.com/simon-brooke/beowulf/releases) and run it using:
|
||||
|
||||
`lein uberjar`.
|
||||
```bash
|
||||
java -jar <path name of uberjar>
|
||||
```
|
||||
|
||||
You will require to have [Leiningen](https://leiningen.org/) installed.
|
||||
Or clone the source and build it using:
|
||||
|
||||
```bash
|
||||
lein uberjar`
|
||||
```
|
||||
|
||||
To build it you will require to have [Leiningen](https://leiningen.org/) installed.
|
||||
|
||||
### Input/output
|
||||
|
||||
|
@ -220,7 +331,14 @@ processors, but I failed to find the Lisp source of Lisp functions as a text
|
|||
file, which is why `resources/lisp1.5.lsp` is largely copytyped and
|
||||
reconstructed from the manual.
|
||||
|
||||
I'm not at this time aware of any other working Lisp 1.5 implementations.
|
||||
### Other implementations
|
||||
|
||||
There's an online (browser native) Lisp 1.5 implementation [here](https://pages.zick.run/ichigo/) (source code [here](https://github.com/zick/IchigoLisp)). It
|
||||
even has a working compiler!
|
||||
|
||||
### History resources
|
||||
|
||||
I'm compiling a [list of links to historical documents on Lisp 1.5](https://simon-brooke.github.io/beowulf/docs/further_reading.html).
|
||||
|
||||
## License
|
||||
|
||||
|
|
16
doc/further_reading.md
Normal file
16
doc/further_reading.md
Normal file
|
@ -0,0 +1,16 @@
|
|||
# Further Reading
|
||||
|
||||
1. [CODING for the MIT-IBM 704 COMPUTER, October 1957](http://bitsavers.org/pdf/mit/computer_center/Coding_for_the_MIT-IBM_704_Computer_Oct57.pdf)
|
||||
This paper is not about Lisp. But it is about the particular individual computer on which Lisp was first implemented, and it is written in part by members of the Lisp team. I have found it useful in understanding the software environment in which, and the constraints under which, Lisp was written.
|
||||
2. [MIT AI Memo 1, John McCarthy, September 1958](https://www.softwarepreservation.org/projects/LISP/MIT/AIM-001.pdf)
|
||||
This is, as far as I can find, the earliest specification document of the Lisp project.
|
||||
3. [Lisp 1 Programmer's Manual, Phyllis Fox, March 1960](https://bitsavers.org/pdf/mit/rle_lisp/LISP_I_Programmers_Manual_Mar60.pdf)
|
||||
4. [Lisp 1.5 Programmer's Manual, Michael I. Levin, August 1962](https://www.softwarepreservation.org/projects/LISP/book/LISP%201.5%20Programmers%20Manual.pdf#page=81)
|
||||
This book is essential reading: it documents in some detail the first fully realised Lisp language system.
|
||||
5. [Early LISP History (1956 - 1959), Herbert Stoyan, August 1984](https://dl.acm.org/doi/pdf/10.1145/800055.802047#page=3)
|
||||
|
||||
|
||||
6. [The Roots of Lisp, Paul Graham, 2001](http://www.paulgraham.com/rootsoflisp.html)
|
||||
6. [The Revenge of the Nerds, Paul Graham, 2002](http://www.paulgraham.com/icad.html)
|
||||
This is mainly about why to use Lisp as a language for modern commercial software, but has useful insights into where it comes from.
|
||||
> So the short explanation of why this 1950s language is not obsolete is that it was not technology but math, and math doesn't get stale.
|
5114
doc/lisp1.5.md
Normal file
5114
doc/lisp1.5.md
Normal file
File diff suppressed because it is too large
Load diff
13
doc/mexpr.md
13
doc/mexpr.md
|
@ -1,8 +1,9 @@
|
|||
# M-Expressions
|
||||
# Interpreting M-Expressions
|
||||
|
||||
M-Expressions ('mexprs') are the grammar which John McCarthy origininally used to write Lisp, and the grammar in which many of the function definitions in the [Lisp 1.5 Programmer's Manual](https://www.softwarepreservation.org/projects/LISP/book/LISP%201.5%20Programmers%20Manual.pdf) are stated. However, I have not seen anywhere a claim that Lisp 1.5 could *read* M-Expressions, and it is not clear to me whether it was even planned that it should do so.
|
||||
M-Expressions ('mexprs') are the grammar which John McCarthy origininally used to write Lisp, and the grammar in which many of the function definitions in the [Lisp 1.5 Programmer's Manual](https://www.softwarepreservation.org/projects/LISP/book/LISP%201.5%20Programmers%20Manual.pdf) are stated. However, I have not seen anywhere a claim that Lisp 1.5 could *read* M-Expressions, and it is not clear to me whether it was even planned that it should do so, although the discussion on [page 10](https://www.softwarepreservation.org/projects/LISP/book/LISP%201.5%20Programmers%20Manual.pdf#page=18) suggests that it was.
|
||||
|
||||
Rather, it seems to me probably that M-Expressions were only ever a grammar intended to be written on paper, like [Backus Naur Form](https://en.wikipedia.org/wiki/Backus%E2%80%93Naur_form), to describe and to reason about algorithms.
|
||||
Rather, it seems to me possible that M-Expressions were only ever a grammar intended to be written on paper, like [Backus Naur Form](https://en.wikipedia.org/wiki/Backus%E2%80%93Naur_form), to describe and to reason about algorithms. I think at the point
|
||||
at which the M-Expression grammar was written, the idea of the [universal Lisp function](https://www.softwarepreservation.org/projects/LISP/book/LISP%201.5%20Programmers%20Manual.pdf#page=18)
|
||||
|
||||
I set out to make Beowulf read M-Expressions essentially out of curiousity, to see whether it could be done. I had this idea that if it could be done, I could implement most of Lisp 1.5 simply by copying in the M-Expression definitions out of the manual.
|
||||
|
||||
|
@ -45,7 +46,7 @@ is
|
|||
((QUOTE T) (QUOTE F))))))
|
||||
```
|
||||
|
||||
This is certainly more prolix and more awkward, but it also risks being flat wrong.
|
||||
This is certainly more prolix and more awkward.
|
||||
|
||||
Is the value of `NIL` the atom `NIL`, or is it the empty list `()`? If the former, then the translation from the M-Expression above is correct. However, that means that recursive functions which recurse down a list seeking the end will fail. So the latter must be the case.
|
||||
|
||||
|
@ -57,8 +58,10 @@ Is the value of `NIL` the atom `NIL`, or is it the empty list `()`? If the forme
|
|||
|
||||
I think there is an ambiguity in referencing constants which are not bound to themselves in the M-Expression notation as given in the manual. This is particularly problematic with regards to `NIL` and `F`, but there may be others instances.
|
||||
|
||||
However, so long as `F` is bound to `NIL`, and `NIL` is also bound to `NIL` (both of which are true by default, although changeable by the user), and `NIL` is the special marker used in the `CDR` of the last cons cell of a flat list, this is a difference which in practice does not make a difference. I still find it worrying, though, that rebinding variables could lead to disaster.
|
||||
|
||||
### Curly braces
|
||||
|
||||
The use of curly braces is not defined in the grammar as stated on page 10. They are not used in the initial definition of `APPLY` on page 13, but they are used in the more developed restatement on page 70. I believe they are to be read as indicating a `DO` statement -- a list of function calls to be made sequentially but without strict functional dependence on one another -- but I don't find the exposition here particularly clear and I'm not sure of this.
|
||||
The use of curly braces is not defined in the grammar as stated on page 10. They are not used in the initial definition of `APPLY` on page 13, but they are used in the more developed restatement on page 70. I believe they are to be read as indicating a section of assembly code to be assembled by the [Lisp Assembly Program](https://www.softwarepreservation.org/projects/LISP/book/LISP%201.5%20Programmers%20Manual.pdf#page=81) -- but I don't find the exposition here particularly clear and I'm not sure of this.
|
||||
|
||||
Consequently, the M-Expression interpreter in Beowulf does not interpret curly braces.
|
328
doc/values.md
Normal file
328
doc/values.md
Normal file
|
@ -0,0 +1,328 @@
|
|||
# The properties of the system, and their values
|
||||
|
||||
## here be dragons
|
||||
|
||||
Lisp is the list processing language; that is what its name means. It processes data structures built of lists - which may be lists of lists, or lists of numbers, or lists of any other sort of data item provided for by the designers of the system.
|
||||
|
||||
But how is a list, in a computer, actually implemented?
|
||||
|
||||
They're implemented as pairs, or, as the manual sometimes rather delightfully called them, 'doublets'. Pairs of what? Pairs of pointers. Of the two pointers of a pair, the first points to the current entry of the list, and the second, by default, points to the remainder of the list, or, if the end of the list has been reached, to a special datum known as `NIL` which among other things indicates that the end of the list has been reached. The pair itself is normally referred to as a 'cons cell' for reasons which are nerdy and not important just now (all right, because they are constructed using a function called `cons`, which is in itself believed to be simply an abbreviation of 'construct').
|
||||
|
||||
Two functions are used to access the two pointers of the cell. In modern Lisps these functions are called `first` and `rest`, because a lot of people who aren't greybeards find these names easier. But they aren't the original names. The original names were `CAR` and `CDR`.
|
||||
|
||||
Why?
|
||||
|
||||
## History
|
||||
|
||||
Lisp was originally written on an [IBM 704 computer at Massachusetts Institute of Technology](https://comphist.dhlab.mit.edu/archives/story/IBM_mechanics), almost seventy years ago.
|
||||
|
||||
The machine had registers which were not eight, or sixteen, or thirty two, or sixty four, bits wide, or any other number which would seem rational to modern computer scientists, but thirty six. Myth - folk memory - tells us that the machine's memory was arranged in pages. As I understand it (but this truly is folk memory) the offset within the page of the word to be fetched was known as the 'decrement', while the serial number of the page in the addressing sequence was known as the 'address'. To fetch a word from memory, you first had to select the page using the 'address', and secondly the word itself using the 'decrement'. So there were specific instructions for selecting the address, and the decrement, from the register separately.
|
||||
|
||||
There were two mnemonics for the machine instructions used to access the content of these registers, respectively:
|
||||
|
||||
CAR
|
||||
|
||||
: *Contents of the Address part of Register*; and
|
||||
|
||||
CDR
|
||||
|
||||
: *Contents of the Decrement part of Register*.
|
||||
|
||||
Is this actually true?
|
||||
|
||||
I think so. If you look at [page 80 of the Lisp 1 Programmer's Manual](https://bitsavers.org/pdf/mit/rle_lisp/LISP_I_Programmers_Manual_Mar60.pdf#page=89), you will see this:
|
||||
|
||||
```IBM704
|
||||
TEN (the TEN-Mode is entered)
|
||||
|
||||
O CAR (((A,B),C)) () \
|
||||
|
|
||||
:1 CDR ((D,(E,F))) () |
|
||||
> Type ins
|
||||
:2 CONS ((G,H), |
|
||||
|
|
||||
230 (I,J)) () /
|
||||
RLN | | oo 7 a |
|
||||
|
||||
O14 (read lines O and 1)
|
||||
```
|
||||
|
||||
Of course, this isn't proof. If `CAR` and `CDR` used here are standard IBM 704 assembler mnemonics -- as I believe they are -- then what is `CONS`? It's used in a syntactically identical way. If it also is an assembler mnemonic, then it's hard to believe that, as legend relates, it is short for 'construct'; on the other hand, if it's a label representing an entry point into a subroutine, then why should `CAR` and `CDR` not also be labels?
|
||||
|
||||
-----
|
||||
|
||||
**Edited 3<sup>rd</sup> April to add:** I've found a document, not related to Lisp (although John McCarthy is credited as one of the authors), which does confirm -- or strictly, amend -- the story. This is the [CODING for the MIT-IBM 704 COMPUTER](http://bitsavers.org/pdf/mit/computer_center/Coding_for_the_MIT-IBM_704_Computer_Oct57.pdf), dating from October 1957. The registers of the 704 were divided into four parts, named respectively the prefix part, the address part, the tag part, and the decrement part, of 3, 15, 3, and 15 bits respectively. The decrement part was not used in addressing; that part of the folklore I was taught isn't right. But the names are correct. Consider [this excerpt](http://bitsavers.org/pdf/mit/computer_center/Coding_for_the_MIT-IBM_704_Computer_Oct57.pdf#page=145) :
|
||||
|
||||
> The address, tag and decrement parts of symbolic instructions are given in that order. In some cases the decrement, tag or address parts are not necessary; therefore the following combinations where OP represents the instruction abbreviation are permissible.
|
||||
|
||||
This doesn't prove there were individual machine instructions with the mnemonics `CAR` and `CDR`; in fact, I'm going to say with some confidence that there were not, by reference to [the table of instructions](http://bitsavers.org/pdf/mit/computer_center/Coding_for_the_MIT-IBM_704_Computer_Oct57.pdf#page=170) appended to the same document. The instructions do have three letter mnemonics, and they do use 'A' and 'D' as abbreviations for 'address' and 'decrement' respectively, but `CAR` and `CDR` are not included.
|
||||
|
||||
So it seems probable that `CAR` and `CDR` were labels for subroutines, as I hypothesised above. But they were quite likely pre-existing subroutines, in use before the instantiation of the Lisp project, because they would be generally useful; and the suggestion that they are contractions of 'contents of the address part' and 'contents of the decrement part', respectively, seem confirmed.
|
||||
|
||||
And, going further down the rabbit hole, [there's this](https://dl.acm.org/doi/pdf/10.1145/800055.802047#page=3). In 1957, before work on the Lisp project started, McCarthy was writing functions to add list processing to the then-new FORTRAN language, on the very same IBM 704 machine.
|
||||
|
||||
> in this time any function that delivered integer values had to have a first letter X. Any function (as opposited to subroutines) had to have a last letter F in its name. Therefore the functions selecting parts of the IBM704 memory register (word) were introduced to be XCSRF, XCPRF, XCDRF, XCTRF and XCARF
|
||||
|
||||
-----
|
||||
|
||||
I think that the answer has to be that if `CAR` and `CDR` had been named by the early Lisp team -- John McCarthy and his immediate colleagues -- they would not have been named as they were. If not `FRST` and `REST`, as in more modern Lisps, then something like `P1` and `P2`. `CAR` and `CDR` are distinctive and memorable (and therefore in my opinion worth preserving) because they very specifically name the parts of a cons cell and of nothing else.
|
||||
|
||||
Let's be clear, here: when `CAR` and `CDR` are used in Lisp, they are returning pointers, certainly -- but not in the sense that one points to a page and the other to a word. Each is an offset into a cell array, which is almost certainly an array of single 36 bit words held on a single page. So both are in effect being used as decrements. Their use in Lisp is an overload onto their original semantic meaning; they are no longer being used for the purpose for which they are named.
|
||||
|
||||
As far as I can tell, these names first appear in print in 1960, both in the Lisp 1 Programmer's Manual referenced above, and in McCarthy's paper [Recursive Functions of Symbolic Expressions and Their Computation by Machine, Part I](https://web.archive.org/web/20230328222647/http://www-formal.stanford.edu/jmc/recursive.pdf). The paper was published in April so was presumably written in 1959
|
||||
|
||||
## Grey Anatomy
|
||||
|
||||
### The Object List
|
||||
|
||||
Lisp keeps track of values by associating them with names. It does so by having what is in effect a global registry of all the names it knows to which values are attached. This being a list processing language, that was of course, in early Lisps, a list: a single specialised first class list known as the 'object list', or `oblist` for short.
|
||||
|
||||
Of course, a list need not just be a list of single items, it can be a list of pairs: it can be a list of pairs of the form `(name . value)`. Hold onto that, because I want to talk about another fundamental part of a working Lisp system, the stack.
|
||||
|
||||
### The Stack
|
||||
|
||||
Considering the case of pure interpreter first, let's think about how a function keeps track of the data it's working on. In order to do its work, it probably calls other functions, to which it passes off data, and they in turn probably call further functions. So, when control returns to our first function, how does it know where its data is? The answer is that each function pushes its argument bindings onto the stack when it starts work, and pops them off again when it exits. So when control returns to a function, its own data is still on the top of the stack. Or, to be precise, actually it doesn't; in practice the function [`EVAL`](https://www.softwarepreservation.org/projects/LISP/book/LISP%201.5%20Programmers%20Manual.pdf#page=79) does it for each function in turn. But it doesn't matter: it gets done.
|
||||
|
||||
What is this stack? Well, it's a list of `(name . value)` pairs. At least, it is in pure Lisps; [Clojure](https://clojure.org/), because it runs on the [Java Virtual Machine](https://en.wikipedia.org/wiki/Java_virtual_machine) and interoperates with other software running on the JVM, uses the JVM stack which is a permanently reserved vector of memory never used for anything else. Consequently it cannot be very large; and the consequence of that is that it's very easy to crash JVM programs because they've run out of stack space.
|
||||
|
||||
The advantage of organising your stack as a vector is that on average it's usually slightly more memory efficient, and that it's somewhat faster to access. The disadvantage is you need a contiguous block of memory for it, and once you've run out, you've at best lost both those advantages but in the normal case your program just crashes. Also, the memory you've reserved for the stack isn't available for any other use, even during the most of the time that the stack isn't using most of it. So of course there's a temptation to keep the amount reserved for the stack as small as possible.
|
||||
|
||||
It's this brutal fragility of vector stacks -- which are used by most modern computer languages -- which makes software people so wary of fully exploiting the beauty and power of recursion, and I really think that's a shame.
|
||||
|
||||
The advantage of organising your stack as a list is that, while there is any memory left on the machine at all, you cannot run out of stack.
|
||||
|
||||
### The Spine
|
||||
|
||||
So, there's an object list where you associate names and values, and there's a stack where you associate names and values. But, why do they have to be different? And why do you have to search in two places to find the value of a name?
|
||||
|
||||
The answer is -- or it was, and arguably it should be -- that you don't. The stack can simply be pushed onto the front of the object list. This has multiple advantages. The first and most obvious is that you only have to search in one place for the value associated with a name.
|
||||
|
||||
The second is more subtle: a function can mask a variable in the object list by binding the same name to a new value, and the functions to which it then calls will only see that new value. This is useful if, for example, printed output is usually sent to the user's terminal, but for a particular operation you want to send it to a line printer or to a file on disk. You simply rebind the name of the standard output stream to your chosen output stream, and call the function whose output you want to redirect.
|
||||
|
||||
So, in summary, there's a lot of merit in making the stack and the object list into a single central structure on which the architecture of our Lisp system is built. But there's more we need to record, and it's important.
|
||||
|
||||
### Fields and Properties
|
||||
|
||||
No, I'm not banging on about [land reform](https://www.journeyman.cc/blog/tags-output/Levelling/) again! I'm not a total monomaniac!
|
||||
|
||||
But there's more than one datum we may want to associate with a single name. A list can be more than a set of bindings between single names and single values. There's more than one thing, for example, that I know about my friend Lucy. I know her age. I know her address. I know her height. I know her children. I know her mother. All of this information -- and much more -- should be associated with her.
|
||||
|
||||
In conventional computing systems we'd use a table. We'd put into the table a field -- a column -- for each datum we wanted to store about a class of things of interest. And we'd reserve space to store that datum for every record, whether every record had something to go there of not. Furthermore, we'd have to reserve space in each field for the maximum size of the datum to be stored in it -- so if we needed to store full names for even some of the people we knew, and one of the people whose full name we needed to store (because he's both very important and very irascible) was *Charles Philip Arthur George Windsor*, then we'd have to reserve space for thirty-six characters for the full name of everyone in our records, even if for most of them half that would be enough.
|
||||
|
||||
But if instead of storing a table for each sort of thing on which we hold data, and a row in that table for each item of that sort on which we store data, we simply tagged each thing on which we hold data with those things which are interesting about them? We could tag my friend Lucy with the fact she's on pilgrimage, and what her pilgrimage route is. Those aren't things we need to know about most people, it would be absurdly wasteful to add a column to a `person` table to record `pilgrimage route`. So in a conventional data system we would lose that data.
|
||||
|
||||
Lisp has had, right back from the days of Lisp 1.5 -- so, for sixty-five years -- a different solution. We can give every symbol arbitrarily many, arbitrarily different, properties. A property is a `(name . value)` pair. We don't have to store the same properties for every object. The values of the properties don't have to have a fixed size, and they don't have to take up space they don't need. It's like having a table with as many fields as we choose, and being able to add more fields at any time.
|
||||
|
||||
So, in summary, I knew, in building Beowulf, that I'd have to implement property lists. I just didn't know how I was going to do it.
|
||||
|
||||
## Archaeology
|
||||
|
||||
What I'm doing with Beowulf is trying to better understand the history of Lisp by reconstructing a very early example; in this case, Lisp 1.5, from about 1962, or sixty one years ago.
|
||||
|
||||
I had had the naive assumption that entries on the object list in early Lisps had their `CAR` pointing to the symbol and their `CDR` pointing to the related value. Consequently, in building [beowulf](https://github.com/simon-brooke/beowulf), I could not work out where the property list went. More careful reading of [the text](https://www.softwarepreservation.org/projects/LISP/book/LISP%201.5%20Programmers%20Manual.pdf#page=67) implies, but does not explicitly state, that my naive assumption is wrong.
|
||||
|
||||
Instead, it appears that the `CAR` points to the symbol, as expected, but the `CDR` points to the property list; and that on the property list there are privileged properties at least as follows:
|
||||
|
||||
APVAL
|
||||
: the simple straightforward ordinary value of the symbol, considered as a variable;
|
||||
|
||||
EXPR
|
||||
: the definition of the function considered as a normal lambda expression (arguments to be evaluated before applying);
|
||||
|
||||
FEXPR
|
||||
: the definition of a function which should be applied to unevaluated arguments (what InterLisp and Portable Standard Lisp would call ['*nlambda*'](https://citeseerx.ist.psu.edu/document?repid=rep1&type=pdf&doi=fe0f4f19cee0c607d7b229feab26fc9ed559fc9c#page=9));
|
||||
|
||||
SUBR
|
||||
: the definition of a compiled subroutine which should be applied to evaluated arguments;
|
||||
|
||||
FSUBR
|
||||
: the definition of a compiled subroutine which should be applied to unevaluated arguments.
|
||||
|
||||
I think there was also another privileged property value which contained the property considered as a constant, but I haven't yet confirmed that.
|
||||
|
||||
From this it would seem that Lisp 1.5 was not merely a ['Lisp 2'](http://xahlee.info/emacs/emacs/lisp1_vs_lisp2.html) but in fact a 'Lisp 6', with six effectively first class namespaces. In fact it's not as bad as that, because of the way [`EVAL`](https://www.softwarepreservation.org/projects/LISP/book/LISP%201.5%20Programmers%20Manual.pdf#page=79) is evaluated.
|
||||
|
||||
Essentially the properties are tried in turn, and only the first value found is used. Thus the heirarchy is
|
||||
|
||||
1. APVAL
|
||||
2. EXPR
|
||||
3. FEXPR
|
||||
4. SUBR
|
||||
5. FSUBR
|
||||
|
||||
This means that, while the other potential values can be retrieved from the property list, interpreted definitions (if present) will always be preferred to uninterpreted definitions, and lambda function definitions (which evaluate their arguments), where present, will always be preferred to non-lamda definitions, which don't.
|
||||
|
||||
**BUT NOTE THAT** the `APVAL` value is sought only when seeking a variable value for the symbol, while the others are only when seeking a function value, so Lisp 1.5 is a 'Lisp 2', not a 'Lisp 1'. I strongly believe that this is wrong: a function is a value, and should be treated as such. But at the same time I do acknowledge the benefit of being able to store both source and compiled forms of the function as properties of the same symbol.
|
||||
|
||||
## The persistent problem
|
||||
|
||||
There's a view in modern software theory -- with which I strongly hold -- that data should be immutable. Data that changes under you is the source of all sorts of bugs. And in modern multi threaded systems, the act of reading a datum whilst some other process is writing it, or worse, two processes attempting simultaneously to write the same datum, is a source of data corruption and even crashes. So I'm very wary of mutable data; and, in modern systems where we normally have a great deal of space and a lot of processor power, making fresh copies of data structures containing the change we wanted to make is a reasonable price to pay for avoiding a whole class of bugs.
|
||||
|
||||
But early software was not like that. It was always constrained by the limits of the hardware on which it ran, to a degree that we are not. And the experience that we now have of the problems caused by mutable data, they did not have. So it's core to the design of Lisp 1.5 that its lists are mutable; and, indeed, one of the biggest challenges in writing Beowulf has been [implementing mutable lists in Clojure](https://github.com/simon-brooke/beowulf/blob/master/src/beowulf/cons_cell.clj#L19), a language carefully designed to prevent them.
|
||||
|
||||
But, just because Lisp 1.5 lists can be mutable, should they be? And when should they be?
|
||||
|
||||
The problem here is that [spine of the system](#the_spine) I talked about earlier. If we build the execution stack on top of the oblist -- as at present I do -- then if we make a new version of the oblist with changes in it, the new changes will not be in the copy of the oblist that the stack is built on top of; and consequently, they'll be invisible to the running program.
|
||||
|
||||
What I do at present, and what I think may be good enough, is that each time execution returns to the read-eval-print loop, the REPL, the user's command line, I rebuild a new execution stack on the top of the oblist as it exists now. So, if the last operation modified the oblist, the next operation will see the new, modified version. But if someone tried to run some persistent program which was writing stuff to property values and hoping to read them back in the same computation, that wouldn't work, and it would be a very hard bug to trace down.
|
||||
|
||||
So my options are:
|
||||
|
||||
1. To implement `PUT` and `GET` in Clojure, so that they can operate on the current copy of the object list, not the one at the base of the stack. I'm slightly unwilling to do that, because my objective is to make Beowulf ultimately as self-hosting as possible.
|
||||
2. To implement `PUT` and `GET` in Lisp, and have them destructively modify the working copy of the object list.
|
||||
|
||||
Neither of these particularly appeal.
|
||||
|
||||
## How property lists should work
|
||||
|
||||
I'm still not fully understanding how property lists in Lisp 1.5 are supposed to work.
|
||||
|
||||
### List format
|
||||
|
||||
Firstly, are they association lists comprising dotted pairs of `(property-name . value)`, i.e.:
|
||||
|
||||
>((property-name<sub>1</sub> . value<sub>1</sub>) (property-name<sub>2</sub> . value<sub>2</sub>) ... (property-name<sub>n</sub> . value<sub>n</sub>))
|
||||
|
||||
I have assumed so, and that is what I presently intend to implement, but the diagrams on [pages 59 and 60](https://www.softwarepreservation.org/projects/LISP/book/LISP%201.5%20Programmers%20Manual.pdf#page=67) seem rather to show a flat list of interleaved names and values:
|
||||
|
||||
> (property-name<sub>1</sub> value<sub>1</sub> property-name<sub>2</sub> value<sub>2</sub> ... property-name<sub>n</sub> value<sub>n</sub>)
|
||||
|
||||
I cannot see what the benefit of this latter arrangement is, and I'm unwilling to do it, although I think it may be what was done. But if it was done that way, *why* was it done that way? These were bright people, and they certainly knew about association lists. So... I'm puzzled.
|
||||
|
||||
### Function signatures
|
||||
|
||||
To associate the value of a property with a symbol, we need three things: we need the symbol, we need the property name, and we need the value. For this reason, [Portable Standard Lisp](https://www.softwarepreservation.org/projects/LISP/utah/USCP-Portable_Standard_LISP_Users_Manual-TR_10-1984.pdf#page=44) and others has a function `put` with three arguments:
|
||||
|
||||
> `(Put U:id IND:id PROP:any)`: any
|
||||
> The indicator `IND` with the property `PROP` is placed on the property list of
|
||||
> the id `U`. If the action of Put occurs, the value of `PROP` is returned. If
|
||||
> either of `U` and `IND` are not ids the type mismatch error occurs and no
|
||||
> property is placed.
|
||||
> `(Put 'Jim 'Height 68)`
|
||||
> The above returns `68` and places `(Height . 68)` on the property list of the id `Jim`
|
||||
|
||||
Cambridge Lisp is identical to this except in lower case. [InterLisp](https://larrymasinter.net/86-interlisp-manual-opt.pdf#page=37) and several others have `putprop`:
|
||||
|
||||
> `(PUTPROP ATM PROP VAL) [Function]`
|
||||
> Puts the property `PROP` with value `VAL` on the property list of `ATM`. `VAL` replaces
|
||||
> any previous value for the property `PROP` on this property list. Returns `VAL`.
|
||||
|
||||
The execrable Common Lisp uses its execrable macro [`setf`](https://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node108.html) but really the less said about that the better.
|
||||
|
||||
So I was looking for a function of three arguments to set properties, and I didn't find one.
|
||||
|
||||
There's a function `DEFINE` which takes one argument, an association list of pairs:
|
||||
```lisp
|
||||
(function-name . function-definition)`
|
||||
```
|
||||
So how does that work, if what it's doing is setting properties? If all you're passing is pairs of name and definition, where does the property name come from?
|
||||
|
||||
The answer is as follows, taken from [the manual](https://www.softwarepreservation.org/projects/LISP/book/LISP%201.5%20Programmers%20Manual.pdf#page=66):
|
||||
|
||||
> #### define [x] : EXPR pseudo-function
|
||||
|
||||
> The argument of `define`, `x`, is a list of pairs
|
||||
|
||||
> > ((u<sub>l</sub> v<sub>l</sub>) (u<sub>2</sub> v<sub>2</sub>) ... (u<sub>n</sub> v<sub>n</sub>))
|
||||
|
||||
> where each `u` is a name and each `v` is a λ-expression for a function . For each `pair`, define puts an `EXPR` on the property list for `u` pointing to `v`. The function of `define` puts things on at the front of the property list. The value of `define` is the list of `u`s.
|
||||
|
||||
So, in fact, the value of the property being set by `define` is fixed: hard wired, not parameterised. That seems an astonishing decision, until you realise that Lisp 1.5's creators weren't creating their functions one by one, in a playful exploration with their system, but entering them in a batch.
|
||||
|
||||
## Learning by doing
|
||||
|
||||
In fact, when I got over my surprise, I realised that that `(name . function-definition)` list is actually very much like this, which is an excerpt from a sysout from a Beowulf prototype:
|
||||
|
||||
```lisp
|
||||
(...
|
||||
(MAPLIST LAMBDA (L F)
|
||||
(COND ((NULL L) NIL)
|
||||
((QUOTE T) (CONS (F (CAR L)) (MAPLIST (CDR L) F)))))
|
||||
(MEMBER LAMBDA (A X)
|
||||
(COND ((NULL X) (QUOTE F))
|
||||
((EQ A (CAR X)) (QUOTE T))
|
||||
((QUOTE T) (MEMBER A (CDR X)))))
|
||||
(MINUSP LAMBDA (X) (LESSP X 0))
|
||||
(NOT LAMBDA (X)
|
||||
(COND (X (QUOTE NIL))
|
||||
((QUOTE T) (QUOTE T))))
|
||||
(NULL LAMBDA (X)
|
||||
(COND ((EQUAL X NIL) (QUOTE T))
|
||||
(T (QUOTE F))))
|
||||
...)
|
||||
```
|
||||
|
||||
I was looking at `DEFINE` and thinking, 'why would one ever want to do that?' and then I found that, behind the scenes, I was actually doing it myself.
|
||||
|
||||
Because the point of a sysout is you don't write it. The point about the REPL -- the Read Eval Print Loop which is the heart of the interactive Lisp development cycle, where you sit playing with things and fiddling with them interactively, and where when one thing works you get onto the next without bothering to make some special effort to record it.
|
||||
|
||||
The point of a sysout is that, at the end of the working day, you invoke one function
|
||||
|
||||
| Function | Type | Signature | Implementation | Documentation |
|
||||
| -------- | ---- | --------- | -------------- | ------------- |
|
||||
| SYSOUT | Host function | (SYSOUT); (SYSOUT FILEPATH) | SUBR | Dump the current content of the object list to file. If no `filepath` is specified, a file name will be constructed of the symbol `Sysout` and the current date. File paths will be considered relative to the filepath set when starting Lisp. |
|
||||
|
||||
At the start of the next working day, you load that sysout in and continue your session.
|
||||
|
||||
The sysout captures the entire working state of the machine. No-one types it in, as an operation in itself. Instead, data structures -- corpuses of functions among them -- simply build up on the object list almost casually, as a side effect of the fact that you're enjoying exploring your problem and finding elegant ways of solving it. So `SYSOUT` and `SYSIN` seem to me, as someone who all his adult life has worked with Lisp interactively, as just an automatic part of the cycle of the day.
|
||||
|
||||
## The process of discovery
|
||||
|
||||
The thing is, I don't think anyone is ever going to use Beowulf the way Lisp 1.5 was used. I mean, probably, no one is ever going to use Beowulf at all; but if they did they wouldn't use Beowulf the way Lisp 1.5 was used.
|
||||
|
||||
I'm a second generation software person. I have worked, in my career, with two people who personally knew and had worked with [Alan Turing](https://en.wikipedia.org/wiki/Alan_Turing). I have worked with, and to an extent been mentored by, [Chris Burton](https://www.bcs.org/articles-opinion-and-research/manchester-s-place-in-computing-history-marked/), who in his apprenticeship was part of the team that built the Manchester Mark One, and who in his retirement led the team who restored it. But I never knew the working conditions they were accustomed to. In my first year at university we used card punches, and, later, when we had a bit of seniority, teletypewriters (yes, that's what TTY stands for), but by the time I'd completed my undergraduate degree and become a research associate I had a Xerox 1108 workstation with a huge bitmapped graphic screen, and an optical mouse, goddamit, running InterLisp, all to myself.
|
||||
|
||||
People in the heroic age did not have computers all to themselves. They did not have terminals all to themselves. They didn't sit at a terminal experimenting in the REPL. They wrote their algorithms in pencil on paper. When they were certain they'd got it right, they'd use a card punch to punch a deck of cards carrying the text of the program, and then they were certain they'd got *that* right, they'd drop it into the input hopper. Some time later their batch would run, and the operator would put the consequent printout into their pigeon hole for them to collect.
|
||||
|
||||
(They wrote amazingly clean code, those old masters. I could tell you a story about Chris Burton, the train, and the printer driver, that software people of today simply would not believe. But it's true. And I think that what taught them that discipline was the high cost of even small errors.)
|
||||
|
||||
Lisp 1.5 doesn't have `PUT`, `PUTPROP` or `DEFUN` because setting properties individually, defining functions individually one at a time, was not something they ever thought about doing. And in learning that, I've learned more than I ever expected to about the real nature of Lisp 1.5, and the (great) people who wrote it.
|
||||
|
||||
-----
|
||||
|
||||
## Deeper delving
|
||||
|
||||
After writing, and publishing, this essay, I went on procrastinating, which is what I do when I'm sure I'm missing something; and to procrastinate, I went on reading the earliest design documents of Lisp I could find. And so I came across the MIT AI team's first ever memo, written by John McCarthy in September 1958. And in that, I find this:
|
||||
|
||||
> 3.2.1. First we have those that extract parts of a 704 word and form a word from parts. We shall distinguish the following parts of a word and indicate each of them by a characteristic letter.
|
||||
>
|
||||
> | Letter | Description |
|
||||
> | ---- | ---------------------------- |
|
||||
> | w | the whole word |
|
||||
> | p | the prefix (bits s, 1, 2) |
|
||||
> | i | the indicator (bits 1 and 2) |
|
||||
> | s | the sign bit |
|
||||
> | d | the decrement (bits 3-17) |
|
||||
> | t | the tag (bits 18-20) |
|
||||
> | a | the address (bits 21-35) |
|
||||
|
||||
In the discussion of functions which access properties on [page 58 of the Lisp 1.5 programmer's manual](https://www.softwarepreservation.org/projects/LISP/book/LISP%201.5%20Programmers%20Manual.pdf#page=66), the word 'indicator' is used in preference to 'symbol' for the name of a property: for example
|
||||
|
||||
> The function `deflist` is a more general defining function. Its first argument is a list of pairs as for define. Its second argument is the *indicator* that is to be used. After `deflist` has been executed with (u<sub>i</sub> v<sub>i</sub>) among its first argument, the property list of u<sub>i</sub> will begin:
|
||||
>
|
||||
> If `deflist` or `define` is used twice on the same object with the same *indicator*, the old value will be replaced by the new one.
|
||||
|
||||
(my emphasis).
|
||||
|
||||
That use of 'indicator' has been nagging at me for a week. It looks like a term of art. If it's just an ordinary atomic symbol, why isn't it called a symbol?
|
||||
|
||||
Is it an indicator in the special sense of the indicator part of the machine word? If it were, then the property list could just be a flat list of values. And what's been worrying and surprising me is that property lists are shown in the manual as flat lists. Eureka? I don't *think* so.
|
||||
|
||||
The reason I don't think so is that there are only two bits in the indicator part of the word, so only four distinct values; whereas we know that Lisp 1.5 has (at least) five distinct indicator values, `APVAL`, `EXPR`, `FEXPR`, `SUBR` and `FSUBR`.
|
||||
|
||||
Furthermore, on [page 39](https://www.softwarepreservation.org/projects/LISP/book/LISP%201.5%20Programmers%20Manual.pdf#page=47), we have:
|
||||
|
||||
> A property list is characterized by having the special constant 77777<sub>8</sub> (i. e., minus 1)
|
||||
> as the first element of the list. The rest of the list contains various properties of the
|
||||
> atomic symbol. Each property is preceded by an *atomic symbol* which is called its
|
||||
> *indicator*.
|
||||
|
||||
(again, my emphasis)
|
||||
|
||||
But I'm going to hypothesise that the properties were originally intended to be discriminated by the indicator bits in the cons cell, that they were originally coded that way, and that there was some code which depended on property lists being flat lists; and that, when it was discovered that four indicators were not enough and that something else was going to have to be used, the new format of the property list using atomic symbols as indicators was bodged in.
|
||||
|
||||
-----
|
||||
|
||||
So what this is about is I've spent most of a whole day procrastinating, because I'm not exactly sure how I'm going to make the change I've got to make. Versions of Beowulf up to and including 0.2.1 used the naive understanding of the architecture; version 0.3.0 *should* use the corrected version. But before it can, I need to be reasonably confident that I understand what the correct solution is.
|
||||
|
||||
I *shall* implement `PUT`, even though it isn't in the spec, because it's a useful building block on which to build `DEFINE` and `DEFLIS`, both of which are. And also, because `PUT` would have been very easy for the Lisp 1.5 implementers to implement, if it had been relevant to their working environment. And I shall implement property list as flat lists of interleaved 'indicator' symbols and values, even with that nonsense 77777<sub>8</sub> as a prefix, because now I know (or think I know) that it was a bodge, it seems right in the spirit of historical reconstruction to reconstruct the bodge.
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
11
docs/codox/beowulf.interop.html
Normal file
11
docs/codox/beowulf.interop.html
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
3
docs/codox/beowulf.manual.html
Normal file
3
docs/codox/beowulf.manual.html
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
3
docs/codox/beowulf.scratch.html
Normal file
3
docs/codox/beowulf.scratch.html
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -1,12 +1,28 @@
|
|||
body {
|
||||
font-family: Helvetica, Arial, sans-serif;
|
||||
font-size: 15px;
|
||||
color: limegreen;
|
||||
background-color: black;
|
||||
}
|
||||
|
||||
a {
|
||||
color: lime;
|
||||
}
|
||||
|
||||
a:active, a:hover {
|
||||
color: yellowgreen;
|
||||
}
|
||||
|
||||
a:visited {
|
||||
color: green;
|
||||
}
|
||||
|
||||
pre, code {
|
||||
font-family: Monaco, DejaVu Sans Mono, Consolas, monospace;
|
||||
font-size: 9pt;
|
||||
margin: 15px 0;
|
||||
color: limegreen;
|
||||
background-color: #111;
|
||||
}
|
||||
|
||||
h1 {
|
||||
|
@ -21,9 +37,13 @@ h2 {
|
|||
font-size: 25px;
|
||||
}
|
||||
|
||||
th, td {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
h5.license {
|
||||
margin: 9px 0 22px 0;
|
||||
color: #555;
|
||||
color: lime;
|
||||
font-weight: normal;
|
||||
font-size: 12px;
|
||||
font-style: italic;
|
||||
|
@ -43,7 +63,7 @@ h5.license {
|
|||
left: 0;
|
||||
right: 0;
|
||||
height: 22px;
|
||||
color: #f5f5f5;
|
||||
color: limegreen;
|
||||
padding: 5px 7px;
|
||||
}
|
||||
|
||||
|
@ -52,8 +72,8 @@ h5.license {
|
|||
right: 0;
|
||||
bottom: 0;
|
||||
overflow: auto;
|
||||
background: #fff;
|
||||
color: #333;
|
||||
background: black;
|
||||
color: green;
|
||||
padding: 0 18px;
|
||||
}
|
||||
|
||||
|
@ -65,15 +85,15 @@ h5.license {
|
|||
}
|
||||
|
||||
.sidebar.primary {
|
||||
background: #e2e2e2;
|
||||
border-right: solid 1px #cccccc;
|
||||
background: #080808;
|
||||
border-right: solid 1px forestgreen;
|
||||
left: 0;
|
||||
width: 250px;
|
||||
}
|
||||
|
||||
.sidebar.secondary {
|
||||
background: #f2f2f2;
|
||||
border-right: solid 1px #d7d7d7;
|
||||
background: #111;
|
||||
border-right: solid 1px darkgreen;
|
||||
left: 251px;
|
||||
width: 200px;
|
||||
}
|
||||
|
@ -91,8 +111,8 @@ h5.license {
|
|||
}
|
||||
|
||||
#header {
|
||||
background: #3f3f3f;
|
||||
box-shadow: 0 0 8px rgba(0, 0, 0, 0.4);
|
||||
background: #080808;
|
||||
box-shadow: 0 0 8px rgba(192, 255, 192, 0.4);
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
|
@ -117,21 +137,13 @@ h5.license {
|
|||
text-decoration: none;
|
||||
}
|
||||
|
||||
#header a {
|
||||
color: #f5f5f5;
|
||||
}
|
||||
|
||||
.sidebar a {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
#header h2 {
|
||||
float: right;
|
||||
font-size: 9pt;
|
||||
font-weight: normal;
|
||||
margin: 4px 3px;
|
||||
padding: 0;
|
||||
color: #bbb;
|
||||
color: #5f5;
|
||||
}
|
||||
|
||||
#header h2 a {
|
||||
|
@ -146,11 +158,11 @@ h5.license {
|
|||
}
|
||||
|
||||
.sidebar h3 a {
|
||||
color: #444;
|
||||
color: #4f4;
|
||||
}
|
||||
|
||||
.sidebar h3.no-link {
|
||||
color: #636363;
|
||||
color: green;
|
||||
}
|
||||
|
||||
.sidebar ul {
|
||||
|
@ -175,7 +187,7 @@ h5.license {
|
|||
|
||||
.sidebar li .no-link {
|
||||
display: block;
|
||||
color: #777;
|
||||
color: #7F7;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
|
@ -217,8 +229,8 @@ h5.license {
|
|||
}
|
||||
|
||||
.sidebar li .tree .top {
|
||||
border-left: 1px solid #aaa;
|
||||
border-bottom: 1px solid #aaa;
|
||||
border-left: 1px solid yellowgreen;
|
||||
border-bottom: 1px solid yellowgreen;
|
||||
height: 19px;
|
||||
}
|
||||
|
||||
|
@ -227,17 +239,17 @@ h5.license {
|
|||
}
|
||||
|
||||
.sidebar li.branch .tree .bottom {
|
||||
border-left: 1px solid #aaa;
|
||||
border-left: 1px solid yellowgreen;
|
||||
}
|
||||
|
||||
.sidebar.primary li.current a {
|
||||
border-left: 3px solid #a33;
|
||||
color: #a33;
|
||||
border-left: 3px solid goldenrod;
|
||||
color: goldenrod;
|
||||
}
|
||||
|
||||
.sidebar.secondary li.current a {
|
||||
border-left: 3px solid #33a;
|
||||
color: #33a;
|
||||
border-left: 3px solid yellow;
|
||||
color: yellow;
|
||||
}
|
||||
|
||||
.namespace-index h2 {
|
||||
|
@ -275,7 +287,7 @@ h5.license {
|
|||
|
||||
.public {
|
||||
margin: 0;
|
||||
border-top: 1px solid #e0e0e0;
|
||||
border-top: 1px solid lime;
|
||||
padding-top: 14px;
|
||||
padding-bottom: 6px;
|
||||
}
|
||||
|
@ -293,7 +305,7 @@ h5.license {
|
|||
}
|
||||
|
||||
.members h4 {
|
||||
color: #555;
|
||||
color: lime;
|
||||
font-weight: normal;
|
||||
font-variant: small-caps;
|
||||
margin: 0 0 5px 0;
|
||||
|
@ -304,7 +316,7 @@ h5.license {
|
|||
padding-left: 12px;
|
||||
margin-top: 2px;
|
||||
margin-left: 7px;
|
||||
border-left: 1px solid #bbb;
|
||||
border-left: 1px solid #5f5;
|
||||
}
|
||||
|
||||
#content .members .inner h3 {
|
||||
|
@ -357,7 +369,7 @@ h4.dynamic {
|
|||
}
|
||||
|
||||
h4.added {
|
||||
color: #508820;
|
||||
color: #7acc32;
|
||||
}
|
||||
|
||||
h4.deprecated {
|
||||
|
@ -397,7 +409,7 @@ h4.deprecated {
|
|||
|
||||
.type-sig {
|
||||
clear: both;
|
||||
color: #088;
|
||||
color: goldenrod;
|
||||
}
|
||||
|
||||
.type-sig pre {
|
||||
|
@ -407,8 +419,8 @@ h4.deprecated {
|
|||
|
||||
.usage code {
|
||||
display: block;
|
||||
color: #008;
|
||||
margin: 2px 0;
|
||||
color: limegreen;
|
||||
}
|
||||
|
||||
.usage code:first-child {
|
||||
|
@ -476,27 +488,27 @@ p {
|
|||
}
|
||||
|
||||
.markdown pre > code, .src-link a {
|
||||
border: 1px solid #e4e4e4;
|
||||
border: 1px solid lime;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.markdown code:not(.hljs), .src-link a {
|
||||
background: #f6f6f6;
|
||||
background: #111;
|
||||
}
|
||||
|
||||
pre.deps {
|
||||
display: inline-block;
|
||||
margin: 0 10px;
|
||||
border: 1px solid #e4e4e4;
|
||||
border: 1px solid lime;
|
||||
border-radius: 2px;
|
||||
padding: 10px;
|
||||
background-color: #f6f6f6;
|
||||
background-color: #111;
|
||||
}
|
||||
|
||||
.markdown hr {
|
||||
border-style: solid;
|
||||
border-top: none;
|
||||
color: #ccc;
|
||||
color: goldenrod;
|
||||
}
|
||||
|
||||
.doc ul, .doc ol {
|
||||
|
@ -509,12 +521,12 @@ pre.deps {
|
|||
}
|
||||
|
||||
.doc table td, .doc table th {
|
||||
border: 1px solid #dddddd;
|
||||
border: 1px solid goldenrod;
|
||||
padding: 4px 6px;
|
||||
}
|
||||
|
||||
.doc table th {
|
||||
background: #f2f2f2;
|
||||
background: #111;
|
||||
}
|
||||
|
||||
.doc dl {
|
||||
|
@ -525,7 +537,7 @@ pre.deps {
|
|||
font-weight: bold;
|
||||
margin: 0;
|
||||
padding: 3px 0;
|
||||
border-bottom: 1px solid #ddd;
|
||||
border-bottom: 1px solid goldenrod;
|
||||
}
|
||||
|
||||
.doc dl dd {
|
||||
|
@ -534,7 +546,7 @@ pre.deps {
|
|||
}
|
||||
|
||||
.doc abbr {
|
||||
border-bottom: 1px dotted #333;
|
||||
border-bottom: 1px dotted goldenrod;
|
||||
font-variant: none;
|
||||
cursor: help;
|
||||
}
|
||||
|
@ -547,5 +559,5 @@ pre.deps {
|
|||
font-size: 70%;
|
||||
padding: 1px 4px;
|
||||
text-decoration: none;
|
||||
color: #5555bb;
|
||||
color: lime5bb;
|
||||
}
|
||||
|
|
17
docs/codox/further_reading.html
Normal file
17
docs/codox/further_reading.html
Normal file
|
@ -0,0 +1,17 @@
|
|||
<!DOCTYPE html PUBLIC ""
|
||||
"">
|
||||
<html><head><meta charset="UTF-8" /><title>Further Reading</title><link rel="icon" type="image/x-icon" href="../img/beowulf_logo_favicon.png" /><link rel="stylesheet" type="text/css" href="css/default.css" /><link rel="stylesheet" type="text/css" href="css/highlight.css" /><script type="text/javascript" src="js/highlight.min.js"></script><script type="text/javascript" src="js/jquery.min.js"></script><script type="text/javascript" src="js/page_effects.js"></script><script>hljs.initHighlightingOnLoad();</script></head><body><div id="header"><h2>Generated by <a href="https://github.com/weavejester/codox">Codox</a></h2><h1><a href="index.html"><span class="project-title"><span class="project-name">Beowulf</span> <span class="project-version">0.3.0-SNAPSHOT</span></span></a></h1></div><div class="sidebar primary"><h3 class="no-link"><span class="inner">Project</span></h3><ul class="index-link"><li class="depth-1 "><a href="index.html"><div class="inner">Index</div></a></li></ul><h3 class="no-link"><span class="inner">Topics</span></h3><ul><li class="depth-1 current"><a href="further_reading.html"><div class="inner"><span>Further Reading</span></div></a></li><li class="depth-1 "><a href="intro.html"><div class="inner"><span>beowulf</span></div></a></li><li class="depth-1 "><a href="mexpr.html"><div class="inner"><span>Interpreting M-Expressions</span></div></a></li><li class="depth-1 "><a href="values.html"><div class="inner"><span>The properties of the system, and their values</span></div></a></li></ul><h3 class="no-link"><span class="inner">Namespaces</span></h3><ul><li class="depth-1"><div class="no-link"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>beowulf</span></div></div></li><li class="depth-2 branch"><a href="beowulf.bootstrap.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>bootstrap</span></div></a></li><li class="depth-2 branch"><a href="beowulf.cons-cell.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>cons-cell</span></div></a></li><li class="depth-2 branch"><a href="beowulf.core.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>core</span></div></a></li><li class="depth-2 branch"><a href="beowulf.gendoc.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>gendoc</span></div></a></li><li class="depth-2 branch"><a href="beowulf.host.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>host</span></div></a></li><li class="depth-2 branch"><a href="beowulf.interop.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>interop</span></div></a></li><li class="depth-2 branch"><a href="beowulf.io.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>io</span></div></a></li><li class="depth-2 branch"><a href="beowulf.manual.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>manual</span></div></a></li><li class="depth-2 branch"><a href="beowulf.oblist.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>oblist</span></div></a></li><li class="depth-2 branch"><a href="beowulf.read.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>read</span></div></a></li><li class="depth-2"><div class="no-link"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>reader</span></div></div></li><li class="depth-3 branch"><a href="beowulf.reader.char-reader.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>char-reader</span></div></a></li><li class="depth-3 branch"><a href="beowulf.reader.generate.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>generate</span></div></a></li><li class="depth-3 branch"><a href="beowulf.reader.macros.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>macros</span></div></a></li><li class="depth-3 branch"><a href="beowulf.reader.parser.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>parser</span></div></a></li><li class="depth-3"><a href="beowulf.reader.simplify.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>simplify</span></div></a></li></ul></div><div class="document" id="content"><div class="doc"><div class="markdown"><h1><a href="#further-reading" name="further-reading"></a>Further Reading</h1>
|
||||
<ol>
|
||||
<li><a href="http://bitsavers.org/pdf/mit/computer_center/Coding_for_the_MIT-IBM_704_Computer_Oct57.pdf">CODING for the MIT-IBM 704 COMPUTER, October 1957</a> This paper is not about Lisp. But it is about the particular individual computer on which Lisp was first implemented, and it is written in part by members of the Lisp team. I have found it useful in understanding the software environment in which, and the constraints under which, Lisp was written.</li>
|
||||
<li><a href="https://www.softwarepreservation.org/projects/LISP/MIT/AIM-001.pdf">MIT AI Memo 1, John McCarthy, September 1958</a> This is, as far as I can find, the earliest specification document of the Lisp project.</li>
|
||||
<li><a href="https://bitsavers.org/pdf/mit/rle_lisp/LISP_I_Programmers_Manual_Mar60.pdf">Lisp 1 Programmer’s Manual, Phyllis Fox, March 1960</a></li>
|
||||
<li><a href="https://www.softwarepreservation.org/projects/LISP/book/LISP%201.5%20Programmers%20Manual.pdf#page=81">Lisp 1.5 Programmer’s Manual, Michael I. Levin, August 1962</a> This book is essential reading: it documents in some detail the first fully realised Lisp language system.</li>
|
||||
<li><a href="https://dl.acm.org/doi/pdf/10.1145/800055.802047#page=3">Early LISP History (1956 - 1959), Herbert Stoyan, August 1984</a></li>
|
||||
<li>
|
||||
<p><a href="http://www.paulgraham.com/rootsoflisp.html">The Roots of Lisp, Paul Graham, 2001</a></p></li>
|
||||
<li>
|
||||
<p><a href="http://www.paulgraham.com/icad.html">The Revenge of the Nerds, Paul Graham, 2002</a> This is mainly about why to use Lisp as a language for modern commercial software, but has useful insights into where it comes from.</p>
|
||||
<blockquote>
|
||||
<p>So the short explanation of why this 1950s language is not obsolete is that it was not technology but math, and math doesn’t get stale.</p>
|
||||
</blockquote></li>
|
||||
</ol></div></div></div></body></html>
|
File diff suppressed because one or more lines are too long
|
@ -1,363 +1,785 @@
|
|||
<!DOCTYPE html PUBLIC ""
|
||||
"">
|
||||
<html><head><meta charset="UTF-8" /><title>beowulf</title><link rel="stylesheet" type="text/css" href="css/default.css" /><link rel="stylesheet" type="text/css" href="css/highlight.css" /><script type="text/javascript" src="js/highlight.min.js"></script><script type="text/javascript" src="js/jquery.min.js"></script><script type="text/javascript" src="js/page_effects.js"></script><script>hljs.initHighlightingOnLoad();</script></head><body><div id="header"><h2>Generated by <a href="https://github.com/weavejester/codox">Codox</a></h2><h1><a href="index.html"><span class="project-title"><span class="project-name">Beowulf</span> <span class="project-version">0.2.1-SNAPSHOT</span></span></a></h1></div><div class="sidebar primary"><h3 class="no-link"><span class="inner">Project</span></h3><ul class="index-link"><li class="depth-1 "><a href="index.html"><div class="inner">Index</div></a></li></ul><h3 class="no-link"><span class="inner">Topics</span></h3><ul><li class="depth-1 current"><a href="intro.html"><div class="inner"><span>beowulf</span></div></a></li><li class="depth-1 "><a href="mexpr.html"><div class="inner"><span>M-Expressions</span></div></a></li></ul><h3 class="no-link"><span class="inner">Namespaces</span></h3><ul><li class="depth-1"><div class="no-link"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>beowulf</span></div></div></li><li class="depth-2 branch"><a href="beowulf.bootstrap.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>bootstrap</span></div></a></li><li class="depth-2 branch"><a href="beowulf.cons-cell.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>cons-cell</span></div></a></li><li class="depth-2 branch"><a href="beowulf.core.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>core</span></div></a></li><li class="depth-2 branch"><a href="beowulf.gendoc.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>gendoc</span></div></a></li><li class="depth-2 branch"><a href="beowulf.host.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>host</span></div></a></li><li class="depth-2 branch"><a href="beowulf.io.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>io</span></div></a></li><li class="depth-2 branch"><a href="beowulf.oblist.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>oblist</span></div></a></li><li class="depth-2 branch"><a href="beowulf.read.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>read</span></div></a></li><li class="depth-2"><div class="no-link"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>reader</span></div></div></li><li class="depth-3 branch"><a href="beowulf.reader.char-reader.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>char-reader</span></div></a></li><li class="depth-3 branch"><a href="beowulf.reader.generate.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>generate</span></div></a></li><li class="depth-3 branch"><a href="beowulf.reader.macros.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>macros</span></div></a></li><li class="depth-3 branch"><a href="beowulf.reader.parser.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>parser</span></div></a></li><li class="depth-3"><a href="beowulf.reader.simplify.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>simplify</span></div></a></li><li class="depth-2"><a href="beowulf.trace.html"><div class="inner"><span class="tree" style="top: -176px;"><span class="top" style="height: 185px;"></span><span class="bottom"></span></span><span>trace</span></div></a></li></ul></div><div class="document" id="content"><div class="doc"><div class="markdown"><h1><a href="#beowulf" name="beowulf"></a>beowulf</h1>
|
||||
<p>LISP 1.5 is to all Lisp dialects as Beowulf is to Emglish literature.</p>
|
||||
<html><head><meta charset="UTF-8" /><title>beowulf</title><link rel="icon" type="image/x-icon" href="../img/beowulf_logo_favicon.png" /><link rel="stylesheet" type="text/css" href="css/default.css" /><link rel="stylesheet" type="text/css" href="css/highlight.css" /><script type="text/javascript" src="js/highlight.min.js"></script><script type="text/javascript" src="js/jquery.min.js"></script><script type="text/javascript" src="js/page_effects.js"></script><script>hljs.initHighlightingOnLoad();</script></head><body><div id="header"><h2>Generated by <a href="https://github.com/weavejester/codox">Codox</a></h2><h1><a href="index.html"><span class="project-title"><span class="project-name">Beowulf</span> <span class="project-version">0.3.0-SNAPSHOT</span></span></a></h1></div><div class="sidebar primary"><h3 class="no-link"><span class="inner">Project</span></h3><ul class="index-link"><li class="depth-1 "><a href="index.html"><div class="inner">Index</div></a></li></ul><h3 class="no-link"><span class="inner">Topics</span></h3><ul><li class="depth-1 "><a href="further_reading.html"><div class="inner"><span>Further Reading</span></div></a></li><li class="depth-1 current"><a href="intro.html"><div class="inner"><span>beowulf</span></div></a></li><li class="depth-1 "><a href="mexpr.html"><div class="inner"><span>Interpreting M-Expressions</span></div></a></li><li class="depth-1 "><a href="values.html"><div class="inner"><span>The properties of the system, and their values</span></div></a></li></ul><h3 class="no-link"><span class="inner">Namespaces</span></h3><ul><li class="depth-1"><div class="no-link"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>beowulf</span></div></div></li><li class="depth-2 branch"><a href="beowulf.bootstrap.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>bootstrap</span></div></a></li><li class="depth-2 branch"><a href="beowulf.cons-cell.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>cons-cell</span></div></a></li><li class="depth-2 branch"><a href="beowulf.core.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>core</span></div></a></li><li class="depth-2 branch"><a href="beowulf.gendoc.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>gendoc</span></div></a></li><li class="depth-2 branch"><a href="beowulf.host.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>host</span></div></a></li><li class="depth-2 branch"><a href="beowulf.interop.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>interop</span></div></a></li><li class="depth-2 branch"><a href="beowulf.io.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>io</span></div></a></li><li class="depth-2 branch"><a href="beowulf.manual.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>manual</span></div></a></li><li class="depth-2 branch"><a href="beowulf.oblist.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>oblist</span></div></a></li><li class="depth-2 branch"><a href="beowulf.read.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>read</span></div></a></li><li class="depth-2"><div class="no-link"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>reader</span></div></div></li><li class="depth-3 branch"><a href="beowulf.reader.char-reader.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>char-reader</span></div></a></li><li class="depth-3 branch"><a href="beowulf.reader.generate.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>generate</span></div></a></li><li class="depth-3 branch"><a href="beowulf.reader.macros.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>macros</span></div></a></li><li class="depth-3 branch"><a href="beowulf.reader.parser.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>parser</span></div></a></li><li class="depth-3"><a href="beowulf.reader.simplify.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>simplify</span></div></a></li></ul></div><div class="document" id="content"><div class="doc"><div class="markdown"><h1><a href="#beowulf" name="beowulf"></a>beowulf</h1>
|
||||
<h2><a href="#þý-liste-cræfte-spræc" name="þý-liste-cræfte-spræc"></a>Þý liste cræfte spræc</h2>
|
||||
<p>LISP 1.5 is to all Lisp dialects as Beowulf is to English literature.</p>
|
||||
<p><img src="https://simon-brooke.github.io/beowulf/docs/img/beowulf_logo_med.png" alt="Beowulf logo" /></p>
|
||||
<h2><a href="#contents" name="contents"></a>Contents</h2>
|
||||
<ul>
|
||||
<li><a href="#what-this-is">What this is</a>
|
||||
<ul>
|
||||
<li><a href="#status">Status</a></li>
|
||||
<li><a href="#but-why-----">BUT WHY?!!?!</a></li>
|
||||
<li><a href="#project-target">Project Target</a></li>
|
||||
<li><a href="#invoking">Invoking</a></li>
|
||||
<li><a href="#building-and-invoking">Building and Invoking</a></li>
|
||||
<li><a href="#reader-macros">Reader macros</a></li>
|
||||
<li><a href="#functions-and-symbols-implemented">Functions and symbols implemented</a></li>
|
||||
<li><a href="#architectural-plan">Architectural plan</a></li>
|
||||
<li><a href="#resources-lisp15lsp">resources/lisp1.5.lsp</a></li>
|
||||
<li><a href="#beowulf-boostrapclj">beowulf/boostrap.clj</a></li>
|
||||
<li><a href="#beowulf-hostclj">beowulf/host.clj</a></li>
|
||||
<li><a href="#beowulf-readclj">beowulf/read.clj</a></li>
|
||||
<li><a href="#commentary">Commentary</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="#installation">Installation</a>
|
||||
<ul>
|
||||
<li><a href="#input-output">Input/output</a></li>
|
||||
<li><a href="#sysout">SYSOUT</a></li>
|
||||
<li><a href="#sysin">SYSIN</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="#learning-lisp-15">Learning Lisp 1.5</a></li>
|
||||
<li><a href="#other-lisp-15-resources">Other Lisp 1.5 resources</a>
|
||||
<ul>
|
||||
<li><a href="#other-implementations">Other implmentations</a></li>
|
||||
<li><a href="#history-resources">History resources</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="#license">License</a></li>
|
||||
</ul>
|
||||
<small><i><a href="http://ecotrust-canada.github.io/markdown-toc/">Table of contents generated with markdown-toc</a></i></small>
|
||||
<h2><a href="#what-this-is" name="what-this-is"></a>What this is</h2>
|
||||
<p>A work-in-progress towards an implementation of Lisp 1.5 in Clojure. The objective is to build a complete and accurate implementation of Lisp 1.5 as described in the manual, with, in so far as is possible, exactly the same bahaviour - except as documented below.</p>
|
||||
<h3><a href="#but-why-" name="but-why-"></a>BUT WHY?!!?!</h3>
|
||||
<p>Because.</p>
|
||||
<p>Because Lisp is the only computer language worth learning, and if a thing is worth learning, it’s worth learning properly; which means going back to the beginning and trying to understand that.</p>
|
||||
<p>Because there is, so far as I know, no working implementation of Lisp 1.5 for modern machines.</p>
|
||||
<p>Because I’m barking mad, and this is therapy.</p>
|
||||
<h3><a href="#status" name="status"></a>Status</h3>
|
||||
<p>Boots to REPL, but few functions yet available.</p>
|
||||
<p>Working Lisp interpreter, but some key features not yet implemented.</p>
|
||||
<ul>
|
||||
<li><a href="https://simon-brooke.github.io/beowulf/">Project website</a>.</li>
|
||||
<li><a href="https://simon-brooke.github.io/beowulf/docs/codox/index.html">Source code documentation</a>.</li>
|
||||
<li><a href="https://simon-brooke.github.io/beowulf/docs/cloverage/index.html">Test Coverage Report</a></li>
|
||||
</ul>
|
||||
<h3><a href="#building-and-invoking" name="building-and-invoking"></a>Building and Invoking</h3>
|
||||
<p>Build with</p>
|
||||
<pre><code>lein uberjar
|
||||
</code></pre>
|
||||
<h3><a href="#project-target" name="project-target"></a>Project Target</h3>
|
||||
<p>The project target is to be able to run the <a href="https://www.softwarepreservation.org/projects/LISP/book/LISP%201.5%20Programmers%20Manual.pdf#page=52">Wang algorithm for the propositional calculus</a> given in chapter 8 of the <em>Lisp 1.5 Programmer’s Manual</em>. When that runs, the project is as far as I am concerned feature complete. I may keep tinkering with it after that and I’ll certainly accept pull requests which are in the spirit of the project (i.e. making Beowulf more usable, and/or implementing parts of Lisp 1.5 which I have not implemented), but this isn’t intended to be a new language for doing real work; it’s an educational and archaeological project, not serious engineering.</p>
|
||||
<p>Some <code>readline</code>-like functionality would be really useful, but my attempt to integrate <a href="https://github.com/jline/jline3">JLine</a> has not (yet) been successful.</p>
|
||||
<p>An in-core structure editor would be an extremely nice thing, and I may well implement one.</p>
|
||||
<p>You are of course welcome to fork the project and do whatever you like with it!</p>
|
||||
<h3><a href="#invoking" name="invoking"></a>Invoking</h3>
|
||||
<p>Invoke with</p>
|
||||
<pre><code>java -jar target/uberjar/beowulf-0.2.1-SNAPSHOT-standalone.jar --help
|
||||
<pre><code>java -jar target/uberjar/beowulf-0.3.0-SNAPSHOT-standalone.jar --help
|
||||
</code></pre>
|
||||
<p>(Obviously, check your version number)</p>
|
||||
<p>Command line arguments as follows:</p>
|
||||
<pre><code> -h, --help Print this message
|
||||
-p PROMPT, --prompt PROMPT Set the REPL prompt to PROMPT
|
||||
-r INITFILE, --read INITFILE Read Lisp functions from the file INITFILE
|
||||
-s, --strict Strictly interpret the Lisp 1.5 language, without extensions.
|
||||
-r INITFILE, --read SYSOUTFILE Read Lisp sysout from the file SYSOUTFILE
|
||||
(defaults to `resources/lisp1.5.lsp`)
|
||||
-s, --strict Strictly interpret the Lisp 1.5 language,
|
||||
without extensions.
|
||||
</code></pre>
|
||||
<p>To end a session, type <code>STOP</code> at the command prompt.</p>
|
||||
<h3><a href="#building-and-invoking" name="building-and-invoking"></a>Building and Invoking</h3>
|
||||
<p>Build with</p>
|
||||
<pre><code>lein uberjar
|
||||
</code></pre>
|
||||
<h3><a href="#reader-macros" name="reader-macros"></a>Reader macros</h3>
|
||||
<p>Currently <code>SETQ</code> and <code>DEFUN</code> are implemented as reader macros, sort of. It would now be possible to reimplement them as <code>FEXPRs</code> and so the reader macro functionality will probably go away.</p>
|
||||
<h3><a href="#functions-and-symbols-implemented" name="functions-and-symbols-implemented"></a>Functions and symbols implemented</h3>
|
||||
<p>The following functions and symbols are implemented:</p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Symbol </th>
|
||||
<th>Function </th>
|
||||
<th>Type </th>
|
||||
<th>Signature </th>
|
||||
<th>Implementation </th>
|
||||
<th>Documentation </th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>NIL </td>
|
||||
<td>Lisp variable </td>
|
||||
<td>? </td>
|
||||
<td>null </td>
|
||||
<td>? </td>
|
||||
<td> </td>
|
||||
<td>see manual pages <a href="#page22">22</a>, <a href="#page69">69</a> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>T </td>
|
||||
<td>Lisp variable </td>
|
||||
<td>? </td>
|
||||
<td>null </td>
|
||||
<td>? </td>
|
||||
<td> </td>
|
||||
<td>see manual pages <a href="#page22">22</a>, <a href="#page69">69</a> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>F </td>
|
||||
<td>Lisp variable </td>
|
||||
<td>? </td>
|
||||
<td>null </td>
|
||||
<td>? </td>
|
||||
<td> </td>
|
||||
<td>see manual pages <a href="#page22">22</a>, <a href="#page69">69</a> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ADD1 </td>
|
||||
<td>Host function </td>
|
||||
<td>([x]) </td>
|
||||
<td>Host lambda function </td>
|
||||
<td>? </td>
|
||||
<td> </td>
|
||||
<td>? </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>AND </td>
|
||||
<td>Host function </td>
|
||||
<td>([& args]) </td>
|
||||
<td>Host lambda function </td>
|
||||
<td>? </td>
|
||||
<td>PREDICATE </td>
|
||||
<td><code>T</code> if and only if none of my <code>args</code> evaluate to either <code>F</code> or <code>NIL</code>, else <code>F</code>. In <code>beowulf.host</code> principally because I don’t yet feel confident to define varargs functions in Lisp. </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>APPEND </td>
|
||||
<td>Host function </td>
|
||||
<td>([x y]) </td>
|
||||
<td>Append the the elements of <code>y</code> to the elements of <code>x</code>. All args are assumed to be <code>beowulf.cons-cell/ConsCell</code> objects. See page 11 of the Lisp 1.5 Programmers Manual. </td>
|
||||
<td>Lisp lambda function </td>
|
||||
<td>? </td>
|
||||
<td> </td>
|
||||
<td>see manual pages <a href="#page11">11</a>, <a href="#page61">61</a> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>APPLY </td>
|
||||
<td>Host function </td>
|
||||
<td>([function args environment depth]) </td>
|
||||
<td>Host lambda function </td>
|
||||
<td>? </td>
|
||||
<td> </td>
|
||||
<td>Apply this <code>function</code> to these <code>arguments</code> in this <code>environment</code> and return the result. For bootstrapping, at least, a version of APPLY written in Clojure. All args are assumed to be symbols or <code>beowulf.cons-cell/ConsCell</code> objects. See page 13 of the Lisp 1.5 Programmers Manual. </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ASSOC </td>
|
||||
<td>Lisp lambda function, Host lambda function </td>
|
||||
<td>? </td>
|
||||
<td>? </td>
|
||||
<td>If a is an association list such as the one formed by PAIRLIS in the above example, then assoc will produce the first pair whose first term is x. Thus it is a table searching function. All args are assumed to be <code>beowulf.cons-cell/ConsCell</code> objects. See page 12 of the Lisp 1.5 Programmers Manual. <strong>NOTE THAT</strong> this function is overridden by an implementation in Lisp, but is currently still present for bootstrapping. </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ATOM </td>
|
||||
<td>Host function </td>
|
||||
<td>([x]) </td>
|
||||
<td>Host lambda function </td>
|
||||
<td>? </td>
|
||||
<td>PREDICATE </td>
|
||||
<td>Returns <code>T</code> if and only if the argument <code>x</code> is bound to an atom; else <code>F</code>. It is not clear to me from the documentation whether <code>(ATOM 7)</code> should return <code>T</code> or <code>F</code>. I’m going to assume <code>T</code>. </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>CAR </td>
|
||||
<td>Host lambda function </td>
|
||||
<td>? </td>
|
||||
<td> </td>
|
||||
<td>Return the item indicated by the first pointer of a pair. NIL is treated specially: the CAR of NIL is NIL. </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>CAAAAR </td>
|
||||
<td>Lisp lambda function </td>
|
||||
<td>? </td>
|
||||
<td>? </td>
|
||||
<td>? </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>CAAADR </td>
|
||||
<td>Lisp lambda function </td>
|
||||
<td>? </td>
|
||||
<td>? </td>
|
||||
<td>? </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>CAAAR </td>
|
||||
<td>Lisp lambda function </td>
|
||||
<td>? </td>
|
||||
<td>? </td>
|
||||
<td>? </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>CAADAR </td>
|
||||
<td>Lisp lambda function </td>
|
||||
<td>? </td>
|
||||
<td>? </td>
|
||||
<td>? </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>CAADDR </td>
|
||||
<td>Lisp lambda function </td>
|
||||
<td>? </td>
|
||||
<td>? </td>
|
||||
<td>? </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>CAADR </td>
|
||||
<td>Lisp lambda function </td>
|
||||
<td>? </td>
|
||||
<td>? </td>
|
||||
<td>? </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>CAAR </td>
|
||||
<td>Lisp lambda function </td>
|
||||
<td>? </td>
|
||||
<td>? </td>
|
||||
<td>? </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>CADAAR </td>
|
||||
<td>Lisp lambda function </td>
|
||||
<td>? </td>
|
||||
<td>? </td>
|
||||
<td>? </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>CADADR </td>
|
||||
<td>Lisp lambda function </td>
|
||||
<td>? </td>
|
||||
<td>? </td>
|
||||
<td>? </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>CADAR </td>
|
||||
<td>Lisp lambda function </td>
|
||||
<td>? </td>
|
||||
<td>? </td>
|
||||
<td>? </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>CADDAR </td>
|
||||
<td>Lisp lambda function </td>
|
||||
<td>? </td>
|
||||
<td>? </td>
|
||||
<td>? </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>CADDDR </td>
|
||||
<td>Lisp lambda function </td>
|
||||
<td>? </td>
|
||||
<td>? </td>
|
||||
<td>? </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>CADDR </td>
|
||||
<td>Lisp lambda function </td>
|
||||
<td>? </td>
|
||||
<td>? </td>
|
||||
<td>? </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>CADR </td>
|
||||
<td>Lisp lambda function </td>
|
||||
<td>? </td>
|
||||
<td>? </td>
|
||||
<td>? </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>CDAAAR </td>
|
||||
<td>Lisp lambda function </td>
|
||||
<td>? </td>
|
||||
<td>? </td>
|
||||
<td>? </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>CDAADR </td>
|
||||
<td>Lisp lambda function </td>
|
||||
<td>? </td>
|
||||
<td>? </td>
|
||||
<td>? </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>CDAAR </td>
|
||||
<td>Lisp lambda function </td>
|
||||
<td>? </td>
|
||||
<td>? </td>
|
||||
<td>? </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>CDADAR </td>
|
||||
<td>Lisp lambda function </td>
|
||||
<td>? </td>
|
||||
<td>? </td>
|
||||
<td>? </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>CDADDR </td>
|
||||
<td>Lisp lambda function </td>
|
||||
<td>? </td>
|
||||
<td>? </td>
|
||||
<td>? </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>CDADR </td>
|
||||
<td>Lisp lambda function </td>
|
||||
<td>? </td>
|
||||
<td>? </td>
|
||||
<td>? </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>CDAR </td>
|
||||
<td>Lisp lambda function </td>
|
||||
<td>? </td>
|
||||
<td>? </td>
|
||||
<td>? </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>CDDAAR </td>
|
||||
<td>Lisp lambda function </td>
|
||||
<td>? </td>
|
||||
<td>? </td>
|
||||
<td>? </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>CDDADR </td>
|
||||
<td>Lisp lambda function </td>
|
||||
<td>? </td>
|
||||
<td>? </td>
|
||||
<td>? </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>CDDAR </td>
|
||||
<td>Lisp lambda function </td>
|
||||
<td>? </td>
|
||||
<td>? </td>
|
||||
<td>? </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>CDDDAR </td>
|
||||
<td>Lisp lambda function </td>
|
||||
<td>? </td>
|
||||
<td>? </td>
|
||||
<td>? </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>CDDDDR </td>
|
||||
<td>Lisp lambda function </td>
|
||||
<td>? </td>
|
||||
<td>? </td>
|
||||
<td>? </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>CDDDR </td>
|
||||
<td>Lisp lambda function </td>
|
||||
<td>? </td>
|
||||
<td>? </td>
|
||||
<td>? </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>CDDR </td>
|
||||
<td>Lisp lambda function </td>
|
||||
<td>? </td>
|
||||
<td>? </td>
|
||||
<td>null </td>
|
||||
<td>? </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>CDR </td>
|
||||
<td>Host lambda function </td>
|
||||
<td>? </td>
|
||||
<td>null </td>
|
||||
<td>? </td>
|
||||
<td> </td>
|
||||
<td>Return the item indicated by the second pointer of a pair. NIL is treated specially: the CDR of NIL is NIL. </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>CONS </td>
|
||||
<td>Host lambda function </td>
|
||||
<td>? </td>
|
||||
<td>null </td>
|
||||
<td> </td>
|
||||
<td>Construct a new instance of cons cell with this <code>car</code> and <code>cdr</code>. </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>CONSP </td>
|
||||
<td>Host lambda function </td>
|
||||
<td>? </td>
|
||||
<td>? </td>
|
||||
<td>Return <code>T</code> if object <code>o</code> is a cons cell, else <code>F</code>. <strong>NOTE THAT</strong> this is an extension function, not available in strct mode. I believe that Lisp 1.5 did not have any mechanism for testing whether an argument was, or was not, a cons cell. </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>COPY </td>
|
||||
<td>Lisp function </td>
|
||||
<td>(X) </td>
|
||||
<td>Lisp lambda function </td>
|
||||
<td>? </td>
|
||||
<td> </td>
|
||||
<td>see manual pages <a href="#page62">62</a> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>DEFINE </td>
|
||||
<td>Host function </td>
|
||||
<td>([args]) </td>
|
||||
<td>Bootstrap-only version of <code>DEFINE</code> which, post boostrap, can be overwritten in LISP. The single argument to <code>DEFINE</code> should be an assoc list which should be nconc’ed onto the front of the oblist. Broadly, (SETQ OBLIST (NCONC ARG1 OBLIST)) </td>
|
||||
<td>Host lambda function </td>
|
||||
<td>? </td>
|
||||
<td>PSEUDO-FUNCTION </td>
|
||||
<td>Bootstrap-only version of <code>DEFINE</code> which, post boostrap, can be overwritten in LISP. The single argument to <code>DEFINE</code> should be an association list of symbols to lambda functions. See page 58 of the manual. </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>DIFFERENCE </td>
|
||||
<td>Host function </td>
|
||||
<td>([x y]) </td>
|
||||
<td>Host lambda function </td>
|
||||
<td>? </td>
|
||||
<td> </td>
|
||||
<td>? </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>DIVIDE </td>
|
||||
<td>Lisp function </td>
|
||||
<td>(X Y) </td>
|
||||
<td>Lisp lambda function </td>
|
||||
<td>? </td>
|
||||
<td> </td>
|
||||
<td>see manual pages <a href="#page26">26</a>, <a href="#page64">64</a> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>DOC </td>
|
||||
<td>Host lambda function </td>
|
||||
<td>? </td>
|
||||
<td>? </td>
|
||||
<td>Open the page for this <code>symbol</code> in the Lisp 1.5 manual, if known, in the default web browser. <strong>NOTE THAT</strong> this is an extension function, not available in strct mode. </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>EFFACE </td>
|
||||
<td>Lisp lambda function </td>
|
||||
<td>? </td>
|
||||
<td>PSEUDO-FUNCTION </td>
|
||||
<td>see manual pages <a href="#page63">63</a> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ERROR </td>
|
||||
<td>Host function </td>
|
||||
<td>([& args]) </td>
|
||||
<td>Host lambda function </td>
|
||||
<td>? </td>
|
||||
<td>PSEUDO-FUNCTION </td>
|
||||
<td>Throw an error </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>EQ </td>
|
||||
<td>Host function </td>
|
||||
<td>([x y]) </td>
|
||||
<td>Host lambda function </td>
|
||||
<td>? </td>
|
||||
<td>PREDICATE </td>
|
||||
<td>Returns <code>T</code> if and only if both <code>x</code> and <code>y</code> are bound to the same atom, else <code>NIL</code>. </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>EQUAL </td>
|
||||
<td>Host function </td>
|
||||
<td>([x y]) </td>
|
||||
<td>Host lambda function </td>
|
||||
<td>? </td>
|
||||
<td>PREDICATE </td>
|
||||
<td>This is a predicate that is true if its two arguments are identical S-expressions, and false if they are different. (The elementary predicate <code>EQ</code> is defined only for atomic arguments.) The definition of <code>EQUAL</code> is an example of a conditional expression inside a conditional expression. NOTE: returns <code>F</code> on failure, not <code>NIL</code> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>EVAL </td>
|
||||
<td>Host function </td>
|
||||
<td>([expr] [expr env depth]) </td>
|
||||
<td>Evaluate this <code>expr</code> and return the result. If <code>environment</code> is not passed, it defaults to the current value of the global object list. The <code>depth</code> argument is part of the tracing system and should not be set by user code. All args are assumed to be numbers, symbols or <code>beowulf.cons-cell/ConsCell</code> objects. </td>
|
||||
<td>Host lambda function </td>
|
||||
<td>? </td>
|
||||
<td> </td>
|
||||
<td>Evaluate this <code>expr</code> and return the result. If <code>environment</code> is not passed, it defaults to the current value of the global object list. The <code>depth</code> argument is part of the tracing system and should not be set by user code. All args are assumed to be numbers, symbols or <code>beowulf.cons-cell/ConsCell</code> objects. However, if called with just a single arg, <code>expr</code>, I’ll assume it’s being called from the Clojure REPL and will coerce the <code>expr</code> to <code>ConsCell</code>. </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>FACTORIAL </td>
|
||||
<td>Lisp lambda function </td>
|
||||
<td>? </td>
|
||||
<td>? </td>
|
||||
<td>? </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>FIXP </td>
|
||||
<td>Host function </td>
|
||||
<td>([x]) </td>
|
||||
<td>Host lambda function </td>
|
||||
<td>? </td>
|
||||
<td>PREDICATE </td>
|
||||
<td>? </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>GENSYM </td>
|
||||
<td>Host function </td>
|
||||
<td>([]) </td>
|
||||
<td>Host lambda function </td>
|
||||
<td>? </td>
|
||||
<td> </td>
|
||||
<td>Generate a unique symbol. </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>GET </td>
|
||||
<td>Lisp function </td>
|
||||
<td>(X Y) </td>
|
||||
<td>Host lambda function </td>
|
||||
<td>? </td>
|
||||
<td> </td>
|
||||
<td>From the manual: ‘<code>get</code> is somewhat like <code>prop</code>; however its value is car of the rest of the list if the <code>indicator</code> is found, and NIL otherwise.’ It’s clear that <code>GET</code> is expected to be defined in terms of <code>PROP</code>, but we can’t implement <code>PROP</code> here because we lack <code>EVAL</code>; and we can’t have <code>EVAL</code> here because both it and <code>APPLY</code> depends on <code>GET</code>. OK, It’s worse than that: the statement of the definition of <code>GET</code> (and of) <code>PROP</code> on page 59 says that the first argument to each must be a list; But the in the definition of <code>ASSOC</code> on page 70, when <code>GET</code> is called its first argument is always an atom. Since it’s <code>ASSOC</code> and <code>EVAL</code> which I need to make work, I’m going to assume that page 59 is wrong. </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>GREATERP </td>
|
||||
<td>Host function </td>
|
||||
<td>([x y]) </td>
|
||||
<td>Host lambda function </td>
|
||||
<td>? </td>
|
||||
<td>PREDICATE </td>
|
||||
<td>? </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>INTEROP </td>
|
||||
<td>Host function </td>
|
||||
<td>([fn-symbol args]) </td>
|
||||
<td>Clojure (or other host environment) interoperation API. <code>fn-symbol</code> is expected to be either 1. a symbol bound in the host environment to a function; or 2. a sequence (list) of symbols forming a qualified path name bound to a function. Lower case characters cannot normally be represented in Lisp 1.5, so both the upper case and lower case variants of <code>fn-symbol</code> will be tried. If the function you’re looking for has a mixed case name, that is not currently accessible. <code>args</code> is expected to be a Lisp 1.5 list of arguments to be passed to that function. Return value must be something acceptable to Lisp 1.5, so either a symbol, a number, or a Lisp 1.5 list. If <code>fn-symbol</code> is not found (even when cast to lower case), or is not a function, or the value returned cannot be represented in Lisp 1.5, an exception is thrown with <code>:cause</code> bound to <code>:interop</code> and <code>:detail</code> set to a value representing the actual problem. </td>
|
||||
<td>Host lambda function </td>
|
||||
<td>? </td>
|
||||
<td>? </td>
|
||||
<td>? </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>INTERSECTION </td>
|
||||
<td>Lisp function </td>
|
||||
<td>(X Y) </td>
|
||||
<td>Lisp lambda function </td>
|
||||
<td>? </td>
|
||||
<td>? </td>
|
||||
<td>? </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>LENGTH </td>
|
||||
<td>Lisp function </td>
|
||||
<td>(L) </td>
|
||||
<td>Lisp lambda function </td>
|
||||
<td>? </td>
|
||||
<td> </td>
|
||||
<td>see manual pages <a href="#page62">62</a> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>LESSP </td>
|
||||
<td>Host function </td>
|
||||
<td>([x y]) </td>
|
||||
<td>Host lambda function </td>
|
||||
<td>? </td>
|
||||
<td>PREDICATE </td>
|
||||
<td>? </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>MAPLIST </td>
|
||||
<td>Lisp lambda function </td>
|
||||
<td>? </td>
|
||||
<td>FUNCTIONAL </td>
|
||||
<td>see manual pages <a href="#page20">20</a>, <a href="#page21">21</a>, <a href="#page63">63</a> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>MEMBER </td>
|
||||
<td>Lisp function </td>
|
||||
<td>(A X) </td>
|
||||
<td>Lisp lambda function </td>
|
||||
<td>? </td>
|
||||
<td>PREDICATE </td>
|
||||
<td>see manual pages <a href="#page11">11</a>, <a href="#page62">62</a> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>MINUSP </td>
|
||||
<td>Lisp function </td>
|
||||
<td>(X) </td>
|
||||
<td>Lisp lambda function </td>
|
||||
<td>? </td>
|
||||
<td>PREDICATE </td>
|
||||
<td>see manual pages <a href="#page26">26</a>, <a href="#page64">64</a> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>NOT </td>
|
||||
<td>Lisp lambda function </td>
|
||||
<td>? </td>
|
||||
<td>PREDICATE </td>
|
||||
<td>see manual pages <a href="#page21">21</a>, <a href="#page23">23</a>, <a href="#page58">58</a> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>NULL </td>
|
||||
<td>Lisp function </td>
|
||||
<td>(X) </td>
|
||||
<td>Lisp lambda function </td>
|
||||
<td>? </td>
|
||||
<td>PREDICATE </td>
|
||||
<td>see manual pages <a href="#page11">11</a>, <a href="#page57">57</a> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>NUMBERP </td>
|
||||
<td>Host function </td>
|
||||
<td>([x]) </td>
|
||||
<td>Host lambda function </td>
|
||||
<td>? </td>
|
||||
<td>PREDICATE </td>
|
||||
<td>? </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>OBLIST </td>
|
||||
<td>Host function </td>
|
||||
<td>([]) </td>
|
||||
<td>Host lambda function </td>
|
||||
<td>? </td>
|
||||
<td> </td>
|
||||
<td>Return a list of the symbols currently bound on the object list. <strong>NOTE THAT</strong> in the Lisp 1.5 manual, footnote at the bottom of page 69, it implies that an argument can be passed but I’m not sure of the semantics of this. </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ONEP </td>
|
||||
<td>Lisp function </td>
|
||||
<td>(X) </td>
|
||||
<td>Lisp lambda function </td>
|
||||
<td>? </td>
|
||||
<td>PREDICATE </td>
|
||||
<td>see manual pages <a href="#page26">26</a>, <a href="#page64">64</a> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>OR </td>
|
||||
<td>Host lambda function </td>
|
||||
<td>? </td>
|
||||
<td>PREDICATE </td>
|
||||
<td><code>T</code> if and only if at least one of my <code>args</code> evaluates to something other than either <code>F</code> or <code>NIL</code>, else <code>F</code>. In <code>beowulf.host</code> principally because I don’t yet feel confident to define varargs functions in Lisp. </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>PAIR </td>
|
||||
<td>Lisp function </td>
|
||||
<td>(X Y) </td>
|
||||
<td>Lisp lambda function </td>
|
||||
<td>? </td>
|
||||
<td> </td>
|
||||
<td>see manual pages <a href="#page60">60</a> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>PAIRLIS </td>
|
||||
<td>Lisp lambda function, Host lambda function </td>
|
||||
<td>? </td>
|
||||
<td>? </td>
|
||||
<td>This function gives the list of pairs of corresponding elements of the lists <code>x</code> and <code>y</code>, and APPENDs this to the list <code>a</code>. The resultant list of pairs, which is like a table with two columns, is called an association list. Eessentially, it builds the environment on the stack, implementing shallow binding. All args are assumed to be <code>beowulf.cons-cell/ConsCell</code> objects. See page 12 of the Lisp 1.5 Programmers Manual. <strong>NOTE THAT</strong> this function is overridden by an implementation in Lisp, but is currently still present for bootstrapping. </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>PLUS </td>
|
||||
<td>Host function </td>
|
||||
<td>([& args]) </td>
|
||||
<td>Host lambda function </td>
|
||||
<td>? </td>
|
||||
<td> </td>
|
||||
<td>? </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>PRETTY </td>
|
||||
<td> </td>
|
||||
<td>? </td>
|
||||
<td>? </td>
|
||||
<td>null </td>
|
||||
<td>? </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>PRINT </td>
|
||||
<td> </td>
|
||||
<td>? </td>
|
||||
<td>null </td>
|
||||
<td>PSEUDO-FUNCTION </td>
|
||||
<td>see manual pages <a href="#page65">65</a>, <a href="#page84">84</a> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>PROG </td>
|
||||
<td>Host nlambda function </td>
|
||||
<td>? </td>
|
||||
<td> </td>
|
||||
<td>The accursed <code>PROG</code> feature. See page 71 of the manual. Lisp 1.5 introduced <code>PROG</code>, and most Lisps have been stuck with it ever since. It introduces imperative programming into what should be a pure functional language, and consequently it’s going to be a pig to implement. Broadly, <code>PROG</code> is a variadic pseudo function called as a <code>FEXPR</code> (or possibly an <code>FSUBR</code>, although I’m not presently sure that would even work.) The arguments, which are unevaluated, are a list of forms, the first of which is expected to be a list of symbols which will be treated as names of variables within the program, and the rest of which (the ‘program body’) are either lists or symbols. Lists are treated as Lisp expressions which may be evaulated in turn. Symbols are treated as targets for the <code>GO</code> statement. <strong>GO:</strong> A <code>GO</code> statement takes the form of <code>(GO target)</code>, where <code>target</code> should be one of the symbols which occur at top level among that particular invocation of <code>PROG</code>s arguments. A <code>GO</code> statement may occur at top level in a PROG, or in a clause of a <code>COND</code> statement in a <code>PROG</code>, but not in a function called from the <code>PROG</code> statement. When a <code>GO</code> statement is evaluated, execution should transfer immediately to the expression which is the argument list immediately following the symbol which is its target. If the target is not found, an error with the code <code>A6</code> should be thrown. <strong>RETURN:</strong> A <code>RETURN</code> statement takes the form <code>(RETURN value)</code>, where <code>value</code> is any value. Following the evaluation of a <code>RETURN</code> statement, the <code>PROG</code> should immediately exit without executing any further expressions, returning the value. <strong>SET and SETQ:</strong> In addition to the above, if a <code>SET</code> or <code>SETQ</code> expression is encountered in any expression within the <code>PROG</code> body, it should affect not the global object list but instead only the local variables of the program. <strong>COND:</strong> In <strong>strict</strong> mode, when in normal execution, a <code>COND</code> statement none of whose clauses match should not return <code>NIL</code> but should throw an error with the code <code>A3</code>… <em>except</em> that inside a <code>PROG</code> body, it should not do so. <em>sigh</em>. <strong>Flow of control:</strong> Apart from the exceptions specified above, expressions in the program body are evaluated sequentially. If execution reaches the end of the program body, <code>NIL</code> is returned. Got all that? Good. </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>PROP </td>
|
||||
<td>Lisp function </td>
|
||||
<td>(X Y U) </td>
|
||||
<td>Lisp lambda function </td>
|
||||
<td>? </td>
|
||||
<td>FUNCTIONAL </td>
|
||||
<td>see manual pages <a href="#page59">59</a> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>QUOTE </td>
|
||||
<td>Lisp lambda function </td>
|
||||
<td>? </td>
|
||||
<td> </td>
|
||||
<td>see manual pages <a href="#page10">10</a>, <a href="#page22">22</a>, <a href="#page71">71</a> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>QUOTIENT </td>
|
||||
<td>Host function </td>
|
||||
<td>([x y]) </td>
|
||||
<td>Host lambda function </td>
|
||||
<td>? </td>
|
||||
<td> </td>
|
||||
<td>I’m not certain from the documentation whether Lisp 1.5 <code>QUOTIENT</code> returned the integer part of the quotient, or a realnum representing the whole quotient. I am for now implementing the latter. </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>RANGE </td>
|
||||
<td>Lisp lambda function </td>
|
||||
<td>? </td>
|
||||
<td>? </td>
|
||||
<td>? </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>READ </td>
|
||||
<td>Host function </td>
|
||||
<td>([] [input]) </td>
|
||||
<td>Host lambda function </td>
|
||||
<td>? </td>
|
||||
<td>PSEUDO-FUNCTION </td>
|
||||
<td>An implementation of a Lisp reader sufficient for bootstrapping; not necessarily the final Lisp reader. <code>input</code> should be either a string representation of a LISP expression, or else an input stream. A single form will be read. </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>REMAINDER </td>
|
||||
<td>Host function </td>
|
||||
<td>([x y]) </td>
|
||||
<td>Host lambda function </td>
|
||||
<td>? </td>
|
||||
<td> </td>
|
||||
<td>? </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>REPEAT </td>
|
||||
<td>Lisp function </td>
|
||||
<td>(N X) </td>
|
||||
<td>Lisp lambda function </td>
|
||||
<td>? </td>
|
||||
<td>? </td>
|
||||
<td>? </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>RPLACA </td>
|
||||
<td>Host function </td>
|
||||
<td>([cell value]) </td>
|
||||
<td>Host lambda function </td>
|
||||
<td>? </td>
|
||||
<td>PSEUDO-FUNCTION </td>
|
||||
<td>Replace the CAR pointer of this <code>cell</code> with this <code>value</code>. Dangerous, should really not exist, but does in Lisp 1.5 (and was important for some performance hacks in early Lisps) </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>RPLACD </td>
|
||||
<td>Host function </td>
|
||||
<td>([cell value]) </td>
|
||||
<td>Host lambda function </td>
|
||||
<td>? </td>
|
||||
<td>PSEUDO-FUNCTION </td>
|
||||
<td>Replace the CDR pointer of this <code>cell</code> with this <code>value</code>. Dangerous, should really not exist, but does in Lisp 1.5 (and was important for some performance hacks in early Lisps) </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>SEARCH </td>
|
||||
<td>Lisp lambda function </td>
|
||||
<td>? </td>
|
||||
<td>FUNCTIONAL </td>
|
||||
<td>see manual pages <a href="#page63">63</a> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>SET </td>
|
||||
<td>Host function </td>
|
||||
<td>([symbol val]) </td>
|
||||
<td>Host lambda function </td>
|
||||
<td>? </td>
|
||||
<td>PSEUDO-FUNCTION </td>
|
||||
<td>Implementation of SET in Clojure. Add to the <code>oblist</code> a binding of the value of <code>var</code> to the value of <code>val</code>. NOTE WELL: this is not SETQ! </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>SUB1 </td>
|
||||
<td>Lisp function </td>
|
||||
<td>(N) </td>
|
||||
<td>Lisp lambda function, Host lambda function </td>
|
||||
<td>? </td>
|
||||
<td> </td>
|
||||
<td>? </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>SUB2 </td>
|
||||
<td>Lisp lambda function </td>
|
||||
<td>? </td>
|
||||
<td>? </td>
|
||||
<td>? </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>SUBLIS </td>
|
||||
<td>Lisp lambda function </td>
|
||||
<td>? </td>
|
||||
<td> </td>
|
||||
<td>see manual pages <a href="#page12">12</a>, <a href="#page61">61</a> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>SUBST </td>
|
||||
<td>Lisp lambda function </td>
|
||||
<td>? </td>
|
||||
<td> </td>
|
||||
<td>see manual pages <a href="#page11">11</a>, <a href="#page61">61</a> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>SYSIN </td>
|
||||
<td>Host function </td>
|
||||
<td>([filename]) </td>
|
||||
<td>Read the contents of the file at this <code>filename</code> into the object list. If the file is not a valid Beowulf sysout file, this will probably corrupt the system, you have been warned. File paths will be considered relative to the filepath set when starting Lisp. It is intended that sysout files can be read both from resources within the jar file, and from the file system. If a named file exists in both the file system and the resources, the file system will be preferred. <strong>NOTE THAT</strong> if the provided <code>filename</code> does not end with <code>.lsp</code> (which, if you’re writing it from the Lisp REPL, it won’t), the extension <code>.lsp</code> will be appended. </td>
|
||||
<td>Host lambda function </td>
|
||||
<td>? </td>
|
||||
<td>? </td>
|
||||
<td>Read the contents of the file at this <code>filename</code> into the object list. If the file is not a valid Beowulf sysout file, this will probably corrupt the system, you have been warned. File paths will be considered relative to the filepath set when starting Lisp. It is intended that sysout files can be read both from resources within the jar file, and from the file system. If a named file exists in both the file system and the resources, the file system will be preferred. <strong>NOTE THAT</strong> if the provided <code>filename</code> does not end with <code>.lsp</code> (which, if you’re writing it from the Lisp REPL, it won’t), the extension <code>.lsp</code> will be appended. <strong>NOTE THAT</strong> this is an extension function, not available in strct mode. </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>SYSOUT </td>
|
||||
<td>Host function </td>
|
||||
<td>([] [filepath]) </td>
|
||||
<td>Dump the current content of the object list to file. If no <code>filepath</code> is specified, a file name will be constructed of the symbol <code>Sysout</code> and the current date. File paths will be considered relative to the filepath set when starting Lisp. </td>
|
||||
<td>Host lambda function </td>
|
||||
<td>? </td>
|
||||
<td>? </td>
|
||||
<td>Dump the current content of the object list to file. If no <code>filepath</code> is specified, a file name will be constructed of the symbol <code>Sysout</code> and the current date. File paths will be considered relative to the filepath set when starting Lisp. <strong>NOTE THAT</strong> this is an extension function, not available in strct mode. </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>TERPRI </td>
|
||||
<td> </td>
|
||||
<td>? </td>
|
||||
<td>null </td>
|
||||
<td>? </td>
|
||||
<td>PSEUDO-FUNCTION </td>
|
||||
<td>see manual pages <a href="#page65">65</a>, <a href="#page84">84</a> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>TIMES </td>
|
||||
<td>Host function </td>
|
||||
<td>([& args]) </td>
|
||||
<td>Host lambda function </td>
|
||||
<td>? </td>
|
||||
<td> </td>
|
||||
<td>? </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>TRACE </td>
|
||||
<td>Host lambda function </td>
|
||||
<td>? </td>
|
||||
<td>PSEUDO-FUNCTION </td>
|
||||
<td>Add this <code>s</code> to the set of symbols currently being traced. If <code>s</code> is not a symbol or sequence of symbols, does nothing. </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>UNION </td>
|
||||
<td>Lisp lambda function </td>
|
||||
<td>? </td>
|
||||
<td>? </td>
|
||||
<td>null </td>
|
||||
<td>? </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>UNTRACE </td>
|
||||
<td>Host lambda function </td>
|
||||
<td>? </td>
|
||||
<td>null </td>
|
||||
<td>? </td>
|
||||
<td>PSEUDO-FUNCTION </td>
|
||||
<td>Remove this <code>s</code> from the set of symbols currently being traced. If <code>s</code> is not a symbol or sequence of symbols, does nothing. </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ZEROP </td>
|
||||
<td>Lisp function </td>
|
||||
<td>(N) </td>
|
||||
<td>Lisp lambda function </td>
|
||||
<td>? </td>
|
||||
<td>PREDICATE </td>
|
||||
<td>see manual pages <a href="#page26">26</a>, <a href="#page64">64</a> </td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p>Functions described as ‘Lisp function’ above are defined in the default sysout file, <code>resources/lisp1.5.lsp</code>, which will be loaded by default unless you specify another initfile on the command line.</p>
|
||||
<p>Functions described as ‘Host function’ are implemented in Clojure, but if you’re brave you can redefine them in Lisp and the Lisp definitions will take precedence over the Clojure implementations.</p>
|
||||
<h3><a href="#architectural-plan" name="architectural-plan"></a>Architectural plan</h3>
|
||||
<p>Not everything documented in this section is yet built. It indicates the direction of travel and intended destination, not the current state.</p>
|
||||
<h4><a href="#resources-lisp1-5-lsp" name="resources-lisp1-5-lsp"></a>resources/lisp1.5.lsp</h4>
|
||||
|
@ -381,19 +803,18 @@
|
|||
<li>It reads the meta-expression language <code>MEXPR</code> in addition to the symbolic expression language <code>SEXPR</code>, which I do not believe the Lisp 1.5 reader ever did;</li>
|
||||
<li>It treats everything between a double semi-colon and an end of line as a comment, as most modern Lisps do; but I do not believe Lisp 1.5 had this feature.</li>
|
||||
</ol>
|
||||
<h3><a href="#but-why-" name="but-why-"></a>BUT WHY?!!?!</h3>
|
||||
<p>Because.</p>
|
||||
<p>Because Lisp is the only computer language worth learning, and if a thing is worth learning, it’s worth learning properly; which means going back to the beginning and trying to understand that.</p>
|
||||
<p>Because there is, so far as I know, no working implementation of Lisp 1.5 for modern machines.</p>
|
||||
<p>Because I’m barking mad, and this is therapy.</p>
|
||||
<h3><a href="#commentary" name="commentary"></a>Commentary</h3>
|
||||
<p>What’s surprised me in working on this is how much more polished Lisp 1.5 is than legend had led me to believe. The language is remarkably close to <a href="http://www.softwarepreservation.org/projects/LISP/standard_lisp_family/#Portable_Standard_LISP_">Portable Standard Lisp</a> which is in my opinion one of the best and most usable early Lisp implementations. </p>
|
||||
<p>What’s even more surprising is how faithful a reimplementation of Lisp 1.5 the first Lisp dialect I learned, <a href="https://en.wikipedia.org/wiki/Acornsoft_LISP">Acornsoft Lisp</a>, turns out to have been.</p>
|
||||
<p>I’m convinced you could still use Lisp 1.5 for interesting and useful software (which isn’t to say that modern Lisps aren’t better, but this is software which is almost sixty years old).</p>
|
||||
<h2><a href="#installation" name="installation"></a>Installation</h2>
|
||||
<p>At present, clone the source and build it using</p>
|
||||
<p><code>lein uberjar</code>.</p>
|
||||
<p>You will require to have <a href="https://leiningen.org/">Leiningen</a> installed.</p>
|
||||
<p>Download the latest <a href="https://github.com/simon-brooke/beowulf/releases">release ‘uberjar’</a> and run it using:</p>
|
||||
<pre><code class="bash"> java -jar <path name of uberjar>
|
||||
</code></pre>
|
||||
<p>Or clone the source and build it using:</p>
|
||||
<pre><code class="bash"> lein uberjar`
|
||||
</code></pre>
|
||||
<p>To build it you will require to have <a href="https://leiningen.org/">Leiningen</a> installed.</p>
|
||||
<h3><a href="#input-output" name="input-output"></a>Input/output</h3>
|
||||
<p>Lisp 1.5 greatly predates modern computers. It had a facility to print to a line printer, or to punch cards on a punch-card machine, and it had a facility to read system images in from tape; but there’s no file I/O as we would currently understand it, and, because there are no character strings and the valid characters within an atom are limited, it isn’t easy to compose a sensible filename.</p>
|
||||
<p>I’ve provided two functions to work around this problem.</p>
|
||||
|
@ -406,6 +827,9 @@
|
|||
<p>The <code>Lisp 1.5 Programmer's Manual</code> is still <a href="https://mitpress.mit.edu/books/lisp-15-programmers-manual">in print, ISBN 13 978-0-262-13011-0</a>; but it’s also <a href="http://www.softwarepreservation.org/projects/LISP/book/LISP%201.5%20Programmers%20Manual.pdf">available online</a>.</p>
|
||||
<h2><a href="#other-lisp-1-5-resources" name="other-lisp-1-5-resources"></a>Other Lisp 1.5 resources</h2>
|
||||
<p>The main resource I’m aware of is the Software Preservation Society’s site, <a href="http://www.softwarepreservation.org/projects/LISP/lisp1.5">here</a>. It has lots of fascinating stuff including full assembler listings for various obsolete processors, but I failed to find the Lisp source of Lisp functions as a text file, which is why <code>resources/lisp1.5.lsp</code> is largely copytyped and reconstructed from the manual.</p>
|
||||
<p>I’m not at this time aware of any other working Lisp 1.5 implementations.</p>
|
||||
<h3><a href="#other-implementations" name="other-implementations"></a>Other implementations</h3>
|
||||
<p>There’s an online (browser native) Lisp 1.5 implementation <a href="https://pages.zick.run/ichigo/">here</a> (source code <a href="https://github.com/zick/IchigoLisp">here</a>). It even has a working compiler!</p>
|
||||
<h3><a href="#history-resources" name="history-resources"></a>History resources</h3>
|
||||
<p>I’m compiling a <a href="https://simon-brooke.github.io/beowulf/docs/further_reading.html">list of links to historical documents on Lisp 1.5</a>.</p>
|
||||
<h2><a href="#license" name="license"></a>License</h2>
|
||||
<p>Copyright © 2019 Simon Brooke. Licensed under the GNU General Public License, version 2.0 or (at your option) any later version.</p></div></div></div></body></html>
|
File diff suppressed because one or more lines are too long
263
docs/codox/values.html
Normal file
263
docs/codox/values.html
Normal file
File diff suppressed because one or more lines are too long
BIN
docs/favicon.ico
Normal file
BIN
docs/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 17 KiB |
BIN
docs/img/beowulf_logo.png
Normal file
BIN
docs/img/beowulf_logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 147 KiB |
BIN
docs/img/beowulf_logo.xcf
Normal file
BIN
docs/img/beowulf_logo.xcf
Normal file
Binary file not shown.
BIN
docs/img/beowulf_logo_favicon.png
Normal file
BIN
docs/img/beowulf_logo_favicon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.5 KiB |
BIN
docs/img/beowulf_logo_med.png
Normal file
BIN
docs/img/beowulf_logo_med.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 47 KiB |
1
docs/index.html
Symbolic link
1
docs/index.html
Symbolic link
|
@ -0,0 +1 @@
|
|||
codox/intro.html
|
0
docs/lisp1.5.html
Normal file
0
docs/lisp1.5.html
Normal file
31
project.clj
31
project.clj
|
@ -1,28 +1,37 @@
|
|||
(defproject beowulf "0.2.1"
|
||||
(defproject beowulf "0.3.0-SNAPSHOT"
|
||||
:aot :all
|
||||
:cloverage {:output "docs/cloverage"
|
||||
:ns-exclude-regex [#"beowulf\.gendoc"]}
|
||||
:codox {:metadata {:doc "**TODO**: write docs"
|
||||
:ns-exclude-regex [#"beowulf\.gendoc" #"beowulf\.scratch"]}
|
||||
:codox {:html {:transforms [[:head] [:append
|
||||
[:link {:rel "icon"
|
||||
:type "image/x-icon"
|
||||
:href "../img/beowulf_logo_favicon.png"}]]]}
|
||||
:metadata {:doc "**TODO**: write docs"
|
||||
:doc/format :markdown}
|
||||
:output-path "docs/codox"
|
||||
:source-uri "https://github.com/simon-brooke/beowulf/blob/master/{filepath}#L{line}"}
|
||||
:description "An implementation of LISP 1.5 in Clojure"
|
||||
:source-uri "https://github.com/simon-brooke/beowulf/blob/master/{filepath}#L{line}"
|
||||
;; :themes [:journeyman]
|
||||
}
|
||||
:description "LISP 1.5 is to all Lisp dialects as Beowulf is to English literature."
|
||||
:license {:name "GPL-2.0-or-later"
|
||||
:url "https://www.eclipse.org/legal/epl-2.0/"}
|
||||
:url "https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html"}
|
||||
:dependencies [[org.clojure/clojure "1.11.1"]
|
||||
[org.clojure/math.combinatorics "0.2.0"] ;; not needed in production builds
|
||||
[org.clojure/math.numeric-tower "0.0.5"]
|
||||
[org.clojure/tools.cli "1.0.214"]
|
||||
[org.clojure/tools.trace "0.7.11"]
|
||||
[clojure.java-time "1.2.0"]
|
||||
[environ "1.2.0"]
|
||||
[instaparse "1.4.12"]
|
||||
[org.jline/jline "3.23.0"]
|
||||
;; [org.jline/jline "3.23.0"]
|
||||
[rhizome "0.2.9"] ;; not needed in production builds
|
||||
]
|
||||
:main ^:skip-aot beowulf.core
|
||||
:main beowulf.core
|
||||
:plugins [[lein-cloverage "1.2.2"]
|
||||
[lein-codox "0.10.7"]
|
||||
[lein-environ "1.1.0"]]
|
||||
:profiles {:uberjar {:aot :all}}
|
||||
:profiles {:jar {:aot :all}
|
||||
:uberjar {:aot :all}}
|
||||
:release-tasks [["vcs" "assert-committed"]
|
||||
["change" "version" "leiningen.release/bump-version" "release"]
|
||||
["vcs" "commit"]
|
||||
|
@ -32,7 +41,5 @@
|
|||
["uberjar"]
|
||||
["change" "version" "leiningen.release/bump-version"]
|
||||
["vcs" "commit"]]
|
||||
|
||||
:target-path "target/%s"
|
||||
:url "https://github.com/simon-brooke/the-great-game"
|
||||
)
|
||||
:url "https://github.com/simon-brooke/the-great-game")
|
||||
|
|
563
resources/codox/themes/journeyman/css/default.css
Normal file
563
resources/codox/themes/journeyman/css/default.css
Normal file
|
@ -0,0 +1,563 @@
|
|||
body {
|
||||
font-family: Helvetica, Arial, sans-serif;
|
||||
font-size: 15px;
|
||||
color: limegreen;
|
||||
background-color: black;
|
||||
}
|
||||
|
||||
a {
|
||||
color: lime;
|
||||
}
|
||||
|
||||
a:active, a:hover {
|
||||
color: yellowgreen;
|
||||
}
|
||||
|
||||
a:visited {
|
||||
color: green;
|
||||
}
|
||||
|
||||
pre, code {
|
||||
font-family: Monaco, DejaVu Sans Mono, Consolas, monospace;
|
||||
font-size: 9pt;
|
||||
margin: 15px 0;
|
||||
color: limegreen;
|
||||
background-color: #111;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-weight: normal;
|
||||
font-size: 29px;
|
||||
margin: 10px 0 2px 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-weight: normal;
|
||||
font-size: 25px;
|
||||
}
|
||||
|
||||
th, td {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
h5.license {
|
||||
margin: 9px 0 22px 0;
|
||||
color: lime;
|
||||
font-weight: normal;
|
||||
font-size: 12px;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.document h1, .namespace-index h1 {
|
||||
font-size: 32px;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
#header, #content, .sidebar {
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
#header {
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 22px;
|
||||
color: limegreen;
|
||||
padding: 5px 7px;
|
||||
}
|
||||
|
||||
#content {
|
||||
top: 32px;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
overflow: auto;
|
||||
background: black;
|
||||
color: green;
|
||||
padding: 0 18px;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
top: 32px;
|
||||
bottom: 0;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.sidebar.primary {
|
||||
background: #080808;
|
||||
border-right: solid 1px forestgreen;
|
||||
left: 0;
|
||||
width: 250px;
|
||||
}
|
||||
|
||||
.sidebar.secondary {
|
||||
background: #111;
|
||||
border-right: solid 1px darkgreen;
|
||||
left: 251px;
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
#content.namespace-index, #content.document {
|
||||
left: 251px;
|
||||
}
|
||||
|
||||
#content.namespace-docs {
|
||||
left: 452px;
|
||||
}
|
||||
|
||||
#content.document {
|
||||
padding-bottom: 10%;
|
||||
}
|
||||
|
||||
#header {
|
||||
background: #080808;
|
||||
box-shadow: 0 0 8px rgba(192, 255, 192, 0.4);
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
#header h1 {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-size: 18px;
|
||||
font-weight: lighter;
|
||||
text-shadow: -1px -1px 0px #333;
|
||||
}
|
||||
|
||||
#header h1 .project-version {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.project-version {
|
||||
padding-left: 0.15em;
|
||||
}
|
||||
|
||||
#header a, .sidebar a {
|
||||
display: block;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#header h2 {
|
||||
float: right;
|
||||
font-size: 9pt;
|
||||
font-weight: normal;
|
||||
margin: 4px 3px;
|
||||
padding: 0;
|
||||
color: #5f5;
|
||||
}
|
||||
|
||||
#header h2 a {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.sidebar h3 {
|
||||
margin: 0;
|
||||
padding: 10px 13px 0 13px;
|
||||
font-size: 19px;
|
||||
font-weight: lighter;
|
||||
}
|
||||
|
||||
.sidebar h3 a {
|
||||
color: #4f4;
|
||||
}
|
||||
|
||||
.sidebar h3.no-link {
|
||||
color: green;
|
||||
}
|
||||
|
||||
.sidebar ul {
|
||||
padding: 7px 0 6px 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.sidebar ul.index-link {
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
|
||||
.sidebar li {
|
||||
display: block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.sidebar li a, .sidebar li .no-link {
|
||||
border-left: 3px solid transparent;
|
||||
padding: 0 10px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.sidebar li .no-link {
|
||||
display: block;
|
||||
color: #7F7;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.sidebar li .inner {
|
||||
display: inline-block;
|
||||
padding-top: 7px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.sidebar li a, .sidebar li .tree {
|
||||
height: 31px;
|
||||
}
|
||||
|
||||
.depth-1 .inner { padding-left: 2px; }
|
||||
.depth-2 .inner { padding-left: 6px; }
|
||||
.depth-3 .inner { padding-left: 20px; }
|
||||
.depth-4 .inner { padding-left: 34px; }
|
||||
.depth-5 .inner { padding-left: 48px; }
|
||||
.depth-6 .inner { padding-left: 62px; }
|
||||
|
||||
.sidebar li .tree {
|
||||
display: block;
|
||||
float: left;
|
||||
position: relative;
|
||||
top: -10px;
|
||||
margin: 0 4px 0 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.sidebar li.depth-1 .tree {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.sidebar li .tree .top, .sidebar li .tree .bottom {
|
||||
display: block;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 7px;
|
||||
}
|
||||
|
||||
.sidebar li .tree .top {
|
||||
border-left: 1px solid yellowgreen;
|
||||
border-bottom: 1px solid yellowgreen;
|
||||
height: 19px;
|
||||
}
|
||||
|
||||
.sidebar li .tree .bottom {
|
||||
height: 22px;
|
||||
}
|
||||
|
||||
.sidebar li.branch .tree .bottom {
|
||||
border-left: 1px solid yellowgreen;
|
||||
}
|
||||
|
||||
.sidebar.primary li.current a {
|
||||
border-left: 3px solid goldenrod;
|
||||
color: goldenrod;
|
||||
}
|
||||
|
||||
.sidebar.secondary li.current a {
|
||||
border-left: 3px solid yellow;
|
||||
color: yellow;
|
||||
}
|
||||
|
||||
.namespace-index h2 {
|
||||
margin: 30px 0 0 0;
|
||||
}
|
||||
|
||||
.namespace-index h3 {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.namespace-index .topics {
|
||||
padding-left: 30px;
|
||||
margin: 11px 0 0 0;
|
||||
}
|
||||
|
||||
.namespace-index .topics li {
|
||||
padding: 5px 0;
|
||||
}
|
||||
|
||||
.namespace-docs h3 {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.public h3 {
|
||||
margin: 0;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.usage {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.public {
|
||||
margin: 0;
|
||||
border-top: 1px solid lime;
|
||||
padding-top: 14px;
|
||||
padding-bottom: 6px;
|
||||
}
|
||||
|
||||
.public:last-child {
|
||||
margin-bottom: 20%;
|
||||
}
|
||||
|
||||
.members .public:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.members {
|
||||
margin: 15px 0;
|
||||
}
|
||||
|
||||
.members h4 {
|
||||
color: lime;
|
||||
font-weight: normal;
|
||||
font-variant: small-caps;
|
||||
margin: 0 0 5px 0;
|
||||
}
|
||||
|
||||
.members .inner {
|
||||
padding-top: 5px;
|
||||
padding-left: 12px;
|
||||
margin-top: 2px;
|
||||
margin-left: 7px;
|
||||
border-left: 1px solid #5f5;
|
||||
}
|
||||
|
||||
#content .members .inner h3 {
|
||||
font-size: 12pt;
|
||||
}
|
||||
|
||||
.members .public {
|
||||
border-top: none;
|
||||
margin-top: 0;
|
||||
padding-top: 6px;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.members .public:first-child {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
h4.type,
|
||||
h4.dynamic,
|
||||
h4.added,
|
||||
h4.deprecated {
|
||||
float: left;
|
||||
margin: 3px 10px 15px 0;
|
||||
font-size: 15px;
|
||||
font-weight: bold;
|
||||
font-variant: small-caps;
|
||||
}
|
||||
|
||||
.public h4.type,
|
||||
.public h4.dynamic,
|
||||
.public h4.added,
|
||||
.public h4.deprecated {
|
||||
font-size: 13px;
|
||||
font-weight: bold;
|
||||
margin: 3px 0 0 10px;
|
||||
}
|
||||
|
||||
.members h4.type,
|
||||
.members h4.added,
|
||||
.members h4.deprecated {
|
||||
margin-top: 1px;
|
||||
}
|
||||
|
||||
h4.type {
|
||||
color: #717171;
|
||||
}
|
||||
|
||||
h4.dynamic {
|
||||
color: #9933aa;
|
||||
}
|
||||
|
||||
h4.added {
|
||||
color: #7acc32;
|
||||
}
|
||||
|
||||
h4.deprecated {
|
||||
color: #880000;
|
||||
}
|
||||
|
||||
.namespace {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.namespace:last-child {
|
||||
margin-bottom: 10%;
|
||||
}
|
||||
|
||||
.index {
|
||||
padding: 0;
|
||||
font-size: 80%;
|
||||
margin: 15px 0;
|
||||
line-height: 16px;
|
||||
}
|
||||
|
||||
.index * {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.index p {
|
||||
padding-right: 3px;
|
||||
}
|
||||
|
||||
.index li {
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
.index ul {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.type-sig {
|
||||
clear: both;
|
||||
color: goldenrod;
|
||||
}
|
||||
|
||||
.type-sig pre {
|
||||
padding-top: 10px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.usage code {
|
||||
display: block;
|
||||
margin: 2px 0;
|
||||
color: limegreen;
|
||||
}
|
||||
|
||||
.usage code:first-child {
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 15px 0;
|
||||
}
|
||||
|
||||
.public p:first-child, .public pre.plaintext {
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.doc {
|
||||
margin: 0 0 26px 0;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.public .doc {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.namespace-index .doc {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.namespace-index .namespace .doc {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.markdown p, .markdown li, .markdown dt, .markdown dd, .markdown td {
|
||||
line-height: 22px;
|
||||
}
|
||||
|
||||
.markdown li {
|
||||
padding: 2px 0;
|
||||
}
|
||||
|
||||
.markdown h2 {
|
||||
font-weight: normal;
|
||||
font-size: 25px;
|
||||
margin: 30px 0 10px 0;
|
||||
}
|
||||
|
||||
.markdown h3 {
|
||||
font-weight: normal;
|
||||
font-size: 20px;
|
||||
margin: 30px 0 0 0;
|
||||
}
|
||||
|
||||
.markdown h4 {
|
||||
font-size: 15px;
|
||||
margin: 22px 0 -4px 0;
|
||||
}
|
||||
|
||||
.doc, .public, .namespace .index {
|
||||
max-width: 680px;
|
||||
overflow-x: visible;
|
||||
}
|
||||
|
||||
.markdown pre > code {
|
||||
display: block;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.markdown pre > code, .src-link a {
|
||||
border: 1px solid lime;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.markdown code:not(.hljs), .src-link a {
|
||||
background: #111;
|
||||
}
|
||||
|
||||
pre.deps {
|
||||
display: inline-block;
|
||||
margin: 0 10px;
|
||||
border: 1px solid lime;
|
||||
border-radius: 2px;
|
||||
padding: 10px;
|
||||
background-color: #111;
|
||||
}
|
||||
|
||||
.markdown hr {
|
||||
border-style: solid;
|
||||
border-top: none;
|
||||
color: goldenrod;
|
||||
}
|
||||
|
||||
.doc ul, .doc ol {
|
||||
padding-left: 30px;
|
||||
}
|
||||
|
||||
.doc table {
|
||||
border-collapse: collapse;
|
||||
margin: 0 10px;
|
||||
}
|
||||
|
||||
.doc table td, .doc table th {
|
||||
border: 1px solid goldenrod;
|
||||
padding: 4px 6px;
|
||||
}
|
||||
|
||||
.doc table th {
|
||||
background: #111;
|
||||
}
|
||||
|
||||
.doc dl {
|
||||
margin: 0 10px 20px 10px;
|
||||
}
|
||||
|
||||
.doc dl dt {
|
||||
font-weight: bold;
|
||||
margin: 0;
|
||||
padding: 3px 0;
|
||||
border-bottom: 1px solid goldenrod;
|
||||
}
|
||||
|
||||
.doc dl dd {
|
||||
padding: 5px 0;
|
||||
margin: 0 0 5px 10px;
|
||||
}
|
||||
|
||||
.doc abbr {
|
||||
border-bottom: 1px dotted goldenrod;
|
||||
font-variant: none;
|
||||
cursor: help;
|
||||
}
|
||||
|
||||
.src-link {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.src-link a {
|
||||
font-size: 70%;
|
||||
padding: 1px 4px;
|
||||
text-decoration: none;
|
||||
color: lime5bb;
|
||||
}
|
97
resources/codox/themes/journeyman/css/highlight.css
Normal file
97
resources/codox/themes/journeyman/css/highlight.css
Normal file
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
github.com style (c) Vasily Polovnyov <vast@whiteants.net>
|
||||
*/
|
||||
|
||||
.hljs {
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
padding: 0.5em;
|
||||
color: #333;
|
||||
background: #f8f8f8;
|
||||
}
|
||||
|
||||
.hljs-comment,
|
||||
.hljs-quote {
|
||||
color: #998;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.hljs-keyword,
|
||||
.hljs-selector-tag,
|
||||
.hljs-subst {
|
||||
color: #333;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.hljs-number,
|
||||
.hljs-literal,
|
||||
.hljs-variable,
|
||||
.hljs-template-variable,
|
||||
.hljs-tag .hljs-attr {
|
||||
color: #008080;
|
||||
}
|
||||
|
||||
.hljs-string,
|
||||
.hljs-doctag {
|
||||
color: #d14;
|
||||
}
|
||||
|
||||
.hljs-title,
|
||||
.hljs-section,
|
||||
.hljs-selector-id {
|
||||
color: #900;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.hljs-subst {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.hljs-type,
|
||||
.hljs-class .hljs-title {
|
||||
color: #458;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.hljs-tag,
|
||||
.hljs-name,
|
||||
.hljs-attribute {
|
||||
color: #000080;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.hljs-regexp,
|
||||
.hljs-link {
|
||||
color: #009926;
|
||||
}
|
||||
|
||||
.hljs-symbol,
|
||||
.hljs-bullet {
|
||||
color: #990073;
|
||||
}
|
||||
|
||||
.hljs-built_in,
|
||||
.hljs-builtin-name {
|
||||
color: #0086b3;
|
||||
}
|
||||
|
||||
.hljs-meta {
|
||||
color: #999;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.hljs-deletion {
|
||||
background: #fdd;
|
||||
}
|
||||
|
||||
.hljs-addition {
|
||||
background: #dfd;
|
||||
}
|
||||
|
||||
.hljs-emphasis {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.hljs-strong {
|
||||
font-weight: bold;
|
||||
}
|
1
resources/codox/themes/journeyman/theme.edn
Normal file
1
resources/codox/themes/journeyman/theme.edn
Normal file
|
@ -0,0 +1 @@
|
|||
{:resources ["css/default.css" "css/highlight.css"]}
|
|
@ -1,79 +1,240 @@
|
|||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Beowulf Sysout file generated at 2023-03-30T09:40:36.483
|
||||
;; Beowulf 0.3.0-SNAPSHOT Sysout file generated at 2023-04-05T23:30:32.954
|
||||
;; generated by simon
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
((NIL)
|
||||
(T . T)
|
||||
(F)
|
||||
(ADD1)
|
||||
(AND)
|
||||
(APPEND)
|
||||
(APPLY)
|
||||
(ATOM)
|
||||
(CAR)
|
||||
(CDR)
|
||||
(CONS)
|
||||
(COPY LAMBDA (X)
|
||||
(COND ((NULL X) (QUOTE NIL))
|
||||
((ATOM X) X)
|
||||
((QUOTE T) (CONS (COPY (CAR X)) (COPY (CDR X))))))
|
||||
(DEFINE)
|
||||
(DIFFERENCE)
|
||||
(DIVIDE LAMBDA (X Y) (CONS (QUOTIENT X Y) (CONS (REMAINDER X Y) (QUOTE NIL))))
|
||||
(ERROR)
|
||||
(EQ)
|
||||
(EQUAL)
|
||||
(EVAL)
|
||||
(FIXP)
|
||||
(GENSYM)
|
||||
(GET LAMBDA (X Y)
|
||||
(COND ((NULL X) (QUOTE NIL))
|
||||
((EQ (CAR X) Y) (CAR (CDR X)))
|
||||
((QUOTE T) (GET (CDR X) Y))))
|
||||
(GREATERP)
|
||||
(INTEROP)
|
||||
(INTERSECTION LAMBDA (X Y)
|
||||
(COND ((NULL X) (QUOTE NIL))
|
||||
((MEMBER (CAR X) Y) (CONS (CAR X) (INTERSECTION (CDR X) Y)))
|
||||
((QUOTE T) (INTERSECTION (CDR X) Y))))
|
||||
(LENGTH LAMBDA (L) (COND ((EQ NIL L) 0) (T (ADD1 (LENGTH (CDR L))))))
|
||||
(LESSP)
|
||||
(MEMBER LAMBDA (A X)
|
||||
(COND ((NULL X) (QUOTE F))
|
||||
((EQ A (CAR X)) (QUOTE T))
|
||||
((QUOTE T) (MEMBER A (CDR X)))))
|
||||
(MINUSP LAMBDA (X) (LESSP X 0))
|
||||
(NULL LAMBDA (X) (COND ((EQUAL X NIL) (QUOTE T)) (T (QUOTE F))))
|
||||
(NUMBERP)
|
||||
(OBLIST)
|
||||
(ONEP LAMBDA (X) (EQ X 1))
|
||||
(PAIR LAMBDA (X Y)
|
||||
(COND ((AND (NULL X) (NULL Y)) NIL)
|
||||
((NULL X) (ERROR 'F2))
|
||||
((NULL Y) (ERROR 'F3))
|
||||
(T (CONS (CONS (CAR X) (CAR Y)) (PAIR (CDR X) (CDR Y))))))
|
||||
(PLUS)
|
||||
(PRETTY)
|
||||
(PRINT)
|
||||
(PROP LAMBDA (X Y U)
|
||||
(COND ((NULL X) (U))
|
||||
((EQ (CAR X) Y) (CDR X))
|
||||
((QUOTE T) (PROP (CDR X) Y U))))
|
||||
(QUOTIENT)
|
||||
(READ)
|
||||
(REMAINDER)
|
||||
(REPEAT LAMBDA (N X)
|
||||
(COND ((EQ N 0) NIL)
|
||||
(T (CONS X (REPEAT (SUB1 N) X)))))
|
||||
(RPLACA)
|
||||
(RPLACD)
|
||||
(SET)
|
||||
(SUB1 LAMBDA (N) (DIFFERENCE N 1))
|
||||
(SYSIN)
|
||||
(SYSOUT)
|
||||
(TERPRI)
|
||||
(TIMES)
|
||||
(TRACE)
|
||||
(UNTRACE)
|
||||
(ZEROP LAMBDA (N) (EQ N 0)))
|
||||
((NIL 32767 APVAL NIL)
|
||||
(T 32767 APVAL T)
|
||||
(F 32767 APVAL NIL)
|
||||
(ADD1 32767 SUBR (BEOWULF HOST ADD1))
|
||||
(AND 32767 SUBR (BEOWULF HOST AND))
|
||||
(APPEND
|
||||
32767
|
||||
EXPR
|
||||
(LAMBDA
|
||||
(X Y) (COND ((NULL X) Y) (T (CONS (CAR X) (APPEND (CDR X) Y))))))
|
||||
(APPLY 32767 SUBR (BEOWULF BOOTSTRAP APPLY))
|
||||
(ASSOC
|
||||
32767
|
||||
EXPR
|
||||
(LAMBDA
|
||||
(X L)
|
||||
(COND
|
||||
((NULL L) NIL)
|
||||
((AND (CONSP (CAR L)) (EQ (CAAR L) X)) (CAR L))
|
||||
(T (ASSOC X (CDR L)))))
|
||||
SUBR (BEOWULF HOST ASSOC))
|
||||
(ATOM 32767 SUBR (BEOWULF HOST ATOM))
|
||||
(CAR 32767 SUBR (BEOWULF HOST CAR))
|
||||
(CAAAAR 32767 EXPR (LAMBDA (X) (CAR (CAR (CAR (CAR X))))))
|
||||
(CAAADR 32767 EXPR (LAMBDA (X) (CAR (CAR (CAR (CDR X))))))
|
||||
(CAAAR 32767 EXPR (LAMBDA (X) (CAR (CAR (CAR X)))))
|
||||
(CAADAR 32767 EXPR (LAMBDA (X) (CAR (CAR (CDR (CAR X))))))
|
||||
(CAADDR 32767 EXPR (LAMBDA (X) (CAR (CAR (CDR (CDR X))))))
|
||||
(CAADR 32767 EXPR (LAMBDA (X) (CAR (CAR (CDR X)))))
|
||||
(CAAR 32767 EXPR (LAMBDA (X) (CAR (CAR X))))
|
||||
(CADAAR 32767 EXPR (LAMBDA (X) (CAR (CDR (CAR (CAR X))))))
|
||||
(CADADR 32767 EXPR (LAMBDA (X) (CAR (CDR (CAR (CDR X))))))
|
||||
(CADAR 32767 EXPR (LAMBDA (X) (CAR (CDR (CAR X)))))
|
||||
(CADDAR 32767 EXPR (LAMBDA (X) (CAR (CDR (CDR (CAR X))))))
|
||||
(CADDDR 32767 EXPR (LAMBDA (X) (CAR (CDR (CDR (CDR X))))))
|
||||
(CADDR 32767 EXPR (LAMBDA (X) (CAR (CDR (CDR X)))))
|
||||
(CADR 32767 EXPR (LAMBDA (X) (CAR (CDR X))))
|
||||
(CDAAAR 32767 EXPR (LAMBDA (X) (CDR (CAR (CAR (CAR X))))))
|
||||
(CDAADR 32767 EXPR (LAMBDA (X) (CDR (CAR (CAR (CDR X))))))
|
||||
(CDAAR 32767 EXPR (LAMBDA (X) (CDR (CAR (CAR X)))))
|
||||
(CDADAR 32767 EXPR (LAMBDA (X) (CDR (CAR (CDR (CAR X))))))
|
||||
(CDADDR 32767 EXPR (LAMBDA (X) (CDR (CAR (CDR (CDR X))))))
|
||||
(CDADR 32767 EXPR (LAMBDA (X) (CDR (CAR (CDR X)))))
|
||||
(CDAR 32767 EXPR (LAMBDA (X) (CDR (CAR X))))
|
||||
(CDDAAR 32767 EXPR (LAMBDA (X) (CDR (CDR (CAR (CAR X))))))
|
||||
(CDDADR 32767 EXPR (LAMBDA (X) (CDR (CDR (CAR (CDR X))))))
|
||||
(CDDAR 32767 EXPR (LAMBDA (X) (CDR (CDR (CAR X)))))
|
||||
(CDDDAR 32767 EXPR (LAMBDA (X) (CDR (CDR (CDR (CAR X))))))
|
||||
(CDDDDR 32767 EXPR (LAMBDA (X) (CDR (CDR (CDR (CDR X))))))
|
||||
(CDDDR 32767 EXPR (LAMBDA (X) (CDR (CDR (CDR X)))))
|
||||
(CDDR 32767 EXPR (LAMBDA (X) (CDR (CDR X))))
|
||||
(CDR 32767 SUBR (BEOWULF HOST CDR))
|
||||
(CONS 32767 SUBR (BEOWULF HOST CONS))
|
||||
(CONSP 32767 SUBR (BEOWULF HOST CONSP))
|
||||
(COPY
|
||||
32767
|
||||
EXPR
|
||||
(LAMBDA
|
||||
(X)
|
||||
(COND
|
||||
((NULL X) NIL)
|
||||
((ATOM X) X) (T (CONS (COPY (CAR X)) (COPY (CDR X)))))))
|
||||
(DEFINE 32767 SUBR (BEOWULF HOST DEFINE))
|
||||
(DIFFERENCE 32767 SUBR (BEOWULF HOST DIFFERENCE))
|
||||
(DIVIDE
|
||||
32767
|
||||
EXPR
|
||||
(LAMBDA (X Y) (CONS (QUOTIENT X Y) (CONS (REMAINDER X Y) NIL))))
|
||||
(DOC 32767 SUBR (BEOWULF HOST DOC))
|
||||
(EFFACE
|
||||
32767
|
||||
EXPR
|
||||
(LAMBDA
|
||||
(X L)
|
||||
(COND
|
||||
((NULL L) NIL)
|
||||
((EQUAL X (CAR L)) (CDR L)) (T (RPLACD L (EFFACE X (CDR L)))))))
|
||||
(ERROR 32767 SUBR (BEOWULF HOST ERROR))
|
||||
(EQ 32767 SUBR (BEOWULF HOST EQ))
|
||||
(EQUAL 32767 SUBR (BEOWULF HOST EQUAL))
|
||||
(EVAL 32767 SUBR (BEOWULF BOOTSTRAP EVAL))
|
||||
(FACTORIAL
|
||||
32767
|
||||
EXPR (LAMBDA (N) (COND ((EQ N 1) 1) (T (TIMES N (FACTORIAL (SUB1 N)))))))
|
||||
(FIXP 32767 SUBR (BEOWULF HOST FIXP))
|
||||
(GENSYM 32767 SUBR (BEOWULF HOST GENSYM))
|
||||
(GET
|
||||
32767
|
||||
;; EXPR
|
||||
;; (LAMBDA
|
||||
;; (X Y)
|
||||
;; (COND
|
||||
;; ((NULL X) NIL)
|
||||
;; ((EQ (CAR X) Y) (CAR (CDR X))) (T (GET (CDR X) Y))))
|
||||
SUBR (BEOWULF HOST GET))
|
||||
(GREATERP 32767 SUBR (BEOWULF HOST GREATERP))
|
||||
(INTEROP 32767 SUBR (BEOWULF INTEROP INTEROP))
|
||||
(INTERSECTION
|
||||
32767
|
||||
EXPR
|
||||
(LAMBDA
|
||||
(X Y)
|
||||
(COND
|
||||
((NULL X) NIL)
|
||||
((MEMBER (CAR X) Y) (CONS (CAR X) (INTERSECTION (CDR X) Y)))
|
||||
(T (INTERSECTION (CDR X) Y)))))
|
||||
(LENGTH
|
||||
32767
|
||||
EXPR
|
||||
(LAMBDA
|
||||
(L)
|
||||
(COND ((EQ NIL L) 0) ((CONSP (CDR L)) (ADD1 (LENGTH (CDR L)))) (T 1))))
|
||||
(LESSP 32767 SUBR (BEOWULF HOST LESSP))
|
||||
(MAPLIST
|
||||
32767
|
||||
EXPR
|
||||
(LAMBDA
|
||||
(L F)
|
||||
(COND
|
||||
((NULL L) NIL) (T (CONS (F (CAR L)) (MAPLIST (CDR L) F))))))
|
||||
(MEMBER
|
||||
32767
|
||||
EXPR
|
||||
(LAMBDA
|
||||
(A X)
|
||||
(COND
|
||||
((NULL X) (QUOTE F))
|
||||
((EQ A (CAR X)) T) (T (MEMBER A (CDR X))))))
|
||||
(MINUSP 32767 EXPR (LAMBDA (X) (LESSP X 0)))
|
||||
(NOT 32767 EXPR (LAMBDA (X) (COND (X NIL) (T T))))
|
||||
(NULL
|
||||
32767 EXPR (LAMBDA (X) (COND ((EQUAL X NIL) T) (T (QUOTE F)))))
|
||||
(NUMBERP 32767 SUBR (BEOWULF HOST NUMBERP))
|
||||
(OBLIST 32767 SUBR (BEOWULF HOST OBLIST))
|
||||
(ONEP 32767 EXPR (LAMBDA (X) (EQ X 1)))
|
||||
(OR 32767 SUBR (BEOWULF HOST OR))
|
||||
(PAIR
|
||||
32767
|
||||
EXPR
|
||||
(LAMBDA
|
||||
(X Y)
|
||||
(COND
|
||||
((AND (NULL X) (NULL Y)) NIL)
|
||||
((NULL X) (ERROR (QUOTE F2)))
|
||||
((NULL Y) (ERROR (QUOTE F3)))
|
||||
(T (CONS (CONS (CAR X) (CAR Y)) (PAIR (CDR X) (CDR Y)))))))
|
||||
(PAIRLIS
|
||||
32767
|
||||
EXPR
|
||||
(LAMBDA
|
||||
(X Y A)
|
||||
(COND
|
||||
((NULL X) A)
|
||||
(T (CONS (CONS (CAR X) (CAR Y)) (PAIRLIS (CDR X) (CDR Y) A)))))
|
||||
SUBR (BEOWULF HOST PAIRLIS))
|
||||
(PLUS 32767 SUBR (BEOWULF HOST PLUS))
|
||||
(PRETTY 32767)
|
||||
(PRINT 32767)
|
||||
(PROG 32767 FSUBR (BEOWULF HOST PROG))
|
||||
(PROP
|
||||
32767
|
||||
EXPR
|
||||
(LAMBDA
|
||||
(X Y U)
|
||||
(COND
|
||||
((NULL X) (U))
|
||||
((EQ (CAR X) Y) (CDR X)) (T (PROP (CDR X) Y U)))))
|
||||
(QUOTE 32767 EXPR (LAMBDA (X) X))
|
||||
(QUOTIENT 32767 SUBR (BEOWULF HOST QUOTIENT))
|
||||
(RANGE
|
||||
32767
|
||||
EXPR
|
||||
(LAMBDA
|
||||
(N M)
|
||||
(COND
|
||||
((LESSP M N) NIL) (T (CONS N (RANGE (ADD1 N) M))))))
|
||||
(READ 32767 SUBR (BEOWULF READ READ))
|
||||
(REMAINDER 32767 SUBR (BEOWULF HOST REMAINDER))
|
||||
(REPEAT
|
||||
32767
|
||||
EXPR
|
||||
(LAMBDA (N X) (COND ((EQ N 0) NIL) (T (CONS X (REPEAT (SUB1 N) X))))))
|
||||
(RPLACA 32767 SUBR (BEOWULF HOST RPLACA))
|
||||
(RPLACD 32767 SUBR (BEOWULF HOST RPLACD))
|
||||
(SEARCH 32767 EXPR
|
||||
(LAMBDA (X P F U)
|
||||
(COND ((NULL X) (U X))
|
||||
((P X) (F X))
|
||||
((QUOTE T) (SEARCH (CDR X) P F U)))))
|
||||
(SET 32767 SUBR (BEOWULF HOST SET))
|
||||
(SUB1 32767 EXPR (LAMBDA (N) (DIFFERENCE N 1)) SUBR (BEOWULF HOST SUB1))
|
||||
(SUB2
|
||||
32767
|
||||
EXPR
|
||||
(LAMBDA
|
||||
(A Z)
|
||||
(COND
|
||||
((NULL A) Z) ((EQ (CAAR A) Z) (CDAR A)) (T (SUB2 (CDAR A) Z)))))
|
||||
(SUBLIS
|
||||
32767 EXPR
|
||||
(LAMBDA (X Y)
|
||||
(COND ((NULL X) Y)
|
||||
((NULL Y) Y)
|
||||
((QUOTE T) (SEARCH X
|
||||
(LAMBDA (J) (EQUAL Y (CAAR J)))
|
||||
(LAMBDA (J) (CDAR J))
|
||||
(LAMBDA (J) (COND ((ATOM Y) Y)
|
||||
((QUOTE T) (CONS
|
||||
(SUBLIS X (CAR Y))
|
||||
(SUBLIS X (CDR Y)))))))))))
|
||||
(SUBST
|
||||
32767
|
||||
EXPR
|
||||
(LAMBDA
|
||||
(X Y Z)
|
||||
(COND
|
||||
((EQUAL Y Z) X)
|
||||
((ATOM Z) Z)
|
||||
(T (CONS (SUBST X Y (CAR Z)) (SUBST X Y (CDR Z)))))))
|
||||
(SYSIN 32767 SUBR (BEOWULF IO SYSIN))
|
||||
(SYSOUT 32767 SUBR (BEOWULF IO SYSOUT))
|
||||
(TERPRI 32767)
|
||||
(TIMES 32767 SUBR (BEOWULF HOST TIMES))
|
||||
(TRACE 32767 SUBR (BEOWULF HOST TRACE))
|
||||
(UNION
|
||||
32767
|
||||
EXPR
|
||||
(LAMBDA
|
||||
(X Y)
|
||||
(COND
|
||||
((NULL X) Y)
|
||||
((MEMBER (CAR X) Y) (UNION (CDR X) Y))
|
||||
(T (CONS (CAR X) (UNION (CDR X) Y))))))
|
||||
(UNTRACE 32767 SUBR (BEOWULF HOST UNTRACE))
|
||||
(ZEROP 32767 EXPR (LAMBDA (N) (EQ N 0))))
|
||||
|
|
3
resources/mexpr/append.mexpr.lsp
Normal file
3
resources/mexpr/append.mexpr.lsp
Normal file
|
@ -0,0 +1,3 @@
|
|||
;; page 61
|
||||
|
||||
append[x; y] = [null[x] -> y; T -> cons[car[x]; append[cdr[x]; y]]]
|
|
@ -1,21 +0,0 @@
|
|||
;; see page 70 of Lisp 1.5 Programmers Manual; this expands somewhat
|
||||
;; on the accounts of eval and apply given on page 13. This is M-expr
|
||||
;; syntax, obviously.
|
||||
|
||||
;; apply
|
||||
;; NOTE THAT I suspect there is a typo in the printed manual in line
|
||||
;; 7 of this definition, namely a missing closing square bracket before
|
||||
;; the final semi-colon; that has been corrected here.
|
||||
|
||||
apply[fn;args;a] = [
|
||||
null[fn] -> NIL;
|
||||
atom[fn] -> [get[fn;EXPR] -> apply[expr; args; a];
|
||||
get[fn;SUBR] -> {spread[args];
|
||||
$ALIST := a;
|
||||
TSX subr4, 4};
|
||||
T -> apply[cdr[sassoc[fn; a; λ[[]; error[A2]]]]; args a]];
|
||||
eq[car[fn]; LABEL] -> apply[caddr[fn]; args;
|
||||
cons[cons[cadr[fn];caddr[fn]]; a]];
|
||||
eq[car[fn]; FUNARG] -> apply[cadr[fn]; args; caddr[fn]];
|
||||
eq[car[fn]; LAMBDA] -> eval[caddr[fn]; nconc[pair[cadr[fn]; args]; a]];
|
||||
T -> apply[eval[fn;a]; args; a]]
|
40
resources/mexpr/apply.mexpr.lsp
Normal file
40
resources/mexpr/apply.mexpr.lsp
Normal file
|
@ -0,0 +1,40 @@
|
|||
;; see page 70 of Lisp 1.5 Programmers Manual; this expands somewhat
|
||||
;; on the accounts of eval and apply given on page 13. This is M-expr
|
||||
;; syntax, obviously.
|
||||
|
||||
;; ## APPLY
|
||||
|
||||
;; NOTE THAT I suspect there is a typo in the printed manual in line
|
||||
;; 7 of this definition, namely a missing closing square bracket before
|
||||
;; the final semi-colon; that has been corrected here.
|
||||
|
||||
;; RIGHT! So the 'EXPR' representation of a function is expected to be
|
||||
;; on the `EXPR` property on the property list of the symbol which is
|
||||
;; its name; an expression is simply a Lisp S-Expression as a structure
|
||||
;; of cons cells and atoms in memory. The 'SUBR' representation, expected
|
||||
;; to be on the `SUBR` property, is literally a subroutine written in
|
||||
;; assembly code, so what is happening in the curly braces is putting the
|
||||
;; arguments into processor registers prior to a jump to subroutine - TSX
|
||||
;; being presumably equivalent to a 6502's JSR call.
|
||||
|
||||
;; This accounts for the difference between this statement and the version
|
||||
;; on page 12: that is a pure interpreter, which can only call those host
|
||||
;; functions that are explicitly hard coded in.
|
||||
|
||||
;; This version knows how to recognise subroutines and jump to them, but I
|
||||
;; think that by implication at least this version can only work if it is
|
||||
;; itself compiled with the Lisp compiler, since the section in curly braces
|
||||
;; appears to be intended to be passed to the Lisp assembler.
|
||||
|
||||
;; apply[fn;args;a] = [
|
||||
;; null[fn] -> NIL;
|
||||
;; atom[fn] -> [get[fn;EXPR] -> apply[expr; args; a];
|
||||
;; get[fn;SUBR] -> {spread[args];
|
||||
;; $ALIST := a;
|
||||
;; TSX subr4, 4};
|
||||
;; T -> apply[cdr[sassoc[fn; a; λ[[]; error[A2]]]]; args a]];
|
||||
;; eq[car[fn]; LABEL] -> apply[caddr[fn]; args;
|
||||
;; cons[cons[cadr[fn];caddr[fn]]; a]];
|
||||
;; eq[car[fn]; FUNARG] -> apply[cadr[fn]; args; caddr[fn]];
|
||||
;; eq[car[fn]; LAMBDA] -> eval[caddr[fn]; nconc[pair[cadr[fn]; args]; a]];
|
||||
;; T -> apply[eval[fn;a]; args; a]]
|
22
resources/mexpr/assoc.mexpr.lsp
Normal file
22
resources/mexpr/assoc.mexpr.lsp
Normal file
|
@ -0,0 +1,22 @@
|
|||
;; Page 12 of the manual; this does NOT do what I expect a modern
|
||||
;; ASSOC to do!
|
||||
|
||||
;; Modern ASSOC would be:
|
||||
;; assoc[x; l] = [null[l] -> NIL;
|
||||
;; and[consp[car[l]]; eq[caar[l]; x]] -> cdar[l];
|
||||
;; T -> assoc[x; cdr[l]]]
|
||||
|
||||
;; In the Lisp 1.5 statement of ASSOC, there's no account of what should happen
|
||||
;; if the key (here `x`) is not present on the association list `a`. It seems
|
||||
;; inevitable that this causes an infinite run up the stack until it fails with
|
||||
;; stack exhaustion. Consequently this may be right but I'm not implementing it!
|
||||
;; assoc[x; a] = [equal[caar[a]; x] -> car[a];
|
||||
;; T -> assoc[x; cdr[a]]]
|
||||
|
||||
;; Consequently, my solution is a hybrid. It returns the pair from the
|
||||
;; association list, as the original does, but it traps the end of list
|
||||
;; condition, as a modern solution would.
|
||||
|
||||
assoc[x; l] = [null[l] -> NIL;
|
||||
and[consp[car[l]]; eq[caar[l]; x]] -> car[l];
|
||||
T -> assoc[x; cdr[l]]]
|
6
resources/mexpr/efface.mexpr.lsp
Normal file
6
resources/mexpr/efface.mexpr.lsp
Normal file
|
@ -0,0 +1,6 @@
|
|||
;; page 63. I'm not at all sure why an implementation using RPLACD is preferred
|
||||
;; over a pure functional implementation here.
|
||||
|
||||
efface[x; l] = [null[l] -> NIL;
|
||||
equal[x; car[l]] -> cdr[l];
|
||||
T -> rplacd[l; efface[x; cdr[l]]]]
|
4
resources/mexpr/maplist.mexpr.lsp
Normal file
4
resources/mexpr/maplist.mexpr.lsp
Normal file
|
@ -0,0 +1,4 @@
|
|||
;; page 63
|
||||
|
||||
maplist[l; f] = [null[l] -> nil;
|
||||
T -> cons[f[car[l]]; maplist[cdr[l]; f]]]
|
1
resources/mexpr/not.mexpr
Normal file
1
resources/mexpr/not.mexpr
Normal file
|
@ -0,0 +1 @@
|
|||
not[x] = [x = F -> T; x = NIL -> T; T -> F]
|
5
resources/mexpr/pairlis.mexpr.lsp
Normal file
5
resources/mexpr/pairlis.mexpr.lsp
Normal file
|
@ -0,0 +1,5 @@
|
|||
;; page 12
|
||||
|
||||
pairlis[x;y;a] = [null[x] -> a;
|
||||
T -> cons[cons[car[x]; car[y]];
|
||||
pairlis[cdr[x]; cdr[y]; a]]]
|
0
resources/mexpr/properties.mexpr.lsp
Normal file
0
resources/mexpr/properties.mexpr.lsp
Normal file
3
resources/mexpr/range.mexpr.lsp
Normal file
3
resources/mexpr/range.mexpr.lsp
Normal file
|
@ -0,0 +1,3 @@
|
|||
;; this isn't a standard Lisp 1.5 function
|
||||
|
||||
range[n; m] = [lessp[m; n] -> NIL; T -> cons[n; range[add1[n]; m]]]
|
5
resources/mexpr/search.mexpr.lsp
Normal file
5
resources/mexpr/search.mexpr.lsp
Normal file
|
@ -0,0 +1,5 @@
|
|||
# page 63
|
||||
|
||||
search[x; p; f; u] = [null[x] -> u[x];
|
||||
p[x] -> f[x];
|
||||
T -> search[cdr[x]; p; f; u]]
|
25
resources/mexpr/sublis.mexpr.lsp
Normal file
25
resources/mexpr/sublis.mexpr.lsp
Normal file
|
@ -0,0 +1,25 @@
|
|||
;; There are two different statements of SUBLIS and SUB2 in the manual, on
|
||||
;; pages 12 and 61 respectively, although they are said to be semantically
|
||||
;; equivalent; this is the version from page 12.
|
||||
|
||||
sub2[a; z] = [null[a] -> z;
|
||||
eq[caar[a]; z] -> cdar[a];
|
||||
T -> sub2[cdar[a]; z]]
|
||||
|
||||
sublis[a; y] = [atom[y] -> sub2[a; y];
|
||||
T -> cons[sublis[a; car[y]];
|
||||
sublis[a; cdr[y]]]]
|
||||
|
||||
;; this is the version from page 61
|
||||
|
||||
sublis[x;y] = [null[x] -> y;
|
||||
null[y] -> y;
|
||||
T -> search[x;
|
||||
λ[[j]; equal[y; caar[j]]];
|
||||
λ[[j]; cdar[j]];
|
||||
λ[[j]; [atom[y] -> y;
|
||||
T -> cons[sublis[x; car[y]];
|
||||
sublis[x; cdr[y]]]]]]]
|
||||
|
||||
;; the test for this is:
|
||||
;; (SUBLIS '((X . SHAKESPEARE) (Y . (THE TEMPEST))) '(X WROTE Y))
|
5
resources/mexpr/subst.mexpr.lsp
Normal file
5
resources/mexpr/subst.mexpr.lsp
Normal file
|
@ -0,0 +1,5 @@
|
|||
;; page 11
|
||||
|
||||
subst[x; y; z] = [equal[y; z] -> x;
|
||||
atom[z] -> z;
|
||||
T -> cons[subst[x; y; car[z]]; subst[x; y; cdr[z]]]]
|
|
@ -1 +1,6 @@
|
|||
(SETQ LENGTH '(LAMBDA (L) (COND ((EQ NIL L) 0) (T (ADD1 (LENGTH (CDR L)))))))
|
||||
(SETQ LENGTH
|
||||
'(LAMBDA (L)
|
||||
(COND
|
||||
((EQ NIL L) 0)
|
||||
((CONSP (CDR L)) (ADD1 (LENGTH (CDR L))))
|
||||
(T 0))))
|
|
@ -9,457 +9,271 @@
|
|||
ALLUPPERCASE are Lisp 1.5 functions (although written in Clojure) and that
|
||||
therefore all arguments must be numbers, symbols or `beowulf.cons_cell.ConsCell`
|
||||
objects."
|
||||
(:require [clojure.string :as s]
|
||||
[beowulf.cons-cell :refer [CAR CDR CONS LIST make-beowulf-list make-cons-cell
|
||||
pretty-print T F]]
|
||||
[beowulf.host :refer [AND ADD1 DIFFERENCE ERROR FIXP GENSYM GREATERP LESSP
|
||||
NUMBERP PLUS QUOTIENT
|
||||
REMAINDER RPLACA RPLACD TIMES]]
|
||||
[beowulf.io :refer [SYSIN SYSOUT]]
|
||||
[beowulf.oblist :refer [*options* oblist NIL]]
|
||||
[beowulf.read :refer [READ]]
|
||||
[beowulf.trace :refer [TRACE traced? UNTRACE]])
|
||||
(:require [beowulf.cons-cell :refer [F make-beowulf-list make-cons-cell
|
||||
pretty-print T]]
|
||||
[beowulf.host :refer [ASSOC ATOM CAAR CADAR CADDR CADR CAR CDR GET
|
||||
LIST NUMBERP PAIRLIS traced?]]
|
||||
[beowulf.oblist :refer [*options* NIL oblist]])
|
||||
(:import [beowulf.cons_cell ConsCell]
|
||||
[clojure.lang Symbol]))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;
|
||||
;;; This file is essentially Lisp as defined in Chapter 1 (pages 1-14) of the
|
||||
;;; Lisp 1.5 Programmer's Manual; that is to say, a very simple Lisp language,
|
||||
;;; which should, I believe, be sufficient in conjunction with the functions
|
||||
;;; provided by `beowulf.host`, be sufficient to bootstrap the full Lisp 1.5
|
||||
;;; interpreter.
|
||||
;;; Copyright (C) 2022-2023 Simon Brooke
|
||||
;;;
|
||||
;;; This program is free software; you can redistribute it and/or
|
||||
;;; modify it under the terms of the GNU General Public License
|
||||
;;; as published by the Free Software Foundation; either version 2
|
||||
;;; of the License, or (at your option) any later version.
|
||||
;;;
|
||||
;;; This program is distributed in the hope that it will be useful,
|
||||
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;;; GNU General Public License for more details.
|
||||
;;;
|
||||
;;; You should have received a copy of the GNU General Public License
|
||||
;;; along with this program; if not, write to the Free Software
|
||||
;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(declare APPLY EVAL)
|
||||
(declare APPLY EVAL prog-eval)
|
||||
|
||||
(defn lax?
|
||||
"Are we in lax mode? If so. return true; is not, throw an exception with
|
||||
this `symbol`."
|
||||
[symbol]
|
||||
(when (:strict *options*)
|
||||
(throw (ex-info (format "%s is not available in Lisp 1.5" symbol)
|
||||
{:cause :strict
|
||||
:extension symbol})))
|
||||
true)
|
||||
;;;; The PROGram feature ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defmacro NULL
|
||||
"Returns `T` if and only if the argument `x` is bound to `NIL`; else `F`."
|
||||
[x]
|
||||
`(if (= ~x NIL) T F))
|
||||
(def find-target
|
||||
(memoize
|
||||
(fn [target body]
|
||||
(loop [body' body]
|
||||
(cond
|
||||
(= body' NIL) (throw (ex-info (str "Mislar GO miercels: `" target "`")
|
||||
{:phase :lisp
|
||||
:function 'PROG
|
||||
:type :lisp
|
||||
:code :A6
|
||||
:target target}))
|
||||
(= (.getCar body') target) body'
|
||||
:else (recur (.getCdr body')))))))
|
||||
|
||||
(defmacro NILP
|
||||
"Not part of LISP 1.5: `T` if `o` is `NIL`, else `NIL`."
|
||||
[x]
|
||||
`(if (= ~x NIL) T NIL))
|
||||
|
||||
(defmacro ATOM
|
||||
"Returns `T` if and only if the argument `x` is bound to an atom; else `F`.
|
||||
It is not clear to me from the documentation whether `(ATOM 7)` should return
|
||||
`T` or `F`. I'm going to assume `T`."
|
||||
[x]
|
||||
`(if (or (symbol? ~x) (number? ~x)) T F))
|
||||
|
||||
(defmacro ATOM?
|
||||
"The convention of returning `F` from predicates, rather than `NIL`, is going
|
||||
to tie me in knots. This is a variant of `ATOM` which returns `NIL`
|
||||
on failure."
|
||||
[x]
|
||||
`(if (or (symbol? ~x) (number? ~x)) T NIL))
|
||||
|
||||
(defn uaf
|
||||
"Universal access function; `l` is expected to be an arbitrary LISP list, `path`
|
||||
a (clojure) list of the characters `a` and `d`. Intended to make declaring
|
||||
all those fiddly `#'c[ad]+r'` functions a bit easier"
|
||||
[l path]
|
||||
(cond
|
||||
(= l NIL) NIL
|
||||
(empty? path) l
|
||||
:else
|
||||
(try
|
||||
(case (last path)
|
||||
\a (uaf (.first l) (butlast path))
|
||||
\d (uaf (.getCdr l) (butlast path))
|
||||
(throw (ex-info (str "uaf: unexpected letter in path (only `a` and `d` permitted): " (last path))
|
||||
{:cause :uaf
|
||||
:detail :unexpected-letter
|
||||
:expr (last path)})))
|
||||
(catch ClassCastException e
|
||||
(throw (ex-info
|
||||
(str "uaf: Not a LISP list? " (type l))
|
||||
{:cause :uaf
|
||||
:detail :not-a-lisp-list
|
||||
:expr l}))))))
|
||||
|
||||
(defmacro CAAR [x] `(uaf ~x '(\a \a)))
|
||||
(defmacro CADR [x] `(uaf ~x '(\a \d)))
|
||||
(defmacro CDDR [x] `(uaf ~x '(\d \d)))
|
||||
(defmacro CDAR [x] `(uaf ~x '(\d \a)))
|
||||
|
||||
(defmacro CAAAR [x] `(uaf ~x '(\a \a \a)))
|
||||
(defmacro CAADR [x] `(uaf ~x '(\a \a \d)))
|
||||
(defmacro CADAR [x] `(uaf ~x '(\a \d \a)))
|
||||
(defmacro CADDR [x] `(uaf ~x '(\a \d \d)))
|
||||
(defmacro CDDAR [x] `(uaf ~x '(\d \d \a)))
|
||||
(defmacro CDDDR [x] `(uaf ~x '(\d \d \d)))
|
||||
(defmacro CDAAR [x] `(uaf ~x '(\d \a \a)))
|
||||
(defmacro CDADR [x] `(uaf ~x '(\d \a \d)))
|
||||
|
||||
(defmacro CAAAAR [x] `(uaf ~x '(\a \a \a \a)))
|
||||
(defmacro CAADAR [x] `(uaf ~x '(\a \a \d \a)))
|
||||
(defmacro CADAAR [x] `(uaf ~x '(\a \d \a \a)))
|
||||
(defmacro CADDAR [x] `(uaf ~x '(\a \d \d \a)))
|
||||
(defmacro CDDAAR [x] `(uaf ~x '(\d \d \a \a)))
|
||||
(defmacro CDDDAR [x] `(uaf ~x '(\d \d \d \a)))
|
||||
(defmacro CDAAAR [x] `(uaf ~x '(\d \a \a \a)))
|
||||
(defmacro CDADAR [x] `(uaf ~x '(\d \a \d \a)))
|
||||
(defmacro CAAADR [x] `(uaf ~x '(\a \a \a \d)))
|
||||
(defmacro CAADDR [x] `(uaf ~x '(\a \a \d \d)))
|
||||
(defmacro CADADR [x] `(uaf ~x '(\a \d \a \d)))
|
||||
(defmacro CADDDR [x] `(uaf ~x '(\a \d \d \d)))
|
||||
(defmacro CDDADR [x] `(uaf ~x '(\d \d \a \d)))
|
||||
(defmacro CDDDDR [x] `(uaf ~x '(\d \d \d \d)))
|
||||
(defmacro CDAADR [x] `(uaf ~x '(\d \a \a \d)))
|
||||
(defmacro CDADDR [x] `(uaf ~x '(\d \a \d \d)))
|
||||
|
||||
(defn EQ
|
||||
"Returns `T` if and only if both `x` and `y` are bound to the same atom,
|
||||
else `NIL`."
|
||||
[x y]
|
||||
(cond (and (instance? ConsCell x)
|
||||
(.equals x y)) T
|
||||
(and (= (ATOM x) T) (= x y)) T
|
||||
:else NIL))
|
||||
|
||||
(defn EQUAL
|
||||
"This is a predicate that is true if its two arguments are identical
|
||||
S-expressions, and false if they are different. (The elementary predicate
|
||||
`EQ` is defined only for atomic arguments.) The definition of `EQUAL` is
|
||||
an example of a conditional expression inside a conditional expression.
|
||||
|
||||
NOTE: returns `F` on failure, not `NIL`"
|
||||
[x y]
|
||||
(cond
|
||||
(= (ATOM x) T) (if (= x y) T F)
|
||||
(= (EQUAL (CAR x) (CAR y)) T) (EQUAL (CDR x) (CDR y))
|
||||
:else F))
|
||||
|
||||
(defn SUBST
|
||||
"This function gives the result of substituting the S-expression `x` for
|
||||
all occurrences of the atomic symbol `y` in the S-expression `z`."
|
||||
[x y z]
|
||||
(cond
|
||||
(= (EQUAL y z) T) x
|
||||
(= (ATOM? z) T) z ;; NIL is a symbol
|
||||
:else
|
||||
(make-cons-cell (SUBST x y (CAR z)) (SUBST x y (CDR z)))))
|
||||
|
||||
(defn APPEND
|
||||
"Append the the elements of `y` to the elements of `x`.
|
||||
|
||||
All args are assumed to be `beowulf.cons-cell/ConsCell` objects.
|
||||
See page 11 of the Lisp 1.5 Programmers Manual."
|
||||
[x y]
|
||||
(cond
|
||||
(= x NIL) y
|
||||
:else
|
||||
(make-cons-cell (CAR x) (APPEND (CDR x) y))))
|
||||
|
||||
(defn MEMBER
|
||||
"This predicate is true if the S-expression `x` occurs among the elements
|
||||
of the list `y`.
|
||||
|
||||
All args are assumed to be symbols or `beowulf.cons-cell/ConsCell` objects.
|
||||
See page 11 of the Lisp 1.5 Programmers Manual."
|
||||
[x y]
|
||||
(cond
|
||||
(= y NIL) F ;; NOTE: returns F on falsity, not NIL
|
||||
(= (EQUAL x (CAR y)) T) T
|
||||
:else (MEMBER x (CDR y))))
|
||||
|
||||
(defn PAIRLIS
|
||||
"This function gives the list of pairs of corresponding elements of the
|
||||
lists `x` and `y`, and APPENDs this to the list `a`. The resultant list
|
||||
of pairs, which is like a table with two columns, is called an
|
||||
association list.
|
||||
|
||||
Eessentially, it builds the environment on the stack, implementing shallow
|
||||
binding.
|
||||
|
||||
All args are assumed to be `beowulf.cons-cell/ConsCell` objects.
|
||||
See page 12 of the Lisp 1.5 Programmers Manual."
|
||||
[x y a]
|
||||
(cond
|
||||
;; the original tests only x; testing y as well will be a little more
|
||||
;; robust if `x` and `y` are not the same length.
|
||||
(or (= NIL x) (= NIL y)) a
|
||||
:else (make-cons-cell
|
||||
(make-cons-cell (CAR x) (CAR y))
|
||||
(PAIRLIS (CDR x) (CDR y) a))))
|
||||
|
||||
(defmacro QUOTE
|
||||
"Quote, but in upper case for LISP 1.5"
|
||||
[f]
|
||||
`(quote ~f))
|
||||
|
||||
(defn ASSOC
|
||||
"If a is an association list such as the one formed by PAIRLIS in the above
|
||||
example, then assoc will produce the first pair whose first term is x. Thus
|
||||
it is a table searching function.
|
||||
|
||||
All args are assumed to be `beowulf.cons-cell/ConsCell` objects.
|
||||
See page 12 of the Lisp 1.5 Programmers Manual."
|
||||
[x a]
|
||||
(cond
|
||||
(= NIL a) NIL ;; this clause is not present in the original but is added for
|
||||
;; robustness.
|
||||
(= (EQUAL (CAAR a) x) T) (CAR a)
|
||||
:else
|
||||
(ASSOC x (CDR a))))
|
||||
|
||||
(defn- SUB2
|
||||
"Internal to `SUBLIS`, q.v., which SUBSTitutes into a list from a store.
|
||||
? I think this is doing variable binding in the stack frame?"
|
||||
[a z]
|
||||
(cond
|
||||
(= NIL a) z
|
||||
(= (CAAR a) z) (CDAR a) ;; TODO: this looks definitely wrong
|
||||
:else
|
||||
(SUB2 (CDR a) z)))
|
||||
|
||||
(defn SUBLIS
|
||||
"Here `a` is assumed to be an association list of the form
|
||||
`((ul . vl)...(un . vn))`, where the `u`s are atomic, and `y` is any
|
||||
S-expression. What `SUBLIS` does, is to treat the `u`s as variables when
|
||||
they occur in `y`, and to SUBSTitute the corresponding `v`s from the pair
|
||||
list.
|
||||
|
||||
My interpretation is that this is variable binding in the stack frame.
|
||||
|
||||
All args are assumed to be `beowulf.cons-cell/ConsCell` objects.
|
||||
See page 12 of the Lisp 1.5 Programmers Manual."
|
||||
[a y]
|
||||
(cond
|
||||
(= (ATOM? y) T) (SUB2 a y)
|
||||
:else
|
||||
(make-cons-cell (SUBLIS a (CAR y)) (SUBLIS a (CDR y)))))
|
||||
|
||||
(defn interop-interpret-q-name
|
||||
"For interoperation with Clojure, it will often be necessary to pass
|
||||
qualified names that are not representable in Lisp 1.5. This function
|
||||
takes a sequence in the form `(PART PART PART... NAME)` and returns
|
||||
a symbol in the form `PART.PART.PART/NAME`. This symbol will then be
|
||||
tried in both that form and lower-cased. Names with hyphens or
|
||||
underscores cannot be represented with this scheme."
|
||||
[l]
|
||||
(if
|
||||
(seq? l)
|
||||
(symbol
|
||||
(s/reverse
|
||||
(s/replace-first
|
||||
(s/reverse
|
||||
(s/join "." (map str l)))
|
||||
"."
|
||||
"/")))
|
||||
l))
|
||||
|
||||
(defn to-beowulf
|
||||
"Return a beowulf-native representation of the Clojure object `o`.
|
||||
Numbers and symbols are unaffected. Collections have to be converted;
|
||||
strings must be converted to symbols."
|
||||
[o]
|
||||
(cond
|
||||
(coll? o) (make-beowulf-list o)
|
||||
(string? o) (symbol (s/upper-case o))
|
||||
:else o))
|
||||
|
||||
(defn to-clojure
|
||||
"If l is a `beowulf.cons_cell.ConsCell`, return a Clojure list having the
|
||||
same members in the same order."
|
||||
[l]
|
||||
(cond
|
||||
(not (instance? beowulf.cons_cell.ConsCell l))
|
||||
l
|
||||
(= (CDR l) NIL)
|
||||
(list (to-clojure (CAR l)))
|
||||
:else
|
||||
(conj (to-clojure (CDR l)) (to-clojure (CAR l)))))
|
||||
|
||||
(defn INTEROP
|
||||
"Clojure (or other host environment) interoperation API. `fn-symbol` is expected
|
||||
to be either
|
||||
|
||||
1. a symbol bound in the host environment to a function; or
|
||||
2. a sequence (list) of symbols forming a qualified path name bound to a
|
||||
function.
|
||||
|
||||
Lower case characters cannot normally be represented in Lisp 1.5, so both the
|
||||
upper case and lower case variants of `fn-symbol` will be tried. If the
|
||||
function you're looking for has a mixed case name, that is not currently
|
||||
accessible.
|
||||
|
||||
`args` is expected to be a Lisp 1.5 list of arguments to be passed to that
|
||||
function. Return value must be something acceptable to Lisp 1.5, so either
|
||||
a symbol, a number, or a Lisp 1.5 list.
|
||||
|
||||
If `fn-symbol` is not found (even when cast to lower case), or is not a function,
|
||||
or the value returned cannot be represented in Lisp 1.5, an exception is thrown
|
||||
with `:cause` bound to `:interop` and `:detail` set to a value representing the
|
||||
actual problem."
|
||||
[fn-symbol args]
|
||||
(if-not (:strict *options*)
|
||||
(let
|
||||
[q-name (if
|
||||
(seq? fn-symbol)
|
||||
(interop-interpret-q-name fn-symbol)
|
||||
fn-symbol)
|
||||
l-name (symbol (s/lower-case q-name))
|
||||
f (cond
|
||||
(try
|
||||
(fn? (eval l-name))
|
||||
(catch java.lang.ClassNotFoundException e nil)) l-name
|
||||
(try
|
||||
(fn? (eval q-name))
|
||||
(catch java.lang.ClassNotFoundException e nil)) q-name
|
||||
:else (throw
|
||||
(ex-info
|
||||
(str "INTEROP: unknown function `" fn-symbol "`")
|
||||
{:cause :interop
|
||||
:detail :not-found
|
||||
:name fn-symbol
|
||||
:also-tried l-name})))
|
||||
args' (to-clojure args)]
|
||||
(print (str "INTEROP: evaluating `" (cons f args') "`"))
|
||||
(flush)
|
||||
(let [result (eval (conj args' f))] ;; this has the potential to blow up the world
|
||||
(println (str "; returning `" result "`"))
|
||||
|
||||
(cond
|
||||
(instance? beowulf.cons_cell.ConsCell result) result
|
||||
(coll? result) (make-beowulf-list result)
|
||||
(symbol? result) result
|
||||
(string? result) (symbol result)
|
||||
(number? result) result
|
||||
:else (throw
|
||||
(ex-info
|
||||
(str "INTEROP: Cannot return `" result "` to Lisp 1.5.")
|
||||
{:cause :interop
|
||||
:detail :not-representable
|
||||
:result result})))))
|
||||
(throw
|
||||
(ex-info
|
||||
(str "INTEROP not allowed in strict mode.")
|
||||
{:cause :interop
|
||||
:detail :strict}))))
|
||||
|
||||
(defn OBLIST
|
||||
"Return a list of the symbols currently bound on the object list.
|
||||
|
||||
**NOTE THAT** in the Lisp 1.5 manual, footnote at the bottom of page 69, it implies
|
||||
that an argument can be passed but I'm not sure of the semantics of
|
||||
this."
|
||||
[]
|
||||
(when (lax? 'OBLIST)
|
||||
(if (instance? ConsCell @oblist)
|
||||
(make-beowulf-list (map CAR @oblist))
|
||||
(defn- prog-cond
|
||||
"Like `EVCON`, q.v. except using `prog-eval` instead of `EVAL` and not
|
||||
throwing an error if no clause matches."
|
||||
[clauses vars env depth]
|
||||
(loop [clauses' clauses]
|
||||
(if-not (= clauses' NIL)
|
||||
(let [test (prog-eval (CAAR clauses') vars env depth)]
|
||||
(if (not (#{NIL F} test))
|
||||
(prog-eval (CADAR clauses') vars env depth)
|
||||
(recur (.getCdr clauses'))))
|
||||
NIL)))
|
||||
|
||||
(defn DEFINE
|
||||
"Bootstrap-only version of `DEFINE` which, post boostrap, can be overwritten
|
||||
in LISP.
|
||||
(defn- merge-vars [vars env]
|
||||
(reduce
|
||||
#(make-cons-cell
|
||||
(make-cons-cell %2 (@vars %2))
|
||||
env)
|
||||
env
|
||||
(keys @vars)))
|
||||
|
||||
The single argument to `DEFINE` should be an assoc list which should be
|
||||
nconc'ed onto the front of the oblist. Broadly,
|
||||
(SETQ OBLIST (NCONC ARG1 OBLIST))"
|
||||
[args]
|
||||
(swap!
|
||||
oblist
|
||||
(fn [ob arg1]
|
||||
(loop [cursor arg1 a arg1]
|
||||
(if (= (CDR cursor) NIL)
|
||||
(do
|
||||
(.rplacd cursor @oblist)
|
||||
(pretty-print a)
|
||||
a)
|
||||
(recur (CDR cursor) a))))
|
||||
(CAR args)))
|
||||
(defn prog-eval
|
||||
"Like `EVAL`, q.v., except handling symbols, and expressions starting
|
||||
`GO`, `RETURN`, `SET` and `SETQ` specially."
|
||||
[expr vars env depth]
|
||||
(cond
|
||||
(number? expr) expr
|
||||
(symbol? expr) (@vars expr)
|
||||
(instance? ConsCell expr) (case (.getCar expr)
|
||||
COND (prog-cond (.getCdr expr)
|
||||
vars env depth)
|
||||
GO (make-cons-cell
|
||||
'*PROGGO* (.getCar (.getCdr expr)))
|
||||
RETURN (make-cons-cell
|
||||
'*PROGRETURN*
|
||||
(prog-eval (.getCar (.getCdr expr))
|
||||
vars env depth))
|
||||
SET (let [v (CADDR expr)]
|
||||
(swap! vars
|
||||
assoc
|
||||
(prog-eval (CADR expr)
|
||||
vars env depth)
|
||||
(prog-eval (CADDR expr)
|
||||
vars env depth))
|
||||
v)
|
||||
SETQ (let [v (CADDR expr)]
|
||||
(swap! vars
|
||||
assoc
|
||||
(CADR expr)
|
||||
(prog-eval v
|
||||
vars env depth))
|
||||
v)
|
||||
;; else
|
||||
(beowulf.bootstrap/EVAL expr
|
||||
(merge-vars vars env)
|
||||
depth))))
|
||||
|
||||
(defn SET
|
||||
"Implementation of SET in Clojure. Add to the `oblist` a binding of the
|
||||
value of `var` to the value of `val`. NOTE WELL: this is not SETQ!"
|
||||
[symbol val]
|
||||
(when
|
||||
(swap!
|
||||
oblist
|
||||
(fn [ob s v] (make-cons-cell (make-cons-cell s v) ob))
|
||||
symbol val)
|
||||
NIL))
|
||||
(defn PROG
|
||||
"The accursed `PROG` feature. See page 71 of the manual.
|
||||
|
||||
Lisp 1.5 introduced `PROG`, and most Lisps have been stuck with it ever
|
||||
since. It introduces imperative programming into what should be a pure
|
||||
functional language, and consequently it's going to be a pig to implement.
|
||||
|
||||
Broadly, `PROG` is a variadic pseudo function called as a `FEXPR` (or
|
||||
possibly an `FSUBR`, although I'm not presently sure that would even work.)
|
||||
|
||||
The arguments, which are unevaluated, are a list of forms, the first of
|
||||
which is expected to be a list of symbols which will be treated as names
|
||||
of variables within the program, and the rest of which (the 'program body')
|
||||
are either lists or symbols. Lists are treated as Lisp expressions which
|
||||
may be evaulated in turn. Symbols are treated as targets for the `GO`
|
||||
statement.
|
||||
|
||||
**GO:**
|
||||
A `GO` statement takes the form of `(GO target)`, where
|
||||
`target` should be one of the symbols which occur at top level among that
|
||||
particular invocation of `PROG`s arguments. A `GO` statement may occur at
|
||||
top level in a PROG, or in a clause of a `COND` statement in a `PROG`, but
|
||||
not in a function called from the `PROG` statement. When a `GO` statement
|
||||
is evaluated, execution should transfer immediately to the expression which
|
||||
is the argument list immediately following the symbol which is its target.
|
||||
|
||||
If the target is not found, an error with the code `A6` should be thrown.
|
||||
|
||||
**RETURN:**
|
||||
A `RETURN` statement takes the form `(RETURN value)`, where
|
||||
`value` is any value. Following the evaluation of a `RETURN` statement,
|
||||
the `PROG` should immediately exit without executing any further
|
||||
expressions, returning the value.
|
||||
|
||||
**SET and SETQ:**
|
||||
In addition to the above, if a `SET` or `SETQ` expression is encountered
|
||||
in any expression within the `PROG` body, it should affect not the global
|
||||
object list but instead only the local variables of the program.
|
||||
|
||||
**COND:**
|
||||
In **strict** mode, when in normal execution, a `COND` statement none of
|
||||
whose clauses match should not return `NIL` but should throw an error with
|
||||
the code `A3`... *except* that inside a `PROG` body, it should not do so.
|
||||
*sigh*.
|
||||
|
||||
**Flow of control:**
|
||||
Apart from the exceptions specified above, expressions in the program body
|
||||
are evaluated sequentially. If execution reaches the end of the program
|
||||
body, `NIL` is returned.
|
||||
|
||||
Got all that?
|
||||
|
||||
Good."
|
||||
[program env depth]
|
||||
(let [trace (traced? 'PROG)
|
||||
vars (atom (reduce merge (map #(assoc {} % NIL) (.getCar program))))
|
||||
body (.getCdr program)
|
||||
targets (set (filter symbol? body))]
|
||||
(when trace (do
|
||||
(println "Program:")
|
||||
(pretty-print program))) ;; for debugging
|
||||
(loop [cursor body]
|
||||
(let [step (.getCar cursor)]
|
||||
(when trace (do (println "Executing step: " step)
|
||||
(println " with vars: " @vars)))
|
||||
(cond (= cursor NIL) NIL
|
||||
(symbol? step) (recur (.getCdr cursor))
|
||||
:else (let [v (prog-eval (.getCar cursor) vars env depth)]
|
||||
(when trace (println " --> " v))
|
||||
(if (instance? ConsCell v)
|
||||
(case (.getCar v)
|
||||
*PROGGO* (let [target (.getCdr v)]
|
||||
(if (targets target)
|
||||
(recur (find-target target body))
|
||||
(throw (ex-info (str "Uncynlic GO miercels `"
|
||||
target "`")
|
||||
{:phase :lisp
|
||||
:function 'PROG
|
||||
:args program
|
||||
:type :lisp
|
||||
:code :A6
|
||||
:target target
|
||||
:targets targets}))))
|
||||
*PROGRETURN* (.getCdr v)
|
||||
;; else
|
||||
(recur (.getCdr cursor)))
|
||||
(recur (.getCdr cursor)))))))))
|
||||
|
||||
;;;; Tracing execution ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defn- trace-call
|
||||
"Show a trace of a call to the function named by this `function-symbol`
|
||||
with these `args` at this depth."
|
||||
[function-symbol args depth]
|
||||
(when (traced? function-symbol)
|
||||
(let [indent (apply str (repeat depth "-"))]
|
||||
(println (str indent "> " function-symbol " " args)))))
|
||||
|
||||
(defn- trace-response
|
||||
"Show a trace of this `response` from the function named by this
|
||||
`function-symbol` at this depth."
|
||||
[function-symbol response depth]
|
||||
(when (traced? function-symbol)
|
||||
(let [indent (apply str (repeat depth "-"))]
|
||||
(println (str "<" indent " " function-symbol " " response))))
|
||||
response)
|
||||
|
||||
(defn- value
|
||||
"Seek a value for this symbol `s` by checking each of these indicators in
|
||||
turn."
|
||||
([s]
|
||||
(value s (list 'APVAL 'EXPR 'FEXPR 'SUBR 'FSUBR)))
|
||||
([s indicators]
|
||||
(when (symbol? s)
|
||||
(first (remove #(= % NIL) (map #(GET s %)
|
||||
indicators))))))
|
||||
|
||||
;;;; APPLY ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defn try-resolve-subroutine
|
||||
"Attempt to resolve this `subr` with these `args`."
|
||||
[subr args]
|
||||
(when (and subr (not= subr NIL))
|
||||
(try @(resolve subr)
|
||||
(catch Throwable any
|
||||
(throw (ex-info "þegnung (SUBR) ne āfand"
|
||||
{:phase :apply
|
||||
:function subr
|
||||
:args args
|
||||
:type :beowulf}
|
||||
any))))))
|
||||
|
||||
(defn- apply-symbolic
|
||||
"Apply this `funtion-symbol` to these `args` in this `environment` and
|
||||
return the result."
|
||||
[^Symbol function-symbol ^ConsCell args ^ConsCell environment depth]
|
||||
(let [fn (try (EVAL function-symbol environment depth)
|
||||
(catch Throwable any (when (:trace *options*)
|
||||
(println any))))
|
||||
indent (apply str (repeat depth "-"))]
|
||||
(if (and fn (not= fn NIL))
|
||||
(if (traced? function-symbol)
|
||||
(do
|
||||
(println (str indent "> " function-symbol " " args))
|
||||
(let [r (APPLY fn args environment depth)]
|
||||
(println (str "<" indent " " r))
|
||||
r))
|
||||
(APPLY fn args environment depth))
|
||||
(case function-symbol ;; there must be a better way of doing this!
|
||||
ADD1 (apply ADD1 args)
|
||||
AND (apply AND args)
|
||||
APPEND (apply APPEND args)
|
||||
APPLY (apply APPLY args)
|
||||
ATOM (ATOM? (CAR args))
|
||||
CAR (CAAR args)
|
||||
CDR (CDAR args)
|
||||
CONS (make-cons-cell (CAR args) (CADR args))
|
||||
DEFINE (DEFINE (CAR args))
|
||||
DIFFERENCE (DIFFERENCE (CAR args) (CADR args))
|
||||
EQ (apply EQ args)
|
||||
EQUAL (apply EQUAL args)
|
||||
ERROR (apply ERROR args)
|
||||
;; think about EVAL. Getting the environment right is subtle
|
||||
FIXP (apply FIXP args)
|
||||
GENSYM (GENSYM)
|
||||
GREATERP (apply GREATERP args)
|
||||
INTEROP (when (lax? INTEROP) (apply INTEROP args))
|
||||
LESSP (apply LESSP args)
|
||||
LIST (apply LIST args)
|
||||
NUMBERP (apply NUMBERP args)
|
||||
OBLIST (OBLIST)
|
||||
PLUS (apply PLUS args)
|
||||
PRETTY (when (lax? 'PRETTY)
|
||||
(apply pretty-print args))
|
||||
PRINT (apply print args)
|
||||
QUOTIENT (apply QUOTIENT args)
|
||||
READ (READ)
|
||||
REMAINDER (apply REMAINDER args)
|
||||
RPLACA (apply RPLACA args)
|
||||
RPLACD (apply RPLACD args)
|
||||
SET (apply SET args)
|
||||
SYSIN (when (lax? 'SYSIN)
|
||||
(apply SYSIN args))
|
||||
SYSOUT (when (lax? 'SYSOUT)
|
||||
(if (empty? args)
|
||||
(SYSOUT)
|
||||
(apply SYSOUT args)))
|
||||
TERPRI (println)
|
||||
TIMES (apply TIMES args)
|
||||
TRACE (apply TRACE args)
|
||||
UNTRACE (apply UNTRACE args)
|
||||
;; else
|
||||
(ex-info "No function found"
|
||||
{:context "APPLY"
|
||||
:function function-symbol
|
||||
:args args})))))
|
||||
[^Symbol function-symbol args ^ConsCell environment depth]
|
||||
(trace-call function-symbol args depth)
|
||||
(let [lisp-fn (value function-symbol '(EXPR FEXPR))
|
||||
args' (cond (= NIL args) args
|
||||
(empty? args) NIL
|
||||
(instance? ConsCell args) args
|
||||
:else (make-beowulf-list args))
|
||||
subr (value function-symbol '(SUBR FSUBR))
|
||||
host-fn (try-resolve-subroutine subr args')
|
||||
result (cond (and lisp-fn
|
||||
(not= lisp-fn NIL)) (APPLY lisp-fn args' environment depth)
|
||||
host-fn (try
|
||||
(apply host-fn (when (instance? ConsCell args') args'))
|
||||
(catch Exception any
|
||||
(throw (ex-info (str "Uncynlic þegnung: "
|
||||
(.getMessage any))
|
||||
{:phase :apply
|
||||
:function function-symbol
|
||||
:args args
|
||||
:type :beowulf}
|
||||
any))))
|
||||
:else (ex-info "þegnung ne āfand"
|
||||
{:phase :apply
|
||||
:function function-symbol
|
||||
:args args
|
||||
:type :beowulf}))]
|
||||
(trace-response function-symbol result depth)
|
||||
result))
|
||||
|
||||
(defn APPLY
|
||||
"Apply this `function` to these `arguments` in this `environment` and return
|
||||
|
@ -469,39 +283,63 @@
|
|||
All args are assumed to be symbols or `beowulf.cons-cell/ConsCell` objects.
|
||||
See page 13 of the Lisp 1.5 Programmers Manual."
|
||||
[function args environment depth]
|
||||
(cond
|
||||
(= NIL function) (if (:strict *options*)
|
||||
NIL
|
||||
(throw (ex-info "NIL is not a function"
|
||||
{:context "APPLY"
|
||||
:function "NIL"
|
||||
:args args})))
|
||||
(= (ATOM? function) T) (apply-symbolic function args environment (inc depth))
|
||||
(= (first function) 'LAMBDA) (EVAL
|
||||
(CADDR function)
|
||||
(PAIRLIS (CADR function) args environment) depth)
|
||||
(= (first function) 'LABEL) (APPLY
|
||||
(CADDR function)
|
||||
args
|
||||
(trace-call 'APPLY (list function args environment) depth)
|
||||
(let [result (cond
|
||||
(= NIL function) (if (:strict *options*)
|
||||
NIL
|
||||
(throw (ex-info "NIL sí ne þegnung"
|
||||
{:phase :apply
|
||||
:function "NIL"
|
||||
:args args
|
||||
:type :beowulf})))
|
||||
(= (ATOM function) T) (apply-symbolic function args environment (inc depth))
|
||||
:else (case (first function)
|
||||
LABEL (APPLY
|
||||
(CADDR function)
|
||||
args
|
||||
(make-cons-cell
|
||||
(make-cons-cell
|
||||
(make-cons-cell
|
||||
(CADR function)
|
||||
(CADDR function))
|
||||
environment)
|
||||
depth)))
|
||||
(CADR function)
|
||||
(CADDR function))
|
||||
environment)
|
||||
depth)
|
||||
FUNARG (APPLY (CADR function) args (CADDR function) depth)
|
||||
LAMBDA (EVAL
|
||||
(CADDR function)
|
||||
(PAIRLIS (CADR function) args environment) depth)
|
||||
(throw (ex-info "Ungecnáwen wyrþan sí þegnung-weard"
|
||||
{:phase :apply
|
||||
:function function
|
||||
:args args
|
||||
:type :beowulf}))))]
|
||||
(trace-response 'APPLY result depth)
|
||||
result))
|
||||
|
||||
;;;; EVAL ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defn- EVCON
|
||||
"Inner guts of primitive COND. All `clauses` are assumed to be
|
||||
`beowulf.cons-cell/ConsCell` objects. Note that tests in Lisp 1.5
|
||||
often return `F`, not `NIL`, on failure.
|
||||
often return `F`, not `NIL`, on failure. If no clause matches,
|
||||
then, strictly, we throw an error with code `:A3`.
|
||||
|
||||
See page 13 of the Lisp 1.5 Programmers Manual."
|
||||
See pages 13 and 71 of the Lisp 1.5 Programmers Manual."
|
||||
[clauses env depth]
|
||||
(let [test (EVAL (CAAR clauses) env depth)]
|
||||
(if
|
||||
(and (not= test NIL) (not= test 'F))
|
||||
(EVAL (CADAR clauses) env depth)
|
||||
(EVCON (CDR clauses) env depth))))
|
||||
(loop [clauses' clauses]
|
||||
(if-not (= clauses' NIL)
|
||||
(let [test (EVAL (CAAR clauses') env depth)]
|
||||
(if (not (#{NIL F} test))
|
||||
;; (and (not= test NIL) (not= test F))
|
||||
(EVAL (CADAR clauses') env depth)
|
||||
(recur (.getCdr clauses'))))
|
||||
(if (:strict *options*)
|
||||
(throw (ex-info "Ne ġefōg dǣl in COND"
|
||||
{:phase :eval
|
||||
:function 'COND
|
||||
:args (list clauses)
|
||||
:type :lisp
|
||||
:code :A3}))
|
||||
NIL))))
|
||||
|
||||
(defn- EVLIS
|
||||
"Map `EVAL` across this list of `args` in the context of this
|
||||
|
@ -515,14 +353,25 @@
|
|||
(EVAL (CAR args) env depth)
|
||||
(EVLIS (CDR args) env depth))))
|
||||
|
||||
(defn- eval-symbolic [^Symbol s env]
|
||||
(let [binding (ASSOC s env)]
|
||||
(if (= binding NIL)
|
||||
(throw (ex-info (format "No binding for symbol `%s`" s)
|
||||
{:phase :eval
|
||||
:symbol s}))
|
||||
(CDR binding))))
|
||||
|
||||
(defn- eval-symbolic
|
||||
[expr env depth]
|
||||
(let [v (ASSOC expr env)
|
||||
indent (apply str (repeat depth "-"))]
|
||||
(when (traced? 'EVAL)
|
||||
(println (str indent ": EVAL: sceald bindele: " (or v "nil"))))
|
||||
(if (instance? ConsCell v)
|
||||
(.getCdr v)
|
||||
(let [v' (value expr (list 'APVAL))]
|
||||
(when (traced? 'EVAL)
|
||||
(println (str indent ": EVAL: deóp bindele: (" expr " . " (or v' "nil") ")")))
|
||||
(if v'
|
||||
v'
|
||||
(throw (ex-info "Ne tácen-bindele āfand"
|
||||
{:phase :eval
|
||||
:function 'EVAL
|
||||
:args (list expr env depth)
|
||||
:type :lisp
|
||||
:code :A8})))))))
|
||||
|
||||
(defn EVAL
|
||||
"Evaluate this `expr` and return the result. If `environment` is not passed,
|
||||
|
@ -530,34 +379,44 @@
|
|||
argument is part of the tracing system and should not be set by user code.
|
||||
|
||||
All args are assumed to be numbers, symbols or `beowulf.cons-cell/ConsCell`
|
||||
objects."
|
||||
objects. However, if called with just a single arg, `expr`, I'll assume it's
|
||||
being called from the Clojure REPL and will coerce the `expr` to `ConsCell`."
|
||||
([expr]
|
||||
(EVAL expr @oblist 0))
|
||||
(let [expr' (if (and (coll? expr) (not (instance? ConsCell expr)))
|
||||
(make-beowulf-list expr)
|
||||
expr)]
|
||||
(EVAL expr' NIL 0)))
|
||||
([expr env depth]
|
||||
(cond
|
||||
(= (NUMBERP expr) T) expr
|
||||
(symbol? expr) (eval-symbolic expr env)
|
||||
(string? expr) (if (:strict *options*)
|
||||
(throw
|
||||
(ex-info
|
||||
(str "EVAL: strings not allowed in strict mode: \"" expr "\"")
|
||||
{:phase :eval
|
||||
:detail :strict
|
||||
:expr expr}))
|
||||
(symbol expr))
|
||||
(=
|
||||
(ATOM? (CAR expr))
|
||||
T) (cond
|
||||
(= (CAR expr) 'QUOTE) (CADR expr)
|
||||
(= (CAR expr) 'COND) (EVCON (CDR expr) env depth)
|
||||
:else (APPLY
|
||||
(CAR expr)
|
||||
(EVLIS (CDR expr) env depth)
|
||||
env
|
||||
depth))
|
||||
:else (APPLY
|
||||
(CAR expr)
|
||||
(EVLIS (CDR expr) env depth)
|
||||
env
|
||||
depth))))
|
||||
(trace-call 'EVAL (list expr env depth) depth)
|
||||
(let [result (cond
|
||||
(= NIL expr) NIL ;; it was probably a mistake to make Lisp
|
||||
;; NIL distinct from Clojure nil
|
||||
(= (NUMBERP expr) T) expr
|
||||
(symbol? expr) (eval-symbolic expr env depth)
|
||||
(string? expr) (if (:strict *options*)
|
||||
(throw
|
||||
(ex-info
|
||||
(str "EVAL: strings not allowed in strict mode: \"" expr "\"")
|
||||
{:phase :eval
|
||||
:detail :strict
|
||||
:expr expr}))
|
||||
(symbol expr))
|
||||
(= (ATOM (CAR expr)) T) (case (CAR expr)
|
||||
COND (EVCON (CDR expr) env depth)
|
||||
FUNCTION (LIST 'FUNARG (CADR expr))
|
||||
PROG (PROG (CDR expr) env depth)
|
||||
QUOTE (CADR expr)
|
||||
;; else
|
||||
(APPLY
|
||||
(CAR expr)
|
||||
(EVLIS (CDR expr) env depth)
|
||||
env
|
||||
depth))
|
||||
:else (APPLY
|
||||
(CAR expr)
|
||||
(EVLIS (CDR expr) env depth)
|
||||
env
|
||||
depth))]
|
||||
(trace-response 'EVAL result depth)
|
||||
result)))
|
||||
|
||||
|
|
|
@ -5,6 +5,26 @@
|
|||
of Clojure lists."
|
||||
(:require [beowulf.oblist :refer [NIL]]))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;
|
||||
;;; Copyright (C) 2022-2023 Simon Brooke
|
||||
;;;
|
||||
;;; This program is free software; you can redistribute it and/or
|
||||
;;; modify it under the terms of the GNU General Public License
|
||||
;;; as published by the Free Software Foundation; either version 2
|
||||
;;; of the License, or (at your option) any later version.
|
||||
;;;
|
||||
;;; This program is distributed in the hope that it will be useful,
|
||||
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;;; GNU General Public License for more details.
|
||||
;;;
|
||||
;;; You should have received a copy of the GNU General Public License
|
||||
;;; along with this program; if not, write to the Free Software
|
||||
;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(declare cons-cell?)
|
||||
|
||||
(def T
|
||||
|
@ -16,6 +36,8 @@
|
|||
false in Lisp 1.5."
|
||||
(symbol "F")) ;; false as distinct from nil
|
||||
|
||||
;;;; The actual cons-cell ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defprotocol MutableSequence
|
||||
"Like a sequence, but mutable."
|
||||
(rplaca
|
||||
|
@ -31,9 +53,8 @@
|
|||
[this]
|
||||
"like `more`, q.v., but returns List `NIL` not Clojure `nil` when empty.")
|
||||
(getUid
|
||||
[this]
|
||||
"Returns a unique identifier for this object")
|
||||
)
|
||||
[this]
|
||||
"Returns a unique identifier for this object"))
|
||||
|
||||
(deftype ConsCell [^:unsynchronized-mutable CAR ^:unsynchronized-mutable CDR uid]
|
||||
;; Note that, because the CAR and CDR fields are unsynchronised mutable - i.e.
|
||||
|
@ -56,7 +77,7 @@
|
|||
(set! (. this CAR) value)
|
||||
this)
|
||||
(throw (ex-info
|
||||
(str "Invalid value in RPLACA: `" value "` (" (type value) ")")
|
||||
(str "Uncynlic miercels in RPLACA: `" value "` (" (type value) ")")
|
||||
{:cause :bad-value
|
||||
:detail :rplaca}))))
|
||||
|
||||
|
@ -71,7 +92,7 @@
|
|||
(set! (. this CDR) value)
|
||||
this)
|
||||
(throw (ex-info
|
||||
(str "Invalid value in RPLACD: `" value "` (" (type value) ")")
|
||||
(str "Uncynlic miercels in RPLACD: `" value "` (" (type value) ")")
|
||||
{:cause :bad-value
|
||||
:detail :rplaca}))))
|
||||
|
||||
|
@ -138,13 +159,19 @@
|
|||
(cond
|
||||
(instance? ConsCell (. this CDR)) (str " " (subs (.toString (. this CDR)) 1))
|
||||
(= NIL (. this CDR)) ")"
|
||||
:else (str " . " (. this CDR))))))
|
||||
:else (str " . " (. this CDR) ")")))))
|
||||
|
||||
;;;; Printing. Here be dragons! ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defn- to-string
|
||||
"Printing ConsCells gave me a *lot* of trouble. This is an internal function
|
||||
used by the print-method override (below) in order that the standard Clojure
|
||||
`print` and `str` functions will print ConsCells correctly. The argument
|
||||
`cell` must, obviously, be an instance of `ConsCell`."
|
||||
;; TODO: I am deeply suspicious both of this and the defmethod which depends
|
||||
;; on it. I *think* they are implicated in the `COPY` bug. If the `toString`
|
||||
;; override in `ConsCell` was right, neither of these would be necessary.
|
||||
;; see https://github.com/simon-brooke/beowulf/issues/5
|
||||
[cell]
|
||||
(loop [c cell
|
||||
n 0
|
||||
|
@ -161,23 +188,26 @@
|
|||
s
|
||||
(to-string car)
|
||||
(cond
|
||||
(or (nil? cdr) (= cdr NIL))
|
||||
")"
|
||||
cons?
|
||||
" "
|
||||
:else
|
||||
(str " . " (to-string cdr) ")")))]
|
||||
(or (nil? cdr) (= cdr NIL)) ")"
|
||||
cons? " "
|
||||
:else (str " . " (to-string cdr) ")")))]
|
||||
(if
|
||||
cons?
|
||||
(recur cdr (inc n) ss)
|
||||
ss))
|
||||
(str c))))
|
||||
|
||||
(defmethod clojure.core/print-method
|
||||
;;; I have not worked out how to document defmethod without blowing up the world.
|
||||
beowulf.cons_cell.ConsCell
|
||||
[this writer]
|
||||
(.write writer (to-string this)))
|
||||
|
||||
(defn pretty-print
|
||||
"This isn't the world's best pretty printer but it sort of works."
|
||||
([^beowulf.cons_cell.ConsCell cell]
|
||||
([cell]
|
||||
(println (pretty-print cell 80 0)))
|
||||
([^beowulf.cons_cell.ConsCell cell width level]
|
||||
([cell width level]
|
||||
(loop [c cell
|
||||
n (inc level)
|
||||
s "("]
|
||||
|
@ -185,7 +215,7 @@
|
|||
(instance? beowulf.cons_cell.ConsCell c)
|
||||
(let [car (.first c)
|
||||
cdr (.getCdr c)
|
||||
cons? (instance? beowulf.cons_cell.ConsCell cdr)
|
||||
tail? (instance? beowulf.cons_cell.ConsCell cdr)
|
||||
print-width (count (print-str c))
|
||||
indent (apply str (repeat n " "))
|
||||
ss (str
|
||||
|
@ -194,7 +224,7 @@
|
|||
(cond
|
||||
(or (nil? cdr) (= cdr NIL))
|
||||
")"
|
||||
cons?
|
||||
tail?
|
||||
(if
|
||||
(< (+ (count indent) print-width) width)
|
||||
" "
|
||||
|
@ -202,17 +232,15 @@
|
|||
:else
|
||||
(str " . " (pretty-print cdr width n) ")")))]
|
||||
(if
|
||||
cons?
|
||||
tail?
|
||||
(recur cdr n ss)
|
||||
ss))
|
||||
(str c)))))
|
||||
|
||||
|
||||
(defmethod clojure.core/print-method
|
||||
;;; I have not worked out how to document defmethod without blowing up the world.
|
||||
beowulf.cons_cell.ConsCell
|
||||
[this writer]
|
||||
(.write writer (to-string this)))
|
||||
(defn cons-cell?
|
||||
"Is this object `o` a beowulf cons-cell?"
|
||||
[o]
|
||||
(instance? beowulf.cons_cell.ConsCell o))
|
||||
|
||||
(defn make-cons-cell
|
||||
"Construct a new instance of cons cell with this `car` and `cdr`."
|
||||
|
@ -220,14 +248,9 @@
|
|||
(try
|
||||
(ConsCell. car cdr (gensym "c"))
|
||||
(catch Exception any
|
||||
(throw (ex-info "Cound not construct cons cell" {:car car
|
||||
(throw (ex-info "Ne meahte cræfte cons cell" {:car car
|
||||
:cdr cdr} any)))))
|
||||
|
||||
(defn cons-cell?
|
||||
"Is this object `o` a beowulf cons-cell?"
|
||||
[o]
|
||||
(instance? beowulf.cons_cell.ConsCell o))
|
||||
|
||||
(defn make-beowulf-list
|
||||
"Construct a linked list of cons cells with the same content as the
|
||||
sequence `x`."
|
||||
|
@ -235,6 +258,7 @@
|
|||
(try
|
||||
(cond
|
||||
(empty? x) NIL
|
||||
(instance? ConsCell x) (make-cons-cell (.getCar x) (.getCdr x))
|
||||
(coll? x) (ConsCell.
|
||||
(if
|
||||
(coll? (first x))
|
||||
|
@ -245,39 +269,6 @@
|
|||
:else
|
||||
NIL)
|
||||
(catch Exception any
|
||||
(throw (ex-info "Could not construct Beowulf list"
|
||||
(throw (ex-info "Ne meahte cræfte Beowulf líste"
|
||||
{:content x}
|
||||
any)))))
|
||||
|
||||
(defn CONS
|
||||
"Construct a new instance of cons cell with this `car` and `cdr`."
|
||||
[car cdr]
|
||||
(beowulf.cons_cell.ConsCell. car cdr (gensym "c")))
|
||||
|
||||
(defn CAR
|
||||
"Return the item indicated by the first pointer of a pair. NIL is treated
|
||||
specially: the CAR of NIL is NIL."
|
||||
[x]
|
||||
(if
|
||||
(= x NIL) NIL
|
||||
(try
|
||||
(or (.getCar x) NIL)
|
||||
(catch Exception any
|
||||
(throw (Exception.
|
||||
(str "Cannot take CAR of `" x "` (" (.getName (.getClass x)) ")") any))))))
|
||||
|
||||
(defn CDR
|
||||
"Return the item indicated by the second pointer of a pair. NIL is treated
|
||||
specially: the CDR of NIL is NIL."
|
||||
[x]
|
||||
(if
|
||||
(= x NIL) NIL
|
||||
(try
|
||||
(.getCdr x)
|
||||
(catch Exception any
|
||||
(throw (Exception.
|
||||
(str "Cannot take CDR of `" x "` (" (.getName (.getClass x)) ")") any))))))
|
||||
|
||||
(defn LIST
|
||||
[& args]
|
||||
(make-beowulf-list args))
|
|
@ -1,16 +1,39 @@
|
|||
(ns beowulf.core
|
||||
"Essentially, the `-main` function and the bootstrap read-eval-print loop."
|
||||
(:require [beowulf.bootstrap :refer [EVAL]]
|
||||
[beowulf.io :refer [SYSIN]]
|
||||
[beowulf.io :refer [default-sysout SYSIN]]
|
||||
[beowulf.oblist :refer [*options* NIL]]
|
||||
[beowulf.read :refer [READ read-from-console]]
|
||||
[beowulf.oblist :refer [*options* oblist]]
|
||||
[clojure.java.io :as io]
|
||||
[clojure.pprint :refer [pprint]]
|
||||
[clojure.string :refer [trim]]
|
||||
[clojure.tools.cli :refer [parse-opts]])
|
||||
(:gen-class))
|
||||
|
||||
(def stop-word "STOP")
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;
|
||||
;;; Copyright (C) 2022-2023 Simon Brooke
|
||||
;;;
|
||||
;;; This program is free software; you can redistribute it and/or
|
||||
;;; modify it under the terms of the GNU General Public License
|
||||
;;; as published by the Free Software Foundation; either version 2
|
||||
;;; of the License, or (at your option) any later version.
|
||||
;;;
|
||||
;;; This program is distributed in the hope that it will be useful,
|
||||
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;;; GNU General Public License for more details.
|
||||
;;;
|
||||
;;; You should have received a copy of the GNU General Public License
|
||||
;;; along with this program; if not, write to the Free Software
|
||||
;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(def stop-word
|
||||
"The word which, if submitted an an input line, will cause Beowulf to quit.
|
||||
Question: should this be `forlǣte`?"
|
||||
"STOP")
|
||||
|
||||
(def cli-options
|
||||
[["-f FILEPATH" "--file-path FILEPATH"
|
||||
|
@ -23,13 +46,19 @@
|
|||
["-h" "--help"]
|
||||
["-p PROMPT" "--prompt PROMPT" "Set the REPL prompt to PROMPT"
|
||||
:default "Sprecan::"]
|
||||
["-r INITFILE" "--read INITFILE" "Read Lisp system from file INITFILE"
|
||||
:default "resources/lisp1.5.lsp"
|
||||
["-r SYSOUTFILE" "--read SYSOUTFILE" "Read Lisp system from file SYSOUTFILE"
|
||||
:default default-sysout
|
||||
:validate [#(and
|
||||
(.exists (io/file %))
|
||||
(.canRead (io/file %)))
|
||||
"Could not find initfile"]]
|
||||
["-s" "--strict" "Strictly interpret the Lisp 1.5 language, without extensions."]])
|
||||
"Could not find sysout file"]]
|
||||
["-s" "--strict" "Strictly interpret the Lisp 1.5 language, without extensions."]
|
||||
["-t" "--time" "Time evaluations."]])
|
||||
|
||||
(defn- re
|
||||
"Like REPL, but it isn't a loop and doesn't print."
|
||||
[input]
|
||||
(EVAL (READ input) NIL 0))
|
||||
|
||||
(defn repl
|
||||
"Read/eval/print loop."
|
||||
|
@ -38,11 +67,15 @@
|
|||
(print prompt)
|
||||
(flush)
|
||||
(try
|
||||
(let [input (trim (read-from-console))]
|
||||
(cond
|
||||
(= input stop-word) (throw (ex-info "\nFærwell!" {:cause :quit}))
|
||||
input (println (str "> " (print-str (EVAL (READ input) @oblist 0))))
|
||||
:else (println)))
|
||||
(if-let [input (trim (read-from-console))]
|
||||
(if (= input stop-word)
|
||||
(throw (ex-info "\nFærwell!" {:cause :quit}))
|
||||
(println
|
||||
(str "> "
|
||||
(print-str (if (:time *options*)
|
||||
(time (re input))
|
||||
(re input))))))
|
||||
(println))
|
||||
(catch
|
||||
Exception
|
||||
e
|
||||
|
@ -77,7 +110,7 @@
|
|||
"\nSprecan '" stop-word "' tó laéfan\n"))
|
||||
|
||||
(binding [*options* (:options args)]
|
||||
;; (pprint *options*)
|
||||
;; (pprint *options*)
|
||||
(when (:read *options*)
|
||||
(try (SYSIN (:read *options*))
|
||||
(catch Throwable any
|
||||
|
@ -93,5 +126,7 @@
|
|||
(case (:cause data)
|
||||
:quit nil
|
||||
;; default
|
||||
(pprint data))
|
||||
(do
|
||||
(println "STÆFLEAHTER: " (.getMessage e))
|
||||
(pprint data)))
|
||||
(println e))))))))
|
||||
|
|
|
@ -1,18 +1,48 @@
|
|||
(ns beowulf.gendoc
|
||||
(:require [beowulf.oblist :refer [oblist]]
|
||||
[clojure.string :refer [join replace]]))
|
||||
"Generate table of documentation of Lisp symbols and functions.
|
||||
|
||||
NOTE: this is *very* hacky. You almost certainly do not want to
|
||||
use this!"
|
||||
(:require ;; [beowulf.io :refer [default-sysout SYSIN]]
|
||||
[beowulf.manual :refer [format-page-references index
|
||||
*manual-url* page-url]]
|
||||
[beowulf.oblist :refer [NIL oblist]]
|
||||
[clojure.java.browse :refer [browse-url]]
|
||||
[clojure.string :as s]))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;
|
||||
;;; Copyright (C) 2022-2023 Simon Brooke
|
||||
;;;
|
||||
;;; This program is free software; you can redistribute it and/or
|
||||
;;; modify it under the terms of the GNU General Public License
|
||||
;;; as published by the Free Software Foundation; either version 2
|
||||
;;; of the License, or (at your option) any later version.
|
||||
;;;
|
||||
;;; This program is distributed in the hope that it will be useful,
|
||||
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;;; GNU General Public License for more details.
|
||||
;;;
|
||||
;;; You should have received a copy of the GNU General Public License
|
||||
;;; along with this program; if not, write to the Free Software
|
||||
;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(def host-functions
|
||||
"Functions which we can infer are written in Clojure."
|
||||
(reduce
|
||||
merge
|
||||
{}
|
||||
(map
|
||||
ns-publics
|
||||
['beowulf.bootstrap
|
||||
'beowulf.host
|
||||
'beowulf.io
|
||||
'beowulf.read])))
|
||||
"Functions which we can infer are written in Clojure. We need to collect these
|
||||
at run-time, not compile time, hence memoised function, not variable."
|
||||
(memoize
|
||||
(fn [] (reduce
|
||||
merge
|
||||
{}
|
||||
(map
|
||||
ns-publics
|
||||
['beowulf.bootstrap
|
||||
'beowulf.host
|
||||
'beowulf.io
|
||||
'beowulf.read])))))
|
||||
|
||||
;; OK, this, improbably, works. There's probably a better way...
|
||||
;; (:doc (meta (eval (read-string (str "#'" "beowulf.read" "/" "READ")))))
|
||||
|
@ -30,48 +60,123 @@
|
|||
(when (keyword? key)
|
||||
(key (get-metadata-for-function function)))))
|
||||
|
||||
|
||||
(defn- get-metadata-for-entry [entry key]
|
||||
(let [fn (host-functions (symbol (first entry)))]
|
||||
(let [fn ((host-functions) (symbol (first entry)))]
|
||||
(get-metadata-for-function fn key)))
|
||||
|
||||
|
||||
(defn infer-type
|
||||
"Try to work out what this `entry` from the oblist actually
|
||||
represents."
|
||||
[entry]
|
||||
(cond
|
||||
(= (second entry) 'LAMBDA) "Lisp function"
|
||||
(host-functions (first entry)) "Host function"
|
||||
:else "?"))
|
||||
(let [interpretation {'APVAL "Lisp variable"
|
||||
'EXPR "Lisp lambda function"
|
||||
'FEXPR "Lisp nlambda function"
|
||||
'SUBR "Host lambda function"
|
||||
'FSUBR "Host nlambda function"}]
|
||||
(s/join ", "
|
||||
(remove nil?
|
||||
(map
|
||||
#(when (some #{%} entry) (interpretation %))
|
||||
(keys interpretation))))))
|
||||
;; (cond
|
||||
;; (= (nth entry 2) 'EXPR) "Lisp function"
|
||||
;; (= (nth entry 2) 'FEXPR) "Labeled form"
|
||||
;; ((host-functions) (first entry)) (try (if (fn? (eval (symbol ((host-functions) (first entry)))))
|
||||
;; "Host function"
|
||||
;; "Host variable")
|
||||
;; (catch Exception _
|
||||
;; "?Host macro?"))
|
||||
;; :else "Lisp variable"))
|
||||
|
||||
(defn- format-clj-signature
|
||||
"Format the signature of the Clojure function represented by `symbol` for
|
||||
Lisp documentation."
|
||||
[symbol arglists]
|
||||
(s/join
|
||||
"; "
|
||||
(doall
|
||||
(map
|
||||
(fn [l]
|
||||
(s/join (concat (list "(" symbol " ")
|
||||
(s/join " " (map #(s/upper-case (str %)) l)) (list ")"))))
|
||||
arglists))))
|
||||
|
||||
(defn infer-signature
|
||||
"Infer the signature of the function value of this oblist `entry`, if any."
|
||||
[entry]
|
||||
(cond
|
||||
(= (count entry) 1) (get-metadata-for-entry entry :arglists)
|
||||
(= (second entry) 'LAMBDA) (nth entry 2)
|
||||
(= (count entry) 1) (format-clj-signature
|
||||
(first entry)
|
||||
(get-metadata-for-entry entry :arglists))
|
||||
(= (second entry) 'LAMBDA) (str (cons (first entry) (nth entry 2)))
|
||||
:else "?"))
|
||||
|
||||
(defn infer-implementation
|
||||
[entry]
|
||||
(or (:implementation (index (keyword (first entry)))) "?"))
|
||||
;; (case (second entry)
|
||||
;; LAMBDA (format "%s-fn" (second entry))
|
||||
;; LABEL (format "%s-fn" (second entry))
|
||||
;; (or (:implementation (index (keyword (first entry)))) (str entry))))
|
||||
|
||||
(defn find-documentation
|
||||
"Find appropriate documentation for this `entry` from the oblist."
|
||||
[entry]
|
||||
(cond
|
||||
(= (count entry) 1) (if-let [doc (get-metadata-for-entry entry :doc)]
|
||||
(replace doc "\n" " ")
|
||||
"?")
|
||||
:else "?"))
|
||||
(let [k (keyword (first entry))]
|
||||
(cond
|
||||
(some #{'SUBR 'FSUBR} entry) (if-let [doc (get-metadata-for-entry entry :doc)]
|
||||
(s/replace doc "\n" " ")
|
||||
"?")
|
||||
(k index) (str "see manual pages " (format-page-references k))
|
||||
:else "?")))
|
||||
|
||||
(defn gen-doc-table
|
||||
[]
|
||||
(join
|
||||
"\n"
|
||||
(doall
|
||||
(concat
|
||||
'("| Symbol | Type | Signature | Documentation |"
|
||||
"|--------|------|-----------|---------------|")
|
||||
(map
|
||||
#(format "| %s | %s | %s | %s |"
|
||||
(first %)
|
||||
(infer-type %)
|
||||
(infer-signature %)
|
||||
(find-documentation %))
|
||||
@oblist)))))
|
||||
([]
|
||||
;; (gen-doc-table default-sysout))
|
||||
;; ([sysfile]
|
||||
;; (when (= NIL @oblist)
|
||||
;; (try (SYSIN sysfile)
|
||||
;; (catch Throwable any
|
||||
;; (println (.getMessage any) " while reading " sysfile))))
|
||||
(s/join
|
||||
"\n"
|
||||
(doall
|
||||
(concat
|
||||
'("| Function | Type | Signature | Implementation | Documentation |"
|
||||
"|--------------|----------------|------------------|----------------|----------------------|")
|
||||
(map
|
||||
#(format "| %-12s | %-14s | %-16s | %-14s | %-20s |"
|
||||
(first %)
|
||||
(infer-type %)
|
||||
(infer-signature %)
|
||||
(infer-implementation %)
|
||||
(find-documentation %))
|
||||
@oblist))))))
|
||||
|
||||
;; (println (gen-doc-table))
|
||||
(defn gen-index
|
||||
([] (gen-index "" "resources/scratch/manual.md"))
|
||||
([url destination]
|
||||
(binding [*manual-url* url]
|
||||
(spit destination
|
||||
(with-out-str
|
||||
(doall
|
||||
(map
|
||||
println
|
||||
(list "## Index"
|
||||
""
|
||||
(gen-doc-table)))))))))
|
||||
|
||||
(defn open-doc
|
||||
"Open the documentation page for this `symbol`, if known, in the default
|
||||
web browser."
|
||||
[symbol]
|
||||
(let [doc (get-metadata-for-function symbol :doc)]
|
||||
(if-let [pages (:page-nos (index (keyword symbol)))]
|
||||
(browse-url (page-url (first pages)))
|
||||
(if doc
|
||||
(println doc)
|
||||
(throw (ex-info "No documentation found"
|
||||
{:phase :host
|
||||
:function 'DOC
|
||||
:args (list symbol)
|
||||
:type :beowulf}))))))
|
|
@ -2,18 +2,275 @@
|
|||
"provides Lisp 1.5 functions which can't be (or can't efficiently
|
||||
be) implemented in Lisp 1.5, which therefore need to be implemented in the
|
||||
host language, in this case Clojure."
|
||||
(:require [clojure.string :refer [upper-case]]
|
||||
[beowulf.cons-cell :refer [F make-beowulf-list T]]
|
||||
;; note hyphen - this is Clojure...
|
||||
[beowulf.oblist :refer [NIL]])
|
||||
(:import [beowulf.cons_cell ConsCell]
|
||||
;; note underscore - same namespace, but Java.
|
||||
(:require [beowulf.cons-cell :refer [F make-beowulf-list make-cons-cell
|
||||
pretty-print T]] ;; note hyphen - this is Clojure...
|
||||
[beowulf.gendoc :refer [open-doc]]
|
||||
[beowulf.oblist :refer [*options* NIL oblist]]
|
||||
[clojure.set :refer [union]]
|
||||
[clojure.string :refer [upper-case]])
|
||||
(:import [beowulf.cons_cell ConsCell] ;; note underscore - same namespace, but Java.
|
||||
))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;
|
||||
;;; Copyright (C) 2022-2023 Simon Brooke
|
||||
;;;
|
||||
;;; This program is free software; you can redistribute it and/or
|
||||
;;; modify it under the terms of the GNU General Public License
|
||||
;;; as published by the Free Software Foundation; either version 2
|
||||
;;; of the License, or (at your option) any later version.
|
||||
;;;
|
||||
;;; This program is distributed in the hope that it will be useful,
|
||||
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;;; GNU General Public License for more details.
|
||||
;;;
|
||||
;;; You should have received a copy of the GNU General Public License
|
||||
;;; along with this program; if not, write to the Free Software
|
||||
;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
;; these are CANDIDATES to be host-implemented. only a subset of them MUST be.
|
||||
;; those which can be implemented in Lisp should be, since that aids
|
||||
;; portability.
|
||||
|
||||
|
||||
(defn lax?
|
||||
"Are we in lax mode? If so. return true; is not, throw an exception with
|
||||
this `symbol`."
|
||||
[symbol]
|
||||
(when (:strict *options*)
|
||||
(throw (ex-info (format "%s ne āfand innan Lisp 1.5" symbol)
|
||||
{:type :strict
|
||||
:phase :host
|
||||
:function symbol})))
|
||||
true)
|
||||
|
||||
;;;; Basic operations on cons cells ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defn CONS
|
||||
"Construct a new instance of cons cell with this `car` and `cdr`."
|
||||
[car cdr]
|
||||
(beowulf.cons_cell.ConsCell. car cdr (gensym "c")))
|
||||
|
||||
(defn CAR
|
||||
"Return the item indicated by the first pointer of a pair. NIL is treated
|
||||
specially: the CAR of NIL is NIL."
|
||||
[x]
|
||||
(cond
|
||||
(= x NIL) NIL
|
||||
(instance? ConsCell x) (or (.getCar x) NIL)
|
||||
:else (throw (ex-info
|
||||
(str "Ne can tace CAR of `" x "` (" (.getName (.getClass x)) ")")
|
||||
{:phase :host
|
||||
:function 'CAR
|
||||
:args (list x)
|
||||
:type :beowulf}))))
|
||||
|
||||
(defn CDR
|
||||
"Return the item indicated by the second pointer of a pair. NIL is treated
|
||||
specially: the CDR of NIL is NIL."
|
||||
[x]
|
||||
(cond
|
||||
(= x NIL) NIL
|
||||
(instance? ConsCell x) (or (.getCdr x) NIL)
|
||||
:else (throw (ex-info
|
||||
(str "Ne can tace CDR of `" x "` (" (.getName (.getClass x)) ")")
|
||||
{:phase :host
|
||||
:function 'CDR
|
||||
:args (list x)
|
||||
:type :beowulf}))))
|
||||
|
||||
|
||||
(defn uaf
|
||||
"Universal access function; `l` is expected to be an arbitrary LISP list, `path`
|
||||
a (clojure) list of the characters `a` and `d`. Intended to make declaring
|
||||
all those fiddly `#'c[ad]+r'` functions a bit easier"
|
||||
[l path]
|
||||
(cond
|
||||
(= l NIL) NIL
|
||||
(empty? path) l
|
||||
:else
|
||||
(try
|
||||
(case (last path)
|
||||
\a (uaf (.first l) (butlast path))
|
||||
\d (uaf (.getCdr l) (butlast path))
|
||||
(throw (ex-info (str "uaf: unexpected letter in path (only `a` and `d` permitted): " (last path))
|
||||
{:cause :uaf
|
||||
:detail :unexpected-letter
|
||||
:expr (last path)})))
|
||||
(catch ClassCastException e
|
||||
(throw (ex-info
|
||||
(str "uaf: Not a LISP list? " (type l))
|
||||
{:cause :uaf
|
||||
:detail :not-a-lisp-list
|
||||
:expr l}
|
||||
e))))))
|
||||
|
||||
(defmacro CAAR [x] `(uaf ~x '(\a \a)))
|
||||
(defmacro CADR [x] `(uaf ~x '(\a \d)))
|
||||
(defmacro CDDR [x] `(uaf ~x '(\d \d)))
|
||||
(defmacro CDAR [x] `(uaf ~x '(\d \a)))
|
||||
|
||||
(defmacro CAAAR [x] `(uaf ~x '(\a \a \a)))
|
||||
(defmacro CAADR [x] `(uaf ~x '(\a \a \d)))
|
||||
(defmacro CADAR [x] `(uaf ~x '(\a \d \a)))
|
||||
(defmacro CADDR [x] `(uaf ~x '(\a \d \d)))
|
||||
(defmacro CDDAR [x] `(uaf ~x '(\d \d \a)))
|
||||
(defmacro CDDDR [x] `(uaf ~x '(\d \d \d)))
|
||||
(defmacro CDAAR [x] `(uaf ~x '(\d \a \a)))
|
||||
(defmacro CDADR [x] `(uaf ~x '(\d \a \d)))
|
||||
|
||||
(defmacro CAAAAR [x] `(uaf ~x '(\a \a \a \a)))
|
||||
(defmacro CAADAR [x] `(uaf ~x '(\a \a \d \a)))
|
||||
(defmacro CADAAR [x] `(uaf ~x '(\a \d \a \a)))
|
||||
(defmacro CADDAR [x] `(uaf ~x '(\a \d \d \a)))
|
||||
(defmacro CDDAAR [x] `(uaf ~x '(\d \d \a \a)))
|
||||
(defmacro CDDDAR [x] `(uaf ~x '(\d \d \d \a)))
|
||||
(defmacro CDAAAR [x] `(uaf ~x '(\d \a \a \a)))
|
||||
(defmacro CDADAR [x] `(uaf ~x '(\d \a \d \a)))
|
||||
(defmacro CAAADR [x] `(uaf ~x '(\a \a \a \d)))
|
||||
(defmacro CAADDR [x] `(uaf ~x '(\a \a \d \d)))
|
||||
(defmacro CADADR [x] `(uaf ~x '(\a \d \a \d)))
|
||||
(defmacro CADDDR [x] `(uaf ~x '(\a \d \d \d)))
|
||||
(defmacro CDDADR [x] `(uaf ~x '(\d \d \a \d)))
|
||||
(defmacro CDDDDR [x] `(uaf ~x '(\d \d \d \d)))
|
||||
(defmacro CDAADR [x] `(uaf ~x '(\d \a \a \d)))
|
||||
(defmacro CDADDR [x] `(uaf ~x '(\d \a \d \d)))
|
||||
|
||||
(defn RPLACA
|
||||
"Replace the CAR pointer of this `cell` with this `value`. Dangerous, should
|
||||
really not exist, but does in Lisp 1.5 (and was important for some
|
||||
performance hacks in early Lisps)"
|
||||
[^ConsCell cell value]
|
||||
(if
|
||||
(instance? ConsCell cell)
|
||||
(if
|
||||
(or
|
||||
(instance? ConsCell value)
|
||||
(number? value)
|
||||
(symbol? value)
|
||||
(= value NIL))
|
||||
(try
|
||||
(.rplaca cell value)
|
||||
cell
|
||||
(catch Throwable any
|
||||
(throw (ex-info
|
||||
(str (.getMessage any) " in RPLACA: `")
|
||||
{:cause :upstream-error
|
||||
:phase :host
|
||||
:function :rplaca
|
||||
:args (list cell value)
|
||||
:type :beowulf}
|
||||
any))))
|
||||
(throw (ex-info
|
||||
(str "Un-ġefōg þing in RPLACA: `" value "` (" (type value) ")")
|
||||
{:cause :bad-value
|
||||
:phase :host
|
||||
:function :rplaca
|
||||
:args (list cell value)
|
||||
:type :beowulf})))
|
||||
(throw (ex-info
|
||||
(str "Uncynlic miercels in RPLACA: `" cell "` (" (type cell) ")")
|
||||
{:cause :bad-cell
|
||||
:phase :host
|
||||
:function :rplaca
|
||||
:args (list cell value)
|
||||
:type :beowulf}))))
|
||||
|
||||
(defn RPLACD
|
||||
"Replace the CDR pointer of this `cell` with this `value`. Dangerous, should
|
||||
really not exist, but does in Lisp 1.5 (and was important for some
|
||||
performance hacks in early Lisps)"
|
||||
[^ConsCell cell value]
|
||||
(if
|
||||
(instance? ConsCell cell)
|
||||
(if
|
||||
(or
|
||||
(instance? ConsCell value)
|
||||
(number? value)
|
||||
(symbol? value)
|
||||
(= value NIL))
|
||||
(try
|
||||
(.rplacd cell value)
|
||||
cell
|
||||
(catch Throwable any
|
||||
(throw (ex-info
|
||||
(str (.getMessage any) " in RPLACD: `")
|
||||
{:cause :upstream-error
|
||||
:phase :host
|
||||
:function :rplacd
|
||||
:args (list cell value)
|
||||
:type :beowulf}
|
||||
any))))
|
||||
(throw (ex-info
|
||||
(str "Un-ġefōg þing in RPLACD: `" value "` (" (type value) ")")
|
||||
{:cause :bad-value
|
||||
:phase :host
|
||||
:function :rplacd
|
||||
:args (list cell value)
|
||||
:type :beowulf})))
|
||||
(throw (ex-info
|
||||
(str "Uncynlic miercels in RPLACD: `" cell "` (" (type cell) ")")
|
||||
{:cause :bad-cell
|
||||
:phase :host
|
||||
:detail :rplacd
|
||||
:args (list cell value)
|
||||
:type :beowulf}))));; PLUS
|
||||
|
||||
(defn LIST
|
||||
[& args]
|
||||
(make-beowulf-list args))
|
||||
|
||||
;;;; Basic predicates ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defmacro NULL
|
||||
"Returns `T` if and only if the argument `x` is bound to `NIL`; else `F`."
|
||||
[x]
|
||||
`(if (= ~x NIL) T F))
|
||||
|
||||
(defmacro NILP
|
||||
"Not part of LISP 1.5: `T` if `o` is `NIL`, else `NIL`."
|
||||
[x]
|
||||
`(if (= ~x NIL) T NIL))
|
||||
|
||||
(defn ATOM
|
||||
"Returns `T` if and only if the argument `x` is bound to an atom; else `F`.
|
||||
It is not clear to me from the documentation whether `(ATOM 7)` should return
|
||||
`T` or `F`. I'm going to assume `T`."
|
||||
[x]
|
||||
(if (or (symbol? x) (number? x)) T F))
|
||||
|
||||
(defmacro ATOM?
|
||||
"The convention of returning `F` from predicates, rather than `NIL`, is going
|
||||
to tie me in knots. This is a variant of `ATOM` which returns `NIL`
|
||||
on failure."
|
||||
[x]
|
||||
`(if (or (symbol? ~x) (number? ~x)) T NIL))
|
||||
|
||||
(defn EQ
|
||||
"Returns `T` if and only if both `x` and `y` are bound to the same atom,
|
||||
else `NIL`."
|
||||
[x y]
|
||||
(cond (and (instance? ConsCell x)
|
||||
(.equals x y)) T
|
||||
(and (= (ATOM x) T) (= x y)) T
|
||||
:else NIL))
|
||||
|
||||
(defn EQUAL
|
||||
"This is a predicate that is true if its two arguments are identical
|
||||
S-expressions, and false if they are different. (The elementary predicate
|
||||
`EQ` is defined only for atomic arguments.) The definition of `EQUAL` is
|
||||
an example of a conditional expression inside a conditional expression.
|
||||
|
||||
NOTE: returns `F` on failure, not `NIL`"
|
||||
[x y]
|
||||
(cond
|
||||
(= (ATOM x) T) (if (= x y) T F)
|
||||
(= (EQUAL (CAR x) (CAR y)) T) (EQUAL (CDR x) (CDR y))
|
||||
:else F))
|
||||
|
||||
(defn AND
|
||||
"`T` if and only if none of my `args` evaluate to either `F` or `NIL`,
|
||||
else `F`.
|
||||
|
@ -21,59 +278,76 @@
|
|||
In `beowulf.host` principally because I don't yet feel confident to define
|
||||
varargs functions in Lisp."
|
||||
[& args]
|
||||
(if (empty? (filter #(or (= 'F %) (= NIL %) (nil? %)) args))
|
||||
'T
|
||||
'F))
|
||||
;; (println "AND: " args " type: " (type args) " seq? " (seq? args))
|
||||
;; (println " filtered: " (seq (filter #{F NIL} args)))
|
||||
(cond (= NIL args) T
|
||||
(seq? args) (if (seq (filter #{F NIL} args)) F T)
|
||||
:else T))
|
||||
|
||||
(defn RPLACA
|
||||
"Replace the CAR pointer of this `cell` with this `value`. Dangerous, should
|
||||
really not exist, but does in Lisp 1.5 (and was important for some
|
||||
performance hacks in early Lisps)"
|
||||
[^ConsCell cell value]
|
||||
(if
|
||||
(instance? ConsCell cell)
|
||||
(if
|
||||
(or
|
||||
(instance? ConsCell value)
|
||||
(number? value)
|
||||
(symbol? value)
|
||||
(= value NIL))
|
||||
(do
|
||||
(.rplaca cell value)
|
||||
cell)
|
||||
(throw (ex-info
|
||||
(str "Invalid value in RPLACA: `" value "` (" (type value) ")")
|
||||
{:cause :bad-value
|
||||
:detail :rplaca})))
|
||||
(throw (ex-info
|
||||
(str "Invalid cell in RPLACA: `" cell "` (" (type cell) ")")
|
||||
{:cause :bad-value
|
||||
:detail :rplaca}))))
|
||||
|
||||
(defn RPLACD
|
||||
"Replace the CDR pointer of this `cell` with this `value`. Dangerous, should
|
||||
really not exist, but does in Lisp 1.5 (and was important for some
|
||||
performance hacks in early Lisps)"
|
||||
[^ConsCell cell value]
|
||||
(if
|
||||
(instance? ConsCell cell)
|
||||
(if
|
||||
(or
|
||||
(instance? ConsCell value)
|
||||
(number? value)
|
||||
(symbol? value)
|
||||
(= value NIL))
|
||||
(do
|
||||
(.rplacd cell value)
|
||||
cell)
|
||||
(throw (ex-info
|
||||
(str "Invalid value in RPLACD: `" value "` (" (type value) ")")
|
||||
{:cause :bad-value
|
||||
:detail :rplaca})))
|
||||
(throw (ex-info
|
||||
(str "Invalid cell in RPLACD: `" cell "` (" (type cell) ")")
|
||||
{:cause :bad-value
|
||||
:detail :rplaca}))));; PLUS
|
||||
(defn OR
|
||||
"`T` if and only if at least one of my `args` evaluates to something other
|
||||
than either `F` or `NIL`, else `F`.
|
||||
|
||||
In `beowulf.host` principally because I don't yet feel confident to define
|
||||
varargs functions in Lisp."
|
||||
[& args]
|
||||
;; (println "OR: " args " type: " (type args) " seq? " (seq? args))
|
||||
;; (println " filtered: " (seq (remove #{F NIL} args)))
|
||||
(cond (= NIL args) F
|
||||
(seq? args) (if (seq (remove #{F NIL} args)) T F)
|
||||
:else F))
|
||||
|
||||
|
||||
;;;; Operations on lists ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;
|
||||
;; TODO: These are candidates for moving to Lisp urgently!
|
||||
|
||||
(defn ASSOC
|
||||
"If a is an association list such as the one formed by PAIRLIS in the above
|
||||
example, then assoc will produce the first pair whose first term is x. Thus
|
||||
it is a table searching function.
|
||||
|
||||
All args are assumed to be `beowulf.cons-cell/ConsCell` objects.
|
||||
See page 12 of the Lisp 1.5 Programmers Manual.
|
||||
|
||||
**NOTE THAT** this function is overridden by an implementation in Lisp,
|
||||
but is currently still present for bootstrapping."
|
||||
[x a]
|
||||
(cond
|
||||
(= NIL a) NIL ;; this clause is not present in the original but is added for
|
||||
;; robustness.
|
||||
(= (EQUAL (CAAR a) x) T) (CAR a)
|
||||
:else
|
||||
(ASSOC x (CDR a))))
|
||||
|
||||
(defn PAIRLIS
|
||||
"This function gives the list of pairs of corresponding elements of the
|
||||
lists `x` and `y`, and APPENDs this to the list `a`. The resultant list
|
||||
of pairs, which is like a table with two columns, is called an
|
||||
association list.
|
||||
|
||||
Eessentially, it builds the environment on the stack, implementing shallow
|
||||
binding.
|
||||
|
||||
All args are assumed to be `beowulf.cons-cell/ConsCell` objects.
|
||||
See page 12 of the Lisp 1.5 Programmers Manual.
|
||||
|
||||
**NOTE THAT** this function is overridden by an implementation in Lisp,
|
||||
but is currently still present for bootstrapping."
|
||||
[x y a]
|
||||
(cond
|
||||
;; the original tests only x; testing y as well will be a little more
|
||||
;; robust if `x` and `y` are not the same length.
|
||||
(or (= NIL x) (= NIL y)) a
|
||||
:else (make-cons-cell
|
||||
(make-cons-cell (CAR x) (CAR y))
|
||||
(PAIRLIS (CDR x) (CDR y) a))))
|
||||
|
||||
;;;; Arithmetic ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;
|
||||
;; TODO: When in strict mode, should we limit arithmetic precision to that
|
||||
;; supported by Lisp 1.5?
|
||||
|
||||
(defn PLUS
|
||||
[& args]
|
||||
|
@ -118,6 +392,16 @@
|
|||
[x]
|
||||
(if (number? x) T F))
|
||||
|
||||
(defn LESSP
|
||||
[x y]
|
||||
(if (< x y) T F))
|
||||
|
||||
(defn GREATERP
|
||||
[x y]
|
||||
(if (> x y) T F))
|
||||
|
||||
;;;; Miscellaneous ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defn GENSYM
|
||||
"Generate a unique symbol."
|
||||
[]
|
||||
|
@ -126,13 +410,162 @@
|
|||
(defn ERROR
|
||||
"Throw an error"
|
||||
[& args]
|
||||
(throw (ex-info "LISP ERROR" {:cause (apply vector args)
|
||||
:phase :eval})))
|
||||
(throw (ex-info "LISP STÆFLEAHTER" {:args args
|
||||
:phase :eval
|
||||
:function 'ERROR
|
||||
:type :lisp
|
||||
:code (or (first args) 'A1)})))
|
||||
|
||||
(defn LESSP
|
||||
[x y]
|
||||
(< x y))
|
||||
;;;; Assignment and the object list ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defn GREATERP
|
||||
[x y]
|
||||
(> x y))
|
||||
(defn OBLIST
|
||||
"Return a list of the symbols currently bound on the object list.
|
||||
|
||||
**NOTE THAT** in the Lisp 1.5 manual, footnote at the bottom of page 69, it implies
|
||||
that an argument can be passed but I'm not sure of the semantics of
|
||||
this."
|
||||
[]
|
||||
(if (instance? ConsCell @oblist)
|
||||
(make-beowulf-list (map CAR @oblist))
|
||||
NIL))
|
||||
|
||||
(def magic-marker
|
||||
"The unexplained magic number which marks the start of a property list."
|
||||
(Integer/parseInt "77777" 8))
|
||||
|
||||
(defn PUT
|
||||
"Put this `value` as the value of the property indicated by this `indicator`
|
||||
of this `symbol`. Return `value` on success.
|
||||
|
||||
NOTE THAT there is no `PUT` defined in the manual, but it would have been
|
||||
easy to have defined it so I don't think this fully counts as an extension."
|
||||
[symbol indicator value]
|
||||
(if-let [binding (ASSOC symbol @oblist)]
|
||||
(if-let [prop (ASSOC indicator (CDDR binding))]
|
||||
(RPLACD prop value)
|
||||
(RPLACD binding
|
||||
(make-cons-cell
|
||||
magic-marker
|
||||
(make-cons-cell
|
||||
indicator
|
||||
(make-cons-cell value (CDDR binding))))))
|
||||
(swap!
|
||||
oblist
|
||||
(fn [ob s p v]
|
||||
(make-cons-cell
|
||||
(make-beowulf-list (list s magic-marker p v))
|
||||
ob))
|
||||
symbol indicator value)))
|
||||
|
||||
(defn GET
|
||||
"From the manual:
|
||||
|
||||
'`get` is somewhat like `prop`; however its value is car of the rest of
|
||||
the list if the `indicator` is found, and NIL otherwise.'
|
||||
|
||||
It's clear that `GET` is expected to be defined in terms of `PROP`, but
|
||||
we can't implement `PROP` here because we lack `EVAL`; and we can't have
|
||||
`EVAL` here because both it and `APPLY` depends on `GET`.
|
||||
|
||||
OK, It's worse than that: the statement of the definition of `GET` (and
|
||||
of) `PROP` on page 59 says that the first argument to each must be a list;
|
||||
But the in the definition of `ASSOC` on page 70, when `GET` is called its
|
||||
first argument is always an atom. Since it's `ASSOC` and `EVAL` which I
|
||||
need to make work, I'm going to assume that page 59 is wrong."
|
||||
[symbol indicator]
|
||||
(let [binding (ASSOC symbol @oblist)
|
||||
val (cond
|
||||
(= binding NIL) NIL
|
||||
(= magic-marker
|
||||
(CADR binding)) (loop [b binding]
|
||||
;; (println "GET loop, seeking " indicator ":")
|
||||
;; (pretty-print b)
|
||||
(if (instance? ConsCell b)
|
||||
(if (= (CAR b) indicator)
|
||||
(CADR b) ;; <- this is what we should actually be returning
|
||||
(recur (CDR b)))
|
||||
NIL))
|
||||
:else (throw
|
||||
(ex-info "Misformatted property list (missing magic marker)"
|
||||
{:phase :host
|
||||
:function :get
|
||||
:args (list symbol indicator)
|
||||
:type :beowulf})))]
|
||||
;; (println "<< GET returning: " val)
|
||||
val))
|
||||
|
||||
(defn DEFLIST
|
||||
"For each pair in this association list `a-list`, set the property with this
|
||||
`indicator` of the symbol which is the first element of the pair to the
|
||||
value which is the second element of the pair. See page 58 of the manual."
|
||||
[a-list indicator]
|
||||
(map
|
||||
#(PUT (CAR %) indicator (CDR %))
|
||||
a-list))
|
||||
|
||||
(defn DEFINE
|
||||
"Bootstrap-only version of `DEFINE` which, post boostrap, can be overwritten
|
||||
in LISP.
|
||||
|
||||
The single argument to `DEFINE` should be an association list of symbols to
|
||||
lambda functions. See page 58 of the manual."
|
||||
[a-list]
|
||||
(DEFLIST a-list 'EXPR))
|
||||
|
||||
(defn SET
|
||||
"Implementation of SET in Clojure. Add to the `oblist` a binding of the
|
||||
value of `var` to the value of `val`. NOTE WELL: this is not SETQ!"
|
||||
[symbol val]
|
||||
(PUT symbol 'APVAL val))
|
||||
|
||||
;;;; TRACE and friends ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(def traced-symbols
|
||||
"Symbols currently being traced."
|
||||
(atom #{}))
|
||||
|
||||
(defn traced?
|
||||
"Return `true` iff `s` is a symbol currently being traced, else `nil`."
|
||||
[s]
|
||||
(try (contains? @traced-symbols s)
|
||||
(catch Throwable _ nil)))
|
||||
|
||||
(defn TRACE
|
||||
"Add this `s` to the set of symbols currently being traced. If `s`
|
||||
is not a symbol or sequence of symbols, does nothing."
|
||||
[s]
|
||||
(swap! traced-symbols
|
||||
#(cond
|
||||
(symbol? s) (conj % s)
|
||||
(and (seq? s) (every? symbol? s)) (union % (set s))
|
||||
:else %)))
|
||||
|
||||
(defn UNTRACE
|
||||
"Remove this `s` from the set of symbols currently being traced. If `s`
|
||||
is not a symbol or sequence of symbols, does nothing."
|
||||
[s]
|
||||
(cond
|
||||
(symbol? s) (swap! traced-symbols #(set (remove (fn [x] (= s x)) %)))
|
||||
(and (seq? s) (every? symbol? s)) (map UNTRACE s))
|
||||
@traced-symbols)
|
||||
|
||||
;;;; Extensions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defn DOC
|
||||
"Open the page for this `symbol` in the Lisp 1.5 manual, if known, in the
|
||||
default web browser.
|
||||
|
||||
**NOTE THAT** this is an extension function, not available in strct mode."
|
||||
[symbol]
|
||||
(when (lax? 'DOC)
|
||||
(open-doc symbol)))
|
||||
|
||||
(defn CONSP
|
||||
"Return `T` if object `o` is a cons cell, else `F`.
|
||||
|
||||
**NOTE THAT** this is an extension function, not available in strct mode.
|
||||
I believe that Lisp 1.5 did not have any mechanism for testing whether an
|
||||
argument was, or was not, a cons cell."
|
||||
[o]
|
||||
(when (lax? 'CONSP)
|
||||
(if (instance? ConsCell o) 'T 'F)))
|
129
src/beowulf/interop.clj
Normal file
129
src/beowulf/interop.clj
Normal file
|
@ -0,0 +1,129 @@
|
|||
(ns beowulf.interop
|
||||
(:require [beowulf.cons-cell :refer [make-beowulf-list]]
|
||||
[beowulf.host :refer [CAR CDR]]
|
||||
[beowulf.oblist :refer [*options* NIL]]
|
||||
[clojure.string :as s :refer [last-index-of lower-case split
|
||||
upper-case]]))
|
||||
|
||||
;;;; INTEROP feature ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defn listify-qualified-name
|
||||
"We need to be able to print something we can link to the particular Clojure
|
||||
function `subr` in a form in which Lisp 1.5 is able to read it back in and
|
||||
relink it.
|
||||
|
||||
This assumes `subr` is either
|
||||
1. a string in the format `#'beowulf.io/SYSIN` or `beowulf.io/SYSIN`; or
|
||||
2. something which, when coerced to a string with `str`, will have such
|
||||
a format."
|
||||
[subr]
|
||||
(make-beowulf-list
|
||||
(map
|
||||
#(symbol (upper-case %))
|
||||
(remove empty? (split (str subr) #"[#'./]")))))
|
||||
|
||||
|
||||
(defn interpret-qualified-name
|
||||
"For interoperation with Clojure, it will often be necessary to pass
|
||||
qualified names that are not representable in Lisp 1.5. This function
|
||||
takes a sequence in the form `(PART PART PART... NAME)` and returns
|
||||
a symbol in the form `part.part.part/NAME`. This symbol will then be
|
||||
tried in both that form and lower-cased. Names with hyphens or
|
||||
underscores cannot be represented with this scheme."
|
||||
([l]
|
||||
(symbol
|
||||
(let [n (s/join "."
|
||||
(concat (map #(lower-case (str %)) (butlast l))
|
||||
(list (last l))))
|
||||
s (last-index-of n ".")]
|
||||
(if s
|
||||
(str (subs n 0 s) "/" (subs n (inc s)))
|
||||
n)))))
|
||||
|
||||
(defn to-beowulf
|
||||
"Return a beowulf-native representation of the Clojure object `o`.
|
||||
Numbers and symbols are unaffected. Collections have to be converted;
|
||||
strings must be converted to symbols."
|
||||
[o]
|
||||
(cond
|
||||
(coll? o) (make-beowulf-list o)
|
||||
(string? o) (symbol (s/upper-case o))
|
||||
:else o))
|
||||
|
||||
(defn to-clojure
|
||||
"If l is a `beowulf.cons_cell.ConsCell`, return a Clojure list having the
|
||||
same members in the same order."
|
||||
[l]
|
||||
(cond
|
||||
(not (instance? beowulf.cons_cell.ConsCell l))
|
||||
l
|
||||
(= (CDR l) NIL)
|
||||
(list (to-clojure (CAR l)))
|
||||
:else
|
||||
(conj (to-clojure (CDR l)) (to-clojure (CAR l)))))
|
||||
|
||||
(defn INTEROP
|
||||
"Clojure (or other host environment) interoperation API. `fn-symbol` is expected
|
||||
to be either
|
||||
|
||||
1. a symbol bound in the host environment to a function; or
|
||||
2. a sequence (list) of symbols forming a qualified path name bound to a
|
||||
function.
|
||||
|
||||
Lower case characters cannot normally be represented in Lisp 1.5, so both the
|
||||
upper case and lower case variants of `fn-symbol` will be tried. If the
|
||||
function you're looking for has a mixed case name, that is not currently
|
||||
accessible.
|
||||
|
||||
`args` is expected to be a Lisp 1.5 list of arguments to be passed to that
|
||||
function. Return value must be something acceptable to Lisp 1.5, so either
|
||||
a symbol, a number, or a Lisp 1.5 list.
|
||||
|
||||
If `fn-symbol` is not found (even when cast to lower case), or is not a function,
|
||||
or the value returned cannot be represented in Lisp 1.5, an exception is thrown
|
||||
with `:cause` bound to `:interop` and `:detail` set to a value representing the
|
||||
actual problem."
|
||||
[fn-symbol args]
|
||||
(if-not (:strict *options*)
|
||||
(let
|
||||
[q-name (if
|
||||
(seq? fn-symbol)
|
||||
(interpret-qualified-name fn-symbol)
|
||||
fn-symbol)
|
||||
l-name (symbol (s/lower-case q-name))
|
||||
f (cond
|
||||
(try
|
||||
(fn? (eval l-name))
|
||||
(catch java.lang.ClassNotFoundException _ nil)) l-name
|
||||
(try
|
||||
(fn? (eval q-name))
|
||||
(catch java.lang.ClassNotFoundException _ nil)) q-name
|
||||
:else (throw
|
||||
(ex-info
|
||||
(str "INTEROP: ungecnáwen þegnung `" fn-symbol "`")
|
||||
{:cause :interop
|
||||
:detail :not-found
|
||||
:name fn-symbol
|
||||
:also-tried l-name})))
|
||||
args' (to-clojure args)]
|
||||
;; (print (str "INTEROP: eahtiende `" (cons f args') "`"))
|
||||
(flush)
|
||||
(let [result (eval (conj args' f))] ;; this has the potential to blow up the world
|
||||
;; (println (str "; ágiefende `" result "`"))
|
||||
(cond
|
||||
(instance? beowulf.cons_cell.ConsCell result) result
|
||||
(coll? result) (make-beowulf-list result)
|
||||
(symbol? result) result
|
||||
(string? result) (symbol result)
|
||||
(number? result) result
|
||||
:else (throw
|
||||
(ex-info
|
||||
(str "INTEROP: Ne can eahtiende `" result "` to Lisp 1.5.")
|
||||
{:cause :interop
|
||||
:detail :not-representable
|
||||
:result result})))))
|
||||
(throw
|
||||
(ex-info
|
||||
(str "INTEROP ne āfand innan Lisp 1.5.")
|
||||
{:cause :interop
|
||||
:detail :strict}))))
|
|
@ -15,13 +15,39 @@
|
|||
oblist with data from that file.
|
||||
|
||||
Hence functions SYSOUT and SYSIN, which do just that."
|
||||
(:require [beowulf.cons-cell :refer [pretty-print]]
|
||||
[beowulf.oblist :refer [*options* oblist]]
|
||||
(:require [beowulf.cons-cell :refer [make-beowulf-list make-cons-cell
|
||||
pretty-print]]
|
||||
[beowulf.host :refer [CADR CAR CDDR CDR]]
|
||||
[beowulf.interop :refer [interpret-qualified-name
|
||||
listify-qualified-name]]
|
||||
[beowulf.oblist :refer [*options* NIL oblist]]
|
||||
[beowulf.read :refer [READ]]
|
||||
[clojure.java.io :refer [file resource]]
|
||||
[clojure.string :refer [ends-with?]]
|
||||
[java-time.api :refer [local-date local-date-time]]))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;
|
||||
;;; Copyright (C) 2022-2023 Simon Brooke
|
||||
;;;
|
||||
;;; This program is free software; you can redistribute it and/or
|
||||
;;; modify it under the terms of the GNU General Public License
|
||||
;;; as published by the Free Software Foundation; either version 2
|
||||
;;; of the License, or (at your option) any later version.
|
||||
;;;
|
||||
;;; This program is distributed in the hope that it will be useful,
|
||||
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;;; GNU General Public License for more details.
|
||||
;;;
|
||||
;;; You should have received a copy of the GNU General Public License
|
||||
;;; along with this program; if not, write to the Free Software
|
||||
;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(def ^:constant default-sysout "lisp1.5.lsp")
|
||||
|
||||
(defn- full-path
|
||||
[fp]
|
||||
(str
|
||||
|
@ -37,11 +63,31 @@
|
|||
""
|
||||
".lsp")))
|
||||
|
||||
;; (find-var (symbol "beowulf.io/SYSIN"))
|
||||
;; (@(resolve (symbol "beowulf.host/TIMES")) 2 2)
|
||||
|
||||
(defn safely-wrap-subr
|
||||
[entry]
|
||||
(cond (= entry NIL) NIL
|
||||
(= (CAR entry) 'SUBR) (make-cons-cell
|
||||
(CAR entry)
|
||||
(make-cons-cell
|
||||
(listify-qualified-name (CADR entry))
|
||||
(CDDR entry)))
|
||||
:else (make-cons-cell
|
||||
(CAR entry) (safely-wrap-subr (CDR entry)))))
|
||||
|
||||
(defn safely-wrap-subrs
|
||||
[objects]
|
||||
(make-beowulf-list (map safely-wrap-subr objects)))
|
||||
|
||||
(defn SYSOUT
|
||||
"Dump the current content of the object list to file. If no `filepath` is
|
||||
specified, a file name will be constructed of the symbol `Sysout` and
|
||||
the current date. File paths will be considered relative to the filepath
|
||||
set when starting Lisp."
|
||||
set when starting Lisp.
|
||||
|
||||
**NOTE THAT** this is an extension function, not available in strct mode."
|
||||
([]
|
||||
(SYSOUT nil))
|
||||
([filepath]
|
||||
|
@ -55,7 +101,41 @@
|
|||
(println (format ";; generated by %s" (System/getenv "USER"))))
|
||||
(println (apply str (repeat 79 ";")))
|
||||
(println)
|
||||
(pretty-print @oblist)))))
|
||||
(let [output (safely-wrap-subrs @oblist)]
|
||||
(pretty-print output)
|
||||
)))))
|
||||
|
||||
(defn resolve-subr
|
||||
"If this oblist `entry` references a subroutine, attempt to fix up that
|
||||
reference."
|
||||
([entry]
|
||||
(or (resolve-subr entry 'SUBR)
|
||||
(resolve-subr entry 'FSUBR)))
|
||||
([entry prop]
|
||||
(cond (= entry NIL) NIL
|
||||
(= (CAR entry) prop) (try
|
||||
(make-cons-cell
|
||||
(CAR entry)
|
||||
(make-cons-cell
|
||||
(interpret-qualified-name
|
||||
(CADR entry))
|
||||
(CDDR entry)))
|
||||
(catch Exception _
|
||||
(print "Warnung: ne can āfinde "
|
||||
(CADR entry))
|
||||
(CDDR entry)))
|
||||
:else (make-cons-cell
|
||||
(CAR entry) (resolve-subr (CDR entry))))))
|
||||
|
||||
|
||||
(defn- resolve-subroutines
|
||||
"Attempt to fix up the references to subroutines (Clojure functions) among
|
||||
these `objects`, being new content for the object list."
|
||||
[objects]
|
||||
(make-beowulf-list
|
||||
(map
|
||||
resolve-subr
|
||||
objects)))
|
||||
|
||||
(defn SYSIN
|
||||
"Read the contents of the file at this `filename` into the object list.
|
||||
|
@ -70,16 +150,22 @@
|
|||
|
||||
**NOTE THAT** if the provided `filename` does not end with `.lsp` (which,
|
||||
if you're writing it from the Lisp REPL, it won't), the extension `.lsp`
|
||||
will be appended."
|
||||
[filename]
|
||||
(let [fp (file (full-path (str filename)))
|
||||
file (when (and (.exists fp) (.canRead fp)) fp)
|
||||
res (try (resource filename)
|
||||
(catch Throwable _ nil))
|
||||
content (try (READ (slurp (or file res)))
|
||||
(catch Throwable any
|
||||
(throw (ex-info "Could not read from file"
|
||||
{:context "SYSIN"
|
||||
:filepath fp}
|
||||
any))))]
|
||||
(swap! oblist #(when (or % (seq content)) content))))
|
||||
will be appended.
|
||||
|
||||
**NOTE THAT** this is an extension function, not available in strct mode."
|
||||
([]
|
||||
(SYSIN (or (:read *options*) (str "resources/" default-sysout))))
|
||||
([filename]
|
||||
(let [fp (file (full-path (str filename)))
|
||||
file (when (and (.exists fp) (.canRead fp)) fp)
|
||||
res (try (resource filename)
|
||||
(catch Throwable _ nil))
|
||||
content (try (READ (slurp (or file res)))
|
||||
(catch Throwable _
|
||||
(throw (ex-info "Ne can ārǣde"
|
||||
{:context "SYSIN"
|
||||
:filename filename
|
||||
:filepath fp}))))]
|
||||
(swap! oblist
|
||||
#(when (or % (seq content))
|
||||
(resolve-subroutines content))))))
|
||||
|
|
769
src/beowulf/manual.clj
Normal file
769
src/beowulf/manual.clj
Normal file
|
@ -0,0 +1,769 @@
|
|||
(ns beowulf.manual
|
||||
"Experimental code for accessing the manual online."
|
||||
(:require [clojure.string :refer [ends-with? join trim]]))
|
||||
|
||||
(def ^:dynamic *manual-url*
|
||||
"https://www.softwarepreservation.org/projects/LISP/book/LISP%201.5%20Programmers%20Manual.pdf")
|
||||
|
||||
(def ^:constant index
|
||||
"This is data extracted from the index pages of `Lisp 1.5 Programmer's Manual`.
|
||||
It's here in the hope that we can automatically link to an online PDF link
|
||||
to the manual when the user invokes a function probably called `DOC` or `HELP`."
|
||||
{:RECIP
|
||||
{:fn-name "RECIP",
|
||||
:call-type "SUBR",
|
||||
:implementation "",
|
||||
:page-nos ["26" "64"]},
|
||||
:QUOTE
|
||||
{:fn-name "QUOTE",
|
||||
:call-type "FSUBR",
|
||||
:implementation "",
|
||||
:page-nos ["10" "22" "71"]},
|
||||
:RECLAIM
|
||||
{:fn-name "RECLAIM",
|
||||
:call-type "SUBR",
|
||||
:implementation "PSEUDO-FUNCTION ",
|
||||
:page-nos ["67"]},
|
||||
:NUMOB
|
||||
{:fn-name "NUMOB",
|
||||
:call-type "SUBR",
|
||||
:implementation "PSEUDO-FUNCTION ",
|
||||
:page-nos ["86"]},
|
||||
:EVLIS
|
||||
{:fn-name "EVLIS",
|
||||
:call-type "SUBR",
|
||||
:implementation "",
|
||||
:page-nos ["71"]},
|
||||
:DASH
|
||||
{:fn-name "DASH",
|
||||
:call-type "SUBR",
|
||||
:implementation "PREDICATE APVAL",
|
||||
:page-nos ["85" "87 "]},
|
||||
:EQUAL
|
||||
{:fn-name "EQUAL",
|
||||
:call-type "SUBR",
|
||||
:implementation "PREDICATE",
|
||||
:page-nos ["11" "26" "57"]},
|
||||
:PRIN1
|
||||
{:fn-name "PRIN1",
|
||||
:call-type "SUBR",
|
||||
:implementation "PSEUDO-FUNCTION ",
|
||||
:page-nos ["65" "84"]},
|
||||
:REMFLAG
|
||||
{:fn-name "REMFLAG",
|
||||
:call-type "SUBR",
|
||||
:implementation "PSEUDO-FUNCTION ",
|
||||
:page-nos ["41" "60"]},
|
||||
:DEFINE
|
||||
{:fn-name "DEFINE",
|
||||
:call-type "EXPR",
|
||||
:implementation "PSEUDO-FUNCTION",
|
||||
:page-nos ["15" "18" "58"]},
|
||||
:PUNCHLAP
|
||||
{:fn-name "PUNCHLAP",
|
||||
:call-type "EXPR",
|
||||
:implementation "PSEUDO-FUNCTION LIBRARY",
|
||||
:page-nos ["68" "76"]},
|
||||
:STARTREAD
|
||||
{:fn-name "STARTREAD",
|
||||
:call-type "SUBR",
|
||||
:implementation "PSEUDO-FUNCTION",
|
||||
:page-nos ["87"]},
|
||||
:PERIOD
|
||||
{:fn-name "PERIOD",
|
||||
:call-type "APVAL",
|
||||
:implementation "",
|
||||
:page-nos ["69" "85"]},
|
||||
:CP1
|
||||
{:fn-name "CP1",
|
||||
:call-type "SUBR",
|
||||
:implementation "",
|
||||
:page-nos ["66"]},
|
||||
:NCONC
|
||||
{:fn-name "NCONC",
|
||||
:call-type "SUBR",
|
||||
:implementation "PSEUDO-FUNCTION ",
|
||||
:page-nos ["62"]},
|
||||
:EQ
|
||||
{:fn-name "EQ",
|
||||
:call-type "SUBR",
|
||||
:implementation "PREDICATE",
|
||||
:page-nos ["3" "23" "57"]},
|
||||
:RPLACD
|
||||
{:fn-name "RPLACD",
|
||||
:call-type "SUBR",
|
||||
:implementation "PSEUDO-FUNCTION",
|
||||
:page-nos ["41" "58"]},
|
||||
:PROG2
|
||||
{:fn-name "PROG2",
|
||||
:call-type "SUBR",
|
||||
:implementation "",
|
||||
:page-nos ["42" "66"]},
|
||||
:UNCOUNT
|
||||
{:fn-name "UNCOUNT",
|
||||
:call-type "SUBR",
|
||||
:implementation "PSEUDO-FUNCTION",
|
||||
:page-nos ["34" "66"]},
|
||||
:ERROR1
|
||||
{:fn-name "ERROR1",
|
||||
:call-type "SUBR",
|
||||
:implementation "PSEUDO-FUNCTION",
|
||||
:page-nos ["88"]},
|
||||
:EXPT
|
||||
{:fn-name "EXPT",
|
||||
:call-type "SUBR",
|
||||
:implementation "",
|
||||
:page-nos ["26" "64"]},
|
||||
:NOT
|
||||
{:fn-name "NOT",
|
||||
:call-type "SUBR",
|
||||
:implementation "PREDICATE",
|
||||
:page-nos ["21" "23" "58"]},
|
||||
:SLASH
|
||||
{:fn-name "SLASH",
|
||||
:call-type "APVAL",
|
||||
:implementation "",
|
||||
:page-nos ["69" "85"]},
|
||||
:RPLACA
|
||||
{:fn-name "RPLACA",
|
||||
:call-type "SUBR",
|
||||
:implementation "PSEUDO-FUNCTION",
|
||||
:page-nos ["41" "58"]},
|
||||
:QUOTIENT
|
||||
{:fn-name "QUOTIENT",
|
||||
:call-type "SUBR",
|
||||
:implementation "",
|
||||
:page-nos ["26" "64"]},
|
||||
:UNPACK
|
||||
{:fn-name "UNPACK",
|
||||
:call-type "SUBR",
|
||||
:implementation "PSEUDO-FUNCTION",
|
||||
:page-nos ["87"]},
|
||||
:CONC
|
||||
{:fn-name "CONC",
|
||||
:call-type "FEXPR",
|
||||
:implementation "",
|
||||
:page-nos ["61"]},
|
||||
:CAR
|
||||
{:fn-name "CAR",
|
||||
:call-type "SUBR",
|
||||
:implementation "",
|
||||
:page-nos ["2" "56"]},
|
||||
:GENSYM
|
||||
{:fn-name "GENSYM",
|
||||
:call-type "SUBR",
|
||||
:implementation "",
|
||||
:page-nos ["66"]},
|
||||
:PROP
|
||||
{:fn-name "PROP",
|
||||
:call-type "SUBR",
|
||||
:implementation "FUNCTIONAL ",
|
||||
:page-nos [" 59"]},
|
||||
:MEMBER
|
||||
{:fn-name "MEMBER",
|
||||
:call-type "SUBR",
|
||||
:implementation "PREDICATE ",
|
||||
:page-nos ["11" "62"]},
|
||||
:UNTRACESET
|
||||
{:fn-name "UNTRACESET",
|
||||
:call-type "EXPR",
|
||||
:implementation "PSEUDO-FUNCTION",
|
||||
:page-nos ["68"]},
|
||||
:UNTRACE
|
||||
{:fn-name "UNTRACE",
|
||||
:call-type "EXPR",
|
||||
:implementation "PSEUDO-FUNCTION",
|
||||
:page-nos ["32" "66"]},
|
||||
:MINUSP
|
||||
{:fn-name "MINUSP",
|
||||
:call-type "SUBR",
|
||||
:implementation "PREDICATE ",
|
||||
:page-nos ["26" "64"]},
|
||||
:F
|
||||
{:fn-name "F",
|
||||
:call-type "APVAL",
|
||||
:implementation "",
|
||||
:page-nos ["22" "69"]},
|
||||
:SPECIAL
|
||||
{:fn-name "SPECIAL",
|
||||
:call-type "SUBR",
|
||||
:implementation "PSEUDO-FUNCTION",
|
||||
:page-nos ["64" "78"]},
|
||||
:LPAR
|
||||
{:fn-name "LPAR",
|
||||
:call-type "APVAL",
|
||||
:implementation "",
|
||||
:page-nos ["69" "85"]},
|
||||
:GO
|
||||
{:fn-name "GO",
|
||||
:call-type "FSUBR",
|
||||
:implementation "PSEUDO-FUNCTION",
|
||||
:page-nos ["30" "72"]},
|
||||
:MKNAM
|
||||
{:fn-name "MKNAM",
|
||||
:call-type "SUBR",
|
||||
:implementation "",
|
||||
:page-nos ["86"]},
|
||||
:COMMON
|
||||
{:fn-name "COMMON",
|
||||
:call-type "SUBR",
|
||||
:implementation "PSEUDO-FUNCTION",
|
||||
:page-nos ["64" "78"]},
|
||||
:NUMBERP
|
||||
{:fn-name "NUMBERP",
|
||||
:call-type "SUBR",
|
||||
:implementation "PREDICATE ",
|
||||
:page-nos ["26" "64"]},
|
||||
:CONS
|
||||
{:fn-name "CONS",
|
||||
:call-type "SUBR",
|
||||
:implementation "",
|
||||
:page-nos ["2" "56"]},
|
||||
:PLUS
|
||||
{:fn-name "PLUS",
|
||||
:call-type "FSUBR",
|
||||
:implementation "",
|
||||
:page-nos ["25" "63"]},
|
||||
:SET
|
||||
{:fn-name "SET",
|
||||
:call-type "SUBR",
|
||||
:implementation "PSEUDO-FUNCTION",
|
||||
:page-nos ["30" "71"]},
|
||||
:DOLLAR
|
||||
{:fn-name "DOLLAR",
|
||||
:call-type "APVAL",
|
||||
:implementation "",
|
||||
:page-nos ["69" "85"]},
|
||||
:SASSOC
|
||||
{:fn-name "SASSOC",
|
||||
:call-type "SUBR",
|
||||
:implementation "FUNCTIONAL",
|
||||
:page-nos ["60"]},
|
||||
:SELECT
|
||||
{:fn-name "SELECT",
|
||||
:call-type "FEXPR",
|
||||
:implementation "",
|
||||
:page-nos ["66"]},
|
||||
:OPDEFINE
|
||||
{:fn-name "OPDEFINE",
|
||||
:call-type "EXPR",
|
||||
:implementation "PSEUDO-FUNCTION ",
|
||||
:page-nos ["65" "75"]},
|
||||
:PAUSE
|
||||
{:fn-name "PAUSE",
|
||||
:call-type "SUBR",
|
||||
:implementation "PSEUDO-FUNCTION",
|
||||
:page-nos ["67"]},
|
||||
:AND
|
||||
{:fn-name "AND",
|
||||
:call-type "FSUBR",
|
||||
:implementation "PREDICATE",
|
||||
:page-nos ["21" "58"]},
|
||||
:COMMA
|
||||
{:fn-name "COMMA",
|
||||
:call-type "APVAL",
|
||||
:implementation "",
|
||||
:page-nos ["69" "85"]},
|
||||
:EFFACE
|
||||
{:fn-name "EFFACE",
|
||||
:call-type "SUBR",
|
||||
:implementation "PSEUDO-FUNCTION",
|
||||
:page-nos ["63"]},
|
||||
:CSETQ
|
||||
{:fn-name "CSETQ",
|
||||
:call-type "FEXPR",
|
||||
:implementation "PSEUDO-FUNCTION",
|
||||
:page-nos ["59"]},
|
||||
:OPCHAR
|
||||
{:fn-name "OPCHAR",
|
||||
:call-type "SUBR",
|
||||
:implementation "PREDICATE ",
|
||||
:page-nos [" 87"]},
|
||||
:PRINTPROP
|
||||
{:fn-name "PRINTPROP",
|
||||
:call-type "EXPR",
|
||||
:implementation "PSEUDO-FUNCTION LIBRARY ",
|
||||
:page-nos ["68"]},
|
||||
:PLB
|
||||
{:fn-name "PLB",
|
||||
:call-type "SUBR",
|
||||
:implementation "PSEUDO- FUNCTION",
|
||||
:page-nos ["67"]},
|
||||
:DIGIT
|
||||
{:fn-name "DIGIT",
|
||||
:call-type "SUBR",
|
||||
:implementation "PREDICATE ",
|
||||
:page-nos ["87"]},
|
||||
:PUNCHDEF
|
||||
{:fn-name "PUNCHDEF",
|
||||
:call-type "EXPR",
|
||||
:implementation "PSEUDO-FUNCTION LIBRARY",
|
||||
:page-nos ["68"]},
|
||||
:ARRAY
|
||||
{:fn-name "ARRAY",
|
||||
:call-type "SUBR",
|
||||
:implementation "PSEUDO-FUNCTION",
|
||||
:page-nos ["27" "64"]},
|
||||
:MAX
|
||||
{:fn-name "MAX",
|
||||
:call-type "FSUBR",
|
||||
:implementation "",
|
||||
:page-nos ["26" "64"]},
|
||||
:INTERN
|
||||
{:fn-name "INTERN",
|
||||
:call-type "SUBR",
|
||||
:implementation "PSEUDO-FUNCTION",
|
||||
:page-nos ["67" "87"]},
|
||||
:NIL
|
||||
{:fn-name "NIL",
|
||||
:call-type "APVAL",
|
||||
:implementation "",
|
||||
:page-nos ["22" "69"]},
|
||||
:TIMES
|
||||
{:fn-name "TIMES",
|
||||
:call-type "FSUBR",
|
||||
:implementation "",
|
||||
:page-nos ["26" "64"]},
|
||||
:ERROR
|
||||
{:fn-name "ERROR",
|
||||
:call-type "SUBR",
|
||||
:implementation "PSEUDO-FUNCTION",
|
||||
:page-nos ["32" "66"]},
|
||||
:PUNCH
|
||||
{:fn-name "PUNCH",
|
||||
:call-type "SUBR",
|
||||
:implementation "PSEUDO-FUNCTION",
|
||||
:page-nos ["65" "84"]},
|
||||
:REMPROP
|
||||
{:fn-name "REMPROP",
|
||||
:call-type "SUBR",
|
||||
:implementation "PSEUDO-FUNCTION",
|
||||
:page-nos ["41" "59"]},
|
||||
:DIVIDE
|
||||
{:fn-name "DIVIDE",
|
||||
:call-type "SUBR",
|
||||
:implementation "",
|
||||
:page-nos ["26" "64"]},
|
||||
:OR
|
||||
{:fn-name "OR",
|
||||
:call-type "FSUBR",
|
||||
:implementation "PREDICATE ",
|
||||
:page-nos ["21" "58"]},
|
||||
:SUBLIS
|
||||
{:fn-name "SUBLIS",
|
||||
:call-type "SUBR",
|
||||
:implementation "",
|
||||
:page-nos ["12" "61"]},
|
||||
:LAP
|
||||
{:fn-name "LAP",
|
||||
:call-type "SUBR",
|
||||
:implementation "PSEUDO-FUNCTION ",
|
||||
:page-nos ["65" "73"]},
|
||||
:PROG
|
||||
{:fn-name "PROG",
|
||||
:call-type "FSUBR",
|
||||
:implementation "",
|
||||
:page-nos ["29" "71"]},
|
||||
:T
|
||||
{:fn-name "T",
|
||||
:call-type "APVAL",
|
||||
:implementation "",
|
||||
:page-nos ["22" "69"]},
|
||||
:GREATERP
|
||||
{:fn-name "GREATERP",
|
||||
:call-type "SUBR",
|
||||
:implementation "PREDICATE",
|
||||
:page-nos ["26" "64"]},
|
||||
:CSET
|
||||
{:fn-name "CSET",
|
||||
:call-type "EXPR",
|
||||
:implementation "PSEUDO-FUNCTION",
|
||||
:page-nos ["17" "59"]},
|
||||
:FUNCTION
|
||||
{:fn-name "FUNCTION",
|
||||
:call-type "FSUBR",
|
||||
:implementation "",
|
||||
:page-nos ["21" "71"]},
|
||||
:LENGTH
|
||||
{:fn-name "LENGTH",
|
||||
:call-type "SUBR",
|
||||
:implementation "",
|
||||
:page-nos ["62"]},
|
||||
:MINUS
|
||||
{:fn-name "MINUS",
|
||||
:call-type "SUBR",
|
||||
:implementation "",
|
||||
:page-nos ["26" "63"]},
|
||||
:COND
|
||||
{:fn-name "COND",
|
||||
:call-type "FSUBR",
|
||||
:implementation "",
|
||||
:page-nos ["18"]},
|
||||
:APPEND
|
||||
{:fn-name "APPEND",
|
||||
:call-type "SUBR",
|
||||
:implementation "",
|
||||
:page-nos ["11" "61"]},
|
||||
:CDR
|
||||
{:fn-name "CDR",
|
||||
:call-type "SUBR",
|
||||
:implementation "",
|
||||
:page-nos ["3" "56"]},
|
||||
:OBLIST
|
||||
{:fn-name "OBLIST",
|
||||
:call-type "APVAL",
|
||||
:implementation "",
|
||||
:page-nos ["69"]},
|
||||
:READ
|
||||
{:fn-name "READ",
|
||||
:call-type "SUBR",
|
||||
:implementation "PSEUDO-FUNCTION ",
|
||||
:page-nos ["5" "84"]},
|
||||
:ERRORSET
|
||||
{:fn-name "ERRORSET",
|
||||
:call-type "SUBR",
|
||||
:implementation "PSEUDO-FUNCTION",
|
||||
:page-nos ["35" "66"]},
|
||||
:UNCOMMON
|
||||
{:fn-name "UNCOMMON",
|
||||
:call-type "SUBR",
|
||||
:implementation "PSEUDO-FUNCTION ",
|
||||
:page-nos ["64" "78"]},
|
||||
:EVAL
|
||||
{:fn-name "EVAL",
|
||||
:call-type "SUBR",
|
||||
:implementation "",
|
||||
:page-nos ["71"]},
|
||||
:MIN
|
||||
{:fn-name "MIN",
|
||||
:call-type "FSUBR",
|
||||
:implementation "",
|
||||
:page-nos ["26" "64"]},
|
||||
:PAIR
|
||||
{:fn-name "PAIR",
|
||||
:call-type "SUBR",
|
||||
:implementation "",
|
||||
:page-nos ["60"]},
|
||||
:BLANK
|
||||
{:fn-name "BLANK",
|
||||
:call-type "APVAL",
|
||||
:implementation "",
|
||||
:page-nos ["69" "85"]},
|
||||
:SETQ
|
||||
{:fn-name "SETQ",
|
||||
:call-type "FSUBR",
|
||||
:implementation "PSEUDO-FUNCTION",
|
||||
:page-nos ["30" "71"]},
|
||||
:GET
|
||||
{:fn-name "GET",
|
||||
:call-type "SUBR",
|
||||
:implementation "",
|
||||
:page-nos ["41" "59"]},
|
||||
:PRINT
|
||||
{:fn-name "PRINT",
|
||||
:call-type "SUBR",
|
||||
:implementation "PSEUDO-FUNCTION ",
|
||||
:page-nos ["65" "84"]},
|
||||
:ENDREAD
|
||||
{:fn-name "ENDREAD",
|
||||
:call-type "SUBR",
|
||||
:implementation "PSEUDO-FUNCTION",
|
||||
:page-nos ["8 8"]},
|
||||
:RETURN
|
||||
{:fn-name "RETURN",
|
||||
:call-type "SUBR",
|
||||
:implementation "PSEUDO-FUNCTION",
|
||||
:page-nos ["30" "72"]},
|
||||
:LITER
|
||||
{:fn-name "LITER",
|
||||
:call-type "SUBR",
|
||||
:implementation "PREDICATE ",
|
||||
:page-nos ["87"]},
|
||||
:EOF
|
||||
{:fn-name "EOF",
|
||||
:call-type "APVAL",
|
||||
:implementation "",
|
||||
:page-nos ["69" "88"]},
|
||||
:TRACE
|
||||
{:fn-name "TRACE",
|
||||
:call-type "EXPR",
|
||||
:implementation "PSEUDO-FUNCTION",
|
||||
:page-nos ["32" "66" "79"]},
|
||||
:TRACESET
|
||||
{:fn-name "TRACESET",
|
||||
:call-type "EXPR",
|
||||
:implementation "PSEUDO-FUNCTION LIBRARY",
|
||||
:page-nos ["68"]},
|
||||
:PACK
|
||||
{:fn-name "PACK",
|
||||
:call-type "SUBR",
|
||||
:implementation "PSEUDO-FUNCTION ",
|
||||
:page-nos ["86"]},
|
||||
:NULL
|
||||
{:fn-name "NULL",
|
||||
:call-type "SUBR",
|
||||
:implementation "PREDICATE ",
|
||||
:page-nos ["11" "57"]},
|
||||
:CLEARBUFF
|
||||
{:fn-name "CLEARBUFF",
|
||||
:call-type "SUBR",
|
||||
:implementation "PSEUDO-FUNCTION",
|
||||
:page-nos ["86"]},
|
||||
:LESSP
|
||||
{:fn-name "LESSP",
|
||||
:call-type "SUBR",
|
||||
:implementation "PREDICATE ",
|
||||
:page-nos ["26" "64"]},
|
||||
:TERPRI
|
||||
{:fn-name "TERPRI",
|
||||
:call-type "SUBR",
|
||||
:implementation "PSEUDO-FUNCTION",
|
||||
:page-nos ["65" "84"]},
|
||||
:ONEP
|
||||
{:fn-name "ONEP",
|
||||
:call-type "SUBR",
|
||||
:implementation "PREDICATE ",
|
||||
:page-nos [" 26" "64"]},
|
||||
:EXCISE
|
||||
{:fn-name "EXCISE",
|
||||
:call-type "SUBR",
|
||||
:implementation "PSEUDO-FUNCTION",
|
||||
:page-nos ["67" "77"]},
|
||||
:REMOB
|
||||
{:fn-name "REMOB",
|
||||
:call-type "SUBR",
|
||||
:implementation "PSEUDO-FUNCTION ",
|
||||
:page-nos ["67"]},
|
||||
:MAP
|
||||
{:fn-name "MAP",
|
||||
:call-type "SUBR",
|
||||
:implementation "FUNCTIONAL ",
|
||||
:page-nos ["63"]},
|
||||
:COMPILE
|
||||
{:fn-name "COMPILE",
|
||||
:call-type "SUBR",
|
||||
:implementation "PSEUDO-FUNCTION",
|
||||
:page-nos ["64" "76"]},
|
||||
:ADD1
|
||||
{:fn-name "ADD1",
|
||||
:call-type "SUBR",
|
||||
:implementation "",
|
||||
:page-nos ["26" "64"]},
|
||||
:ADVANCE
|
||||
{:fn-name "ADVANCE",
|
||||
:call-type "SUBR",
|
||||
:implementation "PSEUDO-FUNCTION",
|
||||
:page-nos ["88"]},
|
||||
:SEARCH
|
||||
{:fn-name "SEARCH",
|
||||
:call-type "SUBR",
|
||||
:implementation "FUNCTIONAL",
|
||||
:page-nos ["63"]},
|
||||
:APPLY
|
||||
{:fn-name "APPLY",
|
||||
:call-type "SUBR",
|
||||
:implementation "",
|
||||
:page-nos ["70"]},
|
||||
:READLAP
|
||||
{:fn-name "READLAP",
|
||||
:call-type "SUBR",
|
||||
:implementation "PSEUDO-FUNCTION ",
|
||||
:page-nos ["65" "76"]},
|
||||
:UNSPECIAL
|
||||
{:fn-name "UNSPECIAL",
|
||||
:call-type "SUBR",
|
||||
:implementation "",
|
||||
:page-nos ["64" "78"]},
|
||||
:SUBST
|
||||
{:fn-name "SUBST",
|
||||
:call-type "SUBR",
|
||||
:implementation "",
|
||||
:page-nos ["11" "61"]},
|
||||
:COPY
|
||||
{:fn-name "COPY",
|
||||
:call-type "SUBR",
|
||||
:implementation "",
|
||||
:page-nos ["62"]},
|
||||
:LOGOR
|
||||
{:fn-name "LOGOR",
|
||||
:call-type "FSUBR",
|
||||
:implementation "",
|
||||
:page-nos ["26" "64"]},
|
||||
:LABEL
|
||||
{:fn-name "LABEL",
|
||||
:call-type "FSUBR",
|
||||
:implementation "",
|
||||
:page-nos ["8" "18" "70"]},
|
||||
:FIXP
|
||||
{:fn-name "FIXP",
|
||||
:call-type "SUBR",
|
||||
:implementation "PREDICATE",
|
||||
:page-nos ["26" "64"]},
|
||||
:SUB1
|
||||
{:fn-name "SUB1",
|
||||
:call-type "SUBR",
|
||||
:implementation "",
|
||||
:page-nos ["26" "64"]},
|
||||
:ATTRIB
|
||||
{:fn-name "ATTRIB",
|
||||
:call-type "SUBR",
|
||||
:implementation "PSEUDO-FUNCTION",
|
||||
:page-nos ["59"]},
|
||||
:DIFFERENCE
|
||||
{:fn-name "DIFFERENCE",
|
||||
:call-type "SUBR",
|
||||
:implementation "",
|
||||
:page-nos ["26" "64"]},
|
||||
:REMAINDER
|
||||
{:fn-name "REMAINDER",
|
||||
:call-type "SUBR",
|
||||
:implementation "",
|
||||
:page-nos ["26" "64"]},
|
||||
:REVERSE
|
||||
{:fn-name "REVERSE",
|
||||
:call-type "SUBR",
|
||||
:implementation "",
|
||||
:page-nos ["6 2"]},
|
||||
:EOR
|
||||
{:fn-name "EOR",
|
||||
:call-type "APVAL",
|
||||
:implementation "",
|
||||
:page-nos ["69" "88"]},
|
||||
:PLUSS
|
||||
{:fn-name "PLUSS",
|
||||
:call-type "APVAL",
|
||||
:implementation "",
|
||||
:page-nos ["69" "85"]},
|
||||
:TEMPUS-FUGIT
|
||||
{:fn-name "TEMPUS-FUGIT",
|
||||
:call-type "SUBR",
|
||||
:implementation "PSEUDO-FUNCTION",
|
||||
:page-nos ["67"]},
|
||||
:LOAD
|
||||
{:fn-name "LOAD",
|
||||
:call-type "SUBR",
|
||||
:implementation "PSEUDO-FUNCTION",
|
||||
:page-nos ["67"]},
|
||||
:CHARCOUNT
|
||||
{:fn-name "CHARCOUNT",
|
||||
:call-type "APVAL",
|
||||
:implementation "",
|
||||
:page-nos ["69" "87"]},
|
||||
:RPAR
|
||||
{:fn-name "RPAR",
|
||||
:call-type "APVAL",
|
||||
:implementation "",
|
||||
:page-nos ["69" "85"]},
|
||||
:COUNT
|
||||
{:fn-name "COUNT",
|
||||
:call-type "SUBR",
|
||||
:implementation "PSEUDO-FUNCTION",
|
||||
:page-nos ["34" "66"]},
|
||||
:SPEAK
|
||||
{:fn-name "SPEAK",
|
||||
:call-type "SUBR",
|
||||
:implementation "PSEUDO-FUNCTION",
|
||||
:page-nos ["34" "66 "]},
|
||||
:LOGXOR
|
||||
{:fn-name "LOGXOR",
|
||||
:call-type "FSUBR",
|
||||
:implementation "",
|
||||
:page-nos ["27" "64"]},
|
||||
:FLOATP
|
||||
{:fn-name "FLOATP",
|
||||
:call-type "SUBR",
|
||||
:implementation "PREDICATE",
|
||||
:page-nos ["26" "64"]},
|
||||
:ATOM
|
||||
{:fn-name "ATOM",
|
||||
:call-type "SUBR",
|
||||
:implementation "PREDICATE",
|
||||
:page-nos ["3" "57"]},
|
||||
:EQSIGN
|
||||
{:fn-name "EQSIGN",
|
||||
:call-type "APVAL",
|
||||
:implementation "",
|
||||
:page-nos ["69" "85"]},
|
||||
:LIST
|
||||
{:fn-name "LIST",
|
||||
:call-type "FSUBR",
|
||||
:implementation "",
|
||||
:page-nos ["57"]},
|
||||
:MAPLIST
|
||||
{:fn-name "MAPLIST",
|
||||
:call-type "SUBR",
|
||||
:implementation "FUNCTIONAL ",
|
||||
:page-nos ["20" "21" "63"]},
|
||||
:LOGAND
|
||||
{:fn-name "LOGAND",
|
||||
:call-type "FSUBR",
|
||||
:implementation "",
|
||||
:page-nos ["27" "64"]},
|
||||
:FLAG
|
||||
{:fn-name "FLAG",
|
||||
:call-type "EXPR",
|
||||
:implementation "PSEUDO-FUNCTION",
|
||||
:page-nos ["41" "60"]},
|
||||
:MAPCON
|
||||
{:fn-name "MAPCON",
|
||||
:call-type "SUBR",
|
||||
:implementation "FUNCTIONAL PSEUDO- FUNCTION",
|
||||
:page-nos ["63"]},
|
||||
:STAR
|
||||
{:fn-name "STAR",
|
||||
:call-type "APVAL",
|
||||
:implementation "",
|
||||
:page-nos ["69" "85"]},
|
||||
:CURCHAR
|
||||
{:fn-name "CURCHAR",
|
||||
:call-type "APVAL",
|
||||
:implementation "",
|
||||
:page-nos ["69" "87"]},
|
||||
:DUMP
|
||||
{:fn-name "DUMP",
|
||||
:call-type "SUBR",
|
||||
:implementation "PSEUDO-FUNCTION",
|
||||
:page-nos ["67"]},
|
||||
:DEFLIST
|
||||
{:fn-name "DEFLIST",
|
||||
:call-type "EXPR",
|
||||
:implementation "PSEUDO-FUNCTION",
|
||||
:page-nos ["41" "58"]},
|
||||
:LEFTSHIFT
|
||||
{:fn-name "LEFTSHIFT",
|
||||
:call-type "SUBR",
|
||||
:implementation "",
|
||||
:page-nos ["27" "64"]},
|
||||
:ZEROP
|
||||
{:fn-name "ZEROP",
|
||||
:call-type "SUBR",
|
||||
:implementation "PREDICATE",
|
||||
:page-nos ["26" "64"]}})
|
||||
|
||||
(defn page-url
|
||||
"Format the URL for the page in the manual with this `page-no`."
|
||||
[page-no]
|
||||
(let [n (read-string page-no)
|
||||
n' (when (and (number? n)
|
||||
(ends-with? *manual-url* ".pdf"))
|
||||
;; annoyingly, the manual has eight pages of front-matter
|
||||
;; before numbering starts.
|
||||
(+ n 8))]
|
||||
(format
|
||||
(if (ends-with? *manual-url* ".pdf") "%s#page=%s" "%s#page%s")
|
||||
*manual-url*
|
||||
(or n' (trim (str page-no))))))
|
||||
|
||||
(defn format-page-references
|
||||
"Format page references from the manual index for the function whose name
|
||||
is `fn-symbol`."
|
||||
[fn-symbol]
|
||||
(let [k (if (keyword? fn-symbol) fn-symbol (keyword fn-symbol))]
|
||||
(join ", "
|
||||
(doall
|
||||
(map
|
||||
(fn [n]
|
||||
(let [p (trim n)]
|
||||
(format "<a href='%s'>%s</a>"
|
||||
(page-url p) p)))
|
||||
(:page-nos (index k)))))))
|
|
@ -1,12 +1,38 @@
|
|||
(ns beowulf.oblist
|
||||
"A namespace mainly devoted to the object list.
|
||||
"A namespace mainly devoted to the object list and other top level
|
||||
global variables.
|
||||
|
||||
Yes, this makes little sense, but if you put it anywhere else you end
|
||||
Yes, this makes little sense, but if you put them anywhere else you end
|
||||
up in cyclic dependency hell."
|
||||
)
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;
|
||||
;;; Copyright (C) 2022-2023 Simon Brooke
|
||||
;;;
|
||||
;;; This program is free software; you can redistribute it and/or
|
||||
;;; modify it under the terms of the GNU General Public License
|
||||
;;; as published by the Free Software Foundation; either version 2
|
||||
;;; of the License, or (at your option) any later version.
|
||||
;;;
|
||||
;;; This program is distributed in the hope that it will be useful,
|
||||
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;;; GNU General Public License for more details.
|
||||
;;;
|
||||
;;; You should have received a copy of the GNU General Public License
|
||||
;;; along with this program; if not, write to the Free Software
|
||||
;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(def NIL
|
||||
"The canonical empty list symbol."
|
||||
"The canonical empty list symbol.
|
||||
|
||||
TODO: this doesn't really work, because (from Clojure) `(empty? NIL)` throws
|
||||
an exception. It might be better to subclass beowulf.cons_cell.ConsCell to create
|
||||
a new singleton class Nil which overrides the `empty` method of
|
||||
IPersistentCollection?"
|
||||
'NIL)
|
||||
|
||||
(def oblist
|
||||
|
|
|
@ -13,10 +13,10 @@
|
|||
|
||||
Both these extensions can be disabled by using the `--strict` command line
|
||||
switch."
|
||||
(:require [beowulf.reader.char-reader :refer [read-chars]]
|
||||
(:require ;; [beowulf.reader.char-reader :refer [read-chars]]
|
||||
[beowulf.reader.generate :refer [generate]]
|
||||
[beowulf.reader.parser :refer [parse]]
|
||||
[beowulf.reader.simplify :refer [remove-optional-space simplify]]
|
||||
[beowulf.reader.simplify :refer [simplify]]
|
||||
[clojure.string :refer [join split starts-with? trim]])
|
||||
(:import [java.io InputStream]
|
||||
[instaparse.gll Failure]))
|
||||
|
@ -28,6 +28,24 @@
|
|||
;;; the real Lisp reader.
|
||||
;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;
|
||||
;;; Copyright (C) 2022-2023 Simon Brooke
|
||||
;;;
|
||||
;;; This program is free software; you can redistribute it and/or
|
||||
;;; modify it under the terms of the GNU General Public License
|
||||
;;; as published by the Free Software Foundation; either version 2
|
||||
;;; of the License, or (at your option) any later version.
|
||||
;;;
|
||||
;;; This program is distributed in the hope that it will be useful,
|
||||
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;;; GNU General Public License for more details.
|
||||
;;;
|
||||
;;; You should have received a copy of the GNU General Public License
|
||||
;;; along with this program; if not, write to the Free Software
|
||||
;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defn strip-line-comments
|
||||
"Strip blank lines and comment lines from this string `s`, expected to
|
||||
|
@ -61,16 +79,18 @@
|
|||
parse-tree (parse source)]
|
||||
(if (instance? Failure parse-tree)
|
||||
(doall (println (number-lines source parse-tree))
|
||||
(throw (ex-info "Parse failed" (assoc parse-tree :source source))))
|
||||
(generate (simplify (remove-optional-space parse-tree))))))
|
||||
(throw (ex-info "Ne can forstande " (assoc parse-tree :source source))))
|
||||
(generate (simplify parse-tree)))))
|
||||
|
||||
(defn read-from-console
|
||||
"Attempt to read a complete lisp expression from the console. NOTE that this
|
||||
will only really work for S-Expressions, not M-Expressions."
|
||||
[]
|
||||
(loop [r (read-line)]
|
||||
(if (= (count (re-seq #"\(" r))
|
||||
(if (and (= (count (re-seq #"\(" r))
|
||||
(count (re-seq #"\)" r)))
|
||||
(= (count (re-seq #"\[" r))
|
||||
(count (re-seq #"\]" r))))
|
||||
r
|
||||
(recur (str r "\n" (read-line))))))
|
||||
|
||||
|
@ -79,7 +99,7 @@
|
|||
the final Lisp reader. `input` should be either a string representation of a LISP
|
||||
expression, or else an input stream. A single form will be read."
|
||||
([]
|
||||
(gsp (read-chars)))
|
||||
(gsp (read-from-console)))
|
||||
([input]
|
||||
(cond
|
||||
(empty? input) (READ)
|
||||
|
|
|
@ -15,36 +15,61 @@
|
|||
rather than the strings which were supplied to `READ`);
|
||||
4. <Tab> offers potential auto-completions taken from the value of `(OBLIST)`, ideally the
|
||||
current value, not the value at the time the session started;
|
||||
5. <Back-arrow> and <Forward-arrow> offer movement and editing within the line."
|
||||
(:import [org.jline.reader LineReader LineReaderBuilder]
|
||||
[org.jline.terminal TerminalBuilder]))
|
||||
5. <Back-arrow> and <Forward-arrow> offer movement and editing within the line.
|
||||
|
||||
TODO: There are multiple problems with JLine; a better solution might be
|
||||
to start from here:
|
||||
https://stackoverflow.com/questions/7931988/how-to-manipulate-control-characters"
|
||||
;; (:import [org.jline.reader LineReader LineReaderBuilder]
|
||||
;; [org.jline.terminal TerminalBuilder])
|
||||
)
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;
|
||||
;;; Copyright (C) 2022-2023 Simon Brooke
|
||||
;;;
|
||||
;;; This program is free software; you can redistribute it and/or
|
||||
;;; modify it under the terms of the GNU General Public License
|
||||
;;; as published by the Free Software Foundation; either version 2
|
||||
;;; of the License, or (at your option) any later version.
|
||||
;;;
|
||||
;;; This program is distributed in the hope that it will be useful,
|
||||
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;;; GNU General Public License for more details.
|
||||
;;;
|
||||
;;; You should have received a copy of the GNU General Public License
|
||||
;;; along with this program; if not, write to the Free Software
|
||||
;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
;; It looks from the example given [here](https://github.com/jline/jline3/blob/master/demo/src/main/java/org/jline/demo/Repl.java)
|
||||
;; as though JLine could be used to build a perfect line-reader for Beowulf; but it also
|
||||
;; looks as though you'd need a DPhil in JLine to write it, and I don't have
|
||||
;; the time.
|
||||
|
||||
(def get-reader
|
||||
"Return a reader, first constructing it if necessary.
|
||||
;; (def get-reader
|
||||
;; "Return a reader, first constructing it if necessary.
|
||||
|
||||
**NOTE THAT** this is not settled API. The existence and call signature of
|
||||
this function is not guaranteed in future versions."
|
||||
(memoize (fn []
|
||||
(let [term (.build (.system (TerminalBuilder/builder) true))]
|
||||
(.build (.terminal (LineReaderBuilder/builder) term))))))
|
||||
;; **NOTE THAT** this is not settled API. The existence and call signature of
|
||||
;; this function is not guaranteed in future versions."
|
||||
;; (memoize (fn []
|
||||
;; (let [term (.build (.system (TerminalBuilder/builder) true))]
|
||||
;; (.build (.terminal (LineReaderBuilder/builder) term))))))
|
||||
|
||||
(defn read-chars
|
||||
"A drop-in replacement for `clojure.core/read-line`, except that line editing
|
||||
and history should be enabled.
|
||||
;; (defn read-chars
|
||||
;; "A drop-in replacement for `clojure.core/read-line`, except that line editing
|
||||
;; and history should be enabled.
|
||||
|
||||
**NOTE THAT** this does not work yet, but it is in the API because I hope
|
||||
that it will work later!"
|
||||
[]
|
||||
(let [eddie (get-reader)]
|
||||
(loop [s (.readLine eddie)]
|
||||
(if (and (= (count (re-seq #"\(" s))
|
||||
(count (re-seq #"\)" s)))
|
||||
(= (count (re-seq #"\[]" s))
|
||||
(count (re-seq #"\]" s))))
|
||||
s
|
||||
(recur (str s " " (.readLine eddie)))))))
|
||||
;; **NOTE THAT** this does not work yet, but it is in the API because I hope
|
||||
;; that it will work later!"
|
||||
;; []
|
||||
;; (let [eddie (get-reader)]
|
||||
;; (loop [s (.readLine eddie)]
|
||||
;; (if (and (= (count (re-seq #"\(" s))
|
||||
;; (count (re-seq #"\)" s)))
|
||||
;; (= (count (re-seq #"\[]" s))
|
||||
;; (count (re-seq #"\]" s))))
|
||||
;; s
|
||||
;; (recur (str s " " (.readLine eddie)))))))
|
|
@ -59,45 +59,65 @@
|
|||
[beowulf.reader.macros :refer [expand-macros]]
|
||||
[beowulf.oblist :refer [NIL]]
|
||||
[clojure.math.numeric-tower :refer [expt]]
|
||||
[clojure.string :refer [upper-case]]))
|
||||
[clojure.string :refer [upper-case]]
|
||||
[clojure.tools.trace :refer [deftrace]]))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;
|
||||
;;; Copyright (C) 2022-2023 Simon Brooke
|
||||
;;;
|
||||
;;; This program is free software; you can redistribute it and/or
|
||||
;;; modify it under the terms of the GNU General Public License
|
||||
;;; as published by the Free Software Foundation; either version 2
|
||||
;;; of the License, or (at your option) any later version.
|
||||
;;;
|
||||
;;; This program is distributed in the hope that it will be useful,
|
||||
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;;; GNU General Public License for more details.
|
||||
;;;
|
||||
;;; You should have received a copy of the GNU General Public License
|
||||
;;; along with this program; if not, write to the Free Software
|
||||
;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(declare generate)
|
||||
|
||||
(defn gen-cond-clause
|
||||
"Generate a cond clause from this simplified parse tree fragment `p`;
|
||||
returns `nil` if `p` does not represent a cond clause."
|
||||
[p]
|
||||
[p context]
|
||||
(when
|
||||
(and (coll? p) (= :cond-clause (first p)))
|
||||
(make-beowulf-list
|
||||
(list (if (= (nth p 1) [:quoted-expr [:atom "T"]])
|
||||
'T
|
||||
(generate (nth p 1)))
|
||||
(generate (nth p 2))))))
|
||||
(generate (nth p 1) context))
|
||||
(generate (nth p 2) context)))))
|
||||
|
||||
(defn gen-cond
|
||||
"Generate a cond statement from this simplified parse tree fragment `p`;
|
||||
returns `nil` if `p` does not represent a (MEXPR) cond statement."
|
||||
[p]
|
||||
[p context]
|
||||
(when
|
||||
(and (coll? p) (= :cond (first p)))
|
||||
(make-beowulf-list
|
||||
(cons
|
||||
'COND
|
||||
(map
|
||||
generate
|
||||
#(generate % (if (= context :mexpr) :cond-mexpr context))
|
||||
(rest p))))))
|
||||
|
||||
(defn gen-fn-call
|
||||
"Generate a function call from this simplified parse tree fragment `p`;
|
||||
returns `nil` if `p` does not represent a (MEXPR) function call."
|
||||
[p]
|
||||
[p context]
|
||||
(when
|
||||
(and (coll? p) (= :fncall (first p)) (= :mvar (first (second p))))
|
||||
(make-cons-cell
|
||||
(generate (second p))
|
||||
(generate (nth p 2)))))
|
||||
(generate (second p) context)
|
||||
(generate (nth p 2) context))))
|
||||
|
||||
|
||||
(defn gen-dot-terminated-list
|
||||
|
@ -118,38 +138,50 @@
|
|||
(generate (first p))
|
||||
(gen-dot-terminated-list (rest p)))))
|
||||
|
||||
;; null[x] = [x = NIL -> T; T -> F]
|
||||
;; [:defn
|
||||
;; [:mexpr [:fncall [:mvar "null"] [:bindings [:args [:mexpr [:mvar "x"]]]]]]
|
||||
;; "="
|
||||
;; [:mexpr [:cond
|
||||
;; [:cond-clause [:mexpr [:iexpr [:lhs [:mexpr [:mvar "x"]]] [:iop "="] [:rhs [:mexpr [:mconst "NIL"]]]]] [:mexpr [:mconst "T"]]]
|
||||
;; [:cond-clause [:mexpr [:mconst "T"]] [:mexpr [:mconst "F"]]]]]]
|
||||
|
||||
(defn generate-defn
|
||||
[tree]
|
||||
(make-beowulf-list
|
||||
(list 'SET
|
||||
(list 'QUOTE (generate (-> tree second second)))
|
||||
(list 'QUOTE
|
||||
(cons 'LAMBDA
|
||||
(cons (generate (nth (second tree) 2))
|
||||
(map generate (-> tree rest rest rest))))))))
|
||||
[tree context]
|
||||
(if (= :mexpr (first tree))
|
||||
(generate-defn (second tree) context)
|
||||
(make-beowulf-list
|
||||
(list 'PUT
|
||||
(list 'QUOTE (generate (-> tree second second second) context))
|
||||
(list 'QUOTE 'EXPR)
|
||||
(list 'QUOTE
|
||||
(cons 'LAMBDA
|
||||
(list (generate (nth (-> tree second second) 2) context)
|
||||
(generate (nth tree 3) context))))))))
|
||||
|
||||
(defn gen-iexpr
|
||||
[tree]
|
||||
[tree context]
|
||||
(let [bundle (reduce #(assoc %1 (first %2) %2)
|
||||
{}
|
||||
(rest tree))]
|
||||
(list (generate (:iop bundle))
|
||||
(generate (:lhs bundle))
|
||||
(generate (:rhs bundle)))))
|
||||
(list (generate (:iop bundle) context)
|
||||
(generate (:lhs bundle) context)
|
||||
(generate (:rhs bundle) context))))
|
||||
|
||||
(defn generate-set
|
||||
"Actually not sure what the mexpr representation of set looks like"
|
||||
[tree]
|
||||
[tree context]
|
||||
(throw (ex-info "Not Yet Implemented" {:feature "generate-set"})))
|
||||
|
||||
(defn generate-assign
|
||||
"Generate an assignment statement based on this `tree`. If the thing
|
||||
being assigned to is a function signature, then we have to do something
|
||||
different to if it's an atom."
|
||||
[tree]
|
||||
[tree context]
|
||||
(case (first (second tree))
|
||||
:fncall (generate-defn tree)
|
||||
(:mvar :atom) (generate-set tree)))
|
||||
:fncall (generate-defn tree context)
|
||||
:mexpr (map #(generate % context) (rest (second tree)))
|
||||
(:mvar :atom) (generate-set tree context)))
|
||||
|
||||
(defn strip-leading-zeros
|
||||
"`read-string` interprets strings with leading zeros as octal; strip
|
||||
|
@ -168,68 +200,77 @@
|
|||
(defn generate
|
||||
"Generate lisp structure from this parse tree `p`. It is assumed that
|
||||
`p` has been simplified."
|
||||
[p]
|
||||
(try
|
||||
(expand-macros
|
||||
(if
|
||||
(coll? p)
|
||||
(case (first p)
|
||||
:λ "LAMBDA"
|
||||
:λexpr (make-cons-cell
|
||||
(generate (nth p 1))
|
||||
(make-cons-cell (generate (nth p 2))
|
||||
(generate (nth p 3))))
|
||||
:args (make-beowulf-list (map generate (rest p)))
|
||||
:atom (symbol (second p))
|
||||
:bindings (generate (second p))
|
||||
:body (make-beowulf-list (map generate (rest p)))
|
||||
(:coefficient :exponent) (generate (second p))
|
||||
:cond (gen-cond p)
|
||||
:cond-clause (gen-cond-clause p)
|
||||
:decimal (read-string (apply str (map second (rest p))))
|
||||
:defn (generate-assign p)
|
||||
:dotted-pair (make-cons-cell
|
||||
(generate (nth p 1))
|
||||
(generate (nth p 2)))
|
||||
:fncall (gen-fn-call p)
|
||||
:iexpr (gen-iexpr p)
|
||||
:integer (read-string (strip-leading-zeros (second p)))
|
||||
:iop (case (second p)
|
||||
"/" 'DIFFERENCE
|
||||
"=" 'EQUAL
|
||||
">" 'GREATERP
|
||||
"<" 'LESSP
|
||||
"+" 'PLUS
|
||||
"*" 'TIMES
|
||||
([p]
|
||||
(generate p :expr))
|
||||
([p context]
|
||||
(try
|
||||
(expand-macros
|
||||
(if
|
||||
(coll? p)
|
||||
(case (first p)
|
||||
:λ "LAMBDA"
|
||||
:λexpr (make-cons-cell
|
||||
(generate (nth p 1) context)
|
||||
(make-cons-cell (generate (nth p 2) context)
|
||||
(generate (nth p 3) context)))
|
||||
:args (make-beowulf-list (map #(generate % context) (rest p)))
|
||||
:atom (symbol (second p))
|
||||
:bindings (generate (second p) context)
|
||||
:body (make-beowulf-list (map #(generate % context) (rest p)))
|
||||
(:coefficient :exponent) (generate (second p) context)
|
||||
:cond (gen-cond p (if (= context :mexpr) :cond-mexpr context))
|
||||
:cond-clause (gen-cond-clause p context)
|
||||
:decimal (read-string (apply str (map second (rest p))))
|
||||
:defn (generate-defn p context)
|
||||
:dotted-pair (make-cons-cell
|
||||
(generate (nth p 1) context)
|
||||
(generate (nth p 2) context))
|
||||
:fncall (gen-fn-call p context)
|
||||
:iexpr (gen-iexpr p context)
|
||||
:integer (read-string (strip-leading-zeros (second p)))
|
||||
:iop (case (second p)
|
||||
"/" 'DIFFERENCE
|
||||
"=" 'EQUAL
|
||||
">" 'GREATERP
|
||||
"<" 'LESSP
|
||||
"+" 'PLUS
|
||||
"*" 'TIMES
|
||||
;; else
|
||||
(throw (ex-info "Unrecognised infix operator symbol"
|
||||
{:phase :generate
|
||||
:fragment p})))
|
||||
:list (gen-dot-terminated-list (rest p))
|
||||
(:lhs :rhs) (generate (second p))
|
||||
:mexpr (generate (second p))
|
||||
:mconst (make-beowulf-list
|
||||
(list 'QUOTE (symbol (upper-case (second p)))))
|
||||
:mvar (symbol (upper-case (second p)))
|
||||
:number (generate (second p))
|
||||
:octal (let [n (read-string (strip-leading-zeros (second p) "0"))
|
||||
scale (generate (nth p 3))]
|
||||
(* n (expt 8 scale)))
|
||||
(throw (ex-info "Unrecognised infix operator symbol"
|
||||
{:phase :generate
|
||||
:fragment p})))
|
||||
:list (gen-dot-terminated-list (rest p))
|
||||
(:lhs :rhs) (generate (second p) context)
|
||||
:mexpr (generate (second p) (if (= context :cond-mexpr) context :mexpr))
|
||||
:mconst (if (= context :cond-mexpr)
|
||||
(case (second p)
|
||||
("T" "F" "NIL") (symbol (second p))
|
||||
;; else
|
||||
(list 'QUOTE (symbol (second p))))
|
||||
;; else
|
||||
(list 'QUOTE (symbol (second p))))
|
||||
:mvar (symbol (upper-case (second p)))
|
||||
:number (generate (second p) context)
|
||||
:octal (let [n (read-string (strip-leading-zeros (second p) "0"))
|
||||
scale (generate (nth p 3) context)]
|
||||
(* n (expt 8 scale)))
|
||||
|
||||
;; the quote read macro (which probably didn't exist in Lisp 1.5, but...)
|
||||
:quoted-expr (make-beowulf-list (list 'QUOTE (generate (second p))))
|
||||
:scale-factor (if
|
||||
(empty? (second p)) 0
|
||||
(read-string (strip-leading-zeros (second p))))
|
||||
:scientific (let [n (generate (second p))
|
||||
exponent (generate (nth p 3))]
|
||||
(* n (expt 10 exponent)))
|
||||
:quoted-expr (make-beowulf-list (list 'QUOTE (generate (second p) context)))
|
||||
:scale-factor (if
|
||||
(empty? (second p)) 0
|
||||
(read-string (strip-leading-zeros (second p))))
|
||||
:scientific (let [n (generate (second p) context)
|
||||
exponent (generate (nth p 3) context)]
|
||||
(* n (expt 10 exponent)))
|
||||
:sexpr (generate (second p) :sexpr)
|
||||
:subr (symbol (second p))
|
||||
|
||||
;; default
|
||||
(throw (ex-info (str "Unrecognised head: " (first p))
|
||||
{:generating p})))
|
||||
p))
|
||||
(catch Throwable any
|
||||
(throw (ex-info "Could not generate"
|
||||
{:generating p}
|
||||
any)))))
|
||||
(throw (ex-info (str "Unrecognised head: " (first p))
|
||||
{:generating p})))
|
||||
p))
|
||||
(catch Throwable any
|
||||
(throw (ex-info "Could not generate"
|
||||
{:generating p}
|
||||
any))))))
|
||||
|
|
|
@ -1,22 +1,53 @@
|
|||
(ns beowulf.reader.macros
|
||||
"Can I implement reader macros? let's see!"
|
||||
(:require [beowulf.cons-cell :refer [CONS LIST make-beowulf-list]]
|
||||
[clojure.string :refer [join]])
|
||||
(:import [beowulf.cons_cell ConsCell]))
|
||||
"Can I implement reader macros? let's see!
|
||||
|
||||
;; We don't need (at least, in the Clojure reader) to rewrite forms like
|
||||
;; "'FOO", because that's handled by the parser. But we do need to rewrite
|
||||
;; things which don't evaluate their arguments, like `SETQ`, because (unless
|
||||
;; LABEL does it, which I'm not yet sure of) we're not yet able to implement
|
||||
;; things which don't evaluate arguments.
|
||||
We don't need (at least, in the Clojure reader) to rewrite forms like
|
||||
`'FOO`, because that's handled by the parser. But we do need to rewrite
|
||||
things which don't evaluate their arguments, like `SETQ`, because (unless
|
||||
LABEL does it, which I'm not yet sure of) we're not yet able to implement
|
||||
things which don't evaluate arguments.
|
||||
|
||||
;; TODO: at this stage, the following should probably also be read macros:
|
||||
;; DEFINE
|
||||
TODO: at this stage, the following should probably also be read macros:
|
||||
DEFINE"
|
||||
(:require [beowulf.cons-cell :refer [make-beowulf-list]]
|
||||
[beowulf.host :refer [CONS LIST]]
|
||||
[clojure.string :refer [join]]))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;
|
||||
;;; We don't need (at least, in the Clojure reader) to rewrite forms like
|
||||
;;; "'FOO", because that's handled by the parser. But we do need to rewrite
|
||||
;;; things which don't evaluate their arguments, like `SETQ`, because (unless
|
||||
;;; LABEL does it, which I'm not yet sure of) we're not yet able to implement
|
||||
;;; things which don't evaluate arguments.
|
||||
;;;
|
||||
;;; TODO: at this stage, the following should probably also be read macros:
|
||||
;;; DEFINE
|
||||
;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;
|
||||
;;; Copyright (C) 2022-2023 Simon Brooke
|
||||
;;;
|
||||
;;; This program is free software; you can redistribute it and/or
|
||||
;;; modify it under the terms of the GNU General Public License
|
||||
;;; as published by the Free Software Foundation; either version 2
|
||||
;;; of the License, or (at your option) any later version.
|
||||
;;;
|
||||
;;; This program is distributed in the hope that it will be useful,
|
||||
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;;; GNU General Public License for more details.
|
||||
;;;
|
||||
;;; You should have received a copy of the GNU General Public License
|
||||
;;; along with this program; if not, write to the Free Software
|
||||
;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(def ^:dynamic *readmacros*
|
||||
{:car {'DEFUN (fn [f]
|
||||
(LIST 'SET (LIST 'QUOTE (second f))
|
||||
(CONS 'LAMBDA (rest (rest f)))))
|
||||
(LIST 'QUOTE (CONS 'LAMBDA (rest (rest f))))))
|
||||
'SETQ (fn [f] (LIST 'SET (LIST 'QUOTE (second f)) (nth f 2)))}})
|
||||
|
||||
(defn expand-macros
|
||||
|
|
|
@ -2,6 +2,26 @@
|
|||
"The actual parser, supporting both S-expression and M-expression syntax."
|
||||
(:require [instaparse.core :as i]))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;
|
||||
;;; Copyright (C) 2022-2023 Simon Brooke
|
||||
;;;
|
||||
;;; This program is free software; you can redistribute it and/or
|
||||
;;; modify it under the terms of the GNU General Public License
|
||||
;;; as published by the Free Software Foundation; either version 2
|
||||
;;; of the License, or (at your option) any later version.
|
||||
;;;
|
||||
;;; This program is distributed in the hope that it will be useful,
|
||||
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;;; GNU General Public License for more details.
|
||||
;;;
|
||||
;;; You should have received a copy of the GNU General Public License
|
||||
;;; along with this program; if not, write to the Free Software
|
||||
;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(def parse
|
||||
"Parse a string presented as argument into a parse tree which can then
|
||||
be operated upon further."
|
||||
|
@ -9,39 +29,46 @@
|
|||
(str
|
||||
;; we tolerate whitespace and comments around legitimate input
|
||||
"raw := expr | opt-comment expr opt-comment;"
|
||||
;; top level: we accept mexprs as well as sexprs.
|
||||
;; top level: we accept mexprs as well as sexprs.
|
||||
"expr := mexpr | sexpr ;"
|
||||
|
||||
;; comments. I'm pretty confident Lisp 1.5 did NOT have these.
|
||||
;; comments. I'm pretty confident Lisp 1.5 did NOT have these.
|
||||
"comment := opt-space <';;'> opt-space #'[^\\n\\r]*';"
|
||||
|
||||
;; there's a notation comprising a left brace followed by mexprs
|
||||
;; followed by a right brace which doesn't seem to be documented
|
||||
;; but I think must represent a prog(?)
|
||||
;; there's a notation comprising a left brace followed by mexprs
|
||||
;; followed by a right brace which doesn't seem to be documented
|
||||
;; but I think must represent assembly code(?)
|
||||
|
||||
;; "prog := lbrace exprs rbrace;"
|
||||
;; mexprs. I'm pretty clear that Lisp 1.5 could never read these,
|
||||
;; but it's a convenience.
|
||||
;; "assembly := lbrace exprs rbrace;"
|
||||
|
||||
;; mexprs. I'm pretty clear that Lisp 1.5 could never read these,
|
||||
;; but it's a convenience.
|
||||
|
||||
;; TODO: this works for now but in fact the Programmer's Manual
|
||||
;; gives a much simpler formulation of M-expression grammar on
|
||||
;; page 9, and of the S-expression grammar on page 8. It would
|
||||
;; be worth going back and redoing this from the book.
|
||||
|
||||
"exprs := expr | exprs;"
|
||||
"mexpr := λexpr | fncall | defn | cond | mvar | mconst | iexpr | number | mexpr comment;
|
||||
λexpr := λ lsqb bindings semi-colon body rsqb;
|
||||
λ := 'λ';
|
||||
λexpr := λ lsqb bindings semi-colon opt-space body opt-space rsqb;
|
||||
λ := 'λ' | 'lambda';
|
||||
bindings := lsqb args rsqb | lsqb rsqb;
|
||||
body := (mexpr semi-colon opt-space)* mexpr;
|
||||
body := (opt-space mexpr semi-colon)* opt-space mexpr;
|
||||
fncall := fn-name bindings;
|
||||
lsqb := '[';
|
||||
rsqb := ']';
|
||||
lbrace := '{';
|
||||
rbrace := '}';
|
||||
lbrace := '{';
|
||||
rbrace := '}';
|
||||
defn := mexpr opt-space '=' opt-space mexpr;
|
||||
cond := lsqb (opt-space cond-clause semi-colon opt-space)* cond-clause rsqb;
|
||||
cond-clause := mexpr opt-space arrow opt-space mexpr opt-space;
|
||||
arrow := '->';
|
||||
args := mexpr | (opt-space mexpr semi-colon opt-space)* opt-space mexpr opt-space;
|
||||
args := arg | (opt-space arg semi-colon opt-space)* opt-space arg opt-space;
|
||||
arg := mexpr;
|
||||
fn-name := mvar;
|
||||
mvar := #'[a-z]+';
|
||||
mconst := #'[A-Z]+';
|
||||
mvar := #'[a-z][a-z0-9]*';
|
||||
mconst := #'[A-Z][A-Z0-9]*';
|
||||
semi-colon := ';';"
|
||||
|
||||
;; Infix operators appear in mexprs, e.g. on page 7. Ooops!
|
||||
|
@ -52,13 +79,13 @@
|
|||
iexp := mexpr | number | opt-space iexp opt-space;
|
||||
iop := '>' | '<' | '+' | '-' | '*' '/' | '=' ;"
|
||||
|
||||
;; comments. I'm pretty confident Lisp 1.5 did NOT have these.
|
||||
;; comments. I'm pretty confident Lisp 1.5 did NOT have these.
|
||||
"opt-comment := opt-space | comment;"
|
||||
"comment := opt-space <';;'> #'[^\\n\\r]*' opt-space;"
|
||||
|
||||
;; sexprs. Note it's not clear to me whether Lisp 1.5 had the quote macro,
|
||||
;; but I've included it on the basis that it can do little harm.
|
||||
"sexpr := quoted-expr | atom | number | dotted-pair | list | sexpr comment;
|
||||
;; sexprs. Note it's not clear to me whether Lisp 1.5 had the quote macro,
|
||||
;; but I've included it on the basis that it can do little harm.
|
||||
"sexpr := quoted-expr | atom | number | subr | dotted-pair | list | sexpr comment;
|
||||
list := lpar sexpr rpar | lpar (sexpr sep)* rpar | lpar (sexpr sep)* dot-terminal | lbrace exprs rbrace;
|
||||
list := lpar opt-space sexpr rpar | lpar opt-space (sexpr sep)* rpar | lpar opt-space (sexpr sep)* dot-terminal;
|
||||
dotted-pair := lpar dot-terminal ;
|
||||
|
@ -73,7 +100,13 @@
|
|||
sep := ',' | opt-space;
|
||||
atom := #'[A-Z][A-Z0-9]*';"
|
||||
|
||||
;; Lisp 1.5 supported octal as well as decimal and scientific notation
|
||||
;; we need a way of representing Clojure functions on the object list;
|
||||
;; subr objects aren't expected to be normally entered on the REPL, but
|
||||
;; must be on the object list or functions to which functions are passed
|
||||
;; won't be able to access them.
|
||||
"subr := #'[a-z][a-z.]*/[A-Za-z][A-Za-z0-9]*';"
|
||||
|
||||
;; Lisp 1.5 supported octal as well as decimal and scientific notation
|
||||
"number := integer | decimal | scientific | octal;
|
||||
integer := #'-?[0-9]+';
|
||||
decimal := integer dot integer;
|
||||
|
|
|
@ -5,7 +5,27 @@
|
|||
[instaparse.failure :as f])
|
||||
(:import [instaparse.gll Failure]))
|
||||
|
||||
(declare simplify)
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;
|
||||
;;; Copyright (C) 2022-2023 Simon Brooke
|
||||
;;;
|
||||
;;; This program is free software; you can redistribute it and/or
|
||||
;;; modify it under the terms of the GNU General Public License
|
||||
;;; as published by the Free Software Foundation; either version 2
|
||||
;;; of the License, or (at your option) any later version.
|
||||
;;;
|
||||
;;; This program is distributed in the hope that it will be useful,
|
||||
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;;; GNU General Public License for more details.
|
||||
;;;
|
||||
;;; You should have received a copy of the GNU General Public License
|
||||
;;; along with this program; if not, write to the Free Software
|
||||
;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(declare simplify-tree)
|
||||
|
||||
(defn remove-optional-space
|
||||
[tree]
|
||||
|
@ -28,17 +48,17 @@
|
|||
(loop [r tree']
|
||||
(if (and r (vector? r) (keyword? (first r)))
|
||||
(if (= (first r) key)
|
||||
(recur (simplify (second r) context))
|
||||
(recur (simplify-tree (second r) context))
|
||||
r)
|
||||
r))
|
||||
tree')))
|
||||
|
||||
(defn simplify
|
||||
(defn simplify-tree
|
||||
"Simplify this parse tree `p`. If `p` is an instaparse failure object, throw
|
||||
an `ex-info`, with `p` as the value of its `:failure` key.
|
||||
|
||||
**NOTE THAT** it is assumed that `remove-optional-space` has been run on the
|
||||
parse tree **BEFORE** it is passed to `simplify`."
|
||||
parse tree **BEFORE** it is passed to `simplify-tree`."
|
||||
([p]
|
||||
(if
|
||||
(instance? Failure p)
|
||||
|
@ -47,7 +67,7 @@
|
|||
{:cause :parse-failure
|
||||
:phase :simplify
|
||||
:failure p}))
|
||||
(simplify p :expr)))
|
||||
(simplify-tree p :expr)))
|
||||
([p context]
|
||||
(cond
|
||||
(string? p) p
|
||||
|
@ -58,8 +78,8 @@
|
|||
(case (first p)
|
||||
(:λexpr
|
||||
:args :bindings :body :cond :cond-clause :defn :dot-terminal
|
||||
:fncall :lhs :quoted-expr :rhs ) (map #(simplify % context) p)
|
||||
(:arg :expr :coefficient :fn-name :number) (simplify (second p) context)
|
||||
:fncall :lhs :quoted-expr :rhs ) (map #(simplify-tree % context) p)
|
||||
(:arg :expr :coefficient :fn-name :number) (simplify-tree (second p) context)
|
||||
(:arrow :dot :e :lpar :lsqb :opt-comment :opt-space :q :quote :rpar :rsqb
|
||||
:semi-colon :sep :space) nil
|
||||
:atom (if
|
||||
|
@ -77,28 +97,35 @@
|
|||
[:fncall
|
||||
[:mvar "cons"]
|
||||
[:args
|
||||
(simplify (nth p 1) context)
|
||||
(simplify (nth p 2) context)]]
|
||||
(map #(simplify % context) p))
|
||||
:iexp (simplify (second p) context)
|
||||
(simplify-tree (nth p 1) context)
|
||||
(simplify-tree (nth p 2) context)]]
|
||||
(map #(simplify-tree % context) p))
|
||||
:iexp (simplify-tree (second p) context)
|
||||
:iexpr [:iexpr
|
||||
[:lhs (simplify (second p) context)]
|
||||
(simplify (nth p 2) context) ;; really should be the operator
|
||||
[:rhs (simplify (nth p 3) context)]]
|
||||
[:lhs (simplify-tree (second p) context)]
|
||||
(simplify-tree (nth p 2) context) ;; really should be the operator
|
||||
[:rhs (simplify-tree (nth p 3) context)]]
|
||||
:mexpr (if
|
||||
(:strict *options*)
|
||||
(throw
|
||||
(ex-info "Cannot parse meta expressions in strict mode"
|
||||
{:cause :strict}))
|
||||
(simplify (second p) :mexpr))
|
||||
[:mexpr (simplify-tree (second p) :mexpr)])
|
||||
:list (if
|
||||
(= context :mexpr)
|
||||
[:fncall
|
||||
[:mvar "list"]
|
||||
[:args (apply vector (map simplify (rest p)))]]
|
||||
(map #(simplify % context) p))
|
||||
:raw (first (remove empty? (map simplify (rest p))))
|
||||
:sexpr (simplify (second p) :sexpr)
|
||||
[:args (apply vector (map simplify-tree (rest p)))]]
|
||||
(map #(simplify-tree % context) p))
|
||||
:raw (first (remove empty? (map simplify-tree (rest p))))
|
||||
:sexpr [:sexpr (simplify-tree (second p) :sexpr)]
|
||||
;;default
|
||||
p)))
|
||||
:else p)))
|
||||
|
||||
(defn simplify
|
||||
"Simplify this parse tree `p`. If `p` is an instaparse failure object, throw
|
||||
an `ex-info`, with `p` as the value of its `:failure` key. Calls
|
||||
`remove-optional-space` before processing."
|
||||
[p]
|
||||
(simplify-tree (remove-optional-space p)))
|
|
@ -1,24 +0,0 @@
|
|||
(ns beowulf.trace
|
||||
"Tracing of function execution")
|
||||
|
||||
(def traced-symbols
|
||||
"Symbols currently being traced."
|
||||
(atom #{}))
|
||||
|
||||
(defn traced?
|
||||
"Return `true` iff `s` is a symbol currently being traced, else `nil`."
|
||||
[s]
|
||||
(try (contains? @traced-symbols s)
|
||||
(catch Throwable _)))
|
||||
|
||||
(defn TRACE
|
||||
"Add this symbol `s` to the set of symbols currently being traced. If `s`
|
||||
is not a symbol, does nothing."
|
||||
[s]
|
||||
(when (symbol? s)
|
||||
(swap! traced-symbols #(conj % s))))
|
||||
|
||||
(defn UNTRACE
|
||||
[s]
|
||||
(when (symbol? s)
|
||||
(swap! traced-symbols #(set (remove (fn [x] (= s x)) %)))))
|
|
@ -1,20 +1,27 @@
|
|||
(ns beowulf.bootstrap-test
|
||||
(:require [clojure.test :refer [deftest testing is]]
|
||||
[beowulf.cons-cell :refer [CAR CDR make-cons-cell T F]]
|
||||
[beowulf.bootstrap :refer [APPEND ASSOC ATOM ATOM? CAAAAR CADR
|
||||
CADDR CADDDR EQ EQUAL MEMBER
|
||||
PAIRLIS SUBLIS SUBST]]
|
||||
(:require [beowulf.bootstrap :refer [EVAL]]
|
||||
[beowulf.cons-cell :refer [F make-cons-cell T]]
|
||||
[beowulf.host :refer [ASSOC ATOM ATOM? CAAAAR CADDDR CADDR CADR
|
||||
CAR CDR EQ EQUAL PAIRLIS]]
|
||||
[beowulf.oblist :refer [NIL]]
|
||||
[beowulf.read :refer [gsp]]))
|
||||
[beowulf.read :refer [gsp READ]]
|
||||
[clojure.test :refer [deftest is testing]]))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;
|
||||
;;; This file is primarily tests of the functions in `beowulf.eval` - which
|
||||
;;; This file is primarily tests of the functions in `beowulf.bootstrap` - which
|
||||
;;; are Clojure functions, but aim to provide sufficient functionality that
|
||||
;;; Beowulf can get up to the level of running its own code.
|
||||
;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defn- reps
|
||||
"'Read eval print string', or 'read eval print single'.
|
||||
Reads and evaluates one input string, and returns the
|
||||
output string."
|
||||
[input]
|
||||
(with-out-str (print (EVAL (READ input)))))
|
||||
|
||||
(deftest atom-tests
|
||||
(testing "ATOM"
|
||||
(let [expected T
|
||||
|
@ -63,12 +70,12 @@
|
|||
(is (= actual expected) "A is CAR of (A B C D)"))
|
||||
(is (thrown-with-msg?
|
||||
Exception
|
||||
#"Cannot take CAR of `.*"
|
||||
#"Ne can tace CAR of `.*"
|
||||
(CAR 'T))
|
||||
"Can't take the CAR of an atom")
|
||||
(is (thrown-with-msg?
|
||||
Exception
|
||||
#"Cannot take CAR of `.*"
|
||||
#"Ne can tace CAR of `.*"
|
||||
(CAR 7))
|
||||
"Can't take the CAR of a number"))
|
||||
(testing "CDR"
|
||||
|
@ -82,12 +89,12 @@
|
|||
(is (= (CAR actual) expected) "the CAR of that cons-cell is B"))
|
||||
(is (thrown-with-msg?
|
||||
Exception
|
||||
#"Cannot take CDR of `.*"
|
||||
#"Ne can tace CDR of `.*"
|
||||
(CDR 'T))
|
||||
"Can't take the CDR of an atom")
|
||||
(is (thrown-with-msg?
|
||||
Exception
|
||||
#"Cannot take CDR of `.*"
|
||||
#"Ne can tace CDR of `.*"
|
||||
(CDR 7))
|
||||
"Can't take the CDR of a number"))
|
||||
(let [s (gsp "((((1 . 2) 3)(4 5) 6)(7 (8 9) (10 11 12) 13) 14 (15 16) 17)")]
|
||||
|
@ -153,55 +160,18 @@
|
|||
actual (EQUAL l m)]
|
||||
(is (= actual expected) "different lists, different content"))))
|
||||
|
||||
(deftest substitution-tests
|
||||
(testing "subst"
|
||||
(let [expected "((A X . A) . C)"
|
||||
;; differs from example in book only because of how the function
|
||||
;; `beowulf.cons-cell/to-string` formats lists.
|
||||
actual (print-str
|
||||
(SUBST
|
||||
(gsp "(X . A)")
|
||||
(gsp "B")
|
||||
(gsp "((A . B) . C)")))]
|
||||
(is (= actual expected)))))
|
||||
|
||||
(deftest append-tests
|
||||
(testing "append"
|
||||
(let [expected "(A B C . D)"
|
||||
actual (print-str
|
||||
(APPEND
|
||||
(gsp "(A B)")
|
||||
(gsp "(C . D)")))]
|
||||
(is (= actual expected)))
|
||||
(let [expected "(A B C D E)"
|
||||
actual (print-str
|
||||
(APPEND
|
||||
(gsp "(A B)")
|
||||
(gsp "(C D E)")))]
|
||||
(is (= actual expected)))))
|
||||
|
||||
(deftest member-tests
|
||||
(testing "member"
|
||||
(let [expected 'T
|
||||
actual (MEMBER
|
||||
(gsp "ALBERT")
|
||||
(gsp "(ALBERT BELINDA CHARLIE DORIS ELFREDA FRED)"))]
|
||||
(is (= actual expected)))
|
||||
(let [expected 'T
|
||||
actual (MEMBER
|
||||
(gsp "BELINDA")
|
||||
(gsp "(ALBERT BELINDA CHARLIE DORIS ELFREDA FRED)"))]
|
||||
(is (= actual expected)))
|
||||
(let [expected 'T
|
||||
actual (MEMBER
|
||||
(gsp "ELFREDA")
|
||||
(gsp "(ALBERT BELINDA CHARLIE DORIS ELFREDA FRED)"))]
|
||||
(is (= actual expected)))
|
||||
(let [expected 'F
|
||||
actual (MEMBER
|
||||
(gsp "BERTRAM")
|
||||
(gsp "(ALBERT BELINDA CHARLIE DORIS ELFREDA FRED)"))]
|
||||
(is (= actual expected)))))
|
||||
;; TODO: need to reimplement this in lisp_test
|
||||
;; (deftest substitution-tests
|
||||
;; (testing "subst"
|
||||
;; (let [expected "((A X . A) . C)"
|
||||
;; ;; differs from example in book only because of how the function
|
||||
;; ;; `beowulf.cons-cell/to-string` formats lists.
|
||||
;; actual (print-str
|
||||
;; (SUBST
|
||||
;; (gsp "(X . A)")
|
||||
;; (gsp "B")
|
||||
;; (gsp "((A . B) . C)")))]
|
||||
;; (is (= actual expected)))))
|
||||
|
||||
(deftest pairlis-tests
|
||||
(testing "pairlis"
|
||||
|
@ -233,12 +203,3 @@
|
|||
'D
|
||||
(gsp "((A . (M N)) (B . (CAR X)) (C . (QUOTE M)) (C . (CDR X)))")))]
|
||||
(is (= actual expected)))))
|
||||
|
||||
(deftest sublis-tests
|
||||
(testing "sublis"
|
||||
(let [expected "(SHAKESPEARE WROTE (THE TEMPEST))"
|
||||
actual (print-str
|
||||
(SUBLIS
|
||||
(gsp "((X . SHAKESPEARE) (Y . (THE TEMPEST)))")
|
||||
(gsp "(X WROTE Y)")))]
|
||||
(is (= actual expected)))))
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
(ns beowulf.host-test
|
||||
(:require [clojure.test :refer [deftest is testing]]
|
||||
[beowulf.cons-cell :refer [CDR F make-beowulf-list T]]
|
||||
[beowulf.host :refer [DIFFERENCE NUMBERP PLUS RPLACA RPLACD TIMES]]
|
||||
[beowulf.cons-cell :refer [F make-beowulf-list T]]
|
||||
[beowulf.host :refer [CDR DIFFERENCE NUMBERP PLUS RPLACA RPLACD TIMES]]
|
||||
[beowulf.oblist :refer [NIL]]
|
||||
[beowulf.read :refer [gsp]]))
|
||||
|
||||
|
@ -15,12 +15,12 @@
|
|||
(is (= actual expected)))
|
||||
(is (thrown-with-msg?
|
||||
Exception
|
||||
#"Invalid value in RPLACA.*"
|
||||
#"Un-ġefōg þing in RPLACA.*"
|
||||
(RPLACA (make-beowulf-list '(A B C D E)) "F"))
|
||||
"You can't represent a string in Lisp 1.5")
|
||||
(is (thrown-with-msg?
|
||||
Exception
|
||||
#"Invalid cell in RPLACA.*"
|
||||
#"Uncynlic miercels in RPLACA.*"
|
||||
(RPLACA '(A B C D E) 'F))
|
||||
"You can't RPLACA into anything which isn't a MutableSequence.")
|
||||
)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
(ns beowulf.interop-test
|
||||
(:require [clojure.test :refer [deftest is testing]]
|
||||
[beowulf.bootstrap :refer [EVAL INTEROP]]
|
||||
[beowulf.bootstrap :refer [EVAL]]
|
||||
[beowulf.interop :refer [INTEROP]]
|
||||
[beowulf.read :refer [gsp]]))
|
||||
|
||||
|
||||
|
|
168
test/beowulf/lisp_test.clj
Normal file
168
test/beowulf/lisp_test.clj
Normal file
|
@ -0,0 +1,168 @@
|
|||
(ns beowulf.lisp-test
|
||||
"The idea here is to test actual Lisp functions"
|
||||
(:require [clojure.test :refer [deftest testing is use-fixtures]]
|
||||
[beowulf.bootstrap :refer [EVAL]]
|
||||
[beowulf.cons-cell :refer [make-beowulf-list]]
|
||||
[beowulf.io :refer [SYSIN]]
|
||||
;; [beowulf.oblist :refer [NIL]]
|
||||
[beowulf.read :refer [READ]]))
|
||||
|
||||
(defn- reps
|
||||
"'Read eval print string', or 'read eval print single'.
|
||||
Reads and evaluates one input string, and returns the
|
||||
output string."
|
||||
[input]
|
||||
(with-out-str (print (EVAL (READ input)))))
|
||||
|
||||
(use-fixtures :once (fn [f]
|
||||
(try (when (SYSIN "resources/lisp1.5.lsp")
|
||||
(f))
|
||||
(catch Throwable any
|
||||
(throw (ex-info "Failed to load Lisp sysout"
|
||||
{:phase test
|
||||
:function 'SYSIN
|
||||
:file "resources/lisp1.5.lsp"}
|
||||
any))))))
|
||||
|
||||
(deftest APPEND-tests
|
||||
(testing "append - dot-terminated lists"
|
||||
(let [expected "(A B C . D)"
|
||||
actual (reps "(APPEND '(A B) (CONS 'C 'D))")]
|
||||
(is (= actual expected)))
|
||||
(let [expected "(A B C . D)"
|
||||
actual (reps "(APPEND (CONS 'A (CONS 'B NIL)) (CONS 'C 'D))")]
|
||||
(is (= actual expected)))
|
||||
;; this is failing: https://github.com/simon-brooke/beowulf/issues/5
|
||||
(let [expected "(A B C . D)"
|
||||
actual (reps "(APPEND '(A B) '(C . D))")]
|
||||
(is (= actual expected))))
|
||||
(testing "append - straight lists"
|
||||
(let [expected "(A B C D E)"
|
||||
actual (reps "(APPEND '(A B) '(C D E))")]
|
||||
(is (= actual expected)))))
|
||||
|
||||
(deftest COPY-tests
|
||||
(testing "copy NIL"
|
||||
(let [expected "NIL"
|
||||
actual (with-out-str (print (EVAL (READ "(COPY NIL)"))))]
|
||||
(is (= actual expected))))
|
||||
(testing "copy straight list"
|
||||
(let [expected (make-beowulf-list '(A B C))
|
||||
actual (EVAL (READ "(COPY '(A B C))"))]
|
||||
(is (= actual expected))))
|
||||
(testing "copy assoc list created in READ"
|
||||
;; this is failing. Problem in READ?
|
||||
;; see https://github.com/simon-brooke/beowulf/issues/5
|
||||
(let [expected (READ "((A . 1) (B . 2) (C . 3))")
|
||||
actual (EVAL (READ "(COPY '((A . 1) (B . 2) (C . 3)))"))]
|
||||
(is (= actual expected))))
|
||||
(testing "copy assoc list created with PAIR"
|
||||
(let [expected (READ "((A . 1) (B . 2) (C . 3))")
|
||||
actual (EVAL (READ "(COPY (PAIR '(A B C) '(1 2 3)))"))]
|
||||
(is (= actual expected)))))
|
||||
|
||||
(deftest DIVIDE-tests
|
||||
(testing "rational divide"
|
||||
(let [expected "(4 0)"
|
||||
input "(DIVIDE 8 2)"
|
||||
actual (reps input)]
|
||||
(is (= actual expected))))
|
||||
(testing "irrational divide"
|
||||
(let [expected "(3.142857 1)"
|
||||
input "(DIVIDE 22 7)"
|
||||
actual (reps input)]
|
||||
(is (= actual expected))))
|
||||
(testing "divide by zero"
|
||||
(let [input "(DIVIDE 22 0)"]
|
||||
(is (thrown-with-msg? clojure.lang.ExceptionInfo
|
||||
#"Uncynlic þegnung: Divide by zero"
|
||||
(reps input)))))
|
||||
|
||||
;; TODO: need to write tests for GET but I don't really
|
||||
;; understand what the correct behaviour is.
|
||||
|
||||
(deftest INTERSECTION-tests
|
||||
(testing "non-intersecting"
|
||||
(let [expected "NIL"
|
||||
input "(INTERSECTION '(A B C) '(D E F))"
|
||||
actual (reps input)]
|
||||
(is (= actual expected))))
|
||||
(testing "intersection with NIL"
|
||||
(let [expected "NIL"
|
||||
input "(INTERSECTION '(A B C) NIL)"
|
||||
actual (reps input)]
|
||||
(is (= actual expected))))
|
||||
(testing "intersection with NIL (2)"
|
||||
(let [expected "NIL"
|
||||
input "(INTERSECTION NIL '(A B C))"
|
||||
actual (reps input)]
|
||||
(is (= actual expected))))
|
||||
(testing "sequential intersection"
|
||||
(let [expected "(C D)"
|
||||
input "(INTERSECTION '(A B C D) '(C D E F))"
|
||||
actual (reps input)]
|
||||
(is (= actual expected))))
|
||||
(testing "non-sequential intersection"
|
||||
(let [expected "(C D)"
|
||||
input "(INTERSECTION '(A B C D) '(F D E C))"
|
||||
actual (reps input)]
|
||||
(is (= actual expected)))))
|
||||
|
||||
(deftest LENGTH-tests
|
||||
(testing "length of NIL"
|
||||
(let [expected "0"
|
||||
input "(LENGTH NIL)"
|
||||
actual (reps input)]
|
||||
(is (= actual expected))))
|
||||
(testing "length of simple list"
|
||||
(let [expected "3"
|
||||
input "(LENGTH '(1 2 3))"
|
||||
actual (reps input)]
|
||||
(is (= actual expected))))
|
||||
;; (testing "length of dot-terminated list"
|
||||
;; (let [expected "3"
|
||||
;; input "(LENGTH '(1 2 3 . 4))"
|
||||
;; actual (reps input)]
|
||||
;; (is (= actual expected))))
|
||||
(testing "length of assoc list"
|
||||
(let [expected "3"
|
||||
input "(LENGTH (PAIR '(A B C) '(1 2 3)))"
|
||||
actual (reps input)]
|
||||
(is (= actual expected))))))
|
||||
|
||||
|
||||
(deftest MEMBER-tests
|
||||
(testing "member"
|
||||
(let [expected "T"
|
||||
actual (reps "(MEMBER 'ALBERT '(ALBERT BELINDA CHARLIE DORIS ELFREDA FRED))")]
|
||||
(is (= actual expected)))
|
||||
(let [expected "T"
|
||||
actual (reps "(MEMBER 'BELINDA '(ALBERT BELINDA CHARLIE DORIS ELFREDA FRED))")]
|
||||
(is (= actual expected)))
|
||||
(let [expected "T"
|
||||
actual (reps "(MEMBER 'ELFREDA '(ALBERT BELINDA CHARLIE DORIS ELFREDA FRED))")]
|
||||
(is (= actual expected)))
|
||||
(let [expected "F"
|
||||
actual (reps "(MEMBER 'BERTRAM '(ALBERT BELINDA CHARLIE DORIS ELFREDA FRED))")]
|
||||
(is (= actual expected)))))
|
||||
|
||||
;; This is failing, and although yes, it does matter, I have not yet tracked the reason.
|
||||
;; (deftest sublis-tests
|
||||
;; (testing "sublis"
|
||||
;; (let [expected "(SHAKESPEARE WROTE (THE TEMPEST))"
|
||||
;; actual (reps
|
||||
;; "(SUBLIS
|
||||
;; '((X . SHAKESPEARE) (Y . (THE TEMPEST)))
|
||||
;; '(X WROTE Y))")]
|
||||
;; (is (= actual expected)))))
|
||||
|
||||
(deftest prog-tests
|
||||
(testing "PROG"
|
||||
(let [expected "5"
|
||||
actual (reps "(PROG (X)
|
||||
(SETQ X 1)
|
||||
START
|
||||
(SETQ X (ADD1 X))
|
||||
(COND ((EQ X 5) (RETURN X))
|
||||
(T (GO START))))")]
|
||||
(is (= actual expected)))))
|
|
@ -6,7 +6,7 @@
|
|||
[beowulf.read :refer [gsp]]
|
||||
[beowulf.reader.generate :refer [generate]]
|
||||
[beowulf.reader.parser :refer [parse]]
|
||||
[beowulf.reader.simplify :refer [simplify]]))
|
||||
[beowulf.reader.simplify :refer [simplify-tree]]))
|
||||
|
||||
;; These tests are taken generally from the examples on page 10 of
|
||||
;; Lisp 1.5 Programmers Manual:
|
||||
|
@ -53,7 +53,7 @@
|
|||
;; I suspect as (CAR (LIST A B C)).
|
||||
|
||||
(let [expected "(CAR (LIST A B C))"
|
||||
actual (print-str (gsp "car[(A B C)]"))]
|
||||
actual (print-str (gsp "car[ list[a; b; c]]"))]
|
||||
(is (= actual expected)))
|
||||
))
|
||||
|
||||
|
@ -68,13 +68,13 @@
|
|||
|
||||
(deftest conditional-tests
|
||||
(testing "Conditional expressions"
|
||||
(let [expected "(COND ((ATOM X) X) ((QUOTE T) (FF (CAR X))))"
|
||||
(let [expected "(COND ((ATOM X) X) (T (FF (CAR X))))"
|
||||
actual (print-str (gsp "[atom[x]->x; T->ff[car[x]]]"))]
|
||||
(is (= actual expected)))
|
||||
(let [expected "(LABEL FF (LAMBDA (X) (COND ((ATOM X) X) ((QUOTE T) (FF (CAR X))))))"
|
||||
(let [expected "(LABEL FF (LAMBDA (X) (COND ((ATOM X) X) (T (FF (CAR X))))))"
|
||||
actual (print-str
|
||||
(generate
|
||||
(simplify
|
||||
(simplify-tree
|
||||
(parse "label[ff;λ[[x];[atom[x]->x; T->ff[car[x]]]]]"))))]
|
||||
(is (= actual expected)))))
|
||||
|
||||
|
@ -88,6 +88,6 @@
|
|||
|
||||
(deftest assignment-tests
|
||||
(testing "Function assignment"
|
||||
(let [expected "(SET (QUOTE FF) (QUOTE (LAMBDA (X) (COND ((ATOM X) X) ((QUOTE T) (FF (CAR X)))))))"
|
||||
(let [expected "(PUT (QUOTE FF) (QUOTE EXPR) (QUOTE (LAMBDA (X) (COND ((ATOM X) X) (T (FF (CAR X)))))))"
|
||||
actual (print-str (gsp "ff[x]=[atom[x] -> x; T -> ff[car[x]]]"))]
|
||||
(is (= actual expected)))))
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
(deftest macro-expansion
|
||||
(testing "Expanding DEFUN"
|
||||
(let [expected "(SET (QUOTE FACT) (LAMBDA (X) (COND ((ZEROP X) 1) (T (TIMES X (FACT (SUB1 X)))))))"
|
||||
(let [expected "(SET (QUOTE FACT) (QUOTE (LAMBDA (X) (COND ((ZEROP X) 1) (T (TIMES X (FACT (SUB1 X))))))))"
|
||||
source "(DEFUN FACT (X) (COND ((ZEROP X) 1) (T (TIMES X (FACT (SUB1 X))))))"
|
||||
actual (print-str (gsp source))]
|
||||
(is (= actual expected)))))
|
Loading…
Reference in a new issue