Squashed commits
This commit is contained in:
commit
dfa5f22627
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
*~
|
||||||
|
\#*
|
||||||
|
.\#*
|
||||||
|
.DS_Store
|
||||||
|
compiled/
|
||||||
|
/doc/
|
31
.travis.yml
Normal file
31
.travis.yml
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
language: c
|
||||||
|
sudo: false
|
||||||
|
|
||||||
|
env:
|
||||||
|
global:
|
||||||
|
# RACKET_DIR is an argument to install-racket.sh
|
||||||
|
- RACKET_DIR=~/racket
|
||||||
|
- PATH="$RACKET_DIR/bin:$PATH"
|
||||||
|
matrix:
|
||||||
|
# RACKET_VERSION is an argument to install-racket.sh
|
||||||
|
- RACKET_VERSION=6.5
|
||||||
|
- RACKET_VERSION=6.6
|
||||||
|
- RACKET_VERSION=6.7
|
||||||
|
- RACKET_VERSION=6.8
|
||||||
|
- RACKET_VERSION=RELEASE
|
||||||
|
- RACKET_VERSION=HEAD
|
||||||
|
|
||||||
|
before_install:
|
||||||
|
- curl -L https://raw.githubusercontent.com/greghendershott/travis-racket/master/install-racket.sh | bash
|
||||||
|
- raco pkg install --deps search-auto doc-coverage cover cover-codecov # or cover-coveralls
|
||||||
|
|
||||||
|
install:
|
||||||
|
- raco pkg install --deps search-auto -j 2
|
||||||
|
|
||||||
|
script:
|
||||||
|
- raco test -x -p "$(basename "$TRAVIS_BUILD_DIR")"
|
||||||
|
- raco setup --check-pkg-deps --no-zo --no-launcher --no-install --no-post-install --no-docs --pkgs "$(basename "$TRAVIS_BUILD_DIR")"
|
||||||
|
- raco doc-coverage "$(basename "$TRAVIS_BUILD_DIR")"
|
||||||
|
- raco cover -s main -s test -s doc -f codecov -f html -d ~/coverage . || true
|
||||||
|
# TODO: add an option to cover to run the "outer" module too, not just the submodules.
|
||||||
|
# TODO: deploy the coverage info.
|
28
LICENSE-more.md
Normal file
28
LICENSE-more.md
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
remember
|
||||||
|
|
||||||
|
Parts of this this software were initially written as part of a project
|
||||||
|
at Cortus, S.A.S. which can be reached at 97 Rue de Freyr, 34000
|
||||||
|
Montpellier, France. I got their permission to redistribute the code in
|
||||||
|
the Public Domain.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
This package is in distributed under the Creative Commons CC0 license
|
||||||
|
https://creativecommons.org/publicdomain/zero/1.0/, as specified by
|
||||||
|
the LICENSE.txt file.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
The CC0 license is equivalent to a dedication to the Public Domain
|
||||||
|
in most countries, but is also effective in countries which do not
|
||||||
|
recognize explicit dedications to the Public Domain.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
In order to avoid any potential licensing issues, this package is explicitly
|
||||||
|
distributed under the Creative Commons CC0 license
|
||||||
|
https://creativecommons.org/publicdomain/zero/1.0/, or under the GNU Lesser
|
||||||
|
General Public License (LGPL) https://opensource.org/licenses/LGPL-3.0, or
|
||||||
|
under the Apache License Version 2.0
|
||||||
|
https://opensource.org/licenses/Apache-2.0, or under the MIT license
|
||||||
|
https://opensource.org/licenses/MIT, at your option.
|
116
LICENSE.txt
Normal file
116
LICENSE.txt
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
CC0 1.0 Universal
|
||||||
|
|
||||||
|
Statement of Purpose
|
||||||
|
|
||||||
|
The laws of most jurisdictions throughout the world automatically confer
|
||||||
|
exclusive Copyright and Related Rights (defined below) upon the creator and
|
||||||
|
subsequent owner(s) (each and all, an "owner") of an original work of
|
||||||
|
authorship and/or a database (each, a "Work").
|
||||||
|
|
||||||
|
Certain owners wish to permanently relinquish those rights to a Work for the
|
||||||
|
purpose of contributing to a commons of creative, cultural and scientific
|
||||||
|
works ("Commons") that the public can reliably and without fear of later
|
||||||
|
claims of infringement build upon, modify, incorporate in other works, reuse
|
||||||
|
and redistribute as freely as possible in any form whatsoever and for any
|
||||||
|
purposes, including without limitation commercial purposes. These owners may
|
||||||
|
contribute to the Commons to promote the ideal of a free culture and the
|
||||||
|
further production of creative, cultural and scientific works, or to gain
|
||||||
|
reputation or greater distribution for their Work in part through the use and
|
||||||
|
efforts of others.
|
||||||
|
|
||||||
|
For these and/or other purposes and motivations, and without any expectation
|
||||||
|
of additional consideration or compensation, the person associating CC0 with a
|
||||||
|
Work (the "Affirmer"), to the extent that he or she is an owner of Copyright
|
||||||
|
and Related Rights in the Work, voluntarily elects to apply CC0 to the Work
|
||||||
|
and publicly distribute the Work under its terms, with knowledge of his or her
|
||||||
|
Copyright and Related Rights in the Work and the meaning and intended legal
|
||||||
|
effect of CC0 on those rights.
|
||||||
|
|
||||||
|
1. Copyright and Related Rights. A Work made available under CC0 may be
|
||||||
|
protected by copyright and related or neighboring rights ("Copyright and
|
||||||
|
Related Rights"). Copyright and Related Rights include, but are not limited
|
||||||
|
to, the following:
|
||||||
|
|
||||||
|
i. the right to reproduce, adapt, distribute, perform, display, communicate,
|
||||||
|
and translate a Work;
|
||||||
|
|
||||||
|
ii. moral rights retained by the original author(s) and/or performer(s);
|
||||||
|
|
||||||
|
iii. publicity and privacy rights pertaining to a person's image or likeness
|
||||||
|
depicted in a Work;
|
||||||
|
|
||||||
|
iv. rights protecting against unfair competition in regards to a Work,
|
||||||
|
subject to the limitations in paragraph 4(a), below;
|
||||||
|
|
||||||
|
v. rights protecting the extraction, dissemination, use and reuse of data in
|
||||||
|
a Work;
|
||||||
|
|
||||||
|
vi. database rights (such as those arising under Directive 96/9/EC of the
|
||||||
|
European Parliament and of the Council of 11 March 1996 on the legal
|
||||||
|
protection of databases, and under any national implementation thereof,
|
||||||
|
including any amended or successor version of such directive); and
|
||||||
|
|
||||||
|
vii. other similar, equivalent or corresponding rights throughout the world
|
||||||
|
based on applicable law or treaty, and any national implementations thereof.
|
||||||
|
|
||||||
|
2. Waiver. To the greatest extent permitted by, but not in contravention of,
|
||||||
|
applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and
|
||||||
|
unconditionally waives, abandons, and surrenders all of Affirmer's Copyright
|
||||||
|
and Related Rights and associated claims and causes of action, whether now
|
||||||
|
known or unknown (including existing as well as future claims and causes of
|
||||||
|
action), in the Work (i) in all territories worldwide, (ii) for the maximum
|
||||||
|
duration provided by applicable law or treaty (including future time
|
||||||
|
extensions), (iii) in any current or future medium and for any number of
|
||||||
|
copies, and (iv) for any purpose whatsoever, including without limitation
|
||||||
|
commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes
|
||||||
|
the Waiver for the benefit of each member of the public at large and to the
|
||||||
|
detriment of Affirmer's heirs and successors, fully intending that such Waiver
|
||||||
|
shall not be subject to revocation, rescission, cancellation, termination, or
|
||||||
|
any other legal or equitable action to disrupt the quiet enjoyment of the Work
|
||||||
|
by the public as contemplated by Affirmer's express Statement of Purpose.
|
||||||
|
|
||||||
|
3. Public License Fallback. Should any part of the Waiver for any reason be
|
||||||
|
judged legally invalid or ineffective under applicable law, then the Waiver
|
||||||
|
shall be preserved to the maximum extent permitted taking into account
|
||||||
|
Affirmer's express Statement of Purpose. In addition, to the extent the Waiver
|
||||||
|
is so judged Affirmer hereby grants to each affected person a royalty-free,
|
||||||
|
non transferable, non sublicensable, non exclusive, irrevocable and
|
||||||
|
unconditional license to exercise Affirmer's Copyright and Related Rights in
|
||||||
|
the Work (i) in all territories worldwide, (ii) for the maximum duration
|
||||||
|
provided by applicable law or treaty (including future time extensions), (iii)
|
||||||
|
in any current or future medium and for any number of copies, and (iv) for any
|
||||||
|
purpose whatsoever, including without limitation commercial, advertising or
|
||||||
|
promotional purposes (the "License"). The License shall be deemed effective as
|
||||||
|
of the date CC0 was applied by Affirmer to the Work. Should any part of the
|
||||||
|
License for any reason be judged legally invalid or ineffective under
|
||||||
|
applicable law, such partial invalidity or ineffectiveness shall not
|
||||||
|
invalidate the remainder of the License, and in such case Affirmer hereby
|
||||||
|
affirms that he or she will not (i) exercise any of his or her remaining
|
||||||
|
Copyright and Related Rights in the Work or (ii) assert any associated claims
|
||||||
|
and causes of action with respect to the Work, in either case contrary to
|
||||||
|
Affirmer's express Statement of Purpose.
|
||||||
|
|
||||||
|
4. Limitations and Disclaimers.
|
||||||
|
|
||||||
|
a. No trademark or patent rights held by Affirmer are waived, abandoned,
|
||||||
|
surrendered, licensed or otherwise affected by this document.
|
||||||
|
|
||||||
|
b. Affirmer offers the Work as-is and makes no representations or warranties
|
||||||
|
of any kind concerning the Work, express, implied, statutory or otherwise,
|
||||||
|
including without limitation warranties of title, merchantability, fitness
|
||||||
|
for a particular purpose, non infringement, or the absence of latent or
|
||||||
|
other defects, accuracy, or the present or absence of errors, whether or not
|
||||||
|
discoverable, all to the greatest extent permissible under applicable law.
|
||||||
|
|
||||||
|
c. Affirmer disclaims responsibility for clearing rights of other persons
|
||||||
|
that may apply to the Work or any use thereof, including without limitation
|
||||||
|
any person's Copyright and Related Rights in the Work. Further, Affirmer
|
||||||
|
disclaims responsibility for obtaining any necessary consents, permissions
|
||||||
|
or other rights required for any use of the Work.
|
||||||
|
|
||||||
|
d. Affirmer understands and acknowledges that Creative Commons is not a
|
||||||
|
party to this document and has no duty or obligation with respect to this
|
||||||
|
CC0 or use of the Work.
|
||||||
|
|
||||||
|
For more information, please see
|
||||||
|
<http://creativecommons.org/publicdomain/zero/1.0/>
|
49
README.md
Normal file
49
README.md
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
[](https://travis-ci.org/jsmaniac/remember)
|
||||||
|
[](https://codecov.io/gh/jsmaniac/remember)
|
||||||
|
[](http://jsmaniac.github.io/travis-stats/#jsmaniac/remember)
|
||||||
|
[](http://docs.racket-lang.org/remember/)
|
||||||
|
[](https://github.com/jsmaniac/remember/issues)
|
||||||
|
[](https://creativecommons.org/publicdomain/zero/1.0/)
|
||||||
|
|
||||||
|
remember
|
||||||
|
========
|
||||||
|
|
||||||
|
This Racket library provides a compile-time memoize feature. It allows
|
||||||
|
remembering a value with `(remember-write! 'category 'value)`. In subsequent
|
||||||
|
compilations, `(get-remembered 'category)` will return a set of all
|
||||||
|
previously-remembered values.
|
||||||
|
|
||||||
|
Installation
|
||||||
|
============
|
||||||
|
|
||||||
|
raco pkg install remember
|
||||||
|
|
||||||
|
Example use case: the `phc-adt` library
|
||||||
|
=======================================
|
||||||
|
|
||||||
|
This library is used to implement "interned" structure and constructor types
|
||||||
|
in the [`phc-adt`](https://github.com/jsmaniac/phc-adt) library. The `phc-adt`
|
||||||
|
library needs to know the set of all structure and constructor types used in
|
||||||
|
the program, and uses `remember` to automatically memoize structure
|
||||||
|
descriptors and constructor names.
|
||||||
|
|
||||||
|
When the `structure` macro defined in
|
||||||
|
[`structure.hl.rkt`](https://github.com/jsmaniac/phc-adt/blob/refactor/structure.hl.rkt)
|
||||||
|
encounters an unknown list of field names, it uses the `remember` library to
|
||||||
|
append the tuple of field names to a user-specified file. That file is loaded
|
||||||
|
in subsequent compilations, so that the tuple of fields is known to `phc-adt`.
|
||||||
|
|
||||||
|
The memoized descriptors are used to know all possible structs that can
|
||||||
|
contain a field with the desired name when accessing it with `(get instance
|
||||||
|
field-name)`. The `get` macro can then retrieve the field's value using the
|
||||||
|
right accessor (for example `(struct123-fieldname instance)`). Knowing all
|
||||||
|
existing structures allows `get` to perform some kind of dynamic dispatch to
|
||||||
|
obtain the appropriate accessor, for example using a `cond` which tests for
|
||||||
|
all possible types.
|
||||||
|
|
||||||
|
The `constructor` macro defined in
|
||||||
|
[`constructor.hl.rkt`](https://github.com/jsmaniac/phc-adt/blob/refactor/constructor.hl.rkt)
|
||||||
|
works in the same way, but remembers the name of the constructor's tag instead
|
||||||
|
of field names. The memoization feature is used so that all uses of a
|
||||||
|
constructor with a given name are equivalent, across all files.
|
||||||
|
|
22
info.rkt
Normal file
22
info.rkt
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
#lang info
|
||||||
|
(define collection "remember")
|
||||||
|
(define deps '("base"
|
||||||
|
"rackunit-lib"
|
||||||
|
"compatibility-lib"
|
||||||
|
"scribble-lib"
|
||||||
|
"typed-racket-lib"
|
||||||
|
"phc-toolkit"
|
||||||
|
"hyper-literate"))
|
||||||
|
(define build-deps '("scribble-lib"
|
||||||
|
"racket-doc"
|
||||||
|
"typed-racket-doc"
|
||||||
|
"scribble-enhanced"))
|
||||||
|
(define scribblings '(("scribblings/remember.scrbl" ())
|
||||||
|
("remember-implementation.hl.rkt" () (omit-start))))
|
||||||
|
(define compile-omit-paths '("test/test-error.rkt"))
|
||||||
|
(define test-omit-paths '("test/test-error.rkt"))
|
||||||
|
(define pkg-desc (string-append "Compile-time memoize across compilations."
|
||||||
|
" Writes values to a file, so that they will"
|
||||||
|
" be remembered during the next compilation."))
|
||||||
|
(define version "0.9")
|
||||||
|
(define pkg-authors '(|Georges Dupéron|))
|
19
licenses/bsd.txt
Normal file
19
licenses/bsd.txt
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
Copyright (c) 2000-2015 Dipanwita Sarkar, Andrew W. Keep, R. Kent Dybvig, Oscar Waddell
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
copy of this software and associated documentation files (the "Software"),
|
||||||
|
to deal in the Software without restriction, including without limitation
|
||||||
|
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
DEALINGS IN THE SOFTWARE.
|
165
licenses/lgpl-3.0--license.txt
Normal file
165
licenses/lgpl-3.0--license.txt
Normal file
|
@ -0,0 +1,165 @@
|
||||||
|
GNU LESSER GENERAL PUBLIC LICENSE
|
||||||
|
Version 3, 29 June 2007
|
||||||
|
|
||||||
|
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
|
||||||
|
This version of the GNU Lesser General Public License incorporates
|
||||||
|
the terms and conditions of version 3 of the GNU General Public
|
||||||
|
License, supplemented by the additional permissions listed below.
|
||||||
|
|
||||||
|
0. Additional Definitions.
|
||||||
|
|
||||||
|
As used herein, "this License" refers to version 3 of the GNU Lesser
|
||||||
|
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
||||||
|
General Public License.
|
||||||
|
|
||||||
|
"The Library" refers to a covered work governed by this License,
|
||||||
|
other than an Application or a Combined Work as defined below.
|
||||||
|
|
||||||
|
An "Application" is any work that makes use of an interface provided
|
||||||
|
by the Library, but which is not otherwise based on the Library.
|
||||||
|
Defining a subclass of a class defined by the Library is deemed a mode
|
||||||
|
of using an interface provided by the Library.
|
||||||
|
|
||||||
|
A "Combined Work" is a work produced by combining or linking an
|
||||||
|
Application with the Library. The particular version of the Library
|
||||||
|
with which the Combined Work was made is also called the "Linked
|
||||||
|
Version".
|
||||||
|
|
||||||
|
The "Minimal Corresponding Source" for a Combined Work means the
|
||||||
|
Corresponding Source for the Combined Work, excluding any source code
|
||||||
|
for portions of the Combined Work that, considered in isolation, are
|
||||||
|
based on the Application, and not on the Linked Version.
|
||||||
|
|
||||||
|
The "Corresponding Application Code" for a Combined Work means the
|
||||||
|
object code and/or source code for the Application, including any data
|
||||||
|
and utility programs needed for reproducing the Combined Work from the
|
||||||
|
Application, but excluding the System Libraries of the Combined Work.
|
||||||
|
|
||||||
|
1. Exception to Section 3 of the GNU GPL.
|
||||||
|
|
||||||
|
You may convey a covered work under sections 3 and 4 of this License
|
||||||
|
without being bound by section 3 of the GNU GPL.
|
||||||
|
|
||||||
|
2. Conveying Modified Versions.
|
||||||
|
|
||||||
|
If you modify a copy of the Library, and, in your modifications, a
|
||||||
|
facility refers to a function or data to be supplied by an Application
|
||||||
|
that uses the facility (other than as an argument passed when the
|
||||||
|
facility is invoked), then you may convey a copy of the modified
|
||||||
|
version:
|
||||||
|
|
||||||
|
a) under this License, provided that you make a good faith effort to
|
||||||
|
ensure that, in the event an Application does not supply the
|
||||||
|
function or data, the facility still operates, and performs
|
||||||
|
whatever part of its purpose remains meaningful, or
|
||||||
|
|
||||||
|
b) under the GNU GPL, with none of the additional permissions of
|
||||||
|
this License applicable to that copy.
|
||||||
|
|
||||||
|
3. Object Code Incorporating Material from Library Header Files.
|
||||||
|
|
||||||
|
The object code form of an Application may incorporate material from
|
||||||
|
a header file that is part of the Library. You may convey such object
|
||||||
|
code under terms of your choice, provided that, if the incorporated
|
||||||
|
material is not limited to numerical parameters, data structure
|
||||||
|
layouts and accessors, or small macros, inline functions and templates
|
||||||
|
(ten or fewer lines in length), you do both of the following:
|
||||||
|
|
||||||
|
a) Give prominent notice with each copy of the object code that the
|
||||||
|
Library is used in it and that the Library and its use are
|
||||||
|
covered by this License.
|
||||||
|
|
||||||
|
b) Accompany the object code with a copy of the GNU GPL and this license
|
||||||
|
document.
|
||||||
|
|
||||||
|
4. Combined Works.
|
||||||
|
|
||||||
|
You may convey a Combined Work under terms of your choice that,
|
||||||
|
taken together, effectively do not restrict modification of the
|
||||||
|
portions of the Library contained in the Combined Work and reverse
|
||||||
|
engineering for debugging such modifications, if you also do each of
|
||||||
|
the following:
|
||||||
|
|
||||||
|
a) Give prominent notice with each copy of the Combined Work that
|
||||||
|
the Library is used in it and that the Library and its use are
|
||||||
|
covered by this License.
|
||||||
|
|
||||||
|
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
||||||
|
document.
|
||||||
|
|
||||||
|
c) For a Combined Work that displays copyright notices during
|
||||||
|
execution, include the copyright notice for the Library among
|
||||||
|
these notices, as well as a reference directing the user to the
|
||||||
|
copies of the GNU GPL and this license document.
|
||||||
|
|
||||||
|
d) Do one of the following:
|
||||||
|
|
||||||
|
0) Convey the Minimal Corresponding Source under the terms of this
|
||||||
|
License, and the Corresponding Application Code in a form
|
||||||
|
suitable for, and under terms that permit, the user to
|
||||||
|
recombine or relink the Application with a modified version of
|
||||||
|
the Linked Version to produce a modified Combined Work, in the
|
||||||
|
manner specified by section 6 of the GNU GPL for conveying
|
||||||
|
Corresponding Source.
|
||||||
|
|
||||||
|
1) Use a suitable shared library mechanism for linking with the
|
||||||
|
Library. A suitable mechanism is one that (a) uses at run time
|
||||||
|
a copy of the Library already present on the user's computer
|
||||||
|
system, and (b) will operate properly with a modified version
|
||||||
|
of the Library that is interface-compatible with the Linked
|
||||||
|
Version.
|
||||||
|
|
||||||
|
e) Provide Installation Information, but only if you would otherwise
|
||||||
|
be required to provide such information under section 6 of the
|
||||||
|
GNU GPL, and only to the extent that such information is
|
||||||
|
necessary to install and execute a modified version of the
|
||||||
|
Combined Work produced by recombining or relinking the
|
||||||
|
Application with a modified version of the Linked Version. (If
|
||||||
|
you use option 4d0, the Installation Information must accompany
|
||||||
|
the Minimal Corresponding Source and Corresponding Application
|
||||||
|
Code. If you use option 4d1, you must provide the Installation
|
||||||
|
Information in the manner specified by section 6 of the GNU GPL
|
||||||
|
for conveying Corresponding Source.)
|
||||||
|
|
||||||
|
5. Combined Libraries.
|
||||||
|
|
||||||
|
You may place library facilities that are a work based on the
|
||||||
|
Library side by side in a single library together with other library
|
||||||
|
facilities that are not Applications and are not covered by this
|
||||||
|
License, and convey such a combined library under terms of your
|
||||||
|
choice, if you do both of the following:
|
||||||
|
|
||||||
|
a) Accompany the combined library with a copy of the same work based
|
||||||
|
on the Library, uncombined with any other library facilities,
|
||||||
|
conveyed under the terms of this License.
|
||||||
|
|
||||||
|
b) Give prominent notice with the combined library that part of it
|
||||||
|
is a work based on the Library, and explaining where to find the
|
||||||
|
accompanying uncombined form of the same work.
|
||||||
|
|
||||||
|
6. Revised Versions of the GNU Lesser General Public License.
|
||||||
|
|
||||||
|
The Free Software Foundation may publish revised and/or new versions
|
||||||
|
of the GNU Lesser 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
|
||||||
|
Library as you received it specifies that a certain numbered version
|
||||||
|
of the GNU Lesser General Public License "or any later version"
|
||||||
|
applies to it, you have the option of following the terms and
|
||||||
|
conditions either of that published version or of any later version
|
||||||
|
published by the Free Software Foundation. If the Library as you
|
||||||
|
received it does not specify a version number of the GNU Lesser
|
||||||
|
General Public License, you may choose any version of the GNU Lesser
|
||||||
|
General Public License ever published by the Free Software Foundation.
|
||||||
|
|
||||||
|
If the Library as you received it specifies that a proxy can decide
|
||||||
|
whether future versions of the GNU Lesser General Public License shall
|
||||||
|
apply, that proxy's public statement of acceptance of any version is
|
||||||
|
permanent authorization for you to choose that version for the
|
||||||
|
Library.
|
3
main.rkt
Normal file
3
main.rkt
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
#lang racket
|
||||||
|
(require "remember-implementation.hl.rkt")
|
||||||
|
(provide (all-from-out "remember-implementation.hl.rkt"))
|
303
remember-implementation.hl.rkt
Normal file
303
remember-implementation.hl.rkt
Normal file
|
@ -0,0 +1,303 @@
|
||||||
|
#lang hyper-literate racket/base
|
||||||
|
@(require scribble-enhanced/doc)
|
||||||
|
@doc-lib-setup
|
||||||
|
|
||||||
|
@title[#:style manual-doc-style
|
||||||
|
#:tag "remember"
|
||||||
|
#:tag-prefix "(lib remember/remember-implementation.hl.rkt)"
|
||||||
|
]{Implementation of Remember}
|
||||||
|
|
||||||
|
@(chunks-toc-prefix
|
||||||
|
'("(lib remember/remember-implementation.hl.rkt)"))
|
||||||
|
|
||||||
|
@(table-of-contents)
|
||||||
|
|
||||||
|
@section{@racket[remember]}
|
||||||
|
|
||||||
|
This module allows macros to remember some values across
|
||||||
|
compilations. Values are stored within the
|
||||||
|
@tc[remembered-values] hash table, which associates a
|
||||||
|
@racket[_category] (a symbol) with a set of values.
|
||||||
|
|
||||||
|
@chunk[<remembered-values>
|
||||||
|
(begin-for-syntax
|
||||||
|
(define remembered-values (make-hash)))]
|
||||||
|
|
||||||
|
A second set tracks values which were recently written, but
|
||||||
|
not initially added via @racket[remembered!] or
|
||||||
|
@racket[remembered-add!].
|
||||||
|
|
||||||
|
@chunk[<remembered-values>
|
||||||
|
(begin-for-syntax
|
||||||
|
(define written-values (make-hash)))]
|
||||||
|
|
||||||
|
The user can specify input files from which remembered
|
||||||
|
values are loaded, and optionally an output file to which
|
||||||
|
new, not-yet-remembered values will be appended:
|
||||||
|
|
||||||
|
@CHUNK[<remember-file>
|
||||||
|
(define-for-syntax remember-output-file-parameter
|
||||||
|
(make-parameter #f (or? path-string? false?)))
|
||||||
|
|
||||||
|
(define-syntax (remember-output-file stx)
|
||||||
|
(syntax-case stx ()
|
||||||
|
[(_ new-value)
|
||||||
|
(string? (syntax-e #'new-value))
|
||||||
|
(begin (remember-output-file-parameter (syntax-e #'new-value))
|
||||||
|
#'(void))]
|
||||||
|
[(_)
|
||||||
|
(quasisyntax/loc stx remember-output-file-parameter)]))
|
||||||
|
|
||||||
|
(define-syntax (remember-input-file stx)
|
||||||
|
(syntax-case stx ()
|
||||||
|
[(_ name)
|
||||||
|
(string? (syntax-e #'name))
|
||||||
|
#'(require (only-in name))]))
|
||||||
|
|
||||||
|
(define-syntax-rule (remember-io-file name)
|
||||||
|
(begin (remember-input-file name)
|
||||||
|
(remember-output-file name)))]
|
||||||
|
|
||||||
|
@CHUNK[<remember>
|
||||||
|
(define-syntax-rule (remembered! category value)
|
||||||
|
(begin-for-syntax
|
||||||
|
(remembered-add! 'category 'value)))
|
||||||
|
|
||||||
|
(define-for-syntax writable?
|
||||||
|
(disjoin number?
|
||||||
|
string?
|
||||||
|
symbol?
|
||||||
|
char?
|
||||||
|
null?
|
||||||
|
(λ (v) (and (pair? v)
|
||||||
|
(writable? (car v))
|
||||||
|
(writable? (cdr v))))
|
||||||
|
(λ (v) (and (vector? v)
|
||||||
|
(andmap writable? (vector->list v))))))
|
||||||
|
|
||||||
|
(define-for-syntax (remembered-add! category value)
|
||||||
|
(unless (writable? value)
|
||||||
|
(error "Value to remember does not seem to be safely writable:"
|
||||||
|
value))
|
||||||
|
(unless (symbol? category)
|
||||||
|
(error (format "The category was not a symbol, when remembering ~a:"
|
||||||
|
value)
|
||||||
|
category))
|
||||||
|
(hash-update! remembered-values
|
||||||
|
category
|
||||||
|
(λ (s) (set-add s value))
|
||||||
|
set))
|
||||||
|
|
||||||
|
(define-for-syntax (remembered-add-written! category value)
|
||||||
|
(unless (writable? value)
|
||||||
|
(error "Value to remember does not seem to be safely writable:"
|
||||||
|
value))
|
||||||
|
(unless (symbol? category)
|
||||||
|
(error (format "The category was not a symbol, when remembering ~a:"
|
||||||
|
value)
|
||||||
|
category))
|
||||||
|
(hash-update! written-values
|
||||||
|
category
|
||||||
|
(λ (s) (set-add s value))
|
||||||
|
set))
|
||||||
|
|
||||||
|
(define-for-syntax (remembered? category value)
|
||||||
|
(unless (writable? value)
|
||||||
|
(error "Value to remember does not seem to be safely writable:"
|
||||||
|
value))
|
||||||
|
(set-member? (hash-ref remembered-values category set) value))
|
||||||
|
|
||||||
|
(define-for-syntax (written? category value)
|
||||||
|
(unless (writable? value)
|
||||||
|
(error "Value to remember does not seem to be safely writable:"
|
||||||
|
value))
|
||||||
|
(set-member? (hash-ref written-values category set) value))
|
||||||
|
|
||||||
|
(define-for-syntax (remembered-or-written? category value)
|
||||||
|
(or (remembered? category value)
|
||||||
|
(written? category value)))
|
||||||
|
|
||||||
|
(define-for-syntax (remember-write! category value)
|
||||||
|
(unless (writable? value)
|
||||||
|
(error "Value to remember does not seem to be safely writable:"
|
||||||
|
value))
|
||||||
|
(unless (or (remembered? category value)
|
||||||
|
(written? category value))
|
||||||
|
(when (remember-output-file-parameter)
|
||||||
|
(with-output-file [port (remember-output-file-parameter)]
|
||||||
|
#:exists 'append
|
||||||
|
(writeln (list 'remembered! category value)
|
||||||
|
port)))
|
||||||
|
(remembered-add-written! category value)))]
|
||||||
|
|
||||||
|
@chunk[<delayed-errors>
|
||||||
|
(begin-for-syntax
|
||||||
|
(define remember-errors-list '())
|
||||||
|
(define remember-lifted-error #f))]
|
||||||
|
|
||||||
|
@chunk[<error>
|
||||||
|
(define-for-syntax (remembered-error! category
|
||||||
|
stx-value
|
||||||
|
[stx-errs (list stx-value)])
|
||||||
|
(set! remember-errors-list
|
||||||
|
(cons (list category stx-value stx-errs) remember-errors-list))
|
||||||
|
|
||||||
|
(unless (disable-remember-immediate-error)
|
||||||
|
(if (not (syntax-local-lift-context))
|
||||||
|
;; Trigger the error right now
|
||||||
|
(remember-all-hard-error)
|
||||||
|
;; Lift a delayed error, which will be triggered later on
|
||||||
|
(lift-maybe-delayed-errors))))
|
||||||
|
|
||||||
|
(define-for-syntax (remembered-add-error! category stx-value)
|
||||||
|
(remembered-add! category (syntax-e stx-value))
|
||||||
|
(remembered-error! category stx-value))]
|
||||||
|
|
||||||
|
@CHUNK[<remember-all-hard-error>
|
||||||
|
;; These two functions allow us to wait around 1000 levels of nested
|
||||||
|
;; macro-expansion before triggering the error.
|
||||||
|
;; If the error is triggered immediately when the lifted statements are
|
||||||
|
;; added at the end of the module, then it can get executed before macros
|
||||||
|
;; used in the righ-hand side of a (define …) are expanded, for example.
|
||||||
|
;; Since these macros may need to remember more values, it's better to
|
||||||
|
;; wait until they are all expanded.
|
||||||
|
;; The number 1000 above in #`(delay-remember-all-hard-error1 1000) is
|
||||||
|
;; arbitrary, but should be enough for most practical purposes, worst
|
||||||
|
;; case the file would require a few more compilations to settle.
|
||||||
|
(define-syntax (delay-remember-all-hard-error1 stx)
|
||||||
|
(syntax-case stx ()
|
||||||
|
[(_ n)
|
||||||
|
(number? (syntax-e #'n))
|
||||||
|
(if (> (syntax-e #'n) 0)
|
||||||
|
#`(let ()
|
||||||
|
(define blob
|
||||||
|
(delay-remember-all-hard-error2 #,(- (syntax-e #'n) 1)))
|
||||||
|
(void))
|
||||||
|
(begin (syntax-local-lift-module-end-declaration
|
||||||
|
#`(remember-all-hard-error-macro))
|
||||||
|
#'(void)))]))
|
||||||
|
|
||||||
|
(define-syntax (delay-remember-all-hard-error2 stx)
|
||||||
|
(syntax-case stx ()
|
||||||
|
[(_ n)
|
||||||
|
(number? (syntax-e #'n))
|
||||||
|
(begin
|
||||||
|
(syntax-local-lift-module-end-declaration
|
||||||
|
#'(delay-remember-all-hard-error1 n))
|
||||||
|
#'n)]))
|
||||||
|
|
||||||
|
(define-for-syntax (remember-all-hard-error)
|
||||||
|
(define remember-errors-list-orig remember-errors-list)
|
||||||
|
(set! remember-errors-list '())
|
||||||
|
(unless (empty? remember-errors-list-orig)
|
||||||
|
(raise-syntax-error
|
||||||
|
'remember
|
||||||
|
(format (~a "The values ~a were not remembered."
|
||||||
|
" Some of them may have been added to the"
|
||||||
|
" appropriate list automatically."
|
||||||
|
" Please recompile this file now.")
|
||||||
|
(string-join (remove-duplicates
|
||||||
|
(reverse
|
||||||
|
(stx-map (compose ~a syntax->datum)
|
||||||
|
(map cadr
|
||||||
|
remember-errors-list-orig))))
|
||||||
|
", "))
|
||||||
|
#f
|
||||||
|
#f
|
||||||
|
(remove-duplicates
|
||||||
|
(append-map caddr remember-errors-list-orig)
|
||||||
|
#:key (λ (e)
|
||||||
|
(cons (syntax->datum e)
|
||||||
|
(build-source-location-list e)))))))
|
||||||
|
(define-syntax (remember-all-hard-error-macro stx)
|
||||||
|
(remember-all-hard-error)
|
||||||
|
#'(void))]
|
||||||
|
|
||||||
|
The @racket[disable-remember-immediate-error] parameter allows code to
|
||||||
|
temporarily prevent @racket[remembered-error!] from lifting a delayed error.
|
||||||
|
This can be useful for example when calling @racket[remembered-error!] from a
|
||||||
|
context where @racket[(syntax-local-lift-context)] is @racket[#false], e.g.
|
||||||
|
outside of the expansion of a macro, but within a @racket[begin-for-syntax]
|
||||||
|
block.
|
||||||
|
|
||||||
|
@chunk[<disable-remember-errors>
|
||||||
|
(define-for-syntax disable-remember-immediate-error (make-parameter #f))]
|
||||||
|
|
||||||
|
The error is still put aside, so that if a delayed error was triggered by
|
||||||
|
another call to @racket[remembered-error!], the error will still be included
|
||||||
|
with the other delayed errors. If no delayed error is triggered during
|
||||||
|
macro-expansion, the error that was put aside will be ignored. To prevent
|
||||||
|
that, the user can call @racket[lift-maybe-delayed-errors] within a context
|
||||||
|
where lifts are possible.
|
||||||
|
|
||||||
|
@chunk[<lift-maybe-delayed-errors>
|
||||||
|
(define-for-syntax (lift-maybe-delayed-errors)
|
||||||
|
(if (syntax-transforming-module-expression?)
|
||||||
|
;; Lift a delayed error, attempting to allow several (1000) levels
|
||||||
|
;; of nested let blocks to expand before pulling the alarm signal.
|
||||||
|
(unless remember-lifted-error
|
||||||
|
(set! remember-lifted-error #t)
|
||||||
|
(syntax-local-lift-module-end-declaration
|
||||||
|
#`(delay-remember-all-hard-error1 1000)))
|
||||||
|
;; Lift a delayed error, which will be triggered after the current
|
||||||
|
;; expansion pass (i.e. before the contents of any let form is
|
||||||
|
;; expanded).
|
||||||
|
(syntax-local-lift-expression
|
||||||
|
#`(remember-all-hard-error-macro))))]
|
||||||
|
|
||||||
|
|
||||||
|
@CHUNK[<get-remembered>
|
||||||
|
(define-for-syntax (get-remembered category)
|
||||||
|
(hash-ref remembered-values category set))]
|
||||||
|
|
||||||
|
@chunk[<provide>
|
||||||
|
(begin-for-syntax
|
||||||
|
(provide get-remembered
|
||||||
|
remembered-add!
|
||||||
|
remembered?
|
||||||
|
remembered-or-written?
|
||||||
|
remember-write!
|
||||||
|
remembered-error!
|
||||||
|
remember-output-file-parameter
|
||||||
|
disable-remember-immediate-error
|
||||||
|
lift-maybe-delayed-errors))
|
||||||
|
(provide remember-input-file
|
||||||
|
remember-output-file
|
||||||
|
remember-io-file
|
||||||
|
remembered!)
|
||||||
|
|
||||||
|
(module+ private
|
||||||
|
(begin-for-syntax
|
||||||
|
(provide remembered-add-written!)))]
|
||||||
|
|
||||||
|
@; TODO: circumvents bug https://github.com/racket/scribble/issues/44
|
||||||
|
@(require racket/require)
|
||||||
|
@chunk[<*>
|
||||||
|
(require mzlib/etc
|
||||||
|
;; TODO: circumvent https://github.com/racket/scribble/issues/44
|
||||||
|
racket/require
|
||||||
|
(subtract-in phc-toolkit/untyped syntax/stx)
|
||||||
|
syntax/stx
|
||||||
|
(for-syntax racket/base
|
||||||
|
racket/function
|
||||||
|
racket/bool
|
||||||
|
racket/set
|
||||||
|
racket/list
|
||||||
|
mzlib/etc
|
||||||
|
;;TODO: https://github.com/racket/scribble/issues/44
|
||||||
|
(subtract-in phc-toolkit/untyped
|
||||||
|
syntax/stx)
|
||||||
|
syntax/stx
|
||||||
|
syntax/srcloc
|
||||||
|
racket/string
|
||||||
|
racket/format))
|
||||||
|
<provide>
|
||||||
|
<remembered-values>
|
||||||
|
<remember-file>
|
||||||
|
<remember>
|
||||||
|
<get-remembered>
|
||||||
|
<delayed-errors>
|
||||||
|
<disable-remember-errors>
|
||||||
|
<lift-maybe-delayed-errors>
|
||||||
|
<remember-all-hard-error>
|
||||||
|
<error>]
|
258
scribblings/remember.scrbl
Normal file
258
scribblings/remember.scrbl
Normal file
|
@ -0,0 +1,258 @@
|
||||||
|
#lang scribble/manual
|
||||||
|
@require[@for-label[remember
|
||||||
|
racket/base]]
|
||||||
|
|
||||||
|
@title{Remember: storage for macros which is persistant across compilations}
|
||||||
|
@author{Georges Dupéron}
|
||||||
|
|
||||||
|
@defmodule[remember]
|
||||||
|
|
||||||
|
This library is implemented using literate programming. The
|
||||||
|
implementation details are presented in
|
||||||
|
@other-doc['(lib "remember/remember-implementation.hl.rkt")].
|
||||||
|
|
||||||
|
This module allows macros to remember some values across
|
||||||
|
compilations. Values are grouped by @racket[_category], so
|
||||||
|
that multiple macros can use this facility without
|
||||||
|
interfering with each other. The @racket[_category] is
|
||||||
|
simply a symbol given when remembering the value.
|
||||||
|
|
||||||
|
The list of all remembered values for a given
|
||||||
|
@racket[_category] is returned by @racket[get-remembered],
|
||||||
|
and it is possible to check if a single value has been
|
||||||
|
remembered using @racket[remembered?].
|
||||||
|
|
||||||
|
Values are loaded from files using
|
||||||
|
@racket[remember-input-file] and @racket[remember-io-file].
|
||||||
|
An output file can be set with
|
||||||
|
@racket[remember-output-file] and
|
||||||
|
@racket[remember-io-file].
|
||||||
|
|
||||||
|
When an output file has been declared, new values passed to
|
||||||
|
@racket[remember-write!] are marked as
|
||||||
|
@racket[remembered-or-written?] and appended to that file
|
||||||
|
(more precisely, the expression
|
||||||
|
@racket[(remembered! _category _value)] is appended to the
|
||||||
|
file, followed by a newline).
|
||||||
|
|
||||||
|
When initially created by the user, the output file should
|
||||||
|
contain the code below, which will be followed by the
|
||||||
|
automatically-generated
|
||||||
|
@racket[(remembered! _category _value)] statements:
|
||||||
|
|
||||||
|
@codeblock[#:keep-lang-line? #t]|{
|
||||||
|
#lang racket
|
||||||
|
(require remember)}|
|
||||||
|
|
||||||
|
The @racket[remembered!] macro indicates an
|
||||||
|
already-remembered value, and is typically used inside input
|
||||||
|
files. The @racket[for-syntax] function
|
||||||
|
@racket[remembered-add!] can also be used instead, to mark a
|
||||||
|
value as @racket[remembered?] without adding it to any file
|
||||||
|
(this can be useful for values which should implicitly be
|
||||||
|
remembered).
|
||||||
|
|
||||||
|
@defproc[#:kind "for-syntax procedure"
|
||||||
|
(get-remembered [category symbol?]) list?]{
|
||||||
|
Returns a list of all values that have been remembered for
|
||||||
|
the given @racket[category] (i.e. all values passed as the
|
||||||
|
second argument to @racket[remembered-add!],
|
||||||
|
@racket[remember-write!] or @racket[remembered!], with the given
|
||||||
|
category as the first argument).}
|
||||||
|
|
||||||
|
@defproc[#:kind "for-syntax procedure"
|
||||||
|
(remembered-add! [category symbol?] [value any/c]) void?]{
|
||||||
|
Marks the given @racket[value] as remembered in the given
|
||||||
|
@racket[category]. If the same value is remembered twice
|
||||||
|
for the same category, the second occurrence is ignored
|
||||||
|
(i.e. values are stored in a distinct @racket[set] for each
|
||||||
|
category).
|
||||||
|
|
||||||
|
This @racket[for-syntax] procedure is called by the
|
||||||
|
@racket[remembered!] macro, but can also be executed on its
|
||||||
|
own.}
|
||||||
|
|
||||||
|
@defproc[#:kind "for-syntax procedure"
|
||||||
|
(remembered? [category symbol?] [value any/c]) boolean?]{
|
||||||
|
Checks whether the given @racket[value] has already been
|
||||||
|
added to the set of remembered values for the given
|
||||||
|
@racket[category].}
|
||||||
|
|
||||||
|
@defproc[#:kind "for-syntax procedure"
|
||||||
|
(remembered-or-written? [category symbol?] [value any/c]) boolean?]{
|
||||||
|
Checks whether the given @racket[value] has already been
|
||||||
|
added to the set of remembered values for the given
|
||||||
|
@racket[category], or if it was freshly written to a file
|
||||||
|
during the current expansion.}
|
||||||
|
|
||||||
|
@defproc[#:kind "for-syntax procedure"
|
||||||
|
(remember-write! [category symbol?] [value any/c]) void?]{
|
||||||
|
Adds the given @racket[value] to the current
|
||||||
|
@racket[remember-output-file] for the given category. More
|
||||||
|
precisely, the expression
|
||||||
|
@racket[(remembered! category value)] is appended to the
|
||||||
|
file, followed by a newline.
|
||||||
|
|
||||||
|
If the value is already @racket[remembered-or-written?],
|
||||||
|
then the file is left unchanged, i.e. two or more calls to
|
||||||
|
@racket[remember-write!] with the same @racket[category]
|
||||||
|
and @racket[value] will only append an expression to the
|
||||||
|
file the first time.
|
||||||
|
|
||||||
|
The value is also added to the set of
|
||||||
|
@racket[remembered-or-written?] values, so that subsequent
|
||||||
|
calls to @racket[remembered-or-written?] return
|
||||||
|
@racket[#t] for that category and value. Calls to
|
||||||
|
@racket[remembered?] will be unaffected, and will still
|
||||||
|
return @racket[#f]. If some declarations are created by a
|
||||||
|
library based on the @racket[get-remembered] set, it is
|
||||||
|
therefore possible to check whether a value was already
|
||||||
|
present, or if it was added by a subsequent
|
||||||
|
@racket[remember-write!].}
|
||||||
|
|
||||||
|
@defproc[#:kind "for-syntax procedure"
|
||||||
|
(remembered-error! [category symbol] [stx-value syntax?]) void?]{
|
||||||
|
Produces a delayed error indicating that this value has
|
||||||
|
not been remembered, but was added to the output file.
|
||||||
|
|
||||||
|
This procedure just triggers the error, and is not
|
||||||
|
concerned with actually adding the value to the output
|
||||||
|
file.
|
||||||
|
|
||||||
|
The error is added in a lifted declaration which is
|
||||||
|
inserted at the end of the current module, using
|
||||||
|
@racket[syntax-local-lift-module-end-declaration]. It
|
||||||
|
should therefore be triggered only when the compilation
|
||||||
|
reaches the end of the file, if no other error was raised
|
||||||
|
before.
|
||||||
|
|
||||||
|
This allows as many @racket[remembered-error!] errors as
|
||||||
|
possible to be accumulated; all of these are then shown
|
||||||
|
when the file is fully expanded. The goal is to be able to
|
||||||
|
add all values to the output file in a single run, instead
|
||||||
|
of aborting after each value which is not remembered. This
|
||||||
|
would otherwise require recompiling the program once for
|
||||||
|
each value which is not initially remembered.
|
||||||
|
|
||||||
|
TODO: it would be nice to factor out the delayed error
|
||||||
|
mechanism into a separate package, so that multiple
|
||||||
|
libraries can add errors, and all of them get reported,
|
||||||
|
without one preventing the others from executing. This
|
||||||
|
function would likely keep the same signature, and just
|
||||||
|
delegate to the delayed-error library.}
|
||||||
|
|
||||||
|
@defparam[disable-remember-immediate-error disable? boolean? #:value #f]{
|
||||||
|
The @racket[disable-remember-immediate-error] parameter allows code to
|
||||||
|
temporarily prevent @racket[remembered-error!] from lifting a delayed error.
|
||||||
|
This can be useful for example when calling @racket[remembered-error!] from a
|
||||||
|
context where @racket[(syntax-local-lift-context)] is @racket[#false], e.g.
|
||||||
|
outside of the expansion of a macro, but within a @racket[begin-for-syntax]
|
||||||
|
block.
|
||||||
|
|
||||||
|
The error is still put aside, so that if a delayed error was triggered by
|
||||||
|
another call to @racket[remembered-error!], the error will still be included
|
||||||
|
with the other delayed errors. If no delayed error is triggered during
|
||||||
|
macro-expansion, the error that was put aside will be ignored. To prevent
|
||||||
|
this from happening, call @racket[lift-maybe-delayed-errors] within a context
|
||||||
|
where lifts are possible.}
|
||||||
|
|
||||||
|
@defproc[(lift-maybe-delayed-errors) void?]{
|
||||||
|
Uses @racket[syntax-local-lift-module-end-declaration] or
|
||||||
|
@racket[syntax-local-lift-expression], depending on the context, to lift an
|
||||||
|
expression which will trigger delayed errors, if any. If no delayed errors
|
||||||
|
have been recorded by @racket[remembered-error!] when the lifted form is
|
||||||
|
executed, then nothing will happen and expansion will proceed.
|
||||||
|
|
||||||
|
Note that when @racket[(syntax-transforming-module-expression?)] returns
|
||||||
|
@racket[#false], @racket[syntax-local-lift-expression] is used. The lifted
|
||||||
|
form is then run as part of the current expansion pass, before the contents of
|
||||||
|
any @racket[let] forms are expanded. This means that calls to
|
||||||
|
@racket[remembered-error!] must not happen within the expansion of nested
|
||||||
|
@racket[let] forms (with respect to the @racket[let] form being expanded (if
|
||||||
|
any) when @racket[lift-maybe-delayed-errors] is called), as they would add
|
||||||
|
delayed errors too late, i.e. after the lifted form got executed.}
|
||||||
|
|
||||||
|
@defform[(remember-input-file name)
|
||||||
|
#:grammar ([name string?])]{
|
||||||
|
The file is loaded with @racket[require], but no
|
||||||
|
identifier is imported from that module. Instead,
|
||||||
|
@racket[remembered?] relies on its internal mutable
|
||||||
|
@racket[for-syntax] hash table which stores remembered
|
||||||
|
values associated to their category.
|
||||||
|
|
||||||
|
@racket[remembered-values]. Values are added to the hash
|
||||||
|
via the @racket[remembered!] macro. The @racket[name] file
|
||||||
|
should therefore @racket[require] the
|
||||||
|
@racketmodname[remember] library, and contain a number of
|
||||||
|
calls to @racket[remembered!], each adding a new value to
|
||||||
|
the mutable hash.}
|
||||||
|
|
||||||
|
@deftogether[
|
||||||
|
(@defform*[((remember-output-file)
|
||||||
|
(remember-output-file name))
|
||||||
|
#:grammar ([name (or/c string? false?)])]
|
||||||
|
@defproc*[#:kind "for-syntax parameter"
|
||||||
|
#:link-target? #f
|
||||||
|
([(remember-output-file) (or/c string? false?)]
|
||||||
|
[(remember-output-file [name (or/c string? false?)]) void?])]
|
||||||
|
)]{
|
||||||
|
Indicates that new values added via
|
||||||
|
@racket[remember-write!] should be appended to the file
|
||||||
|
@racket[name]. More precisely, the expression
|
||||||
|
@racket[(remembered! _category _value)] is appended to the
|
||||||
|
file, followed by a newline.
|
||||||
|
|
||||||
|
Note that if the @racket[_value] given to
|
||||||
|
@racket[remember-write!] is already registered in an input
|
||||||
|
file with @racket[remembered!] for the same category, it
|
||||||
|
will not be appended to the output file.
|
||||||
|
|
||||||
|
For now there can only be one @racket[output] file at the
|
||||||
|
same time, any call to @racket[remember-output-file]
|
||||||
|
overrides the setting from previous calls. Future versions
|
||||||
|
of this library may offer the possibility to specify an
|
||||||
|
output file per @racket[_category].
|
||||||
|
|
||||||
|
The special value @racket[#f] indicates that there is no
|
||||||
|
output file, in which case @racket[remember-write!] simply
|
||||||
|
marks the @racket[value] as
|
||||||
|
@racket[remembered-or-written?] for that category, without
|
||||||
|
altering any file.
|
||||||
|
|
||||||
|
This identifier exists both as a macro and a for-syntax
|
||||||
|
parameter. When called without any argument, it expands to
|
||||||
|
(for the macro) or returns (for the for-syntax parameter)
|
||||||
|
the last value set using either the macro or by passing an
|
||||||
|
argument to the for-syntax parameter.}
|
||||||
|
|
||||||
|
@defparam[remember-output-file-parameter output-file
|
||||||
|
(or/c path-string? false?)
|
||||||
|
#:value #f]{
|
||||||
|
This for-syntax parameter that new values added via @racket[remember-write!]
|
||||||
|
should be appended to the file whose name is stored within the parameter.
|
||||||
|
|
||||||
|
The @racket[remember-output-file] macro simply sets this parameter.}
|
||||||
|
|
||||||
|
@defform[(remember-io-file name)
|
||||||
|
#:grammar ([name string?])]{
|
||||||
|
Indicates that calls to @racket[remembered!] in this file
|
||||||
|
should be taken into account, and that new values added
|
||||||
|
with @racket[remember-write!] should be appended to this
|
||||||
|
file.
|
||||||
|
|
||||||
|
It is equivalent to:
|
||||||
|
@racketblock[(remember-input-file name)
|
||||||
|
(remember-output-file name)]}
|
||||||
|
|
||||||
|
@defform[(remembered! category value)
|
||||||
|
#:grammar ([category identifier?])]{
|
||||||
|
Marks the given @racket[value] as remembered in the given
|
||||||
|
@racket[category]. If the same value is remembered twice
|
||||||
|
for the same category, the second occurrence is ignored
|
||||||
|
(i.e. values are stored in a distinct @racket[set] for each
|
||||||
|
category).
|
||||||
|
|
||||||
|
Calls to this macro are usually present in an input file
|
||||||
|
loaded with @racket[remember-input-file] or
|
||||||
|
@racket[remember-io-file], but can also be inserted in the
|
||||||
|
main file or any other file loaded with @racket[require].}
|
7
test/input-error.rkt
Normal file
7
test/input-error.rkt
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
#lang racket
|
||||||
|
(require remember)
|
||||||
|
(remembered! foo-error (1 2 3))
|
||||||
|
(remembered! foo-error (1 2 3 4))
|
||||||
|
(remembered! foo-error (1 2 3 5))
|
||||||
|
(define + 'wrong)
|
||||||
|
(provide +)
|
5
test/input1.rkt
Normal file
5
test/input1.rkt
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
#lang racket
|
||||||
|
(require remember)
|
||||||
|
(remembered! foo (1 2 3))
|
||||||
|
(remembered! foo (1 2 3 4))
|
||||||
|
(remembered! foo (1 2 3 5))
|
7
test/input3.rkt
Normal file
7
test/input3.rkt
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
#lang racket
|
||||||
|
(require remember)
|
||||||
|
(remembered! foo3 (1 2 3))
|
||||||
|
(remembered! foo3 (1 2 3 4))
|
||||||
|
(remembered! foo3 (1 2 3 5))
|
||||||
|
(define + 'wrong)
|
||||||
|
(provide +)
|
3
test/io2.rkt
Normal file
3
test/io2.rkt
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
#lang racket
|
||||||
|
(require remember)
|
||||||
|
(remembered! bar (1 2 3 xyz))
|
18
test/test-error.rkt
Normal file
18
test/test-error.rkt
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
#lang racket
|
||||||
|
|
||||||
|
(require remember
|
||||||
|
rackunit)
|
||||||
|
(remember-input-file "input-error.rkt")
|
||||||
|
(define-syntax (test-rem stx)
|
||||||
|
(syntax-case stx ()
|
||||||
|
[(_ val)
|
||||||
|
(let ([v (syntax-e #'val)])
|
||||||
|
(unless (remembered? 'err-category v)
|
||||||
|
(remembered-error! 'err-category #'val)))
|
||||||
|
#'(void)]))
|
||||||
|
|
||||||
|
(test-rem one)
|
||||||
|
(test-rem two)
|
||||||
|
(check-equal? (+ 1 2) 3)
|
||||||
|
(test-rem three)
|
||||||
|
(test-rem four)
|
13
test/test1.rkt
Normal file
13
test/test1.rkt
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
#lang racket
|
||||||
|
(require remember
|
||||||
|
rackunit
|
||||||
|
(submod "../remember-implementation.hl.rkt" private))
|
||||||
|
(remember-input-file "input1.rkt")
|
||||||
|
(begin-for-syntax
|
||||||
|
(require rackunit)
|
||||||
|
(define secs (current-seconds))
|
||||||
|
(remembered-add-written! 'foo `(1 2 3 secs))
|
||||||
|
(check-false (remembered? 'foo `(1 2 3 secs)))
|
||||||
|
(check-true (remembered-or-written? 'foo `(1 2 3 secs))))
|
||||||
|
;; check that no identifiers were imported from "input1.rkt".
|
||||||
|
(check-not-equal? + 'wrong)
|
9
test/test2.rkt
Normal file
9
test/test2.rkt
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
#lang racket
|
||||||
|
(require remember)
|
||||||
|
(remember-io-file "io2.rkt")
|
||||||
|
(begin-for-syntax
|
||||||
|
(require rackunit)
|
||||||
|
;; Manually check for an error the first time this
|
||||||
|
;; file is compiled after emptying io2.rkt
|
||||||
|
(check-true (remembered? 'bar '(1 2 3 xyz)))
|
||||||
|
(remember-write! 'bar '(1 2 3 xyz)))
|
8
test/test3.rkt
Normal file
8
test/test3.rkt
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
#lang racket
|
||||||
|
(require remember)
|
||||||
|
(remember-io-file "input3.rkt")
|
||||||
|
(begin-for-syntax
|
||||||
|
(require rackunit
|
||||||
|
racket/set)
|
||||||
|
(check set=? (get-remembered 'foo3)
|
||||||
|
(set '(1 2 3) '(1 2 3 5) '(1 2 3 4))))
|
Loading…
Reference in New Issue
Block a user