Compare commits
1 Commits
xenia/patc
...
objectives
| Author | SHA1 | Date |
|---|---|---|
|
|
d333cb9ef9 |
|
|
@ -1,6 +0,0 @@
|
||||||
version: 2
|
|
||||||
updates:
|
|
||||||
- package-ecosystem: "github-actions"
|
|
||||||
directory: "/"
|
|
||||||
schedule:
|
|
||||||
interval: "daily"
|
|
||||||
|
|
@ -1,50 +0,0 @@
|
||||||
name: Docker
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
tags:
|
|
||||||
- '*'
|
|
||||||
branches:
|
|
||||||
- master
|
|
||||||
pull_request:
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
docker:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Clone Repository
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Configure Docker Metadata
|
|
||||||
id: meta
|
|
||||||
uses: docker/metadata-action@v5
|
|
||||||
with:
|
|
||||||
images: ghcr.io/${{ github.repository }}
|
|
||||||
tags: |
|
|
||||||
type=ref,event=branch
|
|
||||||
type=ref,event=pr
|
|
||||||
type=ref,event=tag
|
|
||||||
type=semver,pattern={{version}}
|
|
||||||
type=semver,pattern={{major}}.{{minor}}
|
|
||||||
|
|
||||||
- name: Authenticate to Package Registry
|
|
||||||
uses: docker/login-action@v3
|
|
||||||
if: ${{ github.event_name != 'pull_request' }}
|
|
||||||
with:
|
|
||||||
registry: ghcr.io
|
|
||||||
username: ${{ github.actor }}
|
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v3
|
|
||||||
|
|
||||||
- name: Build and Publish Rosette Image
|
|
||||||
uses: docker/build-push-action@v6
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
push: ${{ github.event_name != 'pull_request' }}
|
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
|
||||||
cache-from: type=gha
|
|
||||||
cache-to: type=gha,mode=max
|
|
||||||
|
|
@ -1,102 +0,0 @@
|
||||||
name: Tests
|
|
||||||
|
|
||||||
on: [push, pull_request]
|
|
||||||
|
|
||||||
env:
|
|
||||||
CVC4_URL: "http://cvc4.cs.stanford.edu/downloads/builds/x86_64-linux-opt/cvc4-1.8-x86_64-linux-opt"
|
|
||||||
BOOLECTOR_URL: "https://github.com/Boolector/boolector/archive/3.2.1.tar.gz"
|
|
||||||
CVC5_URL: "https://github.com/cvc5/cvc5/releases/download/cvc5-1.0.7/cvc5-Linux"
|
|
||||||
BITWUZLA_URL: "https://github.com/bitwuzla/bitwuzla/archive/93a3d930f622b4cef0063215e63b7c3bd10bd663.tar.gz"
|
|
||||||
STP_URL: "https://github.com/stp/stp/archive/d70085462f07c8a5a2f1225f727cda3ef505b141.tar.gz"
|
|
||||||
YICES2_URL: "https://github.com/SRI-CSL/yices2/archive/e27cf308cffb0ecc6cc7165c10e81ca65bc303b3.tar.gz"
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
test:
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
racket-version: ['8.1', 'current']
|
|
||||||
racket-variant: ['CS']
|
|
||||||
allow-failure: [false]
|
|
||||||
name: Racket ${{ matrix.racket-version }} (${{ matrix.racket-variant }})
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
continue-on-error: ${{ matrix.allow-failure }}
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@master
|
|
||||||
- name: Setup Racket
|
|
||||||
uses: Bogdanp/setup-racket@v1.14
|
|
||||||
with:
|
|
||||||
architecture: x64
|
|
||||||
version: ${{ matrix.racket-version }}
|
|
||||||
variant: ${{ matrix.racket-variant }}
|
|
||||||
- name: Install solvers
|
|
||||||
# Note that setting LD_LIBRARY_PATH can be removed once this bug is
|
|
||||||
# fixed: https://github.com/stp/stp/issues/485
|
|
||||||
run: |
|
|
||||||
mkdir bin &&
|
|
||||||
wget $CVC4_URL -nv -O bin/cvc4 &&
|
|
||||||
chmod +x bin/cvc4 &&
|
|
||||||
wget $BOOLECTOR_URL -nv -O boolector.tar.gz &&
|
|
||||||
mkdir boolector &&
|
|
||||||
tar xzf boolector.tar.gz -C boolector --strip-components=1 &&
|
|
||||||
pushd boolector &&
|
|
||||||
./contrib/setup-cadical.sh &&
|
|
||||||
./contrib/setup-btor2tools.sh &&
|
|
||||||
./configure.sh &&
|
|
||||||
cd build &&
|
|
||||||
make &&
|
|
||||||
popd &&
|
|
||||||
cp boolector/build/bin/boolector bin/ &&
|
|
||||||
rm -rf boolector* &&
|
|
||||||
wget $CVC5_URL -nv -O bin/cvc5 &&
|
|
||||||
chmod +x bin/cvc5 &&
|
|
||||||
sudo apt-get update &&
|
|
||||||
sudo apt-get install -y ninja-build &&
|
|
||||||
pip3 install meson &&
|
|
||||||
wget $BITWUZLA_URL -nv -O bitwuzla.tar.gz &&
|
|
||||||
mkdir bitwuzla &&
|
|
||||||
tar xzf bitwuzla.tar.gz -C bitwuzla --strip-components=1 &&
|
|
||||||
pushd bitwuzla &&
|
|
||||||
./configure.py &&
|
|
||||||
pushd build &&
|
|
||||||
ninja &&
|
|
||||||
popd &&
|
|
||||||
popd &&
|
|
||||||
cp bitwuzla/build/src/main/bitwuzla bin/ &&
|
|
||||||
sudo apt-get install -y build-essential git cmake bison flex libboost-all-dev libtinfo-dev python3 perl &&
|
|
||||||
wget $STP_URL -nv -O stp.tar.gz &&
|
|
||||||
mkdir stp &&
|
|
||||||
tar xzf stp.tar.gz -C stp --strip-components=1 &&
|
|
||||||
pushd stp &&
|
|
||||||
echo "LD_LIBRARY_PATH=$PWD/deps/cadical/build:$PWD/deps/cadiback/:$LD_LIBRARY_PATH" >> $GITHUB_ENV &&
|
|
||||||
./scripts/deps/setup-gtest.sh &&
|
|
||||||
./scripts/deps/setup-outputcheck.sh &&
|
|
||||||
./scripts/deps/setup-cms.sh &&
|
|
||||||
./scripts/deps/setup-minisat.sh &&
|
|
||||||
mkdir build &&
|
|
||||||
pushd build &&
|
|
||||||
cmake .. &&
|
|
||||||
cmake --build . &&
|
|
||||||
popd &&
|
|
||||||
popd &&
|
|
||||||
ln -s stp/build/stp bin/stp &&
|
|
||||||
sudo apt-get install -y gperf &&
|
|
||||||
wget $YICES2_URL -nv -O yices2.tar.gz &&
|
|
||||||
mkdir yices2 &&
|
|
||||||
tar xvf yices2.tar.gz -C yices2 --strip-components=1 &&
|
|
||||||
pushd yices2 &&
|
|
||||||
autoconf &&
|
|
||||||
./configure --prefix=$PWD/out/ &&
|
|
||||||
make &&
|
|
||||||
make install &&
|
|
||||||
popd &&
|
|
||||||
cp yices2/out/bin/yices-smt2 bin/yices-smt2
|
|
||||||
- name: Install Rosette
|
|
||||||
run: raco pkg install --auto --name rosette
|
|
||||||
- name: Compile Rosette tests
|
|
||||||
run: raco make test/all-rosette-tests.rkt
|
|
||||||
- name: Run Rosette tests
|
|
||||||
run: raco test test/all-rosette-tests.rkt
|
|
||||||
- name: Compile SDSL tests
|
|
||||||
run: raco make test/all-sdsl-tests.rkt
|
|
||||||
- name: Run SDSL tests
|
|
||||||
run: raco test test/all-sdsl-tests.rkt
|
|
||||||
|
|
@ -8,12 +8,7 @@
|
||||||
ehthumbs.db
|
ehthumbs.db
|
||||||
Thumbs.db
|
Thumbs.db
|
||||||
|
|
||||||
**/doc
|
|
||||||
**/doc/**
|
|
||||||
**/bin/**
|
**/bin/**
|
||||||
**/compiled
|
**/compiled
|
||||||
**/compiled/**
|
**/compiled/**
|
||||||
*~
|
*~
|
||||||
node_modules
|
|
||||||
.cache
|
|
||||||
yarn.lock
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
[submodule "z3"]
|
||||||
|
path = z3
|
||||||
|
url = https://github.com/Z3Prover/z3
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
language: java
|
||||||
|
sudo: false
|
||||||
|
|
||||||
|
env:
|
||||||
|
global:
|
||||||
|
- Z3_DIR=~/z3
|
||||||
|
- Z3_REV="4cf72e23e6d51df47ed67c35ea9a90016d9b69d5"
|
||||||
|
- RACKET_DIR=~/racket
|
||||||
|
- RACKET_URL="https://www.cs.utah.edu/plt/installers/6.4/racket-6.4-x86_64-linux-ubuntu-precise.sh"
|
||||||
|
|
||||||
|
before_install:
|
||||||
|
- if [[ ! -e "$RACKET_DIR/bin/racket" ]]; then
|
||||||
|
rm -rf $RACKET_DIR;
|
||||||
|
curl -L -o racket.sh $RACKET_URL;
|
||||||
|
sh ./racket.sh --in-place --dest $RACKET_DIR;
|
||||||
|
else echo "using racket from cache"; fi
|
||||||
|
- if [[ ! -e "$Z3_DIR/build/z3" ]]; then
|
||||||
|
rm -rf $Z3_DIR;
|
||||||
|
git clone https://github.com/z3prover/z3.git $Z3_DIR;
|
||||||
|
cd $Z3_DIR;
|
||||||
|
git checkout $Z3_REV;
|
||||||
|
python scripts/mk_make.py;
|
||||||
|
cd $Z3_DIR/build;
|
||||||
|
make -j2;
|
||||||
|
cd $TRAVIS_BUILD_DIR;
|
||||||
|
else echo "using z3 from cache"; fi
|
||||||
|
- mkdir bin/
|
||||||
|
- cp $Z3_DIR/build/z3 bin/
|
||||||
|
- bin/z3 --version
|
||||||
|
- cd $Z3_DIR && git rev-parse HEAD && cd $TRAVIS_BUILD_DIR
|
||||||
|
- export PATH="${RACKET_DIR}/bin:${PATH}"
|
||||||
|
|
||||||
|
install: raco pkg install --auto $TRAVIS_BUILD_DIR/rosette
|
||||||
|
|
||||||
|
script:
|
||||||
|
- raco test
|
||||||
|
- time raco make test/all-rosette-tests.rkt
|
||||||
|
- raco test test/all-rosette-tests.rkt
|
||||||
|
- time raco make test/all-sdsl-tests.rkt
|
||||||
|
- raco test test/all-sdsl-tests.rkt
|
||||||
|
|
||||||
|
cache:
|
||||||
|
directories:
|
||||||
|
- $Z3_DIR
|
||||||
|
- $RACKET_DIR
|
||||||
97
Dockerfile
97
Dockerfile
|
|
@ -1,97 +0,0 @@
|
||||||
FROM alpine:3.15
|
|
||||||
|
|
||||||
## ========================== [ Install Racket ] =========================== ##
|
|
||||||
|
|
||||||
## Define default Racket version and variant. The Racket version is of the form
|
|
||||||
## <major>.<minor>. The variant can be "cs" (Chez Scheme), "bc" (Before Chez) or
|
|
||||||
## "natipkg" (where external libraries are included in the Racket packages).
|
|
||||||
##
|
|
||||||
ARG RACKET_VERSION=8.4
|
|
||||||
ARG RACKET_VARIANT=cs
|
|
||||||
|
|
||||||
## Install Racket. We first install system dependencies: [gcompat] is needed for
|
|
||||||
## Racket and [ncurses] is needed for the [xrepl] and [expeditor] packages,
|
|
||||||
## providing the REPL. We then download the installer, run it with the right
|
|
||||||
## parameters, then remove it. After that, all that remains is to set-up the
|
|
||||||
## Racket packages and install [expeditor]. See later for a description of the
|
|
||||||
## arguments to [raco pkg install].
|
|
||||||
##
|
|
||||||
RUN apk add --no-cache gcompat ncurses
|
|
||||||
RUN wget "https://download.racket-lang.org/installers/${RACKET_VERSION}/racket-minimal-${RACKET_VERSION}-x86_64-linux-${RACKET_VARIANT}.sh"
|
|
||||||
RUN echo 'yes\n1\n' | sh racket-minimal-${RACKET_VERSION}-x86_64-linux-${RACKET_VARIANT}.sh --create-dir --unix-style --dest /usr/
|
|
||||||
RUN rm racket-minimal-${RACKET_VERSION}-x86_64-linux-${RACKET_VARIANT}.sh
|
|
||||||
RUN raco setup --no-docs
|
|
||||||
RUN raco pkg install -i --batch --auto --no-docs expeditor-lib
|
|
||||||
|
|
||||||
## =================== [ Install Rosette's Dependencies ] =================== ##
|
|
||||||
|
|
||||||
## Work on Rosette's installation within /usr/local. This directory will be
|
|
||||||
## cleaned up later on so it could be anything.
|
|
||||||
##
|
|
||||||
WORKDIR /usr/local/rosette
|
|
||||||
|
|
||||||
## Get all the info.rkt files. Trying to install Rosette based only on these
|
|
||||||
## files would fail, but we can use them to only install dependencies.
|
|
||||||
##
|
|
||||||
COPY info.rkt .
|
|
||||||
COPY rosette/info.rkt rosette/
|
|
||||||
|
|
||||||
## Install only Rosette's dependencies. We have to install the external
|
|
||||||
## dependencies [libstdc++] and [libgcc] because Z3 needs them at runtime. As
|
|
||||||
## for the Racket dependencies only, we achieve that in three steps:
|
|
||||||
##
|
|
||||||
## 1. We use [raco pkg install --no-setup] to download and register Rosette
|
|
||||||
## and all its dependencies without setting them up, that is without
|
|
||||||
## compiling them. At this point, the system is in an inconsistent state,
|
|
||||||
## where packages are registered but not actually present. The other flags
|
|
||||||
## are the following:
|
|
||||||
##
|
|
||||||
## -i install packages for all users
|
|
||||||
## --batch disable interactive mode and suppress prompts
|
|
||||||
## --auto download missing packages automatically
|
|
||||||
##
|
|
||||||
## 2. We use [raco pkg remove --no-setup] to unregister Rosette. This keeps
|
|
||||||
## the dependencies as registered. The system is still in an inconsistent
|
|
||||||
## state. See above for the flags.
|
|
||||||
##
|
|
||||||
## 3. We use [raco setup] to set up all the registered package. This brings
|
|
||||||
## the system back in a consistent state. Since Rosette's dependencies were
|
|
||||||
## registered but not Rosette itself, this achieves our goal. The flags are
|
|
||||||
## the following:
|
|
||||||
##
|
|
||||||
## --fail-fast fail on the first error encountered
|
|
||||||
## --no-docs do not compile the documentations
|
|
||||||
##
|
|
||||||
RUN apk add --no-cache libstdc++ libgcc
|
|
||||||
RUN raco pkg install -i --batch --auto --no-setup ../rosette
|
|
||||||
RUN raco pkg remove -i --no-setup rosette
|
|
||||||
RUN raco setup --fail-fast --no-docs
|
|
||||||
|
|
||||||
## ========================== [ Install Rosette ] =========================== ##
|
|
||||||
|
|
||||||
## Get all of Rosette; build and install it. The dependencies should all be
|
|
||||||
## installed, so we can remove the --auto flag which will lead us to failure if
|
|
||||||
## a dependency cannot be found. The additional flags are the following:
|
|
||||||
##
|
|
||||||
## --copy copy content to install path (instead of linking)
|
|
||||||
##
|
|
||||||
COPY . .
|
|
||||||
RUN raco pkg install -i --batch --copy --no-docs ./rosette
|
|
||||||
RUN rm -R /usr/local/rosette
|
|
||||||
|
|
||||||
## ===================== [ Prepare Clean Entry Point ] ====================== ##
|
|
||||||
|
|
||||||
## For further use of the image, we can start with user `rosette`, group
|
|
||||||
## `rosette` in `/rosette` by default.
|
|
||||||
##
|
|
||||||
RUN addgroup rosette
|
|
||||||
RUN adduser --system --shell /bin/false --disabled-password \
|
|
||||||
--home /rosette --ingroup rosette rosette
|
|
||||||
RUN chown -R rosette:rosette /rosette
|
|
||||||
USER rosette
|
|
||||||
WORKDIR /rosette
|
|
||||||
|
|
||||||
## Rosette files are simply Racket files using the Rosette library: the default
|
|
||||||
## entry point of this image is therefore the Racket executable.
|
|
||||||
##
|
|
||||||
ENTRYPOINT ["/usr/bin/racket", "-I", "rosette"]
|
|
||||||
166
NOTES.md
166
NOTES.md
|
|
@ -1,166 +0,0 @@
|
||||||
# Release Notes
|
|
||||||
|
|
||||||
## Version 4.1
|
|
||||||
|
|
||||||
This is a minor bug-fixing release.
|
|
||||||
|
|
||||||
## Version 4.0
|
|
||||||
|
|
||||||
This is a major release with significant changes to the language and the runtime. Rosette 4.0 is *not backward compatible* with Rosette 3.x. But porting Rosette 3.x code to Rosette 4.0 should be straightforward for most applications.
|
|
||||||
|
|
||||||
This release includes the following features:
|
|
||||||
|
|
||||||
- Support for assumptions (see `assume`).
|
|
||||||
- New symbolic evaluation core that tracks verification conditions (VCs) rather than path conditions and assertions.
|
|
||||||
- New symbolic reflection constructs for working with VCs, including `vc`, `with-vc`, and `clear-vc!`.
|
|
||||||
- New symbolic reflection facilities for managing symbolic `terms`, including the option of using a garbage-collected data structure.
|
|
||||||
- Updated `verify`, `synthesize`, `solve`, and `optimize` queries.
|
|
||||||
- New synthesis library with efficient support for grammar holes (see `define-grammar`).
|
|
||||||
- New list and vector operators that use bitvectors instead of integers.
|
|
||||||
- Updates to The Rosette Guide to document the new language in detail.
|
|
||||||
|
|
||||||
The following features have been removed:
|
|
||||||
|
|
||||||
- The `debug` query.
|
|
||||||
- Reflection facilities for working with path conditions and assertions: `pc`, `with-asserts`, `with-asserts-only`, `clear-asserts!`, and `asserts`.
|
|
||||||
- Support for CPLEX.
|
|
||||||
|
|
||||||
## Version 3.2
|
|
||||||
|
|
||||||
This release includes minor updates and a new [value destructuring library].
|
|
||||||
|
|
||||||
[value destructuring library]: https://docs.racket-lang.org/rosette-guide/sec_utility-libs.html#%28part._.Value_.Destructuring_.Library%29
|
|
||||||
|
|
||||||
## Version 3.1
|
|
||||||
|
|
||||||
This release includes bug fixes and updates Rosette to use the latest version of Z3 as its default SMT solver.
|
|
||||||
|
|
||||||
This release also includes the following new functionality contributed by [Sorawee Porncharoenwase][]:
|
|
||||||
|
|
||||||
- An interactive [value browser][] to help programmers navigate and read complex symbolic values.
|
|
||||||
- An *error tracer* for finding bugs in Rosette programs that manifest as exceptions intercepted during symbolic evaluation. To use the error tracer, run the command `raco symtrace <prog>`. The [debugging][] chapter in the Rosette guide describes some common issues due to intercepted exceptions, how to test for them, and how to find them with the error tracer.
|
|
||||||
|
|
||||||
|
|
||||||
[Sorawee Porncharoenwase]: https://github.com/sorawee
|
|
||||||
[debugging]: https://docs.racket-lang.org/rosette-guide/ch_error-tracing.html
|
|
||||||
[value browser]: https://docs.racket-lang.org/rosette-guide/sec_utility-libs.html#%28part._.Value_.Browser_.Library%29
|
|
||||||
|
|
||||||
## Version 3.0
|
|
||||||
|
|
||||||
This is a major release with significant changes to the language and the runtime. Rosette 3.0 is *not backward compatible* with Rosette 2.x. But porting Rosette 2.x code to Rosette 3.0 should be straightforward for most applications.
|
|
||||||
|
|
||||||
The semantics of Rosette 3.0 differs from Rosette 2.x in two ways:
|
|
||||||
|
|
||||||
- The `current-bitwidth` parameter that controls the reasoning precision is set to `#f` by default. As a result, symbolic constants that are declared to be integers or reals are interpreted in the theories of integers and reals, respectively. This means that the semantics of assertions over these types follows that of Racket. But reasoning about such assertions is expensive (or undecidable), so Rosette 3.0 still provides the option of approximating integer and real constants with finite-precision bitvectors. The key difference is that programs must now *explicitly opt into* this approximation by setting `current-bitwidth` to a positive integer.
|
|
||||||
- If `current-bitwidth` is set to a positive integer _k_, the solutions produced by the `verify`, `synthesize`, `solve`, and `debug` queries are guaranteed to be correct under the _k_-bit semantics for integer and real constants. They are _not_ guaranteed to be sound with respect to the infinite-precision semantics.
|
|
||||||
|
|
||||||
This release also includes the following new functionality and features contributed by [James Bornholt][] and [Phitchaya Mangpo Phothilimthana][]:
|
|
||||||
|
|
||||||
- Developed a new *symbolic profiler* for diagnosing performance issues in Rosette programs. The symbolic profiler instruments Rosette and tracks key performance metrics to identify potential issues. To use the symbolic profiler, run the command `raco symprofile program.rkt`. A new [performance][] chapter in the Rosette guide details common performance issues and how to use the symbolic profiler to identify them.
|
|
||||||
- Extended and generalized the interface to constraint solvers. The new interface allows the client code to specify a path to the solver, set the logic, provide solver-specific configuration options, and export the problem encodings sent to the solver.
|
|
||||||
- Added support for four new solvers: [Boolector][], [CVC4][], [Yices][], and [CPLEX][]. These solvers are not included in the default distribution and need to be installed separately for use with Rosette.
|
|
||||||
|
|
||||||
[performance]: https://docs.racket-lang.org/rosette-guide/ch_performance.html
|
|
||||||
[Boolector]: https://docs.racket-lang.org/rosette-guide/sec_solvers-and-solutions.html#%28def._%28%28lib._rosette%2Fsolver%2Fsmt%2Fboolector..rkt%29._boolector%29%29
|
|
||||||
[CVC4]: https://docs.racket-lang.org/rosette-guide/sec_solvers-and-solutions.html#%28def._%28%28lib._rosette%2Fsolver%2Fsmt%2Fcvc4..rkt%29._cvc4%29%29
|
|
||||||
[Yices]: https://docs.racket-lang.org/rosette-guide/sec_solvers-and-solutions.html#%28def._%28%28lib._rosette%2Fsolver%2Fsmt%2Fyices..rkt%29._yices%29%29
|
|
||||||
[CPLEX]: https://docs.racket-lang.org/rosette-guide/sec_solvers-and-solutions.html#%28def._%28%28lib._rosette%2Fsolver%2Fmip%2Fcplex..rkt%29._cplex%29%29
|
|
||||||
[Phitchaya Mangpo Phothilimthana]: https://github.com/mangpo
|
|
||||||
|
|
||||||
## Version 2.2
|
|
||||||
|
|
||||||
This release includes bug fixes and the following updates:
|
|
||||||
|
|
||||||
- Added support for quantified formulas. Quantifiers can appear in assertions passed to `solve` and `verify` queries. They should not be used with `synthesize` queries. When using quantified formulas, `current-bitwidth` must be set to `#f`.
|
|
||||||
- Added the `unknown` solution type. An `unknown` solution is returned if the underlying solver cannot decide if a given set of constraints is (un)satisfiable.
|
|
||||||
- Added the `distinct?` predicate that returns true iff all of its arguments are pairwaise un-equal. This has a direct (efficient) translation to Z3 if the arguments are primitive solvable values (booleans, integers, reals, or bitvectors).
|
|
||||||
|
|
||||||
## Version 2.1
|
|
||||||
|
|
||||||
This release includes the following updates to Rosette 2.0:
|
|
||||||
|
|
||||||
- Added support for the `push` / `pop` interface to Z3.
|
|
||||||
- Switched to log-based evaluation for Rosette documentation. Documentation generation no longer depends on Z3.
|
|
||||||
- Improved the implementation of the lifted `struct` construct. The new implementation is a minimal patch to the corresponding Racket implementation, and it enables creation and use of `struct`s in the REPL.
|
|
||||||
- Improved the implementation of `#%top-interaction` to disallow mutation of top-level variables in the REPL. This enables definition and use of recursive procedures in the REPL, as well as definition and use of generic interfaces.
|
|
||||||
|
|
||||||
## Version 2.0
|
|
||||||
|
|
||||||
This is a major release with significant changes to the language and
|
|
||||||
the symbolic evaluator. Rosette 2.0 is *not backward compatible* with
|
|
||||||
Rosette 1.x.
|
|
||||||
|
|
||||||
This release includes the following features:
|
|
||||||
|
|
||||||
- New symbolic datatypes.
|
|
||||||
|
|
||||||
- Replaced the `number?` type with `integer?` and `real?` types.
|
|
||||||
These datatypes are translated to the theories of integers and
|
|
||||||
reals if `current-bitwidth` is set to `#f`. Otherwise, they are
|
|
||||||
translated to bitvectors of length `(current-bitwidth)`.
|
|
||||||
|
|
||||||
- Added the `bitvector?` datatype, which embeds the SMT theory of
|
|
||||||
bitvectors into Rosette.
|
|
||||||
|
|
||||||
- Added the `function?` datatype, which embeds uninterpreted
|
|
||||||
functions into Rosette.
|
|
||||||
|
|
||||||
- New solver-aided queries.
|
|
||||||
|
|
||||||
- Changed the behavior of solver-aided queries to no longer throw
|
|
||||||
exceptions when a model is not found. Instead they return an
|
|
||||||
`unsat?` solution.
|
|
||||||
|
|
||||||
- Changed the `solve` and `verify` queries to ensure that any
|
|
||||||
solution obtained with finite-precision reasoning is correct under
|
|
||||||
the aribitrary-precision semantics of integers and reals.
|
|
||||||
|
|
||||||
- Added the `optimize` query, which exposes Z3's optimization
|
|
||||||
features.
|
|
||||||
|
|
||||||
- Improved implementation for the `define-synthax` form and other
|
|
||||||
high-level synthesis constructs.
|
|
||||||
|
|
||||||
- Improved printing of symbolic values by [James Bornholt][].
|
|
||||||
|
|
||||||
- Ported sample SDSLs to Rosette 2.0.
|
|
||||||
|
|
||||||
- Updated The Rosette Guide to document the new language in detail.
|
|
||||||
|
|
||||||
The following features have been removed:
|
|
||||||
|
|
||||||
- Support for Kodkod and CVC4 solvers.
|
|
||||||
|
|
||||||
- Support for the `enum` datatype.
|
|
||||||
|
|
||||||
- Support for internal logging via `current-log-handler`.
|
|
||||||
|
|
||||||
[James Bornholt]: https://github.com/jamesbornholt
|
|
||||||
|
|
||||||
## Version 1.1
|
|
||||||
|
|
||||||
- This release includes a new reader for `rosette` and `rosette/safe`
|
|
||||||
implemented by [bmastenbrook](https://github.com/bmastenbrook).
|
|
||||||
|
|
||||||
- It also includes a fix for a bug in the evaluation of symbolic
|
|
||||||
boxes. Thanks to Alan Borning for reporting it.
|
|
||||||
|
|
||||||
## Version 1.0
|
|
||||||
|
|
||||||
- This is the initial release of the Rosette language and Symbolic
|
|
||||||
Virtual Machine, as described in [PLDI'14][1] and [Onward13][2].
|
|
||||||
|
|
||||||
- It includes two symbolic datatypes: `boolean?` and `number?`.
|
|
||||||
Assertions over numbers are translated to the theory of bitvectors.
|
|
||||||
|
|
||||||
- Rosette 1.0 supports the Kodkod, Z3, and CVC4 solvers.
|
|
||||||
|
|
||||||
- This release also includes the source code for three solver-aided
|
|
||||||
DSLs: WebSynth (web scraping by demonstration), IFC (verification
|
|
||||||
for secure stack machine semantics), and SynthCL (synthesis and
|
|
||||||
verification for an Open-CL imperative language).
|
|
||||||
|
|
||||||
|
|
||||||
[1]: http://dl.acm.org/citation.cfm?id=2594340
|
|
||||||
[2]: http://dl.acm.org/citation.cfm?id=2509586
|
|
||||||
|
|
||||||
60
README.md
60
README.md
|
|
@ -1,40 +1,36 @@
|
||||||
The Rosette Language
|
rosette
|
||||||
====================
|
=======
|
||||||
|
|
||||||
[](https://github.com/emina/rosette/actions?query=workflow%3ATests)
|
[](https://travis-ci.org/emina/rosette)
|
||||||
|
|
||||||
[Rosette](http://emina.github.io/rosette/) is a solver-aided programming language that extends [Racket](http://racket-lang.org) with language constructs for program synthesis, verification, and more. This repository includes the source code for Rosette, as well as several example solver-aided DSLs.
|
This repository includes the source code for the Rosette solver-aided host language, as well as several example
|
||||||
|
solver-aided DSLs.
|
||||||
|
|
||||||
## Installing Rosette
|
### Installing Rosette
|
||||||
|
|
||||||
The easiest way to install Rosette is from Racket's package manager:
|
* Download and install Racket 6.4 from http://racket-lang.org
|
||||||
|
|
||||||
* Download and install Racket 8.1 or later from http://racket-lang.org
|
|
||||||
|
|
||||||
* Use Racket's `raco` tool to install Rosette:
|
|
||||||
|
|
||||||
`$ raco pkg install rosette`
|
|
||||||
|
|
||||||
### Installing from source
|
|
||||||
|
|
||||||
Alternatively, you can install Rosette from source:
|
|
||||||
|
|
||||||
* Download and install Racket 8.1 or later from http://racket-lang.org
|
|
||||||
|
|
||||||
* Clone the rosette repository:
|
* Clone the rosette repository:
|
||||||
|
|
||||||
`$ git clone https://github.com/emina/rosette.git`
|
`$ git clone https://github.com/emina/rosette.git`
|
||||||
|
|
||||||
* Uninstall any previous versions of Rosette:
|
* Use Racket's `raco` tool to install Rosette as one of your Racket collections:
|
||||||
|
|
||||||
`$ raco pkg remove rosette`
|
|
||||||
|
|
||||||
* Use Racket's `raco` tool to install Rosette:
|
|
||||||
|
|
||||||
`$ cd rosette`
|
`$ cd rosette`
|
||||||
`$ raco pkg install`
|
`$ raco link rosette`
|
||||||
|
`$ raco setup -l rosette`
|
||||||
|
|
||||||
## Executing Rosette programs
|
* Create a `bin` subdirectory in the `rosette` directory:
|
||||||
|
|
||||||
|
`$ mkdir bin`
|
||||||
|
`$ ls`
|
||||||
|
`bin doc rosette sdsl test LICENSE README.md`
|
||||||
|
|
||||||
|
* Download or build a copy of the [Z3](https://github.com/Z3Prover/z3) solver, version 4.4.2.
|
||||||
|
|
||||||
|
* Copy the `z3` executable (with no filename extension) to the `rosette/bin` directory.
|
||||||
|
|
||||||
|
### Executing Rosette programs
|
||||||
|
|
||||||
* Open the target program in DrRacket (e.g., [`rosette/sdsl/fsm/demo.rkt`](https://github.com/emina/rosette/blob/master/sdsl/fsm/demo.rkt))
|
* Open the target program in DrRacket (e.g., [`rosette/sdsl/fsm/demo.rkt`](https://github.com/emina/rosette/blob/master/sdsl/fsm/demo.rkt))
|
||||||
and hit run!
|
and hit run!
|
||||||
|
|
@ -45,7 +41,7 @@ Alternatively, you can install Rosette from source:
|
||||||
`$ raco make <your program>`
|
`$ raco make <your program>`
|
||||||
`$ racket <your program>`
|
`$ racket <your program>`
|
||||||
|
|
||||||
## Available languages
|
### Available languages
|
||||||
|
|
||||||
* Rosette ships with two languages: `#lang rosette/safe` and `#lang rosette`.
|
* Rosette ships with two languages: `#lang rosette/safe` and `#lang rosette`.
|
||||||
|
|
||||||
|
|
@ -63,7 +59,7 @@ Alternatively, you can install Rosette from source:
|
||||||
|
|
||||||
* The `rosette` language includes all of Racket. This places the burden
|
* The `rosette` language includes all of Racket. This places the burden
|
||||||
on the programmer to decide whether a given Racket construct (which
|
on the programmer to decide whether a given Racket construct (which
|
||||||
is not overridden by Rosette) is safe to use in a given context.
|
is not overriden by Rosette) is safe to use in a given context.
|
||||||
Rosette provides no guarantees or checks for programs that use
|
Rosette provides no guarantees or checks for programs that use
|
||||||
unsafe constructs. In the best case, such a program will fail with
|
unsafe constructs. In the best case, such a program will fail with
|
||||||
an exception if a symbolic value flows to a construct that does not
|
an exception if a symbolic value flows to a construct that does not
|
||||||
|
|
@ -71,11 +67,5 @@ Alternatively, you can install Rosette from source:
|
||||||
incorrect semantics or cause more serious problems (e.g., data loss if
|
incorrect semantics or cause more serious problems (e.g., data loss if
|
||||||
it writes to a file).
|
it writes to a file).
|
||||||
|
|
||||||
* For more on using Rosette, see [_The Rosette Guide_][1]. Rosette's internals are described in [this PLDI'14 paper][2].
|
* For more on using Rosette, see [_The Rosette Guide_](http://homes.cs.washington.edu/~emina/rosette/guide/index.html). Rosette's internals are described in [_A lightweight symbolic
|
||||||
|
virtual machine for solver-aided host languages._](http://homes.cs.washington.edu/~emina/pubs/rosette.pldi14.pdf) (PLDI'14).
|
||||||
[1]: https://docs.racket-lang.org/rosette-guide/index.html
|
|
||||||
[2]: http://dl.acm.org/citation.cfm?id=2594340
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
31
info.rkt
31
info.rkt
|
|
@ -1,31 +0,0 @@
|
||||||
#lang info
|
|
||||||
|
|
||||||
(define collection 'multi)
|
|
||||||
|
|
||||||
(define deps '("custom-load"
|
|
||||||
"sandbox-lib"
|
|
||||||
"scribble-lib"
|
|
||||||
("racket" #:version "8.1")
|
|
||||||
"r6rs-lib"
|
|
||||||
"rfc6455"
|
|
||||||
"net-lib"
|
|
||||||
"web-server-lib"
|
|
||||||
"rackunit-lib"
|
|
||||||
"slideshow-lib"
|
|
||||||
"gui-lib"
|
|
||||||
"base"))
|
|
||||||
|
|
||||||
(define build-deps '("rackunit-doc"
|
|
||||||
"draw-lib"
|
|
||||||
"errortrace-lib"
|
|
||||||
"pict-lib"
|
|
||||||
"pict-doc"
|
|
||||||
"scribble-lib"
|
|
||||||
"racket-doc"
|
|
||||||
"gui-doc"
|
|
||||||
"errortrace-doc"))
|
|
||||||
|
|
||||||
(define test-omit-paths (if (getenv "PLT_PKG_BUILD_SERVICE") 'all '()))
|
|
||||||
|
|
||||||
(define pkg-desc "Rosette solver-aided host language")
|
|
||||||
(define version "4.0")
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
(require (for-syntax racket/syntax "../core/lift.rkt") racket/provide
|
(require (for-syntax racket/syntax "../core/lift.rkt") racket/provide
|
||||||
"../core/safe.rkt" "generic.rkt"
|
"../core/safe.rkt" "generic.rkt"
|
||||||
(only-in "../core/store.rkt" store!)
|
(only-in "../core/effects.rkt" apply!)
|
||||||
(only-in "../core/type.rkt" define-lifted-type type-cast)
|
(only-in "../core/type.rkt" define-lifted-type type-cast)
|
||||||
(only-in "../core/equality.rkt" @eq? @equal?)
|
(only-in "../core/equality.rkt" @eq? @equal?)
|
||||||
(only-in "../core/bool.rkt" instance-of? && ||)
|
(only-in "../core/bool.rkt" instance-of? && ||)
|
||||||
|
|
@ -40,15 +40,12 @@
|
||||||
[(box v) v]
|
[(box v) v]
|
||||||
[(union vs) (apply merge* (for/list ([gv vs]) (cons (car gv) (unbox (cdr gv)))))]))
|
[(union vs) (apply merge* (for/list ([gv vs]) (cons (car gv) (unbox (cdr gv)))))]))
|
||||||
|
|
||||||
(define (box-ref x idx) (unbox x)) ; For the purpose of tracking mutations to the store,
|
|
||||||
(define (box-set! x idx v) (set-box! x v)) ; boxes are treated as 1-element vectors that ignore the index argument.
|
|
||||||
|
|
||||||
(define (@set-box! b v)
|
(define (@set-box! b v)
|
||||||
(match (type-cast @box? b 'set-box!)
|
(match (type-cast @box? b 'set-box!)
|
||||||
[(? box? x)
|
[(? box? x)
|
||||||
(store! x 0 v box-ref box-set!)]
|
(apply! set-box! unbox x v)]
|
||||||
[(union vs)
|
[(union vs)
|
||||||
(for ([gv vs])
|
(for ([gv vs])
|
||||||
(let ([x (cdr gv)])
|
(let ([x (cdr gv)])
|
||||||
(store! x 0 (merge (car gv) v (unbox x)) box-ref box-set!)))]))
|
(apply! set-box! unbox x (merge (car gv) v (unbox x)))))]))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,161 +0,0 @@
|
||||||
#lang racket
|
|
||||||
|
|
||||||
(require
|
|
||||||
(only-in "list.rkt" @list?)
|
|
||||||
(only-in "vector.rkt" @vector? @vector-set!)
|
|
||||||
(only-in "../core/lift.rkt" lift-id)
|
|
||||||
(only-in "../core/forall.rkt" for/all for*/all)
|
|
||||||
(only-in "../core/term.rkt" get-type type-cast term?)
|
|
||||||
"../core/union.rkt"
|
|
||||||
"../core/bitvector.rkt"
|
|
||||||
"../core/merge.rkt"
|
|
||||||
"../core/safe.rkt")
|
|
||||||
|
|
||||||
(provide @list-ref-bv @list-set-bv
|
|
||||||
@take-bv @take-right-bv
|
|
||||||
@drop-bv @drop-right-bv @list-tail-bv
|
|
||||||
@split-at-bv @split-at-right-bv
|
|
||||||
@length-bv
|
|
||||||
@vector-ref-bv @vector-set!-bv @vector-length-bv)
|
|
||||||
|
|
||||||
(define (bv-lit-or-term? v)
|
|
||||||
(or (bv? v) (and (term? v) (bitvector? (get-type v)))))
|
|
||||||
|
|
||||||
(define-syntax-rule (lift-body #:with (id xs idx seq-length) #:type t #:max n #:body body ...)
|
|
||||||
(let* ([t (get-type idx)]
|
|
||||||
[2^k (expt 2 (bitvector-size t))]
|
|
||||||
[sz (seq-length xs)]
|
|
||||||
[n (min sz 2^k)])
|
|
||||||
(when (>= (- 2^k 1) sz)
|
|
||||||
(assert (@bvult idx (@integer->bitvector sz t))
|
|
||||||
(index-too-large-error 'id xs idx)))
|
|
||||||
body ...))
|
|
||||||
|
|
||||||
(define-syntax (define-lift-bv stx)
|
|
||||||
(syntax-case stx ()
|
|
||||||
[(_ (proc-bv xs idx arg ...) @seq? seq?)
|
|
||||||
#`(define-lift-bv #,(lift-id #'proc-bv) (proc-bv xs idx arg ...) @seq? seq?)]
|
|
||||||
[(_ @proc-bv (proc-bv xs idx arg ...) @seq? seq?)
|
|
||||||
#'(define (@proc-bv xs idx arg ...)
|
|
||||||
(if (and (seq? xs) (bv-lit-or-term? idx))
|
|
||||||
(proc-bv xs idx arg ...)
|
|
||||||
(match* ((type-cast @seq? xs 'proc-bv)
|
|
||||||
(bvcoerce idx 'proc-bv))
|
|
||||||
[((? seq? xs) (? bv-lit-or-term? idx))
|
|
||||||
(proc-bv xs idx arg ...)]
|
|
||||||
[(xs idx)
|
|
||||||
(for*/all ([xs xs][idx idx])
|
|
||||||
(proc-bv xs idx arg ...))])))]))
|
|
||||||
|
|
||||||
(define-syntax (define-length-bv stx)
|
|
||||||
(syntax-case stx ()
|
|
||||||
[(_ length-bv @seq? seq? seq-length)
|
|
||||||
#`(begin
|
|
||||||
(define (length-bv xs t) ; (-> seq bitvector? @bv?)
|
|
||||||
(@integer->bitvector (seq-length xs) t))
|
|
||||||
(define (#,(lift-id #'length-bv) xs t)
|
|
||||||
(match (type-cast @seq? xs 'length-bv)
|
|
||||||
[(? seq? xs) (length-bv xs t)]
|
|
||||||
[xs (for/all ([xs xs]) (length-bv xs t))])))]))
|
|
||||||
|
|
||||||
(define-syntax-rule (define-ref-bv ref-bv @seq? seq? seq-ref seq-length)
|
|
||||||
(begin
|
|
||||||
(define (ref-bv xs idx) ; (-> type? bv-lit-or-term? any/c)
|
|
||||||
(if (bv? idx)
|
|
||||||
(seq-ref xs (@bitvector->natural idx))
|
|
||||||
(lift-body
|
|
||||||
#:with (ref-bv xs idx seq-length)
|
|
||||||
#:type t
|
|
||||||
#:max n
|
|
||||||
#:body
|
|
||||||
(apply
|
|
||||||
merge*
|
|
||||||
(for/list ([x xs] [i n])
|
|
||||||
(cons (@bveq (bv i t) idx) x))))))
|
|
||||||
|
|
||||||
(define-lift-bv (ref-bv xs idx) @seq? seq?)))
|
|
||||||
|
|
||||||
; ---- list bv procedures ---- ;
|
|
||||||
|
|
||||||
(define-length-bv length-bv @list? list? length)
|
|
||||||
(define-ref-bv list-ref-bv @list? list? list-ref length)
|
|
||||||
|
|
||||||
(define (list-set-bv xs idx v)
|
|
||||||
(if (bv? idx)
|
|
||||||
(list-set xs (@bitvector->natural idx) v)
|
|
||||||
(lift-body
|
|
||||||
#:with (list-set-bv xs idx length)
|
|
||||||
#:type t
|
|
||||||
#:max n
|
|
||||||
#:body (for/list ([(x i) (in-indexed xs)])
|
|
||||||
(if (< i n)
|
|
||||||
(merge (@bveq (bv i t) idx) v x)
|
|
||||||
x)))))
|
|
||||||
|
|
||||||
(define-lift-bv (list-set-bv xs idx v) @list? list?)
|
|
||||||
|
|
||||||
(define (pair-length ps bound)
|
|
||||||
(if (list? ps)
|
|
||||||
(min (length ps) bound)
|
|
||||||
(let loop ([ps ps] [acc 0])
|
|
||||||
(if (and (pair? ps) (< acc bound))
|
|
||||||
(loop (cdr ps) (add1 acc))
|
|
||||||
acc))))
|
|
||||||
|
|
||||||
(define-syntax (define-get-bv stx)
|
|
||||||
(syntax-case stx ()
|
|
||||||
[(_ get-bv seq-get)
|
|
||||||
#`(begin
|
|
||||||
(define (get-bv xs idx)
|
|
||||||
(if (bv? idx)
|
|
||||||
(seq-get xs (@bitvector->natural idx))
|
|
||||||
(let* ([t (get-type idx)]
|
|
||||||
[2^k (expt 2 (bitvector-size t))]
|
|
||||||
[sz (pair-length xs (sub1 2^k))])
|
|
||||||
(when (> (- 2^k 1) sz)
|
|
||||||
(assert (@bvule idx (@integer->bitvector sz t))
|
|
||||||
(index-too-large-error 'id xs idx)))
|
|
||||||
(apply
|
|
||||||
merge*
|
|
||||||
(for/list ([i (add1 sz)])
|
|
||||||
(cons (@bveq (bv i t) idx)
|
|
||||||
|
|
||||||
(seq-get xs i)))))))
|
|
||||||
(define (#,(lift-id #'get-bv) xs idx)
|
|
||||||
(if (and (not (union? xs)) (bv-lit-or-term? idx))
|
|
||||||
(get-bv xs idx)
|
|
||||||
(match* (xs (bvcoerce idx 'get-bv))
|
|
||||||
[((not (? union? xs)) (? bv-lit-or-term? idx))
|
|
||||||
(get-bv xs idx)]
|
|
||||||
[(xs idx)
|
|
||||||
(for*/all ([xs xs][idx idx])
|
|
||||||
(get-bv xs idx))]))))]))
|
|
||||||
|
|
||||||
(define-get-bv take-bv take)
|
|
||||||
(define-get-bv take-right-bv take-right)
|
|
||||||
(define-get-bv drop-bv drop)
|
|
||||||
(define-get-bv drop-right-bv drop-right)
|
|
||||||
(define-get-bv list-tail-bv list-tail)
|
|
||||||
|
|
||||||
(define (@split-at-bv xs idx)
|
|
||||||
(values (@take-bv xs idx) (@drop-bv xs idx)))
|
|
||||||
|
|
||||||
(define (@split-at-right-bv xs idx)
|
|
||||||
(values (@drop-right-bv xs idx) (@take-right-bv xs idx)))
|
|
||||||
|
|
||||||
; ---- vector bv procedures ---- ;
|
|
||||||
|
|
||||||
(define (vector-set!-bv xs idx v)
|
|
||||||
(if (bv? idx)
|
|
||||||
(@vector-set! xs (@bitvector->natural idx) v)
|
|
||||||
(lift-body
|
|
||||||
#:with (vector-set!-bv xs idx vector-length)
|
|
||||||
#:type t
|
|
||||||
#:max n
|
|
||||||
#:body
|
|
||||||
(for ([x xs] [i n])
|
|
||||||
(@vector-set! xs i (merge (@bveq (bv i t) idx) v x))))))
|
|
||||||
|
|
||||||
(define-length-bv vector-length-bv @vector? vector? vector-length)
|
|
||||||
(define-ref-bv vector-ref-bv @vector? vector? vector-ref vector-length)
|
|
||||||
(define-lift-bv (vector-set!-bv xs idx v) @vector? vector?)
|
|
||||||
|
|
@ -99,7 +99,7 @@
|
||||||
|
|
||||||
;; List Iteration
|
;; List Iteration
|
||||||
(define (bad-lengths-error name . args)
|
(define (bad-lengths-error name . args)
|
||||||
(argument-error name "lists of equal length" (map ~.a args)))
|
(thunk (error name "all lists must have same size\n given: ~a" (map ~.a args))))
|
||||||
|
|
||||||
(define (lengths xs)
|
(define (lengths xs)
|
||||||
(match xs
|
(match xs
|
||||||
|
|
@ -168,9 +168,7 @@
|
||||||
(iterator-next l1 (f (car l1) (car l2)) (loop (cdr l1) (cdr l2)))))]
|
(iterator-next l1 (f (car l1) (car l2)) (loop (cdr l1) (cdr l2)))))]
|
||||||
[(f l . args)
|
[(f l . args)
|
||||||
(assert-arity-includes f (add1 (length args)) (quote id))
|
(assert-arity-includes f (add1 (length args)) (quote id))
|
||||||
(let ([len (length l)])
|
(assert (andmap (curry = (length l)) args) (apply bad-lengths-error (quote id) l args))
|
||||||
(assert (for/and ([arg args]) (= len (length arg)))
|
|
||||||
(apply bad-lengths-error (quote id) l args)))
|
|
||||||
(if (null? l)
|
(if (null? l)
|
||||||
(iterator-next)
|
(iterator-next)
|
||||||
(let loop ([l l] [args args])
|
(let loop ([l l] [args args])
|
||||||
|
|
@ -282,6 +280,20 @@
|
||||||
(@+ rank (@if (ranked>? (key-of x) i (key-of y) j) 1 0))))])
|
(@+ rank (@if (ranked>? (key-of x) i (key-of y) j) 1 0))))])
|
||||||
(for/list ([i len])
|
(for/list ([i len])
|
||||||
(for/fold ([v 0]) ([x xs] [r ranks]) (merge (@= i r) x v))))]))
|
(for/fold ([v 0]) ([x xs] [r ranks]) (merge (@= i r) x v))))]))
|
||||||
|
#|(define vars (for/list ([i (in-range len)]) (define-symbolic* rank @integer?) rank))
|
||||||
|
(for ([v vars])
|
||||||
|
(assert (@<= 0 v))
|
||||||
|
(assert (@< v len)))
|
||||||
|
(let loop ([vars vars] [xs l])
|
||||||
|
(match* (vars xs)
|
||||||
|
[((or (list) (list _)) _) (void)]
|
||||||
|
[((list v v-rest ...) (list x x-rest ...))
|
||||||
|
(let ([key (key-of x)])
|
||||||
|
(for ([v1 v-rest] [x1 x-rest])
|
||||||
|
(assert (@if (less? key (key-of x1)) (@< v v1) (@< v1 v)))))
|
||||||
|
(loop v-rest x-rest)]))
|
||||||
|
(for/list ([i (in-range (length l))])
|
||||||
|
(apply merge* (for/list ([x l] [v vars]) (cons (@= v i) x))))]))|#
|
||||||
(define (fast-sort less? getkey cache-keys? xs)
|
(define (fast-sort less? getkey cache-keys? xs)
|
||||||
(sort xs less? #:key getkey #:cache-keys? cache-keys?))
|
(sort xs less? #:key getkey #:cache-keys? cache-keys?))
|
||||||
(define/lift/applicator fast-sort less? getkey cache-keys? xs)
|
(define/lift/applicator fast-sort less? getkey cache-keys? xs)
|
||||||
|
|
@ -310,7 +322,7 @@
|
||||||
[else (let ([a (car l)]) (@if (f a) a (loop (cdr l))))])))]
|
[else (let ([a (car l)]) (@if (f a) a (loop (cdr l))))])))]
|
||||||
(define/lift/applicator memf f list)
|
(define/lift/applicator memf f list)
|
||||||
(define/lift/applicator findf f list)
|
(define/lift/applicator findf f list)
|
||||||
(define (@member x xs [is-equal? @equal?]) (@memf (curry is-equal? x) xs))
|
(define (@member x xs) (@memf (curry @equal? x) xs))
|
||||||
(define (@memq x xs) (@memf (curry @eq? x) xs))
|
(define (@memq x xs) (@memf (curry @eq? x) xs))
|
||||||
(define @assoc (case-lambda [(x xs) (@findf (compose (curry @equal? x) @car) xs)]
|
(define @assoc (case-lambda [(x xs) (@findf (compose (curry @equal? x) @car) xs)]
|
||||||
[(x xs eq?) (assert-arity-includes eq? 2 'assoc)
|
[(x xs eq?) (assert-arity-includes eq? 2 'assoc)
|
||||||
|
|
@ -409,8 +421,7 @@
|
||||||
(define @cons? @pair?)
|
(define @cons? @pair?)
|
||||||
|
|
||||||
(define @flatten
|
(define @flatten
|
||||||
(match-lambda [(union vs) (merge** vs @flatten)]
|
(match-lambda [(union vs) (merge** vs flatten)]
|
||||||
[(cons x y) (@append (@flatten x) (@flatten y))]
|
|
||||||
[other (flatten other)]))
|
[other (flatten other)]))
|
||||||
|
|
||||||
(define @append*
|
(define @append*
|
||||||
|
|
@ -429,8 +440,8 @@
|
||||||
|
|
||||||
|
|
||||||
(define @apply
|
(define @apply
|
||||||
(case-lambda [() (assert #f (argument-error 'apply "at least 2 arguments" 0))]
|
(case-lambda [() (error 'apply "arity mismatch;\n expected: at least 2\n given: 0")]
|
||||||
[(proc) (assert #f (argument-error 'apply "at least 2 arguments" 1))]
|
[(proc) (error 'apply "arity mismatch;\n expected: at least 2\n given: 1")]
|
||||||
[(proc xs) (lift/apply/higher-order apply proc xs : list? -> @list?)]
|
[(proc xs) (lift/apply/higher-order apply proc xs : list? -> @list?)]
|
||||||
[(proc x0 xs) (lift/apply/higher-order apply proc x0 xs : list? -> @list?)]
|
[(proc x0 xs) (lift/apply/higher-order apply proc x0 xs : list? -> @list?)]
|
||||||
[(proc x0 x1 xs) (lift/apply/higher-order apply proc x0 x1 xs : list? -> @list?)]
|
[(proc x0 x1 xs) (lift/apply/higher-order apply proc x0 x1 xs : list? -> @list?)]
|
||||||
|
|
@ -510,26 +521,29 @@
|
||||||
(merge** ys (insert* _ i v))]))))
|
(merge** ys (insert* _ i v))]))))
|
||||||
|
|
||||||
(splicing-local
|
(splicing-local
|
||||||
[(define ($list-set xs i v)
|
[(define (replace xs i v)
|
||||||
(for/list ([(x idx) (in-indexed xs)])
|
(let-values ([(left right) (split-at xs i)])
|
||||||
(merge (@= i idx) v x)))]
|
(append left (cons v (cdr right)))))
|
||||||
(define (@list-set xs i v)
|
(define (replace* xs i v)
|
||||||
(or (and (list? xs) (number? i) (list-set xs i v))
|
(apply merge* (for/list ([(x idx) (in-indexed xs)])
|
||||||
(match* ((type-cast @list? xs 'list-set) (type-cast @integer? i 'list-set))
|
(cons (@= i idx) (replace xs idx v)))))]
|
||||||
[((? list? xs) (? number? i)) (list-set xs i v)]
|
(define (@replace xs i v)
|
||||||
|
(or (and (list? xs) (number? i) (replace xs i v))
|
||||||
|
(match* ((type-cast @list? xs 'replace) (type-cast @integer? i 'replace))
|
||||||
|
[((? list? xs) (? number? i)) (replace xs i v)]
|
||||||
[((? list? xs) i)
|
[((? list? xs) i)
|
||||||
(assert-bound [0 @<= i @< (length xs)] 'list-set)
|
(assert-bound [0 @<= i @< (length xs)] 'replace)
|
||||||
($list-set xs i v)]
|
(replace* xs i v)]
|
||||||
[((union ys) (? number? i))
|
[((union ys) (? number? i))
|
||||||
(assert-bound [0 <= i] 'list-set)
|
(assert-bound [0 <= i] 'replace)
|
||||||
(apply merge* (assert-some
|
(apply merge* (assert-some
|
||||||
(for/list ([y ys] #:when (< i (length (cdr y))))
|
(for/list ([y ys] #:when (< i (length (cdr y))))
|
||||||
(cons (car y) (list-set (cdr y) i v)))
|
(cons (car y) (replace (cdr y) i v)))
|
||||||
#:unless (length ys)
|
#:unless (length ys)
|
||||||
(index-too-large-error 'list-set xs i)))]
|
(index-too-large-error 'replace xs i)))]
|
||||||
[((union ys) i)
|
[((union ys) i)
|
||||||
(assert-bound [0 @<= i @< (@length xs)] 'list-set)
|
(assert-bound [0 @<= i @< (@length xs)] 'replace)
|
||||||
(merge** ys ($list-set _ i v))]))))
|
(merge** ys (replace* _ i v))]))))
|
||||||
|
|
||||||
|
|
||||||
#|
|
#|
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
"../core/safe.rkt" "../core/lift.rkt" "seq.rkt" "../core/forall.rkt" "generic.rkt"
|
"../core/safe.rkt" "../core/lift.rkt" "seq.rkt" "../core/forall.rkt" "generic.rkt"
|
||||||
(only-in "list.rkt" @list?)
|
(only-in "list.rkt" @list?)
|
||||||
(only-in "../form/control.rkt" @when)
|
(only-in "../form/control.rkt" @when)
|
||||||
(only-in "../core/store.rkt" store!)
|
(only-in "../core/effects.rkt" apply!)
|
||||||
(only-in "../core/term.rkt" define-lifted-type @any/c type-cast)
|
(only-in "../core/term.rkt" define-lifted-type @any/c type-cast)
|
||||||
(only-in "../core/equality.rkt" @eq? @equal?)
|
(only-in "../core/equality.rkt" @eq? @equal?)
|
||||||
(only-in "../core/bool.rkt" instance-of? && ||)
|
(only-in "../core/bool.rkt" instance-of? && ||)
|
||||||
|
|
@ -58,15 +58,16 @@
|
||||||
|
|
||||||
(define (merge-set! vec idx val guard)
|
(define (merge-set! vec idx val guard)
|
||||||
(for ([i (in-range (vector-length vec))])
|
(for ([i (in-range (vector-length vec))])
|
||||||
(store! vec i (merge (&& guard (@= i idx)) val (vector-ref vec i)) vector-ref vector-set!)))
|
(apply! vector-set! vector-ref
|
||||||
|
vec i (merge (&& guard (@= i idx)) val (vector-ref vec i)))))
|
||||||
|
|
||||||
(define (@vector-set! vec idx val)
|
(define (@vector-set! vec idx val)
|
||||||
;(printf "vector-set! ~a ~a ~a\n" (eq-hash-code vec) idx val)
|
;(printf "vector-set! ~a ~a ~a\n" (eq-hash-code vec) idx val)
|
||||||
(if (and (vector? vec) (number? idx))
|
(if (and (vector? vec) (number? idx))
|
||||||
(store! vec idx val vector-ref vector-set!)
|
(apply! vector-set! vector-ref vec idx val)
|
||||||
(match* ((type-cast @vector? vec 'vector-set!) (type-cast @integer? idx 'vector-set!))
|
(match* ((type-cast @vector? vec 'vector-set!) (type-cast @integer? idx 'vector-set!))
|
||||||
[((? vector? vs) (? number? idx))
|
[((? vector? vs) (? number? idx))
|
||||||
(store! vs idx val vector-ref vector-set!)]
|
(apply! vector-set! vector-ref vs idx val)]
|
||||||
[((? vector? vs) idx)
|
[((? vector? vs) idx)
|
||||||
(assert-bound [0 @<= idx @< (vector-length vs)] 'vector-set!)
|
(assert-bound [0 @<= idx @< (vector-length vs)] 'vector-set!)
|
||||||
(merge-set! vs idx val #t)]
|
(merge-set! vs idx val #t)]
|
||||||
|
|
@ -75,26 +76,28 @@
|
||||||
(assert-|| (for/list ([v vs] #:when (< idx (vector-length (cdr v))))
|
(assert-|| (for/list ([v vs] #:when (< idx (vector-length (cdr v))))
|
||||||
(let ([guard (car v)]
|
(let ([guard (car v)]
|
||||||
[vec (cdr v)])
|
[vec (cdr v)])
|
||||||
(store! vec idx (merge guard val (vector-ref vec idx)) vector-ref vector-set!)
|
(apply! vector-set! vector-ref
|
||||||
|
vec idx (merge guard val (vector-ref vec idx)))
|
||||||
guard))
|
guard))
|
||||||
#:unless (length vs)
|
#:unless (length vs)
|
||||||
(index-too-large-error 'vector-set! vec idx))]
|
(index-too-large-error 'vector-set! vec idx))]
|
||||||
[((union vs) idx)
|
[((union vs) idx)
|
||||||
(assert-bound [0 @<= idx @< (merge** vs vector-length)] 'vector-set!)
|
(assert-bound [0 @<= idx @< (merge** vs vector-length)] 'vector-set!)
|
||||||
(for ([v vs])
|
(for/list ([v vs])
|
||||||
(merge-set! (cdr v) idx val (car v)))])))
|
(and (merge-set! (cdr v) idx val (car v)) (car v)))])))
|
||||||
|
|
||||||
(define (@vector-fill! vec val)
|
(define (@vector-fill! vec val)
|
||||||
(match (type-cast @vector? vec 'vector-fill!)
|
(match (type-cast @vector? vec 'vector-fill!)
|
||||||
[(? vector? vs)
|
[(? vector? vs)
|
||||||
(for ([i (in-range (vector-length vs))])
|
(for ([i (in-range (vector-length vs))])
|
||||||
(store! vs i val vector-ref vector-set!))]
|
(apply! vector-set! vector-ref vs i val))]
|
||||||
[(union vs)
|
[(union vs)
|
||||||
(for ([v vs])
|
(for ([v vs])
|
||||||
(let ([guard (car v)]
|
(let ([guard (car v)]
|
||||||
[vec (cdr v)])
|
[vec (cdr v)])
|
||||||
(for ([i (in-range (vector-length vec))])
|
(for ([i (in-range (vector-length vec))])
|
||||||
(store! vec i (merge guard val (vector-ref vec i)) vector-ref vector-set!))))]))
|
(apply! vector-set! vector-ref
|
||||||
|
vec i (merge guard val (vector-ref vec i))))))]))
|
||||||
|
|
||||||
; Vector copy helper procedure. Requires dest and src to be
|
; Vector copy helper procedure. Requires dest and src to be
|
||||||
; vectors (rather than unions of vectors), and dest-start, src-start
|
; vectors (rather than unions of vectors), and dest-start, src-start
|
||||||
|
|
|
||||||
|
|
@ -4,21 +4,19 @@
|
||||||
(require
|
(require
|
||||||
(for-syntax racket/syntax (only-in "core/lift.rkt" drop@))
|
(for-syntax racket/syntax (only-in "core/lift.rkt" drop@))
|
||||||
racket/provide
|
racket/provide
|
||||||
"core/bool.rkt" "core/real.rkt" "core/numerics.rkt" "core/bitvector.rkt" "core/bvlib.rkt"
|
"core/bool.rkt" "core/real.rkt" "core/numerics.rkt" "core/bitvector.rkt"
|
||||||
"core/function.rkt"
|
"core/procedure.rkt" "core/equality.rkt" "core/reflect.rkt"
|
||||||
"core/procedure.rkt" "core/equality.rkt" "core/distinct.rkt" "core/reflect.rkt"
|
"adt/box.rkt" "adt/list.rkt" "adt/vector.rkt"
|
||||||
"adt/box.rkt" "adt/list.rkt" "adt/vector.rkt" "adt/bvseq.rkt"
|
|
||||||
"struct/struct.rkt" "struct/generics.rkt"
|
"struct/struct.rkt" "struct/generics.rkt"
|
||||||
"form/define.rkt" "form/control.rkt" "form/module.rkt" "form/app.rkt")
|
"form/state.rkt" "form/define.rkt" "form/control.rkt" "form/module.rkt" "form/app.rkt")
|
||||||
|
|
||||||
(provide
|
(provide
|
||||||
(rename-out [@|| ||]) ; The character sequence || does not play nicely with the filtered-out form.
|
(rename-out [@|| ||]) ; The character sequence || does not play nicely with the filtered-out form.
|
||||||
(filtered-out drop@
|
(filtered-out drop@
|
||||||
(combine-out
|
(combine-out
|
||||||
; core/bool.rkt
|
; core/bool.rkt
|
||||||
vc with-vc clear-vc! vc? vc-true? vc-true vc-assumes vc-asserts
|
pc with-asserts with-asserts-only asserts clear-asserts!
|
||||||
@assert @assume
|
@assert @boolean? @false? @! @&& @=> @<=>
|
||||||
@boolean? @false? @! @&& @=> @<=> @forall @exists
|
|
||||||
; core/real.rkt
|
; core/real.rkt
|
||||||
@integer? @real? @= @< @<= @>= @>
|
@integer? @real? @= @< @<= @>= @>
|
||||||
@+ @* @- @/ @quotient @remainder @modulo @abs
|
@+ @* @- @/ @quotient @remainder @modulo @abs
|
||||||
|
|
@ -28,34 +26,21 @@
|
||||||
@add1 @sub1 @sgn @truncate @floor @ceiling @min @max
|
@add1 @sub1 @sgn @truncate @floor @ceiling @min @max
|
||||||
@exact->inexact @inexact->exact @expt
|
@exact->inexact @inexact->exact @expt
|
||||||
; core/bitvector.rkt
|
; core/bitvector.rkt
|
||||||
bv @bv? bitvector bitvector-size bitvector?
|
bv bv? bitvector bitvector-size bitvector?
|
||||||
@bveq @bvslt @bvsgt @bvsle @bvsge @bvult @bvugt @bvule @bvuge
|
@bveq @bvslt @bvsgt @bvsle @bvsge @bvult @bvugt @bvule @bvuge
|
||||||
@bvnot @bvor @bvand @bvxor @bvshl @bvlshr @bvashr
|
@bvnot @bvor @bvand @bvxor @bvshl @bvlshr @bvashr
|
||||||
@bvneg @bvadd @bvsub @bvmul @bvudiv @bvsdiv @bvurem @bvsrem @bvsmod
|
@bvneg @bvadd @bvsub @bvmul @bvudiv @bvsdiv @bvurem @bvsrem @bvsmod
|
||||||
@concat @extract @sign-extend @zero-extend
|
@concat @extract @sign-extend @zero-extend
|
||||||
@z3_ext_rotate_left @z3_ext_rotate_right
|
|
||||||
@integer->bitvector @bitvector->integer @bitvector->natural
|
@integer->bitvector @bitvector->integer @bitvector->natural
|
||||||
; core/bvlib.rkt
|
|
||||||
bit lsb msb bvzero? bvadd1 bvsub1
|
|
||||||
bvsmin bvsmax bvumin bvumax
|
|
||||||
rotate-left rotate-right bvrol bvror
|
|
||||||
bool->bitvector bitvector->bool bitvector->bits
|
|
||||||
; core/function.rkt
|
|
||||||
@fv? ~> function?
|
|
||||||
; core/distinct.rkt
|
|
||||||
@distinct?
|
|
||||||
; core/equality.rkt
|
; core/equality.rkt
|
||||||
@eq? @equal?
|
@eq? @equal?
|
||||||
; core/reflect.rkt
|
; core/reflect.rkt
|
||||||
symbolics type? solvable? @any/c type-of type-cast for/all for*/all
|
symbolics type? type-of type-cast for/all for*/all
|
||||||
symbolic? concrete?
|
|
||||||
term? constant? expression?
|
term? constant? expression?
|
||||||
term expression constant term-type
|
term expression constant term-type
|
||||||
term=? term->datum
|
term=? term->datum clear-terms! term-cache
|
||||||
terms terms-count terms-ref with-terms clear-terms! gc-terms!
|
|
||||||
union? union union-contents union-guards union-values
|
union? union union-contents union-guards union-values
|
||||||
union-filter in-union in-union* in-union-guards in-union-values
|
union-filter in-union in-union* in-union-guards in-union-values
|
||||||
result? result-value result-state normal normal? failed failed?
|
|
||||||
; adt/box.rkt
|
; adt/box.rkt
|
||||||
@box @box-immutable @box? @unbox @set-box!
|
@box @box-immutable @box? @unbox @set-box!
|
||||||
; adt/list.rkt : Pair Constructors and Selectors
|
; adt/list.rkt : Pair Constructors and Selectors
|
||||||
|
|
@ -79,34 +64,30 @@
|
||||||
@take @drop @split-at @take-right @drop-right @split-at-right
|
@take @drop @split-at @take-right @drop-right @split-at-right
|
||||||
@add-between @append* @flatten @remove-duplicates
|
@add-between @append* @flatten @remove-duplicates
|
||||||
@filter-map @count @partition @append-map @filter-not @shuffle
|
@filter-map @count @partition @append-map @filter-not @shuffle
|
||||||
@argmin @argmax @list-set
|
@argmin @argmax
|
||||||
; adt/list.rkt : Non-Standard Functions
|
; adt/list.rkt : Non-Standard Functions
|
||||||
@insert
|
@insert @replace
|
||||||
; adt/vector.rkt : Basic Functions
|
; adt/vector.rkt : Basic Functions
|
||||||
@vector? @vector @vector-immutable
|
@vector? @vector @vector-immutable
|
||||||
@vector-length @vector-ref @vector-set! @vector->list @list->vector @vector->immutable-vector
|
@vector-length @vector-ref @vector-set! @vector->list @vector->immutable-vector
|
||||||
@vector-fill! @vector-copy!
|
@vector-fill! @vector-copy!
|
||||||
; adt/vector.rkt : Additional Vector Functions
|
; adt/vector.rkt : Additional Vector Functions
|
||||||
@vector-append
|
@vector-append
|
||||||
; adt/procedure.rkt
|
; adt/procedure.rkt
|
||||||
@procedure? @apply @procedure-rename @negate @void?
|
@procedure? @apply @procedure-rename @negate @void?
|
||||||
; adt/bvseq.rkt
|
|
||||||
@list-ref-bv @list-set-bv @length-bv
|
|
||||||
@take-bv @take-right-bv
|
|
||||||
@drop-bv @drop-right-bv @list-tail-bv
|
|
||||||
@split-at-bv @split-at-right-bv
|
|
||||||
@vector-ref-bv @vector-set!-bv @vector-length-bv
|
|
||||||
; struct/struct.rkt
|
; struct/struct.rkt
|
||||||
struct struct-field-index define/generic define-struct
|
struct struct-field-index define/generic define-struct
|
||||||
; struct/generics.rkt
|
; struct/generics.rkt
|
||||||
@define-generics @make-struct-type-property
|
@define-generics @make-struct-type-property
|
||||||
|
; form/state.rkt
|
||||||
|
current-oracle oracle oracle?
|
||||||
; form/define.rkt
|
; form/define.rkt
|
||||||
define-symbolic define-symbolic*
|
define-symbolic define-symbolic*
|
||||||
; form/control.rkt
|
; form/control.rkt
|
||||||
@if @and @or @not @nand @nor @xor @implies
|
@if @and @or @not @nand @nor @xor @implies
|
||||||
@unless @when @cond @case else
|
@unless @when @cond @case else
|
||||||
; form/module.rkt
|
; form/module.rkt
|
||||||
@#%module-begin @#%top-interaction @module @module* @module+
|
@#%module-begin @#%top-interaction @module @module @module+
|
||||||
; form/app.rkt
|
; form/app.rkt
|
||||||
#%app #%plain-app
|
#%app #%plain-app
|
||||||
)))
|
)))
|
||||||
|
|
@ -173,7 +154,7 @@
|
||||||
expand-syntax-to-top-form
|
expand-syntax-to-top-form
|
||||||
; input and output
|
; input and output
|
||||||
read read-syntax
|
read read-syntax
|
||||||
write display print writeln displayln println fprintf printf eprintf format newline
|
write display print displayln fprintf printf eprintf format newline
|
||||||
pretty-print pretty-write pretty-display pretty-format
|
pretty-print pretty-write pretty-display pretty-format
|
||||||
call-with-input-file
|
call-with-input-file
|
||||||
current-input-port current-output-port current-error-port eof
|
current-input-port current-output-port current-error-port eof
|
||||||
|
|
|
||||||
|
|
@ -1,32 +1,31 @@
|
||||||
#lang racket
|
#lang racket
|
||||||
|
|
||||||
(require racket/stxparam racket/stxparam-exptime
|
(require (for-syntax racket/syntax) racket/stxparam racket/stxparam-exptime)
|
||||||
(for-syntax racket/syntax syntax/transformer))
|
|
||||||
(require "term.rkt" "union.rkt" "bool.rkt" "polymorphic.rkt"
|
(require "term.rkt" "union.rkt" "bool.rkt" "polymorphic.rkt"
|
||||||
"merge.rkt" "safe.rkt" "lift.rkt" "forall.rkt")
|
"merge.rkt" "safe.rkt" "lift.rkt" "forall.rkt")
|
||||||
(require (only-in "real.rkt" @>= @> @= @integer? T*->integer?))
|
(require (only-in "real.rkt" @>= @> @= @integer? T*->integer?))
|
||||||
|
|
||||||
(provide
|
(provide
|
||||||
(rename-out [lift-op bvlift-op]) bvcoerce
|
(rename-out [@bv bv]) bv? bv-value bv-type
|
||||||
(rename-out [@bv bv]) @bv? bv? bv-value bv-type
|
|
||||||
(rename-out [@bitvector bitvector]) bitvector-size bitvector?
|
(rename-out [@bitvector bitvector]) bitvector-size bitvector?
|
||||||
@bveq @bvslt @bvsgt @bvsle @bvsge @bvult @bvugt @bvule @bvuge
|
@bveq @bvslt @bvsgt @bvsle @bvsge @bvult @bvugt @bvule @bvuge
|
||||||
@bvnot @bvor @bvand @bvxor @bvshl @bvlshr @bvashr
|
@bvnot @bvor @bvand @bvxor @bvshl @bvlshr @bvashr
|
||||||
@bvneg @bvadd @bvsub @bvmul @bvudiv @bvsdiv @bvurem @bvsrem @bvsmod
|
@bvneg @bvadd @bvsub @bvmul @bvudiv @bvsdiv @bvurem @bvsrem @bvsmod
|
||||||
@concat @extract @sign-extend @zero-extend
|
@concat @extract @sign-extend @zero-extend @integer->bitvector @bitvector->integer @bitvector->natural)
|
||||||
@z3_ext_rotate_left @z3_ext_rotate_right
|
|
||||||
@integer->bitvector @bitvector->integer @bitvector->natural)
|
|
||||||
|
|
||||||
;; ----------------- Bitvector Types ----------------- ;;
|
;; ----------------- Bitvector Types ----------------- ;;
|
||||||
|
|
||||||
; Cache of all bitvector types constructed so far, mapping sizes to types.
|
; Cache of all bitvector types constructed so far, mapping sizes to types.
|
||||||
(define bitvector-types (make-hasheq))
|
(define bitvector-types (make-hash))
|
||||||
|
|
||||||
; Returns the bitvector type of the given size.
|
; Returns the bitvector type of the given size.
|
||||||
(define (bitvector-type size)
|
(define (bitvector-type size)
|
||||||
(assert (and (exact-positive-integer? size) (fixnum? size))
|
(unless (exact-positive-integer? size)
|
||||||
(argument-error 'bitvector "(and/c exact-positive-integer? fixnum?)" size))
|
(raise-argument-error 'bitvector "exact-positive-integer?" size))
|
||||||
(hash-ref! bitvector-types size (λ () (bitvector size))))
|
(or (hash-ref bitvector-types size #f)
|
||||||
|
(let ([t (bitvector size)])
|
||||||
|
(hash-set! bitvector-types size t)
|
||||||
|
t)))
|
||||||
|
|
||||||
; Represents a bitvector type.
|
; Represents a bitvector type.
|
||||||
(struct bitvector (size)
|
(struct bitvector (size)
|
||||||
|
|
@ -51,9 +50,9 @@
|
||||||
[(bv _ (== self)) v]
|
[(bv _ (== self)) v]
|
||||||
[(term _ (== self)) v]
|
[(term _ (== self)) v]
|
||||||
[(union (list _ ... (cons gt (and (? typed? vt) (app get-type (== self)))) _ ...) _)
|
[(union (list _ ... (cons gt (and (? typed? vt) (app get-type (== self)))) _ ...) _)
|
||||||
(assert gt (type-error caller self v))
|
(assert gt (thunk (error caller "expected ~a, given ~.a" self v)))
|
||||||
vt]
|
vt]
|
||||||
[_ (assert #f (type-error caller self v))]))
|
[_ (assert #f (thunk (error caller "expected ~a, given ~.a" self v)))]))
|
||||||
(define (type-eq? self u v) (@bveq u v))
|
(define (type-eq? self u v) (@bveq u v))
|
||||||
(define (type-equal? self u v) (@bveq u v))
|
(define (type-equal? self u v) (@bveq u v))
|
||||||
(define (type-compress self f? ps) (generic-merge* ps))
|
(define (type-compress self f? ps) (generic-merge* ps))
|
||||||
|
|
@ -65,13 +64,15 @@
|
||||||
(define (solvable-range self) self)]
|
(define (solvable-range self) self)]
|
||||||
#:methods gen:custom-write
|
#:methods gen:custom-write
|
||||||
[(define (write-proc self port m)
|
[(define (write-proc self port m)
|
||||||
(fprintf port "(bitvector ~a)" (bitvector-size self)))])
|
(fprintf port "(bitvector? ~a)" (bitvector-size self)))])
|
||||||
|
|
||||||
; Pattern matching for bitvector types.
|
; Pattern matching for bitvector types.
|
||||||
(define-match-expander @bitvector
|
(define-match-expander @bitvector
|
||||||
(syntax-rules ()
|
(syntax-rules ()
|
||||||
[(_ sz) (bitvector sz)])
|
[(_ sz) (bitvector sz)])
|
||||||
(make-variable-like-transformer #'bitvector-type))
|
(syntax-id-rules (set!)
|
||||||
|
[(@bitvector sz) (bitvector-type sz)]
|
||||||
|
[@bitvector bitvector-type]))
|
||||||
|
|
||||||
(define (bvsmin t) (- (expt 2 (- (bitvector-size t) 1))))
|
(define (bvsmin t) (- (expt 2 (- (bitvector-size t) 1))))
|
||||||
(define (bvsmin? b) (and (bv? b) (= (bv-value b) (bvsmin (bv-type b)))))
|
(define (bvsmin? b) (and (bv? b) (= (bv-value b) (bvsmin (bv-type b)))))
|
||||||
|
|
@ -86,17 +87,11 @@
|
||||||
#:transparent
|
#:transparent
|
||||||
#:methods gen:typed
|
#:methods gen:typed
|
||||||
[(define (get-type self) (bv-type self))]
|
[(define (get-type self) (bv-type self))]
|
||||||
#:property prop:custom-print-quotable 'never
|
|
||||||
#:methods gen:custom-write
|
#:methods gen:custom-write
|
||||||
[(define (write-proc self port mode)
|
[(define (write-proc self port mode)
|
||||||
(match self
|
(fprintf port "(bv ~a ~a)"
|
||||||
[(bv v (bitvector bw))
|
(bv-value self)
|
||||||
(let*-values ([(q r) (quotient/remainder bw 4)]
|
(bitvector-size (bv-type self))))])
|
||||||
[(p b mw) (if (zero? r) (values "x" 16 q) (values "b" 2 bw))])
|
|
||||||
(fprintf port "(bv #~a~a ~a)"
|
|
||||||
p
|
|
||||||
(~r (ufinitize v bw) #:base b #:pad-string "0" #:min-width mw)
|
|
||||||
bw))]))])
|
|
||||||
|
|
||||||
; Returns a signed representation of the given number, using the specified bitwidth.
|
; Returns a signed representation of the given number, using the specified bitwidth.
|
||||||
; Assumes that val is a real, non-infinite, non-NaN number.
|
; Assumes that val is a real, non-infinite, non-NaN number.
|
||||||
|
|
@ -119,29 +114,22 @@
|
||||||
; be either an exact-positive-integer? or a bitvector type.
|
; be either an exact-positive-integer? or a bitvector type.
|
||||||
; The number may be a real, non-infinite, non-NaN concrete value.
|
; The number may be a real, non-infinite, non-NaN concrete value.
|
||||||
(define (make-bv val precision)
|
(define (make-bv val precision)
|
||||||
(assert (and (real? val) (not (infinite? val)) (not (nan? val)))
|
(unless (and (real? val) (not (infinite? val)) (not (nan? val)))
|
||||||
(arguments-error 'bv "expected a real, non-infinite, non-NaN number" "value" val))
|
(raise-arguments-error 'bv "expected a real, non-infinite, non-NaN number" "value" val))
|
||||||
(cond [(exact-positive-integer? precision)
|
(cond [(exact-positive-integer? precision)
|
||||||
(bv (sfinitize val precision) (bitvector-type precision))]
|
(bv (sfinitize val precision) (bitvector-type precision))]
|
||||||
[(bitvector? precision)
|
[(bitvector? precision)
|
||||||
(bv (sfinitize val (bitvector-size precision)) precision)]
|
(bv (sfinitize val (bitvector-size precision)) precision)]
|
||||||
[else
|
[else
|
||||||
(assert #f (arguments-error 'bv "exact-positive-integer? or bitvector? type" "precision" precision))]))
|
(raise-arguments-error 'bv "exact-positive-integer? or bitvector? type" "precision" precision)]))
|
||||||
|
|
||||||
; Pattern matching for bitvector literals.
|
; Pattern matching for bitvector literals.
|
||||||
(define-match-expander @bv
|
(define-match-expander @bv
|
||||||
(syntax-rules ()
|
(syntax-rules ()
|
||||||
[(_ val-pat type-pat) (bv val-pat type-pat)])
|
[(_ val-pat type-pat) (bv val-pat type-pat)])
|
||||||
(make-variable-like-transformer #'make-bv))
|
(syntax-id-rules (set!)
|
||||||
|
[(@bv v t) (make-bv v t)]
|
||||||
(define (@bv? v)
|
[@bv make-bv]))
|
||||||
(match v
|
|
||||||
[(? bv?) #t]
|
|
||||||
[(term _ (? bitvector?)) #t]
|
|
||||||
[(union _ (? bitvector?)) #t]
|
|
||||||
[(union xs (== @any/c))
|
|
||||||
(apply || (for/list ([gv xs] #:when (@bv? (cdr gv))) (car gv)))]
|
|
||||||
[_ #f]))
|
|
||||||
|
|
||||||
|
|
||||||
;; ----------------- Lifitng Utilities ----------------- ;;
|
;; ----------------- Lifitng Utilities ----------------- ;;
|
||||||
|
|
@ -195,6 +183,7 @@
|
||||||
[_ (loop rest)])]
|
[_ (loop rest)])]
|
||||||
[(list _ rest ...)
|
[(list _ rest ...)
|
||||||
(loop rest)]))
|
(loop rest)]))
|
||||||
|
#:unless (max (length xs) (length ys))
|
||||||
#:error (bitvector-type-error (object-name op) x y))]
|
#:error (bitvector-type-error (object-name op) x y))]
|
||||||
[(_ _) (assert #f (bitvector-type-error (object-name op) x y))]))
|
[(_ _) (assert #f (bitvector-type-error (object-name op) x y))]))
|
||||||
|
|
||||||
|
|
@ -218,6 +207,7 @@
|
||||||
[_ (loop rest)])]
|
[_ (loop rest)])]
|
||||||
[(list _ rest ...)
|
[(list _ rest ...)
|
||||||
(loop rest)]))
|
(loop rest)]))
|
||||||
|
#:unless (apply max (length vs) (map length ws))
|
||||||
#:error (apply bitvector-type-error (object-name op) xs))]
|
#:error (apply bitvector-type-error (object-name op) xs))]
|
||||||
[_ (assert #f (apply bitvector-type-error (object-name op) xs))]))
|
[_ (assert #f (apply bitvector-type-error (object-name op) xs))]))
|
||||||
|
|
||||||
|
|
@ -339,13 +329,6 @@
|
||||||
(ite (bveq (bv 0 t) (bvand x (bv (bvsmin t) t))) (bv 0 t) (bv -1 t))]
|
(ite (bveq (bv 0 t) (bvand x (bv (bvsmin t) t))) (bv 0 t) (bv -1 t))]
|
||||||
[(_ _) (expression @bvashr x y)]))
|
[(_ _) (expression @bvashr x y)]))
|
||||||
|
|
||||||
|
|
||||||
(define (z3_ext_rotate_left x y)
|
|
||||||
(expression @z3_ext_rotate_left x y))
|
|
||||||
|
|
||||||
(define (z3_ext_rotate_right x y)
|
|
||||||
(expression @z3_ext_rotate_right x y))
|
|
||||||
|
|
||||||
(define-lifted-operator @bvnot bvnot T*->T)
|
(define-lifted-operator @bvnot bvnot T*->T)
|
||||||
(define-lifted-operator @bvand bvand T*->T)
|
(define-lifted-operator @bvand bvand T*->T)
|
||||||
(define-lifted-operator @bvor bvor T*->T)
|
(define-lifted-operator @bvor bvor T*->T)
|
||||||
|
|
@ -353,8 +336,6 @@
|
||||||
(define-lifted-operator @bvshl bvshl T*->T)
|
(define-lifted-operator @bvshl bvshl T*->T)
|
||||||
(define-lifted-operator @bvlshr bvlshr T*->T)
|
(define-lifted-operator @bvlshr bvlshr T*->T)
|
||||||
(define-lifted-operator @bvashr bvashr T*->T)
|
(define-lifted-operator @bvashr bvashr T*->T)
|
||||||
(define-lifted-operator @z3_ext_rotate_left z3_ext_rotate_left T*->T)
|
|
||||||
(define-lifted-operator @z3_ext_rotate_right z3_ext_rotate_right T*->T)
|
|
||||||
|
|
||||||
;; ----------------- Simplification ruules for bitwise operators ----------------- ;;
|
;; ----------------- Simplification ruules for bitwise operators ----------------- ;;
|
||||||
|
|
||||||
|
|
@ -496,10 +477,8 @@
|
||||||
[((expression (== @bvadd) (expression (== @bvneg) (== y)) z) _) z]
|
[((expression (== @bvadd) (expression (== @bvneg) (== y)) z) _) z]
|
||||||
[((expression (== @bvadd) z (expression (== @bvneg) (== y))) _) z]
|
[((expression (== @bvadd) z (expression (== @bvneg) (== y))) _) z]
|
||||||
[((expression (== @bvadd) (bv a _) b) (bv (app - a) _)) b]
|
[((expression (== @bvadd) (bv a _) b) (bv (app - a) _)) b]
|
||||||
[((expression (== @bvadd) (? bv? a) b) (? bv?)) (@bvadd (@bvadd a y) b)]
|
|
||||||
[((expression (== @bvadd) a b) (expression (== @bvneg) a)) b]
|
[((expression (== @bvadd) a b) (expression (== @bvneg) a)) b]
|
||||||
[((expression (== @bvadd) a b) (expression (== @bvneg) b)) a]
|
[((expression (== @bvadd) a b) (expression (== @bvneg) b)) a]
|
||||||
[((expression (== ite) a (? bv? b) (? bv? c)) (? bv?)) (ite a (bvadd b y) (bvadd c y))]
|
|
||||||
[((expression (== @bvadd) a ...) (expression (== @bvadd) b ...))
|
[((expression (== @bvadd) a ...) (expression (== @bvadd) b ...))
|
||||||
(let ([alen (length a)]
|
(let ([alen (length a)]
|
||||||
[blen (length b)])
|
[blen (length b)])
|
||||||
|
|
@ -591,19 +570,10 @@
|
||||||
[(_ _ (bv b _))
|
[(_ _ (bv b _))
|
||||||
(bv (sfinitize (bitwise-and (bitwise-not (arithmetic-shift -1 len)) (arithmetic-shift b (- j))) len)
|
(bv (sfinitize (bitwise-and (bitwise-not (arithmetic-shift -1 len)) (arithmetic-shift b (- j))) len)
|
||||||
(bitvector-type len))]
|
(bitvector-type len))]
|
||||||
[(_ _ (expression (== @extract) _ k a)) (extract (+ i k) (+ j k) a)]
|
[(_ 0 (expression (== @concat) _ (and (? typed? (app get-type (bitvector (== len)))) a))) a]
|
||||||
[(_ _ (expression (== @concat) _ (and (? typed? (app get-type (bitvector size))) a)))
|
[(_ _ (expression (== @concat)
|
||||||
#:when (< i size)
|
(and (? typed? (app get-type (bitvector (== len)))) a)
|
||||||
(extract i j a)]
|
(? typed? (app get-type (bitvector (== j)))))) a]
|
||||||
[(_ _ (expression (== @concat) a (? typed? (app get-type (bitvector size)))))
|
|
||||||
#:when (>= j size)
|
|
||||||
(extract (- i size) (- j size) a)]
|
|
||||||
[(_ 0 (expression (and @bvop (or (== @sign-extend) (== @zero-extend)))
|
|
||||||
(and (? typed? (app get-type (bitvector size))) a)
|
|
||||||
_))
|
|
||||||
(if (< i size)
|
|
||||||
(extract i j a)
|
|
||||||
(expression @bvop a (bitvector-type (add1 i))))]
|
|
||||||
[(_ _ _) (expression @extract i j x)]))
|
[(_ _ _) (expression @extract i j x)]))
|
||||||
|
|
||||||
(define-operator @extract
|
(define-operator @extract
|
||||||
|
|
@ -624,14 +594,9 @@
|
||||||
[((? number?) _) (merge+ (for*/list ([k (in-range i -1 -1)])
|
[((? number?) _) (merge+ (for*/list ([k (in-range i -1 -1)])
|
||||||
(cons (@= k j) (extract i k x)))
|
(cons (@= k j) (extract i k x)))
|
||||||
#:unless (+ i 1) #:error (extract*-err x i j))]
|
#:unless (+ i 1) #:error (extract*-err x i j))]
|
||||||
[(_ _)
|
[(_ _) (merge+ (for*/list ([n size] [k (add1 n)])
|
||||||
(if (equal? i j)
|
(cons (&& (@= n i) (@= k j)) (extract n k x)))
|
||||||
(merge+ (for*/list ([n size])
|
#:unless (+ size (/ (* size (- size 1)) 2)) #:error (extract*-err x i j))]))]
|
||||||
(cons (&& (@= n i) (@= n j)) (extract n n x)))
|
|
||||||
#:unless size #:error (extract*-err x i j))
|
|
||||||
(merge+ (for*/list ([n size] [k (add1 n)])
|
|
||||||
(cons (&& (@= n i) (@= k j)) (extract n k x)))
|
|
||||||
#:unless (+ size (/ (* size (- size 1)) 2)) #:error (extract*-err x i j)))]))]
|
|
||||||
(lambda (@i @j @x)
|
(lambda (@i @j @x)
|
||||||
(define i (type-cast @integer? @i 'extract))
|
(define i (type-cast @integer? @i 'extract))
|
||||||
(define j (type-cast @integer? @j 'extract))
|
(define j (type-cast @integer? @j 'extract))
|
||||||
|
|
@ -729,9 +694,9 @@
|
||||||
[(v (union ts))
|
[(v (union ts))
|
||||||
(merge+ (for/list ([gt ts] #:when (bitvector? (cdr gt)))
|
(merge+ (for/list ([gt ts] #:when (bitvector? (cdr gt)))
|
||||||
(cons (car gt) (integer->bitvector v (cdr gt))))
|
(cons (car gt) (integer->bitvector v (cdr gt))))
|
||||||
#:unless (length ts) #:error (arguments-error 'integer->bitvector "expected a bitvector type t" "t" @t))]
|
#:unless (length ts) #:error (arguments-error "expected a bitvector type t" "t" @t))]
|
||||||
[(v (? bitvector? t)) (integer->bitvector v t)]
|
[(v (? bitvector? t)) (integer->bitvector v t)]
|
||||||
[(_ _) (assert #f (arguments-error 'integer->bitvector "expected a bitvector type t" "t" @t))])))
|
[(_ _) (assert #f (arguments-error "expected a bitvector type t" "t" @t))])))
|
||||||
|
|
||||||
(define-operator @bitvector->integer
|
(define-operator @bitvector->integer
|
||||||
#:identifier 'bitvector->integer
|
#:identifier 'bitvector->integer
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,12 @@
|
||||||
#lang racket
|
#lang racket
|
||||||
|
|
||||||
(require "term.rkt" "union.rkt" "exn.rkt" "result.rkt" "reporter.rkt")
|
(require "term.rkt" "union.rkt")
|
||||||
|
|
||||||
(provide
|
(provide @boolean? @false?
|
||||||
;; ---- lifted boolean? operations ---- ;;
|
! && || => <=> @! @&& @|| @=> @<=>
|
||||||
@boolean? @false? @true?
|
and-&& or-|| instance-of?
|
||||||
! && || => <=> @! @&& @|| @=> @<=> @exists @forall
|
@assert pc with-asserts with-asserts-only
|
||||||
and-&& or-|| instance-of? T*->boolean?
|
(rename-out [export-asserts asserts]) clear-asserts!)
|
||||||
;; ---- VC generation ---- ;;
|
|
||||||
@assert @assume $assert $assume
|
|
||||||
(rename-out [get-vc vc]) clear-vc! merge-vc! with-vc
|
|
||||||
vc? vc-assumes vc-asserts
|
|
||||||
vc-true vc-true?)
|
|
||||||
|
|
||||||
;; ----------------- Boolean type ----------------- ;;
|
;; ----------------- Boolean type ----------------- ;;
|
||||||
(define-lifted-type @boolean?
|
(define-lifted-type @boolean?
|
||||||
|
|
@ -26,9 +21,9 @@
|
||||||
[(? boolean?) v]
|
[(? boolean?) v]
|
||||||
[(term _ (== self)) v]
|
[(term _ (== self)) v]
|
||||||
[(union : [g (and (or (? boolean?) (term _ (== self))) u)] _ ...)
|
[(union : [g (and (or (? boolean?) (term _ (== self))) u)] _ ...)
|
||||||
($assert g (argument-error caller "boolean?" v))
|
(@assert g (thunk (raise-argument-error caller "expected a boolean?" v)))
|
||||||
u]
|
u]
|
||||||
[_ ($assert #f (argument-error caller "boolean?" v))]))
|
[_ (@assert #f (thunk (raise-argument-error caller "expected a boolean?" v)))]))
|
||||||
(define (type-compress self force? ps)
|
(define (type-compress self force? ps)
|
||||||
(match ps
|
(match ps
|
||||||
[(list _) ps]
|
[(list _) ps]
|
||||||
|
|
@ -48,34 +43,15 @@
|
||||||
[(x y) (op (type-cast @boolean? x caller) (type-cast @boolean? y caller))]
|
[(x y) (op (type-cast @boolean? x caller) (type-cast @boolean? y caller))]
|
||||||
[xs (apply op (for/list ([x xs]) (type-cast @boolean? x caller)))])]))
|
[xs (apply op (for/list ([x xs]) (type-cast @boolean? x caller)))])]))
|
||||||
|
|
||||||
; A generic typing procedure for a lifted operator that takes N >= 0 arguments of type T
|
(define boolean?*->boolean? (const @boolean?))
|
||||||
; and returns a @boolean?. See term.rkt.
|
|
||||||
(define (T*->boolean? . xs) @boolean?)
|
|
||||||
|
|
||||||
(define-syntax-rule (define-lifted-operator @op $op)
|
(define-syntax-rule (define-lifted-operator @op $op)
|
||||||
(define-operator @op
|
(define-operator @op
|
||||||
#:identifier '$op
|
#:identifier '$op
|
||||||
#:range T*->boolean?
|
#:range boolean?*->boolean?
|
||||||
#:unsafe $op
|
#:unsafe $op
|
||||||
#:safe (lift-op $op)))
|
#:safe (lift-op $op)))
|
||||||
|
|
||||||
(define-syntax-rule (define-quantifier $op @op)
|
|
||||||
(begin
|
|
||||||
(define $op (quantifier @op))
|
|
||||||
(define-operator @op
|
|
||||||
#:identifier '$op
|
|
||||||
#:range T*->boolean?
|
|
||||||
#:unsafe $op
|
|
||||||
#:safe
|
|
||||||
(lambda (@vars @body)
|
|
||||||
(match* (@vars (type-cast @boolean? @body '$op))
|
|
||||||
[((list (constant _ (? primitive-solvable?)) (... ...)) body)
|
|
||||||
($op @vars body)]
|
|
||||||
[(_ _)
|
|
||||||
($assert
|
|
||||||
#f
|
|
||||||
(argument-error '$op "list of symbolic constants of primitive solvable types" @vars))])))))
|
|
||||||
|
|
||||||
;; ----------------- Basic boolean operators ----------------- ;;
|
;; ----------------- Basic boolean operators ----------------- ;;
|
||||||
(define (! x)
|
(define (! x)
|
||||||
(match x
|
(match x
|
||||||
|
|
@ -86,26 +62,9 @@
|
||||||
(define && (logical-connective @&& @|| #t #f))
|
(define && (logical-connective @&& @|| #t #f))
|
||||||
(define || (logical-connective @|| @&& #f #t))
|
(define || (logical-connective @|| @&& #f #t))
|
||||||
|
|
||||||
(define (=> x y) ; (|| (! x) y))
|
(define (=> x y) (|| (! x) y))
|
||||||
(cond
|
|
||||||
[(equal? x y) #t]
|
|
||||||
[(eq? x #f) #t]
|
|
||||||
[(eq? y #t) #t]
|
|
||||||
[(eq? x #t) y]
|
|
||||||
[(eq? y #f) (! x)]
|
|
||||||
[(cancel? x y) y]
|
|
||||||
[else
|
|
||||||
(match y
|
|
||||||
[(expression (== @||) _ ... (== x) _ ...) #t]
|
|
||||||
[(expression (== @&&) (== x) b) (=> x b)]
|
|
||||||
[(expression (== @&&) b (== x)) (=> x b)]
|
|
||||||
[(expression (== @&&) (expression (== @||) _ ... (== x) _ ...) b) (=> x b)]
|
|
||||||
[(expression (== @&&) b (expression (== @||) _ ... (== x) _ ...)) (=> x b)]
|
|
||||||
[(expression (== @<=>) (== x) b) (=> x b)]
|
|
||||||
[(expression (== @<=>) b (== x)) (=> x b)]
|
|
||||||
[_ (|| (! x) y)])]))
|
|
||||||
|
|
||||||
(define (<=> x y)
|
(define (<=> x y) ;(|| (&& x y) (&& (! x) (! y))))))
|
||||||
(cond [(equal? x y) #t]
|
(cond [(equal? x y) #t]
|
||||||
[(boolean? x) (if x y (! y))]
|
[(boolean? x) (if x y (! y))]
|
||||||
[(boolean? y) (if y x (! x))]
|
[(boolean? y) (if y x (! x))]
|
||||||
|
|
@ -132,15 +91,7 @@
|
||||||
[_ (loop (cdr xs))]))]
|
[_ (loop (cdr xs))]))]
|
||||||
[_ #f]))
|
[_ #f]))
|
||||||
|
|
||||||
(define (@true? v)
|
;; ----------------- Additional operators ----------------- ;;
|
||||||
(or (eq? #t v) (! (@false? v))))
|
|
||||||
|
|
||||||
(define-quantifier exists @exists)
|
|
||||||
(define-quantifier forall @forall)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
;; ----------------- Additional operators and utilities ----------------- ;;
|
|
||||||
(define-syntax and-&&
|
(define-syntax and-&&
|
||||||
(syntax-rules ()
|
(syntax-rules ()
|
||||||
[(_) #t]
|
[(_) #t]
|
||||||
|
|
@ -164,25 +115,6 @@
|
||||||
(and (union? v) (apply || (for/list ([g (in-union-guards v symbolic-type)]) g))))]
|
(and (union? v) (apply || (for/list ([g (in-union-guards v symbolic-type)]) g))))]
|
||||||
[_ #f]))
|
[_ #f]))
|
||||||
|
|
||||||
(define ⊥ (void))
|
|
||||||
|
|
||||||
(define-syntax first-term-or-bool
|
|
||||||
(syntax-rules ()
|
|
||||||
[(_ e) e]
|
|
||||||
[(_ e0 e ...) (let ([v e0])
|
|
||||||
(if (void? v)
|
|
||||||
(first-term-or-bool e ...)
|
|
||||||
v))]))
|
|
||||||
|
|
||||||
;; ----------------- Partial evaluation rules for ∀ and ∃ ----------------- ;;
|
|
||||||
|
|
||||||
(define-syntax-rule (quantifier @op)
|
|
||||||
(lambda (vars body)
|
|
||||||
(match* (vars body)
|
|
||||||
[((list) _) body]
|
|
||||||
[(_ (? boolean?)) body]
|
|
||||||
[(_ _) (expression @op vars body)])))
|
|
||||||
|
|
||||||
;; ----------------- Partial evaluation rules for && and || ----------------- ;;
|
;; ----------------- Partial evaluation rules for && and || ----------------- ;;
|
||||||
(define-syntax-rule (logical-connective op co iden !iden)
|
(define-syntax-rule (logical-connective op co iden !iden)
|
||||||
(case-lambda
|
(case-lambda
|
||||||
|
|
@ -195,7 +127,7 @@
|
||||||
[((== !iden) _) !iden]
|
[((== !iden) _) !iden]
|
||||||
[(_ (== !iden)) !iden]
|
[(_ (== !iden)) !iden]
|
||||||
[(_ _)
|
[(_ _)
|
||||||
(first-term-or-bool
|
(first-value
|
||||||
(simplify-connective op co !iden x y)
|
(simplify-connective op co !iden x y)
|
||||||
(if (term<? x y) (expression op x y) (expression op y x)))])]
|
(if (term<? x y) (expression op x y) (expression op y x)))])]
|
||||||
[xs
|
[xs
|
||||||
|
|
@ -206,19 +138,27 @@
|
||||||
[(list x) x]
|
[(list x) x]
|
||||||
[ys (apply expression op (sort ys term<?))])])]))
|
[ys (apply expression op (sort ys term<?))])])]))
|
||||||
|
|
||||||
|
(define ⊥ (void))
|
||||||
|
(define-syntax first-value
|
||||||
|
(syntax-rules ()
|
||||||
|
[(_ e) e]
|
||||||
|
[(_ e0 e ...) (let ([v e0])
|
||||||
|
(if (void? v)
|
||||||
|
(first-value e ...)
|
||||||
|
v))]))
|
||||||
|
|
||||||
(define (simplify-connective op co !iden x y)
|
(define (simplify-connective op co !iden x y)
|
||||||
(match* (x y)
|
(match* (x y)
|
||||||
[(_ (== x)) x]
|
[(_ (== x)) x]
|
||||||
[((? expression?) (? expression?))
|
[((? expression?) (? expression?))
|
||||||
(first-term-or-bool
|
(first-value
|
||||||
(if (term<? y x)
|
(simplify-connective:expr/any op co !iden x y)
|
||||||
(simplify-connective:expr/any op co !iden x y)
|
(simplify-connective:expr/any op co !iden y x)
|
||||||
(simplify-connective:expr/any op co !iden y x))
|
|
||||||
(simplify-connective:expr/expr op co !iden x y))]
|
(simplify-connective:expr/expr op co !iden x y))]
|
||||||
[((? expression?) _)
|
[((? expression?) _)
|
||||||
(if (term<? y x) (simplify-connective:expr/any op co !iden x y) ⊥)]
|
(simplify-connective:expr/any op co !iden x y)]
|
||||||
[(_ (? expression?))
|
[(_ (? expression?))
|
||||||
(if (term<? x y) (simplify-connective:expr/any op co !iden y x) ⊥)]
|
(simplify-connective:expr/any op co !iden y x)]
|
||||||
[(_ _) ⊥]))
|
[(_ _) ⊥]))
|
||||||
|
|
||||||
(define (simplify-connective:expr/any op co !iden x y)
|
(define (simplify-connective:expr/any op co !iden x y)
|
||||||
|
|
@ -228,13 +168,21 @@
|
||||||
[(expression (== op) _ ... (== y) _ ...) x]
|
[(expression (== op) _ ... (== y) _ ...) x]
|
||||||
[(expression (== op) _ ... (expression (== @!) (== y)) _ ...) !iden]
|
[(expression (== op) _ ... (expression (== @!) (== y)) _ ...) !iden]
|
||||||
[(expression (== @!) (expression (== co) _ ... (== y) _ ...)) !iden]
|
[(expression (== @!) (expression (== co) _ ... (== y) _ ...)) !iden]
|
||||||
[_ ⊥]))
|
[(expression (== @!) (expression (== co) _ ... (expression (== @!) (== y)) _ ...)) x]
|
||||||
|
[(expression (== @!) (expression (== op) _ ... (expression (== @!) (== y)) _ ...)) y]
|
||||||
|
[(expression (== @!) a)
|
||||||
|
(match y
|
||||||
|
[(expression (== op) _ ... (== a) _ ...) !iden]
|
||||||
|
[_ ⊥])]
|
||||||
|
[_ ⊥]))
|
||||||
|
|
||||||
|
; Applies the following simplification rules symmetrically:
|
||||||
|
; (1) (op (op a1 ... an) (op ai ... aj)) ==> (op a1 ... an)
|
||||||
|
; (2) (op (op a1 ... ai ... an) (op b1 ... (neg ai) ... bn) ==> !iden
|
||||||
|
; (3) (op (co a1 ... an) (co ai ... aj)) ==> (co ai ... aj)
|
||||||
|
; Returns ⊥ if none of the rules applicable; otherwise returns the simplified result.
|
||||||
(define (simplify-connective:expr/expr op co !iden a b)
|
(define (simplify-connective:expr/expr op co !iden a b)
|
||||||
(match* (a b)
|
(match* (a b)
|
||||||
[((expression (== op) _ ... x _ ...) (expression (== @!) x)) !iden]
|
|
||||||
[((expression (== @!) x) (expression (== op) _ ... x _ ...)) !iden]
|
|
||||||
[((expression (== op) xs ...) (expression (== op) ys ...))
|
[((expression (== op) xs ...) (expression (== op) ys ...))
|
||||||
(cond [(sublist? xs ys) b]
|
(cond [(sublist? xs ys) b]
|
||||||
[(sublist? ys xs) a]
|
[(sublist? ys xs) a]
|
||||||
|
|
@ -244,22 +192,16 @@
|
||||||
(cond [(sublist? xs ys) a]
|
(cond [(sublist? xs ys) a]
|
||||||
[(sublist? ys xs) b]
|
[(sublist? ys xs) b]
|
||||||
[else ⊥])]
|
[else ⊥])]
|
||||||
[((expression (== op) xs ...) (expression (== co) ys ...))
|
|
||||||
(cond [(for*/or ([x xs][y ys]) (equal? x y)) a]
|
|
||||||
[else ⊥])]
|
|
||||||
[((expression (== co) xs ...) (expression (== op) ys ...))
|
|
||||||
(cond [(for*/or ([y ys][x xs]) (equal? x y)) b]
|
|
||||||
[else ⊥])]
|
|
||||||
[(_ _) ⊥]))
|
[(_ _) ⊥]))
|
||||||
|
|
||||||
(define (simplify-fp op co !iden xs)
|
(define (simplify-fp op co !iden xs)
|
||||||
(or
|
(or
|
||||||
(and (> (length xs) 10) xs)
|
|
||||||
(let-values ([(!ys ys) (for/fold ([!ys '()][ys '()]) ([x xs])
|
(let-values ([(!ys ys) (for/fold ([!ys '()][ys '()]) ([x xs])
|
||||||
(match x
|
(match x
|
||||||
[(expression (== @!) y) (values (cons y !ys) ys)]
|
[(expression (== @!) y) (values (cons y !ys) ys)]
|
||||||
[_ (values !ys (cons x ys))]))])
|
[_ (values !ys (cons x ys))]))])
|
||||||
(for/first ([!y !ys] #:when (member !y ys)) (list !iden)))
|
(for/first ([!y !ys] #:when (member !y ys)) (list !iden)))
|
||||||
|
(and (> (length xs) 100) xs)
|
||||||
(let outer ([xs xs])
|
(let outer ([xs xs])
|
||||||
(match xs
|
(match xs
|
||||||
[(list x rest ..1)
|
[(list x rest ..1)
|
||||||
|
|
@ -282,196 +224,53 @@
|
||||||
[(_ _) #f]))
|
[(_ _) #f]))
|
||||||
|
|
||||||
|
|
||||||
;; ----------------- VC generation ----------------- ;;
|
;; ----------------- Assertions and path condition ----------------- ;;
|
||||||
|
(define (export-asserts) (remove-duplicates (asserts)))
|
||||||
|
|
||||||
; A verification condition (VC) consists of two @boolean?
|
(define (clear-asserts!) (asserts '()))
|
||||||
; values representing assumptions and assertions issued
|
|
||||||
; during execution. A VC is legal if at least one of its
|
|
||||||
; constituent fields is true under all models.
|
|
||||||
|
|
||||||
(struct vc (assumes asserts) #:transparent)
|
(define asserts
|
||||||
|
|
||||||
; The true verification condition.
|
|
||||||
(define vc-true (vc #t #t))
|
|
||||||
|
|
||||||
(define (vc-true? s) (equal? s vc-true))
|
|
||||||
|
|
||||||
; Returns (vc (s.assumes && (s.asserts => g)) s.asserts).
|
|
||||||
(define (assuming s g) ; g must be a symbolic or concrete boolean
|
|
||||||
(vc (&& (vc-assumes s) (=> (vc-asserts s) g)) (vc-asserts s)))
|
|
||||||
|
|
||||||
; Returns (vc s.assumes (s.asserts && (s.assumes => g))).
|
|
||||||
(define (asserting s g) ; g must be a symbolic or concrete boolean
|
|
||||||
(vc (vc-assumes s) (&& (vc-asserts s) (=> (vc-assumes s) g))))
|
|
||||||
|
|
||||||
; The current-vc parameter keeps track of the current verification condition,
|
|
||||||
; which is an instance of vc?. The default value for this parameter is vc-true.
|
|
||||||
(define current-vc
|
|
||||||
(make-parameter
|
(make-parameter
|
||||||
vc-true
|
'()
|
||||||
(lambda (v) (unless (vc? v) (raise-argument-error 'vc "vc?" v)) v)))
|
(match-lambda [(? list? xs) xs]
|
||||||
|
[x (if (eq? x #t) (asserts) (cons x (asserts)))])))
|
||||||
|
|
||||||
; Returns the current vc, without exposing the parameter outside the module.
|
(define pc
|
||||||
(define (get-vc) (current-vc))
|
(make-parameter
|
||||||
|
#t
|
||||||
|
(lambda (new-pc)
|
||||||
|
(or (boolean? new-pc)
|
||||||
|
(and (term? new-pc) (equal? @boolean? (term-type new-pc)))
|
||||||
|
(error 'pc "expected a boolean path condition, given a ~s" (type-of new-pc)))
|
||||||
|
(or (&& (pc) new-pc)
|
||||||
|
(error 'pc "infeasible path condition")))))
|
||||||
|
|
||||||
; Clears the current vc by setting it to the true spec.
|
|
||||||
(define (clear-vc!) (current-vc vc-true))
|
|
||||||
|
|
||||||
; Returns #t if x && (g => y) is equivalent to x according to the embedded
|
|
||||||
; rewrite rules. Otherwise returns #f.
|
|
||||||
(define (merge-absorbs? x g y)
|
|
||||||
(match y
|
|
||||||
[(== x) #t] ; x && (g => x)
|
|
||||||
[(expression (== @&&) (== x) (== g)) #t] ; x && (g => (x && g))
|
|
||||||
[(expression (== @&&) (== g) (== x)) #t] ; x && (g => (x && g))
|
|
||||||
[(expression (== @&&) (== x) (expression (== @||) _ ... (== g) _ ...)) #t] ; x && (g => (x && (_ => g)))
|
|
||||||
[(expression (== @&&) (expression (== @||) _ ... (== g) _ ...) (== x)) #t] ; x && (g => ((_ => g) && x))
|
|
||||||
[_ #f]))
|
|
||||||
|
|
||||||
; Returns (field x) && (gs[0] => (field ys[0])) ... && (gs[n-1] => (field gs[n-1])).
|
|
||||||
(define (merge-field field x gs ys)
|
|
||||||
(define xf (field x))
|
|
||||||
(apply && xf
|
|
||||||
(for*/list ([(g y) (in-parallel gs ys)]
|
|
||||||
[yf (in-value (field y))]
|
|
||||||
#:unless (merge-absorbs? xf g yf))
|
|
||||||
(=> g yf))))
|
|
||||||
|
|
||||||
;; Returns (field x) && (gs[0] => (field ys[0])) ... && (gs[n-1] => (field gs[n-1])).
|
|
||||||
;; Assumes that ys[i] => x for all i, and at most one gs evaluates to true in any model.
|
|
||||||
;(define (merge-field field x gs ys)
|
|
||||||
; (define xf (field x))
|
|
||||||
; (define gs=>ys
|
|
||||||
; (for*/list ([(g y) (in-parallel gs ys)]
|
|
||||||
; [yf (in-value (field y))]
|
|
||||||
; #:unless (merge-absorbs? xf g yf))
|
|
||||||
; (=> g yf)))
|
|
||||||
; (match gs=>ys
|
|
||||||
; [(list) xf]
|
|
||||||
; [(list gy) (&& xf gy)]
|
|
||||||
; [(or (list (expression (== @||) _ ... g _ ...) (expression (== @||) _ ... (expression (== @!) g) _ ...))
|
|
||||||
; (list (expression (== @||) _ ... (expression (== @!) g) _ ...) (expression (== @||) _ ... g _ ...)))
|
|
||||||
; (apply && gs=>ys)]
|
|
||||||
; [_ (apply && xf gs=>ys)]))
|
|
||||||
|
|
||||||
; Takes as input a list of n guards and n vcs and sets the current vc
|
|
||||||
; to (current-vc) && (vc-guard guard1 vc1) && ... && (vc-guard guardn vcn).
|
|
||||||
; Then, it checks if either the assumes or the asserts of the resulting vc
|
|
||||||
; are false? and if so, throws either an exn:fail:svm:assume? or
|
|
||||||
; exn:fail:svm:assert? exception. This procedure makes the following assumptions:
|
|
||||||
; * at most one of the given guards is true in any model,
|
|
||||||
; * (vc-assumes vcs[i]) => (vc-assumes (current-vc)) for all i, and
|
|
||||||
; * (vc-asserts vcs[i]) => (vc-asserts (current-vc)) for all i.
|
|
||||||
(define (merge-vc! guards vcs)
|
|
||||||
(unless (null? vcs)
|
|
||||||
(define vc*
|
|
||||||
(vc (merge-field vc-assumes (current-vc) guards vcs)
|
|
||||||
(merge-field vc-asserts (current-vc) guards vcs)))
|
|
||||||
(current-vc vc*)
|
|
||||||
(when (false? (vc-assumes vc*))
|
|
||||||
(raise-exn:fail:svm:assume:core "contradiction"))
|
|
||||||
(when (false? (vc-asserts vc*))
|
|
||||||
(raise-exn:fail:svm:assert:core "contradiction"))))
|
|
||||||
|
|
||||||
; Sets the current vc to (vc-proc (current-vc) g) where g is (@true? val).
|
|
||||||
; If g is #f or the resulting vc's vc-field value is #f,
|
|
||||||
; uses raise-exn throws an exn:fail:svm exception.
|
|
||||||
(define-syntax-rule (vc-set! val msg vc-proc vc-field raise-exn)
|
|
||||||
(let* ([guard (@true? val)]
|
|
||||||
[vc* (vc-proc (current-vc) guard)])
|
|
||||||
(current-vc vc*)
|
|
||||||
(when (false? guard)
|
|
||||||
(raise-exn msg))
|
|
||||||
(when (false? (vc-field vc*))
|
|
||||||
(raise-exn "contradiction"))))
|
|
||||||
|
|
||||||
; Sets the current vc to (asserting (current-vc) g) where g is (@true? val).
|
|
||||||
; If g is #f or the resulting vc's asserts field is #f, throws an
|
|
||||||
; exn:fail:svm:assert exception of the given kind.
|
|
||||||
(define-syntax-rule (vc-assert! val msg raise-kind)
|
|
||||||
(vc-set! val msg asserting vc-asserts raise-kind))
|
|
||||||
|
|
||||||
; Sets the current vc to (assuming (current-vc) g) where g is (@true? val).
|
|
||||||
; If g is #f or the resulting vc's assumes field is #f, throws an
|
|
||||||
; exn:fail:svm:assume exception of the given kind.
|
|
||||||
(define-syntax-rule (vc-assume! val msg raise-kind)
|
|
||||||
(vc-set! val msg assuming vc-assumes raise-kind))
|
|
||||||
|
|
||||||
; The $assert form has three variants: ($assert val), ($assert val msg),
|
|
||||||
; and ($assert val msg kind), where val is the value being asserted, msg
|
|
||||||
; is the failure message, and kind is a procedure that returns a subtype of
|
|
||||||
; exn:fail:svm:assert. Default values for msg and kind are #f and
|
|
||||||
; raise-exn:fail:svm:assert:core, respectively.
|
|
||||||
; The first two variants of this form are used for issuing assertions from
|
|
||||||
; within the Rosette core. The third variant is used to implement the @assert
|
|
||||||
; form that is exposed to user code. An $assert call modifies the current vc to
|
|
||||||
; reflect the issued assertion. If the issued assertion or the vc-assert of the
|
|
||||||
; current vc reduce to #f, the call throws an exception of the given kind after
|
|
||||||
; updating the vc.
|
|
||||||
(define-syntax ($assert stx)
|
|
||||||
(syntax-case stx ()
|
|
||||||
[(_ val) (syntax/loc stx ($assert val #f raise-exn:fail:svm:assert:core))]
|
|
||||||
[(_ val msg) (syntax/loc stx ($assert val msg raise-exn:fail:svm:assert:core))]
|
|
||||||
[(_ val msg kind) (syntax/loc stx (vc-assert! val msg kind))]))
|
|
||||||
|
|
||||||
; Analogous to the $assert form, except that it modifies the current vc to
|
|
||||||
; reflect the issued assumption.
|
|
||||||
(define-syntax ($assume stx)
|
|
||||||
(syntax-case stx ()
|
|
||||||
[(_ val) (syntax/loc stx ($assume val #f raise-exn:fail:svm:assume:core))]
|
|
||||||
[(_ val msg) (syntax/loc stx ($assume val msg raise-exn:fail:svm:assume:core))]
|
|
||||||
[(_ val msg kind) (syntax/loc stx (vc-assume! val msg kind))]))
|
|
||||||
|
|
||||||
; The @assert form modifies the current vc to reflect the issued assertion.
|
|
||||||
; The form has two variants (@assert val) and (@assert val msg), where val
|
|
||||||
; is the value being asserted and msg is the optional error message in case
|
|
||||||
; val is #f. This form is exposed to user code.
|
|
||||||
(define-syntax (@assert stx)
|
(define-syntax (@assert stx)
|
||||||
(syntax-case stx ()
|
(syntax-case stx ()
|
||||||
[(_ val) (syntax/loc stx ($assert val #f raise-exn:fail:svm:assert:user))]
|
[(_ val) (syntax/loc stx (@assert val #f))]
|
||||||
[(_ val msg) (syntax/loc stx ($assert val msg raise-exn:fail:svm:assert:user))]))
|
[(_ val msg)
|
||||||
|
|
||||||
; The @assume form modifies the current vc to reflect the issued assumption.
|
|
||||||
; The form has two variants (@assume val) and (@assume val msg), where val
|
|
||||||
; is the value being assume and msg is the optional error message in case
|
|
||||||
; val is #f. This form is exposed to user code.
|
|
||||||
(define-syntax (@assume stx)
|
|
||||||
(syntax-case stx ()
|
|
||||||
[(_ val) (syntax/loc stx ($assume val #f raise-exn:fail:svm:assume:user))]
|
|
||||||
[(_ val msg) (syntax/loc stx ($assume val msg raise-exn:fail:svm:assume:user))]))
|
|
||||||
|
|
||||||
(define (halt-svm ex)
|
|
||||||
(define result (failed ex (current-vc)))
|
|
||||||
((current-reporter) 'exception result)
|
|
||||||
result)
|
|
||||||
|
|
||||||
(define (halt-err ex) ; Treat an exn:fail? error as an assertion failure.
|
|
||||||
(define result
|
|
||||||
(failed (make-exn:fail:svm:assert:err (exn-message ex) (exn-continuation-marks ex))
|
|
||||||
(asserting (current-vc) #f)))
|
|
||||||
((current-reporter) 'exception result)
|
|
||||||
result)
|
|
||||||
|
|
||||||
; The with-vc form has two variants, (with-vc body) and (with-vc vc0 body).
|
|
||||||
; The former expands into (with-vc (current-vc) body). The latter sets the current
|
|
||||||
; vc to vc0, evaluates the given body, returns the result, and reverts current-vc
|
|
||||||
; to the value it held before the call to with-vc.
|
|
||||||
;
|
|
||||||
; If the evaluation of the body terminates normally, (with-vc vc0 body)
|
|
||||||
; outputs (normal v vc*) where v is the value computed by the body, and vc* is
|
|
||||||
; the vc (i.e., assumes and asserts) generated during the evaluation,
|
|
||||||
; with vc0 as the initial vc.
|
|
||||||
;
|
|
||||||
; If the evaluation of the body terminates abnormally with an exn:fail? exception,
|
|
||||||
; (with-vc vc0 body) outputs (failed v vc*) where v is an exn:fail:svm? exception
|
|
||||||
; that represents the cause of the abnormal termination, and vc* is the vc
|
|
||||||
; generated during the evaluation, with vc0 as the initial vc.
|
|
||||||
(define-syntax (with-vc stx)
|
|
||||||
(syntax-case stx ()
|
|
||||||
[(_ body) (syntax/loc stx (with-vc (current-vc) body))]
|
|
||||||
[(_ vc0 body)
|
|
||||||
(syntax/loc stx
|
(syntax/loc stx
|
||||||
(parameterize ([current-vc vc0])
|
(let ([guard (not-false? val)])
|
||||||
(with-handlers ([exn:fail:svm? halt-svm]
|
(asserts (=> (pc) guard))
|
||||||
[exn:fail? halt-err])
|
(when (false? guard)
|
||||||
(normal (let () body) (current-vc)))))]))
|
(raise-assertion-error msg))))]))
|
||||||
|
|
||||||
|
(define (not-false? v)
|
||||||
|
(or (eq? v #t) (! (@false? v))))
|
||||||
|
|
||||||
|
(define (raise-assertion-error msg)
|
||||||
|
(if (procedure? msg)
|
||||||
|
(msg)
|
||||||
|
(error 'assert (if msg (format "~a" msg) "failed"))))
|
||||||
|
|
||||||
|
(define-syntax (with-asserts stx)
|
||||||
|
(syntax-case stx (begin)
|
||||||
|
[(_ (begin form ...)) #'(with-asserts (let () form ...))]
|
||||||
|
[(_ form) #`(parameterize ([asserts (asserts)])
|
||||||
|
(let* ([val form]
|
||||||
|
[bools (remove-duplicates (asserts))])
|
||||||
|
(values val bools)))]))
|
||||||
|
|
||||||
|
(define-syntax-rule (with-asserts-only form)
|
||||||
|
(let-values ([(out asserts) (with-asserts form)])
|
||||||
|
asserts))
|
||||||
|
|
|
||||||
|
|
@ -1,93 +0,0 @@
|
||||||
#lang racket
|
|
||||||
|
|
||||||
(require
|
|
||||||
(only-in racket/splicing splicing-let)
|
|
||||||
"bitvector.rkt" "merge.rkt" "safe.rkt" "term.rkt" "bool.rkt" "forall.rkt" "lift.rkt"
|
|
||||||
(only-in "real.rkt" @integer? @> @>= @=)
|
|
||||||
(only-in "numerics.rkt" extreme))
|
|
||||||
|
|
||||||
(provide bit lsb msb bvzero? bvadd1 bvsub1
|
|
||||||
bvsmin bvsmax bvumin bvumax
|
|
||||||
rotate-left rotate-right bvrol bvror
|
|
||||||
bool->bitvector bitvector->bool bitvector->bits)
|
|
||||||
|
|
||||||
(define-syntax (define-lifted stx)
|
|
||||||
(syntax-case stx ()
|
|
||||||
[(_ (id arg ...) expr ...)
|
|
||||||
#'(define-lifted id (lambda (arg ...) expr ...))]
|
|
||||||
[(_ id impl)
|
|
||||||
#'(define id (procedure-rename (bvlift-op impl) 'id))]))
|
|
||||||
|
|
||||||
(define (bit i x)
|
|
||||||
(@extract i i x))
|
|
||||||
|
|
||||||
(define (lsb x) (bit 0 x))
|
|
||||||
|
|
||||||
(define-lifted (msb x)
|
|
||||||
(let ([pos (sub1 (bitvector-size (get-type x)))])
|
|
||||||
(bit pos x)))
|
|
||||||
|
|
||||||
(define-lifted bvsmin (curry extreme @bvsle))
|
|
||||||
(define-lifted bvsmax (curry extreme @bvsge))
|
|
||||||
(define-lifted bvumin (curry extreme @bvule))
|
|
||||||
(define-lifted bvumax (curry extreme @bvuge))
|
|
||||||
|
|
||||||
(define (bool->bitvector x [t 1])
|
|
||||||
(merge (@false? x) (bv 0 t) (bv 1 t)))
|
|
||||||
|
|
||||||
(define (bitvector->bool x)
|
|
||||||
(! (bvzero? x)))
|
|
||||||
|
|
||||||
(define-lifted (bvzero? x)
|
|
||||||
(@bveq x (bv 0 (get-type x))))
|
|
||||||
|
|
||||||
(define-lifted (bvadd1 x)
|
|
||||||
(@bvadd x (bv 1 (get-type x))))
|
|
||||||
|
|
||||||
(define-lifted (bvsub1 x)
|
|
||||||
(@bvsub x (bv 1 (get-type x))))
|
|
||||||
|
|
||||||
(define-lifted (bitvector->bits v)
|
|
||||||
(for/list ([i (bitvector-size (get-type v))])
|
|
||||||
(bit i v)))
|
|
||||||
|
|
||||||
(define-syntax-rule (define-rotate id proc)
|
|
||||||
(splicing-let ([dir proc])
|
|
||||||
(define (id @i @x)
|
|
||||||
(define i (type-cast @integer? @i 'id))
|
|
||||||
(define x (bvcoerce @x id))
|
|
||||||
(match i
|
|
||||||
[0 x]
|
|
||||||
[_
|
|
||||||
(assert (@>= i 0) (arguments-error 'id "expected i >= 0" "i" i))
|
|
||||||
(for/all ([x x])
|
|
||||||
(let ([sz (bitvector-size (get-type x))])
|
|
||||||
(assert (@> sz i) (arguments-error 'id "expected (size-of x) > i" "x" x "i" i))
|
|
||||||
(if (integer? i)
|
|
||||||
(dir i sz x)
|
|
||||||
(merge+ (cons (cons (@= i 0) x)
|
|
||||||
(for/list ([n (in-range 1 sz)])
|
|
||||||
(cons (@= n i)
|
|
||||||
(dir n sz x))))
|
|
||||||
#:unless sz
|
|
||||||
#:error (arguments-error 'id "expected (size-of x) > i >= 0" "x" x "i" i)))))]))))
|
|
||||||
|
|
||||||
(define-rotate rotate-left
|
|
||||||
(lambda (i sz x)
|
|
||||||
(@concat (@extract (- sz i 1) 0 x) (@extract (- sz 1) (- sz i) x))))
|
|
||||||
|
|
||||||
(define-rotate rotate-right
|
|
||||||
(lambda (i sz x)
|
|
||||||
(@concat (@extract (- i 1) 0 x) (@extract (- sz 1) i x))))
|
|
||||||
|
|
||||||
; x and y must be bitvectors (not unions) of the same length.
|
|
||||||
; shift1 and shift2 are shift operators.
|
|
||||||
(define-syntax-rule (bvrotate x y shift1 shift2)
|
|
||||||
(let* ([sz (bitvector-size (get-type y))]
|
|
||||||
[n (bv sz sz)]
|
|
||||||
[amount (@bvurem y n)])
|
|
||||||
(@bvor (shift1 x amount) (shift2 x (@bvsub n amount)))))
|
|
||||||
|
|
||||||
(define-lifted (bvrol x y) (bvrotate x y @bvshl @bvlshr))
|
|
||||||
(define-lifted (bvror x y) (bvrotate x y @bvlshr @bvshl))
|
|
||||||
|
|
||||||
|
|
@ -1,53 +0,0 @@
|
||||||
#lang racket
|
|
||||||
|
|
||||||
(require "term.rkt" "bool.rkt" "real.rkt" "bitvector.rkt" "equality.rkt")
|
|
||||||
|
|
||||||
(provide @distinct?)
|
|
||||||
|
|
||||||
|
|
||||||
; Returns true iff all of the given argument values are non-equal to each other
|
|
||||||
; (i.e., pairwise distinct).
|
|
||||||
(define distinct?
|
|
||||||
(case-lambda
|
|
||||||
[() #t]
|
|
||||||
[(x) #t]
|
|
||||||
[(x y) (! (@equal? x y))]
|
|
||||||
[xs
|
|
||||||
(define t (apply type-of xs))
|
|
||||||
(match t
|
|
||||||
[(== @boolean?)
|
|
||||||
(distinct-primitive-solvable? t 2 => xs)]
|
|
||||||
[(or (== @integer?) (== @real?))
|
|
||||||
(distinct-primitive-solvable? t +inf.0 < xs)]
|
|
||||||
[(bitvector sz)
|
|
||||||
(distinct-primitive-solvable? t (expt 2 sz) (operator-unsafe @bvslt) xs)]
|
|
||||||
[_
|
|
||||||
(define x (car xs))
|
|
||||||
(and-&&
|
|
||||||
(apply &&
|
|
||||||
(let loop ([ys (cdr xs)])
|
|
||||||
(cond [(null? ys) null]
|
|
||||||
[else (match (! (@equal? x (car ys)))
|
|
||||||
[#t (loop (cdr ys))]
|
|
||||||
[#f (list #f)]
|
|
||||||
[t (cons t (loop (cdr ys)))])])))
|
|
||||||
(apply distinct? (cdr xs)))])]))
|
|
||||||
|
|
||||||
(define-operator @distinct?
|
|
||||||
#:identifier 'distinct?
|
|
||||||
#:range T*->boolean?
|
|
||||||
#:unsafe distinct?
|
|
||||||
#:safe distinct?)
|
|
||||||
|
|
||||||
|
|
||||||
; Returns true iff all of the given argument values are non-equal to each other
|
|
||||||
; (i.e., pairwise distinct). This procedure assumes that each x in xs is a value
|
|
||||||
; of type t; that t is primitive-solvable?; that c is the cardinality of type t;
|
|
||||||
; and that t<? is a strict total order over literals of type t.
|
|
||||||
(define (distinct-primitive-solvable? t c t<? xs)
|
|
||||||
(and (<= (length xs) c)
|
|
||||||
(let ([xs (for/list ([x xs]) (type-cast t x 'distinct?))])
|
|
||||||
(and (= (length xs) (set-count (list->set xs)))
|
|
||||||
(let-values ([(terms lits) (partition term? xs)])
|
|
||||||
(or (null? terms)
|
|
||||||
(apply expression @distinct? (append (sort lits t<?) (sort terms term<?)))))))))
|
|
||||||
|
|
@ -0,0 +1,176 @@
|
||||||
|
#lang racket
|
||||||
|
|
||||||
|
(require
|
||||||
|
(for-syntax racket))
|
||||||
|
|
||||||
|
(provide speculate speculate* apply! location=? (rename-out [state-val location-final-value]))
|
||||||
|
|
||||||
|
; The env parameter stores an eq? based hash-map which we use to keep
|
||||||
|
; track of boxes, vectors and structs that are mutated.
|
||||||
|
(define env (make-parameter #f))
|
||||||
|
|
||||||
|
; The speculate expression takes the form (speculate body), where body is
|
||||||
|
; an expression. A speculate call produces two values: the value that the
|
||||||
|
; body would produce if executed in the current environment, and a closure
|
||||||
|
; that stores a representation of all state updates that the execution of
|
||||||
|
; body would make. The closure accepts a two argument function f, and
|
||||||
|
; applies encapsulated state updates so that each updated location is set
|
||||||
|
; to (f v body-v), where body-v is the final value the body would assign to v.
|
||||||
|
;
|
||||||
|
; Any exceptions thrown by body are caught, all updates are rolled-back without
|
||||||
|
; encapsulating the final states, and the result of speculate is (values #f #f).
|
||||||
|
(define-syntax-rule (speculate body)
|
||||||
|
; using an eq? rather than equal? hash map to manage the environment bindings
|
||||||
|
; is critical for mutable objects whose hash code may change upon mutation. note
|
||||||
|
; that variables are keyed by the symbol representing their name, so eq? comparisons
|
||||||
|
; for them are equivalent to equal? comparisons.
|
||||||
|
(parameterize ([env (make-custom-hash eq? eq-hash-code)])
|
||||||
|
; roll-back state updates, encapsulate
|
||||||
|
; updates to set! variables as specified above,
|
||||||
|
; and return the value of the body together with the
|
||||||
|
; encapsulation of the state changes
|
||||||
|
(with-handlers ([exn:fail? rollback/suppress])
|
||||||
|
(values body (rollback/encapsulate)))))
|
||||||
|
|
||||||
|
; The speculate* expression takes the form (speculate* body), where body is
|
||||||
|
; an expression. A speculate* call produces two values: the value that the
|
||||||
|
; body would produce if executed in the current environment, and a list of
|
||||||
|
; locations, each of which encapsulates the pre and post state of a location
|
||||||
|
; mutated during the execution of the body. The returned locations can be
|
||||||
|
; compared with location=?.
|
||||||
|
;
|
||||||
|
; Each encapsulated update acts as a procedure that accepts a two-argument
|
||||||
|
; function f. The location for the encapsulated updated is then set to
|
||||||
|
; (f v body-v), where body-v is the final value the body would assign to the
|
||||||
|
; location and v is the current value in that location. The procedure
|
||||||
|
; (location-final-value loc) can be used to obtain the final value that the
|
||||||
|
; body would assign to a given location.
|
||||||
|
;
|
||||||
|
; Any exceptions thrown by body are caught, all updates are rolled-back without
|
||||||
|
; encapsulating the final states, and the result of speculate is (values #f #f).
|
||||||
|
(define-syntax-rule (speculate* body)
|
||||||
|
; using an eq? rather than equal? hash map to manage the environment bindings
|
||||||
|
; is critical for mutable objects whose hash code may change upon mutation. note
|
||||||
|
; that variables are keyed by the symbol representing their name, so eq? comparisons
|
||||||
|
; for them are equivalent to equal? comparisons.
|
||||||
|
(parameterize ([env (make-custom-hash eq? eq-hash-code)])
|
||||||
|
; roll-back state updates, encapsulate
|
||||||
|
; updates to set! variables as specified above,
|
||||||
|
; and return the value of the body together with the
|
||||||
|
; encapsulation of the state changes
|
||||||
|
(with-handlers ([exn:fail? rollback/suppress])
|
||||||
|
(values body (rollback/collect)))))
|
||||||
|
|
||||||
|
; A function that handles calls to structure mutators.
|
||||||
|
(define apply!
|
||||||
|
(case-lambda
|
||||||
|
[(setter getter receiver key val)
|
||||||
|
(record! receiver key getter setter)
|
||||||
|
(setter receiver key val)]
|
||||||
|
[(setter getter receiver val)
|
||||||
|
(record! receiver setter getter setter)
|
||||||
|
(setter receiver val)]))
|
||||||
|
|
||||||
|
; Stores the state of a mutation to the location in a given receiver,
|
||||||
|
; together with getters and setters that can be used to read/write
|
||||||
|
; the mutated location. The val field stores the value that was read
|
||||||
|
; from the location at some point in time (e.g., beginning/end of
|
||||||
|
; speculation). The attached procedure accepts a two argument function f
|
||||||
|
; and sets the encapsulated location to (f (getter) val).
|
||||||
|
(struct state (receiver location val getter setter)
|
||||||
|
#:transparent
|
||||||
|
#:property prop:procedure
|
||||||
|
(lambda (self proc)
|
||||||
|
(let ([receiver (state-receiver self)]
|
||||||
|
[location (state-location self)]
|
||||||
|
[getter (state-getter self)]
|
||||||
|
[setter (state-setter self)])
|
||||||
|
(record! receiver location getter setter)
|
||||||
|
(cond [(dict? receiver)
|
||||||
|
(setter receiver location (proc (getter receiver location) (state-val self)))]
|
||||||
|
[else ; struct or box
|
||||||
|
(setter receiver (proc (getter receiver) (state-val self)))]))))
|
||||||
|
|
||||||
|
(define (get getter receiver location)
|
||||||
|
(cond [(dict? receiver) (getter receiver location)]
|
||||||
|
[else (getter receiver)]))
|
||||||
|
|
||||||
|
(define (state-rollback! s)
|
||||||
|
(let ([receiver (state-receiver s)]
|
||||||
|
[location (state-location s)]
|
||||||
|
[getter (state-getter s)]
|
||||||
|
[setter (state-setter s)])
|
||||||
|
(cond [(dict? receiver)
|
||||||
|
(setter receiver location (state-val s))]
|
||||||
|
[else ; struct or box
|
||||||
|
(setter receiver (state-val s))])))
|
||||||
|
|
||||||
|
; Returns true iff both objects encapsulate updates to the same location.
|
||||||
|
(define (location=? s0 s1)
|
||||||
|
(match* (s0 s1)
|
||||||
|
[((state rec0 loc0 _ _ _) (state rec1 loc1 _ _ _))
|
||||||
|
(and (eq? rec0 rec1) (equal? loc0 loc1))]
|
||||||
|
[(_ _) #f]))
|
||||||
|
|
||||||
|
|
||||||
|
; Adds a record of the given variable's or object's current state
|
||||||
|
; to the environment, if the environment is valid and does not
|
||||||
|
; already have a mapping for the record!-ed variable or object.
|
||||||
|
(define-syntax-rule (record! obj location getter setter)
|
||||||
|
(when (and (env)
|
||||||
|
(not (env-has-state? obj location))) ; we do this check separately so that the getter/setter
|
||||||
|
(env-set! obj location getter setter))) ; lambdas don't get created unless they are needed
|
||||||
|
|
||||||
|
; Returns a true value if the current environment (assumed not be #f)
|
||||||
|
; has a state record for the given mutation receiver and location of
|
||||||
|
; mutation. For structs, the location is the field-setter function for
|
||||||
|
; the mutated field. For dictionary objects, the location is the key within the
|
||||||
|
; dictionary to which the dict-set! operation is being applied. For boxes,
|
||||||
|
; the location is the set-box! procedure.
|
||||||
|
(define (env-has-state? receiver location)
|
||||||
|
(let ([env (env)])
|
||||||
|
(and (dict-has-key? env receiver) ; compound object
|
||||||
|
(dict-has-key? (dict-ref env receiver) location))))
|
||||||
|
|
||||||
|
; Augments env with a mapping from the given receiver to a state record reflecting
|
||||||
|
; the current state at the given location, as obtained by the given getter
|
||||||
|
; procedure. This function assumes that (env-has-state? receiver location) is false.
|
||||||
|
(define (env-set! receiver location getter setter)
|
||||||
|
(let ([env (env)]
|
||||||
|
[new-state (state receiver location (get getter receiver location) getter setter)])
|
||||||
|
(let ([locations (dict-ref! env receiver make-hash)]) ; compound object
|
||||||
|
(dict-set! locations location new-state))))
|
||||||
|
|
||||||
|
; Reverts the state of set! variables and struct fields to
|
||||||
|
; their initial values, without encapsulating the final state updates.
|
||||||
|
; Returns (values #f #f). The error argument is ignored.
|
||||||
|
(define (rollback/suppress err)
|
||||||
|
;(printf "\n\nERROR: ~a\n\n" err)
|
||||||
|
(unless (zero? (dict-count (env)))
|
||||||
|
(for* ([states (in-dict-values (env))]
|
||||||
|
[s (if (list? states) (in-list states) (in-dict-values states))])
|
||||||
|
(state-rollback! s))) ; roll-back
|
||||||
|
(values #f #f))
|
||||||
|
|
||||||
|
; Reverts the state of set! variables and struct fields to
|
||||||
|
; their initial values, and returns an encapsulation of
|
||||||
|
; the final state updates.
|
||||||
|
(define (rollback/encapsulate)
|
||||||
|
(if (zero? (dict-count (env)))
|
||||||
|
void
|
||||||
|
(let ([updates (rollback/collect)])
|
||||||
|
(lambda (proc)
|
||||||
|
(for ([s (in-list updates)])
|
||||||
|
(s proc))))))
|
||||||
|
|
||||||
|
; Reverts the state of set! variables and struct fields to
|
||||||
|
; their initial values, and returns a list that contains a
|
||||||
|
; copy of the final state of each location bound in the current
|
||||||
|
; environment.
|
||||||
|
(define (rollback/collect)
|
||||||
|
(for*/list ([states (in-dict-values (env))]
|
||||||
|
[s (if (list? states) (in-list states) (in-dict-values states))])
|
||||||
|
(let ([final (get (state-getter s) (state-receiver s) (state-location s))])
|
||||||
|
(state-rollback! s) ; roll-back
|
||||||
|
(struct-copy state s [val final])))) ; collect final states
|
||||||
|
|
||||||
|
|
@ -5,53 +5,17 @@
|
||||||
(provide @eq? ; (-> any/c any/c @boolean?)
|
(provide @eq? ; (-> any/c any/c @boolean?)
|
||||||
@equal?) ; (-> any/c any/c @boolean?)
|
@equal?) ; (-> any/c any/c @boolean?)
|
||||||
|
|
||||||
; We must use identity-based hashing and comparison of user-provided values,
|
(define-syntax-rule (define-equality-predicate @=? =? type=?)
|
||||||
; because user-defined structs can override equal/hash and cause unexpected
|
|
||||||
; errors when the overriden equal? is repeatedly called by a hash map. We also
|
|
||||||
; have to use (below) identity-based comparisons for shortcircuiting for the
|
|
||||||
; same reason---equal? might be overriden by a user-defined struct.
|
|
||||||
(struct key (x y)
|
|
||||||
#:transparent
|
|
||||||
#:methods gen:equal+hash
|
|
||||||
[(define (equal-proc a b equal?-recur)
|
|
||||||
(and (eq? (key-x a) (key-x b))
|
|
||||||
(eq? (key-y a) (key-y b))))
|
|
||||||
(define (hash-proc a hash-recur)
|
|
||||||
(hash-recur (cons (eq-hash-code (key-x a)) (eq-hash-code (key-y a)))))
|
|
||||||
(define (hash2-proc a hash2-recur)
|
|
||||||
(hash2-recur (cons (eq-hash-code (key-y a)) (eq-hash-code (key-x a)))))])
|
|
||||||
|
|
||||||
(define-syntax-rule (define-equality-predicate @=? type=? @cache @make-hash)
|
|
||||||
(define (@=? x y)
|
(define (@=? x y)
|
||||||
(let* ([cache (@cache)]
|
(cond [(=? x y) #t]
|
||||||
[toplevel? (false? cache)]
|
[(union? x) (if (union? y)
|
||||||
[k (key x y)])
|
(union=union? x y @=?)
|
||||||
(when toplevel?
|
(union=value? x y @=?))]
|
||||||
(set! cache (@make-hash))
|
[(union? y) (union=value? y x @=?)]
|
||||||
(@cache cache))
|
[else (type=? (type-of x y) x y)])))
|
||||||
(if (hash-has-key? cache k)
|
|
||||||
(hash-ref cache k)
|
|
||||||
(begin
|
|
||||||
(hash-set! cache k #t)
|
|
||||||
(let ([result
|
|
||||||
(cond [(eq? x y) #t] ; We must use identity-based comparisons for short-circuiting.
|
|
||||||
[(union? x) (if (union? y)
|
|
||||||
(union=union? x y @=?)
|
|
||||||
(union=value? x y @=?))]
|
|
||||||
[(union? y) (union=value? y x @=?)]
|
|
||||||
[else (type=? (type-of x y) x y)])])
|
|
||||||
(if toplevel?
|
|
||||||
(@cache #f)
|
|
||||||
(hash-set! cache k result))
|
|
||||||
result))))))
|
|
||||||
|
|
||||||
|
(define-equality-predicate @equal? equal? type-equal?)
|
||||||
|
(define-equality-predicate @eq? eq? type-eq?)
|
||||||
(define equal-cache (make-parameter #f))
|
|
||||||
(define eq-cache (make-parameter #f))
|
|
||||||
|
|
||||||
(define-equality-predicate @equal? type-equal? equal-cache make-hash)
|
|
||||||
(define-equality-predicate @eq? type-eq? eq-cache make-hash)
|
|
||||||
|
|
||||||
; (-> union? union? (-> any/c any/c @boolean?) @boolean?)
|
; (-> union? union? (-> any/c any/c @boolean?) @boolean?)
|
||||||
(define (union=union? x y =?)
|
(define (union=union? x y =?)
|
||||||
|
|
@ -59,10 +23,7 @@
|
||||||
[((union vs t) (union ws s))
|
[((union vs t) (union ws s))
|
||||||
(and (or (subtype? t s) (subtype? s t))
|
(and (or (subtype? t s) (subtype? s t))
|
||||||
(apply || (for*/list ([v vs] [w ws])
|
(apply || (for*/list ([v vs] [w ws])
|
||||||
(and-&&
|
(and-&& (=? (cdr v) (cdr w)) (car v) (car w)))))]))
|
||||||
(=? (cdr v) (cdr w))
|
|
||||||
(car v)
|
|
||||||
(car w)))))]))
|
|
||||||
|
|
||||||
; (-> union? (not/c union?) (-> any/c any/c @boolean?) @boolean?)
|
; (-> union? (not/c union?) (-> any/c any/c @boolean?) @boolean?)
|
||||||
(define (union=value? x y =?)
|
(define (union=value? x y =?)
|
||||||
|
|
|
||||||
|
|
@ -1,61 +0,0 @@
|
||||||
#lang racket
|
|
||||||
|
|
||||||
(require
|
|
||||||
(only-in "bool.rkt" with-vc $assume merge-vc!)
|
|
||||||
"exn.rkt" "result.rkt" "store.rkt" "merge.rkt")
|
|
||||||
|
|
||||||
(provide eval-assuming eval-guarded!)
|
|
||||||
|
|
||||||
; Takes as input a concrete or symbolic boolean and a thunk,
|
|
||||||
; evaluates thunk under the assumption that the guard holds,
|
|
||||||
; and returns the result. This result takes one of two forms.
|
|
||||||
;
|
|
||||||
; If the evaluation of the thunk terminates normally, the result
|
|
||||||
; is (normal (normal v st) vc*) where v is the value computed by the
|
|
||||||
; thunk, st captures all stores mutations performed during evaluation,
|
|
||||||
; and vc* captures the verification condition generated during the
|
|
||||||
; evaluation, starting from the current vc.
|
|
||||||
;
|
|
||||||
; If the thunk terminates abnormally, the result is (failed ex vc*),
|
|
||||||
; where ex is an exn:fail:svm? exception that represents the cause
|
|
||||||
; of the abnormal termination, and vc* captures the verification
|
|
||||||
; condition generated during the evaluation, starting from the current vc.
|
|
||||||
;
|
|
||||||
; Neither the current store nor the current vc are modified after
|
|
||||||
; eval-assuming returns.
|
|
||||||
(define (eval-assuming guard thunk)
|
|
||||||
(with-vc
|
|
||||||
(begin
|
|
||||||
($assume guard)
|
|
||||||
(with-store (thunk)))))
|
|
||||||
|
|
||||||
; Takes as input a list of n guards and n thunks, evaluates each thunk
|
|
||||||
; under its guard using eval-assuming, merges the resulting vcs into
|
|
||||||
; the current vc, merges the resulting stores (if any) into the current
|
|
||||||
; store, and merges the resulting values (if any) before returning them
|
|
||||||
; as output. If all of the thunks fail under their guards, eval-guarded
|
|
||||||
; raises an exn:fail:svm:merge exception after the specs are merged into
|
|
||||||
; the current vc.
|
|
||||||
; This procedure makes the following assumptions, based on the Lean
|
|
||||||
; formalization:
|
|
||||||
; (1) At most one guard evaluates to true under any model.
|
|
||||||
; (2) For all models m under which (vc) evaluates to vc-true, there is
|
|
||||||
; exactly one guard in guards that evaluates to #t under m.
|
|
||||||
; (3) For all models m under which (vc) doesn't evaluate to vc-true,
|
|
||||||
; every vc produced by evaluating the given thunks evaluates to
|
|
||||||
; the same spec as (vc) under m.
|
|
||||||
(define (eval-guarded! guards thunks)
|
|
||||||
(define results (map eval-assuming guards thunks))
|
|
||||||
(merge-vc! guards (map result-state results))
|
|
||||||
(define-values (gs rs)
|
|
||||||
(for/lists (gs rs) ([g guards][r results] #:when (normal? r))
|
|
||||||
(values g (result-value r))))
|
|
||||||
(if (null? rs)
|
|
||||||
(raise-exn:fail:svm:merge)
|
|
||||||
(begin
|
|
||||||
(merge-stores! gs (map result-state rs))
|
|
||||||
(apply merge* (for/list ([g gs][r rs])
|
|
||||||
(cons g (result-value r)))))))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,107 +0,0 @@
|
||||||
#lang racket
|
|
||||||
|
|
||||||
(require (only-in racket/string string-split)
|
|
||||||
(for-syntax racket/syntax racket/string)
|
|
||||||
racket/provide)
|
|
||||||
|
|
||||||
(provide (matching-identifiers-out #px"^exn:fail:svm.*\\?$" (all-defined-out))
|
|
||||||
(matching-identifiers-out #px"^make\\-exn:fail:svm.*$" (all-defined-out))
|
|
||||||
(matching-identifiers-out #px"^raise\\-exn:fail:svm.*$" (all-defined-out))
|
|
||||||
exn:fatal? fatal
|
|
||||||
argument-error arguments-error type-error contract-error index-too-large-error)
|
|
||||||
|
|
||||||
;; --------------- Exceptions --------------- ;;
|
|
||||||
|
|
||||||
; Four kinds of failures can happen during symbolic evaluation:
|
|
||||||
; (1) the execution reaches (assert e) where e evaluates to #f, or asserting e reduces vc's asserts to #f;
|
|
||||||
; (2) the execution reaches (assume e) where e evaluates to #f, or assuming e reduces vc's assumes to #f;
|
|
||||||
; (3) the execution reaches e where e raises an exn:fail? exception; and
|
|
||||||
; (4) all paths at a given merge point led to a failure.
|
|
||||||
; Within the first two types of failures, we distinguish between
|
|
||||||
; assertions and assumptions issued by user code and core (Rosette) code.
|
|
||||||
; The third type of failure is treated as an assertion failure for the
|
|
||||||
; purposes of verification condition generation. Finally,
|
|
||||||
; the fourth type of failure is tracked via exn:fail:svm:merge.
|
|
||||||
|
|
||||||
; The top of the exception hierarchy for failures raised
|
|
||||||
; during symbolic evaluation.
|
|
||||||
(struct exn:fail:svm exn:fail ())
|
|
||||||
|
|
||||||
; An assert exception can be one of the following kinds:
|
|
||||||
; * :core represents an assertion failure raised in Rosette code,
|
|
||||||
; * :user represents an assertion failure raised in user code, and
|
|
||||||
; * :err indicates that an exn:fail? exception was raised during evaluation.
|
|
||||||
(struct exn:fail:svm:assert exn:fail:svm ())
|
|
||||||
(struct exn:fail:svm:assert:core exn:fail:svm:assert ())
|
|
||||||
(struct exn:fail:svm:assert:user exn:fail:svm:assert ())
|
|
||||||
(struct exn:fail:svm:assert:err exn:fail:svm:assert ())
|
|
||||||
|
|
||||||
; An assume exception can be one of the following kinds:
|
|
||||||
; * :core represents an assumption failure raised in Rosette code, and
|
|
||||||
; * :user represents an assumption failure raised in user code.
|
|
||||||
(struct exn:fail:svm:assume exn:fail:svm ())
|
|
||||||
(struct exn:fail:svm:assume:core exn:fail:svm:assume ())
|
|
||||||
(struct exn:fail:svm:assume:user exn:fail:svm:assume ())
|
|
||||||
|
|
||||||
; An merge exception is raised when all paths at a branching point lead to a failure.
|
|
||||||
(struct exn:fail:svm:merge exn:fail:svm ())
|
|
||||||
|
|
||||||
(define-syntax (define-make-and-raise stx)
|
|
||||||
(syntax-case stx ()
|
|
||||||
[(_ id)
|
|
||||||
(with-syntax ([make-id (format-id #'id "make-~a" (syntax-e #'id))]
|
|
||||||
[raise-id (format-id #'id "raise-~a" (syntax-e #'id))]
|
|
||||||
[prefix (list-ref (string-split (symbol->string (syntax-e #'id)) ":") 3)])
|
|
||||||
#'(begin
|
|
||||||
(define (make-id [msg #f] [cont-marks #f])
|
|
||||||
(id (format "[~a] ~a" prefix (or msg "failed"))
|
|
||||||
(or cont-marks (current-continuation-marks))))
|
|
||||||
(define (raise-id [msg #f] [cont-marks #f])
|
|
||||||
(raise (make-id msg cont-marks)))))]
|
|
||||||
[(_ id ...)
|
|
||||||
#'(begin (define-make-and-raise id) ...)]))
|
|
||||||
|
|
||||||
; Creates two procedures make-* and raise-* for each exception type that
|
|
||||||
; creates and raises an exception of the given type, respectively.
|
|
||||||
(define-make-and-raise
|
|
||||||
exn:fail:svm:assert:core
|
|
||||||
exn:fail:svm:assert:user
|
|
||||||
exn:fail:svm:assert:err
|
|
||||||
exn:fail:svm:assume:core
|
|
||||||
exn:fail:svm:assume:user
|
|
||||||
exn:fail:svm:merge)
|
|
||||||
|
|
||||||
;; --------------- Messages --------------- ;;
|
|
||||||
|
|
||||||
; Fatal errors indicate bugs in the Rosette implementation.
|
|
||||||
; Since Rosette only catches and handles errors of subtype exn:fail?,
|
|
||||||
; exn:fatal is a subtype of exn and hence will not be caught as part
|
|
||||||
; of symbolic evaluation.
|
|
||||||
(struct exn:fatal exn ())
|
|
||||||
(define (fatal msg) (raise (exn:fatal msg (current-continuation-marks))))
|
|
||||||
|
|
||||||
(define (argument-error name expected given)
|
|
||||||
(format "~a: contract violation\n expected: ~a\n given: ~a"
|
|
||||||
name expected given))
|
|
||||||
|
|
||||||
(define (arguments-error name message . field-value)
|
|
||||||
(define o (open-output-string))
|
|
||||||
(fprintf o "~a: ~a" name message)
|
|
||||||
(let loop ([fvs field-value])
|
|
||||||
(match fvs
|
|
||||||
[(list) (get-output-string o)]
|
|
||||||
[(list f) (fatal (format "arguments-error: missing value after field string ~a" f))]
|
|
||||||
[(list f v rest ...)
|
|
||||||
(fprintf o "\n ~a: ~a" f v)
|
|
||||||
(loop rest)])))
|
|
||||||
|
|
||||||
(define (type-error name expected given)
|
|
||||||
(argument-error name (format "~a" expected) given))
|
|
||||||
|
|
||||||
(define (contract-error name contract given)
|
|
||||||
(argument-error name (format "~a" (contract-name contract)) given))
|
|
||||||
|
|
||||||
(define (index-too-large-error who xs idx)
|
|
||||||
(arguments-error who "index is too large" "index" idx "in" xs))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,15 +1,12 @@
|
||||||
#lang racket
|
#lang racket
|
||||||
|
|
||||||
(require racket/splicing (for-syntax racket/syntax)
|
(require racket/splicing (for-syntax racket/syntax)
|
||||||
syntax/parse/define
|
|
||||||
(only-in racket/unsafe/ops [unsafe-car car] [unsafe-cdr cdr])
|
(only-in racket/unsafe/ops [unsafe-car car] [unsafe-cdr cdr])
|
||||||
(only-in "merge.rkt" merge merge* merge-same)
|
(only-in "merge.rkt" merge merge*)
|
||||||
(only-in "bool.rkt" ! || &&)
|
(only-in "bool.rkt" ! || pc)
|
||||||
(only-in "union.rkt" union union?)
|
(only-in "union.rkt" union)
|
||||||
(only-in "term.rkt" expression)
|
(only-in "effects.rkt" speculate* location=? location-final-value)
|
||||||
(only-in "polymorphic.rkt" guarded guarded-test guarded-value ite ite*)
|
"safe.rkt")
|
||||||
(only-in "equality.rkt" @equal?)
|
|
||||||
"safe.rkt" "../core/eval.rkt" "../core/store.rkt" "../core/result.rkt")
|
|
||||||
|
|
||||||
(provide for/all for*/all guard-apply)
|
(provide for/all for*/all guard-apply)
|
||||||
|
|
||||||
|
|
@ -20,13 +17,15 @@
|
||||||
; (for/all ([v0 val0])
|
; (for/all ([v0 val0])
|
||||||
; (for/all ([v1 val1])
|
; (for/all ([v1 val1])
|
||||||
; expr))
|
; expr))
|
||||||
(define-syntax-parser for*/all
|
(define-syntax for*/all
|
||||||
#:disable-colon-notation
|
(syntax-rules ()
|
||||||
[(_ () e ...+) (syntax/loc this-syntax (begin e ...))]
|
[(_ () expr) expr]
|
||||||
[(_ (v0:gv0 v:gv ...) e ...+)
|
[(_ ([v gv]) expr)
|
||||||
(syntax/loc this-syntax
|
(for/all ([v gv]) expr)]
|
||||||
(for/all (v0:gv0)
|
[(_ ([v0 gv0] [v gv] ...) expr)
|
||||||
(for*/all (v:gv ...) e ...)))])
|
(for/all ([v0 gv0])
|
||||||
|
(for*/all ([v gv] ...) expr))]))
|
||||||
|
|
||||||
|
|
||||||
; This macro takes the following form:
|
; This macro takes the following form:
|
||||||
; (for/all ([v val]) expr)
|
; (for/all ([v val]) expr)
|
||||||
|
|
@ -37,68 +36,74 @@
|
||||||
; symbolic reference could point. If the provided
|
; symbolic reference could point. If the provided
|
||||||
; value is not a symbolic reference, then the expression
|
; value is not a symbolic reference, then the expression
|
||||||
; is simply evaluated with v bound to the value itself.
|
; is simply evaluated with v bound to the value itself.
|
||||||
(define-syntax-parser for/all
|
(define-syntax (for/all stx)
|
||||||
[(_ ([v:id val]) e ...+)
|
(syntax-case stx ()
|
||||||
(syntax/loc this-syntax
|
[(_ ([v val]) expr)
|
||||||
(let ([proc (lambda (v) e ...)])
|
(identifier? #'v)
|
||||||
(match val
|
(syntax/loc stx (let ([proc (lambda (v) expr)])
|
||||||
[(union gvs) (guard-apply proc gvs)]
|
(match val
|
||||||
[other (proc other)])))]
|
[(union gvs) (guard-apply proc gvs)]
|
||||||
[(_ ([v:id val #:exhaustive]) e ...+)
|
[v (proc v)])))]))
|
||||||
#:with ooo (quote-syntax ...)
|
|
||||||
(syntax/loc this-syntax
|
|
||||||
(let ([proc (lambda (v) e ...)])
|
|
||||||
(match val
|
|
||||||
[(or (? union? sym) (and (expression (or (== ite) (== ite*)) _ ooo) sym))
|
|
||||||
(guard-apply proc (flatten-guarded sym))]
|
|
||||||
[other (proc other)])))]
|
|
||||||
[(_ ([v:id val concrete]) e ...+)
|
|
||||||
(syntax/loc this-syntax (for/all ([v val concrete @equal?]) e ...))]
|
|
||||||
[(_ ([v:id val concrete ==]) e ...+)
|
|
||||||
(syntax/loc this-syntax
|
|
||||||
(let ([sym val] [=== ==])
|
|
||||||
(guard-apply
|
|
||||||
(lambda (v) e ...)
|
|
||||||
(for/list ([c concrete]) (cons (=== sym c) c)))))])
|
|
||||||
|
|
||||||
(define (flatten-guarded v)
|
|
||||||
(merge-same
|
|
||||||
(let loop ([guards '()][val v])
|
|
||||||
(match val
|
|
||||||
[(expression (== ite) c t e)
|
|
||||||
(append (loop (cons c guards) t)
|
|
||||||
(loop (cons (! c) guards) e))]
|
|
||||||
[(expression (== ite*) gvs ...)
|
|
||||||
(apply append
|
|
||||||
(for/list ([gv gvs])
|
|
||||||
(loop (cons (guarded-test gv) guards)
|
|
||||||
(guarded-value gv))))]
|
|
||||||
[(union gvs)
|
|
||||||
(apply append
|
|
||||||
(for/list ([gv gvs])
|
|
||||||
(loop (cons (car gv) guards)
|
|
||||||
(cdr gv))))]
|
|
||||||
[_ (list (cons (apply && guards) val))]))))
|
|
||||||
|
|
||||||
; Applies the given procedure to each of the guarded values,
|
; Applies the given procedure to each of the guarded values,
|
||||||
; given as guard/value structures. The application of the procedure
|
; given as guard/value pairs. The application of the procedure
|
||||||
; to each value is done under the value's guard, and so are all
|
; to each value is done under the value's guard, and so are all
|
||||||
; the state updates performed during the evaluation. The result
|
; the state updates performed during the evaluation. The result
|
||||||
; of this procedure is the result of this evaluation process.
|
; of this procedure is the result of this evaluation process.
|
||||||
; The guard-apply procedure also merges any state updates resulting
|
; The guard-apply procedure also merges any state updates resulting
|
||||||
; from successful guarded evaluations of proc on the given values.
|
; from successful guarded evaluations of proc on the given values.
|
||||||
;
|
;
|
||||||
; At most one of the given guards may be true under any model.
|
; All given guards are required to be pairwise mutually exclusive,
|
||||||
(define (guard-apply proc guarded-values [guard-of car] [value-of cdr])
|
; and at least one of the guards must always evaluate to true.
|
||||||
; If any of the guarded-values has #t as its guard, it's executed
|
(define (guard-apply proc guarded-values)
|
||||||
; directly, since all the guards must be #f under all models.
|
(define-values (guards outputs states)
|
||||||
(define gv (findf (lambda (gv) (eq? (guard-of gv) #t)) guarded-values))
|
(guard-speculate* proc guarded-values))
|
||||||
(cond
|
(when (null? guards)
|
||||||
[gv (proc (value-of gv))]
|
(assert #f (thunk (error 'for/all "all paths infeasible"))))
|
||||||
[else (eval-guarded! (map guard-of guarded-values)
|
(when (ormap pair? states)
|
||||||
(map (lambda (gv) (thunk (proc (value-of gv)))) guarded-values))]))
|
(merge-states guards states))
|
||||||
|
(apply merge* (map cons guards outputs)))
|
||||||
|
|
||||||
|
; Speculatively executes the given procedure on the provided
|
||||||
|
; guarded values and returns three lists---guards, outputs,
|
||||||
|
; and states---of equal length. For each input pair (cons g v)
|
||||||
|
; in guarded-values for which (proc v) terminates without an
|
||||||
|
; error, there is an index i such that the ith element of the
|
||||||
|
; guards list is g, the ith element of the outputs list is
|
||||||
|
; (proc v), and the ith element of the states list is the list
|
||||||
|
; of all states updates that were performed when executing (proc v).
|
||||||
|
; Note that all state update objects for the ith execution are
|
||||||
|
; are unique according to location=?, but two state updates in
|
||||||
|
; different executions may be location=?. (That is, proc would
|
||||||
|
; update the same location if it were called with two different
|
||||||
|
; values.)
|
||||||
|
(define (guard-speculate* proc guarded-values)
|
||||||
|
(for/fold ([guards '()] [outputs '()] [states '()]) ([gv guarded-values])
|
||||||
|
(define guard (car gv))
|
||||||
|
(define val (cdr gv))
|
||||||
|
(define-values (output state)
|
||||||
|
(speculate*
|
||||||
|
(parameterize ([pc guard])
|
||||||
|
(proc val))))
|
||||||
|
(cond [state (values (cons guard guards) (cons output outputs) (cons state states))]
|
||||||
|
[else (assert (! guard) (thunk (error 'for/all "all paths infeasible")))
|
||||||
|
(values guards outputs states)])))
|
||||||
|
|
||||||
|
|
||||||
|
; Given a list of n guards and their corresponding lists of
|
||||||
|
; state-update objects, performs an n-way merge of all updates
|
||||||
|
; to memory locations that are encapsulated in those states.
|
||||||
|
(define (merge-states guards states)
|
||||||
|
(define locations (remove-duplicates (apply append states) location=?))
|
||||||
|
(define guarded-states (append-map (lambda (g sts) (map (curry cons g) sts)) guards states))
|
||||||
|
(define max-guards-per-location (length guards))
|
||||||
|
(define (merge-procedure gss)
|
||||||
|
(if (= (length gss) max-guards-per-location)
|
||||||
|
(lambda (pre post) (apply merge* gss))
|
||||||
|
(lambda (pre post) (apply merge* (cons (! (apply || (map car gss))) pre) gss))))
|
||||||
|
(for ([loc locations])
|
||||||
|
(loc (merge-procedure
|
||||||
|
(for/list ([gs guarded-states]
|
||||||
|
#:when (location=? loc (cdr gs)))
|
||||||
|
(cons (car gs) (location-final-value (cdr gs))))))))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,11 @@
|
||||||
#lang racket
|
#lang racket
|
||||||
|
|
||||||
(require racket/generic
|
(require racket/generic
|
||||||
(for-syntax syntax/transformer)
|
"type.rkt" "bool.rkt" "safe.rkt" "union.rkt" "equality.rkt" "merge.rkt"
|
||||||
"term.rkt" "bool.rkt" "safe.rkt" "union.rkt" "equality.rkt" "merge.rkt"
|
|
||||||
(only-in "procedure.rkt" @procedure?))
|
(only-in "procedure.rkt" @procedure?))
|
||||||
|
|
||||||
(provide (rename-out [fv-stx fv]) @fv? fv? fv-type
|
(provide (rename-out [fv-stx fv]) fv? fv-cond fv-else fv-type
|
||||||
~> function function? function-domain function-range)
|
function function? function-domain function-range)
|
||||||
|
|
||||||
#|-----------------------------------------------------------------------------------|#
|
#|-----------------------------------------------------------------------------------|#
|
||||||
; A function type is a solvable applicable type. That is, it implements the solvable?
|
; A function type is a solvable applicable type. That is, it implements the solvable?
|
||||||
|
|
@ -14,8 +13,10 @@
|
||||||
; is a non-empty list of primitive-solvable? types, and its range is a primitive-solvable?
|
; is a non-empty list of primitive-solvable? types, and its range is a primitive-solvable?
|
||||||
; type.
|
; type.
|
||||||
;
|
;
|
||||||
; The only values that have function types are instances of the fv struct.
|
; The only values that have function types are instances of the fv struct. This struct
|
||||||
; An fv value is a procedure and can be directly applied to values
|
; represents functions that are essentially lookup tables. Each fv has a set of 'cond'
|
||||||
|
; cases, which map specific inputs to outputs. All unmapped inputs are mapped to the
|
||||||
|
; 'else' value. An fv value is a procedure and can be directly applied to values
|
||||||
; (symbolic, concrete, or a mix of the two).
|
; (symbolic, concrete, or a mix of the two).
|
||||||
#|-----------------------------------------------------------------------------------|#
|
#|-----------------------------------------------------------------------------------|#
|
||||||
|
|
||||||
|
|
@ -56,7 +57,7 @@
|
||||||
(assert (apply || (union-guards u)) (argument-error caller (~a self) v))
|
(assert (apply || (union-guards u)) (argument-error caller (~a self) v))
|
||||||
u])]
|
u])]
|
||||||
[_ (assert #f (argument-error caller (~a self) v))]))
|
[_ (assert #f (argument-error caller (~a self) v))]))
|
||||||
(define (type-eq? self u v) (eq? u v))
|
(define (type-eq? self u v) (equal? u v))
|
||||||
(define (type-equal? self u v) (equal? u v))
|
(define (type-equal? self u v) (equal? u v))
|
||||||
(define (type-compress self force? ps) ps)
|
(define (type-compress self force? ps) ps)
|
||||||
(define (type-construct self vs) (car vs))
|
(define (type-construct self vs) (car vs))
|
||||||
|
|
@ -64,53 +65,50 @@
|
||||||
#:methods gen:solvable
|
#:methods gen:solvable
|
||||||
[(define/generic generic-solvable-default solvable-default)
|
[(define/generic generic-solvable-default solvable-default)
|
||||||
(define (solvable-default self)
|
(define (solvable-default self)
|
||||||
(fv self (procedure-reduce-arity
|
(fv null (generic-solvable-default (function-range self)) self))
|
||||||
(lambda args (generic-solvable-default (function-range self)))
|
|
||||||
(length (function-domain self)))))
|
|
||||||
(define (solvable-domain self) (function-domain self))
|
(define (solvable-domain self) (function-domain self))
|
||||||
(define (solvable-range self) (function-range self))]
|
(define (solvable-range self) (function-range self))]
|
||||||
#:methods gen:custom-write
|
#:methods gen:custom-write
|
||||||
[(define (write-proc self port m)
|
[(define (write-proc self port m)
|
||||||
(match-define (function dom ran) self)
|
(match-define (function dom ran) self)
|
||||||
(for ([t dom]) (fprintf port "~a~a" t "~>"))
|
(for ([t dom]) (fprintf port "~a->" t))
|
||||||
(fprintf port "~a" ran))])
|
(fprintf port "~a" ran))])
|
||||||
|
|
||||||
(define ~>
|
|
||||||
(case-lambda
|
|
||||||
[(d r) (function (list d) r)]
|
|
||||||
[(d0 d1 r) (function (list d0 d1) r)]
|
|
||||||
[(d0 d1 . rest) (function `(,d0 ,d1 ,@(drop-right rest 1)) (last rest))]))
|
|
||||||
|
|
||||||
; Represents a function value.
|
; Represents a function value.
|
||||||
(struct fv (type λ)
|
(struct fv (cond else type λ)
|
||||||
#:property prop:procedure
|
#:property prop:procedure
|
||||||
[struct-field-index λ]
|
[struct-field-index λ]
|
||||||
#:methods gen:typed
|
#:methods gen:typed
|
||||||
[(define (get-type self) (fv-type self))]
|
[(define (get-type self) (fv-type self))]
|
||||||
|
#:methods gen:equal+hash
|
||||||
|
[(define (equal-proc u1 u2 rec=?)
|
||||||
|
(and (rec=? (fv-type u1) (fv-type u2))
|
||||||
|
(rec=? (fv-else u1) (fv-else u2))
|
||||||
|
(rec=? (fv-cond u1) (fv-cond u2))))
|
||||||
|
(define (hash-proc u1 rec-hash)
|
||||||
|
(rec-hash (list (fv-type u1) (fv-cond u1) (fv-else u1))))
|
||||||
|
(define (hash2-proc u1 rec-hash)
|
||||||
|
(rec-hash (list (fv-type u1) (fv-cond u1) (fv-else u1))))]
|
||||||
#:methods gen:custom-write
|
#:methods gen:custom-write
|
||||||
[(define (write-proc self port m)
|
[(define (write-proc self port m)
|
||||||
(fprintf port "(fv ~a)" (fv-type self)))])
|
(fprintf port "(fv ~a ~a ~a)" (fv-cond self) (fv-else self) (fv-type self)))])
|
||||||
|
|
||||||
(define (make-fv type proc)
|
(define (make-fv ios o type)
|
||||||
(fv type
|
(fv ios o type
|
||||||
(procedure-reduce-arity
|
(procedure-reduce-arity
|
||||||
(lambda args
|
(lambda args
|
||||||
(apply proc
|
(let* ([args (for/list ([a args] [t (function-domain type)])
|
||||||
(for/list ([a args] [t (function-domain type)])
|
(type-cast t a))]
|
||||||
(type-cast t a))))
|
[parts (for/list ([io ios])
|
||||||
|
(cons (@equal? (car io) args) (cdr io)))])
|
||||||
|
(apply merge* (cons (! (apply || (map car parts))) o) parts)))
|
||||||
(length (function-domain type)))))
|
(length (function-domain type)))))
|
||||||
|
|
||||||
(define-match-expander fv-stx
|
(define-match-expander fv-stx
|
||||||
(lambda (stx)
|
(lambda (stx)
|
||||||
(syntax-case stx ()
|
(syntax-case stx ()
|
||||||
[(_ pat ...) #'(fv pat ... _)]))
|
[(_ pat ...) #'(fv pat ... _)]))
|
||||||
(make-variable-like-transformer #'make-fv))
|
(syntax-id-rules ()
|
||||||
|
[(_ ios o type) (make-fv ios o type)]
|
||||||
|
[_ make-fv]))
|
||||||
|
|
||||||
(define (@fv? v)
|
|
||||||
(match v
|
|
||||||
[(? fv?) #t]
|
|
||||||
[(term _ (? function?)) #t]
|
|
||||||
[(union _ (? function?)) #t]
|
|
||||||
[(union xs (or (== @procedure?) (== @any/c)))
|
|
||||||
(apply || (for/list ([gv xs] #:when (@fv? (cdr gv))) (car gv)))]
|
|
||||||
[_ #f]))
|
|
||||||
|
|
|
||||||
|
|
@ -52,12 +52,12 @@
|
||||||
(define-syntax (define/lift stx)
|
(define-syntax (define/lift stx)
|
||||||
(syntax-case stx (: :: ->)
|
(syntax-case stx (: :: ->)
|
||||||
[(_ (id0 id ...) :: contracted? -> rosette-type?)
|
[(_ (id0 id ...) :: contracted? -> rosette-type?)
|
||||||
(or (identifier? #'contracted?) (raise-argument-error "identifier?" #'contracted?))
|
(or (identifier? #'contracted) (raise-argument-error "identifier?" #'contracted?))
|
||||||
#'(begin
|
#'(begin
|
||||||
(define/lift id0 :: contracted? -> rosette-type?)
|
(define/lift id0 :: contracted? -> rosette-type?)
|
||||||
(define/lift id :: contracted? -> rosette-type?) ...)]
|
(define/lift id :: contracted? -> rosette-type?) ...)]
|
||||||
[(_ id :: contracted? -> rosette-type?) ; repeated from (_ id : contracted? -> rosette-type?) - params don't work
|
[(_ id :: contracted? -> rosette-type?) ; repeated from (_ id : contracted? -> rosette-type?) - params don't work
|
||||||
(or (identifier? #'contracted?) (raise-argument-error "identifier?" #'contracted?))
|
(or (identifier? #'contracted) (raise-argument-error "identifier?" #'contracted?))
|
||||||
#`(define (#,(lift-id #'id) val)
|
#`(define (#,(lift-id #'id) val)
|
||||||
(if (contracted? val)
|
(if (contracted? val)
|
||||||
(id val)
|
(id val)
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
#lang racket
|
#lang racket
|
||||||
|
|
||||||
(require (only-in racket/unsafe/ops [unsafe-car car] [unsafe-cdr cdr])
|
(require (only-in rnrs/base-6 assert)
|
||||||
"term.rkt" "union.rkt" "bool.rkt" "reporter.rkt")
|
(only-in racket/unsafe/ops [unsafe-car car] [unsafe-cdr cdr])
|
||||||
|
"term.rkt" "union.rkt" "bool.rkt")
|
||||||
|
|
||||||
(provide merge merge* unsafe-merge* merge-same)
|
(provide merge merge* unsafe-merge*)
|
||||||
|
|
||||||
(define (merge b x y)
|
(define (merge b x y)
|
||||||
(match* (b x y)
|
(match* (b x y)
|
||||||
|
|
@ -31,59 +32,71 @@
|
||||||
(do-merge* #t ps))
|
(do-merge* #t ps))
|
||||||
|
|
||||||
(define-syntax-rule (do-merge* force? ps)
|
(define-syntax-rule (do-merge* force? ps)
|
||||||
(let ([simp (simplify ps)])
|
(match (compress force? (simplify ps))
|
||||||
((current-reporter) 'merge (length simp))
|
[(list (cons g v)) (assert (not (false? g))) v]
|
||||||
(match (compress force? simp)
|
[(list _ (... ...) (cons #t v) _ (... ...)) v]
|
||||||
[(list (cons g v)) v]
|
[vs (apply union vs)]))
|
||||||
[(list _ (... ...) (cons #t v) _ (... ...)) v]
|
|
||||||
[vs (apply union vs)])))
|
|
||||||
|
|
||||||
(define (guard g gvs)
|
(define (guard-&& a b)
|
||||||
(for*/list ([gv gvs]
|
(match b
|
||||||
[gg (in-value (&& g (car gv)))]
|
[(expression (== @&&) c ...) (apply && a c)]
|
||||||
#:when gg)
|
[_ (&& a b)]))
|
||||||
(cons gg (cdr gv))))
|
|
||||||
|
(define (guard g vs)
|
||||||
|
(filter-map (lambda (v)
|
||||||
|
(let ([gv (guard-&& g (car v))])
|
||||||
|
(and gv (cons gv (cdr v)))))
|
||||||
|
vs))
|
||||||
|
|
||||||
(define (simplify ps)
|
(define (simplify ps)
|
||||||
(match ps
|
(let loop ([ps ps] [out '()])
|
||||||
[(list _ ... (and (cons #t _) p) _ ...)
|
(match ps
|
||||||
(list p)]
|
[(list) out]
|
||||||
[_ (for/fold ([out '()]) ([p ps])
|
[(list (and (cons #t v) p) _ ...)
|
||||||
(match p
|
(list p)]
|
||||||
[(cons #f _) out]
|
[(list (cons #f _) rest ...)
|
||||||
[(cons g (union (and (not (? null?)) gvs)))
|
(loop rest out)]
|
||||||
(append (guard g gvs) out)]
|
[(list (cons g (union (and (not (? null?)) vs))) rest ...)
|
||||||
[_ (cons p out)]))]))
|
(loop rest (append (guard g vs) out))]
|
||||||
|
[(list p rest ...)
|
||||||
|
(loop rest (cons p out))])))
|
||||||
|
|
||||||
(define (type-of-value gv) (type-of (cdr gv)))
|
(define (group ps)
|
||||||
|
(let ([types (remove-duplicates (for/list ([p ps]) (type-of (cdr p))))])
|
||||||
|
(for*/list ([t types] [p ps] #:when (equal? t (type-of (cdr p)))) p)))
|
||||||
|
|
||||||
(define (compress force? ps)
|
(define (compress force? ps)
|
||||||
(match ps
|
(match ps
|
||||||
[(list _) ps]
|
[(list _) ps]
|
||||||
[(list (cons _ (app type-of t)) (cons _ (app type-of t)))
|
[(list (cons g (app type-of t)) (cons h (app type-of t)))
|
||||||
(type-compress t force? (merge-same ps))]
|
(type-compress t force? (merge-same ps))]
|
||||||
[(list _ _) ps]
|
[(list _ _) ps]
|
||||||
[_ (append-map
|
[_ (let loop ([ps (group ps)] [type #f] [acc '()])
|
||||||
(lambda (group)
|
;(printf "compress ~a ~a ~a\n" ps type acc)
|
||||||
(type-compress
|
(match ps
|
||||||
(type-of (cdar group))
|
[(list)
|
||||||
force?
|
(append-map (lambda (group)
|
||||||
(merge-same group)))
|
(type-compress (type-of (cdar group))
|
||||||
(group-by type-of-value ps))]))
|
force?
|
||||||
|
(merge-same group)))
|
||||||
|
acc)]
|
||||||
|
[(list (and (cons _ (app type-of (== type))) p) rest ...)
|
||||||
|
(loop rest type (cons (cons p (car acc)) (cdr acc)))]
|
||||||
|
[(list p rest ...)
|
||||||
|
(loop rest (type-of (cdr p)) (cons (list p) acc))]))]))
|
||||||
|
|
||||||
(define (merge-same ps)
|
(define (merge-same ps)
|
||||||
|
;(printf "merge ~a\n" ps)
|
||||||
(match ps
|
(match ps
|
||||||
[(or (list) (list _)) ps]
|
[(or (list) (list _)) ps]
|
||||||
[(list (cons g v) (cons h u))
|
[(list (cons g v) (cons h u)) (if (eq? v u) (list (cons (|| g h) v)) ps)]
|
||||||
(if (eq? v u) (list (cons (|| g h) v)) ps)]
|
[_ (let loop ([ps ps] [out '()])
|
||||||
[_ (let loop ([ps (group-by cdr ps eq?)] [out '()])
|
(if (null? ps)
|
||||||
(match ps
|
out
|
||||||
[(list) out]
|
(match-let*-values
|
||||||
[(list (list gv) rest ...)
|
([((cons g v)) (car ps)]
|
||||||
(loop rest (cons gv out))]
|
[((list (cons h _) ...) rest) (partition (compose (curry eq? v) cdr) (cdr ps))]
|
||||||
[(list group rest ...)
|
[(g) (apply || g h)])
|
||||||
(let ([g (apply || (map car group))]
|
(if (equal? g #t)
|
||||||
[v (cdar group)])
|
|
||||||
(if (eq? g #t)
|
|
||||||
(list (cons g v))
|
(list (cons g v))
|
||||||
(loop rest (cons (cons g v) out))))]))]))
|
(loop rest (cons (cons g v) out))))))]))
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,9 @@
|
||||||
(provide @number? @positive? @negative? @zero? @even? @odd?
|
(provide @number? @positive? @negative? @zero? @even? @odd?
|
||||||
@add1 @sub1 @sgn @truncate @floor @ceiling @min @max
|
@add1 @sub1 @sgn @truncate @floor @ceiling @min @max
|
||||||
@exact->inexact @inexact->exact @expt
|
@exact->inexact @inexact->exact @expt
|
||||||
extreme)
|
;@sqrt @bitwise-not @bitwise-and @bitwise-ior @bitwise-xor
|
||||||
|
;@<< @>> @>>> @bitwise-bit-set? @bitwise-bit-field
|
||||||
|
)
|
||||||
|
|
||||||
(define (@number? v) (or (number? v) (@real? v)))
|
(define (@number? v) (or (number? v) (@real? v)))
|
||||||
(define (@positive? x) (@> x 0))
|
(define (@positive? x) (@> x 0))
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,9 @@
|
||||||
[(x y) (or (and (typed? x) (get-type x)) (get-type y))]
|
[(x y) (or (and (typed? x) (get-type x)) (get-type y))]
|
||||||
[xs (for/first ([x xs] #:when (typed? x)) (get-type x))]))
|
[xs (for/first ([x xs] #:when (typed? x)) (get-type x))]))
|
||||||
|
|
||||||
|
; A generic typing procedure for a lifted operator that takes N >= 0 arguments of type T
|
||||||
|
; and returns a @boolean?. See term.rkt.
|
||||||
|
(define (T*->boolean? . xs) @boolean?)
|
||||||
|
|
||||||
; Polymorphic operators and procedures that are shared by
|
; Polymorphic operators and procedures that are shared by
|
||||||
; multiple primitive types.
|
; multiple primitive types.
|
||||||
|
|
@ -152,7 +155,7 @@
|
||||||
(match* ((car p) (cdr p))
|
(match* ((car p) (cdr p))
|
||||||
[(a (expression (== ite) a x _)) (cons a x)]
|
[(a (expression (== ite) a x _)) (cons a x)]
|
||||||
[(a (expression (== ite) (expression (== @!) a) _ x)) (cons a x)]
|
[(a (expression (== ite) (expression (== @!) a) _ x)) (cons a x)]
|
||||||
[((and (expression (== @!) a) !a) (expression (== ite) a _ x)) (cons !a x)]
|
[((expression (== @!) a) (expression (== ite) a _ x)) (cons a x)]
|
||||||
[(_ _) p]))
|
[(_ _) p]))
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
(require
|
(require
|
||||||
racket/provide
|
racket/provide
|
||||||
(for-syntax racket/syntax (only-in "lift.rkt" with@))
|
(for-syntax racket/syntax (only-in "lift.rkt" with@))
|
||||||
(only-in "type.rkt" define-lifted-type type-cast typed? get-type subtype? type-applicable? @any/c)
|
(only-in "type.rkt" define-lifted-type typed? get-type subtype? type-applicable? @any/c)
|
||||||
(only-in "bool.rkt" || @false?)
|
(only-in "bool.rkt" || @false?)
|
||||||
(only-in "union.rkt" union union? in-union-guards union-filter union-guards)
|
(only-in "union.rkt" union union? in-union-guards union-filter union-guards)
|
||||||
(only-in "safe.rkt" assert argument-error)
|
(only-in "safe.rkt" assert argument-error)
|
||||||
|
|
@ -26,7 +26,7 @@
|
||||||
@procedure?
|
@procedure?
|
||||||
@any/c))
|
@any/c))
|
||||||
(define (type-applicable? self) #t)
|
(define (type-applicable? self) #t)
|
||||||
(define (type-eq? self v0 v1) (eq? v0 v1))
|
(define (type-eq? self v0 v1) (equal? v0 v1))
|
||||||
(define (type-cast self v [caller 'type-cast])
|
(define (type-cast self v [caller 'type-cast])
|
||||||
(match v
|
(match v
|
||||||
[(union _ (== @procedure?)) v]
|
[(union _ (== @procedure?)) v]
|
||||||
|
|
@ -75,8 +75,8 @@
|
||||||
[(union gvs) (guard-apply (curryr procedure-rename name) gvs)]
|
[(union gvs) (guard-apply (curryr procedure-rename name) gvs)]
|
||||||
[(? procedure?) (procedure-rename proc name)]))
|
[(? procedure?) (procedure-rename proc name)]))
|
||||||
|
|
||||||
(define (@negate p)
|
(define (@negate f)
|
||||||
(define f (type-cast @procedure? p 'negate))
|
(unless (@procedure? f) (raise-argument-error 'negate "procedure?" f))
|
||||||
(let-values ([(arity) (procedure-arity f)] [(_ kwds) (procedure-keywords f)])
|
(let-values ([(arity) (procedure-arity f)] [(_ kwds) (procedure-keywords f)])
|
||||||
(case (and (null? kwds) arity) ; optimize some simple cases
|
(case (and (null? kwds) arity) ; optimize some simple cases
|
||||||
[(0) (lambda () (@false? (f)))]
|
[(0) (lambda () (@false? (f)))]
|
||||||
|
|
|
||||||
|
|
@ -97,11 +97,6 @@
|
||||||
[(or (? real?) (term _ (== @real?))) (values i gx)]
|
[(or (? real?) (term _ (== @real?))) (values i gx)]
|
||||||
[_ (values i r)])))
|
[_ (values i r)])))
|
||||||
|
|
||||||
(define-match-expander ≈
|
|
||||||
(lambda (stx)
|
|
||||||
(syntax-case stx ()
|
|
||||||
[(_ v) #`(or v #,(exact->inexact (syntax->datum #'v)))])))
|
|
||||||
|
|
||||||
(define (numeric-coerce v [caller 'numeric-coerce])
|
(define (numeric-coerce v [caller 'numeric-coerce])
|
||||||
(match v
|
(match v
|
||||||
[(? real?) v]
|
[(? real?) v]
|
||||||
|
|
@ -273,9 +268,9 @@
|
||||||
(define ($op x y)
|
(define ($op x y)
|
||||||
(match* (x y)
|
(match* (x y)
|
||||||
[((? integer?) (? integer?)) (op x y)]
|
[((? integer?) (? integer?)) (op x y)]
|
||||||
[(_ (≈ 1)) 0]
|
[(_ 1) 0]
|
||||||
[(_ (≈ -1)) 0]
|
[(_ -1) 0]
|
||||||
[((≈ 0) _) 0]
|
[(0 _) 0]
|
||||||
[(_ (== x)) 0]
|
[(_ (== x)) 0]
|
||||||
[(_ (expression (== @-) (== x))) 0]
|
[(_ (expression (== @-) (== x))) 0]
|
||||||
[((expression (== @-) (== y)) _) 0]
|
[((expression (== @-) (== y)) _) 0]
|
||||||
|
|
@ -295,7 +290,7 @@
|
||||||
(define T*->integer? (const @integer?))
|
(define T*->integer? (const @integer?))
|
||||||
|
|
||||||
(define (undefined-for-zero-error name)
|
(define (undefined-for-zero-error name)
|
||||||
(arguments-error name "undefined for 0"))
|
(thunk (raise-arguments-error name "undefined for 0")))
|
||||||
|
|
||||||
(define-syntax-rule (define-lifted-int-operator @op $op op)
|
(define-syntax-rule (define-lifted-int-operator @op $op op)
|
||||||
(define-operator @op
|
(define-operator @op
|
||||||
|
|
@ -369,8 +364,8 @@
|
||||||
(define (simplify-+ x y)
|
(define (simplify-+ x y)
|
||||||
(match* (x y)
|
(match* (x y)
|
||||||
[((? real?) (? real?)) (+ x y)]
|
[((? real?) (? real?)) (+ x y)]
|
||||||
[(_ (≈ 0)) x]
|
[(_ 0) x]
|
||||||
[((≈ 0) _) y]
|
[(0 _) y]
|
||||||
[((? expression?) (? expression?))
|
[((? expression?) (? expression?))
|
||||||
(or (simplify-+:expr/term x y) (simplify-+:expr/term y x))]
|
(or (simplify-+:expr/term x y) (simplify-+:expr/term y x))]
|
||||||
[((? expression?) _) (simplify-+:expr/term x y)]
|
[((? expression?) _) (simplify-+:expr/term x y)]
|
||||||
|
|
@ -418,12 +413,12 @@
|
||||||
(define (simplify-* x y)
|
(define (simplify-* x y)
|
||||||
(match* (x y)
|
(match* (x y)
|
||||||
[((? real?) (? real?)) (* x y)]
|
[((? real?) (? real?)) (* x y)]
|
||||||
[((≈ 0) _) 0]
|
[(0 _) 0]
|
||||||
[((≈ 1) _) y]
|
[(1 _) y]
|
||||||
[((≈ -1) _) ($- y)]
|
[(-1 _) ($- y)]
|
||||||
[(_ (≈ 0)) 0]
|
[(_ 0) 0]
|
||||||
[(_ (≈ 1)) x]
|
[(_ 1) x]
|
||||||
[(_ (≈ -1)) ($- x)]
|
[(_ -1) ($- x)]
|
||||||
[((? expression?) (? expression?))
|
[((? expression?) (? expression?))
|
||||||
(or (simplify-*:expr/term x y) (simplify-*:expr/term y x))]
|
(or (simplify-*:expr/term x y) (simplify-*:expr/term y x))]
|
||||||
[((? expression?) _) (simplify-*:expr/term x y)]
|
[((? expression?) _) (simplify-*:expr/term x y)]
|
||||||
|
|
@ -461,7 +456,7 @@
|
||||||
; Pattern matching broken in 6.1 when the first rule is in the third position.
|
; Pattern matching broken in 6.1 when the first rule is in the third position.
|
||||||
; TODO: place the first rule in 3rd position and test with 6.2.
|
; TODO: place the first rule in 3rd position and test with 6.2.
|
||||||
(match* (x ys)
|
(match* (x ys)
|
||||||
[((expression (== @/) (≈ 1) c) (list a ... c b ...))
|
[((expression (== @/) 1 c) (list a ... c b ...))
|
||||||
(append a b)]
|
(append a b)]
|
||||||
[((? term?) (list a ... (expression (== @/) 1 (== x)) b ...)) (append a b)]
|
[((? term?) (list a ... (expression (== @/) 1 (== x)) b ...)) (append a b)]
|
||||||
[((? real?) (list (? real? a) b ...)) (and (= 1 (* x a)) b)]
|
[((? real?) (list (? real? a) b ...)) (and (= 1 (* x a)) b)]
|
||||||
|
|
@ -471,9 +466,9 @@
|
||||||
(lambda (x y)
|
(lambda (x y)
|
||||||
(match* (x y)
|
(match* (x y)
|
||||||
[((? real?) (? real?)) (op x y)]
|
[((? real?) (? real?)) (op x y)]
|
||||||
[((≈ 0) _) 0]
|
[(0 _) 0]
|
||||||
[(_ (≈ 1)) x]
|
[(_ 1) x]
|
||||||
[(_ (≈ -1)) ($- x)]
|
[(_ -1) ($- x)]
|
||||||
[(_ (== x)) 1]
|
[(_ (== x)) 1]
|
||||||
[(_ (expression (== @-) (== x))) -1]
|
[(_ (expression (== @-) (== x))) -1]
|
||||||
[((expression (== @-) (== y)) _) -1]
|
[((expression (== @-) (== y)) _) -1]
|
||||||
|
|
|
||||||
|
|
@ -1,74 +1,44 @@
|
||||||
#lang racket
|
#lang racket
|
||||||
|
|
||||||
(require (only-in "forall.rkt" for/all for*/all)
|
(require (only-in "forall.rkt" for/all for*/all)
|
||||||
"term.rkt" "union.rkt" "result.rkt")
|
"term.rkt" "union.rkt")
|
||||||
|
|
||||||
(provide type? solvable? @any/c type-of type-cast for/all for*/all
|
(provide type? type-of type-cast for/all for*/all
|
||||||
term? constant? expression?
|
term? constant? expression?
|
||||||
term expression constant
|
term expression constant
|
||||||
term-type term=? term->datum
|
term-type term=?
|
||||||
terms terms-count terms-ref with-terms clear-terms! gc-terms!
|
term->datum clear-terms! term-cache
|
||||||
union? union union-contents union-guards union-values
|
union? union union-contents union-guards union-values
|
||||||
union-filter in-union in-union* in-union-guards in-union-values
|
union-filter in-union in-union* in-union-guards in-union-values
|
||||||
(struct-out normal) (struct-out failed) result? result-value result-state
|
symbolics)
|
||||||
symbolics concrete? symbolic?)
|
|
||||||
|
|
||||||
(define (term=? s0 s1)
|
(define (term=? s0 s1)
|
||||||
(and (term? s0) (term? s1) (equal? s0 s1)))
|
(and (term? s0) (term? s1) (equal? s0 s1)))
|
||||||
|
|
||||||
(define (symbolics vs)
|
(define (symbolics vs)
|
||||||
(match vs
|
(match vs
|
||||||
[(list (? constant?) ...) (remove-duplicates vs)]
|
[(list (? constant?) ...) vs]
|
||||||
[(? constant?) (list vs)]
|
[_ (let ([cache (make-hash)])
|
||||||
[_ (let ([terms (mutable-set)]
|
(let loop ([vs vs])
|
||||||
[objs (mutable-set)]
|
(hash-ref!
|
||||||
[result '()])
|
cache
|
||||||
(let loop ([datum vs])
|
vs
|
||||||
(if (term? datum)
|
(lambda ()
|
||||||
(let ([id (term-id datum)])
|
(remove-duplicates
|
||||||
(unless (set-member? terms id)
|
(match vs
|
||||||
(set-add! terms id)
|
[(union (list (cons guard value) ...))
|
||||||
(match datum
|
(append (append-map loop guard) (append-map loop value))]
|
||||||
[(expression _ x ...) (for-each loop x)]
|
[(expression _ x ...) (append-map loop x)]
|
||||||
[(? constant?) (set! result (cons datum result))])))
|
[(? constant? v) (list v)]
|
||||||
(unless (set-member? objs datum)
|
[(box v) (loop v)]
|
||||||
(set-add! objs datum)
|
[(? list?) (append-map loop vs)]
|
||||||
(match datum
|
[(cons x y) (append (loop x) (loop y))]
|
||||||
[(union (list (cons guard value) ...))
|
[(vector v ...) (append-map loop v)]
|
||||||
(for-each loop guard) (for-each loop value)]
|
[(and (? typed?) (app get-type t))
|
||||||
[(box v) (loop v)]
|
(match (type-deconstruct t vs)
|
||||||
[(? list?) (for-each loop datum)]
|
[(list (== vs)) '()]
|
||||||
[(cons x y) (loop x) (loop y)]
|
[components (append-map loop components)])]
|
||||||
[(vector v ...) (for-each loop v)]
|
[_ '()]))))))]))
|
||||||
[(and (? typed?) (app get-type t))
|
|
||||||
(match (type-deconstruct t datum)
|
|
||||||
[(list (== datum)) (void)]
|
|
||||||
[components (for-each loop components)])]
|
|
||||||
[_ (void)]))))
|
|
||||||
(reverse result))]))
|
|
||||||
|
|
||||||
(define (concrete? val)
|
|
||||||
(define objs (mutable-set))
|
|
||||||
(let all-concrete? ([val val])
|
|
||||||
(and (not (term? val))
|
|
||||||
(not (union? val))
|
|
||||||
(or
|
|
||||||
(set-member? objs val)
|
|
||||||
(begin
|
|
||||||
(set-add! objs val)
|
|
||||||
(match val
|
|
||||||
[(box v) (all-concrete? v)]
|
|
||||||
[(? list?) (for/and ([v val]) (all-concrete? v))]
|
|
||||||
[(cons x y) (and (all-concrete? x) (all-concrete? y))]
|
|
||||||
[(? vector?) (for/and ([v val]) (all-concrete? v))]
|
|
||||||
[(and (? typed?) (app get-type t))
|
|
||||||
(match (type-deconstruct t val)
|
|
||||||
[(list (== val)) #t]
|
|
||||||
[components (for/and ([v components]) (all-concrete? v))])]
|
|
||||||
[_ #t]))))))
|
|
||||||
|
|
||||||
(define (symbolic? val) (not (concrete? val)))
|
|
||||||
|
|
||||||
|
|
||||||
(define (term->datum val)
|
(define (term->datum val)
|
||||||
(let convert ([val val] [cache (make-hash)])
|
(let convert ([val val] [cache (make-hash)])
|
||||||
|
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
#lang racket
|
|
||||||
|
|
||||||
(provide (all-defined-out))
|
|
||||||
|
|
||||||
; The reporter is called when "interesting"
|
|
||||||
; events happen during symbolic execution; for example,
|
|
||||||
; when a merge occurs or a new term is created.
|
|
||||||
(define current-reporter
|
|
||||||
(make-parameter
|
|
||||||
void
|
|
||||||
(lambda (new-reporter)
|
|
||||||
(unless (procedure? new-reporter)
|
|
||||||
(raise-argument-error 'current-reporder "procedure?" new-reporter))
|
|
||||||
new-reporter)))
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
#lang racket
|
|
||||||
|
|
||||||
(provide (struct-out normal) (struct-out failed)
|
|
||||||
result? result-value result-state)
|
|
||||||
|
|
||||||
; Represents the result of symbolic evaluation,
|
|
||||||
; which includes an output value and a representation
|
|
||||||
; of some aspect of the symbolic state.
|
|
||||||
(struct result (value state) #:transparent)
|
|
||||||
|
|
||||||
; Represents the result of a normally terminated evaluation.
|
|
||||||
(struct normal result () #:transparent)
|
|
||||||
|
|
||||||
; Represents the result of an evaluation that resulted in
|
|
||||||
; an exn:fail? exception being raised. In this case,
|
|
||||||
; the result-value field stores the exception that was raised.
|
|
||||||
(struct failed result () #:transparent)
|
|
||||||
|
|
@ -1,39 +1,58 @@
|
||||||
#lang racket
|
#lang racket
|
||||||
|
|
||||||
(require "bool.rkt" "exn.rkt")
|
(require (only-in "type.rkt" type-cast)
|
||||||
|
"bool.rkt"
|
||||||
|
racket/performance-hint)
|
||||||
|
|
||||||
(provide argument-error arguments-error type-error contract-error index-too-large-error
|
(provide argument-error arguments-error type-error contract-error index-too-large-error
|
||||||
assert assert-some assert-|| assert-bound assert-arity-includes)
|
assert assert-some assert-|| assert-bound assert-arity-includes)
|
||||||
|
|
||||||
|
(begin-encourage-inline
|
||||||
|
|
||||||
|
(define (arguments-error name message . field-value)
|
||||||
|
(thunk (apply raise-arguments-error name message field-value)))
|
||||||
|
|
||||||
|
(define (argument-error name expected given)
|
||||||
|
(thunk (raise-argument-error name expected given)))
|
||||||
|
|
||||||
|
(define (type-error name expected given)
|
||||||
|
(argument-error name (format "~a" expected) given))
|
||||||
|
|
||||||
|
(define (contract-error name contract given)
|
||||||
|
(argument-error name (format "~a" (contract-name contract)) given))
|
||||||
|
|
||||||
|
(define (index-too-large-error who xs idx)
|
||||||
|
(arguments-error who "index is too large" "index" idx "in" xs))
|
||||||
|
)
|
||||||
|
|
||||||
(define-syntax (assert stx)
|
(define-syntax (assert stx)
|
||||||
(syntax-case stx ()
|
(syntax-case stx ()
|
||||||
[(_ expr) (syntax/loc stx ($assert expr #f))]
|
[(_ expr err-thunk) (syntax/loc stx (@assert expr err-thunk))]
|
||||||
[(_ expr msg) (syntax/loc stx ($assert expr msg))]))
|
[(_ expr) (syntax/loc stx (@assert expr #f))]))
|
||||||
|
|
||||||
(define-syntax assert-some
|
(define-syntax assert-some
|
||||||
(syntax-rules ()
|
(syntax-rules ()
|
||||||
[(_ expr #:unless size msg)
|
[(_ expr #:unless size err-thunk)
|
||||||
(let* ([val expr])
|
(let* ([val expr])
|
||||||
(unless (= size (length val))
|
(unless (= size (length val))
|
||||||
(assert (apply || (map car val)) msg))
|
(assert (apply || (map car val)) err-thunk))
|
||||||
val)]
|
val)]
|
||||||
[(_ expr #:unless size)
|
[(_ expr #:unless size)
|
||||||
(assert-some expr #:unless size #f)]
|
(assert-some expr #:unless size #f)]
|
||||||
[(_ expr msg)
|
[(_ expr err-thunk)
|
||||||
(let* ([val expr])
|
(let* ([val expr])
|
||||||
(assert (apply || (map car val)) msg)
|
(assert (apply || (map car val)) err-thunk)
|
||||||
val)]
|
val)]
|
||||||
[(_ expr)
|
[(_ expr)
|
||||||
(assert-some expr #f)]))
|
(assert-some expr #f)]))
|
||||||
|
|
||||||
(define-syntax assert-||
|
(define-syntax assert-||
|
||||||
(syntax-rules ()
|
(syntax-rules ()
|
||||||
[(_ expr #:unless size msg)
|
[(_ expr #:unless size err-thunk)
|
||||||
(let ([val expr])
|
(let ([val expr])
|
||||||
(unless (= size (length val))
|
(unless (= size (length val))
|
||||||
(assert (apply || val) msg)))]
|
(assert (apply || val) err-thunk)))]
|
||||||
[(_ expr #:unless size) (assert-|| expr #:unless size #f)]))
|
[(_ expr #:unless size) (assert-|| expr #:unless size #f)]))
|
||||||
|
|
||||||
|
|
||||||
(define-syntax assert-bound
|
(define-syntax assert-bound
|
||||||
|
|
|
||||||
|
|
@ -1,181 +0,0 @@
|
||||||
#lang racket
|
|
||||||
|
|
||||||
(require "result.rkt" "merge.rkt")
|
|
||||||
|
|
||||||
(provide with-store store! merge-stores!
|
|
||||||
location? location-base location-offset
|
|
||||||
location-ref location-set!)
|
|
||||||
|
|
||||||
; The current-store parameter contains a store that
|
|
||||||
; maps (abstract) memory locations to values. Each mapped
|
|
||||||
; location identifies a storage cell that has been the mutated
|
|
||||||
; via a store! call in the dynamic extent of a call to
|
|
||||||
; with-store. The store maps each such location to the value
|
|
||||||
; that was stored at that location before the current call
|
|
||||||
; to with-store.
|
|
||||||
(define current-store (make-parameter #f))
|
|
||||||
|
|
||||||
; A store maps abstract memory locations to their initial values.
|
|
||||||
; An abstract memory location identifies a storage cell that holds
|
|
||||||
; a single value; locations consist of a base object (e.g., a vector)
|
|
||||||
; and an offset value (e.g., the index 0) that identifies a
|
|
||||||
; storage cell within that object.
|
|
||||||
;
|
|
||||||
; A store uses a refs set, as returned by make-refs, to keep track
|
|
||||||
; of the locations that have been mutated via store! calls.
|
|
||||||
; The initial value of each mutated location is held in the store's
|
|
||||||
; vals list. This list maps locations to the values they held prior
|
|
||||||
; to the current call to with-store.
|
|
||||||
(struct store (refs [vals #:mutable]) #:transparent)
|
|
||||||
|
|
||||||
; Returns an empty store.
|
|
||||||
(define (make-store) (store (make-refs) (list)))
|
|
||||||
|
|
||||||
; Returns an empty set of base/offset pairs.
|
|
||||||
(define (make-refs) (make-hasheq))
|
|
||||||
|
|
||||||
; Adds the given base/offset pair to rs if not
|
|
||||||
; already present. Returns #t if rs changed as
|
|
||||||
; a result of this operation; otherwise returns #f.
|
|
||||||
(define (refs-add! rs base offset)
|
|
||||||
(define bits (hash-ref rs base 0))
|
|
||||||
(and (not (bitwise-bit-set? bits offset))
|
|
||||||
(hash-set! rs base (bitwise-ior bits (arithmetic-shift 1 offset)))
|
|
||||||
#t))
|
|
||||||
|
|
||||||
; Extends the store s with a mapping from the location
|
|
||||||
; (loc base offset getter setter) to its current value,
|
|
||||||
; unless s already contains a mapping for this location.
|
|
||||||
(define (store-add! s base offset getter setter)
|
|
||||||
(when (refs-add! (store-refs s) base offset)
|
|
||||||
(let ([l (location base offset getter setter)]
|
|
||||||
[vals (store-vals s)])
|
|
||||||
(set-store-vals! s (cons (cons l (location-ref l)) vals)))))
|
|
||||||
|
|
||||||
; Performs the mutation to the storage
|
|
||||||
; cell at the location (loc base offset getter setter),
|
|
||||||
; and if this cell has not been mutated before, its
|
|
||||||
; initial value is added to current-store.
|
|
||||||
; The getter and setter procedures should read / write
|
|
||||||
; the cell's value when applied to its base and offset.
|
|
||||||
(define (store! base offset val getter setter)
|
|
||||||
(let ([s (current-store)])
|
|
||||||
(when s
|
|
||||||
(store-add! s base offset getter setter)))
|
|
||||||
;(printf "store! ~a ~a ~a ~a ~a, ~a\n" base offset val getter setter (current-store))
|
|
||||||
(setter base offset val))
|
|
||||||
|
|
||||||
; Returns true if the store s is empty.
|
|
||||||
(define (store-empty? s)
|
|
||||||
(zero? (length (store-vals s))))
|
|
||||||
|
|
||||||
; Represents the location of a single mutable storage cell.
|
|
||||||
; A cell location consists of a base object (e.g., a vector)
|
|
||||||
; and an offset value (e.g., 0) that identifies a
|
|
||||||
; storage cell within that object. Locations
|
|
||||||
; also include getter and setter procedures that can be
|
|
||||||
; used to read from and write to the referenced cell.
|
|
||||||
; Two locations are equal? iff their base and offset
|
|
||||||
; are both eq? to one another.
|
|
||||||
(struct location (base offset accessor mutator)
|
|
||||||
#:transparent
|
|
||||||
#:methods gen:equal+hash
|
|
||||||
[(define (equal-proc l1 l2 rec-equal?)
|
|
||||||
(and (eq? (location-base l1) (location-base l2))
|
|
||||||
(eq? (location-offset l1) (location-offset l2))))
|
|
||||||
(define (hash-proc l rec-equal-hash)
|
|
||||||
(equal-hash-code (cons (eq-hash-code (location-base l)) (eq-hash-code (location-offset l)))))
|
|
||||||
(define (hash2-proc l rec-equal-hash2)
|
|
||||||
(equal-secondary-hash-code (cons (eq-hash-code (location-base l)) (eq-hash-code (location-offset l)))))])
|
|
||||||
|
|
||||||
; Returns the current value stored at the location l.
|
|
||||||
(define (location-ref l)
|
|
||||||
((location-accessor l) (location-base l) (location-offset l)))
|
|
||||||
|
|
||||||
; Stores the value v at the location l.
|
|
||||||
(define (location-set! l v)
|
|
||||||
(store! (location-base l) (location-offset l) v (location-accessor l) (location-mutator l)))
|
|
||||||
|
|
||||||
; Rollbacks the contents of all mutated storage cells to their initial
|
|
||||||
; values, as given in (current-store), and raises the exception e.
|
|
||||||
; The current-store is assumed to contain the values that
|
|
||||||
; mutated cells held before the current call to with-store.
|
|
||||||
; This procedure can be called only in the dynamic extent of a
|
|
||||||
; with-store call.
|
|
||||||
(define (rollback-exn! e)
|
|
||||||
;(printf "exn: ~a\n" e)
|
|
||||||
(for ([lv (store-vals (current-store))])
|
|
||||||
(match-define (cons (location base offset _ setter) init) lv)
|
|
||||||
(setter base offset init))
|
|
||||||
(raise e))
|
|
||||||
|
|
||||||
; Rollbacks the contents of all mutated storage cells to their initial
|
|
||||||
; values, as given in (current-store), and returns a list of pairs
|
|
||||||
; that maps a reference to each mutated cell to its current value.
|
|
||||||
; The current-store is assumed to contain the values that
|
|
||||||
; mutated cells held before the current call to with-store.
|
|
||||||
; This procedure can be called only in the dynamic extent of a
|
|
||||||
; with-store call.
|
|
||||||
(define (rollback-capture!)
|
|
||||||
;(printf "capture: ~a\n" (store-vals (current-store)))
|
|
||||||
(for/list ([lv (store-vals (current-store))])
|
|
||||||
(match-define (cons (location base offset getter setter) init) lv)
|
|
||||||
(define fin (getter base offset))
|
|
||||||
(setter base offset init)
|
|
||||||
(cons (car lv) fin)))
|
|
||||||
|
|
||||||
; The with-store form takes as input an expression, evaluates it,
|
|
||||||
; and reverts each mutated memory location to its pre-state
|
|
||||||
; (i.e., the value it held before the call to with-store).
|
|
||||||
;
|
|
||||||
; If the evaluation of the body terminates normally, (with-store body)
|
|
||||||
; outputs a result (normal v s) where v is the value computed by the body,
|
|
||||||
; and s is an association list that maps each mutated location? to its
|
|
||||||
; post-state (i.e., the value it held after the evaluation of the body).
|
|
||||||
; In essence, evaluating the body in the current environment has the
|
|
||||||
; same effect on memory as evaluating (with-store body) and then setting
|
|
||||||
; the returned memory locations to their post-state value.
|
|
||||||
;
|
|
||||||
; If the evaluation of the body terminates abnormally with an exn:fail?
|
|
||||||
; exception, with-store reverts all mutated locations to their pre-state
|
|
||||||
; and re-raises the same exception.
|
|
||||||
(define-syntax-rule (with-store body)
|
|
||||||
(parameterize ([current-store (make-store)])
|
|
||||||
(with-handlers ([exn:fail? rollback-exn!])
|
|
||||||
(let ([out body])
|
|
||||||
(normal out (rollback-capture!))))))
|
|
||||||
|
|
||||||
; Takes as input a list of n guards and a list of n stores, where
|
|
||||||
; each store is a list of location/value pairs. For each location l
|
|
||||||
; occurring in the stores, merge-store mutates l to contain the value
|
|
||||||
; m = (merge* ... (cons gi vi) ...), where gi = guards[i] and
|
|
||||||
; vi = stores[i][l] if stores[i] has a binding for l; otherwise,
|
|
||||||
; vi = (location-ref l). The procedure assumes that no store contains a
|
|
||||||
; duplicate binding for any location.
|
|
||||||
;
|
|
||||||
; This store merging procedure is correct under the assumption that
|
|
||||||
; (1) the guards are disjoint under all models (i.e., at most one
|
|
||||||
; is ever true), and (2) the verification conditions force at least
|
|
||||||
; one guard to be true under all models that satisfy both the
|
|
||||||
; asserts and the assumes.
|
|
||||||
(define (merge-stores! guards stores)
|
|
||||||
(match stores
|
|
||||||
[(list (list) ...) (void)] ; Nothing to merge.
|
|
||||||
[(list s) (for ([lv s]) ; If given only one store, just apply its effects
|
|
||||||
(location-set! (car lv) (cdr lv)))] ; since its guard must be true under the current spec.
|
|
||||||
[_ (define hash-stores (map make-hash stores))
|
|
||||||
(for ([lv (remove-duplicates (apply append stores) equal? #:key car)])
|
|
||||||
(define loc (car lv))
|
|
||||||
(define val (location-ref loc))
|
|
||||||
(location-set! loc
|
|
||||||
(apply merge*
|
|
||||||
(for/list ([g guards] [hs hash-stores])
|
|
||||||
(cons g (if (hash-has-key? hs loc)
|
|
||||||
(hash-ref hs loc)
|
|
||||||
val))))))]))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,38 +1,31 @@
|
||||||
#lang racket
|
#lang racket
|
||||||
|
|
||||||
(require racket/syntax (for-syntax racket racket/syntax syntax/parse)
|
(require racket/syntax (for-syntax racket racket/syntax) racket/generic "type.rkt")
|
||||||
racket/generic syntax/parse
|
|
||||||
"type.rkt" "reporter.rkt")
|
|
||||||
|
|
||||||
(provide
|
(provide
|
||||||
terms terms-count terms-ref with-terms clear-terms! gc-terms!
|
term-cache clear-terms!
|
||||||
term? constant? expression?
|
term? constant? expression?
|
||||||
(rename-out [a-term term] [an-expression expression] [a-constant constant] [term-ord term-id])
|
(rename-out [a-term term] [an-expression expression] [a-constant constant])
|
||||||
term-type term<? sublist? @app
|
term-type term<? sublist? @app
|
||||||
define-operator operator? operator-unsafe
|
define-operator operator? operator-unsafe
|
||||||
(all-from-out "type.rkt"))
|
(all-from-out "type.rkt"))
|
||||||
|
|
||||||
#|-----------------------------------------------------------------------------------|#
|
#|-----------------------------------------------------------------------------------|#
|
||||||
; The current-terms cache stores terms for the purposes of partial cannonicalization.
|
; Term cache stores terms for the purposes of partial cannonicalization.
|
||||||
; That is, it ensures that no syntactically identical terms are created.
|
; That is, it ensures that no syntactically identical terms are created.
|
||||||
; The current-index parameter is used to assign unique IDs (creation timestamps) to terms.
|
; It also assigns unique IDs (creation timestamps) to terms. These IDs
|
||||||
; These IDs are never reused, and they are used to impose an ordering on the children
|
; are never reused, and they are used to impose an ordering on the children
|
||||||
; of expressions with commutative operators.
|
; of expressions with commutative operators.
|
||||||
#|-----------------------------------------------------------------------------------|#
|
#|-----------------------------------------------------------------------------------|#
|
||||||
|
(define term-cache (make-parameter (make-hash)))
|
||||||
|
(define term-count (make-parameter 0))
|
||||||
|
|
||||||
;; Initialize with #f so that the hash table cooperates with garbage collector.
|
; Clears the entire term-cache if invoked with #f (default), or
|
||||||
;; See #247
|
|
||||||
(define current-terms (make-parameter #f))
|
|
||||||
(current-terms (make-hash))
|
|
||||||
|
|
||||||
(define current-index (make-parameter 0))
|
|
||||||
|
|
||||||
; Clears the entire term cache if invoked with #f (default), or
|
|
||||||
; it clears all terms reachable from the given set of leaf terms.
|
; it clears all terms reachable from the given set of leaf terms.
|
||||||
(define (clear-terms! [terms #f])
|
(define (clear-terms! [terms #f])
|
||||||
(if (false? terms)
|
(if (false? terms)
|
||||||
(hash-clear! (current-terms))
|
(hash-clear! (term-cache))
|
||||||
(let ([cache (current-terms)]
|
(let ([cache (term-cache)]
|
||||||
[evicted (list->mutable-set terms)])
|
[evicted (list->mutable-set terms)])
|
||||||
(for ([t terms])
|
(for ([t terms])
|
||||||
(hash-remove! cache (term-val t)))
|
(hash-remove! cache (term-val t)))
|
||||||
|
|
@ -46,70 +39,12 @@
|
||||||
(set-add! evicted t))
|
(set-add! evicted t))
|
||||||
(loop))))))
|
(loop))))))
|
||||||
|
|
||||||
; Sets the current term cache to a garbage-collected (weak) hash.
|
|
||||||
; The setting preserves all reachable terms from (current-terms).
|
|
||||||
(define (gc-terms!)
|
|
||||||
(unless (hash-weak? (current-terms)) ; Already a weak hash.
|
|
||||||
(define cache
|
|
||||||
(impersonate-hash
|
|
||||||
(make-weak-hash)
|
|
||||||
(lambda (h k)
|
|
||||||
(values k (lambda (h k e) (ephemeron-value e #f))))
|
|
||||||
(lambda (h k v)
|
|
||||||
(values k (make-ephemeron k v)))
|
|
||||||
(lambda (h k) k)
|
|
||||||
(lambda (h k) k)
|
|
||||||
hash-clear!))
|
|
||||||
(for ([(k v) (current-terms)])
|
|
||||||
(hash-set! cache k v))
|
|
||||||
(current-terms cache)))
|
|
||||||
|
|
||||||
; Returns the term from current-terms that has the given contents. If
|
|
||||||
; no such term exists, failure-result is returned, unless it is a procedure.
|
|
||||||
; If failure-result is a procedure, it is called and its result is returned instead.
|
|
||||||
(define (terms-ref contents [failure-result (lambda () (error 'terms-ref "no term for ~a" contents))])
|
|
||||||
(hash-ref (current-terms) contents failure-result))
|
|
||||||
|
|
||||||
; Returns a list of all terms in the current-term scache, in an unspecified order.
|
|
||||||
(define (terms)
|
|
||||||
(hash-values (current-terms)))
|
|
||||||
|
|
||||||
; Returns the size of the current-terms cache.
|
|
||||||
(define (terms-count)
|
|
||||||
(hash-count (current-terms)))
|
|
||||||
|
|
||||||
; Evaluates expr with (terms) set to terms-expr, returns the result, and
|
|
||||||
; restores (terms) to its old value. If terms-expr is not given, it defaults to
|
|
||||||
; (terms), so (with-terms expr) is equivalent to (with-terms (terms) expr).
|
|
||||||
(define-syntax (with-terms stx)
|
|
||||||
;; Parameterize with #f so that the hash table cooperates with garbage collector.
|
|
||||||
;; See #247
|
|
||||||
(syntax-parse stx
|
|
||||||
[(_ expr)
|
|
||||||
#'(let ([orig-terms (current-terms)])
|
|
||||||
(parameterize ([current-terms #f])
|
|
||||||
(current-terms (hash-copy orig-terms))
|
|
||||||
expr))]
|
|
||||||
[(_ terms-expr expr)
|
|
||||||
#'(let ([orig-terms (current-terms)])
|
|
||||||
(parameterize ([current-terms #f])
|
|
||||||
(current-terms (hash-copy-clear orig-terms))
|
|
||||||
(let ([ts terms-expr]
|
|
||||||
[cache (current-terms)])
|
|
||||||
(for ([t ts])
|
|
||||||
(hash-set! cache (term-val t) t))
|
|
||||||
expr)))]))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#|-----------------------------------------------------------------------------------|#
|
#|-----------------------------------------------------------------------------------|#
|
||||||
; The term structure defines a symbolic value, which can be a variable or an expression.
|
; The term structure defines a symbolic value, which can be a variable or an expression.
|
||||||
; The val field of a constant is its unique identifier, and it can be anything. The val
|
; The val field of a constant is its unique identifier, and it can be anything. The val
|
||||||
; field of an expression is a list, in which the first argument is always a function.
|
; field of an expression is a list, in which the first argument is always a function.
|
||||||
; That function can be interpreted (that is, an operator), or uninterpreted (that is,
|
; That function can be interpreted (that is, an operator), or uninterpreted (that is,
|
||||||
; its interpretation is determined by the solver). Terms are totally ordered and a
|
; its interpretation is determined by the solver).
|
||||||
; subterm is guaranteed to be term<? than its parent.
|
|
||||||
#|-----------------------------------------------------------------------------------|#
|
#|-----------------------------------------------------------------------------------|#
|
||||||
(struct term
|
(struct term
|
||||||
(val ; (or/c any/c (cons/c function? (non-empty-listof any/c)))
|
(val ; (or/c any/c (cons/c function? (non-empty-listof any/c)))
|
||||||
|
|
@ -117,7 +52,6 @@
|
||||||
ord) ; integer?
|
ord) ; integer?
|
||||||
#:methods gen:typed
|
#:methods gen:typed
|
||||||
[(define (get-type v) (term-type v))]
|
[(define (get-type v) (term-type v))]
|
||||||
#:property prop:custom-print-quotable 'never
|
|
||||||
#:methods gen:custom-write
|
#:methods gen:custom-write
|
||||||
[(define (write-proc self port mode)
|
[(define (write-proc self port mode)
|
||||||
(fprintf port "~a" (term->string self)))])
|
(fprintf port "~a" (term->string self)))])
|
||||||
|
|
@ -132,21 +66,13 @@
|
||||||
(define (term<? s1 s2) (< (term-ord s1) (term-ord s2)))
|
(define (term<? s1 s2) (< (term-ord s1) (term-ord s2)))
|
||||||
|
|
||||||
(define-syntax-rule (make-term term-constructor args type rest ...)
|
(define-syntax-rule (make-term term-constructor args type rest ...)
|
||||||
(let ([val args]
|
(let ([val args])
|
||||||
[ty type])
|
(or (hash-ref (term-cache) val #f)
|
||||||
(define cached (hash-ref (current-terms) val #f))
|
(let* ([ord (term-count)]
|
||||||
(cond
|
[out (term-constructor val type ord rest ...)])
|
||||||
[cached
|
(term-count (add1 ord))
|
||||||
(unless (equal? (term-type cached) ty)
|
(hash-set! (term-cache) val out)
|
||||||
(error 'define-symbolic "type should remain unchanged"))
|
out))))
|
||||||
cached]
|
|
||||||
[else
|
|
||||||
(define ord (current-index))
|
|
||||||
(define out (term-constructor val ty ord rest ...))
|
|
||||||
(current-index (add1 ord))
|
|
||||||
((current-reporter) 'new-term out)
|
|
||||||
(hash-set! (current-terms) val out)
|
|
||||||
out])))
|
|
||||||
|
|
||||||
(define (make-const id t)
|
(define (make-const id t)
|
||||||
(unless (and (type? t) (solvable? t))
|
(unless (and (type? t) (solvable? t))
|
||||||
|
|
@ -197,8 +123,6 @@
|
||||||
(struct operator (identifier range safe unsafe)
|
(struct operator (identifier range safe unsafe)
|
||||||
#:property prop:procedure
|
#:property prop:procedure
|
||||||
(struct-field-index safe)
|
(struct-field-index safe)
|
||||||
#:property prop:object-name
|
|
||||||
(struct-field-index identifier)
|
|
||||||
#:methods gen:custom-write
|
#:methods gen:custom-write
|
||||||
[(define (write-proc self port mode)
|
[(define (write-proc self port mode)
|
||||||
(fprintf port "~a" (id->string (operator-identifier self))))])
|
(fprintf port "~a" (id->string (operator-identifier self))))])
|
||||||
|
|
|
||||||
|
|
@ -43,8 +43,8 @@
|
||||||
[type-cast type val [caller]] ; (-> type? any/c symbol? any/c)
|
[type-cast type val [caller]] ; (-> type? any/c symbol? any/c)
|
||||||
[type-name type] ; (-> type? symbol?)
|
[type-name type] ; (-> type? symbol?)
|
||||||
[type-applicable? type] ; (-> type? boolean?)
|
[type-applicable? type] ; (-> type? boolean?)
|
||||||
[type-eq? type u v] ; (-> type? any/c any/c @boolean?)
|
[type-eq? type u v] ; (-> type? (-> any/c any/c @boolean?)))
|
||||||
[type-equal? type u v] ; (-> type? any/c any/c @boolean?)
|
[type-equal? type u v] ; (-> type? (-> any/c any/c @boolean?)))
|
||||||
[type-compress type force? ps] ; (-> type? (listof (cons @boolean? any/c)) (listof (cons @boolean? any/c)))
|
[type-compress type force? ps] ; (-> type? (listof (cons @boolean? any/c)) (listof (cons @boolean? any/c)))
|
||||||
[type-construct type vals] ; (-> type? (listof any/c) any/c)
|
[type-construct type vals] ; (-> type? (listof any/c) any/c)
|
||||||
[type-deconstruct type val]) ; (-> type? any/c (listof any/c))
|
[type-deconstruct type val]) ; (-> type? any/c (listof any/c))
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#lang racket
|
#lang racket
|
||||||
|
|
||||||
(require "term.rkt" "reporter.rkt")
|
(require "term.rkt")
|
||||||
|
|
||||||
(provide union? (rename-out [a-union union])
|
(provide union? (rename-out [a-union union])
|
||||||
union-contents union-type union-guards union-values union-filter
|
union-contents union-type union-guards union-values union-filter
|
||||||
|
|
@ -20,24 +20,25 @@
|
||||||
[(define (get-type self) (union-type self))]
|
[(define (get-type self) (union-type self))]
|
||||||
#:methods gen:custom-write
|
#:methods gen:custom-write
|
||||||
[(define (write-proc self port mode)
|
[(define (write-proc self port mode)
|
||||||
(fprintf port "(union")
|
(fprintf port "{")
|
||||||
(case mode
|
(case mode
|
||||||
[(#t #f)
|
[(#t #f)
|
||||||
(fprintf port " #:size ~a #:hash ~a" (length (union-contents self)) (equal-hash-code self))]
|
(fprintf port "~a:~a" (equal-hash-code self) (length (union-contents self)))]
|
||||||
[else
|
[else
|
||||||
(let ([vs (union-contents self)])
|
(let ([vs (union-contents self)])
|
||||||
(unless (null? vs)
|
(unless (null? vs)
|
||||||
(parameterize ([error-print-width (max 4 (quotient (error-print-width) (* 2 (length vs))))])
|
(parameterize ([error-print-width (max 4 (quotient (error-print-width) (* 2 (length vs))))])
|
||||||
(for ([v vs])
|
(fprintf-entry port (car vs) mode)
|
||||||
|
(for ([v (cdr vs)])
|
||||||
(fprintf port " ")
|
(fprintf port " ")
|
||||||
(fprintf-entry port v mode)))))])
|
(fprintf-entry port v mode)))))])
|
||||||
(fprintf port ")"))])
|
(fprintf port "}"))])
|
||||||
|
|
||||||
(define (fprintf-entry port p mode)
|
(define (fprintf-entry port p mode)
|
||||||
(fprintf port "[")
|
(fprintf port "[")
|
||||||
(fprintf port "~a" (car p))
|
(print (car p) port mode)
|
||||||
(fprintf port " ")
|
(fprintf port " ")
|
||||||
(fprintf port "~a" (cdr p))
|
(print (cdr p) port mode)
|
||||||
(fprintf port "]"))
|
(fprintf port "]"))
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -50,9 +51,17 @@
|
||||||
#:property prop:procedure [struct-field-index procedure])
|
#:property prop:procedure [struct-field-index procedure])
|
||||||
|
|
||||||
(define (make-union . vs)
|
(define (make-union . vs)
|
||||||
((current-reporter) 'new-union (length vs))
|
|
||||||
(match vs
|
(match vs
|
||||||
[(list) nil]
|
[(list) nil]
|
||||||
|
[(list (and c1 (cons g1 v1) (and c2 (cons g2 v2))))
|
||||||
|
(let ([vs (if (term<? g1 g2) vs (list c2 c1))]
|
||||||
|
[t (type-of v1 v2)])
|
||||||
|
(cond [(procedure? v1)
|
||||||
|
(λunion vs t (type-compress (lifted-type procedure?) #t (if (procedure? v2) vs (list c1))))]
|
||||||
|
[(procedure? v2)
|
||||||
|
(λunion vs t (type-compress (lifted-type procedure?) #t (list c2)))]
|
||||||
|
[else
|
||||||
|
(union vs t)]))]
|
||||||
[_
|
[_
|
||||||
(let ([vs (sort vs term<? #:key car)]
|
(let ([vs (sort vs term<? #:key car)]
|
||||||
[t (apply type-of (map cdr vs))])
|
[t (apply type-of (map cdr vs))])
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,27 @@
|
||||||
#lang racket
|
#lang racket
|
||||||
|
|
||||||
(require "../core/eval.rkt" "../core/store.rkt" "../core/result.rkt"
|
(require "../core/effects.rkt"
|
||||||
"../core/term.rkt" "../core/equality.rkt"
|
"../core/term.rkt" "../core/equality.rkt"
|
||||||
"../core/merge.rkt" "../core/bool.rkt")
|
"../core/merge.rkt" "../core/bool.rkt")
|
||||||
|
|
||||||
(provide @if @and @or @not @nand @nor @xor @implies
|
(provide @if @and @or @not @nand @nor @xor @implies
|
||||||
@unless @when @cond @case else)
|
@unless @when @cond @case else)
|
||||||
|
|
||||||
|
; Symbolic conditions are handled by speculatively executing both branches,
|
||||||
|
; and then merging their results and updates to state (if any). When a branch is
|
||||||
|
; executed speculatively, its state mutations are captured and then undone.
|
||||||
|
; The result of the capture is a closure that can be used with a merging
|
||||||
|
; procedure to selectively re-apply the updates. If an error is thrown
|
||||||
|
; during speculation, all updates are undone, but they are not captured
|
||||||
|
; (since the branch is infeasible). After both branches have been speculatively
|
||||||
|
; executed, their results and updates to state are merged using the merge function.
|
||||||
|
;
|
||||||
|
; Speculative execution of either branch is guarded by the path condition, stored
|
||||||
|
; in the pc parameter. Parameterizing pc with a new value coinjoins that
|
||||||
|
; value with the current path condition. If the result of the conjunction is false,
|
||||||
|
; indicating that the branch is infeasible, an error is thrown, and the branch is
|
||||||
|
; not executed. The error is captured by the speculate form and later handled by
|
||||||
|
; the merge function.
|
||||||
(define-syntax (@if stx)
|
(define-syntax (@if stx)
|
||||||
(syntax-case stx ()
|
(syntax-case stx ()
|
||||||
[(_ test-expr then-expr else-expr)
|
[(_ test-expr then-expr else-expr)
|
||||||
|
|
@ -16,10 +31,28 @@
|
||||||
(thunk else-expr)))]))
|
(thunk else-expr)))]))
|
||||||
|
|
||||||
(define (branch-and-merge test-expr then-branch else-branch)
|
(define (branch-and-merge test-expr then-branch else-branch)
|
||||||
(define test (@true? test-expr))
|
(define test (! (@false? test-expr)))
|
||||||
(cond [(eq? test #t) (then-branch)]
|
(cond [(eq? test #t) (then-branch)]
|
||||||
[(eq? test #f) (else-branch)]
|
[(eq? test #f) (else-branch)]
|
||||||
[else (eval-guarded! (list test (! test)) (list then-branch else-branch))]))
|
[else
|
||||||
|
(let-values ([(then-val then-state) (speculate (parameterize ([pc test]) (then-branch)))]
|
||||||
|
[(else-val else-state) (speculate (parameterize ([pc (! test)]) (else-branch)))])
|
||||||
|
(cond [(and then-state else-state) ; both branches feasible
|
||||||
|
(then-state (lambda (pre post-then) (merge test post-then pre)))
|
||||||
|
(else-state (lambda (post-then post-else) (merge test post-then post-else)))
|
||||||
|
(merge test then-val else-val)]
|
||||||
|
[then-state ; only then branch feasible
|
||||||
|
(@assert test "both branches infeasible")
|
||||||
|
(then-state select-post)
|
||||||
|
then-val]
|
||||||
|
[else-state ; only else branch feasible
|
||||||
|
(@assert (! test) "both branches infeasible")
|
||||||
|
(else-state select-post)
|
||||||
|
else-val]
|
||||||
|
[else ; neither branch feasible
|
||||||
|
(@assert #f "both branches infeasible")]))]))
|
||||||
|
|
||||||
|
(define (select-post pre post) post)
|
||||||
|
|
||||||
(define-syntax (@and stx)
|
(define-syntax (@and stx)
|
||||||
(syntax-case stx ()
|
(syntax-case stx ()
|
||||||
|
|
@ -49,7 +82,7 @@
|
||||||
[(_ expr ...) (syntax/loc stx (@not (@and expr ...)))]))
|
[(_ expr ...) (syntax/loc stx (@not (@and expr ...)))]))
|
||||||
|
|
||||||
(define (@xor a b)
|
(define (@xor a b)
|
||||||
(@if a (@if b #f a) b))
|
(merge a (merge b #f a) b))
|
||||||
|
|
||||||
(define-syntax (@unless stx)
|
(define-syntax (@unless stx)
|
||||||
(syntax-case stx ()
|
(syntax-case stx ()
|
||||||
|
|
@ -70,16 +103,22 @@
|
||||||
|
|
||||||
(define-syntax (@case stx)
|
(define-syntax (@case stx)
|
||||||
(syntax-case stx (else)
|
(syntax-case stx (else)
|
||||||
|
[(_ expr) (syntax/loc stx (@case expr [else (void)]))]
|
||||||
|
[(_ expr [else else-expr ...]) (syntax/loc stx (begin expr else-expr ...))]
|
||||||
[(_ expr
|
[(_ expr
|
||||||
|
[(then-val0 ...) then-expr0 ...]
|
||||||
[(then-val ...) then-expr ...] ...
|
[(then-val ...) then-expr ...] ...
|
||||||
[else else-expr ...])
|
[else else-expr ...])
|
||||||
(syntax/loc stx
|
(syntax/loc stx
|
||||||
(let ([tmp expr])
|
(let ([tmp expr])
|
||||||
(@cond [(@or (@equal? tmp (quote then-val)) ...) then-expr ...] ...
|
(@cond [(@or (@equal? tmp (quote then-val0)) ...) then-expr0 ...]
|
||||||
[else else-expr ...])))]
|
[(@or (@equal? tmp (quote then-val)) ...) then-expr ...] ...
|
||||||
|
[else else-expr ...])))]
|
||||||
[(_ expr
|
[(_ expr
|
||||||
|
[(then-val0 ...) then-expr0 ...]
|
||||||
[(then-val ...) then-expr ...] ...)
|
[(then-val ...) then-expr ...] ...)
|
||||||
(syntax/loc stx
|
(syntax/loc stx
|
||||||
(@case expr
|
(@case expr
|
||||||
[(then-val ...) then-expr ...] ...
|
[(then-val0 ...) then-expr0 ...]
|
||||||
[else (void)]))]))
|
[(then-val ...) then-expr ...] ...
|
||||||
|
[else (void)]))]))
|
||||||
|
|
|
||||||
|
|
@ -1,48 +1,89 @@
|
||||||
#lang racket
|
#lang racket
|
||||||
|
|
||||||
(require syntax/parse (for-syntax syntax/parse racket)
|
(require (for-syntax racket)
|
||||||
"../core/term.rkt")
|
"../util/array.rkt" "../core/term.rkt" "../core/function.rkt" "state.rkt")
|
||||||
|
|
||||||
(provide define-symbolic define-symbolic*)
|
(provide define-symbolic define-symbolic*)
|
||||||
|
|
||||||
(define-for-syntax (module-or-top? . args)
|
#|--------------define forms--------------|#
|
||||||
(case (syntax-local-context)
|
|
||||||
[(module top-level) #t]
|
|
||||||
[else #f]))
|
|
||||||
|
|
||||||
(define-for-syntax (static? k)
|
|
||||||
(with-handlers ([exn:fail? module-or-top?])
|
|
||||||
(natural? (eval k))))
|
|
||||||
|
|
||||||
(define-syntax (define-symbolic stx)
|
(define-syntax (define-symbolic stx)
|
||||||
(syntax-parse stx
|
(syntax-case stx (->)
|
||||||
[(_ var:id type)
|
[(_ fun (-> dom0 dom ... ran))
|
||||||
#'(define var (constant #'var type))]
|
(syntax/loc stx (define-symbolic fun (function (list dom0 dom ...) ran)))]
|
||||||
[(_ var:id type #:length k)
|
[(_ var type)
|
||||||
#:declare k (expr/c #'natural? #:name "length argument")
|
(identifier? #'var)
|
||||||
#:fail-unless (static? #'k) "expected a natural? for #:length"
|
(syntax/loc stx (define var (constant #'var type)))]
|
||||||
#'(define var
|
[(_ var type [ k ... ])
|
||||||
(for/list ([i k.c])
|
(and (identifier? #'var) (implies (identifier? #'type) (identifier-binding #'type)))
|
||||||
(constant (list #'var i) type)))]
|
(define-array stx #'var #'type #'(k ...))]
|
||||||
[(_ var:id ...+ type)
|
[(_ v ... type)
|
||||||
#'(begin (define-symbolic var type) ...)]))
|
(andmap identifier? (syntax->list #'(v ...)))
|
||||||
|
(syntax/loc stx (define-values (v ...) (values (constant #'v type) ...)))]))
|
||||||
(define current-index (make-parameter 0))
|
|
||||||
|
|
||||||
(define (index!)
|
|
||||||
(define idx (current-index))
|
|
||||||
(current-index (add1 idx))
|
|
||||||
idx)
|
|
||||||
|
|
||||||
(define-syntax (define-symbolic* stx)
|
(define-syntax (define-symbolic* stx)
|
||||||
(syntax-parse stx
|
(syntax-case stx (->)
|
||||||
[(_ var:id type)
|
[(_ fun (-> dom0 dom ... ran))
|
||||||
#'(define var (constant (list #'var (index!)) type))]
|
(syntax/loc stx (define-symbolic* fun (function (list dom0 dom ...) ran)))]
|
||||||
[(_ var:id type #:length k)
|
[(_ [var oracle] type)
|
||||||
#:declare k (expr/c #'natural? #:name "length argument")
|
(identifier? #'var)
|
||||||
#'(define var
|
(syntax/loc stx (define var (constant (list #'var (oracle #'var)) type)))]
|
||||||
(for/list ([i k.c])
|
[(_ var type)
|
||||||
(define-symbolic* var type)
|
(identifier? #'var)
|
||||||
var))]
|
(syntax/loc stx (define-symbolic* [var (current-oracle)] type))]
|
||||||
[(_ var:id ...+ type)
|
[(_ var type [ k ... ])
|
||||||
#'(begin (define-symbolic* var type) ...)]))
|
(and (identifier? #'var) (implies (identifier? #'type) (identifier-binding #'type)))
|
||||||
|
(syntax/loc stx (define var (reshape (list k ...) (for/list ([i (in-range (* k ...))])
|
||||||
|
(define-symbolic* var type)
|
||||||
|
var))))]
|
||||||
|
[(_ v0 v ... type)
|
||||||
|
(and (identifier? #'v0) (andmap identifier? (syntax->list #'(v ...))))
|
||||||
|
(syntax/loc stx (begin (define-symbolic* v0 type) (define-symbolic* v type) ...))]
|
||||||
|
))
|
||||||
|
|
||||||
|
#|--------------helper functions--------------|#
|
||||||
|
|
||||||
|
(module util racket
|
||||||
|
(require racket/syntax)
|
||||||
|
(provide var-ids indices)
|
||||||
|
|
||||||
|
(define (var-ids id-stx dim-spec [separator '@])
|
||||||
|
(for/list ([idx (apply indices (dims dim-spec))])
|
||||||
|
(format-id id-stx "~a~a~a" id-stx separator idx #:source id-stx)))
|
||||||
|
|
||||||
|
(define (dims spec)
|
||||||
|
(begin0 spec
|
||||||
|
(for ([dim spec])
|
||||||
|
(unless (and (integer? dim) (>= dim 0))
|
||||||
|
(error 'define-symbolic "expected a non-negative integer, given ~a" dim)))))
|
||||||
|
|
||||||
|
(define (indices . k)
|
||||||
|
(cond [(null? k) k]
|
||||||
|
[(null? (cdr k)) (build-list (car k) (lambda (i) (format-symbol "~a" i)))]
|
||||||
|
[else (let ([car-idx (indices (car k))]
|
||||||
|
[cdr-idx (apply indices (cdr k))])
|
||||||
|
(append-map (lambda (i)
|
||||||
|
(map (lambda (j)
|
||||||
|
(format-symbol "~a:~a" i j))
|
||||||
|
cdr-idx))
|
||||||
|
car-idx))])))
|
||||||
|
|
||||||
|
(require (for-syntax 'util) 'util)
|
||||||
|
|
||||||
|
(define-for-syntax (define-array stx var type dims)
|
||||||
|
(with-syntax ([var var]
|
||||||
|
[type type]
|
||||||
|
[(k ...) dims])
|
||||||
|
(with-handlers ([exn:fail?
|
||||||
|
(lambda (e)
|
||||||
|
(case (syntax-local-context)
|
||||||
|
[(module top-level)
|
||||||
|
(quasisyntax/loc stx
|
||||||
|
(define var (reshape (list k ...)
|
||||||
|
(map (lambda (id) (constant id type))
|
||||||
|
(var-ids #'var (list k ...))))))]
|
||||||
|
[else (raise e)]))])
|
||||||
|
(with-syntax ([(v ...) (var-ids #'var (eval #'(list k ...)))])
|
||||||
|
(quasisyntax/loc stx
|
||||||
|
(define var (reshape (list k ...) (list (constant #'v type) ...))))))))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,9 @@
|
||||||
#lang racket
|
#lang racket
|
||||||
|
|
||||||
(require (for-syntax racket/dict syntax/parse syntax/parse/define syntax/id-table (only-in racket pretty-print)
|
(require (for-syntax racket/dict syntax/parse syntax/id-table (only-in racket pretty-print)
|
||||||
(only-in "../core/lift.rkt" drop@))
|
(only-in "../core/lift.rkt" drop@))
|
||||||
racket/require racket/undefined
|
racket/require racket/undefined
|
||||||
(filtered-in drop@ "../adt/box.rkt")
|
(filtered-in drop@ "../adt/box.rkt")
|
||||||
(for-syntax (only-in "../struct/struct.rkt" [struct @struct]) (only-in "../struct/generics.rkt" @define-generics))
|
|
||||||
(only-in "../struct/struct.rkt" [struct @struct]) (only-in "../struct/generics.rkt" @define-generics)
|
|
||||||
(only-in racket/splicing splicing-let splicing-let-values))
|
(only-in racket/splicing splicing-let splicing-let-values))
|
||||||
|
|
||||||
(provide @#%module-begin @#%top-interaction
|
(provide @#%module-begin @#%top-interaction
|
||||||
|
|
@ -31,27 +29,11 @@
|
||||||
|
|
||||||
(define-syntax (@#%top-interaction stx)
|
(define-syntax (@#%top-interaction stx)
|
||||||
(syntax-case stx ()
|
(syntax-case stx ()
|
||||||
[(_ . (id rest ...))
|
|
||||||
(and (identifier? #'id) (free-identifier=? #'id #'@define-generics))
|
|
||||||
(syntax/loc stx (id rest ...))]
|
|
||||||
[(_ . (id e ...))
|
|
||||||
(and (identifier? #'id) (free-identifier=? #'id #'begin))
|
|
||||||
(syntax/loc stx (begin (@#%top-interaction . e) ...))]
|
|
||||||
[(_ . form)
|
[(_ . form)
|
||||||
(let* ([core (local-expand #'form 'top-level (list))]
|
(let* ([core (local-expand #'form 'top-level '())]
|
||||||
[vars (find-mutated-vars core #f)]
|
[vars (find-mutated-vars core #t)]
|
||||||
[top-vars (for/list ([(var mutated?) (in-dict vars)]
|
[transformed (box-mutated-vars core vars)])
|
||||||
#:unless (or (not mutated?)
|
|
||||||
(equal? 'lexical (identifier-binding var))))
|
|
||||||
var)]
|
|
||||||
[transformed
|
|
||||||
(begin (unless (null? top-vars)
|
|
||||||
(raise-syntax-error
|
|
||||||
'set!
|
|
||||||
"assignment disallowed;\n cannot set top-level variables" #'form #f top-vars))
|
|
||||||
(box-mutated-vars core vars))])
|
|
||||||
;(printf "core:\n~a\n" core)
|
;(printf "core:\n~a\n" core)
|
||||||
;(printf "mutated vars\n~a\n" (dict->list vars))
|
|
||||||
;(printf "transformed: ~a\n" transformed)
|
;(printf "transformed: ~a\n" transformed)
|
||||||
transformed)]))
|
transformed)]))
|
||||||
|
|
||||||
|
|
@ -99,14 +81,7 @@
|
||||||
[(var:id ...) (syntax->list stx)]
|
[(var:id ...) (syntax->list stx)]
|
||||||
[(var:id ... . rest:id) (syntax->list #'(var ... rest))]))
|
[(var:id ... . rest:id) (syntax->list #'(var ... rest))]))
|
||||||
|
|
||||||
(begin-for-syntax
|
|
||||||
(define-simple-macro (quasisyntax* orig-stx new-stx)
|
|
||||||
(let ([orig-stx* orig-stx]
|
|
||||||
[new-stx* (quasisyntax new-stx)])
|
|
||||||
(datum->syntax new-stx* (syntax-e new-stx*) orig-stx* orig-stx*))))
|
|
||||||
|
|
||||||
(define-for-syntax (box-mutated-vars form tbl)
|
(define-for-syntax (box-mutated-vars form tbl)
|
||||||
(define varref-tbl (make-free-id-table))
|
|
||||||
(define (mutated? id) (free-id-table-ref tbl id #f))
|
(define (mutated? id) (free-id-table-ref tbl id #f))
|
||||||
(define (any-mutated? ids) (for/or ([id ids]) (mutated? id)))
|
(define (any-mutated? ids) (for/or ([id ids]) (mutated? id)))
|
||||||
(define (bmv/list lstx)
|
(define (bmv/list lstx)
|
||||||
|
|
@ -116,7 +91,7 @@
|
||||||
|
|
||||||
(define (bmv/rest stx lit lstx)
|
(define (bmv/rest stx lit lstx)
|
||||||
(let-values ([(pure? forms) (bmv/list lstx)])
|
(let-values ([(pure? forms) (bmv/list lstx)])
|
||||||
(if pure? stx (quasisyntax* stx (#,lit #,@forms)))))
|
(if pure? stx (quasisyntax/loc stx (#,lit #,@forms)))))
|
||||||
|
|
||||||
(define (bmv/proc-body formals rest)
|
(define (bmv/proc-body formals rest)
|
||||||
(let-values ([(pure? fs) (bmv/list rest)]
|
(let-values ([(pure? fs) (bmv/list rest)]
|
||||||
|
|
@ -133,40 +108,33 @@
|
||||||
(syntax-disarm stx orig-insp)
|
(syntax-disarm stx orig-insp)
|
||||||
#:literal-sets (kernel-literals)
|
#:literal-sets (kernel-literals)
|
||||||
[var:id
|
[var:id
|
||||||
(cond [(and (mutated? #'var) (lexical? #'var)) (quasisyntax* stx (unbox var))]
|
(cond [(and (mutated? #'var) (lexical? #'var)) (syntax/loc stx (unbox var))]
|
||||||
[else #'var])]
|
[else #'var])]
|
||||||
[(set! var expr)
|
[(set! var expr)
|
||||||
(let ([e (bmv #'expr)])
|
(let ([e (bmv #'expr)])
|
||||||
(cond [(lexical? #'var) (quasisyntax* stx (set-box! var #,e))]
|
(cond [(lexical? #'var) (quasisyntax/loc stx (set-box! var #,e))]
|
||||||
[(eq? e #'expr) stx]
|
[(eq? e #'expr) stx]
|
||||||
[else (quasisyntax* stx (set! var #,e))]))]
|
[else (quasisyntax/loc stx (set! var #,e))]))]
|
||||||
[(define-values (var) expr)
|
[(define-values (var) expr)
|
||||||
(let ([e (bmv #'expr)])
|
(let ([e (bmv #'expr)])
|
||||||
(cond [(mutated? #'var)
|
(cond [(mutated? #'var)
|
||||||
(with-syntax ([(loc) (generate-temporaries #'(var))])
|
(with-syntax ([(loc) (generate-temporaries #'(var))])
|
||||||
(dict-set! varref-tbl #'var #'loc)
|
(quasisyntax/loc stx
|
||||||
(quasisyntax* stx
|
(splicing-let ([loc (box #,e)])
|
||||||
(begin
|
|
||||||
(define loc (box #,e))
|
|
||||||
(define-syntax var
|
(define-syntax var
|
||||||
(syntax-id-rules (set!)
|
(syntax-id-rules (set!)
|
||||||
[(set! var val) (set-box! loc val)]
|
[(set! var val) (set-box! loc val)]
|
||||||
[(var . arg) ((unbox loc) . arg)]
|
[(var . arg) ((unbox loc) . arg)]
|
||||||
[var (unbox loc)])))))]
|
[var (unbox loc)])))))]
|
||||||
[(eq? e #'expr) stx]
|
[(eq? e #'expr) stx]
|
||||||
[else (quasisyntax* stx (define-values (var) #,e))]))]
|
[else (quasisyntax/loc stx (define-values (var) #,e))]))]
|
||||||
[(define-values (var ...) expr)
|
[(define-values (var ...) expr)
|
||||||
(let ([e (bmv #'expr)]
|
(let ([e (bmv #'expr)]
|
||||||
[vs (syntax->list #'(var ...))])
|
[vs (syntax->list #'(var ...))])
|
||||||
(cond [(any-mutated? vs)
|
(cond [(any-mutated? vs)
|
||||||
(let ([locs (generate-temporaries vs)])
|
(let ([locs (generate-temporaries vs)])
|
||||||
(for ([v (in-list vs)]
|
(quasisyntax/loc stx
|
||||||
[loc (in-list locs)]
|
(splicing-let-values ([#,locs #,e])
|
||||||
#:when (mutated? v))
|
|
||||||
(dict-set! varref-tbl v loc))
|
|
||||||
(quasisyntax* stx
|
|
||||||
(begin
|
|
||||||
(define-values #,locs #,e)
|
|
||||||
#,@(for/list ([v vs][loc locs] #:when (mutated? v))
|
#,@(for/list ([v vs][loc locs] #:when (mutated? v))
|
||||||
#`(set! #,loc (box #,loc)))
|
#`(set! #,loc (box #,loc)))
|
||||||
#,@(for/list ([v vs][loc locs])
|
#,@(for/list ([v vs][loc locs])
|
||||||
|
|
@ -178,14 +146,14 @@
|
||||||
[#,v (unbox #,loc)]))
|
[#,v (unbox #,loc)]))
|
||||||
#`(define-values (#,v) #,loc))))))]
|
#`(define-values (#,v) #,loc))))))]
|
||||||
[(eq? e #'expr) stx]
|
[(eq? e #'expr) stx]
|
||||||
[else (quasisyntax* stx (define-values (var ...) #,e))]))]
|
[else (quasisyntax/loc stx (define-values (var ...) #,e))]))]
|
||||||
[(let-values ([(var ...) expr] ...) body ...)
|
[(let-values ([(var ...) expr] ...) body ...)
|
||||||
(let-values ([(pure-es? es) (bmv/list #'(expr ...))]
|
(let-values ([(pure-es? es) (bmv/list #'(expr ...))]
|
||||||
[(pure-fs? fs) (bmv/list #'(body ...))]
|
[(pure-fs? fs) (bmv/list #'(body ...))]
|
||||||
[(vs) (syntax->list #'(var ... ...))])
|
[(vs) (syntax->list #'(var ... ...))])
|
||||||
(cond [(any-mutated? vs)
|
(cond [(any-mutated? vs)
|
||||||
(with-syntax ([(e ...) es])
|
(with-syntax ([(e ...) es])
|
||||||
(quasisyntax* stx
|
(quasisyntax/loc stx
|
||||||
(let-values ([(var ...) e] ...)
|
(let-values ([(var ...) e] ...)
|
||||||
#,@(for/list ([v vs] #:when (mutated? v))
|
#,@(for/list ([v vs] #:when (mutated? v))
|
||||||
#`(set! #,v (box #,v)))
|
#`(set! #,v (box #,v)))
|
||||||
|
|
@ -193,7 +161,7 @@
|
||||||
[(and pure-es? pure-fs?) stx]
|
[(and pure-es? pure-fs?) stx]
|
||||||
[else
|
[else
|
||||||
(with-syntax ([(e ...) es])
|
(with-syntax ([(e ...) es])
|
||||||
(quasisyntax* stx
|
(quasisyntax/loc stx
|
||||||
(let-values ([(var ...) e] ...)
|
(let-values ([(var ...) e] ...)
|
||||||
#,@fs)))]))]
|
#,@fs)))]))]
|
||||||
[(letrec-values ([(var ...) expr] ...) body ...)
|
[(letrec-values ([(var ...) expr] ...) body ...)
|
||||||
|
|
@ -202,7 +170,7 @@
|
||||||
[(vs) (syntax->list #'(var ... ...))])
|
[(vs) (syntax->list #'(var ... ...))])
|
||||||
(cond [(any-mutated? vs)
|
(cond [(any-mutated? vs)
|
||||||
(let ([ves (syntax->list #'((var ...) ...))])
|
(let ([ves (syntax->list #'((var ...) ...))])
|
||||||
(quasisyntax* stx
|
(quasisyntax/loc stx
|
||||||
(letrec-values ([#,vs (apply values (make-list #,(length vs) undefined))])
|
(letrec-values ([#,vs (apply values (make-list #,(length vs) undefined))])
|
||||||
#,@(for/list ([v vs] #:when (mutated? v))
|
#,@(for/list ([v vs] #:when (mutated? v))
|
||||||
#`(set! #,v (box #,v)))
|
#`(set! #,v (box #,v)))
|
||||||
|
|
@ -215,7 +183,7 @@
|
||||||
[(and pure-es? pure-fs?) stx]
|
[(and pure-es? pure-fs?) stx]
|
||||||
[else
|
[else
|
||||||
(with-syntax ([(e ...) es])
|
(with-syntax ([(e ...) es])
|
||||||
(quasisyntax* stx
|
(quasisyntax/loc stx
|
||||||
(letrec-values ([(var ...) e] ...)
|
(letrec-values ([(var ...) e] ...)
|
||||||
#,@fs)))]))]
|
#,@fs)))]))]
|
||||||
[(letrec-syntaxes+values stx-decls ([(var ...) expr] ...) body ...)
|
[(letrec-syntaxes+values stx-decls ([(var ...) expr] ...) body ...)
|
||||||
|
|
@ -224,7 +192,7 @@
|
||||||
[(vs) (syntax->list #'(var ... ...))])
|
[(vs) (syntax->list #'(var ... ...))])
|
||||||
(cond [(any-mutated? vs)
|
(cond [(any-mutated? vs)
|
||||||
(let ([ves (syntax->list #'((var ...) ...))])
|
(let ([ves (syntax->list #'((var ...) ...))])
|
||||||
(quasisyntax* stx
|
(quasisyntax/loc stx
|
||||||
(letrec-syntaxes+values stx-decls ([#,vs (apply values (make-list #,(length vs) undefined))])
|
(letrec-syntaxes+values stx-decls ([#,vs (apply values (make-list #,(length vs) undefined))])
|
||||||
#,@(for/list ([v vs] #:when (mutated? v))
|
#,@(for/list ([v vs] #:when (mutated? v))
|
||||||
#`(set! #,v (box #,v)))
|
#`(set! #,v (box #,v)))
|
||||||
|
|
@ -237,13 +205,13 @@
|
||||||
[(and pure-es? pure-fs?) stx]
|
[(and pure-es? pure-fs?) stx]
|
||||||
[else
|
[else
|
||||||
(with-syntax ([(e ...) es])
|
(with-syntax ([(e ...) es])
|
||||||
(quasisyntax* stx
|
(quasisyntax/loc stx
|
||||||
(letrec-syntaxes+values stx-decls ([(var ...) e] ...)
|
(letrec-syntaxes+values stx-decls ([(var ...) e] ...)
|
||||||
#,@fs)))]))]
|
#,@fs)))]))]
|
||||||
[(#%plain-lambda formals . rest)
|
[(#%plain-lambda formals . rest)
|
||||||
(let ([body (bmv/proc-body #'formals #'rest)])
|
(let ([body (bmv/proc-body #'formals #'rest)])
|
||||||
(cond [(eq? body #'rest) stx]
|
(cond [(eq? body #'rest) stx]
|
||||||
[else (quasisyntax* stx (#%plain-lambda formals #,@body))]))]
|
[else (quasisyntax/loc stx (#%plain-lambda formals #,@body))]))]
|
||||||
[(case-lambda . rest)
|
[(case-lambda . rest)
|
||||||
(let* ([r (syntax->list #'rest)]
|
(let* ([r (syntax->list #'rest)]
|
||||||
[fs (for/list ([fb r])
|
[fs (for/list ([fb r])
|
||||||
|
|
@ -251,19 +219,16 @@
|
||||||
(let ([body (bmv/proc-body #'f #'b)])
|
(let ([body (bmv/proc-body #'f #'b)])
|
||||||
(if (eq? body #'b)
|
(if (eq? body #'b)
|
||||||
fb
|
fb
|
||||||
(quasisyntax* fb (f #,@body))))))])
|
(quasisyntax/loc fb (f #,@body))))))])
|
||||||
(cond [(equal? r fs) stx]
|
(cond [(equal? r fs) stx]
|
||||||
[else (quasisyntax* stx (case-lambda #,@fs))]))]
|
[else (quasisyntax/loc stx (case-lambda #,@fs))]))]
|
||||||
[(if . rest) (bmv/rest stx #'if #'rest)]
|
[(if . rest) (bmv/rest stx #'if #'rest)]
|
||||||
[(#%expression . rest) (bmv/rest stx #'#%expression #'rest)]
|
[(#%expression . rest) (bmv/rest stx #'#%expression #'rest)]
|
||||||
[(#%plain-app . rest) (bmv/rest stx #'#%plain-app #'rest)]
|
[(#%plain-app . rest) (bmv/rest stx #'#%plain-app #'rest)]
|
||||||
[(begin . rest) (bmv/rest stx #'begin #'rest)]
|
[(begin . rest) (bmv/rest stx #'begin #'rest)]
|
||||||
[(begin0 . rest) (bmv/rest stx #'begin0 #'rest)]
|
[(begin0 . rest) (bmv/rest stx #'begin0 #'rest)]
|
||||||
[(with-continuation-mark . rest) (bmv/rest stx #'with-continuation-mark #'rest)]
|
[(with-continuation-mark . rest) (bmv/rest stx #'with-continuation-mark #'rest)]
|
||||||
[(#%plain-module-begin . rest) (quasisyntax* stx (#%module-begin #,@(map bmv (syntax->list #'rest))))]
|
[(#%plain-module-begin . rest) (quasisyntax/loc stx (#%module-begin #,@(map bmv (syntax->list #'rest))))]
|
||||||
[(#%variable-reference x)
|
|
||||||
#`(#%variable-reference
|
|
||||||
#,(free-id-table-ref varref-tbl #'x (λ () #'x)))]
|
|
||||||
[_ stx]))
|
[_ stx]))
|
||||||
|
|
||||||
(bmv form))
|
(bmv form))
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
#lang racket
|
||||||
|
|
||||||
|
(provide current-oracle oracle? (rename-out [make-oracle oracle]))
|
||||||
|
|
||||||
|
#|--------------current state parameters--------------|#
|
||||||
|
|
||||||
|
(struct oracle ([tbl])
|
||||||
|
#:property prop:procedure
|
||||||
|
(lambda (self var)
|
||||||
|
(let* ([vars (oracle-tbl self)]
|
||||||
|
[choice-idx (hash-ref vars var 0)])
|
||||||
|
(hash-set! vars var (+ choice-idx 1))
|
||||||
|
choice-idx))
|
||||||
|
#:methods gen:custom-write
|
||||||
|
[(define (write-proc self port mode)
|
||||||
|
(fprintf port "oracle~a" (oracle-tbl self)))])
|
||||||
|
|
||||||
|
(define make-oracle
|
||||||
|
(case-lambda
|
||||||
|
[() (oracle (make-hash))]
|
||||||
|
[(other) (oracle (hash-copy (oracle-tbl other)))]))
|
||||||
|
|
||||||
|
(define current-oracle
|
||||||
|
(make-parameter (make-oracle)
|
||||||
|
(lambda (oracle)
|
||||||
|
(unless (oracle? oracle)
|
||||||
|
(error 'current-oracle "expected an oracle procedure, given ~s" oracle))
|
||||||
|
oracle)))
|
||||||
|
|
||||||
|
|
@ -1,208 +1,42 @@
|
||||||
#lang racket
|
#lang racket
|
||||||
|
|
||||||
(require (for-syntax (only-in racket/syntax
|
(require (for-syntax (only-in racket/syntax format-id))
|
||||||
format-id wrong-syntax generate-temporary
|
|
||||||
current-syntax-context)
|
|
||||||
(only-in syntax/stx stx-pair? stx-car stx-cdr))
|
|
||||||
(only-in racket/generic define-generics)
|
(only-in racket/generic define-generics)
|
||||||
|
(only-in "../form/control.rkt" @if)
|
||||||
|
(only-in "../core/bool.rkt" @assert)
|
||||||
(only-in "../core/forall.rkt" for/all)
|
(only-in "../core/forall.rkt" for/all)
|
||||||
"../core/union.rkt")
|
"../core/union.rkt")
|
||||||
|
|
||||||
(provide @define-generics @make-struct-type-property)
|
(provide @define-generics @make-struct-type-property)
|
||||||
|
|
||||||
(begin-for-syntax
|
|
||||||
|
|
||||||
;; parse is copied from racket/generic
|
|
||||||
;; One modification (marked below): If the #:defined-predicate option
|
|
||||||
;; is not present, it returns #f instead of (generate-temporary)
|
|
||||||
(define (parse stx [options (hasheq)])
|
|
||||||
(syntax-case stx ()
|
|
||||||
[(#:defined-predicate name . args)
|
|
||||||
(identifier? #'name)
|
|
||||||
(if (hash-ref options 'support #f)
|
|
||||||
(wrong-syntax (stx-car stx)
|
|
||||||
"duplicate #:defined-predicate specification")
|
|
||||||
(parse #'args (hash-set options 'support #'name)))]
|
|
||||||
[(#:defined-predicate . other)
|
|
||||||
(wrong-syntax (stx-car stx) "invalid #:defined-predicate specification")]
|
|
||||||
[(#:defined-table name . args)
|
|
||||||
(identifier? #'name)
|
|
||||||
(if (hash-ref options 'table #f)
|
|
||||||
(wrong-syntax (stx-car stx)
|
|
||||||
"duplicate #:defined-table specification")
|
|
||||||
(parse #'args (hash-set options 'table #'name)))]
|
|
||||||
[(#:defined-table . other)
|
|
||||||
(wrong-syntax (stx-car stx) "invalid #:defined-table specification")]
|
|
||||||
[(#:defaults (clause ...) . args)
|
|
||||||
(if (hash-ref options 'defaults #f)
|
|
||||||
(wrong-syntax (stx-car stx) "duplicate #:defaults specification")
|
|
||||||
(let loop ([defaults '()]
|
|
||||||
[clauses (reverse (syntax->list #'(clause ...)))])
|
|
||||||
(if (pair? clauses)
|
|
||||||
(syntax-case (car clauses) ()
|
|
||||||
[(pred #:dispatch disp defn ...)
|
|
||||||
(loop (cons #'[pred disp defn ...] defaults)
|
|
||||||
(cdr clauses))]
|
|
||||||
[(pred defn ...)
|
|
||||||
(with-syntax ([name (generate-temporary #'pred)])
|
|
||||||
(loop (cons #'[pred #:same defn ...] defaults)
|
|
||||||
(cdr clauses)))]
|
|
||||||
[clause
|
|
||||||
(wrong-syntax #'clause "invalid #:defaults specification")])
|
|
||||||
(parse #'args
|
|
||||||
(hash-set* options 'defaults defaults)))))]
|
|
||||||
[(#:defaults . other)
|
|
||||||
(wrong-syntax (stx-car stx) "invalid #:defaults specification")]
|
|
||||||
[(#:fast-defaults (clause ...) . args)
|
|
||||||
(if (hash-ref options 'fast-defaults #f)
|
|
||||||
(wrong-syntax (stx-car stx)
|
|
||||||
"duplicate #:fast-defaults specification")
|
|
||||||
(let loop ([fast-defaults '()]
|
|
||||||
[clauses (reverse (syntax->list #'(clause ...)))])
|
|
||||||
(if (pair? clauses)
|
|
||||||
(syntax-case (car clauses) ()
|
|
||||||
[(pred #:dispatch disp defn ...)
|
|
||||||
(loop (cons #'[pred disp defn ...] fast-defaults)
|
|
||||||
(cdr clauses))]
|
|
||||||
[(pred defn ...)
|
|
||||||
(with-syntax ([name (generate-temporary #'pred)])
|
|
||||||
(loop (cons #'[pred #:same defn ...] fast-defaults)
|
|
||||||
(cdr clauses)))]
|
|
||||||
[clause
|
|
||||||
(wrong-syntax #'clause
|
|
||||||
"invalid #:fast-defaults specification")])
|
|
||||||
(parse #'args
|
|
||||||
(hash-set* options
|
|
||||||
'fast-defaults fast-defaults)))))]
|
|
||||||
[(#:fast-defaults . other)
|
|
||||||
(wrong-syntax (stx-car stx) "invalid #:fast-defaults specification")]
|
|
||||||
[(#:fallbacks [fallback ...] . args)
|
|
||||||
(if (hash-ref options 'fallbacks #f)
|
|
||||||
(wrong-syntax (stx-car stx) "duplicate #:fallbacks specification")
|
|
||||||
(parse #'args (hash-set options 'fallbacks #'[fallback ...])))]
|
|
||||||
[(#:fallbacks . other)
|
|
||||||
(wrong-syntax (stx-car stx) "invalid #:fallbacks specification")]
|
|
||||||
[(#:derive-property prop impl . args)
|
|
||||||
(parse #'args
|
|
||||||
(hash-set options
|
|
||||||
'derived
|
|
||||||
(cons (list #'prop #'impl)
|
|
||||||
(hash-ref options 'derived '()))))]
|
|
||||||
[(#:derive-property . other)
|
|
||||||
(wrong-syntax (stx-car stx) "invalid #:derive-property specification")]
|
|
||||||
[(kw . args)
|
|
||||||
(keyword? (syntax-e #'kw))
|
|
||||||
(wrong-syntax #'kw "invalid keyword argument")]
|
|
||||||
[((_ . _) . args)
|
|
||||||
(if (hash-ref options 'methods #f)
|
|
||||||
(wrong-syntax (stx-car stx) "duplicate methods list specification")
|
|
||||||
(let loop ([methods (list (stx-car stx))] [stx #'args])
|
|
||||||
(syntax-case stx ()
|
|
||||||
[((_ . _) . args) (loop (cons (stx-car stx) methods) #'args)]
|
|
||||||
[_ (parse stx (hash-set options 'methods (reverse methods)))])))]
|
|
||||||
[(other . args)
|
|
||||||
(wrong-syntax #'other
|
|
||||||
"expected a method identifier with formal arguments")]
|
|
||||||
[() (values (hash-ref options 'methods '())
|
|
||||||
;; MODIFICATION: Third argument to hash-ref changed
|
|
||||||
;; from generate-temporary to #f
|
|
||||||
(hash-ref options 'support #f)
|
|
||||||
(hash-ref options 'table #f)
|
|
||||||
(hash-ref options 'fast-defaults '())
|
|
||||||
(hash-ref options 'defaults '())
|
|
||||||
(hash-ref options 'fallbacks '())
|
|
||||||
(hash-ref options 'derived '()))]
|
|
||||||
[other
|
|
||||||
(wrong-syntax #'other
|
|
||||||
"expected a list of arguments with no dotted tail")]))
|
|
||||||
|
|
||||||
(define (index-of name-stx formals-stx)
|
|
||||||
(let loop ([i 0] [formals formals-stx])
|
|
||||||
(unless (stx-pair? formals)
|
|
||||||
(wrong-syntax
|
|
||||||
formals-stx
|
|
||||||
"did not find the generic name ~a among the required, by-position arguments"
|
|
||||||
(syntax->datum name-stx)))
|
|
||||||
(define c (stx-car formals))
|
|
||||||
(cond [(identifier? c)
|
|
||||||
(if (free-identifier=? name-stx (stx-car formals))
|
|
||||||
(datum->syntax name-stx i)
|
|
||||||
(loop (+ i 1) (stx-cdr formals)))]
|
|
||||||
[(keyword? (syntax->datum c)) ; count only by-position required arguments
|
|
||||||
(loop i (stx-cdr (stx-cdr formals)))]
|
|
||||||
[else
|
|
||||||
(wrong-syntax c "required arguments must precede optional arguments")]))))
|
|
||||||
|
|
||||||
(define-syntax (@define-generics stx)
|
(define-syntax (@define-generics stx)
|
||||||
(syntax-case stx ()
|
(syntax-case stx ()
|
||||||
[(_ id . rest)
|
[(_ id [method self arg ...] ...)
|
||||||
(parameterize ([current-syntax-context stx])
|
(with-syntax ([id? (format-id #'id "~a?" #'id #:source #'id)])
|
||||||
(unless (identifier? #'id)
|
(syntax/loc stx
|
||||||
(wrong-syntax #'id "expected an identifier"))
|
(begin
|
||||||
(define-values
|
(define-generics id
|
||||||
(methods support table fasts defaults fallbacks derived)
|
[method self arg ...] ...)
|
||||||
(parse #'rest))
|
(set! id? (lift id? receiver))
|
||||||
|
(set! method (lift method receiver arg ...)) ...)))]))
|
||||||
(when table
|
|
||||||
(wrong-syntax table
|
|
||||||
"#:defined-table option is not supported in Rosette"))
|
|
||||||
|
|
||||||
(with-syntax ([id? (format-id #'id "~a?" #'id #:source #'id)]
|
|
||||||
[((method-name . method-args) ...) methods]
|
|
||||||
[support-name support])
|
|
||||||
(with-syntax ([(method-index ...)
|
|
||||||
(map (lambda (args) (index-of #'id args))
|
|
||||||
(syntax-e #'(method-args ...)))])
|
|
||||||
(syntax/loc stx
|
|
||||||
(begin
|
|
||||||
(define-generics id . rest)
|
|
||||||
(lift-if-exists id? 0)
|
|
||||||
(lift-if-exists support-name 0)
|
|
||||||
(lift-if-exists method-name method-index) ...)))))]))
|
|
||||||
|
|
||||||
(define (@make-struct-type-property name [guard #f] [supers null] [can-impersonate? #f])
|
(define (@make-struct-type-property name [guard #f] [supers null] [can-impersonate? #f])
|
||||||
(define-values (prop:p p? p-ref)
|
(define-values (prop:p p? p-ref)
|
||||||
(make-struct-type-property name guard supers can-impersonate?))
|
(make-struct-type-property name guard supers can-impersonate?))
|
||||||
(values prop:p (lift p? 0) (lift p-ref 0)))
|
(values prop:p (lift p? self) (lift p-ref self)))
|
||||||
|
|
||||||
(define-syntax (lift-if-exists stx)
|
(define-syntax-rule (lift proc receiver arg ...)
|
||||||
(syntax-case stx ()
|
(let ([proc proc])
|
||||||
[(_ proc receiver-index)
|
(procedure-rename
|
||||||
(if (syntax->datum #'proc)
|
(lambda (receiver arg ...)
|
||||||
(syntax/loc stx
|
(if (union? receiver)
|
||||||
(set! proc (lift proc receiver-index)))
|
(for/all ([r receiver]) (proc r arg ...))
|
||||||
(syntax/loc stx
|
(proc receiver arg ...)))
|
||||||
(void)))]))
|
(or (object-name proc) 'proc))))
|
||||||
|
|
||||||
(define (lift proc receiver-index)
|
|
||||||
(define-values (required-kws allowed-kws) (procedure-keywords proc))
|
|
||||||
(define arity (procedure-arity proc))
|
|
||||||
(procedure-rename
|
|
||||||
(if (null? allowed-kws)
|
|
||||||
(procedure-reduce-arity
|
|
||||||
(lambda args
|
|
||||||
(define receiver (list-ref args receiver-index))
|
|
||||||
(if (union? receiver)
|
|
||||||
(for/all ([r receiver])
|
|
||||||
(apply proc (list-set args receiver-index r)))
|
|
||||||
(apply proc args)))
|
|
||||||
arity)
|
|
||||||
(procedure-reduce-keyword-arity
|
|
||||||
(make-keyword-procedure
|
|
||||||
(lambda (kws kw-args . args)
|
|
||||||
(define receiver (list-ref args receiver-index))
|
|
||||||
(if (union? receiver)
|
|
||||||
(for/all ([r receiver])
|
|
||||||
(keyword-apply proc kws kw-args (list-set args receiver-index r)))
|
|
||||||
(keyword-apply proc kws kw-args args))))
|
|
||||||
arity
|
|
||||||
required-kws
|
|
||||||
allowed-kws))
|
|
||||||
(or (object-name proc) 'lifted)))
|
|
||||||
|
|
||||||
#|
|
#|
|
||||||
; sanity check
|
; sanity check
|
||||||
(@define-generics foo [some foo])
|
(define-generics foo [some foo])
|
||||||
some
|
some
|
||||||
|
|
||||||
(struct bar (arg)
|
(struct bar (arg)
|
||||||
|
|
@ -211,36 +45,13 @@ some
|
||||||
|
|
||||||
(some (bar 'yes))
|
(some (bar 'yes))
|
||||||
|
|
||||||
(require (only-in rosette/base/form/define define-symbolic)
|
(require (only-in rosette/base/define define-symbolic)
|
||||||
(only-in rosette/base/core/bool @boolean?)
|
(only-in rosette/base/bool @boolean?))
|
||||||
(only-in rosette/base/core/real @* @+))
|
|
||||||
|
|
||||||
(define-symbolic b @boolean?)
|
(define-symbolic b @boolean?)
|
||||||
|
|
||||||
(some (@if b (bar 'yes) (bar 'no)))
|
(some (@if b (bar 'yes) (bar 'no)))
|
||||||
(foo? (@if b (bar 'yes) (bar 'no)))
|
(foo? (@if b (bar 'yes) (bar 'no)))|#
|
||||||
|
|
||||||
(@define-generics xyzzy
|
|
||||||
(h xyzzy))
|
|
||||||
|
|
||||||
(@define-generics variadic
|
|
||||||
(f variadic . x)
|
|
||||||
(g variadic))
|
|
||||||
|
|
||||||
(struct multiplier (y) #:transparent
|
|
||||||
#:methods gen:variadic
|
|
||||||
[(define (f self . x) (foldl @* (multiplier-y self) x))
|
|
||||||
(define (g self) 'g-mult)]
|
|
||||||
#:methods gen:xyzzy
|
|
||||||
[(define (h self) 42)])
|
|
||||||
|
|
||||||
(struct adder (z) #:transparent
|
|
||||||
#:methods gen:variadic
|
|
||||||
[(define (f self . x) (foldl @+ (adder-z self) x))
|
|
||||||
(define (g self) 'g-add)])
|
|
||||||
|
|
||||||
(define thing (@if b (multiplier 3) (adder 3)))
|
|
||||||
(variadic? thing)
|
|
||||||
thing
|
|
||||||
(f thing 2 5)
|
|
||||||
(g thing)
|
|
||||||
(h thing)|#
|
|
||||||
|
|
|
||||||
|
|
@ -2,121 +2,66 @@
|
||||||
|
|
||||||
|
|
||||||
(require (for-syntax "../core/lift.rkt" racket/syntax)
|
(require (for-syntax "../core/lift.rkt" racket/syntax)
|
||||||
(only-in racket/private/generic-methods generic-property)
|
(only-in "../core/effects.rkt" apply!)
|
||||||
(only-in "../core/store.rkt" store!)
|
|
||||||
"../core/term.rkt" "../core/lift.rkt" "../core/safe.rkt"
|
"../core/term.rkt" "../core/lift.rkt" "../core/safe.rkt"
|
||||||
(only-in "../core/bool.rkt" || && and-&&)
|
(only-in "../core/bool.rkt" || && and-&&)
|
||||||
(only-in "../core/type.rkt" @any/c type-cast gen:typed get-type)
|
(only-in "../core/type.rkt" @any/c type-cast)
|
||||||
(only-in "../core/procedure.rkt" @procedure?)
|
(only-in "../core/procedure.rkt" @procedure?)
|
||||||
(only-in "../core/merge.rkt" merge merge*)
|
(only-in "../core/merge.rkt" merge merge*)
|
||||||
(only-in "../core/union.rkt" union union? in-union-guards)
|
(only-in "../core/union.rkt" union union? in-union-guards)
|
||||||
(only-in "../core/equality.rkt" @equal? @eq?)
|
(only-in "../core/equality.rkt" @equal? @eq?)
|
||||||
(only-in "../adt/generic.rkt" adt-type-cast))
|
(only-in "../adt/generic.rkt" adt-type-cast))
|
||||||
|
|
||||||
(provide @make-struct-type
|
(provide @struct-predicate @make-struct-field-accessor @make-struct-field-mutator)
|
||||||
@make-struct-field-accessor
|
|
||||||
@make-struct-field-mutator)
|
|
||||||
|
|
||||||
(define (@make-struct-type
|
(define (@make-struct-field-mutator lifted? i field-id)
|
||||||
name super-type init-field-cnt auto-field-cnt
|
(let ([native? (struct-type-native? lifted?)]
|
||||||
[auto-v #f]
|
[setter (make-struct-field-mutator (struct-type-set! lifted?) i field-id)]
|
||||||
[props '()]
|
[getter (make-struct-field-accessor (struct-type-ref lifted?) i field-id)])
|
||||||
[inspector (current-inspector)]
|
(procedure-rename
|
||||||
[proc-spec #f]
|
(lambda (receiver value)
|
||||||
[immutables '()]
|
(if (native? receiver)
|
||||||
[guard #f]
|
(apply! setter getter receiver value)
|
||||||
[constructor-name #f])
|
(match (type-cast lifted? receiver (object-name setter))
|
||||||
|
[(? native? r) (apply! setter getter receiver value)]
|
||||||
|
[(union rs) (for ([r rs])
|
||||||
|
(apply! setter getter (cdr r) (merge (car r) value (getter (cdr r)))))])))
|
||||||
|
(object-name setter))))
|
||||||
|
|
||||||
; (printf "@make-struct-type:\n")
|
(define (@make-struct-field-accessor lifted? i field-id)
|
||||||
; (printf " name: ~a\n" name)
|
;(printf "@make-struct-field-accessor ~a ~a ~a\n" lifted? i field-id)
|
||||||
; (printf " super-type: ~a\n" super-type)
|
(let ([native? (struct-type-native? lifted?)]
|
||||||
; (printf " init-field-cnt: ~a\n" init-field-cnt)
|
[getter (make-struct-field-accessor (struct-type-ref lifted?) i field-id)])
|
||||||
; (printf " auto-field-cnt: ~a\n" auto-field-cnt)
|
(procedure-rename
|
||||||
; (printf " props: ~a\n" props)
|
(lambda (receiver)
|
||||||
; (printf " inspector: ~a\n" inspector)
|
(if (native? receiver)
|
||||||
; (printf " proc-spec: ~a\n" proc-spec)
|
(getter receiver)
|
||||||
; (printf " immutables: ~a\n" immutables)
|
(match (type-cast lifted? receiver (object-name getter))
|
||||||
|
[(? native? r) (getter r)]
|
||||||
|
[(union r) (merge** r getter)])))
|
||||||
|
(object-name getter))))
|
||||||
|
|
||||||
(define-values (struct:t make-t t? t-ref t-set!)
|
(define (@struct-predicate struct:super is-a? make ref set! field-count immutable? transparent? procedure? equal+hash)
|
||||||
(make-struct-type
|
;(printf "@struct-type:\n")
|
||||||
name super-type init-field-cnt auto-field-cnt auto-v
|
;(printf " super=~a, ?=~a, make=~a\n ref=~a, set!=~a, field-count=~a\n" struct:super is-a? make ref set! field-count)
|
||||||
(cons (cons (generic-property gen:typed)
|
;(printf " immutable?=~a, transparent?=~a, procedure?=~a\n equal+hash=~a\n" immutable? transparent? procedure? equal+hash)
|
||||||
(vector (lambda (self) @struct:t)))
|
(define (t? v)
|
||||||
props) ; all struct values are typed
|
|
||||||
inspector proc-spec immutables
|
|
||||||
guard constructor-name))
|
|
||||||
|
|
||||||
(define (@t? v)
|
|
||||||
(match v
|
(match v
|
||||||
[(? t?) #t]
|
[(? is-a?) #t]
|
||||||
[(and (? typed? v) (app get-type t))
|
[(and (? typed? v) (app get-type t))
|
||||||
(or (and t (subtype? t @struct:t))
|
(or (and t (subtype? t st))
|
||||||
(and (union? v) (apply || (for/list ([g (in-union-guards v @struct:t)]) g))))]
|
(and (union? v) (apply || (for/list ([g (in-union-guards v st)]) g))))]
|
||||||
[_ #f]))
|
[_ #f]))
|
||||||
|
(define super (and struct:super (typed? struct:super) (get-type struct:super)))
|
||||||
(define super (and super-type (typed? super-type) (get-type super-type)))
|
(define st
|
||||||
(define field-count (+ init-field-cnt auto-field-cnt))
|
|
||||||
(define immutable? (and (= init-field-cnt (length immutables)) (zero? auto-field-cnt)))
|
|
||||||
(define transparent? (not inspector))
|
|
||||||
(define equal+hash (let ([e+h (assoc (generic-property gen:equal+hash) props)])
|
|
||||||
(and e+h (cdr e+h))))
|
|
||||||
(define procedure? (or proc-spec (not (false? (assoc prop:procedure props)))))
|
|
||||||
|
|
||||||
; (printf " super: ~a\n" super)
|
|
||||||
; (printf " field-count: ~a\n" field-count)
|
|
||||||
; (printf " immutable?: ~a\n" immutable?)
|
|
||||||
; (printf " transparent?: ~a\n" transparent?)
|
|
||||||
; (printf " procedure?: ~a\n" procedure?)
|
|
||||||
; (printf " equal+hash: ~a\n" equal+hash)
|
|
||||||
|
|
||||||
(define @struct:t
|
|
||||||
(struct-type
|
(struct-type
|
||||||
(procedure-rename @t? (object-name t?))
|
(procedure-rename t? (object-name is-a?))
|
||||||
super t? make-t t-ref t-set! field-count
|
super is-a? make ref set! field-count
|
||||||
(and immutable? (implies super (struct-type-immutable? super)))
|
(and immutable? (implies super (struct-type-immutable? super)))
|
||||||
(and transparent? (implies super (struct-type-transparent? super)))
|
(and transparent? (implies super (struct-type-transparent? super)))
|
||||||
(or procedure? (and super (struct-type-procedure? super)))
|
(or procedure? (and super (struct-type-procedure? super)))
|
||||||
equal+hash))
|
equal+hash))
|
||||||
|
st)
|
||||||
(values struct:t make-t @struct:t t-ref t-set!))
|
|
||||||
|
|
||||||
(define (struct-field-accessor-name @struct:t i field-id)
|
|
||||||
(if field-id
|
|
||||||
(format "~a-~a" (object-name (struct-type-make @struct:t)) field-id)
|
|
||||||
(format "~a-field~a" (object-name (struct-type-make @struct:t)) i)))
|
|
||||||
|
|
||||||
(define (struct-field-mutator-name @struct:t i field-id)
|
|
||||||
(format "set-~a!" (struct-field-accessor-name @struct:t i field-id)))
|
|
||||||
|
|
||||||
(define (@make-struct-field-mutator struct:t i field-id)
|
|
||||||
(let* ([@struct:t (get-type struct:t)]
|
|
||||||
[native? (struct-type-native? @struct:t)]
|
|
||||||
[name (string->symbol (struct-field-mutator-name @struct:t i field-id))]
|
|
||||||
[setter (struct-type-set! @struct:t)]
|
|
||||||
[getter (struct-type-ref @struct:t)])
|
|
||||||
(procedure-rename
|
|
||||||
(lambda (receiver value)
|
|
||||||
(if (native? receiver)
|
|
||||||
(store! receiver i value getter setter)
|
|
||||||
(match (type-cast @struct:t receiver name)
|
|
||||||
[(? native? r) (store! r i value getter setter)]
|
|
||||||
[(union rs) (for ([r rs])
|
|
||||||
(store! (cdr r) i (merge (car r) value (getter (cdr r) i)) getter setter))])))
|
|
||||||
name)))
|
|
||||||
|
|
||||||
(define (@make-struct-field-accessor struct:t i field-id)
|
|
||||||
(let* ([@struct:t (get-type struct:t)]
|
|
||||||
[native? (struct-type-native? @struct:t)]
|
|
||||||
[name (string->symbol (struct-field-accessor-name @struct:t i field-id))]
|
|
||||||
[getter (struct-type-ref @struct:t)])
|
|
||||||
(procedure-rename
|
|
||||||
(lambda (receiver)
|
|
||||||
(if (native? receiver)
|
|
||||||
(getter receiver i)
|
|
||||||
(match (type-cast @struct:t receiver name)
|
|
||||||
[(? native? r) (getter r i)]
|
|
||||||
[(union r) (merge** r (getter _ i))])))
|
|
||||||
name)))
|
|
||||||
|
|
||||||
(struct struct-type (pred super native? make ref set! fields immutable? transparent? procedure? equal+hash)
|
(struct struct-type (pred super native? make ref set! fields immutable? transparent? procedure? equal+hash)
|
||||||
#:property prop:procedure
|
#:property prop:procedure
|
||||||
|
|
@ -219,3 +164,4 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,10 @@
|
||||||
|
|
||||||
(require racket/stxparam "struct-type.rkt"
|
(require racket/stxparam "struct-type.rkt"
|
||||||
(only-in "../core/type.rkt" gen:typed get-type)
|
(only-in "../core/type.rkt" gen:typed get-type)
|
||||||
racket/private/generic-methods; (except-in racket/private/generic-methods define/generic)
|
(except-in racket/private/generic-methods define/generic)
|
||||||
(for-syntax racket/base racket/struct-info racket/syntax
|
(for-syntax racket/base racket/struct-info racket/syntax))
|
||||||
racket/private/procedure-alias "struct-type.rkt"))
|
|
||||||
|
|
||||||
(provide struct define/generic (rename-out [define-struct* define-struct]))
|
(provide struct struct-field-index define/generic define-struct)
|
||||||
|
|
||||||
(define-syntax (struct stx)
|
(define-syntax (struct stx)
|
||||||
(define (config-has-name? config)
|
(define (config-has-name? config)
|
||||||
|
|
@ -25,32 +24,42 @@
|
||||||
(identifier? #'super-id))
|
(identifier? #'super-id))
|
||||||
(if (not (config-has-name? #'config))
|
(if (not (config-has-name? #'config))
|
||||||
(syntax/loc stx
|
(syntax/loc stx
|
||||||
(define-struct/derived orig (id super-id) fields #:constructor-name id . config))
|
(define-struct/typed orig (id super-id) fields #:constructor-name id . config))
|
||||||
(syntax/loc stx
|
(syntax/loc stx
|
||||||
(define-struct/derived orig (id super-id) fields . config)))]
|
(define-struct/typed orig (id super-id) fields . config)))]
|
||||||
[(_ id fields . config)
|
[(_ id fields . config)
|
||||||
(identifier? #'id)
|
(identifier? #'id)
|
||||||
(if (not (config-has-name? #'config))
|
(if (not (config-has-name? #'config))
|
||||||
(syntax/loc stx
|
(syntax/loc stx
|
||||||
(define-struct/derived orig id fields #:constructor-name id . config))
|
(define-struct/typed orig id fields #:constructor-name id . config))
|
||||||
(syntax/loc stx
|
(syntax/loc stx
|
||||||
(define-struct/derived orig id fields . config)))]
|
(define-struct/typed orig id fields . config)))]
|
||||||
[(_ id . rest)
|
[(_ id . rest)
|
||||||
(identifier? #'id)
|
(identifier? #'id)
|
||||||
(syntax/loc stx
|
(syntax/loc stx
|
||||||
(define-struct/derived orig id . rest))]
|
(define-struct/typed orig id . rest))]
|
||||||
[(_ thing . _)
|
[(_ thing . _)
|
||||||
(raise-syntax-error #f
|
(raise-syntax-error #f
|
||||||
"expected an identifier for the structure type name"
|
"expected an identifier for the structure type name"
|
||||||
stx
|
stx
|
||||||
#'thing)])))
|
#'thing)])))
|
||||||
|
|
||||||
(#%provide define-struct*
|
(define-syntax (define-struct stx)
|
||||||
define-struct/derived
|
(syntax-case stx ()
|
||||||
struct-field-index
|
[(_ id (field ...) struct-option ...)
|
||||||
struct-copy
|
(syntax/loc stx (struct id (field ...) struct-option ...))]
|
||||||
(for-syntax
|
[(_ (id super) (field ...) struct-option ...)
|
||||||
(rename checked-struct-info-rec? checked-struct-info?)))
|
(syntax/loc stx (struct id super (field ...) struct-option ...))]))
|
||||||
|
|
||||||
|
|
||||||
|
(define-syntax (define-struct/typed stx)
|
||||||
|
(syntax-case stx ()
|
||||||
|
[(_ orig head rest ...)
|
||||||
|
(let* ([id (if (identifier? #'head) #'head (car (syntax->list #'head)))]
|
||||||
|
[id? (format-id id "~a?" (syntax-e id))])
|
||||||
|
#`(define-struct/derived orig head rest ...
|
||||||
|
#:methods gen:typed
|
||||||
|
[(define (get-type s) #,id?)]))]))
|
||||||
|
|
||||||
(define-values-for-syntax
|
(define-values-for-syntax
|
||||||
(struct:struct-auto-info
|
(struct:struct-auto-info
|
||||||
|
|
@ -93,21 +102,12 @@
|
||||||
(datum->syntax orig (syntax-e orig) stx orig))
|
(datum->syntax orig (syntax-e orig) stx orig))
|
||||||
(syntax-case stx ()
|
(syntax-case stx ()
|
||||||
[(self arg ...) (datum->syntax stx
|
[(self arg ...) (datum->syntax stx
|
||||||
(cons
|
(cons (syntax-property (transfer-srcloc orig #'self)
|
||||||
(syntax-property
|
'constructor-for
|
||||||
(syntax-property (transfer-srcloc orig #'self)
|
(syntax-local-introduce #'self))
|
||||||
'constructor-for
|
(syntax-e (syntax (arg ...))))
|
||||||
(syntax-local-introduce #'self))
|
|
||||||
alias-of (syntax-local-introduce #'self))
|
|
||||||
(syntax-e (syntax (arg ...))))
|
|
||||||
stx
|
stx
|
||||||
stx)]
|
stx)]
|
||||||
[self (identifier? #'self)
|
|
||||||
(syntax-property
|
|
||||||
(syntax-property (transfer-srcloc orig #'self)
|
|
||||||
'constructor-for
|
|
||||||
(syntax-local-introduce #'self))
|
|
||||||
alias-of (syntax-local-introduce #'self))]
|
|
||||||
[_ (transfer-srcloc orig stx)]))
|
[_ (transfer-srcloc orig stx)]))
|
||||||
|
|
||||||
(define-values-for-syntax (make-self-ctor-struct-info)
|
(define-values-for-syntax (make-self-ctor-struct-info)
|
||||||
|
|
@ -133,19 +133,6 @@
|
||||||
(lambda (stx)
|
(lambda (stx)
|
||||||
(raise-syntax-error #f "allowed only within a structure type definition" stx)))
|
(raise-syntax-error #f "allowed only within a structure type definition" stx)))
|
||||||
|
|
||||||
(define-for-syntax (make-struct-field-index fields)
|
|
||||||
(lambda (stx)
|
|
||||||
(syntax-case stx ()
|
|
||||||
[(_ id)
|
|
||||||
(identifier? #'id)
|
|
||||||
(let loop ([pos 0] [fields (syntax->list fields)])
|
|
||||||
(cond
|
|
||||||
[(null? fields)
|
|
||||||
(raise-syntax-error #f "no such field" stx #'name)]
|
|
||||||
[(free-identifier=? #'id (car fields))
|
|
||||||
(datum->syntax #'here pos stx)]
|
|
||||||
[else (loop (add1 pos) (cdr fields))]))])))
|
|
||||||
|
|
||||||
(define (check-struct-type name what)
|
(define (check-struct-type name what)
|
||||||
(when what
|
(when what
|
||||||
(unless (struct-type? what)
|
(unless (struct-type? what)
|
||||||
|
|
@ -163,11 +150,10 @@
|
||||||
(raise-argument-error name "symbol?" what))
|
(raise-argument-error name "symbol?" what))
|
||||||
what)
|
what)
|
||||||
|
|
||||||
(define-syntax (define-struct* stx)
|
(define-syntax-parameter define/generic
|
||||||
(syntax-case stx ()
|
(lambda (stx)
|
||||||
[(_ . rest)
|
(raise-syntax-error 'define/generic "only allowed inside methods" stx)))
|
||||||
(with-syntax ([stx stx])
|
|
||||||
#'(define-struct/derived stx . rest))]))
|
|
||||||
|
|
||||||
(define-syntax (define-struct/derived full-stx)
|
(define-syntax (define-struct/derived full-stx)
|
||||||
(define make-field list)
|
(define make-field list)
|
||||||
|
|
@ -370,7 +356,7 @@
|
||||||
(when (lookup config '#:constructor-name)
|
(when (lookup config '#:constructor-name)
|
||||||
(bad "multiple" "#:constructor-name or #:extra-constructor-name" "s" (car p)))
|
(bad "multiple" "#:constructor-name or #:extra-constructor-name" "s" (car p)))
|
||||||
(unless (identifier? (cadr p))
|
(unless (identifier? (cadr p))
|
||||||
(bad "need an identifier after" (car p) "" (cadr p)))
|
(bad "need an identifier after" (car p) (cadr p)))
|
||||||
(loop (cddr p)
|
(loop (cddr p)
|
||||||
(extend-config (extend-config config '#:constructor-name (cadr p))
|
(extend-config (extend-config config '#:constructor-name (cadr p))
|
||||||
'#:only-constructor?
|
'#:only-constructor?
|
||||||
|
|
@ -410,6 +396,15 @@
|
||||||
stx
|
stx
|
||||||
(car p))])))
|
(car p))])))
|
||||||
|
|
||||||
|
(define (prop:procedure? p)
|
||||||
|
(and (identifier? (car p))
|
||||||
|
(free-identifier=? (car p) #'prop:procedure)))
|
||||||
|
|
||||||
|
(define (gen:equal+hash? p)
|
||||||
|
(and (not (identifier? (car p)))
|
||||||
|
(free-identifier=? (cadr (syntax->list (car p)))
|
||||||
|
#'gen:equal+hash)))
|
||||||
|
|
||||||
(define stx (syntax-case full-stx ()
|
(define stx (syntax-case full-stx ()
|
||||||
[(_ stx . _) #'stx]))
|
[(_ stx . _) #'stx]))
|
||||||
|
|
||||||
|
|
@ -518,6 +513,19 @@
|
||||||
(build-name id ; (field-id f)
|
(build-name id ; (field-id f)
|
||||||
id "-" (field-id f)))
|
id "-" (field-id f)))
|
||||||
fields)]
|
fields)]
|
||||||
|
[sets (let loop ([fields fields])
|
||||||
|
(cond
|
||||||
|
[(null? fields) null]
|
||||||
|
[(not (or mutable? (field-mutable? (car fields))))
|
||||||
|
(loop (cdr fields))]
|
||||||
|
[else
|
||||||
|
(cons (build-name id ; (field-id (car fields))
|
||||||
|
"set-"
|
||||||
|
id
|
||||||
|
"-"
|
||||||
|
(field-id (car fields))
|
||||||
|
"!")
|
||||||
|
(loop (cdr fields)))]))]
|
||||||
[super-struct: (if super-info
|
[super-struct: (if super-info
|
||||||
(or (car super-info)
|
(or (car super-info)
|
||||||
(raise-syntax-error
|
(raise-syntax-error
|
||||||
|
|
@ -526,138 +534,73 @@
|
||||||
stx
|
stx
|
||||||
super-id))
|
super-id))
|
||||||
(and super-expr
|
(and super-expr
|
||||||
#`(let ([the-super #,super-expr])
|
#`(check-struct-type 'fm #,super-expr)))]
|
||||||
(if (struct-type? the-super)
|
[prune
|
||||||
the-super
|
(lambda (stx)
|
||||||
(check-struct-type 'fm the-super)))))]
|
(identifier-prune-lexical-context stx
|
||||||
[prune (lambda (stx) (identifier-prune-lexical-context stx
|
(list (syntax-e stx) '#%top)))]
|
||||||
(list (syntax-e stx) '#%top)))]
|
|
||||||
[reflect-name-expr (if reflect-name-expr
|
[reflect-name-expr (if reflect-name-expr
|
||||||
(quasisyntax (check-reflection-name 'fm #,reflect-name-expr))
|
(quasisyntax (check-reflection-name 'fm #,reflect-name-expr))
|
||||||
(quasisyntax '#,id))])
|
(quasisyntax '#,id))]
|
||||||
|
[proc? (for/or ([p props]) (prop:procedure? p))]
|
||||||
(define struct-name-size (string-length (symbol->string (syntax-e id))))
|
[equal+hash (for/first ([p props] #:when (gen:equal+hash? p)) (cdr p))]
|
||||||
(define struct-name/locally-introduced (syntax-local-introduce id))
|
[props
|
||||||
(define struct-name-to-predicate-directive
|
(if (null? props)
|
||||||
(vector (syntax-local-introduce ?)
|
#'null
|
||||||
0
|
#`(list #,@(for/list ([p props]); #:unless (gen:equal+hash? p))
|
||||||
struct-name-size
|
#`(cons #,(car p) #,(cdr p)))))])
|
||||||
struct-name/locally-introduced
|
|
||||||
0
|
|
||||||
struct-name-size))
|
|
||||||
|
|
||||||
(define struct-name-to-old-style-maker-directive
|
|
||||||
(if ctor-name
|
|
||||||
#f
|
|
||||||
(vector (syntax-local-introduce make-)
|
|
||||||
5
|
|
||||||
struct-name-size
|
|
||||||
struct-name/locally-introduced
|
|
||||||
0
|
|
||||||
struct-name-size)))
|
|
||||||
|
|
||||||
(define (struct-name-to-selector/mutator-directive id-stx selector?)
|
|
||||||
(vector (syntax-local-introduce id-stx)
|
|
||||||
(if selector? 0 4)
|
|
||||||
struct-name-size
|
|
||||||
struct-name/locally-introduced
|
|
||||||
0
|
|
||||||
struct-name-size))
|
|
||||||
(define (field-to-selector/mutator-directive field id-stx selector?)
|
|
||||||
(define fld-size (string-length (symbol->string (syntax-e (field-id field)))))
|
|
||||||
(vector (syntax-local-introduce id-stx)
|
|
||||||
(+ (if selector? 1 5) struct-name-size)
|
|
||||||
fld-size
|
|
||||||
(syntax-local-introduce (field-id field))
|
|
||||||
0
|
|
||||||
fld-size))
|
|
||||||
|
|
||||||
(define-values (sets field-to-mutator-directives)
|
|
||||||
(let loop ([fields fields])
|
|
||||||
(cond
|
|
||||||
[(null? fields) (values null null)]
|
|
||||||
[(not (or mutable? (field-mutable? (car fields))))
|
|
||||||
(loop (cdr fields))]
|
|
||||||
[else
|
|
||||||
(define-values (other-sets other-directives)
|
|
||||||
(loop (cdr fields)))
|
|
||||||
(define this-set
|
|
||||||
(build-name id ; (field-id (car fields))
|
|
||||||
"set-"
|
|
||||||
id
|
|
||||||
"-"
|
|
||||||
(field-id (car fields))
|
|
||||||
"!"))
|
|
||||||
(values (cons this-set other-sets)
|
|
||||||
(cons (field-to-selector/mutator-directive (car fields)
|
|
||||||
this-set
|
|
||||||
#f)
|
|
||||||
other-directives))])))
|
|
||||||
|
|
||||||
(define all-directives
|
|
||||||
(append
|
|
||||||
(list struct-name-to-predicate-directive)
|
|
||||||
(if struct-name-to-old-style-maker-directive
|
|
||||||
(list struct-name-to-old-style-maker-directive)
|
|
||||||
'())
|
|
||||||
field-to-mutator-directives
|
|
||||||
(map (λ (field sel)
|
|
||||||
(field-to-selector/mutator-directive field sel #t))
|
|
||||||
fields
|
|
||||||
sels)
|
|
||||||
(map (λ (sel)
|
|
||||||
(struct-name-to-selector/mutator-directive
|
|
||||||
sel
|
|
||||||
#t))
|
|
||||||
sels)
|
|
||||||
(map (λ (mut)
|
|
||||||
(struct-name-to-selector/mutator-directive
|
|
||||||
mut
|
|
||||||
#f))
|
|
||||||
sets)))
|
|
||||||
|
|
||||||
(let ([run-time-defns
|
(let ([run-time-defns
|
||||||
(lambda ()
|
(lambda ()
|
||||||
(quasisyntax/loc stx
|
(quasisyntax/loc stx
|
||||||
(define-values (#,struct: #,make- #,? #,@sels #,@sets)
|
(define-values (#,struct: #,make- #,? #,@sels #,@sets)
|
||||||
(let-values ([(struct: make- ? -ref -set!)
|
(let*-values
|
||||||
(syntax-parameterize ([struct-field-index
|
([(struct: make- ? -ref -set!)
|
||||||
(make-struct-field-index (quote-syntax #,(map field-id fields)))])
|
(syntax-parameterize
|
||||||
(@make-struct-type #,reflect-name-expr
|
([struct-field-index
|
||||||
#,super-struct:
|
(lambda (stx)
|
||||||
#,(- (length fields) auto-count)
|
(syntax-case stx #,(map field-id fields)
|
||||||
#,auto-count
|
#,@(let loop ([fields fields][pos 0])
|
||||||
#,auto-val
|
(cond
|
||||||
#,(if (null? props)
|
[(null? fields) null]
|
||||||
#'null
|
[else (cons #`[(_ #,(field-id (car fields))) #'#,pos]
|
||||||
#`(list #,@(map (lambda (p)
|
(loop (cdr fields) (add1 pos)))]))
|
||||||
#`(cons #,(car p) #,(cdr p)))
|
[(_ name) (raise-syntax-error #f "no such field" stx #'name)]))])
|
||||||
props)))
|
(make-struct-type #,reflect-name-expr
|
||||||
#,(or inspector
|
#,super-struct:
|
||||||
#`(current-inspector))
|
#,(- (length fields) auto-count)
|
||||||
#f
|
#,auto-count
|
||||||
'#,(let loop ([i 0]
|
#,auto-val
|
||||||
[fields fields])
|
#,props
|
||||||
(cond
|
#,(or inspector #`(current-inspector))
|
||||||
[(null? fields) null]
|
#f
|
||||||
[(field-auto? (car fields)) null]
|
'#,(let loop ([i 0]
|
||||||
[(not (or mutable? (field-mutable? (car fields))))
|
[fields fields])
|
||||||
(cons i (loop (add1 i) (cdr fields)))]
|
(cond
|
||||||
[else (loop (add1 i) (cdr fields))]))
|
[(null? fields) null]
|
||||||
#,guard
|
[(field-auto? (car fields)) null]
|
||||||
'#,(if ctor-only? ctor-name id)))])
|
[(not (or mutable? (field-mutable? (car fields))))
|
||||||
(values struct: make- ?
|
(cons i (loop (add1 i) (cdr fields)))]
|
||||||
|
[else (loop (add1 i) (cdr fields))]))
|
||||||
|
#,guard
|
||||||
|
'#,(if ctor-only? ctor-name id)))]
|
||||||
|
[(@?)
|
||||||
|
(@struct-predicate #,super-struct: ? make- -ref -set!
|
||||||
|
#,(- (length fields) auto-count)
|
||||||
|
#,(null? sets) (eq? #f #,(or inspector #`(current-inspector)))
|
||||||
|
#,proc? #,equal+hash)])
|
||||||
|
|
||||||
|
(values struct: make- @?
|
||||||
#,@(let loop ([i 0][fields fields])
|
#,@(let loop ([i 0][fields fields])
|
||||||
(if (null? fields)
|
(if (null? fields)
|
||||||
null
|
null
|
||||||
(cons #`(@make-struct-field-accessor struct: #,i '#,(field-id (car fields)))
|
(cons #`(@make-struct-field-accessor @? #,i '#,(field-id (car fields)))
|
||||||
(loop (add1 i) (cdr fields)))))
|
(loop (add1 i) (cdr fields)))))
|
||||||
#,@(let loop ([i 0][fields fields])
|
#,@(let loop ([i 0][fields fields])
|
||||||
(if (null? fields)
|
(if (null? fields)
|
||||||
null
|
null
|
||||||
(if (not (or mutable? (field-mutable? (car fields))))
|
(if (not (or mutable? (field-mutable? (car fields))))
|
||||||
(loop (add1 i) (cdr fields))
|
(loop (add1 i) (cdr fields))
|
||||||
(cons #`(@make-struct-field-mutator struct: #,i '#,(field-id (car fields)))
|
(cons #`(@make-struct-field-mutator @? #,i '#,(field-id (car fields)))
|
||||||
(loop (add1 i) (cdr fields)))))))))))]
|
(loop (add1 i) (cdr fields)))))))))))]
|
||||||
[compile-time-defns
|
[compile-time-defns
|
||||||
(lambda ()
|
(lambda ()
|
||||||
|
|
@ -748,22 +691,20 @@
|
||||||
#,(run-time-defns))
|
#,(run-time-defns))
|
||||||
;; Other contexts: order should't matter:
|
;; Other contexts: order should't matter:
|
||||||
#`(begin
|
#`(begin
|
||||||
#,(run-time-defns)
|
|
||||||
#,(compile-time-defns)))]
|
#,(compile-time-defns)
|
||||||
|
#,(run-time-defns) ))]
|
||||||
[omit-define-syntaxes?
|
[omit-define-syntaxes?
|
||||||
(run-time-defns)]
|
(run-time-defns)]
|
||||||
[omit-define-values?
|
[omit-define-values?
|
||||||
(compile-time-defns)]
|
(compile-time-defns)]
|
||||||
[else #'(begin)])])
|
[else #'(begin)])])
|
||||||
(syntax-protect
|
(syntax-protect
|
||||||
(syntax-property
|
(if super-id
|
||||||
(if super-id
|
(syntax-property result
|
||||||
(syntax-property result
|
'disappeared-use
|
||||||
'disappeared-use
|
(syntax-local-introduce super-id))
|
||||||
(syntax-local-introduce super-id))
|
result))))))))))]
|
||||||
result)
|
|
||||||
'sub-range-binders
|
|
||||||
all-directives))))))))))]
|
|
||||||
[(_ _ id . _)
|
[(_ _ id . _)
|
||||||
(not (or (identifier? #'id)
|
(not (or (identifier? #'id)
|
||||||
(and (syntax->list #'id)
|
(and (syntax->list #'id)
|
||||||
|
|
@ -798,155 +739,3 @@
|
||||||
"bad syntax"
|
"bad syntax"
|
||||||
stx)]))
|
stx)]))
|
||||||
|
|
||||||
(define-syntax (struct-copy stx)
|
|
||||||
(if (not (eq? (syntax-local-context) 'expression))
|
|
||||||
(quasisyntax/loc stx (#%expression #,stx))
|
|
||||||
(syntax-case stx ()
|
|
||||||
[(form-name info struct-expr field+val ...)
|
|
||||||
(let ([ans (syntax->list #'(field+val ...))])
|
|
||||||
;; Check syntax:
|
|
||||||
(unless (identifier? #'info)
|
|
||||||
(raise-syntax-error #f "not an identifier for structure type" stx #'info))
|
|
||||||
(for-each (lambda (an)
|
|
||||||
(syntax-case an ()
|
|
||||||
[(field val)
|
|
||||||
(unless (identifier? #'field)
|
|
||||||
(raise-syntax-error #f
|
|
||||||
"not an identifier for field name"
|
|
||||||
stx
|
|
||||||
#'field))]
|
|
||||||
[(field #:parent p val)
|
|
||||||
(unless (identifier? #'field)
|
|
||||||
(raise-syntax-error #f
|
|
||||||
"not an identifier for field name"
|
|
||||||
stx
|
|
||||||
#'field))
|
|
||||||
(unless (identifier? #'p)
|
|
||||||
(raise-syntax-error #f
|
|
||||||
"not an identifier for parent struct name"
|
|
||||||
stx
|
|
||||||
#'field))]
|
|
||||||
[_
|
|
||||||
(raise-syntax-error #f
|
|
||||||
(string-append
|
|
||||||
"bad syntax;\n"
|
|
||||||
" expected a field update of the form (<field-id> <expr>)\n"
|
|
||||||
" or (<field-id> #:parent <parent-id> <expr>)")
|
|
||||||
stx
|
|
||||||
an)]))
|
|
||||||
ans)
|
|
||||||
(let-values ([(construct pred accessors parent)
|
|
||||||
(let ([v (syntax-local-value #'info (lambda () #f))])
|
|
||||||
(unless (struct-info? v)
|
|
||||||
(raise-syntax-error #f "identifier is not bound to a structure type" stx #'info))
|
|
||||||
(let ([v (extract-struct-info v)])
|
|
||||||
(values (cadr v)
|
|
||||||
(caddr v)
|
|
||||||
(cadddr v)
|
|
||||||
(list-ref v 5))))])
|
|
||||||
|
|
||||||
(let* ([ensure-really-parent
|
|
||||||
(λ (id)
|
|
||||||
(let loop ([parent parent])
|
|
||||||
(cond
|
|
||||||
[(eq? parent #t)
|
|
||||||
(raise-syntax-error #f "identifier not bound to a parent struct" stx id)]
|
|
||||||
[(not parent)
|
|
||||||
(raise-syntax-error #f "parent struct information not known" stx id)]
|
|
||||||
[(free-identifier=? id parent) (void)]
|
|
||||||
[else
|
|
||||||
(let ([v (syntax-local-value parent (lambda () #f))])
|
|
||||||
(unless (struct-info? v)
|
|
||||||
(raise-syntax-error #f "unknown parent struct" stx id)) ;; probably won't happen(?)
|
|
||||||
(let ([v (extract-struct-info v)])
|
|
||||||
(loop (list-ref v 5))))])))]
|
|
||||||
[new-fields
|
|
||||||
(map (lambda (an)
|
|
||||||
(syntax-case an ()
|
|
||||||
[(field expr)
|
|
||||||
(list (datum->syntax #'field
|
|
||||||
(string->symbol
|
|
||||||
(format "~a-~a"
|
|
||||||
(syntax-e #'info)
|
|
||||||
(syntax-e #'field)))
|
|
||||||
#'field)
|
|
||||||
#'expr
|
|
||||||
(car (generate-temporaries (list #'field))))]
|
|
||||||
[(field #:parent id expr)
|
|
||||||
(begin
|
|
||||||
(ensure-really-parent #'id)
|
|
||||||
(list (datum->syntax #'field
|
|
||||||
(string->symbol
|
|
||||||
(format "~a-~a"
|
|
||||||
(syntax-e #'id)
|
|
||||||
(syntax-e #'field)))
|
|
||||||
#'field)
|
|
||||||
#'expr
|
|
||||||
(car (generate-temporaries (list #'field)))))]))
|
|
||||||
ans)]
|
|
||||||
|
|
||||||
;; new-binding-for : syntax[field-name] -> (union syntax[expression] #f)
|
|
||||||
[new-binding-for
|
|
||||||
(lambda (f)
|
|
||||||
(ormap (lambda (new-field)
|
|
||||||
(and (free-identifier=? (car new-field) f)
|
|
||||||
(caddr new-field)))
|
|
||||||
new-fields))])
|
|
||||||
|
|
||||||
(unless construct
|
|
||||||
(raise-syntax-error #f
|
|
||||||
"constructor not statically known for structure type"
|
|
||||||
stx
|
|
||||||
#'info))
|
|
||||||
(unless pred
|
|
||||||
(raise-syntax-error #f
|
|
||||||
"predicate not statically known for structure type"
|
|
||||||
stx
|
|
||||||
#'info))
|
|
||||||
(unless (andmap values accessors)
|
|
||||||
(raise-syntax-error #f
|
|
||||||
"not all accessors are statically known for structure type"
|
|
||||||
stx
|
|
||||||
#'info))
|
|
||||||
|
|
||||||
|
|
||||||
(let ([dests
|
|
||||||
(map (lambda (new-field)
|
|
||||||
(or (ormap (lambda (f2)
|
|
||||||
(and f2
|
|
||||||
(free-identifier=? (car new-field) f2)
|
|
||||||
f2))
|
|
||||||
accessors)
|
|
||||||
(raise-syntax-error #f
|
|
||||||
"accessor name not associated with the given structure type"
|
|
||||||
stx
|
|
||||||
(car new-field))))
|
|
||||||
new-fields)])
|
|
||||||
;; Check for duplicates using dests, not as, because mod=? as might not be id=?
|
|
||||||
(let ((dupe (check-duplicate-identifier dests)))
|
|
||||||
(when dupe
|
|
||||||
(raise-syntax-error #f
|
|
||||||
"duplicate field assignment"
|
|
||||||
stx
|
|
||||||
;; Map back to an original field:
|
|
||||||
(ormap (lambda (nf)
|
|
||||||
(and nf
|
|
||||||
(free-identifier=? dupe (car nf))
|
|
||||||
(car nf)))
|
|
||||||
(reverse new-fields)))))
|
|
||||||
|
|
||||||
;; the actual result
|
|
||||||
#`(let ((the-struct struct-expr))
|
|
||||||
(if (#,pred the-struct)
|
|
||||||
(let #,(map (lambda (new-field)
|
|
||||||
#`[#,(caddr new-field) #,(cadr new-field)])
|
|
||||||
new-fields)
|
|
||||||
(#,construct
|
|
||||||
#,@(map
|
|
||||||
(lambda (field) (or (new-binding-for field)
|
|
||||||
#`(#,field the-struct)))
|
|
||||||
(reverse accessors))))
|
|
||||||
(raise-argument-error 'form-name
|
|
||||||
#,(format "~a?" (syntax-e #'info))
|
|
||||||
the-struct)))))))])))
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,76 @@
|
||||||
|
#lang racket
|
||||||
|
|
||||||
|
(require racket/syntax racket/splicing )
|
||||||
|
(provide define-array array-procedure reshape split-at* list-ref*)
|
||||||
|
|
||||||
|
; Provides a macro for defining multidimensional arrays. The
|
||||||
|
; form (define-array var dims vals) defines a multidimensional
|
||||||
|
; array from a dimension specification and a flat list of values. The macro
|
||||||
|
; introduces a name transformer var. When used as an identifer
|
||||||
|
; the var transformer returns the array in the form of a list of
|
||||||
|
; lists. When used as a function, it takes a sequence of indices
|
||||||
|
; and returns the element (or a subarray) at the specified position.
|
||||||
|
;
|
||||||
|
; The dimension specification should be list of positive natural
|
||||||
|
; numbers. For example, '(3 2 3) specifies a 3x2x3 array. The
|
||||||
|
; vals list should contain exactly (apply * dims) values.
|
||||||
|
;
|
||||||
|
; The form (define-array var vals) assumes that vals is already a
|
||||||
|
; list of lists. It simply introduces a name transformer for var,
|
||||||
|
; as described above.
|
||||||
|
(define-syntax (define-array stx)
|
||||||
|
(syntax-case stx ()
|
||||||
|
[(_ id dims vals)
|
||||||
|
#`(define-array id (reshape dims vals))]
|
||||||
|
[(_ id vals)
|
||||||
|
#`(splicing-let ([array (array-procedure vals)])
|
||||||
|
(define-syntax id
|
||||||
|
(syntax-id-rules (set!)
|
||||||
|
[(set! id e)
|
||||||
|
(error 'set! "cannot modify an immutable reference: ~s" (syntax->datum #'id))]
|
||||||
|
[(id idx (... ...)) (array idx (... ...))]
|
||||||
|
[id (array)])))]))
|
||||||
|
|
||||||
|
; This macro expands to a procedure wrapper that allows
|
||||||
|
; the elements in the given nested list representation of
|
||||||
|
; an array to be accessed using a call of the form (vals idx ...).
|
||||||
|
; Applying the resulting procedure to no arguments yields the
|
||||||
|
; entire array (that is, list of lists).
|
||||||
|
(define-syntax-rule (array-procedure vals)
|
||||||
|
(let ([array vals])
|
||||||
|
(procedure-rename
|
||||||
|
(lambda pos
|
||||||
|
(apply list-ref* array pos))
|
||||||
|
(string->symbol (format "array~aD" (length array))))))
|
||||||
|
|
||||||
|
; This function returns a nested list representation
|
||||||
|
; of the given flat list using the given shape specification.
|
||||||
|
; The shape specification is a flat list of positive natural
|
||||||
|
; numbers. For example, '(3 2) specifies a nested list that
|
||||||
|
; corresponds to a 3x2 array in row major order, i.e.,
|
||||||
|
; (reshape '(3 2) '(0 1 2 3 4 5)) yields '((0 1 2) (3 4 5)).
|
||||||
|
; The behavior of this function is unspecified if the length of
|
||||||
|
; the vals list is not exactly (apply * dims).
|
||||||
|
(define (reshape dims vals)
|
||||||
|
(cond [(null? dims) null]
|
||||||
|
[(null? (cdr dims)) vals]
|
||||||
|
[else (let ([rest (cdr dims)])
|
||||||
|
(map (curry reshape (cdr dims)) (split-at* vals (apply * rest))))]))
|
||||||
|
|
||||||
|
; Splits a list of size k*n into k sublists of size n. The
|
||||||
|
; sublists are returned in a list. The behavior of this function
|
||||||
|
; is unspecified if the length of the list is not a multiple of n.
|
||||||
|
(define (split-at* vals n)
|
||||||
|
(if (null? vals)
|
||||||
|
null
|
||||||
|
(let-values ([(left right) (split-at vals n)])
|
||||||
|
(cons left (split-at* right n)))))
|
||||||
|
|
||||||
|
; Returns the value in the given nested list representation of a
|
||||||
|
; mulitdimensional array that is at the specified position. The
|
||||||
|
; value itself may be a list; for example, (list-ref* '((0 1) (2 3)) 0)
|
||||||
|
; produces '(0 1) while (list-ref* '((0 1) (2 3)) 1 0) produces 2.
|
||||||
|
(define (list-ref* vals . pos)
|
||||||
|
(if (null? pos)
|
||||||
|
vals
|
||||||
|
(apply list-ref* (list-ref vals (car pos)) (cdr pos))))
|
||||||
|
|
@ -0,0 +1,101 @@
|
||||||
|
#lang racket
|
||||||
|
|
||||||
|
(provide ord-dict? [rename-out (make-ordered-dictionary ord-dict)
|
||||||
|
(make-immutable-ordered-dictionary immutable-ord-dict)]
|
||||||
|
last-key last-value
|
||||||
|
first-key first-value
|
||||||
|
dict-take dict-drop)
|
||||||
|
|
||||||
|
(define (last-key dict)
|
||||||
|
(last (ord-dict-order dict)))
|
||||||
|
|
||||||
|
(define (last-value dict)
|
||||||
|
(ord-dict-ref dict (last-key dict)))
|
||||||
|
|
||||||
|
(define (first-key dict)
|
||||||
|
(first (ord-dict-order dict)))
|
||||||
|
|
||||||
|
(define (first-value dict)
|
||||||
|
(ord-dict-ref dict (first-key dict)))
|
||||||
|
|
||||||
|
(define (dict-take dict pos)
|
||||||
|
(sub-dict dict (take (order dict) pos)))
|
||||||
|
|
||||||
|
(define (dict-drop dict pos)
|
||||||
|
(sub-dict dict (drop (order dict) pos)))
|
||||||
|
|
||||||
|
(define (sub-dict dict sub-order)
|
||||||
|
(let ([tbl (table dict)])
|
||||||
|
(ord-dict (for/hash ([key sub-order]) (values key (dict-ref tbl key)))
|
||||||
|
sub-order)))
|
||||||
|
|
||||||
|
(define ord-dict-ref
|
||||||
|
(case-lambda [(dict key) (dict-ref (table dict) key)]
|
||||||
|
[(dict key failure-result) (dict-ref (table dict) key failure-result)]))
|
||||||
|
|
||||||
|
(define (ord-dict-set! dict key value)
|
||||||
|
(unless (dict-has-key? (table dict) key)
|
||||||
|
(set-order! dict (append (order dict) (list key))))
|
||||||
|
(dict-set! (table dict) key value))
|
||||||
|
|
||||||
|
(define (ord-dict-remove! dict key)
|
||||||
|
(when (dict-has-key? (table dict) key)
|
||||||
|
(set-order! dict (remove key (order dict)))
|
||||||
|
(dict-remove! (table dict) key)))
|
||||||
|
|
||||||
|
(define (ord-dict-count dict) (dict-count (table dict)))
|
||||||
|
|
||||||
|
(define (ord-dict-iterate-first dict)
|
||||||
|
(and (not (null? (order dict)))
|
||||||
|
(order dict)))
|
||||||
|
|
||||||
|
(define (ord-dict-iterate-next dict pos)
|
||||||
|
(and (not (null? pos))
|
||||||
|
(not (null? (cdr pos)))
|
||||||
|
(cdr pos)))
|
||||||
|
|
||||||
|
(define (ord-dict-iterate-key dict pos) (car pos))
|
||||||
|
|
||||||
|
(define (ord-dict-iterate-value dict pos)
|
||||||
|
(dict-ref (table dict) (car pos)))
|
||||||
|
|
||||||
|
(struct ord-dict (table [order #:mutable])
|
||||||
|
#:property prop:dict
|
||||||
|
(vector ord-dict-ref
|
||||||
|
ord-dict-set! #f
|
||||||
|
ord-dict-remove! #f
|
||||||
|
ord-dict-count
|
||||||
|
ord-dict-iterate-first ord-dict-iterate-next
|
||||||
|
ord-dict-iterate-key ord-dict-iterate-value)
|
||||||
|
#:property prop:custom-write
|
||||||
|
(lambda (self port mode)
|
||||||
|
(let ([order (order self)]
|
||||||
|
[table (table self)])
|
||||||
|
(fprintf port "ordered-dict~s" (map (lambda (key) (cons key (dict-ref table key))) order)))))
|
||||||
|
|
||||||
|
(struct immutable-ord-dict ord-dict ()
|
||||||
|
#:property prop:dict
|
||||||
|
(vector ord-dict-ref
|
||||||
|
#f #f
|
||||||
|
#f #f
|
||||||
|
ord-dict-count
|
||||||
|
ord-dict-iterate-first ord-dict-iterate-next
|
||||||
|
ord-dict-iterate-key ord-dict-iterate-value))
|
||||||
|
|
||||||
|
(define table ord-dict-table)
|
||||||
|
(define order ord-dict-order)
|
||||||
|
(define set-order! set-ord-dict-order!)
|
||||||
|
|
||||||
|
(define (make-ordered-dictionary [assocs null])
|
||||||
|
(ord-dict (make-hash assocs) (map car assocs)))
|
||||||
|
|
||||||
|
(define (make-immutable-ordered-dictionary dict)
|
||||||
|
(if (immutable-ord-dict? dict)
|
||||||
|
dict
|
||||||
|
(let ([dict-hash (for/hash ([(key value) (in-dict dict)]) (values key value))])
|
||||||
|
(if (ord-dict? dict)
|
||||||
|
(immutable-ord-dict dict-hash (order dict))
|
||||||
|
(immutable-ord-dict dict-hash (for/list ([key (in-dict-keys dict)]) key))))))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
#lang scribble/manual
|
||||||
|
|
||||||
|
@(require (for-label
|
||||||
|
rosette/base/define racket)
|
||||||
|
scribble/core scribble/html-properties scribble/eval racket/sandbox
|
||||||
|
"../util/lifted.rkt")
|
||||||
|
|
||||||
|
@(define box-ops (select '(box? box box-immutable unbox set-box! box-cas!)))
|
||||||
|
|
||||||
|
@title[#:tag "sec:box"]{Boxes}
|
||||||
|
|
||||||
|
A box is a single (im)mutable storage cell, which behaves like a one-element (im)mutable @seclink["sec:vec"]{vector}.
|
||||||
|
Lifted box operations are shown below.
|
||||||
|
@tabular[#:style (style #f (list (attributes '((id . "lifted")(class . "boxed")))))
|
||||||
|
(list (list @box-ops))]
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
#lang scribble/manual
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@title[#:tag "ch:built-in-datatypes" #:style 'toc]{Built-In Datatypes}
|
||||||
|
|
||||||
|
The @seclink["ch:syntactic-forms"]{previous chapter} describes the
|
||||||
|
Racket syntax forms that are @tech[#:key "lifted constructs"]{lifted} by Rosette to
|
||||||
|
work on symbolic values.
|
||||||
|
This chapter describes the lifted datatypes and their corresponding operations. Most
|
||||||
|
lifted operations retain their Racket semantics, with the exception of
|
||||||
|
numeric functions (Section @seclink["sec:primitives"]{4.1}) and
|
||||||
|
equality predicates (Section @seclink["sec:equality"]{4.2}).
|
||||||
|
|
||||||
|
@(table-of-contents)
|
||||||
|
@include-section["primitives.scrbl"]
|
||||||
|
@include-section["equality.scrbl"]
|
||||||
|
@include-section["pairs.scrbl"]
|
||||||
|
@include-section["vectors.scrbl"]
|
||||||
|
@include-section["boxes.scrbl"]
|
||||||
|
@include-section["procedures.scrbl"]
|
||||||
|
@include-section["solvers+solutions.scrbl"]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
#lang scribble/manual
|
||||||
|
|
||||||
|
@(require (for-label racket))
|
||||||
|
|
||||||
|
|
||||||
|
@title[#:tag "ch:programmer-defined-datatypes" #:style 'toc]{Programmer-Defined Datatypes}
|
||||||
|
|
||||||
|
@seclink["ch:built-in-datatypes"]{Chapter 4} presents the built-in Racket datatypes that
|
||||||
|
are lifted by Rosette to work in the presence of symbolic values. This chapter introduces two mechanisms
|
||||||
|
for creating new programmer-defined datatypes: @seclink["sec:struct"]{structures} and
|
||||||
|
@seclink["sec:enum"]{enumerations}. Rosette structures lift Racket structures to work
|
||||||
|
with symbolic values. Enumerations are similar to Java's enums, and they
|
||||||
|
can also be used with solver-aided facilities.
|
||||||
|
|
||||||
|
@[table-of-contents]
|
||||||
|
@include-section["structs.scrbl"]
|
||||||
|
@include-section["enums.scrbl"]
|
||||||
|
|
@ -0,0 +1,93 @@
|
||||||
|
#lang scribble/manual
|
||||||
|
|
||||||
|
@(require (for-label
|
||||||
|
rosette/base/define rosette/solver/solution rosette/query/tools rosette/query/eval
|
||||||
|
rosette/base/term rosette/base/primitive rosette/base/enum
|
||||||
|
(only-in rosette/base/safe assert)
|
||||||
|
racket)
|
||||||
|
scribble/core scribble/html-properties scribble/eval racket/sandbox
|
||||||
|
"../util/lifted.rkt")
|
||||||
|
|
||||||
|
|
||||||
|
@(define rosette-eval (rosette-evaluator))
|
||||||
|
|
||||||
|
@declare-exporting[rosette/base/enum
|
||||||
|
#:use-sources
|
||||||
|
(rosette/base/enum)]
|
||||||
|
|
||||||
|
|
||||||
|
@title[#:tag "sec:enum"]{Enumerations}
|
||||||
|
|
||||||
|
An @deftech{enumerated datatype} is a type consisting of an ordered set of labeled concrete
|
||||||
|
elements. Enumerated types also contain symbolic values. A symbolic value of an enumerated
|
||||||
|
type evaluates to one of its concrete elements under a @racket[solution?] returned by a
|
||||||
|
solver-aided query. Like @seclink["sec:primitives"]{primitive datatypes}, enumerated types
|
||||||
|
include symbolic constants, which can be created using @racket[define-symbolic] or @racket[define-symbolic*].
|
||||||
|
|
||||||
|
@defform[(define-enum id labels)#:contracts
|
||||||
|
[(labels list?)]]{
|
||||||
|
Creates an enumerated type @var[id?] consisting of elements that are
|
||||||
|
labeled with the given list of @racket[labels]. The label values must be
|
||||||
|
distinct according to @racket[equal?], and they must be immutable. Elements
|
||||||
|
of the resulting type are ordered according to the @racket[labels] list, so that
|
||||||
|
the i@superscript{th} element has the i@superscript{th} label.
|
||||||
|
|
||||||
|
Elements of @var[id?] are recognized by the predicate @var[id?], and
|
||||||
|
they are ordered by the predicate @var[id<?]. The identifer @racket[id] is a bound to a
|
||||||
|
procedure that takes as input a label and returns the corresponding enum element.
|
||||||
|
@examples[#:eval rosette-eval
|
||||||
|
(define-enum suit '(club diamond heart spade))
|
||||||
|
(suit 'club)
|
||||||
|
(suit? (suit 'club))
|
||||||
|
(suit<? (suit 'diamond) (suit 'heart))
|
||||||
|
(define-symbolic s suit?)
|
||||||
|
(define env (solve (assert (suit<? s (suit 'diamond)))))
|
||||||
|
(evaluate s env)
|
||||||
|
(suit "club")
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
@section{Generic Operations on Enumerated Datatypes}
|
||||||
|
|
||||||
|
Rosette provides the following generic procedures for operating on enum types and
|
||||||
|
elements:
|
||||||
|
|
||||||
|
@defproc[(enum? [t any/c]) boolean?]{
|
||||||
|
Returns true iff @racket[t] is a concrete predicate that recognizes
|
||||||
|
memebers of an enumerated datatype.
|
||||||
|
@examples[#:eval rosette-eval
|
||||||
|
(define-enum suit '(club diamond heart spade))
|
||||||
|
(enum? suit?)
|
||||||
|
(enum? number?)
|
||||||
|
(define-symbolic b boolean?)
|
||||||
|
(enum? (if b suit? number?))
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
@defproc[(label [element any/c]) any/c]{
|
||||||
|
Returns the label of the given (concrete or symbolic) enum element, or throws an error
|
||||||
|
if the given value is not an element of an enumerated datatype.
|
||||||
|
@examples[#:eval rosette-eval
|
||||||
|
(define-enum rgb '(red green blue))
|
||||||
|
(label (rgb 'green))
|
||||||
|
(define-symbolic c rgb?)
|
||||||
|
(label c)
|
||||||
|
(label "green")
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
@defproc[(ordinal [element any/c]) natural/c]{
|
||||||
|
Returns the ordinal of the given (concrete or symbolic) enum element, or throws an error
|
||||||
|
if the given value is not an element of an enumerated datatype.
|
||||||
|
@examples[#:eval rosette-eval
|
||||||
|
(define-enum rgb '(red green blue))
|
||||||
|
(ordinal (rgb 'green))
|
||||||
|
(define-symbolic c rgb?)
|
||||||
|
(ordinal c)
|
||||||
|
(ordinal "green")
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@(kill-evaluator rosette-eval)
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
#lang scribble/manual
|
||||||
|
|
||||||
|
@(require (for-label
|
||||||
|
rosette/base/define racket)
|
||||||
|
scribble/core scribble/html-properties scribble/eval racket/sandbox
|
||||||
|
"../util/lifted.rkt")
|
||||||
|
|
||||||
|
|
||||||
|
@(define rosette-eval (rosette-evaluator))
|
||||||
|
|
||||||
|
@title[#:tag "sec:equality"]{Equality}
|
||||||
|
|
||||||
|
Rosette supports two generic equality predicates, @racket[eq?] and @racket[equal?].
|
||||||
|
The @racket[equal?] predicate follows the Racket semantics, extended to work with symbolic values.
|
||||||
|
In particular, two values are @racket[equal?] only when they are @racket[eq?], unless a more permissive
|
||||||
|
notion of @racket[equal?] is specified for a particular datatype.
|
||||||
|
|
||||||
|
@examples[#:eval rosette-eval
|
||||||
|
(equal? 1 #t)
|
||||||
|
(equal? (list 1) (list 1))
|
||||||
|
(equal? (box 1) (box 1))
|
||||||
|
(equal? (list (box 1)) (list (box 1)))
|
||||||
|
(define-symbolic n number?)
|
||||||
|
(equal? (box n) (box 1))]
|
||||||
|
|
||||||
|
The @racket[eq?] predicate follows the Racket semantics for primitive and mutable datatypes, but
|
||||||
|
not for transparent immutable datatypes, such as lists. Rosette treats instances of such datatypes as values,
|
||||||
|
while Racket treats them as references. Racket's @racket[eq?] therefore returns @racket[#f] when
|
||||||
|
given two instances of a transparent immutable type, regardless of their contents.
|
||||||
|
The lifted @racket[eq?], in contrast, returns @racket[#t] when the given instances have
|
||||||
|
@racket[eq?] contents.
|
||||||
|
|
||||||
|
@examples[#:eval rosette-eval
|
||||||
|
(eq? 1 1)
|
||||||
|
(eq? (list 1) (list 1))
|
||||||
|
(eq? (box 1) (box 1))
|
||||||
|
(eq? (list (box 1)) (list (box 1)))
|
||||||
|
(define-symbolic n number?)
|
||||||
|
(eq? n 1)]
|
||||||
|
|
||||||
|
@(kill-evaluator rosette-eval)
|
||||||
|
|
@ -0,0 +1,68 @@
|
||||||
|
#lang scribble/manual
|
||||||
|
|
||||||
|
@(require (for-label
|
||||||
|
rosette/base/define rosette/query/tools rosette/query/eval
|
||||||
|
rosette/base/term rosette/base/primitive
|
||||||
|
(only-in rosette/base/safe assert)
|
||||||
|
racket)
|
||||||
|
scribble/core scribble/html-properties scribble/eval racket/sandbox
|
||||||
|
"../util/lifted.rkt")
|
||||||
|
|
||||||
|
|
||||||
|
@(define rosette-eval (rosette-evaluator))
|
||||||
|
|
||||||
|
@(define pairs:constructors+selectors (select '(pair? null? cons car cdr null list? list list* build-list)))
|
||||||
|
@(define list-operations (select '(length list-ref list-tail append reverse)))
|
||||||
|
@(define list-iteration (select '(map andmap ormap for-each foldl foldr)))
|
||||||
|
@(define list-filtering (select '(filter remove remq remv remove* remq* remv* sort)))
|
||||||
|
@(define list-searching (select '(member memv memq memf findf assoc assv assq assf)))
|
||||||
|
@(define more-pair-ops (select '(caar cadr cdar cddr caaar caadr cadar caddr cdaar cdadr cddar cdddr caaaar caaadr caadar caaddr cadaar cadadr caddar cadddr cdaaar cdaadr cdadar cdaddr cddaar cddadr cdddar cddddr)))
|
||||||
|
@(define more-list-ops (select '(empty cons? empty? first rest second third fourth fifth sixth seventh eighth ninth tenth last last-pair make-list take drop split-at takef dropf splitf-at take-right drop-right split-at-right takef-right dropf-right splitf-at-right add-between append* flatten remove-duplicates filter-map count partition range append-map filter-not shuffle permutations in-permutations argmin argmax )))
|
||||||
|
|
||||||
|
@title[#:tag "sec:pair"]{Pairs and Lists}
|
||||||
|
|
||||||
|
A pair combines two values, and a list is either the
|
||||||
|
constant @racket[null] or a pair whose second
|
||||||
|
element is a list. Pairs and lists are immutable, and they may
|
||||||
|
be concrete or symbolic.
|
||||||
|
Two pairs are @racket[eq?] (resp. @racket[equal?])
|
||||||
|
if their corresponding elements are @racket[eq?] (resp. @racket[equal?]).
|
||||||
|
|
||||||
|
As values of @tech[#:key "composite datatype"]{composite datatypes}, symbolic pairs
|
||||||
|
and lists cannot be created
|
||||||
|
via @seclink["sec:symbolic-constants-and-assertions"]{@code{define-symbolic[*]}}.
|
||||||
|
Instead, they are created by applying pair- or list-producing procedures to symbolic inputs,
|
||||||
|
or by controlling the application of such procedures with symbolic values. This
|
||||||
|
pattern for creating non-primitive symbolic values generalizes to all non-primitive datatypes.
|
||||||
|
@examples[#:eval rosette-eval
|
||||||
|
(define-symbolic x y z n number?)
|
||||||
|
(code:line (define xs (take (list x y z) n)) (code:comment "(1) xs is a symbolic list"))
|
||||||
|
(define sol (solve (assert (null? xs))))
|
||||||
|
(evaluate xs sol)
|
||||||
|
(define sol
|
||||||
|
(solve (begin
|
||||||
|
(assert (= (length xs) 2))
|
||||||
|
(assert (not (equal? xs (reverse xs))))
|
||||||
|
(assert (equal? xs (sort xs <))))))
|
||||||
|
(evaluate xs sol)]
|
||||||
|
|
||||||
|
@examples[#:eval rosette-eval
|
||||||
|
(define-symbolic b boolean?)
|
||||||
|
(code:line (define p (if b (cons 1 2) (cons 4 #f))) (code:comment "(2) p is a symbolic pair"))
|
||||||
|
(define sol (solve (assert (boolean? (cdr p)))))
|
||||||
|
(evaluate p sol)
|
||||||
|
(define sol (solve (assert (odd? (car p)))))
|
||||||
|
(evaluate p sol)
|
||||||
|
]
|
||||||
|
|
||||||
|
Rosette lifts the following operations on pairs and lists:
|
||||||
|
@tabular[#:style (style #f (list (attributes '((id . "lifted")(class . "boxed")))))
|
||||||
|
(list (list @elem{Pair Operations} @pairs:constructors+selectors)
|
||||||
|
(list @elem{List Operations} @list-operations)
|
||||||
|
(list @elem{List Iteration} @list-iteration)
|
||||||
|
(list @elem{List Filtering} @list-filtering)
|
||||||
|
(list @elem{List Searching} @list-searching)
|
||||||
|
(list @elem{Additional Pair Operations} @more-pair-ops)
|
||||||
|
(list @elem{Additional List Operations} @more-list-ops))]
|
||||||
|
|
||||||
|
@(kill-evaluator rosette-eval)
|
||||||
|
|
@ -0,0 +1,82 @@
|
||||||
|
#lang scribble/manual
|
||||||
|
|
||||||
|
@(require (for-label
|
||||||
|
rosette/base/define rosette/query/tools rosette/query/eval rosette/solver/solution
|
||||||
|
rosette/base/term (only-in rosette/base/num current-bitwidth)
|
||||||
|
(only-in rosette/base/safe assert)
|
||||||
|
(only-in rosette/base/assert asserts)
|
||||||
|
(only-in rosette/base/enum enum?)
|
||||||
|
(only-in rosette/base/base << >> >>>))
|
||||||
|
(for-label racket)
|
||||||
|
scribble/core scribble/html-properties scribble/eval racket/sandbox
|
||||||
|
"../util/lifted.rkt")
|
||||||
|
|
||||||
|
|
||||||
|
@(define rosette-eval (rosette-evaluator))
|
||||||
|
|
||||||
|
@(define bools (select '(boolean? not false? true false boolean=? nand nor implies xor)))
|
||||||
|
|
||||||
|
@(define nums (select '(number? complex? real? rational? integer? exact-integer? exact-nonnegative-integer? exact-positive-integer? inexact-real? fixnum? flonum? double-flonum? single-flonum? zero? positive? negative? even? odd? exact? inexact? inexact->exact exact->inexact real->single-flonum real->double-flonum + - * / quotient remainder quotient/ modulo add1 sub1 abs max min gcd lcm round floor ceiling truncate numerator denominator rationalize = < <= > >= sqrt integer-sqrt integer-sqrt/ expt exp log sin cos tan asin acos atan make-rectangular make-polar real-part imag-part magnitude angle bitwise-ior bitwise-and bitwise-xor bitwise-not bitwise-bit-set? bitwise-bit-field arithmetic-shift integer-length random random-seed make-pseudo-random-generator pseudo-random-generator? current-pseudo-random-generator pseudo-random-generator->vector vector->pseudo-random-generator vector->pseudo-random-generator! pseudo-random-generator-vector? number->string string->number real->decimal-string integer-bytes->integer integer->integer-bytes floating-point-bytes->real real->floating-point-bytes system-big-endian? pi pi.f degrees->radians radians->degrees sqr sgn conjugate sinh cosh tanh exact-round exact-floor exact-ceiling exact-truncate order-of-magnitude nan? infinite?)))
|
||||||
|
|
||||||
|
|
||||||
|
@title[#:tag "sec:primitives"]{Booleans and Numbers}
|
||||||
|
|
||||||
|
@declare-exporting[rosette/base/base #:use-sources (rosette/base/num rosette/base/base)]
|
||||||
|
|
||||||
|
Rosette divides built-in datatypes into two kinds: @deftech[#:key "primitive datatype"]{primitive} and
|
||||||
|
@deftech[#:key "composite datatype"]{composite}. Both kinds of
|
||||||
|
datatypes include concrete Racket values and symbolic Rosette values, but only primitive
|
||||||
|
datatypes include symbolic constants, introduced by @seclink["sec:symbolic-constants-and-assertions"]{@code{define-symbolic[*]}}.
|
||||||
|
The boolean and number types are the sole primitive datatypes in Rosette. Values of these types are recognized
|
||||||
|
using the @racket[boolean?] and @racket[number?] predicates.
|
||||||
|
|
||||||
|
|
||||||
|
Rosette lifts the following operations on primitive datatypes, including a few additional operations on
|
||||||
|
numbers (@defidentifier[#'>>], @defidentifier[#'>>>], @defidentifier[#'<<]) that have their usual meaning from C or Java:
|
||||||
|
@tabular[#:style (style #f (list (attributes '((id . "lifted")(class . "boxed")))))
|
||||||
|
(list (list @elem{Booleans} @bools)
|
||||||
|
(list @elem{Numbers} @elem{@nums, @racket[>>], @racket[>>>], @racket[<<]}))]
|
||||||
|
|
||||||
|
|
||||||
|
Lifted boolean operations retain their Racket semantics on both concrete and symbolic values.
|
||||||
|
In particular, Rosette extends the intepretation of these operations to work on symbolic values in (logically) the
|
||||||
|
same way that they work on concrete values.
|
||||||
|
|
||||||
|
@examples[#:eval rosette-eval
|
||||||
|
(define-symbolic b boolean?)
|
||||||
|
(boolean? b)
|
||||||
|
(boolean? #t)
|
||||||
|
(boolean? #f)
|
||||||
|
(boolean? 1)
|
||||||
|
(code:line (not b) (code:comment "produces a logical negation of b"))]
|
||||||
|
|
||||||
|
Lifted numeric operations, in contrast, only match their Racket semantics when applied to concrete values.
|
||||||
|
Symbolic numbers are treated as signed finite precision integers, and all operations
|
||||||
|
that involve symbolic numbers employ finite (rather than arbitrary) precision computations.
|
||||||
|
Applying an operation to a concrete and a symbolic number implicitly coerces the concrete
|
||||||
|
number to a finite integer representation.
|
||||||
|
|
||||||
|
@examples[#:eval rosette-eval
|
||||||
|
(+ 4.584294 pi)
|
||||||
|
(define-symbolic n number?)
|
||||||
|
(code:line (define sol (solve (assert (= n pi)))) (code:comment "pi is coerced to 3,"))
|
||||||
|
(code:line (evaluate n sol) (code:comment "so n is bound to 3"))]
|
||||||
|
|
||||||
|
@defparam[current-bitwidth bitwidth (and/c integer? positive?)
|
||||||
|
#:value 5]{
|
||||||
|
The @racket[current-bitwidth]
|
||||||
|
parameter controls the precision of numeric operations on symbolic values, by specifying the number of bits in
|
||||||
|
the signed representation of
|
||||||
|
a symbolic number. Default is 5 bits. This parameter should be kept as
|
||||||
|
small as possible to ensure faster evaluation of @seclink["sec:queries"]{solver-aided queries}.
|
||||||
|
As a general rule, it should also be set once, before any numeric operations are evaluated.
|
||||||
|
@examples[#:eval rosette-eval
|
||||||
|
(code:line (current-bitwidth 4) (code:comment "use 4-bit precision for symbolic operations"))
|
||||||
|
(define sol
|
||||||
|
(solve (begin (assert (> n 0))
|
||||||
|
(assert (< (add1 n) 0)))))
|
||||||
|
(code:line (evaluate n sol) (code:comment "7 + 1 = -8 in 4-bit signed representation"))
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
@(kill-evaluator rosette-eval)
|
||||||
|
|
@ -1,23 +1,22 @@
|
||||||
#lang scribble/manual
|
#lang scribble/manual
|
||||||
|
|
||||||
@(require (for-label
|
@(require (for-label
|
||||||
rosette/base/form/define rosette/query/query
|
rosette/base/define rosette/query/tools rosette/query/eval
|
||||||
rosette/base/core/term
|
rosette/base/term rosette/base/primitive
|
||||||
(only-in rosette/base/base assert)
|
(only-in rosette/base/safe assert)
|
||||||
racket)
|
racket)
|
||||||
scribble/core scribble/html-properties scribble/examples racket/sandbox racket/runtime-path
|
scribble/core scribble/html-properties scribble/eval racket/sandbox
|
||||||
"../util/lifted.rkt")
|
"../util/lifted.rkt")
|
||||||
|
|
||||||
|
|
||||||
@(define-runtime-path root ".")
|
@(define rosette-eval (rosette-evaluator))
|
||||||
@(define rosette-eval (rosette-log-evaluator (logfile root "procedures-log")))
|
|
||||||
|
|
||||||
@(define proc-ops (select '(procedure? apply compose compose1 procedure-rename procedure->method procedure-closure-contents-eq? )))
|
@(define proc-ops (select '(procedure? apply compose compose1 procedure-rename procedure->method procedure-closure-contents-eq? )))
|
||||||
@(define more-proc-ops (select '(identity const thunk thunk* negate curry curryr normalized-arity? normalize-arity arity=? arity-includes? prop:procedure)))
|
@(define more-proc-ops (select '(identity const thunk thunk* negate curry curryr normalized-arity? normalize-arity arity=? arity-includes? prop:procedure)))
|
||||||
|
|
||||||
@title[#:tag "sec:proc"]{Procedures}
|
@title[#:tag "sec:proc"]{Procedures}
|
||||||
|
|
||||||
Rosette procedures are references to procedure objects, just like in Racket.
|
Procedures are references to procedure objects, just like in Racket.
|
||||||
Two procedure references are @racket[eq?] and @racket[equal?] only if they point to the
|
Two procedure references are @racket[eq?] and @racket[equal?] only if they point to the
|
||||||
same procedure object. Procedures may be concrete or symbolic. Symbolic procedures
|
same procedure object. Procedures may be concrete or symbolic. Symbolic procedures
|
||||||
may, in the worst case, take as much time to execute as the slowest concrete procedure to
|
may, in the worst case, take as much time to execute as the slowest concrete procedure to
|
||||||
|
|
@ -26,8 +25,8 @@ which any symbolic procedure could @racket[evaluate] under any @racket[solution?
|
||||||
@(rosette-eval '(require (only-in racket string->symbol)))
|
@(rosette-eval '(require (only-in racket string->symbol)))
|
||||||
@examples[#:eval rosette-eval
|
@examples[#:eval rosette-eval
|
||||||
(define-symbolic b boolean?)
|
(define-symbolic b boolean?)
|
||||||
(define-symbolic x integer?)
|
(define-symbolic x number?)
|
||||||
(code:line (define p (if b * -)) (code:comment "p is a symbolic procedure."))
|
(code:line (define p (if b * -)) (code:comment "p is a symbolic procedure"))
|
||||||
(define sol (synthesize #:forall (list x)
|
(define sol (synthesize #:forall (list x)
|
||||||
#:guarantee (assert (= x (p x 1)))))
|
#:guarantee (assert (= x (p x 1)))))
|
||||||
(evaluate p sol)
|
(evaluate p sol)
|
||||||
|
|
@ -36,7 +35,7 @@ which any symbolic procedure could @racket[evaluate] under any @racket[solution?
|
||||||
(evaluate p sol)
|
(evaluate p sol)
|
||||||
]
|
]
|
||||||
|
|
||||||
Rosette lifts the following operations on procedures:
|
Rosette lifts the following procedure operations:
|
||||||
@tabular[#:style (style #f (list (attributes '((id . "lifted")(class . "boxed")))))
|
@tabular[#:style (style #f (list (attributes '((id . "lifted")(class . "boxed")))))
|
||||||
(list (list @elem{@proc-ops, @more-proc-ops}))]
|
(list (list @elem{@proc-ops, @more-proc-ops}))]
|
||||||
|
|
||||||
|
|
@ -0,0 +1,172 @@
|
||||||
|
#lang scribble/manual
|
||||||
|
|
||||||
|
@(require (for-label
|
||||||
|
rosette/solver/solver rosette/solver/solution rosette/query/state
|
||||||
|
rosette/solver/kodkod/kodkod (only-in rosette/query/debug debug)
|
||||||
|
rosette/solver/smt/z3 rosette/solver/smt/cvc4
|
||||||
|
rosette/base/define rosette/query/tools rosette/query/eval rosette/solver/solution
|
||||||
|
rosette/base/term (only-in rosette/base/num current-bitwidth) rosette/base/primitive
|
||||||
|
(only-in rosette/base/safe assert)
|
||||||
|
racket)
|
||||||
|
scribble/core scribble/html-properties scribble/eval racket/sandbox
|
||||||
|
"../util/lifted.rkt")
|
||||||
|
|
||||||
|
|
||||||
|
@(define rosette-eval (rosette-evaluator))
|
||||||
|
|
||||||
|
@title[#:tag "sec:solvers-and-solutions"]{Solvers and Solutions}
|
||||||
|
|
||||||
|
@declare-exporting[rosette/query/eval
|
||||||
|
rosette/solver/solver
|
||||||
|
rosette/solver/solution
|
||||||
|
rosette/query/state
|
||||||
|
rosette/solver/kodkod/kodkod
|
||||||
|
rosette/solver/smt/z3
|
||||||
|
rosette/solver/smt/cvc4
|
||||||
|
#:use-sources
|
||||||
|
(rosette/query/eval rosette/solver/solver rosette/solver/solution rosette/query/state rosette/solver/kodkod/kodkod rosette/solver/smt/z3 rosette/solver/smt/cvc4)]
|
||||||
|
|
||||||
|
A @deftech{solver} is an automatic reasoning engine, used to answer
|
||||||
|
@seclink["sec:queries"]{queries} about Rosette programs. The result of
|
||||||
|
a solver invocation is a @deftech{solution}, containing either
|
||||||
|
a @tech{binding} of symbolic constants to concrete values, or
|
||||||
|
an @tech[#:key "MUC"]{unsatisfiable core}.
|
||||||
|
Solvers and solutions may not be symbolic. Two solvers (resp. solutions) are @racket[eq?]/@racket[equal?]
|
||||||
|
if they refer to the same object.
|
||||||
|
|
||||||
|
@section{The Solver Interface and Classes}
|
||||||
|
|
||||||
|
|
||||||
|
@defparam[current-solver solver (is-a?/c solver<%>)]{
|
||||||
|
The @racket[current-solver] parameter holds the solver object used for
|
||||||
|
answering solver-aided queries. If a query requires creation of additional
|
||||||
|
temporary solvers, they all have the same @racket[class?] as the @racket[current-solver].
|
||||||
|
Supported solvers include @racket[kodkod%] and, if
|
||||||
|
@seclink["sec:get"]{installed}, @racket[z3%] and @racket[cvc4%].
|
||||||
|
@examples[#:eval rosette-eval
|
||||||
|
(eval:alts (current-solver) (display (current-solver)))
|
||||||
|
(require rosette/solver/smt/z3 rosette/solver/smt/cvc4 (only-in racket new))
|
||||||
|
(code:line (current-solver (new z3%)) (code:comment "change the current solver"))
|
||||||
|
(eval:alts (current-solver) (display (current-solver)))
|
||||||
|
(code:line (current-solver (new cvc4%)) (code:comment "change it again"))
|
||||||
|
(eval:alts (current-solver) (display (current-solver)))]
|
||||||
|
}
|
||||||
|
|
||||||
|
@(rosette-eval '(require rosette/solver/kodkod/kodkod))
|
||||||
|
@(rosette-eval '(current-solver (new kodkod%)))
|
||||||
|
|
||||||
|
@definterface[solver<%> ()
|
||||||
|
@elem{The solver interface specifies basic operations for
|
||||||
|
posing and answering questions about the satisfiability of a set of
|
||||||
|
formulas, expressed as (symbolic) boolean values. As a general rule,
|
||||||
|
Rosette programs should not invoke these operations directly. The recommended
|
||||||
|
way to access the solver is by posing @seclink["sec:queries"]{solver-aided queries}.}
|
||||||
|
@defmethod[(assert [formula boolean?]...) void?]{
|
||||||
|
Adds the given formulas to the solver's worklist.}
|
||||||
|
@defmethod[(clear) void?]{
|
||||||
|
Clears the solver's worklist.}
|
||||||
|
@defmethod[(solve) solution?]{
|
||||||
|
Searches for a binding from symbolic constants to concrete values that
|
||||||
|
satisfies all assertions in the solver's worklist. If such a binding---or, a @racket[model]---exists,
|
||||||
|
it is returned in the form of a satisfiable (@racket[sat?]) solution. Otherwise,
|
||||||
|
an unsatisfiable (@racket[unsat?]) solution is returned, but without
|
||||||
|
computing an unsatisfiable core. A solution with a core can be obtained by calling
|
||||||
|
@racket[debug] on @(this-obj). }
|
||||||
|
@defmethod[(debug) solution?]{
|
||||||
|
Searches for a minimal unsatisfiable core of the assertions in the solver's worklist.
|
||||||
|
If the worklist assertions are satisfiable, or @(this-obj) does
|
||||||
|
not support core extraction, an error is thrown. Otherwise, the result is an
|
||||||
|
@racket[unsat?] solution with a minimal @racket[core].}
|
||||||
|
]
|
||||||
|
|
||||||
|
@defmodule[#:multi (rosette/solver/kodkod/kodkod) #:no-declare #:use-sources (rosette/solver/kodkod/kodkod)]
|
||||||
|
@defclass[kodkod% object% (solver<%>)
|
||||||
|
@elem{A Rosette front-end to the @hyperlink["http://alloy.mit.edu/kodkod/"]{Kodkod} solver. This solver supports
|
||||||
|
minimal core extraction.}]
|
||||||
|
|
||||||
|
@defmodule[#:multi (rosette/solver/smt/z3) #:no-declare #:use-sources (rosette/solver/smt/z3)]
|
||||||
|
@defclass[z3% object% (solver<%>)
|
||||||
|
@elem{A Rosette front-end to the @hyperlink["http://z3.codeplex.com"]{Z3} solver from Microsoft.
|
||||||
|
This solver does not support minimal core extraction.}]
|
||||||
|
|
||||||
|
@defmodule[#:multi (rosette/solver/smt/cvc4) #:no-declare #:use-sources (rosette/solver/smt/cvc4)]
|
||||||
|
@defclass[cvc4% object% (solver<%>)
|
||||||
|
@elem{A Rosette front-end to the @hyperlink["http://cvc4.cs.nyu.edu/web/"]{CVC4} solver from NYU.
|
||||||
|
This solver does not support minimal core extraction.}]
|
||||||
|
|
||||||
|
|
||||||
|
@section{Satisfiable and Unsatisfiable Solutions}
|
||||||
|
|
||||||
|
A solution to a set of formulas consists of either a @racket[model],
|
||||||
|
if the formulas are satisfiable, or a @racket[core], if they are not.
|
||||||
|
The @racket[sat?] and @racket[unsat?] predicates recognize
|
||||||
|
satisfiable and unsatisfiable solutions, respectively. A satisfiable solution
|
||||||
|
can be used as a procedure: when applied to a bound symbolic constant, it returns
|
||||||
|
a concrete value for that constant; when applied to any other value, it returns
|
||||||
|
the value itself.
|
||||||
|
|
||||||
|
A solution supports the following operations:
|
||||||
|
|
||||||
|
@defproc[(solution? [value any/c]) boolean?]{
|
||||||
|
Returns true iff the given @racket[value] is a solution.}
|
||||||
|
|
||||||
|
@defproc[(sat? [solution solution?]) boolean?]{
|
||||||
|
Returns true iff the given @racket[solution] is satisfiable.}
|
||||||
|
|
||||||
|
@defproc[(unsat? [solution solution?]) boolean?]{
|
||||||
|
Returns true iff the given @racket[solution] is unsatisfiable.}
|
||||||
|
|
||||||
|
@defproc[(sat [binding (hash/c constant? any/c #:immutable #t)]) solution?]{
|
||||||
|
Returns a satisfiable solution that holds the given binding from symbolic
|
||||||
|
constants to values. The provided hashmap must bind every symbolic constant
|
||||||
|
in its keyset to a concrete value of the same type.
|
||||||
|
}
|
||||||
|
|
||||||
|
@defproc*[([(unsat) solution?]
|
||||||
|
[(unsat [a-core (listof boolean?)]) solution?])]{
|
||||||
|
Returns an unsatisfiable solution. If @racket[a-core] is provided,
|
||||||
|
it must be a list of boolean values that are collectively unsatisfiable.
|
||||||
|
Otherwise, the @racket[core] of the produced solution is
|
||||||
|
set to #f, to indicate that there is no satisfying solution but
|
||||||
|
core extraction was not performed. (Core extraction is an expensive
|
||||||
|
operation that is not supported by all solvers; those that do support it
|
||||||
|
usually don't compute a core unless explicitly asked for one.)}
|
||||||
|
|
||||||
|
@defproc[(empty-solution) solution?]{
|
||||||
|
Returns a satisfiable solution with an empty binding as a @racket[model].}
|
||||||
|
|
||||||
|
@defproc[(model [solution solution?]) (or/c (hash/c constant? any/c #:immutable #t) #f)]{
|
||||||
|
Returns the binding stored in the given solution. If the solution is
|
||||||
|
@racket[sat?], the binding is an immutable hashmap from symbolic constants
|
||||||
|
to values. Otherwise, the binding is @racket[#f].
|
||||||
|
}
|
||||||
|
|
||||||
|
@defproc[(core [solution solution?]) (or/c (listof (and/c constant? boolean?)) #f)]{
|
||||||
|
Returns unsatisfiable core stored in the given solution. If the solution is
|
||||||
|
@racket[unsat?] and a core was computed, the result is a list of boolean values that
|
||||||
|
are collectively unsatisfiable. Otherwise, the result is @racket[#f].
|
||||||
|
}
|
||||||
|
|
||||||
|
@defproc[(evaluate [value any/c] [solution (and/c solution? sat?)]) any/c]{
|
||||||
|
Given a Rosette value and a satisfiable solution, @racket[evaluate] produces a
|
||||||
|
new value obtained by replacing every symbolic constant @var[c] in @racket[value]
|
||||||
|
with @racket[(solution #, @var[c])] and simplifying the result.
|
||||||
|
@examples[#:eval rosette-eval
|
||||||
|
(define-symbolic a b boolean?)
|
||||||
|
(define-symbolic x y number?)
|
||||||
|
(define sol
|
||||||
|
(solve (begin (assert a)
|
||||||
|
(assert (= x 1))
|
||||||
|
(assert (= y 2)))))
|
||||||
|
(sat? sol)
|
||||||
|
(evaluate (list 4 5 x) sol)
|
||||||
|
(define v (vector a))
|
||||||
|
(evaluate v sol)
|
||||||
|
(code:line (eq? v (evaluate v sol)) (code:comment "evaluation produces a new vector"))
|
||||||
|
(evaluate (+ x y) sol)
|
||||||
|
(evaluate (and a b) sol)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
@(kill-evaluator rosette-eval)
|
||||||
|
|
||||||
|
|
@ -0,0 +1,67 @@
|
||||||
|
#lang scribble/manual
|
||||||
|
|
||||||
|
@(require (for-label
|
||||||
|
rosette/base/define rosette/query/tools rosette/query/eval
|
||||||
|
rosette/base/term rosette/base/primitive
|
||||||
|
(only-in rosette/base/safe assert)
|
||||||
|
racket racket/generic)
|
||||||
|
scribble/core scribble/html-properties scribble/eval racket/sandbox
|
||||||
|
"../util/lifted.rkt")
|
||||||
|
|
||||||
|
@(define rosette-eval (rosette-evaluator))
|
||||||
|
@(define prop-facilities (select '(make-struct-type-property struct-type-property? struct-type-property-accessor-procedure?)))
|
||||||
|
@(define props (select '(prop:arity-string prop:blame prop:chaperone-contract prop:chaperone-unsafe-undefined prop:checked-procedure prop:contract prop:contracted prop:custom-print-quotable prop:custom-write prop:dict prop:dict/contract prop:equal+hash prop:evt prop:exn:missing-module prop:exn:srclocs prop:flat-contract prop:impersonator-of prop:input-port prop:legacy-match-expander prop:liberal-define-context prop:match-expander prop:output-port prop:place-location prop:procedure prop:provide-pre-transformer prop:provide-transformer prop:rename-transformer prop:require-transformer prop:sequence prop:serializable prop:set!-transformer prop:stream prop:struct-auto-info prop:struct-info)))
|
||||||
|
@(define generics-facilities (select '(define-generics raise-support-error exn:fail:support define/generic generic-instance/c impersonate-generics chaperone-generics redirect-generics )))
|
||||||
|
@(define generics (select '(gen:custom-write gen:dict gen:equal+hash gen:set gen:stream)))
|
||||||
|
|
||||||
|
@title[#:tag "sec:struct"]{Structures}
|
||||||
|
|
||||||
|
A @deftech{structure type} is a record datatype that includes zero or more fields.
|
||||||
|
A @deftech{structure} is an instance of a structure type; it is a first-class value
|
||||||
|
that maps each field of its type to a value. Structure types are defined
|
||||||
|
using Racket's @racket[struct] syntax. Defining a structure type in this way also
|
||||||
|
defines the necessary procedures for creating instances of that type and for accessing
|
||||||
|
their fields.
|
||||||
|
|
||||||
|
|
||||||
|
Rosette structures can be concrete or symbolic. Their semantics matches that of Racket,
|
||||||
|
with one important exception: immutable transparent structures are treated as values
|
||||||
|
rather than references. This @seclink["sec:equality"]{means} that two such structures are
|
||||||
|
@racket[eq?] if they belong to the same type and their corresponding field values are @racket[eq?].
|
||||||
|
|
||||||
|
@examples[#:eval rosette-eval
|
||||||
|
(eval:alts (code:line (struct point (x y) #:transparent) (code:comment "immutable transparent type")) (void))
|
||||||
|
(eval:alts (code:line (eq? (point 1 2) (point 1 2)) (code:comment "point structures are values")) #t)
|
||||||
|
(eval:alts (code:line (struct pt (x y)) (code:comment "opaque immutable type")) (void))
|
||||||
|
(eval:alts (code:line (eq? (pt 1 2) (pt 1 2)) (code:comment "pt structures are references")) #f)
|
||||||
|
(eval:alts (code:line (struct pnt (x y) #:mutable #:transparent) (code:comment "mutable transparent type")) (void))
|
||||||
|
(eval:alts (code:line (eq? (pnt 1 2) (pnt 1 2)) (code:comment "pnt structures are references")) #f)]
|
||||||
|
|
||||||
|
Like @tech[#:key "composite datatype"]{composite built-in datatypes},
|
||||||
|
symbolic structures cannot be created using @racket[define-symbolic]. Instead,
|
||||||
|
they are created implicitly, by, for example, using an @racket[if] expression
|
||||||
|
together with a symbolic value.
|
||||||
|
|
||||||
|
@(rosette-eval '(require (only-in racket [struct racket/struct])))
|
||||||
|
@examples[#:eval rosette-eval
|
||||||
|
(eval:alts (code:line (struct point (x y) #:transparent) (code:comment "immutable transparent type"))
|
||||||
|
(racket/struct point (x y) #:transparent))
|
||||||
|
(define-symbolic b boolean?)
|
||||||
|
(eval:alts (code:line (define p (if b (point 1 2) (point 3 4))) (code:comment "p holds a symbolic structure"))
|
||||||
|
(define p (if b (cons 1 2) (cons 3 4))))
|
||||||
|
(eval:alts (point-x p) (car p))
|
||||||
|
(eval:alts (point-y p) (cdr p))
|
||||||
|
(eval:alts (define env (solve (assert (= (point-x p) 3)))) (define env (solve (assert (= (car p) 3)))))
|
||||||
|
(eval:alts (evaluate p env) (point 3 4))]
|
||||||
|
|
||||||
|
@section{Structure Type Properties and Generic Interfaces}
|
||||||
|
|
||||||
|
In addition to lifting the @racket[struct] syntax, Rosette also lifts the following structure
|
||||||
|
properties, generic interfaces, and facilities for defining new properties and interfaces:
|
||||||
|
@tabular[#:style (style #f (list (attributes '((id . "lifted")(class . "boxed")))))
|
||||||
|
(list (list @elem{Defining Properties} @elem{@prop-facilities})
|
||||||
|
(list @elem{Lifted Properties} @elem{@props})
|
||||||
|
(list @elem{Defining Generics} @elem{@generics-facilities})
|
||||||
|
(list @elem{Lifted Generics} @elem{@generics} ))]
|
||||||
|
|
||||||
|
@(kill-evaluator rosette-eval)
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
#lang rosette/safe
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
(define-values (prop:foo foo? foo-value) (make-struct-type-property 'foo))
|
||||||
|
|
||||||
|
(struct point (x y) #:transparent #:property prop:foo 3)
|
||||||
|
|
||||||
|
(define-symbolic b boolean?)
|
||||||
|
(define p (if b (point 1 2) (point 3 4)))
|
||||||
|
(foo? p)
|
||||||
|
(foo-value p)
|
||||||
|
|
||||||
|
(eq? (point 1 2) (point 1 2))
|
||||||
|
|
||||||
|
(evaluate p (solve (assert (= (point-x p) 3))))
|
||||||
|
|
||||||
|
(struct pt (x y))
|
||||||
|
(eq? (pt 1 2) (pt 1 2))
|
||||||
|
|
||||||
|
(struct farm (x)
|
||||||
|
#:methods gen:equal+hash
|
||||||
|
[(define (equal-proc self f rec) (and (rec (farm-x self) (farm-x f))))
|
||||||
|
(define (hash-proc self rec) 1)
|
||||||
|
(define (hash2-proc self rec) 2)])
|
||||||
|
|
||||||
|
(define-enum suit '(club diamond heart spade))
|
||||||
|
(suit 'club)
|
||||||
|
(define-symbolic s suit?)
|
||||||
|
(label s)
|
||||||
|
(ordinal s)
|
||||||
|
(label (if b (suit 'club) 3))
|
||||||
|
|
||||||
|
(define env (solve (assert (suit<? s (suit 'diamond)))))
|
||||||
|
(evaluate s env)
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
#lang scribble/manual
|
||||||
|
|
||||||
|
@(require (for-label
|
||||||
|
rosette/base/define rosette/query/tools rosette/query/eval
|
||||||
|
rosette/base/term rosette/base/primitive
|
||||||
|
(only-in rosette/base/safe assert)
|
||||||
|
racket)
|
||||||
|
scribble/core scribble/html-properties scribble/eval racket/sandbox
|
||||||
|
"../util/lifted.rkt")
|
||||||
|
|
||||||
|
|
||||||
|
@(define rosette-eval (rosette-evaluator))
|
||||||
|
|
||||||
|
@(define vector-ops (select '(vector? make-vector vector vector-immutable vector-length vector-ref vector-set! vector->list list->vector vector->immutable-vector vector-fill! vector-copy! vector->values build-vector immutable?)))
|
||||||
|
@(define more-vector-ops (select '(vector-set*! vector-map vector-map! vector-append vector-take vector-take-right vector-drop vector-drop-right vector-split-at vector-split-at-right vector-copy vector-filter vector-filter-not vector-count vector-argmin vector-argmax vector-member vector-memv vector-memq)))
|
||||||
|
|
||||||
|
|
||||||
|
@title[#:tag "sec:vec"]{Vectors}
|
||||||
|
|
||||||
|
A vector is a fixed-length (im)mutable array.
|
||||||
|
Vectors may be concrete or symbolic, and they may be accessed using concrete
|
||||||
|
or symbolic indices. A concrete vector supports constant-time access for
|
||||||
|
concrete slot indices, and linear-time access for symbolic slot indices.
|
||||||
|
A symbolic vector supports (worst-case) linear- and quadratic-time access for concrete and
|
||||||
|
symbolic indices, respectively. Access time for symbolic vectors is given with
|
||||||
|
respect to the longest possible concrete array to which any symbolic vector
|
||||||
|
could @racket[evaluate] under any @racket[solution?].
|
||||||
|
|
||||||
|
Like @seclink["sec:pair"]{pairs and lists}, immutable vectors are values: two such vectors are @racket[eq?] if
|
||||||
|
they have the same length and @racket[eq?] contents. Mutable vectors are references
|
||||||
|
rather than values, and two mutable vectors are @racket[eq?] if and only if they
|
||||||
|
point to the same array object. Two vectors (regardless of mutability) are @racket[equal?]
|
||||||
|
if they have the same length and @racket[equal?] contents.
|
||||||
|
|
||||||
|
@examples[#:eval rosette-eval
|
||||||
|
(define v1 (vector 1 2 #f))
|
||||||
|
(define v2 (vector 1 2 #f))
|
||||||
|
(eq? v1 v2)
|
||||||
|
(equal? v1 v2)
|
||||||
|
(define v3 (vector-immutable 1 2 #f))
|
||||||
|
(define v4 (vector-immutable 1 2 #f))
|
||||||
|
(eq? v3 v4)
|
||||||
|
(equal? v1 v3)
|
||||||
|
]
|
||||||
|
|
||||||
|
@examples[#:eval rosette-eval
|
||||||
|
(define-symbolic x y z n number?)
|
||||||
|
(code:line (define xs (take (list x y z) n)) (code:comment "xs is a symbolic list"))
|
||||||
|
(code:line (define vs (list->vector xs)) (code:comment "vs is a symbolic vector"))
|
||||||
|
(define sol (solve (assert (= 4 (vector-ref vs (sub1 n))))))
|
||||||
|
(evaluate vs sol)
|
||||||
|
(evaluate xs sol)]
|
||||||
|
|
||||||
|
The following vector operations are lifted to work on both concrete and symbolic values:
|
||||||
|
@tabular[#:style (style #f (list (attributes '((id . "lifted")(class . "boxed")))))
|
||||||
|
(list (list @elem{@vector-ops, @more-vector-ops}))]
|
||||||
|
|
||||||
|
@(kill-evaluator rosette-eval)
|
||||||
|
|
@ -0,0 +1,241 @@
|
||||||
|
#lang scribble/manual
|
||||||
|
|
||||||
|
@(require (for-label racket)
|
||||||
|
(for-label
|
||||||
|
rosette/base/define (only-in rosette/base/safe assert)
|
||||||
|
rosette/query/tools
|
||||||
|
(except-in rosette/query/debug false true) rosette/query/eval
|
||||||
|
(only-in rosette/lib/meta/constructs ??) rosette/lib/meta/display rosette/lib/tools/render))
|
||||||
|
|
||||||
|
@(require racket/sandbox racket/runtime-path
|
||||||
|
scribble/eval scriblib/footnote
|
||||||
|
(only-in racket [unsyntax racket/unsyntax])
|
||||||
|
(only-in racket/draw read-bitmap))
|
||||||
|
|
||||||
|
@(require (only-in "../refs.scrbl" ~cite rosette:onward13 rosette:pldi14))
|
||||||
|
@(require "../util/lifted.rkt")
|
||||||
|
|
||||||
|
@(define-runtime-path dbg "pict.png")
|
||||||
|
|
||||||
|
@(require scribble/core)
|
||||||
|
|
||||||
|
@(define (symbolic s) @racketresultfont[s])
|
||||||
|
|
||||||
|
@(define rosette-eval (rosette-evaluator))
|
||||||
|
|
||||||
|
|
||||||
|
@(rosette-eval '(require (only-in racket hash)))
|
||||||
|
@(define-footnote footnote footnote-part)
|
||||||
|
|
||||||
|
@title[#:tag "ch:essentials"]{Rosette Essentials}
|
||||||
|
|
||||||
|
Rosette adds to Racket a collection of solver-aided facilities.
|
||||||
|
These facilities enable programmers to conveniently access a constraint solver
|
||||||
|
that can answer interesting questions about program behaviors. They are based on three
|
||||||
|
key concepts: @emph{symbolic values}, @emph{assertions} and @emph{queries}.
|
||||||
|
We use assertions to express desired program behaviors and symbolic values to
|
||||||
|
formulate queries about these behaviors.
|
||||||
|
|
||||||
|
This chapter illustrates the basics of solver-aided programming with a
|
||||||
|
few simple examples. More advanced tutorials, featuring extended examples, can be found
|
||||||
|
in Section 2 of @~cite[rosette:onward13 rosette:pldi14].
|
||||||
|
|
||||||
|
The following chapters describe the subset
|
||||||
|
of Racket that can be @seclink["sec:langs"]{safely} used with solver-aided facilities, including the
|
||||||
|
supported datatypes (both @seclink["ch:built-in-datatypes"]{built-in}
|
||||||
|
and @seclink["ch:programmer-defined-datatypes"]{programmer-defined}),
|
||||||
|
@seclink["ch:syntactic-forms"]{syntactic forms}, and @seclink["ch:libraries"]{libraries}.
|
||||||
|
|
||||||
|
@section[#:tag "sec:symbolic-values"]{Symbolic Values}
|
||||||
|
|
||||||
|
The Rosette language includes two kinds of values: concrete and symbolic. Concrete values are plain Racket values (@racket[#t], @racket[#f], @racket[0], @racket[1], etc.), and Rosette programs that operate only on concrete values behave just like Racket programs. Accessing the solver-aided features of Rosette---such as code synthesis or verification---requires the use of symbolic values.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@deftech[#:key "symbolic constant"]{Symbolic constants} are the simplest kind of symbolic value. They can be created using the @racket[define-symbolic] form:
|
||||||
|
@def+int[#:eval rosette-eval
|
||||||
|
(define-symbolic b boolean?)
|
||||||
|
b]
|
||||||
|
This generates a fresh symbolic constant of type boolean and binds it to the variable @racket[b].
|
||||||
|
|
||||||
|
You can think of a symbolic constant as a placeholder for a concrete constant of the same type. As we will see shortly, the solver, once called, determines which concrete value a given symbolic constant represents: it will tell us whether the constant @symbolic{b} is @racket[#t] or @racket[#f], depending on what question we ask about the behavior of a program (or a procedure) applied to @symbolic{b}.
|
||||||
|
|
||||||
|
Symbolic values, including constants, can be used just like concrete values of the same type. They can be stored in data structures or passed to procedures to obtain other values, either concrete or symbolic:
|
||||||
|
@interaction[#:eval rosette-eval
|
||||||
|
(boolean? b)
|
||||||
|
(number? b)
|
||||||
|
(vector b 1)
|
||||||
|
(not b)
|
||||||
|
(boolean? (not b))]
|
||||||
|
In our example, all but the fourth expression produce concrete values. The fourth expression returns another symbolic value---specifically, a symbolic @emph{expression} of type boolean. This expression represents the negation of @symbolic{b}. If the solver determines that @symbolic{b} is @racket[#t], for example, then @symbolic{(! b)} will be interpreted as @racket[#f].
|
||||||
|
|
||||||
|
|
||||||
|
Rosette provides one more construct for creating symbolic constants besides @racket[define-symbolic]:
|
||||||
|
@def+int[#:eval rosette-eval
|
||||||
|
(define-symbolic* n number?)]
|
||||||
|
The two constructs differ in how they bind variables to constants when evaluated more than once.
|
||||||
|
The @racket[define-symbolic] form binds the variable to the same (unique) constant every time it is evaluated. The @racket[define-symbolic*] form, in contrast, creates a stream of (unique) constants, binding the variable to the next constant from its stream whenever the form is evaluated. The following example illustrates the difference:
|
||||||
|
@defs+int[#:eval rosette-eval
|
||||||
|
((define (static)
|
||||||
|
(define-symbolic x boolean?) (code:comment "creates the same constant when evaluated")
|
||||||
|
x)
|
||||||
|
|
||||||
|
(define (dynamic)
|
||||||
|
(define-symbolic* y number?) (code:comment "creates a different constant when evaluated")
|
||||||
|
y))
|
||||||
|
|
||||||
|
(eq? (static) (static))
|
||||||
|
(eq? (dynamic) (dynamic))]
|
||||||
|
|
||||||
|
Printed constant names, such as @symbolic{x} or @symbolic{b}, are just comments. Two constants created by evaluating two distinct @racket[define-symbolic] (or, @racket[define-symbolic*]) forms are distinct, even if they have the same printed name. They may still represent the same concrete value, but that is determined by the solver:
|
||||||
|
|
||||||
|
@def+int[#:eval rosette-eval
|
||||||
|
(define (yet-another-x)
|
||||||
|
(define-symbolic x boolean?)
|
||||||
|
x)
|
||||||
|
|
||||||
|
; Produces a boolean expression whose meaning is 'true' if and only if the
|
||||||
|
; constant returned by (static) and the constant returned by (yet-another-x)
|
||||||
|
; have the same concrete interpretation.
|
||||||
|
(eq? (static) (yet-another-x))]
|
||||||
|
|
||||||
|
|
||||||
|
@section[#:tag "sec:asserts"]{Assertions}
|
||||||
|
|
||||||
|
Like many other languages, Rosette provides a construct for expressing @emph{assertions}---important properties of programs that are checked in every execution. Rosette assertions work just like Java or Racket assertions when given a concrete value: if the value is false, the execution terminates with a runtime error. Otherwise, the execution proceeds normally.
|
||||||
|
@interaction[#:eval rosette-eval
|
||||||
|
(assert #t) (code:comment "passes and returns void")
|
||||||
|
(assert #f) (code:comment "fails with an exception")]
|
||||||
|
|
||||||
|
When given a symbolic boolean value, however, a Rosette assertion has no immediate effect. Instead, its effect (whether it passes or fails) is eventually determined by the solver.
|
||||||
|
@interaction[#:eval rosette-eval
|
||||||
|
(assert (not b)) (code:comment "pushes the asserted property onto the solver's worklist and returns void")]
|
||||||
|
|
||||||
|
@(rosette-eval '(clear-asserts))
|
||||||
|
|
||||||
|
@section[#:tag "sec:queries"]{Solver-Aided Queries}
|
||||||
|
|
||||||
|
The solver reasons about asserted properties only when we ask a question about them---for example, "Does my program have an execution that violates an assertion?" We pose such @emph{solver-aided queries} with the help of constructs explained in the remainder of this chapter.
|
||||||
|
|
||||||
|
We will illustrate the queries on the following toy example, where the @racket[factored] polynomial is intended to behave just like @racket[poly] on all inputs:
|
||||||
|
@defs+int[#:eval rosette-eval
|
||||||
|
((define (poly x)
|
||||||
|
(+ (* x x x x) (* 6 x x x) (* 11 x x) (* 6 x)))
|
||||||
|
|
||||||
|
(define (factored x)
|
||||||
|
(* x (+ x 1) (+ x 2) (+ x 2)))
|
||||||
|
|
||||||
|
(define (same p f x)
|
||||||
|
(assert (= (p x) (f x)))))
|
||||||
|
|
||||||
|
(code:comment "check zeros; all seems well ...")
|
||||||
|
(same poly factored 0)
|
||||||
|
(same poly factored -1)
|
||||||
|
(same poly factored -2)]
|
||||||
|
|
||||||
|
|
||||||
|
@subsection[#:tag "sec:verify"]{Verification}
|
||||||
|
|
||||||
|
To verify that @racket[poly] and @racket[factored] behave identically, we could simply enumerate all k-bit integers and apply the @racket[same] check to each. This naive approach to verification would, of course, be very slow for a large k. A better approach is to delegate such checks to a constraint solver, which can search large input spaces more effectively. In Rosette, this is done with the help of the @racket[verify] query:
|
||||||
|
@interaction[#:eval rosette-eval
|
||||||
|
(define-symbolic i number?)
|
||||||
|
(define cex (verify (same poly factored i)))]
|
||||||
|
|
||||||
|
The @racket[(verify #, @var[expr])] form queries the solver for a @deftech{binding} from symbolic constants to concrete values that causes the evaluation of @var[expr] to fail when the bound symbolic constants are replaced with the corresponding concrete values. If such a binding exists, as it does in our case, it is called a @emph{counterexample}.
|
||||||
|
|
||||||
|
Bindings are first-class values in Rosette, and they can be freely manipulated by programs. We can also interpret any Rosette value with respect to a binding using the built-in @racket[evaluate] procedure:
|
||||||
|
@interaction[#:eval rosette-eval
|
||||||
|
(evaluate i cex)
|
||||||
|
(same poly factored 4)]
|
||||||
|
In our example, evaluating @racket[i] with respect to @racket[cex] reveals that @racket[poly] and @racket[factored] produce different results on the input 4 (thus causing the assertion in the @racket[same] procedure to fail).
|
||||||
|
|
||||||
|
@(rosette-eval '(clear-asserts))
|
||||||
|
@(rosette-eval '(require (only-in racket/draw read-bitmap)))
|
||||||
|
|
||||||
|
@subsection[#:tag "sec:debug"]{Debugging}
|
||||||
|
|
||||||
|
Now that we have an input on which @racket[factored] differs from @racket[poly], the next step is to debug it, by figuring out which of its subexpressions are responsible for the fault. Rosette provides a query for this as well. To access it, we import the debugging facilities, mark @racket[factored] as a candidate for debugging, and issue a @racket[debug] query:
|
||||||
|
|
||||||
|
@racketblock[
|
||||||
|
(require rosette/query/debug rosette/lib/tools/render)
|
||||||
|
|
||||||
|
(define (poly x)
|
||||||
|
(+ (* x x x x) (* 6 x x x) (* 11 x x) (* 6 x)))
|
||||||
|
|
||||||
|
(define/debug (factored x) (code:comment "define/debug marks a procedure as part of")
|
||||||
|
(* x (+ x 1) (+ x 2) (+ x 2))) (code:comment "the code to be debugged")
|
||||||
|
|
||||||
|
(define (same p f x)
|
||||||
|
(assert (= (p x) (f x))))
|
||||||
|
|
||||||
|
#, @elem{>} (define core (debug [number?] (same poly factored 4)))
|
||||||
|
#, @elem{>} (render core)
|
||||||
|
#,(call-with-input-file dbg (lambda (in) (read-bitmap in 'png)))]
|
||||||
|
|
||||||
|
@(rosette-eval '(require rosette/query/debug))
|
||||||
|
@(rosette-eval '(define (poly x)
|
||||||
|
(+ (* x x x x) (* 6 x x x) (* 11 x x) (* 6 x))))
|
||||||
|
@(rosette-eval '(define/debug (factored x)
|
||||||
|
(* x (+ x 1) (+ x 2) (+ x 2))))
|
||||||
|
@(rosette-eval '(define (same p f x)
|
||||||
|
(assert (= (p x) (f x)))))
|
||||||
|
@(rosette-eval '(define core (debug [number?] (same poly factored 4))))
|
||||||
|
|
||||||
|
The @racket[(debug [#, @var[predicate]] #, @var[expr])] query takes as input an expression whose execution leads to an assertion failure, and one or more dynamic type predicates specifying which executed expressions should be treated as potentially faulty by the solver. That is, the predicates express the hypothesis that the failure is caused by an expression with one of the given types. Expressions that produce values of a different type are assumed to be correct.@footnote{For now, only primitive (@racket[boolean?] and @racket[number?]) and @seclink["sec:enum"]{enumeration} types are supported.}
|
||||||
|
|
||||||
|
The output of a @racket[debug] query is a minimal set of program expressions, called a @deftech[#:key "MUC"]{minimal unsatisfiable core}, that form an irreducible cause of the failure. Expressions outside of the core are irrelevant to the failure---there is no way to replace them with constants so that the resulting program satisfies the failing assertion. The failing assertion can only be satisfied if we are allowed to also replace one of the core expressions with a carefully chosen constant. In general, a failing expression may have many different cores, but since every core highlights a buggy subexpression, examining one or two cores often leads to the root cause of the error.
|
||||||
|
|
||||||
|
Like bindings, cores are first-class values. In our example, we simply visualize the core using the utility procedure @racket[render].@footnote{@racket[render] can only visualize cores for code that has been saved to a file.} The visualization reveals that the grayed-out subexpression @racket[(+ x 1)] is irrelevant to the failure of @racket[factored] on the input 4. To repair this failure, we have to modify at least one of the remaining expressions, which are highlighted in red.
|
||||||
|
|
||||||
|
@subsection[#:tag "sec:synthesize"]{Synthesis}
|
||||||
|
|
||||||
|
The solver can not only find failure-inducing inputs and localize faults, it can also synthesize repairs for buggy expressions. To repair a program, we first replace each buggy expression with a syntactic "@deftech{hole}." A program with holes is called a @deftech{sketch}. The solver completes a sketch by filling its holes with expressions, in such a way that all assertions in the resulting program pass on all inputs.
|
||||||
|
|
||||||
|
The following code snippet shows the sketch for our buggy @racket[factored] procedure. We obtained it by replacing the constants in the @seclink["sec:debug"]{minimal core} with @racket[(??)] holes, which are filled with numerical constants.@footnote{This simple replacement strategy is sufficient since we know that a factorization of an @var{n}-degree polynomial takes the form @tt{(* (+ x @var[c]@subscript{0}) ... (+ x @var[c]@subscript{@var{n}}))}, where @var[c]@subscript{@var{i}} is a constant.}
|
||||||
|
@defs+int[#:eval rosette-eval
|
||||||
|
((require rosette/lib/meta/meta)
|
||||||
|
|
||||||
|
(define (poly x)
|
||||||
|
(+ (* x x x x) (* 6 x x x) (* 11 x x) (* 6 x)))
|
||||||
|
|
||||||
|
(define (factored x)
|
||||||
|
(* (+ x (??)) (+ x 1) (+ x (??)) (+ x (??))))
|
||||||
|
|
||||||
|
(define (same p f x)
|
||||||
|
(assert (= (p x) (f x)))))]
|
||||||
|
|
||||||
|
The @racket[(??)] construct is imported from the @racket[rosette/lib/meta/meta] library, which also provides constructs for specifying more complex holes. For example, you can specify a hole that is filled with an expression, drawn from a grammar you define.
|
||||||
|
|
||||||
|
|
||||||
|
We query the solver for a correct completion of our sketch as follows:
|
||||||
|
@interaction[#:eval rosette-eval
|
||||||
|
(define-symbolic i number?)
|
||||||
|
(define binding
|
||||||
|
(synthesize #:forall (list i)
|
||||||
|
#:guarantee (same poly factored i)))
|
||||||
|
(eval:alts (print-forms binding) '(define (factored x) (* (+ x 0) (+ x 1) (+ x 2) (+ x 3))))]
|
||||||
|
The @racket[(synthesize #:forall #, @var[input] #:guarantee #, @var[expr])] query uses the @var[input] form to specify a set of distinguished symbolic values, which are treated as inputs to the expression @var[expr]. The result, if any, is a binding for the remaining symbolic values, created by evaluating holes. This binding guarantees successful evaluation of @var[expr] for @emph{all} possible bindings of the @var[input] values. Passing it to the @racket[print-forms] procedure yields a syntactic representation of the completed sketch.@footnote{@racket[print-forms] can only print the completion of a sketch that has been saved to a file.}
|
||||||
|
|
||||||
|
@subsection[#:tag "sec:solve"]{Angelic Execution}
|
||||||
|
|
||||||
|
Rosette supports one more solver-aided query, which we call "angelic execution." This query is the opposite of verification. Given a program with symbolic values, it instructs the solver to find a binding for them that will cause the program to execute successfully---that is, without any assertion failures.
|
||||||
|
|
||||||
|
Angelic execution can be used to solve puzzles, to run incomplete code, or to "invert" a program, by searching for inputs that produce a desired output. For example, we can ask the solver to find two distinct input values, which are not zeros of the @racket[poly] function, but which @racket[poly] still maps to the same output:
|
||||||
|
@interaction[#:eval rosette-eval
|
||||||
|
(define-symbolic x y number?)
|
||||||
|
(define sol
|
||||||
|
(solve (begin (assert (not (= x y)))
|
||||||
|
(assert (< (abs x) 10))
|
||||||
|
(assert (< (abs y) 10))
|
||||||
|
(assert (not (= (poly x) 0)))
|
||||||
|
(assert (= (poly x) (poly y))))))
|
||||||
|
(evaluate x sol)
|
||||||
|
(evaluate y sol)
|
||||||
|
(evaluate (poly x) sol)
|
||||||
|
(evaluate (poly y) sol)]
|
||||||
|
|
||||||
|
You can find more examples of angelic execution and other solver-aided queries in the @hyperlink["https://github.com/emina/rosette/blob/master/sdsl/"]{@racket[sdsl]} folder of your Rosette distribution.
|
||||||
|
|
||||||
|
@(kill-evaluator rosette-eval)
|
||||||
|
|
||||||
|
@(footnote-part)
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 5.1 KiB |
|
|
@ -0,0 +1,43 @@
|
||||||
|
#lang rosette/safe
|
||||||
|
|
||||||
|
;(configure [bitwidth 8])
|
||||||
|
|
||||||
|
(require rosette/query/debug rosette/lib/tools/render)
|
||||||
|
|
||||||
|
(define (poly x)
|
||||||
|
(+ (* x x x x) (* 6 x x x) (* 11 x x) (* 6 x)))
|
||||||
|
|
||||||
|
(define/debug (factored x)
|
||||||
|
(* x (+ x 1) (+ x 2) (+ x 2)))
|
||||||
|
|
||||||
|
(define (same p f x)
|
||||||
|
(assert (= (p x) (f x))))
|
||||||
|
|
||||||
|
(define-symbolic i number?)
|
||||||
|
|
||||||
|
(define cex (verify (same poly factored i)))
|
||||||
|
|
||||||
|
(evaluate i cex)
|
||||||
|
|
||||||
|
(define core (debug [number?] (same poly factored 4)))
|
||||||
|
(render core)
|
||||||
|
|
||||||
|
(require rosette/lib/meta/meta)
|
||||||
|
|
||||||
|
(define (factored* x)
|
||||||
|
(* (+ x (??)) (+ x 1) (+ x (??)) (+ x (??))))
|
||||||
|
|
||||||
|
(define binding
|
||||||
|
(synthesize #:forall (list i)
|
||||||
|
#:guarantee (same poly factored* i)))
|
||||||
|
|
||||||
|
(print-forms binding)
|
||||||
|
|
||||||
|
(define-symbolic x y number?)
|
||||||
|
(define env
|
||||||
|
(solve (begin (assert (not (= x y)))
|
||||||
|
(assert (< (abs x) 10))
|
||||||
|
(assert (< (abs y) 10))
|
||||||
|
(assert (not (= (poly x) 0)))
|
||||||
|
(assert (= (poly x) (poly y))))))
|
||||||
|
env
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
#lang rosette/safe
|
||||||
|
|
||||||
|
(define (poly x)
|
||||||
|
(+ (* x x x x) (* 6 x x x) (* 11 x x) (* 6 x)))
|
||||||
|
|
||||||
|
(define (same-as-poly other x)
|
||||||
|
(assert (= (poly x) (other x))))
|
||||||
|
|
||||||
|
(define (factored x)
|
||||||
|
(* x (+ x 1) (+ x 2) (+ x 2)))
|
||||||
|
|
||||||
|
(define-symbolic n number?)
|
||||||
|
(define cex (time (verify (same-as-poly factored n))))
|
||||||
|
(evaluate n cex)
|
||||||
|
|
||||||
|
(require rosette/query/debug rosette/lib/tools/render)
|
||||||
|
|
||||||
|
(define/debug (factored-buggy x)
|
||||||
|
(* x (+ x 1) (+ x 2) (+ x 2)))
|
||||||
|
|
||||||
|
(define core (time (debug [number?] (same-as-poly factored-buggy 4))))
|
||||||
|
(render core)
|
||||||
|
|
||||||
|
(require rosette/lib/meta/meta)
|
||||||
|
|
||||||
|
(define (factored-sketch x)
|
||||||
|
(* (+ x (??)) (+ x 1) (+ x (??)) (+ x (??))))
|
||||||
|
|
||||||
|
(define sol (time (synthesize #:forall (list n)
|
||||||
|
#:guarantee (same-as-poly factored-sketch n))))
|
||||||
|
|
||||||
|
(print-forms sol)
|
||||||
|
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
#lang scribble/manual
|
||||||
|
|
||||||
|
@(require (for-label racket))
|
||||||
|
|
||||||
|
|
||||||
|
@title[#:tag "ch:syntactic-forms" #:style 'toc]{Syntactic Forms}
|
||||||
|
|
||||||
|
The core of the Rosette language (@racket[rosette/safe]) consists of two kinds of syntax forms: a set of basic forms @deftech[#:key "lifted constructs"]{lifted} from Racket, and a set of forms for @seclink["ch:essentials"]{solver-aided programming}. We use the term "lifted" to refer to parts of the Racket language that can be used with symbolic values and other solver-aided constructs.
|
||||||
|
|
||||||
|
@[table-of-contents]
|
||||||
|
@include-section["racket-forms.scrbl"]
|
||||||
|
@include-section["rosette-forms.scrbl"]
|
||||||
|
|
||||||
|
|
@ -1,8 +1,13 @@
|
||||||
#lang scribble/manual
|
#lang scribble/manual
|
||||||
|
|
||||||
@(require (for-label rosette/base/form/define)
|
@(require (for-label
|
||||||
|
rosette/base/define rosette/query/tools rosette/query/eval rosette/solver/solution
|
||||||
|
rosette/base/term (only-in rosette/query/debug define/debug debug)
|
||||||
|
(only-in rosette/base/safe assert)
|
||||||
|
(only-in rosette/base/assert asserts)
|
||||||
|
(only-in rosette/base/enum enum?))
|
||||||
(for-label racket)
|
(for-label racket)
|
||||||
scribble/core scribble/html-properties scribble/examples racket/sandbox
|
scribble/core scribble/html-properties scribble/eval racket/sandbox
|
||||||
"../util/lifted.rkt")
|
"../util/lifted.rkt")
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -23,7 +28,7 @@
|
||||||
(select '(let let* letrec let-values let*-values letrec-values let-syntax
|
(select '(let let* letrec let-values let*-values letrec-values let-syntax
|
||||||
letrec-syntax let-syntaxes letrec-syntaxes letrec-syntaxes+values)))
|
letrec-syntax let-syntaxes letrec-syntaxes letrec-syntaxes+values)))
|
||||||
@(define local-defs (select '(local)))
|
@(define local-defs (select '(local)))
|
||||||
@(define conditionals (select '(if and or)))
|
@(define conditionals (select '(if cond else and or)))
|
||||||
@(define dispatch (select '(case)))
|
@(define dispatch (select '(case)))
|
||||||
@(define definitions
|
@(define definitions
|
||||||
(select '(define define-values define-syntax define-syntaxes
|
(select '(define define-values define-syntax define-syntaxes
|
||||||
|
|
@ -31,7 +36,7 @@
|
||||||
@(define sequencing (select '(begin begin0 begin-for-syntax)))
|
@(define sequencing (select '(begin begin0 begin-for-syntax)))
|
||||||
@(define guarded-eval (select '(when unless)))
|
@(define guarded-eval (select '(when unless)))
|
||||||
@(define assignment (select '(set! set!-values)))
|
@(define assignment (select '(set! set!-values)))
|
||||||
@(define quasiquoting (select '(quasiquote unquote)))
|
@(define quasiquoting (select '(quasiquote unquote unquote-splicing)))
|
||||||
@(define syntax-quoting (select '(quote-syntax)))
|
@(define syntax-quoting (select '(quote-syntax)))
|
||||||
|
|
||||||
@(define rosette-eval (rosette-evaluator))
|
@(define rosette-eval (rosette-evaluator))
|
||||||
|
|
@ -48,7 +53,7 @@ Rosette lifts the following @seclink["syntax" #:doc '(lib "scribblings/reference
|
||||||
(list @elem{Procedure Expressions} @procs)
|
(list @elem{Procedure Expressions} @procs)
|
||||||
(list @elem{Local Binding} @local-binding)
|
(list @elem{Local Binding} @local-binding)
|
||||||
(list @elem{Local Definitions} @local-defs)
|
(list @elem{Local Definitions} @local-defs)
|
||||||
(list @elem{Conditionals} (list @conditionals ", " @racket[cond] " with " @racket[[#, @var[test] #, @var[body] ...+]] " and " @racket[[else #, @var[body] ...+]] " clauses"))
|
(list @elem{Conditionals} @conditionals)
|
||||||
(list @elem{Dispatch} @dispatch)
|
(list @elem{Dispatch} @dispatch)
|
||||||
(list @elem{Definitions} @definitions)
|
(list @elem{Definitions} @definitions)
|
||||||
(list @elem{Sequencing} @sequencing)
|
(list @elem{Sequencing} @sequencing)
|
||||||
|
|
@ -58,15 +63,14 @@ Rosette lifts the following @seclink["syntax" #:doc '(lib "scribblings/reference
|
||||||
(list @elem{Syntax Quoting} @syntax-quoting))]
|
(list @elem{Syntax Quoting} @syntax-quoting))]
|
||||||
|
|
||||||
Lifted forms have the same meaning in Rosette programs as they do in Racket programs. For example, the Racket expression @racket[(if #, @var[test-expr] #, @var[then-expr] #, @var[else-expr])] evaluates @var[test-expr] first and then, depending on the outcome, it returns the result of evaluating either @var[then-expr] or @var[else-expr]. Rosette preserves this interpretation of @racket[if] for concrete values, and also extends it to work with symbolic values:
|
Lifted forms have the same meaning in Rosette programs as they do in Racket programs. For example, the Racket expression @racket[(if #, @var[test-expr] #, @var[then-expr] #, @var[else-expr])] evaluates @var[test-expr] first and then, depending on the outcome, it returns the result of evaluating either @var[then-expr] or @var[else-expr]. Rosette preserves this interpretation of @racket[if] for concrete values, and also extends it to work with symbolic values:
|
||||||
@examples[#:eval rosette-eval #:label #f
|
@interaction[#:eval rosette-eval
|
||||||
(let ([y 0])
|
(define y 0)
|
||||||
(if #t (void) (set! y 3))
|
(code:line (if #t (void) (set! y 3)) (code:comment "y unchanged"))
|
||||||
(printf "y unchanged: ~a\n" y)
|
y
|
||||||
(if #f (set! y 3) (void))
|
(code:line (if #f (set! y 3) (void)) (code:comment "y unchanged"))
|
||||||
(printf "y unchanged: ~a\n" y)
|
y
|
||||||
(define-symbolic x boolean?)
|
(define-symbolic x boolean?)
|
||||||
(if x (void) (set! y 3))
|
(eval:alts (if x (void) (set! y 3)) (#%top-interaction . (if x (void) (set! y 3))))
|
||||||
(printf "y symbolic: ~a\n" y))]
|
(code:line y (code:comment "y set to a symbolic value that is 0 if x is true, 3 otherwise"))]
|
||||||
|
|
||||||
|
|
||||||
@(kill-evaluator rosette-eval)
|
@(kill-evaluator rosette-eval)
|
||||||
|
|
@ -0,0 +1,199 @@
|
||||||
|
#lang scribble/manual
|
||||||
|
|
||||||
|
@(require (for-label
|
||||||
|
rosette/base/define rosette/query/tools rosette/query/eval rosette/solver/solution
|
||||||
|
rosette/base/term (only-in rosette/query/debug define/debug debug)
|
||||||
|
(only-in rosette/base/safe assert)
|
||||||
|
(only-in rosette/base/assert asserts)
|
||||||
|
(only-in rosette/base/enum enum?))
|
||||||
|
(for-label racket)
|
||||||
|
scribble/core scribble/html-properties scribble/eval racket/sandbox
|
||||||
|
"../util/lifted.rkt")
|
||||||
|
|
||||||
|
@(define rosette-eval (rosette-evaluator))
|
||||||
|
|
||||||
|
@title[#:tag "ch:syntactic-forms:rosette"]{Solver-Aided Forms}
|
||||||
|
|
||||||
|
The @seclink["ch:essentials"]{Essentials} chapter introduced the key concepts of solver-aided programming. This section defines the corresponding syntactic constructs more precisely.
|
||||||
|
|
||||||
|
@declare-exporting[rosette/base/define
|
||||||
|
rosette/query/tools
|
||||||
|
rosette/base/safe
|
||||||
|
#:use-sources
|
||||||
|
(rosette/base/define
|
||||||
|
rosette/query/tools
|
||||||
|
rosette/base/safe)]
|
||||||
|
|
||||||
|
@section[#:tag "sec:symbolic-constants-and-assertions"]{Symbolic Constants and Assertions}
|
||||||
|
|
||||||
|
@defform[(define-symbolic id ...+ type)
|
||||||
|
#:contracts
|
||||||
|
[(type (or/c boolean? number? enum?))]]{
|
||||||
|
Binds each provided identifier to a distinct @tech["symbolic constant"] of the given
|
||||||
|
primitive or enumeration type. The identifiers are bound to the same constants every time the form is
|
||||||
|
evaluated.
|
||||||
|
@examples[#:eval rosette-eval
|
||||||
|
(define (always-same)
|
||||||
|
(define-symbolic x number?)
|
||||||
|
x)
|
||||||
|
(always-same)
|
||||||
|
(always-same)
|
||||||
|
(eq? (always-same) (always-same))]
|
||||||
|
}
|
||||||
|
@defform[(define-symbolic* id ...+ type)
|
||||||
|
#:contracts
|
||||||
|
[(type (or/c boolean? number? enum?))]]{
|
||||||
|
Creates a stream of distinct @tech["symbolic constant"] of the given
|
||||||
|
type for each identifier, binding the identifier to the
|
||||||
|
next element from its stream every time the form is evaluated.
|
||||||
|
@examples[#:eval rosette-eval
|
||||||
|
(define (always-different)
|
||||||
|
(define-symbolic* x number?)
|
||||||
|
x)
|
||||||
|
(always-different)
|
||||||
|
(always-different)
|
||||||
|
(eq? (always-different) (always-different))]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@defform[(assert expr maybe-message)
|
||||||
|
#:grammar
|
||||||
|
[(maybe-message (code:line) expr)]]{
|
||||||
|
If @racket[expr] evaluates to @racket[#f], an error is thrown using the
|
||||||
|
optional failure message. If @racket[expr] evaluates to a symbolic boolean value,
|
||||||
|
that value is pushed onto the stack of assertions that will eventually be used to formulate
|
||||||
|
a query to the underlying solver. If @racket[expr] evaluates to any other value, @racket[assert]
|
||||||
|
has no effect.
|
||||||
|
@examples[#:eval rosette-eval
|
||||||
|
(code:line (assert #t) (code:comment "no effect"))
|
||||||
|
(code:line (assert 1) (code:comment "no effect"))
|
||||||
|
(code:line (asserts) (code:comment "empty assertion stack"))
|
||||||
|
(define-symbolic x boolean?)
|
||||||
|
(assert x)
|
||||||
|
(code:line (asserts) (code:comment "x pushed onto the assertion stack"))
|
||||||
|
(assert #f "bad value")]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@section{Angelic Execution, Verification, and Synthesis}
|
||||||
|
|
||||||
|
@(rosette-eval '(clear-asserts))
|
||||||
|
|
||||||
|
|
||||||
|
@defform[(solve expr)]{
|
||||||
|
Searches for a binding of symbolic constants to concrete values that satisfies all assertions encountered
|
||||||
|
before the invocation of @racket[solve] and during the evaluation of @racket[expr].
|
||||||
|
If such a binding exists, it is returned in the form of a satisfiable @racket[solution?]; otherwise,
|
||||||
|
an error is thrown. The assertions encountered while
|
||||||
|
evaluating @racket[expr] are removed from the global assertion stack once @racket[solve] returns. As a result,
|
||||||
|
@racket[solve] has no observable effect on the assertion stack. We refer to the
|
||||||
|
@racket[solve] query as @deftech{angelic execution} because it causes the solver to behave as an angelic oracle---
|
||||||
|
it supplies "good" bindings for symbolic constants that cause the execution to terminate successfully.
|
||||||
|
@examples[#:eval rosette-eval
|
||||||
|
(define-symbolic x y boolean?)
|
||||||
|
(assert x)
|
||||||
|
(code:line (asserts) (code:comment "x pushed onto the assertion stack"))
|
||||||
|
(define sol (solve (assert y)))
|
||||||
|
(code:line (asserts) (code:comment "assertion stack same as before"))
|
||||||
|
(code:line (evaluate x sol) (code:comment "x must be true"))
|
||||||
|
(code:line (evaluate y sol) (code:comment "y must be true"))
|
||||||
|
(solve (assert (not x)))]
|
||||||
|
}
|
||||||
|
|
||||||
|
@;@(rosette-eval '(clear-asserts))
|
||||||
|
@;@defform[(solve/evaluate expr)]{
|
||||||
|
@; Invokes @racket[solve] on @racket[expr] to obtain a satisfying solution, and
|
||||||
|
@; returns the result of evaluating @racket[expr]
|
||||||
|
@; with respect to that solution. Throws an error if no satisfying solution is found.
|
||||||
|
@; @examples[#:eval rosette-eval
|
||||||
|
@; (define-symbolic x y boolean?)
|
||||||
|
@; (assert x)
|
||||||
|
@; (solve/evaluate (begin (assert y) (cons x y)))]
|
||||||
|
@;}
|
||||||
|
|
||||||
|
@(kill-evaluator rosette-eval)
|
||||||
|
@(set! rosette-eval (rosette-evaluator))
|
||||||
|
@defform*[((verify guarantee-expr)
|
||||||
|
(verify #:assume assume-expr #:guarantee guarantee-expr))]{
|
||||||
|
Searches for a binding of symbolic constants to concrete values that violates at least one of the
|
||||||
|
assertions encountered during the evaluation of @racket[guarantee-expr], but that satisfies all
|
||||||
|
assertions encountered before the invocation of @racket[verify] and during the evaluation of
|
||||||
|
@racket[assume-expr]. If such a binding exists, it is returned in the form of a
|
||||||
|
satisfiable @racket[solution?]; otherwise, an error is thrown. The assertions encountered while
|
||||||
|
evaluating @racket[assume-expr] and @racket[guarantee-expr] are removed from the global assertion stack once
|
||||||
|
@racket[verify] returns.
|
||||||
|
@examples[#:eval rosette-eval
|
||||||
|
(define-symbolic x y boolean?)
|
||||||
|
(assert x)
|
||||||
|
(code:line (asserts) (code:comment "x pushed onto the assertion stack"))
|
||||||
|
(define sol (verify (assert y)))
|
||||||
|
(code:line (asserts) (code:comment "assertion stack same as before"))
|
||||||
|
(code:line (evaluate x sol) (code:comment "x must be true"))
|
||||||
|
(code:line (evaluate y sol) (code:comment "y must be false"))
|
||||||
|
(verify #:assume (assert y) #:guarantee (assert (and x y)))]
|
||||||
|
}
|
||||||
|
|
||||||
|
@(rosette-eval '(clear-asserts))
|
||||||
|
@defform[(synthesize #:forall input-expr
|
||||||
|
maybe-init
|
||||||
|
maybe-assume
|
||||||
|
#:guarantee guarantee-expr)
|
||||||
|
#:grammar
|
||||||
|
([maybe-init (code:line) (code:line #:init init-expr)]
|
||||||
|
[maybe-assume (code:line) (code:line #:assume assume-expr)])
|
||||||
|
#:contracts
|
||||||
|
([input-expr (listof constant?)]
|
||||||
|
[init-expr (or/c (and/c sat? solution?) (listof (and/c sat? solution?)))])]{
|
||||||
|
Searches for a binding of symbolic constants
|
||||||
|
to concrete values that has the following properties:
|
||||||
|
@itemlist[#:style 'ordered
|
||||||
|
@item{it does not map constants in the @racket[input-expr] list; and,}
|
||||||
|
@item{it satisfies all assertions encountered during the evaluation of
|
||||||
|
@racket[guarantee-expr], for every binding of @racket[input-expr] constants to values that satisfies
|
||||||
|
the assertions encountered before the invocation of @racket[synthesize] and during the evaluation of
|
||||||
|
@racket[assume-expr].}]
|
||||||
|
If no such binding exists, an error is thrown. The assertions encountered while
|
||||||
|
evaluating @racket[assume-expr] and @racket[guarantee-expr] are removed from the global assertion stack once
|
||||||
|
@racket[synthesize] returns. The optional @racket[init-expr], if given, must evaluate to bindings for constants
|
||||||
|
in @racket[input-expr] that satisfy all assertions encountered before the invocation of @racket[synthesize]
|
||||||
|
and during the evaluation of @racket[assume-expr]. Providing these optional bindings may speed up the query.
|
||||||
|
@examples[#:eval rosette-eval
|
||||||
|
(define-symbolic x c number?)
|
||||||
|
(assert (even? x))
|
||||||
|
(code:line (asserts) (code:comment "assertion pushed on the stack"))
|
||||||
|
(define sol
|
||||||
|
(synthesize #:forall (list x)
|
||||||
|
#:guarantee (assert (= (/ x 2) (>> x c)))))
|
||||||
|
(code:line (asserts) (code:comment "assertion stack same as before"))
|
||||||
|
(code:line (evaluate x sol) (code:comment "the value of x is unknown"))
|
||||||
|
(code:line (evaluate c sol) (code:comment "c must be 1"))]
|
||||||
|
}
|
||||||
|
|
||||||
|
@section{Debugging}
|
||||||
|
|
||||||
|
@defmodule[rosette/query/debug #:use-sources (rosette/query/debug)]
|
||||||
|
|
||||||
|
@defform[(define/debug head body ...)
|
||||||
|
#:grammar
|
||||||
|
([head id (id ...)])]{
|
||||||
|
Defines a procedure or an expression, and marks it as a candidate for debugging.
|
||||||
|
When a @racket[debug] query is applied to a failing execution,
|
||||||
|
forms that are not marked in this way are considered
|
||||||
|
correct. The solver will apply the debugging algorithm only to
|
||||||
|
expressions and procedures marked as potentially faulty using
|
||||||
|
@racket[define/debug].
|
||||||
|
}
|
||||||
|
|
||||||
|
@defform[(debug [type ...+] expr)
|
||||||
|
#:contracts
|
||||||
|
([type (or/c boolean? number? enum?)])]{
|
||||||
|
Searches for a minimal set of @racket[define/debug] expressions of
|
||||||
|
the given type(s) that are collectively responsible for the observed failure of @racket[expr].
|
||||||
|
If no expressions of the specified types are relevent to the failure, an error is thrown. The
|
||||||
|
returned expressions, if any, are called a minimal unsatisfiable core. The core expressions
|
||||||
|
are relevant to the observed failure in that it cannot be prevented without modifying at least one
|
||||||
|
core expression. In particular, if all of the non-core expressions were replaced with
|
||||||
|
fresh constants created using @racket[define-symbolic*], @racket[(solve expr)] would still fail. It
|
||||||
|
can only execute successfully if at least one of the core expressions is also replaced with a fresh constant.}
|
||||||
|
|
||||||
|
@(kill-evaluator rosette-eval)
|
||||||
|
|
@ -1,15 +1,9 @@
|
||||||
#lang scribble/manual
|
#lang scribble/manual
|
||||||
|
|
||||||
@(require (for-label racket) (only-in racket match)
|
@(require (for-label racket) (only-in racket match) scribble/core scribble/html-properties)
|
||||||
racket/runtime-path (only-in racket build-path)
|
|
||||||
scribble/core scribble/html-properties)
|
|
||||||
|
|
||||||
@(define-runtime-path guide.css "util/guide.css")
|
|
||||||
@(define guide-style
|
|
||||||
(make-style "GuideStyle"
|
|
||||||
(list (make-css-addition guide.css))))
|
|
||||||
|
|
||||||
@title[#:style guide-style]{The Rosette Guide}
|
@title{The Rosette Guide}
|
||||||
@author{Emina Torlak}
|
@author{Emina Torlak}
|
||||||
|
|
||||||
This document is intended both as an introduction to solver-aided programming with Rosette,
|
This document is intended both as an introduction to solver-aided programming with Rosette,
|
||||||
|
|
@ -34,8 +28,7 @@ Chapters @seclink["ch:syntactic-forms"]{3}-@seclink["ch:libraries"]{6} define th
|
||||||
@include-section["libs/libraries.scrbl"]
|
@include-section["libs/libraries.scrbl"]
|
||||||
@include-section["reflection/symbolic-reflection.scrbl"]
|
@include-section["reflection/symbolic-reflection.scrbl"]
|
||||||
@include-section["unsafe/unsafe.scrbl"]
|
@include-section["unsafe/unsafe.scrbl"]
|
||||||
@include-section["performance/performance.scrbl"]
|
|
||||||
@include-section["error-tracing/error-tracing.scrbl"]
|
|
||||||
|
|
||||||
@(require (only-in "refs.scrbl" generate-bibliography))
|
@(require (only-in "refs.scrbl" generate-bibliography))
|
||||||
@(define bib @(generate-bibliography #:tag "refs" #:sec-title "References"))
|
@(define bib @(generate-bibliography #:tag "refs" #:sec-title "References"))
|
||||||
|
|
@ -4,11 +4,13 @@
|
||||||
|
|
||||||
@title[#:tag "ch:libraries" #:style 'toc]{Libraries}
|
@title[#:tag "ch:libraries" #:style 'toc]{Libraries}
|
||||||
|
|
||||||
Chapters @seclink["ch:getting-started"]{1}-@seclink["ch:programmer-defined-datatypes"]{5} introduce the basic constructs and datatypes for programming in Rosette. This chapter describes the parts of the core Racket libraries (e.g., I/O procedures) that are exported by @racketmodname[rosette/safe], as well as Rosette libraries that provide additional facilities for solver-aided development.
|
Chapters @seclink["ch:getting-started"]{1}-@seclink["ch:programmer-defined-datatypes"]{5} introduce the basic constructs and datatypes for programming in Rosette. This chapter describes the parts of the core Racket libraries (e.g., I/O procedures) that are exported by @racket[rosette/safe], as well as two Rosette libraries that provide additional facilities for solver-aided synthesis and debugging.
|
||||||
|
|
||||||
@[table-of-contents]
|
@[table-of-contents]
|
||||||
|
|
||||||
|
|
||||||
@include-section["racket-libs.scrbl"]
|
@include-section["racket-libs.scrbl"]
|
||||||
@include-section["rosette-libs.scrbl"]
|
@include-section["rosette-libs.scrbl"]
|
||||||
@include-section["utility-libs.scrbl"]
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -17,4 +17,5 @@ Rosette exports the following facilities from the core Racket libraries:
|
||||||
|
|
||||||
These facilities are safe to use in Rosette programs, even in the presence of symbolic values, assertions, and solver-aided queries. They are not, however, @tech[#:key "lifted constructs"]{lifted}: if their Racket implementation expects a concrete value of a given type, they will fail when given a symbolic value. These constructs are safe to use in the sense that they will fail in a predictable fashion, according to their concrete Racket specification, instead of causing the enclosing Rosette program to exhibit unexpected behavior.
|
These facilities are safe to use in Rosette programs, even in the presence of symbolic values, assertions, and solver-aided queries. They are not, however, @tech[#:key "lifted constructs"]{lifted}: if their Racket implementation expects a concrete value of a given type, they will fail when given a symbolic value. These constructs are safe to use in the sense that they will fail in a predictable fashion, according to their concrete Racket specification, instead of causing the enclosing Rosette program to exhibit unexpected behavior.
|
||||||
|
|
||||||
The @racketmodname[rosette/safe] language allows programs to import arbitrary Racket code using the standard @racket[require] mechanism. This is strongly discouraged, however, unless the use of such code obeys the restrictions outlined in the @seclink["ch:unsafe"]{Chapter 8}. Violating these restrictions may lead to incorrect program behavior, crashes, and loss of data (for programs that perform external side-effects, such as writing to files). In other words, arbitrary Racket code is, by default, unsafe to use.
|
The @racket[rosette/safe] language allows programs to import arbitrary Racket code using the standard @racket[require] mechanism. This is strongly discouraged, however, unless the use of such code obeys the restrictions outlined in the @seclink["ch:unsafe"]{Chapter 8}. Violating these restrictions may lead to incorrect program behavior, crashes, and loss of data (for programs that perform external side-effects, such as writing to files). In other words, arbitrary Racket code is, by default, unsafe to use.
|
||||||
|
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
#lang rosette
|
||||||
|
|
||||||
|
(require rosette/lib/meta/meta)
|
||||||
|
|
||||||
|
(define (div2 x) ([choose >> >>> << + - *] x (??)))
|
||||||
|
(define-symbolic i number?)
|
||||||
|
(define m1
|
||||||
|
(synthesize #:forall (list i)
|
||||||
|
#:assume (assert (>= i 0))
|
||||||
|
#:guarantee (assert (= (div2 i) (quotient i 2)))))
|
||||||
|
(print-forms m1)
|
||||||
|
(generate-expressions m1)
|
||||||
|
(generate-forms m1)
|
||||||
|
|
||||||
|
(define-synthax [shift terminal ... k]
|
||||||
|
#:assert (>= k 0)
|
||||||
|
[choose
|
||||||
|
terminal ... (??)
|
||||||
|
([choose >> << >>>] (shift terminal ... (- k 1))
|
||||||
|
(shift terminal ... (- k 1)))])
|
||||||
|
|
||||||
|
(define (div2mul4 x) (shift x 2))
|
||||||
|
|
||||||
|
(define m2
|
||||||
|
(synthesize #:forall (list i)
|
||||||
|
#:assume (assert (>= i 0))
|
||||||
|
#:guarantee (assert (= (div2mul4 i) (* 4 (quotient i 2))))))
|
||||||
|
(print-forms m2)
|
||||||
|
|
@ -0,0 +1,155 @@
|
||||||
|
#lang scribble/manual
|
||||||
|
|
||||||
|
@(require (for-label
|
||||||
|
rosette/base/define rosette/solver/solution rosette/query/tools rosette/query/eval
|
||||||
|
rosette/base/term rosette/base/enum
|
||||||
|
(except-in rosette/query/debug false true)
|
||||||
|
(only-in rosette/lib/meta/constructs ?? choose define-synthax)
|
||||||
|
(only-in rosette/lib/meta/generate generate-expressions generate-forms)
|
||||||
|
(only-in rosette/lib/meta/display print-expressions print-forms)
|
||||||
|
(only-in rosette/base/base << >> >>>)
|
||||||
|
(only-in rosette/base/safe assert)
|
||||||
|
rosette/lib/tools/render
|
||||||
|
racket (only-in pict pict?))
|
||||||
|
scribble/core scribble/html-properties scribble/eval racket/sandbox
|
||||||
|
"../util/lifted.rkt")
|
||||||
|
|
||||||
|
|
||||||
|
@(define rosette-eval (rosette-evaluator))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@title[#:tag "sec:rosette-libs"]{Solver-Aided Libraries}
|
||||||
|
|
||||||
|
In principle, solver-aided programming requires only symbolic values and the basic constructs described in Chapter @seclink["ch:syntactic-forms:rosette"]{3}. In practice, however, it is often convenient to work with richer constructs, which are built on top of these primitives. Rosette ships with two libraries that provide such constructs, as well as utility procedures for turning the results of synthesis and debugging queries into code.
|
||||||
|
|
||||||
|
@section{Synthesis Library}
|
||||||
|
|
||||||
|
@defmodule[rosette/lib/meta/meta #:use-sources (rosette/lib/meta/constructs rosette/lib/meta/generate rosette/lib/meta/display)]
|
||||||
|
|
||||||
|
@defform[(??)]{
|
||||||
|
Introduces an integer @tech{hole} into a program---a placeholder for a concrete integer constant.
|
||||||
|
Chapter @seclink["sec:synthesize"]{2.3.3} shows an example of using integer holes to @tech{sketch}
|
||||||
|
a factored polynomial function, which is then completed with the help of a @racket[synthesize] query.
|
||||||
|
The @racket[(??)] construct @seclink["sec:symbolic-constants-and-assertions"]{creates}
|
||||||
|
and returns a fresh symbolic constant of type @racket[number?].
|
||||||
|
}
|
||||||
|
|
||||||
|
@(rosette-eval '(require rosette/lib/meta/meta))
|
||||||
|
@defform[(choose expr ...+)]{
|
||||||
|
Introduces a choice @tech{hole} into a program---a placeholder to be filled with one of the given expressions.
|
||||||
|
This construct defines @var[n]-1 fresh boolean constants and uses them to conditionally select one of the @var[n]
|
||||||
|
provided expressions.
|
||||||
|
@examples[#:eval rosette-eval
|
||||||
|
(define (div2 x) ([choose >> >>> << + - *] x (??)))
|
||||||
|
(define-symbolic i number?)
|
||||||
|
(eval:alts
|
||||||
|
(print-forms
|
||||||
|
(synthesize #:forall (list i)
|
||||||
|
#:assume (assert (>= i 0))
|
||||||
|
#:guarantee (assert (= (div2 i) (quotient i 2)))))
|
||||||
|
'(define (div2 x) (>> x 1)))
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
@defform[(define-synthax (id arg ...) maybe-guard body)
|
||||||
|
#:grammar
|
||||||
|
([maybe-guard (code:line) (code:line #:assert guard)])]{
|
||||||
|
Defines a grammar of expressions that can be used to
|
||||||
|
fill holes of the form @racket[(id expr ...)]. That is, writing
|
||||||
|
@racket[(id expr ...)] introduces a @tech{hole} that is to
|
||||||
|
be filled with an expression from the @racket[id] grammar.
|
||||||
|
|
||||||
|
@examples[#:eval rosette-eval
|
||||||
|
|
||||||
|
(code:comment "Defines the following grammar:")
|
||||||
|
(code:comment " shift := terminal ... | const | (op shift shift)")
|
||||||
|
(code:comment " op := >> | << | >>>")
|
||||||
|
(code:comment " const := (??)")
|
||||||
|
(define-synthax (shift terminal ... k)
|
||||||
|
#:assert (>= k 0)
|
||||||
|
[choose
|
||||||
|
terminal ... (??)
|
||||||
|
([choose >> << >>>] (shift terminal ... (- k 1))
|
||||||
|
(shift terminal ... (- k 1)))])
|
||||||
|
|
||||||
|
(code:comment "A sketch with a hole to be filled with a shift expression of depth <= 2.")
|
||||||
|
(define (div2mul4 x) (shift x 2))
|
||||||
|
|
||||||
|
(define-symbolic i number?)
|
||||||
|
(eval:alts
|
||||||
|
(print-forms
|
||||||
|
(synthesize #:forall (list i)
|
||||||
|
#:assume (assert (>= i 0))
|
||||||
|
#:guarantee (assert (= (div2mul4 i) (* 4 (quotient i 2))))))
|
||||||
|
'(define (div2mul4 x) (<< (>>> x 1) 2)))
|
||||||
|
]
|
||||||
|
|
||||||
|
Recursive grammars, such as @racket[shift], must be equipped with
|
||||||
|
a @racket[guard] that limits the size of a hole expression drawn
|
||||||
|
from the grammar. Since @racket[define-synthax] uses macros to implement recursive grammars,
|
||||||
|
instantiating a recursive grammar with a large limit (e.g., k > 3) can cause long compilation times.
|
||||||
|
The @racket[define-synthax] construct may be changed in the future to a more efficient
|
||||||
|
procedure-based implementation.
|
||||||
|
}
|
||||||
|
|
||||||
|
@(rosette-eval '(require (only-in racket datum->syntax)))
|
||||||
|
|
||||||
|
@defproc[(generate-expressions [solution solution?]) (listof (cons/c syntax? syntax?))]{
|
||||||
|
Given a satisfiable @racket[solution?] to a @racket[synthesize] query, returns a list that
|
||||||
|
associates each hole involved in the query with a synthesized expression. Hole completions
|
||||||
|
can only be generated for programs that have been saved to disk. In the
|
||||||
|
following example, @racket[generate-expressions] returns a list that associates the
|
||||||
|
@racket[choose] hole (line 1, column 19) with the expression @racket[>>], and the
|
||||||
|
@racket[??] hole (line 1, column 46) with the expression @racket[1].
|
||||||
|
@examples[#:eval rosette-eval
|
||||||
|
(define (div2 x) ([choose >> >>> << + - *] x (??)))
|
||||||
|
(define-symbolic i number?)
|
||||||
|
(eval:alts
|
||||||
|
(generate-expressions
|
||||||
|
(synthesize #:forall (list i)
|
||||||
|
#:assume (assert (>= i 0))
|
||||||
|
#:guarantee (assert (= (div2 i) (quotient i 2)))))
|
||||||
|
(list (cons (datum->syntax #f 'choose (list #f 1 19 #f #f)) (datum->syntax #f '>>))
|
||||||
|
(cons (datum->syntax #f '?? (list #f 1 46 #f #f)) (datum->syntax #f '1))))
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
@defproc[(generate-forms [solution solution?]) (listof (cons/c syntax? syntax?))]{
|
||||||
|
Given a satisfiable @racket[solution?] to a @racket[synthesize] query, returns a list that
|
||||||
|
associates each top-level @tech{sketch} involved in the query with a completion of that sketch.
|
||||||
|
Sketch completions can only be generated for programs that have been saved to disk.
|
||||||
|
In the following example, @racket[generate-forms] returns a list that associates the
|
||||||
|
@racket[div2] sketch (line 2, column 1) with its synthesized completion.
|
||||||
|
@examples[#:eval rosette-eval
|
||||||
|
(define (div2 x) ([choose >> >>> << + - *] x (??)))
|
||||||
|
(define-symbolic i number?)
|
||||||
|
(eval:alts
|
||||||
|
(generate-forms
|
||||||
|
(synthesize #:forall (list i)
|
||||||
|
#:assume (assert (>= i 0))
|
||||||
|
#:guarantee (assert (= (div2 i) (quotient i 2)))))
|
||||||
|
(list (cons (datum->syntax #f 'define (list #f 2 1 #f #f)) (datum->syntax #f '(define (div2 x) (>> x 1))))))
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
@deftogether[(@defproc[(print-expressions [solution solution?]) void?]
|
||||||
|
@defproc[(print-forms [solution solution?]) void?])]{
|
||||||
|
Pretty-prints the result of applying
|
||||||
|
@racket[generate-expressions] or @racket[generate-forms] to the given
|
||||||
|
@racket[solution].
|
||||||
|
}
|
||||||
|
|
||||||
|
@section{Debugging Library}
|
||||||
|
@defmodule[rosette/lib/tools/render #:use-sources (rosette/lib/tools/render)]
|
||||||
|
|
||||||
|
@defproc[(render [solution solution?] [font-size natural/c 16]) pict?]{
|
||||||
|
Given an unsatisfiable @racket[solution?] to a @racket[debug] query, returns a
|
||||||
|
@racket[pict?] visualization of that solution. The visualization displays the
|
||||||
|
debugged code, highlighting the faulty expressions (i.e., those in the @racket[solution]'s minimal core) in red.
|
||||||
|
The optional @racket[font-size] parameter controls the size of the font used to typeset the code.
|
||||||
|
Visualizations can only be constructed for programs that have been saved to disk.
|
||||||
|
See Chapter @seclink["sec:debug"]{2.3.2} for an example of using @racket[render].
|
||||||
|
}
|
||||||
|
|
||||||
|
@(kill-evaluator rosette-eval)
|
||||||
|
|
@ -0,0 +1,80 @@
|
||||||
|
#lang scribble/manual
|
||||||
|
|
||||||
|
@(require (for-label
|
||||||
|
rosette/solver/solver rosette/solver/solution rosette/query/state
|
||||||
|
rosette/solver/kodkod/kodkod
|
||||||
|
rosette/base/define rosette/query/tools rosette/query/eval rosette/solver/solution
|
||||||
|
rosette/base/term rosette/base/type rosette/base/primitive rosette/base/enum rosette/base/union
|
||||||
|
rosette/base/forall rosette/lib/reflect/lift
|
||||||
|
(only-in rosette/base/assert pc asserts clear-asserts with-asserts with-asserts-only)
|
||||||
|
(only-in rosette/base/safe assert)
|
||||||
|
racket)
|
||||||
|
scribble/core scribble/html-properties scribble/eval racket/sandbox
|
||||||
|
"../util/lifted.rkt")
|
||||||
|
@(require (only-in "../refs.scrbl" ~cite rosette:pldi14))
|
||||||
|
|
||||||
|
@(define rosette-eval (rosette-evaluator))
|
||||||
|
|
||||||
|
@title[#:tag "sec:state-reflection"]{Reflecting on Symbolic State}
|
||||||
|
|
||||||
|
Like standard program execution, Rosette's symbolic evaluation @~cite[rosette:pldi14] can be understood as a sequence of transitions from one @deftech{program state} to the next. In addition to the memory and register values, the state of a Rosette program also includes the current @deftech{path condition} and the current @deftech{assertion store}. The path condition is a boolean value encoding the branch decisions taken to reach the present state, and the assertion store is the set of boolean values (i.e., constraints) that have been asserted so far. This section describes the built-in facilities for accessing and modifying various aspects of the symbolic state from within a Rosette program.
|
||||||
|
|
||||||
|
@declare-exporting[rosette/base/assert #:use-sources (rosette/base/assert)]
|
||||||
|
|
||||||
|
@defproc[(pc) boolean?]{
|
||||||
|
Returns the current path condition.
|
||||||
|
@examples[#:eval rosette-eval
|
||||||
|
(define-symbolic a b boolean?)
|
||||||
|
(if a
|
||||||
|
(if b
|
||||||
|
#f
|
||||||
|
(pc))
|
||||||
|
#f)]
|
||||||
|
}
|
||||||
|
|
||||||
|
@defproc[(asserts) (listof boolean?)]{
|
||||||
|
Returns the current assertion store.
|
||||||
|
@examples[#:eval rosette-eval
|
||||||
|
(define-symbolic a b boolean?)
|
||||||
|
(assert a)
|
||||||
|
(asserts)
|
||||||
|
(assert b)
|
||||||
|
(asserts)]
|
||||||
|
}
|
||||||
|
@(rosette-eval '(clear-asserts))
|
||||||
|
|
||||||
|
|
||||||
|
@defproc[(clear-asserts) void?]{
|
||||||
|
Empties the current assertion store.
|
||||||
|
@examples[#:eval rosette-eval
|
||||||
|
(define-symbolic a b boolean?)
|
||||||
|
(assert a)
|
||||||
|
(assert b)
|
||||||
|
(asserts)
|
||||||
|
(clear-asserts)
|
||||||
|
(asserts)]
|
||||||
|
}
|
||||||
|
|
||||||
|
@(rosette-eval '(clear-asserts))
|
||||||
|
@defform[(with-asserts expr)]{
|
||||||
|
Returns two values: the result of evaluating @racket[expr] and the assertions
|
||||||
|
generated during the evaluation of @racket[expr]. These
|
||||||
|
assertions will not appear in the assertion store after
|
||||||
|
@racket[with-asserts] returns.
|
||||||
|
|
||||||
|
@examples[#:eval rosette-eval
|
||||||
|
(define-symbolic a b boolean?)
|
||||||
|
(define-values (result asserted)
|
||||||
|
(with-asserts
|
||||||
|
(begin (assert a)
|
||||||
|
(assert b)
|
||||||
|
4)))
|
||||||
|
(printf "result = ~a\n" result)
|
||||||
|
(printf "asserted = ~a\n" asserted)
|
||||||
|
(asserts)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
@(kill-evaluator rosette-eval)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
@title[#:tag "ch:symbolic-reflection" #:style 'toc]{Symbolic Reflection}
|
@title[#:tag "ch:symbolic-reflection" #:style 'toc]{Symbolic Reflection}
|
||||||
|
|
||||||
This chapter describes @deftech{symbolic reflection}, a
|
This chapter describes @emph{symbolic reflection}, a convenient
|
||||||
mechanism for manipulating the representation of symbolic values
|
mechanism for manipulating the representation of symbolic values
|
||||||
(Section @seclink["sec:value-reflection"]{7.1}) and
|
(Section @seclink["sec:value-reflection"]{7.1}) and
|
||||||
the state of the symbolic evaluation from within a Rosette program
|
the state of the symbolic evaluation from within a Rosette program
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
#lang rosette
|
||||||
|
|
||||||
|
(define-symbolic b boolean?)
|
||||||
|
(define v (vector 1))
|
||||||
|
(define w (vector 2 3))
|
||||||
|
(define s (if b v w))
|
||||||
|
s
|
||||||
|
(type-of s)
|
||||||
|
(eq? s v)
|
||||||
|
(eq? s w)
|
||||||
|
(define u (if b '(1 2) 3))
|
||||||
|
u
|
||||||
|
(type-of u)
|
||||||
|
|
||||||
|
(define (test)
|
||||||
|
(define-symbolic c boolean?)
|
||||||
|
(define v (if c #t 0))
|
||||||
|
(define u (if b (vector v) 4))
|
||||||
|
(list v u))
|
||||||
|
|
||||||
|
(test)
|
||||||
|
|
||||||
|
(union-contents u)
|
||||||
|
|
@ -0,0 +1,301 @@
|
||||||
|
#lang scribble/manual
|
||||||
|
|
||||||
|
@(require (for-label
|
||||||
|
rosette/solver/solver rosette/solver/solution rosette/query/state
|
||||||
|
rosette/solver/kodkod/kodkod
|
||||||
|
rosette/base/define rosette/query/tools rosette/query/eval rosette/solver/solution
|
||||||
|
rosette/base/term rosette/base/type rosette/base/primitive rosette/base/enum rosette/base/union
|
||||||
|
rosette/base/forall rosette/lib/reflect/lift (only-in rosette/base/assert asserts)
|
||||||
|
(only-in rosette/base/safe assert)
|
||||||
|
racket)
|
||||||
|
scribble/core scribble/html-properties scribble/eval racket/sandbox
|
||||||
|
"../util/lifted.rkt")
|
||||||
|
|
||||||
|
|
||||||
|
@(define rosette-eval (rosette-evaluator))
|
||||||
|
|
||||||
|
@title[#:tag "sec:value-reflection"]{Reflecting on Symbolic Values}
|
||||||
|
|
||||||
|
There are two kinds of symbolic values in Rosette: @emph{symbolic terms} and
|
||||||
|
@emph{symbolic unions}. A Rosette program can inspect the representation of
|
||||||
|
both kinds of values. This is useful for @tech[#:key "lifted constructs"]{lifting} additional
|
||||||
|
(unlifted) Racket procedures to work on symbolic values, and for
|
||||||
|
controlling the performance of Rosette's symbolic evaluator.
|
||||||
|
|
||||||
|
@section[#:tag "sec:symbolic-terms"]{Symbolic Terms}
|
||||||
|
|
||||||
|
A symbolic term is either a symbolic constant, created via @racket[define-symbolic],
|
||||||
|
or a symbolic expressions, produced by applying a lifted operator to one or more
|
||||||
|
symbolic terms. Terms are strongly typed. The only types that include symbolic terms
|
||||||
|
as values are @tech[#:key "primitive datatype"]{primitive datatypes} and programmer-defined
|
||||||
|
@seclink["sec:enum"]{enumerations}. Symbolic values of all other types take the form of
|
||||||
|
@seclink["sec:symbolic-unions"]{symbolic unions}.
|
||||||
|
|
||||||
|
@declare-exporting[rosette/base/term #:use-sources (rosette/base/type rosette/base/op rosette/base/term)]
|
||||||
|
|
||||||
|
@defproc[(type? [value any/c]) boolean?]{
|
||||||
|
Returns true when given a predicate that recognizes a @seclink["ch:built-in-datatypes"]{built-in type}, a programmer-defined @seclink["sec:enum"]{enumeration},
|
||||||
|
or a programmer-defined @seclink["sec:struct"]{structure} type. Otherwise returns false.
|
||||||
|
@examples[#:eval rosette-eval
|
||||||
|
(type? number?)
|
||||||
|
(type? boolean?)
|
||||||
|
(type? list?)
|
||||||
|
(define-enum suit '(club diamond heart spade))
|
||||||
|
(type? suit?)
|
||||||
|
(type? 1)]
|
||||||
|
}
|
||||||
|
|
||||||
|
@defproc*[([(type-of [value any/c]) type?])]{
|
||||||
|
Returns the most specific @racket[type?] predicate that accepts the given @racket[value].
|
||||||
|
@examples[#:eval rosette-eval
|
||||||
|
(define-symbolic x number?)
|
||||||
|
(type-of x)
|
||||||
|
(type-of (+ x 1))
|
||||||
|
(type-of #t)]
|
||||||
|
}
|
||||||
|
|
||||||
|
@deftogether[(@defproc[(term? [value any/c]) boolean?]
|
||||||
|
@defproc[(expression? [value any/c]) boolean?]
|
||||||
|
@defproc[(constant? [value any/c]) boolean?])]{
|
||||||
|
Predicates for recognizing symbolic terms, expressions, and constants, respectively.
|
||||||
|
@examples[#:eval rosette-eval
|
||||||
|
(code:line (define-symbolic x number?) (code:comment "constant"))
|
||||||
|
(code:line (define e (+ x 1)) (code:comment "expression"))
|
||||||
|
(list (term? x) (term? e))
|
||||||
|
(list (constant? x) (constant? e))
|
||||||
|
(list (expression? x) (expression? e))
|
||||||
|
(term? 1)]
|
||||||
|
}
|
||||||
|
|
||||||
|
@defproc*[([(term-name [value constant?]) (or/c syntax? (cons/c syntax? any/c))]
|
||||||
|
[(term-name [value any/c]) #f])]{
|
||||||
|
Given a @racket[constant?] term, returns the unique identifier for that term.
|
||||||
|
This identifier may be a syntax object or a pair consisting of a
|
||||||
|
syntax object and another value (e.g., a natural number).
|
||||||
|
@examples[#:eval rosette-eval
|
||||||
|
(define-symbolic x number?)
|
||||||
|
(define-symbolic* b boolean?)
|
||||||
|
(term-name x)
|
||||||
|
(term-name b)
|
||||||
|
(term-name (+ x 1))
|
||||||
|
(term-name 1)]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@defproc*[([(term-op [value expression?]) any/c]
|
||||||
|
[(term-op [value any/c]) #f])]{
|
||||||
|
Given an @racket[expression?] term, returns a value that represents
|
||||||
|
its operator. The operator value is @racket[equal?] to the lifted
|
||||||
|
procedure used to construct the value, but they are not the same object,
|
||||||
|
and the output of @racket[term-op] should not be used as a procedure by Rosette programs.
|
||||||
|
@examples[#:eval rosette-eval
|
||||||
|
(define-symbolic x number?)
|
||||||
|
(term-op x)
|
||||||
|
(term-op (+ x 1))
|
||||||
|
(term-op 1)]
|
||||||
|
}
|
||||||
|
|
||||||
|
@defproc*[([(term-child [value expression?]) (listof any/c)]
|
||||||
|
[(term-child [value any/c]) #f])]{
|
||||||
|
Given an @racket[expression?] term, returns the list of its children.
|
||||||
|
At least one child in this list is itself a @racket[term?], and all children
|
||||||
|
in the list have a @tech[#:key "primitive datatype"]{primitive} or
|
||||||
|
@seclink["sec:enum"]{enumeration} type. The number of children and
|
||||||
|
their types are determined by the expression's operator.
|
||||||
|
@examples[#:eval rosette-eval
|
||||||
|
(define-symbolic x number?)
|
||||||
|
(term-op x)
|
||||||
|
(term-child (+ x 1))
|
||||||
|
(term-child 1)]
|
||||||
|
}
|
||||||
|
|
||||||
|
@defproc*[([(term-property [t term?] [prop any/c] [value any/c]) term?]
|
||||||
|
[(term-property [t term?] [prop any/c]) any/c])]{
|
||||||
|
Each term can be annotated with any number of property-value pairs.
|
||||||
|
The three-argument version of @racket[term-property]
|
||||||
|
returns a fresh copy of the term @racket[t], annotated with the given property-value pair.
|
||||||
|
The two-argument version returns the value that the term @racket[t] associates with the property @racket[prop],
|
||||||
|
or @racket[#f] if @racket[t] has no value for @racket[prop].
|
||||||
|
}
|
||||||
|
|
||||||
|
@defproc*[([(term-track-origin [t term?] [origin any/c]) term?]
|
||||||
|
[(term-origin [t term?]) any/c])]{
|
||||||
|
Functionally sets and retrieves the distinguished @racket['origin]
|
||||||
|
property of a term. See @racket[term-property].
|
||||||
|
}
|
||||||
|
|
||||||
|
@defproc*[([(term->datum [t term?]) any/c])]{
|
||||||
|
Returns a plain Racket datum that corresponds to the given term.
|
||||||
|
Expressions are converted into lists, and constants are converted
|
||||||
|
into symbols. The output of @racket[term->datum] is suitable for pretty-printing.
|
||||||
|
@examples[#:eval rosette-eval
|
||||||
|
(define-symbolic x number?)
|
||||||
|
(define-symbolic* b boolean?)
|
||||||
|
(term->datum x)
|
||||||
|
(term->datum b)
|
||||||
|
(term->datum (+ x 1))]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@section[#:tag "sec:symbolic-unions"]{Symbolic Unions}
|
||||||
|
|
||||||
|
@declare-exporting[rosette/base/union #:use-sources (rosette/base/union)]
|
||||||
|
|
||||||
|
Rosette represents a symbolic value of a @tech[#:key "composite datatype"]{composite datatype} (such as a list or a programmer-defined structure) as a union of @deftech{guarded values} of that type. A guarded value is a pair that combines a guard, which is a symbolic boolean term, and another (non-union) value. The guards in a symbolic union are, by construction, disjoint: only one of them can ever be true. For example, the symbolic vector @racket[s] defined below is represented as a symbolic union of two guarded vectors:
|
||||||
|
@interaction[#:eval rosette-eval
|
||||||
|
(define-symbolic b boolean?)
|
||||||
|
(define v (vector 1))
|
||||||
|
(define w (vector 2 3))
|
||||||
|
(define s (if b v w))
|
||||||
|
s
|
||||||
|
(type-of s)
|
||||||
|
(eq? s v)
|
||||||
|
(eq? s w)]
|
||||||
|
|
||||||
|
The values that appear in a union are themselves never unions. They may, however, contain unions. They may also belong to several different types. In that case, the type of the union is the most specific @racket[type?] predicate that accepts all members of the union. This will always be a composite type---possibly, the most general composite type @racket[any/c].
|
||||||
|
@interaction[#:eval rosette-eval
|
||||||
|
(define-symbolic b boolean?)
|
||||||
|
(define-symbolic c boolean?)
|
||||||
|
(define v (if c "c" 0))
|
||||||
|
(define u (if b (vector v) 4))
|
||||||
|
u
|
||||||
|
(type-of u)]
|
||||||
|
|
||||||
|
Symbolic unions are recognized with the @racket[union?] predicate, and Rosette programs can inspect their contents using the @racket[union-contents] procedure. These two procedures may be used directly to @tech[#:key "lifted constructs"]{lift} Racket code to work on symbolic unions, but Rosette also provides dedicated lifting constructs, described in the @seclink["sec:lifting-constructs"]{next section}, that make this process easier and the resulting lifted code more efficient.
|
||||||
|
|
||||||
|
@defproc[(union? [value any/c]) boolean?]{
|
||||||
|
Returns true if the given value is a symbolic union. Otherwise returns false.
|
||||||
|
@examples[#:eval rosette-eval
|
||||||
|
(define-symbolic b boolean?)
|
||||||
|
(define u (if b '(1 2) 3))
|
||||||
|
(union? u)
|
||||||
|
(union? b)]
|
||||||
|
}
|
||||||
|
|
||||||
|
@defproc[(union-contents [value union?]) (listof (cons/c (and/c boolean? term?) (not/c union?)))]{
|
||||||
|
Returns a list of guard-value pairs contained in the given union.
|
||||||
|
@examples[#:eval rosette-eval
|
||||||
|
(define-symbolic b boolean?)
|
||||||
|
(define u (if b '(1 2) 3))
|
||||||
|
(union-contents u)]
|
||||||
|
}
|
||||||
|
|
||||||
|
@section[#:tag "sec:lifting-constructs"]{Constructs for Symbolic Lifting}
|
||||||
|
|
||||||
|
|
||||||
|
Rosette provides two main constructs for @tech[#:key "lifted constructs"]{lifting} Racket code to work on symbolic unions: @racket[for/all] and @racket[define-lift]. The @racket[for/all] construct is built into the language. It is used in Rosette's internal code for lifting operations on @tech[#:key "composite datatype"]{composite datatypes}. The @racket[define-lift] construct is syntactic sugar implemented on top of @racket[for/all]; it is exported by the @racket[rosette/lib/reflect/lift] library.
|
||||||
|
|
||||||
|
@declare-exporting[rosette/base/forall rosette/lib/reflect/lift #:use-sources (rosette/base/forall rosette/lib/reflect/lift)]
|
||||||
|
|
||||||
|
@defform[(for/all ([id val-expr]) body)]{
|
||||||
|
If @racket[val-expr] evaluates to a value that is not a @racket[union?],
|
||||||
|
@racket[for/all] behaves like a @racket[let] expression. It binds
|
||||||
|
@racket[id] to the value and evaluates the @racket[body] with that binding.
|
||||||
|
|
||||||
|
If @racket[val-expr] evaluates to a symbolic union, then for each
|
||||||
|
guard-value pair @racket['(#, @var[g] . #, @var[v])] in that union, @racket[for/all]
|
||||||
|
binds @racket[id] to @var[v] and evaluates the @racket[body]
|
||||||
|
under the guard @var[g]. The results of the individual evaluations of
|
||||||
|
the @racket[body] are re-assembled into a single (concrete or symbolic)
|
||||||
|
output value, which is the result of the @racket[for/all] expression.
|
||||||
|
If the evaluation of @racket[body] executes any procedure @var[p] that is neither
|
||||||
|
implemented in nor provided by the @racket[rosette/safe] language, then @var[p]
|
||||||
|
@bold{must be pure}---it may not perform any observable side-effects,
|
||||||
|
such as writes to memory or disk. There is no purity requirement for using procedures
|
||||||
|
that are implemented in or exported by @racket[rosette/safe] (e.g., @racket[vector-set!]).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
The @racket[for/all] construct is useful both for lifting pure Racket procedures to work
|
||||||
|
on symbolic unions and for controling the performance of Rosette's symbolic evaluation.
|
||||||
|
The following examples show both use cases:
|
||||||
|
|
||||||
|
@itemlist[
|
||||||
|
@item{@emph{Lifting a pure Racket procedure
|
||||||
|
to work on symbolic unions.}
|
||||||
|
|
||||||
|
@defs+int[#:eval rosette-eval
|
||||||
|
[(require (only-in racket [string-length racket/string-length]))
|
||||||
|
|
||||||
|
(define (string-length value)
|
||||||
|
(for/all ([str value])
|
||||||
|
(racket/string-length str)))]
|
||||||
|
|
||||||
|
(string-length "abababa")
|
||||||
|
(string-length 3)
|
||||||
|
(define-symbolic b boolean?)
|
||||||
|
(string-length (if b "a" "abababa"))
|
||||||
|
(string-length (if b "a" 3))
|
||||||
|
(asserts)
|
||||||
|
(string-length (if b 3 #f))]}
|
||||||
|
|
||||||
|
@item{@emph{Making symbolic evaluation more efficient.} @(rosette-eval '(clear-asserts))
|
||||||
|
@defs+int[#:eval rosette-eval
|
||||||
|
[(require (only-in racket build-list))
|
||||||
|
|
||||||
|
(define limit 1000)
|
||||||
|
|
||||||
|
(define (slow xs)
|
||||||
|
(and (= (length xs) limit) (car (map add1 xs))))
|
||||||
|
|
||||||
|
(define (fast xs)
|
||||||
|
(for/all ([xs xs]) (slow xs)))
|
||||||
|
|
||||||
|
(define ys (build-list limit identity))
|
||||||
|
|
||||||
|
(define-symbolic a boolean?)
|
||||||
|
|
||||||
|
(define xs (if a ys (cdr ys)))]
|
||||||
|
|
||||||
|
(time (slow xs))
|
||||||
|
(time (fast xs))]
|
||||||
|
|
||||||
|
Note that the above transformation will not always lead to better performance.
|
||||||
|
Experimenting is the best way to determine whether and where to insert
|
||||||
|
performance-guiding @racket[for/all]s.
|
||||||
|
}]
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@defform[(for/all* ([id val-expr] ...+) body)]{
|
||||||
|
Expands to a nested use of @racket[for/all],
|
||||||
|
just like @racket[let*] expands to a nested use of @racket[let].
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@defmodule[rosette/lib/reflect/lift #:no-declare]
|
||||||
|
|
||||||
|
@defform*[((define-lift id [(arg-type ...) racket-procedure-id])
|
||||||
|
(define-lift id [arg-type racket-procedure-id]))]{
|
||||||
|
Binds @racket[id] to a procedure that lifts @racket[racket-procedure-id] to
|
||||||
|
work on symbolic unions. In particular, the lifted procedure will work when given
|
||||||
|
either a concrete Racket value or a symbolic union contains a guarded value of
|
||||||
|
a suitable type, as given by @racket[arg-type]. Note that the lifted procedure
|
||||||
|
will not work on symbolic terms, only on symbolic unions or concrete values. The
|
||||||
|
Racket procedure bound to @racket[racket-procedure-id] must be pure (see @racket[for/all]).
|
||||||
|
|
||||||
|
When @racket[racket-procedure-id] takes a specific number of arguments,
|
||||||
|
the first form should be used, and the type of each argument should be given.
|
||||||
|
When @racket[racket-procedure-id] takes a variable number of arguments,
|
||||||
|
the type of all arguments should be given. Note that the second form omits
|
||||||
|
the parentheses around the argument type to indicate a variable number of
|
||||||
|
arguments, just like Racket's case-lambda form.
|
||||||
|
|
||||||
|
|
||||||
|
The following example shows how to lift Racket's @racket[string-length] procedure
|
||||||
|
to work on symbolic unions that contain strings.
|
||||||
|
|
||||||
|
@defs+int[#:eval rosette-eval
|
||||||
|
[(require rosette/lib/reflect/lift)
|
||||||
|
(require (only-in racket [string-length racket/string-length] string?))
|
||||||
|
|
||||||
|
(define-lift string-length [(string?) racket/string-length])]
|
||||||
|
|
||||||
|
(string-length "abababa")
|
||||||
|
(define-symbolic b boolean?)
|
||||||
|
(string-length (if b "a" "abababa"))
|
||||||
|
(string-length (if b "a" 3))
|
||||||
|
(asserts)]
|
||||||
|
}
|
||||||
|
@(kill-evaluator rosette-eval)
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
#lang scribble/manual
|
||||||
|
|
||||||
|
@(require scriblib/autobib scribble/core (only-in racket match))
|
||||||
|
@(provide (all-defined-out))
|
||||||
|
|
||||||
|
@(define-cite ~cite citet generate-bibliography #:style number-style)
|
||||||
|
|
||||||
|
@(abbreviate-given-names #t)
|
||||||
|
|
||||||
|
@(define rosette:onward13
|
||||||
|
(make-bib
|
||||||
|
#:title @hyperlink["http://homes.cs.washington.edu/~emina/pubs/rosette.onward13.pdf"]{Growing Solver-Aided Languages with Rosette}
|
||||||
|
#:author (authors "Emina Torlak" "Rastislav Bodik")
|
||||||
|
#:date 2013
|
||||||
|
#:location "New Ideas, New Paradigms, and Reflections on Programming and Software (Onward!)"))
|
||||||
|
|
||||||
|
@(define rosette:pldi14
|
||||||
|
(make-bib
|
||||||
|
#:title @hyperlink["http://homes.cs.washington.edu/~emina/pubs/rosette.pldi14.pdf"]{A Lightweight Symbolic Virtual Machine for Solver-Aided Host Languages}
|
||||||
|
#:author (authors "Emina Torlak" "Rastislav Bodik")
|
||||||
|
#:date 2014
|
||||||
|
#:location "Programming Language Design and Implementation (PLDI)"))
|
||||||
|
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
#lang scribble/manual
|
||||||
|
|
||||||
|
@(require (for-label racket) (only-in racket match) scribble/core scribble/html-properties)
|
||||||
|
|
||||||
|
|
||||||
|
@title{The Rosette Guide}
|
||||||
|
@author{Emina Torlak}
|
||||||
|
|
||||||
|
This document is intended both as an introduction to solver-aided programming with Rosette,
|
||||||
|
and as a reference manual for the Rosette language. It assumes @hyperlink["http://racket-lang.org"]{Racket}
|
||||||
|
programming experience, so if you are unfamiliar with Racket,
|
||||||
|
you may want to start by reading @hyperlink["http://docs.racket-lang.org/guide/"]{The Racket Guide}.
|
||||||
|
|
||||||
|
@;Chapters @seclink["ch:getting-started"]{1} and @seclink["ch:essentials"]{2} introduce the Rosette system and illustrate the key concepts of solver-aided programming.
|
||||||
|
@;Chapters @seclink["ch:syntactic-forms"]{3}-@seclink["ch:libraries"]{6} define the core Rosette language
|
||||||
|
@;(@seclink["sec:langs"]{@racket[rosette/safe]}) and describe its main libraries. Chapter @seclink["ch:symbolic-reflection"]{7} and
|
||||||
|
@;@seclink["ch:unsafe"]{8} describe the advanced features of the full language (@seclink["sec:langs"]{@racket[rosette]}). If you are new to Rosette, consider starting with the core language. The full language is richer than the core, but it can also be @seclink["sec:langs"]{harder to use}.
|
||||||
|
@;
|
||||||
|
@;@defmodulelang*[(rosette/safe rosette)]
|
||||||
|
@;
|
||||||
|
@;@(table-of-contents)
|
||||||
|
@;
|
||||||
|
@;@include-section["welcome/welcome.scrbl"]
|
||||||
|
@;@include-section["essentials/essentials.scrbl"]
|
||||||
|
@;@include-section["forms/forms.scrbl"]
|
||||||
|
@;@include-section["datatypes/builtin-datatypes.scrbl"]
|
||||||
|
@;@include-section["datatypes/defined-datatypes.scrbl"]
|
||||||
|
@;@include-section["libs/libraries.scrbl"]
|
||||||
|
@;@include-section["reflection/symbolic-reflection.scrbl"]
|
||||||
|
@;@include-section["unsafe/unsafe.scrbl"]
|
||||||
|
@;
|
||||||
|
@;
|
||||||
|
@;@(require (only-in "refs.scrbl" generate-bibliography))
|
||||||
|
@;@(define bib @(generate-bibliography #:tag "refs" #:sec-title "References"))
|
||||||
|
@;@(match bib
|
||||||
|
@; [(part tag-prefix tags title-content _ to-collect (list (table _ rest)) parts)
|
||||||
|
@; (part tag-prefix tags title-content (style #f '(toc))
|
||||||
|
@; to-collect
|
||||||
|
@; (list (table (style #f (list (attributes '((class . "bib"))))) rest))
|
||||||
|
@; parts)])
|
||||||
|
@;
|
||||||
|
@;@index-section[]
|
||||||
|
|
@ -1,36 +1,35 @@
|
||||||
#lang scribble/manual
|
#lang scribble/manual
|
||||||
|
|
||||||
@(require (for-label racket) scribble/core scribble/example)
|
@(require (for-label racket) scribble/core scribble/eval)
|
||||||
@(require (for-label rosette/base/form/define rosette/query/query rosette/solver/solution
|
@(require (for-label rosette/base/define rosette/query/tools rosette/query/eval rosette/solver/solution
|
||||||
(only-in rosette/base/base assert vc-true? vc) )
|
rosette/base/term (only-in rosette/query/debug define/debug debug)
|
||||||
racket/runtime-path racket/sandbox)
|
(only-in rosette/base/safe assert) ))
|
||||||
@(require (only-in "../refs.scrbl" ~cite rosette:pldi14 rosette:popl22))
|
@(require (only-in "../refs.scrbl" ~cite rosette:pldi14))
|
||||||
@(require "../util/lifted.rkt")
|
@(require "../util/lifted.rkt")
|
||||||
|
|
||||||
@(define-runtime-path root ".")
|
@(define rosette-eval (rosette-evaluator))
|
||||||
@(define rosette-eval (rosette-log-evaluator (logfile root "unsafe-log")))
|
|
||||||
|
|
||||||
@title[#:tag "ch:unsafe"]{Unsafe Operations}
|
@title[#:tag "ch:unsafe"]{Unsafe Operations}
|
||||||
|
|
||||||
Throughout this guide, we have assumed that Rosette programs are
|
Throughout this guide, we have assumed that Rosette programs are
|
||||||
written in the @racketmodname[rosette/safe] dialect of the full language.
|
written in the @racket[rosette/safe] dialect of the full language.
|
||||||
This dialect extends a core subset of Racket with @seclink["ch:essentials"]{solver-aided
|
This dialect extends a core subset of Racket with @seclink["ch:essentials"]{solver-aided
|
||||||
functionality}. In this chapter, we briefly discuss the @racketmodname[rosette]
|
functionality}. In this chapter, we briefly discuss the @racket[rosette]
|
||||||
dialect of the language, which exports all of Racket.
|
dialect of the language, which exports all of Racket.
|
||||||
|
|
||||||
Safe use of the full @racketmodname[rosette] language requires a basic understanding
|
Safe use of the full @racket[rosette] language requires a basic understanding
|
||||||
of how Rosette's Symbolic Virtual Machine (SVM) works @~cite[rosette:pldi14 rosette:popl22].
|
of how Rosette's Symbolic Virtual Machine (SVM) works @~cite[rosette:pldi14].
|
||||||
Briefly, the SVM hijacks the normal Racket execution for all procedures and
|
Briefly, the SVM hijacks the normal Racket execution for all procedures and
|
||||||
constructs that are exported by @racketmodname[rosette/safe]. Any programs that are
|
constructs that are exported by @racket[rosette/safe]. Any programs that are
|
||||||
implemented exclusively in the @racketmodname[rosette/safe] language are therefore
|
implemented exclusively in the @racket[rosette/safe] language are therefore
|
||||||
fully under the SVM's control. This means that the SVM can correctly interpret
|
fully under the SVM's control. This means that the SVM can correctly interpret
|
||||||
the application of a procedure or a macro to a symbolic value, and it
|
the application of a procedure or a macro to a symbolic value, and it
|
||||||
can correctly handle any side-effects (in particular, writes to memory) performed
|
can correctly handle any side-effects (in particular, writes to memory) performed
|
||||||
by @racketmodname[rosette/safe] code.
|
by @racket[rosette/safe] code.
|
||||||
|
|
||||||
The following snippet demonstrates the non-standard execution that the SVM needs to
|
The following snippet demonstrates the non-standard execution that the SVM needs to
|
||||||
perform in order to assign the expected meaning to Rosette code:
|
perform in order to assign the expected meaning to Rosette code:
|
||||||
@examples[#:eval rosette-eval
|
@interaction[#:eval rosette-eval
|
||||||
(define y (vector 0 1 2))
|
(define y (vector 0 1 2))
|
||||||
|
|
||||||
(define-symbolic b boolean?)
|
(define-symbolic b boolean?)
|
||||||
|
|
@ -41,37 +40,36 @@ perform in order to assign the expected meaning to Rosette code:
|
||||||
(vector-set! y 2 4))
|
(vector-set! y 2 4))
|
||||||
|
|
||||||
(code:comment "The state of y correctly accounts for both possibilities:")
|
(code:comment "The state of y correctly accounts for both possibilities:")
|
||||||
(code:comment " * If the solver finds that b must be #t, then the contents")
|
(code:comment " * If the solver finds that b must be #t, then the contents of y will be #(0 3 2).")
|
||||||
(code:comment " of y will be #(0 3 2).")
|
|
||||||
(code:comment " * Otherwise, the contents of y will be #(0 1 4)")
|
(code:comment " * Otherwise, the contents of y will be #(0 1 4)")
|
||||||
|
|
||||||
y
|
y
|
||||||
|
|
||||||
(define sol1 (solve (assert b)))
|
(define env1 (solve (assert b)))
|
||||||
(evaluate y sol1)
|
(evaluate y env1)
|
||||||
|
|
||||||
(define sol2 (solve (assert (not b))))
|
(define env2 (solve (assert (not b))))
|
||||||
(evaluate y sol2)]
|
(evaluate y env2)]
|
||||||
|
|
||||||
Because the SVM controls only the execution of @racketmodname[rosette/safe] code,
|
Because the SVM only controls the execution of @racket[rosette/safe] code,
|
||||||
it cannot, in general, guarantee the safety or correctness of arbitrary @racketmodname[rosette] programs.
|
it cannot, in general, guarantee the safety or correctness of arbitrary @racket[rosette] programs.
|
||||||
As soon as a @racketmodname[rosette] program calls an @tech[#:key "lifted construct"]{unlifted} Racket construct
|
As soon as a @racket[rosette] program calls an @tech[#:key "lifted construct"]{unlifted} Racket construct
|
||||||
(that is, a procedure or a macro not implemented in or provided by the @racketmodname[rosette/safe] language),
|
(that is, a procedure or a macro not implemented in or provided by the @racket[rosette/safe] language),
|
||||||
the execution escapes back to the Racket interpreter. The SVM has no control over the side-effects
|
the execution escapes back to the Racket interpreter. The SVM has no control over the side-effects
|
||||||
performed by the Racket interpreter, or the meaning that it (perhaps incorrectly) assigns to programs
|
performed by the Racket interpreter, or the meaning that it (perhaps incorrectly) assigns to programs
|
||||||
in the presence of symbolic values. As a result, the programmer is responsible for ensuring that
|
in the presence of symbolic values. As a result, the programmer is responsible for ensuring that
|
||||||
a @racketmodname[rosette] program continues to behave correctly after the execution returns from the Racket interpreter.
|
a @racket[rosette] program continues to behave correctly after the execution returns from the Racket interpreter.
|
||||||
|
|
||||||
As an example of incorrect behavior, consider the following @racketmodname[rosette] snippet.
|
As an example of incorrect behavior, consider the following @racket[rosette] snippet.
|
||||||
The procedures @racket[make-hash], @racket[hash-ref], and @racket[hash-clear!] are not in @racketmodname[rosette/safe].
|
The procedures @racket[make-hash], @racket[hash-ref], and @racket[hash-clear!] are not in @racket[rosette/safe].
|
||||||
Whenever they are invoked, the execution escapes to the Racket interpreter.
|
Whenever they are invoked, the execution escapes to the Racket interpreter.
|
||||||
|
|
||||||
@(rosette-eval '(require (only-in racket make-hash hash-clear! hash-ref)))
|
@(rosette-eval '(require (only-in racket make-hash hash-clear! hash-ref)))
|
||||||
@examples[#:eval rosette-eval
|
@defs+int[#:eval rosette-eval
|
||||||
|
|
||||||
(define h (make-hash '((1 . 2))))
|
[(define h (make-hash '((1 . 2))))
|
||||||
(define-symbolic key integer?)
|
(define-symbolic key number?)
|
||||||
(define-symbolic b boolean?)
|
(define-symbolic b boolean?)]
|
||||||
|
|
||||||
|
|
||||||
(code:comment "The following call produces an incorrect value. Intuitively, we expect the")
|
(code:comment "The following call produces an incorrect value. Intuitively, we expect the")
|
||||||
|
|
@ -83,7 +81,6 @@ Whenever they are invoked, the execution escapes to the Racket interpreter.
|
||||||
(code:comment "The following call produces an incorrect state. Intuitively, we expect h")
|
(code:comment "The following call produces an incorrect state. Intuitively, we expect h")
|
||||||
(code:comment "to be empty if b is true and unchanged otherwise.")
|
(code:comment "to be empty if b is true and unchanged otherwise.")
|
||||||
(when b
|
(when b
|
||||||
(pretty-print (vc))
|
|
||||||
(hash-clear! h))
|
(hash-clear! h))
|
||||||
h]
|
h]
|
||||||
|
|
||||||
|
|
@ -91,12 +88,12 @@ When is it safe to use a Racket procedure or macro? The answer depends on their
|
||||||
A conservative rule is to only use an unlifted construct @var[c] in an @deftech{effectively concrete} @tech{program state}.
|
A conservative rule is to only use an unlifted construct @var[c] in an @deftech{effectively concrete} @tech{program state}.
|
||||||
The SVM is in such a state when
|
The SVM is in such a state when
|
||||||
@itemlist[#:style 'ordered
|
@itemlist[#:style 'ordered
|
||||||
@item{the current @tech{verification condition} is true, i.e., @racket[(vc-true? (vc))]; and,}
|
@item{the current @tech{path condition} is @racket[#t];}
|
||||||
|
@item{the @tech{assertion store} is empty; and,}
|
||||||
@item{all local and global variables that may be read by @var[c] contain @deftech{fully concrete value}s. A
|
@item{all local and global variables that may be read by @var[c] contain @deftech{fully concrete value}s. A
|
||||||
value (e.g., a list) is fully concrete if no symbolic values can be reached by recursively traversing its structure.}]
|
value (e.g., a list) is fully concrete if no symbolic values can be reached by recursively traversing its structure.}]
|
||||||
The above uses of @racket[hash-ref] and @racket[hash-clear!] violate these
|
The two uses of @racket[hash-ref] and @racket[hash-clear!] in our buggy example violate the third and first
|
||||||
requirements: @racket[hash-ref] is reading a symbolic value, and @racket[hash-clear!] is
|
requirements, respectively.
|
||||||
evaluated in a state with a symbolic verification condition.
|
|
||||||
|
|
||||||
Being conservative, the above rule disallows many scenarios in which it is still safe to use
|
Being conservative, the above rule disallows many scenarios in which it is still safe to use
|
||||||
Racket constructs. These, however, have to be considered on a case-by-case basis. For example,
|
Racket constructs. These, however, have to be considered on a case-by-case basis. For example,
|
||||||
|
|
@ -104,5 +101,3 @@ it is safe to use Racket's iteration and comprehension constructs, such as @rack
|
||||||
as long as they iterate over concrete sequences, and all guard expressions produce fully concrete values in
|
as long as they iterate over concrete sequences, and all guard expressions produce fully concrete values in
|
||||||
each iteration. In practice, Rosette programs can safely use many common Racket constructs, and with a
|
each iteration. In practice, Rosette programs can safely use many common Racket constructs, and with a
|
||||||
bit of experience, it becomes easy to see when it is okay to break the effectively-concrete rule.
|
bit of experience, it becomes easy to see when it is okay to break the effectively-concrete rule.
|
||||||
|
|
||||||
@(kill-evaluator rosette-eval)
|
|
||||||
|
|
@ -55,8 +55,3 @@ li p {
|
||||||
margin: 0em;
|
margin: 0em;
|
||||||
padding: 0.1em;
|
padding: 0.1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.footnoteblock {
|
|
||||||
margin-top: 1em;
|
|
||||||
border-top: 1px solid black;
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
#lang racket
|
||||||
|
|
||||||
|
(provide select rosette-evaluator)
|
||||||
|
|
||||||
|
(require
|
||||||
|
(for-label racket racket/generic)
|
||||||
|
(only-in rosette rosette union union-contents union?)
|
||||||
|
racket/sandbox
|
||||||
|
(only-in scribble/manual elem racket))
|
||||||
|
|
||||||
|
(define lifted?
|
||||||
|
(let ([lifted (apply set (rosette))])
|
||||||
|
(lambda (id) (set-member? lifted id))))
|
||||||
|
|
||||||
|
(define (select racket-ids)
|
||||||
|
(apply elem
|
||||||
|
(add-between (map (lambda (id) (racket #,#`#,id))
|
||||||
|
(filter lifted? racket-ids)) ", ")))
|
||||||
|
|
||||||
|
(define (rosette-printer v)
|
||||||
|
(match v
|
||||||
|
[(? void?) (void)]
|
||||||
|
[(? custom-write?)
|
||||||
|
((custom-write-accessor v) v (current-output-port) 1)]
|
||||||
|
[(? pair?) (printf "'~a" v)]
|
||||||
|
[(? null?) (printf "'()")]
|
||||||
|
[(? symbol?) (printf "'~a" v)]
|
||||||
|
[_ (printf "~a" v)]))
|
||||||
|
|
||||||
|
(define (rosette-evaluator)
|
||||||
|
(parameterize ([sandbox-output 'string]
|
||||||
|
[sandbox-error-output 'string]
|
||||||
|
[sandbox-path-permissions `((execute ,(byte-regexp #".*")))]
|
||||||
|
[sandbox-memory-limit #f]
|
||||||
|
[sandbox-eval-limits #f]
|
||||||
|
[current-print rosette-printer])
|
||||||
|
(make-evaluator 'rosette/safe)))
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,76 @@
|
||||||
|
#lang scribble/manual
|
||||||
|
|
||||||
|
@(require (for-label racket))
|
||||||
|
|
||||||
|
|
||||||
|
@title[#:tag "ch:getting-started"]{Getting Started}
|
||||||
|
|
||||||
|
Rosette is a @emph{solver-aided} programming system with two components:
|
||||||
|
|
||||||
|
@itemlist[@item{A @emph{programming language} that extends a
|
||||||
|
subset of Racket with @seclink["ch:essentials"]{constructs} for accessing
|
||||||
|
a constraint solver. With the solver's help, Rosette
|
||||||
|
can answer interesting questions about programs---such as, whether
|
||||||
|
they are buggy and if so, how to repair them.}
|
||||||
|
@item{A @emph{symbolic virtual machine} (SVM) that executes Rosette programs and
|
||||||
|
compiles them to logical constraints. The SVM enables Rosette
|
||||||
|
to use the solver to automatically reason about program behaviors.}]
|
||||||
|
|
||||||
|
The name "Rosette" refers both to the language and the whole system.
|
||||||
|
|
||||||
|
@section[#:tag "sec:get"]{Installing Rosette}
|
||||||
|
|
||||||
|
Rosette is built on top of Racket, and it ships with a Java-based solver.
|
||||||
|
To install Rosette, you will need to
|
||||||
|
|
||||||
|
@itemlist[@item{@hyperlink["http://docs.racket-lang.org"]{Download} and install Racket (version 6.1 or later).}
|
||||||
|
@item{Make sure that the default Java installation on your machine is a 64-bit server VM, version 1.7x:
|
||||||
|
@nested{
|
||||||
|
@verbatim{> java -version
|
||||||
|
java version "1.7.0_25"
|
||||||
|
Java(TM) SE Runtime Environment (build 1.7.0_25-b15)
|
||||||
|
Java HotSpot(TM) 64-Bit Server VM (build 23.25-b01, mixed mode)}}}
|
||||||
|
@item{Obtain the Rosette source code from GitHub:
|
||||||
|
@nested{
|
||||||
|
@verbatim|{> git clone git@github.com:emina/rosette.git
|
||||||
|
> ls rosette
|
||||||
|
LICENSE README.md bin guide rosette sdsl test}|}}
|
||||||
|
@item{Use Racket's @tt{raco} tool to install Rosette as one of your Racket collections:
|
||||||
|
@nested{
|
||||||
|
@verbatim|{> cd rosette
|
||||||
|
> raco link rosette
|
||||||
|
> raco setup -l rosette}|}}]
|
||||||
|
|
||||||
|
Your Rosette installation includes binaries for the
|
||||||
|
@hyperlink["http://alloy.mit.edu/kodkod/"]{Kodkod}
|
||||||
|
constraint solver, and it is ready for use as-is. If you
|
||||||
|
want to experiment with different solvers, you can also
|
||||||
|
(optionally) install the the @hyperlink["http://z3.codeplex.com"]{Z3}
|
||||||
|
solver from Microsoft Research, or the @hyperlink["http://cvc4.cs.nyu.edu/web/"]{CVC4}
|
||||||
|
solver from NYU: simply place the solver binary into the @tt{rosette/bin} folder.
|
||||||
|
|
||||||
|
@section[#:tag "sec:run"]{Interacting with Rosette}
|
||||||
|
|
||||||
|
You can interact with Rosette programs just as you would with Racket programs: either through the @hyperlink["http://docs.racket-lang.org/guide/intro.html"]{DrRacket} IDE or through the @hyperlink["http://docs.racket-lang.org/guide/other-editors.html"]{@tt{racket}} command-line interpreter. We suggest that you use DrRacket, especially at the beginning.
|
||||||
|
|
||||||
|
Example Rosette programs can be found in the @tt{rosette/sdsl} folder. Most of these are implemented in @emph{solver-aided domain-specific languages} (SDSLs) that are embedded in the Rosette language. To interact with an @hyperlink["https://github.com/emina/rosette/blob/master/sdsl/fsm/demo.rkt"]{example program}, open it in DrRacket and hit Run!
|
||||||
|
|
||||||
|
@section[#:tag "sec:langs"]{Rosette Dialects}
|
||||||
|
|
||||||
|
The Rosette system ships with two dialects of the Rosette language:
|
||||||
|
|
||||||
|
@itemlist[@item{a @emph{safe} dialect, which is used throughout this guide, and}
|
||||||
|
@item{an @emph{unsafe} dialect, which is briefly described in the @seclink["ch:unsafe"]{last chapter}.}]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
To use the safe dialect, start your programs with the following line:
|
||||||
|
|
||||||
|
@racketmod[rosette/safe]
|
||||||
|
|
||||||
|
To use the unsafe dialect, type this line instead:
|
||||||
|
|
||||||
|
@racketmod[rosette]
|
||||||
|
|
||||||
|
We strongly recommend that you start with the safe dialect, which includes a core subset of Racket. The unsafe dialect includes all of Racket, but unless you understand and observe the restrictions on using non-core features, your seemingly correct programs may crash or produce unexpected results.
|
||||||
|
|
||||||
|
|
@ -1,469 +0,0 @@
|
||||||
#lang scribble/manual
|
|
||||||
|
|
||||||
@(require (for-label
|
|
||||||
rosette/base/form/define
|
|
||||||
rosette/base/core/term
|
|
||||||
(only-in rosette/base/core/union union?)
|
|
||||||
(only-in rosette/base/base bv bv? bitvector bitvector? bitvector-size
|
|
||||||
bveq bvslt bvsgt bvsle bvsge bvult bvugt bvule bvuge
|
|
||||||
bvnot bvor bvand bvxor bvshl bvlshr bvashr
|
|
||||||
bvneg bvadd bvsub bvmul bvudiv bvsdiv bvurem bvsrem bvsmod
|
|
||||||
concat extract sign-extend zero-extend
|
|
||||||
integer->bitvector bitvector->integer bitvector->natural
|
|
||||||
bit lsb msb bvzero? bvadd1 bvsub1
|
|
||||||
bvsmin bvsmax bvumin bvumax
|
|
||||||
rotate-left rotate-right bvrol bvror
|
|
||||||
bool->bitvector bitvector->bool bitvector->bits
|
|
||||||
assert vc))
|
|
||||||
(for-label racket)
|
|
||||||
scribble/core scribble/html-properties scribble/examples racket/sandbox
|
|
||||||
"../util/lifted.rkt")
|
|
||||||
|
|
||||||
|
|
||||||
@(define rosette-eval (rosette-evaluator))
|
|
||||||
|
|
||||||
@title[#:tag "sec:bitvectors"]{Bitvectors}
|
|
||||||
|
|
||||||
@declare-exporting[rosette/base/base #:use-sources (rosette/base/base)]
|
|
||||||
|
|
||||||
Rosette extends Racket with a primitive bitvector datatype whose values are
|
|
||||||
fixed-size words---or, machine integers. Mainstream programming languages, such as
|
|
||||||
C or Java, support bitvector types with a few fixed sizes (e.g., 8 bits, 16 bits,
|
|
||||||
and 32 bits). Rosette supports bitvectors of arbitrary size, as well as both signed and
|
|
||||||
unsigned versions of various bitvector operations (such as comparisons, division, remainder, etc.).
|
|
||||||
Technically, Rosette's bitvector datatype embeds the
|
|
||||||
@hyperlink["http://smtlib.cs.uiowa.edu/logics-all.shtml#QF_BV"]{theory of bitvectors}
|
|
||||||
into a programming language.
|
|
||||||
|
|
||||||
@examples[#:eval rosette-eval
|
|
||||||
(code:line (bv 4 (bitvector 7)) (code:comment "A bitvector literal of size 7."))
|
|
||||||
(code:line (bv 4 7) (code:comment "A shorthand for the same literal."))
|
|
||||||
(code:line (define-symbolic x y (bitvector 7)) (code:comment "Symbolic bitvector constants."))
|
|
||||||
(code:line (bvslt (bv 4 7) (bv -1 7)) (code:comment "Signed 7-bit < comparison of 4 and -1."))
|
|
||||||
(code:line (bvult (bv 4 7) (bv -1 7)) (code:comment "Unsigned 7-bit < comparison of 4 and -1."))
|
|
||||||
(define-symbolic b boolean?)
|
|
||||||
(code:line (bvadd x (if b y (bv 3 4))) (code:comment "This typechecks only when b is true,"))
|
|
||||||
(code:line (vc) (code:comment "so Rosette emits a corresponding assertion."))]
|
|
||||||
|
|
||||||
@defproc[(bitvector [size (and/c integer? positive? (not/c term?) (not/c union?))]) bitvector?]{
|
|
||||||
|
|
||||||
Returns a type predicate that recognizes bitvectors of the given @racket[size].
|
|
||||||
Note that @racket[size] must be a concrete positive integer.
|
|
||||||
The type predicate itself is recognized by the @racket[bitvector?] predicate.
|
|
||||||
|
|
||||||
@examples[#:eval rosette-eval
|
|
||||||
(define bv6? (bitvector 6))
|
|
||||||
(bv6? 1)
|
|
||||||
(bv6? (bv 3 6))
|
|
||||||
(bv6? (bv 3 5))
|
|
||||||
(define-symbolic b boolean?)
|
|
||||||
(bv6? (if b (bv 3 6) #t))]
|
|
||||||
}
|
|
||||||
|
|
||||||
@defproc[(bitvector? [v any/c]) boolean?]{
|
|
||||||
|
|
||||||
Returns true if @racket[v] is a concrete type predicate that recognizes bitvector values.
|
|
||||||
|
|
||||||
@examples[#:eval rosette-eval
|
|
||||||
(define bv6? (bitvector 6))
|
|
||||||
(define bv7? (bitvector 7))
|
|
||||||
(define-symbolic b boolean?)
|
|
||||||
(code:line (bitvector? bv6?) (code:comment "A concrete bitvector type."))
|
|
||||||
(code:line (bitvector? (if b bv6? bv7?)) (code:comment "Not a concrete type."))
|
|
||||||
(code:line (bitvector? integer?) (code:comment "Not a bitvector type."))
|
|
||||||
(code:line (bitvector? 3) (code:comment "Not a type."))]}
|
|
||||||
|
|
||||||
@defproc[(bv [val (and/c integer? (not/c term?) (not/c union?))]
|
|
||||||
[size (and/c (or/c bitvector? (and/c integer? positive?))
|
|
||||||
(not/c term?) (not/c union?))]) bv?]{
|
|
||||||
Returns a bitvector literal of the given @racket[size], which may be given either as a
|
|
||||||
concrete @racket[bitvector?] type or a concrete positive integer.}
|
|
||||||
|
|
||||||
@defproc[(bv? [v any/c]) boolean?]{
|
|
||||||
Recognizes concrete or symbolic bitvector values of any size.
|
|
||||||
@examples[#:eval rosette-eval
|
|
||||||
(bv? 1)
|
|
||||||
(bv? (bv 1 1))
|
|
||||||
(bv? (bv 2 2))
|
|
||||||
(define-symbolic b boolean?)
|
|
||||||
(bv? (if b (bv 3 6) #t))]
|
|
||||||
}
|
|
||||||
|
|
||||||
@(rosette-eval '(clear-vc!))
|
|
||||||
|
|
||||||
@section{Comparison Operators}
|
|
||||||
|
|
||||||
@defproc*[([(bveq [x (bitvector n)] [y (bitvector n)]) boolean?]
|
|
||||||
[(bvslt [x (bitvector n)] [y (bitvector n)]) boolean?]
|
|
||||||
[(bvult [x (bitvector n)] [y (bitvector n)]) boolean?]
|
|
||||||
[(bvsle [x (bitvector n)] [y (bitvector n)]) boolean?]
|
|
||||||
[(bvule [x (bitvector n)] [y (bitvector n)]) boolean?]
|
|
||||||
[(bvsgt [x (bitvector n)] [y (bitvector n)]) boolean?]
|
|
||||||
[(bvugt [x (bitvector n)] [y (bitvector n)]) boolean?]
|
|
||||||
[(bvsge [x (bitvector n)] [y (bitvector n)]) boolean?]
|
|
||||||
[(bvuge [x (bitvector n)] [y (bitvector n)]) boolean?])]{
|
|
||||||
|
|
||||||
Compares two bitvector values of the same bitvector type.
|
|
||||||
Comparison relations include
|
|
||||||
equality (@racket[bveq]) and signed / unsigned versions of
|
|
||||||
<, <=, >, and >= (@racket[bvslt], @racket[bvult], @racket[bvsle], @racket[bvule],
|
|
||||||
@racket[bvsgt], and @racket[bvugt]).
|
|
||||||
|
|
||||||
@examples[#:eval rosette-eval
|
|
||||||
(code:line (define-symbolic u v (bitvector 7)) (code:comment "Symbolic bitvector constants."))
|
|
||||||
(code:line (bvslt (bv 4 7) (bv -1 7)) (code:comment "Signed 7-bit < comparison of 4 and -1."))
|
|
||||||
(code:line (bvult (bv 4 7) (bv -1 7)) (code:comment "Unsigned 7-bit < comparison of 4 and -1."))
|
|
||||||
(define-symbolic b boolean?)
|
|
||||||
(code:line (bvsge u (if b v (bv 3 4))) (code:comment "This typechecks only when b is true,"))
|
|
||||||
(code:line (vc) (code:comment "so Rosette emits a corresponding assertion."))]
|
|
||||||
}
|
|
||||||
|
|
||||||
@(rosette-eval '(clear-vc!))
|
|
||||||
|
|
||||||
@section{Bitwise Operators}
|
|
||||||
|
|
||||||
@defproc[(bvnot [x (bitvector n)]) (bitvector n)]{
|
|
||||||
|
|
||||||
Returns the bitwise negation of the given bitvector value.
|
|
||||||
|
|
||||||
@examples[#:eval rosette-eval
|
|
||||||
(bvnot (bv -1 4))
|
|
||||||
(bvnot (bv 0 4))
|
|
||||||
(define-symbolic b boolean?)
|
|
||||||
(code:line (bvnot (if b 0 (bv 0 4))) (code:comment "This typechecks only when b is false,"))
|
|
||||||
(code:line (vc) (code:comment "so Rosette emits a corresponding assertion."))]
|
|
||||||
}
|
|
||||||
|
|
||||||
@(rosette-eval '(clear-vc!))
|
|
||||||
|
|
||||||
@defproc*[([(bvand [x (bitvector n)] ...+) (bitvector n)]
|
|
||||||
[(bvor [x (bitvector n)] ...+) (bitvector n)]
|
|
||||||
[(bvxor [x (bitvector n)] ...+) (bitvector n)])]{
|
|
||||||
|
|
||||||
Returns the bitwise ``and'', ``or'', ``xor'' of one or more bitvector values of the same type.
|
|
||||||
|
|
||||||
@examples[#:eval rosette-eval
|
|
||||||
(bvand (bv -1 4) (bv 2 4))
|
|
||||||
(bvor (bv 0 3) (bv 1 3))
|
|
||||||
(bvxor (bv -1 5) (bv 1 5))
|
|
||||||
(define-symbolic b boolean?)
|
|
||||||
(code:line
|
|
||||||
(bvand (bv -1 4)
|
|
||||||
(if b 0 (bv 2 4))) (code:comment "This typechecks only when b is false,"))
|
|
||||||
(code:line
|
|
||||||
(vc) (code:comment "so Rosette emits a corresponding assertion."))]
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@(rosette-eval '(clear-vc!))
|
|
||||||
@defproc*[([(bvshl [x (bitvector n)] [y (bitvector n)]) (bitvector n)]
|
|
||||||
[(bvlshr [x (bitvector n)] [y (bitvector n)]) (bitvector n)]
|
|
||||||
[(bvashr [x (bitvector n)] [y (bitvector n)]) (bitvector n)])]{
|
|
||||||
|
|
||||||
Returns the left, logical right, or arithmetic right shift of @racket[x] by
|
|
||||||
@racket[y] bits, where @racket[x] and @racket[y] are bitvector values of the same type.
|
|
||||||
|
|
||||||
@examples[#:eval rosette-eval
|
|
||||||
(bvshl (bv 1 4) (bv 2 4))
|
|
||||||
(bvlshr (bv -1 3) (bv 1 3))
|
|
||||||
(bvashr (bv -1 5) (bv 1 5))
|
|
||||||
(define-symbolic b boolean?)
|
|
||||||
(code:line (bvshl (bv -1 4)
|
|
||||||
(if b 0 (bv 2 4))) (code:comment "This typechecks only when b is false,"))
|
|
||||||
(code:line (vc) (code:comment "so Rosette emits a corresponding assertion."))]
|
|
||||||
}
|
|
||||||
|
|
||||||
@section{Arithmetic Operators}
|
|
||||||
|
|
||||||
@defproc[(bvneg [x (bitvector n)]) (bitvector n)]{
|
|
||||||
|
|
||||||
Returns the arithmetic negation of the given bitvector value.
|
|
||||||
|
|
||||||
@examples[#:eval rosette-eval
|
|
||||||
(bvneg (bv -1 4))
|
|
||||||
(bvneg (bv 0 4))
|
|
||||||
(define-symbolic z (bitvector 3))
|
|
||||||
(bvneg z)]
|
|
||||||
}
|
|
||||||
|
|
||||||
@(rosette-eval '(clear-vc!))
|
|
||||||
@(rosette-eval '(clear-terms!))
|
|
||||||
|
|
||||||
@defproc*[([(bvadd [x (bitvector n)] ...+) (bitvector n)]
|
|
||||||
[(bvsub [x (bitvector n)] ...+) (bitvector n)]
|
|
||||||
[(bvmul [x (bitvector n)] ...+) (bitvector n)])]{
|
|
||||||
|
|
||||||
Returns the sum, difference, or product of one or more bitvector values of the same type.
|
|
||||||
|
|
||||||
@examples[#:eval rosette-eval
|
|
||||||
(bvadd (bv -1 4) (bv 2 4))
|
|
||||||
(bvsub (bv 0 3) (bv 1 3))
|
|
||||||
(bvmul (bv -1 5) (bv 1 5))
|
|
||||||
(define-symbolic b boolean?)
|
|
||||||
(bvadd (bv -1 4) (bv 2 4) (if b (bv 1 4) "bad"))
|
|
||||||
(vc)]
|
|
||||||
}
|
|
||||||
|
|
||||||
@(rosette-eval '(clear-vc!))
|
|
||||||
|
|
||||||
@defproc*[([(bvsdiv [x (bitvector n)] [y (bitvector n)]) (bitvector n)]
|
|
||||||
[(bvudiv [x (bitvector n)] [y (bitvector n)]) (bitvector n)]
|
|
||||||
[(bvsrem [x (bitvector n)] [y (bitvector n)]) (bitvector n)]
|
|
||||||
[(bvurem [x (bitvector n)] [y (bitvector n)]) (bitvector n)]
|
|
||||||
[(bvsmod [x (bitvector n)] [y (bitvector n)]) (bitvector n)])]{
|
|
||||||
|
|
||||||
Returns (un)signed quotient, remainder, or modulo of two bitvector values of the same type.
|
|
||||||
All five operations are defined even when the second argument is zero.
|
|
||||||
|
|
||||||
@examples[#:eval rosette-eval
|
|
||||||
(bvsdiv (bv -3 4) (bv 2 4))
|
|
||||||
(bvudiv (bv -3 3) (bv 2 3))
|
|
||||||
(bvsmod (bv 1 5) (bv 0 5))
|
|
||||||
(define-symbolic b boolean?)
|
|
||||||
(bvsrem (bv -3 4) (if b (bv 2 4) "bad"))
|
|
||||||
(vc)]
|
|
||||||
}
|
|
||||||
|
|
||||||
@(rosette-eval '(clear-vc!))
|
|
||||||
|
|
||||||
@section{Conversion Operators}
|
|
||||||
|
|
||||||
@defproc[(concat [x bv?] ...+) bv?]{
|
|
||||||
|
|
||||||
Returns the bitwise concatenation of the given bitvector values.
|
|
||||||
|
|
||||||
@examples[#:eval rosette-eval
|
|
||||||
(concat (bv -1 4) (bv 0 1) (bv -1 3))
|
|
||||||
(define-symbolic b boolean?)
|
|
||||||
(concat (bv -1 4) (if b (bv 0 1) (bv 0 2)) (bv -1 3))]
|
|
||||||
}
|
|
||||||
|
|
||||||
@defproc[(extract [i integer?] [j integer?] [x (bitvector n)]) (bitvector (+ 1 (- i j)))]{
|
|
||||||
|
|
||||||
Extracts bits @racket[i] down to @racket[j] from a bitvector of size @racket[n], yielding a
|
|
||||||
bitvector of size i - j + 1. This procedure assumes that @racket[n] > @racket[i] >= @racket[j] >= 0.
|
|
||||||
|
|
||||||
@examples[#:eval rosette-eval
|
|
||||||
(extract 2 1 (bv -1 4))
|
|
||||||
(extract 3 3 (bv 1 4))
|
|
||||||
(define-symbolic i j integer?)
|
|
||||||
(extract i j (bv 1 2))
|
|
||||||
(vc)]
|
|
||||||
}
|
|
||||||
|
|
||||||
@(rosette-eval '(clear-vc!))
|
|
||||||
|
|
||||||
@defproc*[([(sign-extend [x bv?] [t (or/c bitvector? union?)]) bv?]
|
|
||||||
[(zero-extend [x bv?] [t (or/c bitvector? union?)]) bv?])]{
|
|
||||||
|
|
||||||
Returns a bitvector of type @racket[t] that represents the (un)signed
|
|
||||||
extension of the bitvector @racket[x].
|
|
||||||
Note that both @racket[x] and @racket[t] may be symbolic. The size of @racket[t]
|
|
||||||
must not be smaller than the size of @racket[x]'s type.
|
|
||||||
|
|
||||||
@examples[#:eval rosette-eval
|
|
||||||
(sign-extend (bv -3 4) (bitvector 6))
|
|
||||||
(zero-extend (bv -3 4) (bitvector 6))
|
|
||||||
(define-symbolic b c boolean?)
|
|
||||||
(zero-extend (bv -3 4) (if b (bitvector 5) (bitvector 6)))
|
|
||||||
(zero-extend (bv -3 4) (if b (bitvector 5) "bad"))
|
|
||||||
(vc)
|
|
||||||
(zero-extend (bv -3 4) (if c (bitvector 5) (bitvector 1)))
|
|
||||||
(vc)]
|
|
||||||
}
|
|
||||||
|
|
||||||
@(rosette-eval '(clear-vc!))
|
|
||||||
|
|
||||||
@defproc*[([(bitvector->integer [x bv?]) integer?]
|
|
||||||
[(bitvector->natural [x bv?]) integer?])]{
|
|
||||||
|
|
||||||
Returns the (un)signed integer value of the given bitvector.
|
|
||||||
|
|
||||||
@examples[#:eval rosette-eval
|
|
||||||
(bitvector->integer (bv -1 4))
|
|
||||||
(bitvector->natural (bv -1 4))
|
|
||||||
(define-symbolic b boolean?)
|
|
||||||
(bitvector->integer (if b (bv -1 3) (bv -3 4)))
|
|
||||||
(bitvector->integer (if b (bv -1 3) "bad"))
|
|
||||||
(vc)]
|
|
||||||
}
|
|
||||||
|
|
||||||
@(rosette-eval '(clear-vc!))
|
|
||||||
|
|
||||||
@defproc*[([(integer->bitvector [i integer?] [t (or/c bitvector? union?)]) bv?])]{
|
|
||||||
|
|
||||||
Returns a bitvector of type @racket[t] that represents the @var[k] lowest order bits
|
|
||||||
of the integer @racket[i], where @var[k] is the size of @racket[t].
|
|
||||||
Note that both @racket[i] and @racket[t] may be symbolic.
|
|
||||||
|
|
||||||
@examples[#:eval rosette-eval
|
|
||||||
(integer->bitvector 4 (bitvector 2))
|
|
||||||
(integer->bitvector 15 (bitvector 4))
|
|
||||||
(define-symbolic b c boolean?)
|
|
||||||
(integer->bitvector (if b pi 3) (if c (bitvector 5) (bitvector 6)))
|
|
||||||
(vc)]
|
|
||||||
}
|
|
||||||
|
|
||||||
@section{Additional Operators}
|
|
||||||
|
|
||||||
@(rosette-eval '(clear-vc!))
|
|
||||||
|
|
||||||
@defproc[(bit [i integer?] [x (bitvector n)]) (bitvector 1)]{
|
|
||||||
|
|
||||||
Extracts the @racket[i]th bit from the bitvector @racket[x] of size @racket[n], yielding a
|
|
||||||
bitvector of size 1. This procedure assumes that @racket[n] > @racket[i] >= 0.
|
|
||||||
|
|
||||||
@examples[#:eval rosette-eval
|
|
||||||
(bit 1 (bv 3 4))
|
|
||||||
(bit 2 (bv 1 4))
|
|
||||||
(define-symbolic i integer?)
|
|
||||||
(define-symbolic x (bitvector 4))
|
|
||||||
(bit i x)
|
|
||||||
(vc)]
|
|
||||||
}
|
|
||||||
|
|
||||||
@(rosette-eval '(clear-vc!))
|
|
||||||
|
|
||||||
@defproc*[([(lsb [x (bitvector n)]) (bitvector 1)]
|
|
||||||
[(msb [x (bitvector n)]) (bitvector 1)])]{
|
|
||||||
|
|
||||||
Returns the least or most significant bit of @racket[x].
|
|
||||||
|
|
||||||
@examples[#:eval rosette-eval
|
|
||||||
(lsb (bv 3 4))
|
|
||||||
(msb (bv 3 4))
|
|
||||||
(define-symbolic x (bitvector 4))
|
|
||||||
(define-symbolic y (bitvector 8))
|
|
||||||
(lsb (if b x y))
|
|
||||||
(msb (if b x y))
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
@(rosette-eval '(clear-vc!))
|
|
||||||
|
|
||||||
@defproc[(bvzero? [x (bitvector n)]) boolean?]{
|
|
||||||
|
|
||||||
Returns @racket[(bveq x (bv 0 n))].
|
|
||||||
|
|
||||||
@examples[#:eval rosette-eval
|
|
||||||
(define-symbolic x (bitvector 4))
|
|
||||||
(bvzero? x)
|
|
||||||
(define-symbolic y (bitvector 8))
|
|
||||||
(bvzero? y)
|
|
||||||
(define-symbolic b boolean?)
|
|
||||||
(bvzero? (if b x y))
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
@(rosette-eval '(clear-vc!))
|
|
||||||
|
|
||||||
@defproc*[([(bvadd1 [x (bitvector n)]) (bitvector n)]
|
|
||||||
[(bvsub1 [x (bitvector n)]) (bitvector n)])]{
|
|
||||||
|
|
||||||
Returns @racket[(bvadd x (bv 1 n))] or @racket[(bvsub x (bv 1 n))].
|
|
||||||
|
|
||||||
@examples[#:eval rosette-eval
|
|
||||||
(define-symbolic x (bitvector 4))
|
|
||||||
(bvadd1 x)
|
|
||||||
(define-symbolic y (bitvector 8))
|
|
||||||
(bvsub1 y)
|
|
||||||
(define-symbolic b boolean?)
|
|
||||||
(bvadd1 (if b x y))
|
|
||||||
(bvsub1 (if b x y))
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@(rosette-eval '(clear-vc!))
|
|
||||||
|
|
||||||
@defproc*[([(bvsmin [x (bitvector n)] ...+) (bitvector n)]
|
|
||||||
[(bvumin [x (bitvector n)] ...+) (bitvector n)]
|
|
||||||
[(bvsmax [x (bitvector n)] ...+) (bitvector n)]
|
|
||||||
[(bvumax [x (bitvector n)] ...+) (bitvector n)])]{
|
|
||||||
|
|
||||||
Returns the (un)signed minimum or maximum of one or more bitvector values of the same type.
|
|
||||||
|
|
||||||
@examples[#:eval rosette-eval
|
|
||||||
(bvsmin (bv -1 4) (bv 2 4))
|
|
||||||
(bvumin (bv -1 4) (bv 2 4))
|
|
||||||
(bvsmax (bv -1 4) (bv 2 4))
|
|
||||||
(bvumax (bv -1 4) (bv 2 4))
|
|
||||||
(define-symbolic b boolean?)
|
|
||||||
(bvsmin (bv -1 4) (bv 2 4) (if b (bv 1 4) (bv 3 8)))
|
|
||||||
(vc)]
|
|
||||||
}
|
|
||||||
|
|
||||||
@(rosette-eval '(clear-vc!))
|
|
||||||
|
|
||||||
@defproc*[([(bvrol [x (bitvector n)] [y (bitvector n)]) (bitvector n)]
|
|
||||||
[(bvror [x (bitvector n)] [y (bitvector n)]) (bitvector n)])]{
|
|
||||||
|
|
||||||
Returns the left or right rotation of @racket[x] by @racket[(bvurem y n)] bits, where
|
|
||||||
@racket[x] and @racket[y] are bitvector values of the same type.
|
|
||||||
|
|
||||||
@examples[#:eval rosette-eval
|
|
||||||
(bvrol (bv 3 4) (bv 2 4))
|
|
||||||
(bvrol (bv 3 4) (bv -2 4))
|
|
||||||
(define-symbolic b boolean?)
|
|
||||||
(code:line
|
|
||||||
(bvror (bv 3 4)
|
|
||||||
(if b 0 (bv 2 4))) (code:comment "This typechecks only when b is false,"))
|
|
||||||
(code:line
|
|
||||||
(vc) (code:comment "so Rosette emits a corresponding assertion."))]
|
|
||||||
}
|
|
||||||
|
|
||||||
@(rosette-eval '(clear-vc!))
|
|
||||||
|
|
||||||
@defproc*[([(rotate-left [i integer?] [x (bitvector n)]) (bitvector n)]
|
|
||||||
[(rotate-right [i integer?] [x (bitvector n)]) (bitvector n)])]{
|
|
||||||
|
|
||||||
Returns the left or right rotation of @racket[x] by @racket[i] bits.
|
|
||||||
These procedures assume that @racket[n] > @racket[i] >= 0. See @racket[bvrol]
|
|
||||||
and @racket[bvror] for an alternative way to perform rotations that usually
|
|
||||||
leads to faster solving times.
|
|
||||||
|
|
||||||
@examples[#:eval rosette-eval
|
|
||||||
(rotate-left 3 (bv 3 4))
|
|
||||||
(rotate-right 1 (bv 3 4))
|
|
||||||
(define-symbolic i integer?)
|
|
||||||
(define-symbolic b boolean?)
|
|
||||||
(rotate-left i (if b (bv 3 4) (bv 7 8)))
|
|
||||||
(vc)
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
@(rosette-eval '(clear-vc!))
|
|
||||||
|
|
||||||
@defproc[(bitvector->bits [x (bitvector n)]) (listof (bitvector 1))]{
|
|
||||||
|
|
||||||
Returns the bits of @racket[x] as a list, i.e., @racket[(list (bit 0 x) ... (bit (- n 1) x))].
|
|
||||||
|
|
||||||
@examples[#:eval rosette-eval
|
|
||||||
(bitvector->bits (bv 3 4))
|
|
||||||
(define-symbolic y (bitvector 2))
|
|
||||||
(bitvector->bits y)
|
|
||||||
(define-symbolic b boolean?)
|
|
||||||
(bitvector->bits (if b (bv 3 4) y)) ]
|
|
||||||
}
|
|
||||||
|
|
||||||
@defproc[(bitvector->bool [x (bitvector n)]) boolean?]{
|
|
||||||
Returns @racket[(not (bvzero? x))].
|
|
||||||
}
|
|
||||||
|
|
||||||
@(rosette-eval '(clear-vc!))
|
|
||||||
|
|
||||||
@defproc[(bool->bitvector [b any/c] [t (or/c positive-integer? (bitvector n)) (bitvector 1)]) bv?]{
|
|
||||||
|
|
||||||
Returns @racket[(bv 0 t)] if @racket[(false? b)] and otherwise returns @racket[(bv 1 t)], where
|
|
||||||
@racket[t] is @racket[(bitvector 1)] by default. If provided, @racket[t] must be a concrete positive
|
|
||||||
integer or a concrete bitvector type value.
|
|
||||||
|
|
||||||
@examples[#:eval rosette-eval
|
|
||||||
(bool->bitvector #f 3)
|
|
||||||
(bool->bitvector "non-false-value")
|
|
||||||
(define-symbolic b boolean?)
|
|
||||||
(bool->bitvector b)
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
@(kill-evaluator rosette-eval)
|
|
||||||
|
|
@ -1,187 +0,0 @@
|
||||||
#lang scribble/manual
|
|
||||||
|
|
||||||
@(require (for-label
|
|
||||||
rosette/base/form/define rosette/query/form rosette/query/query rosette/solver/solution
|
|
||||||
rosette/base/core/term (only-in rosette/query/finitize current-bitwidth)
|
|
||||||
(only-in rosette/base/base ! && || => <=> exists forall function? assert vc with-vc
|
|
||||||
result-state result-value))
|
|
||||||
(except-in (for-label racket) =>)
|
|
||||||
scribble/core scribble/html-properties scribble/examples racket/sandbox racket/runtime-path
|
|
||||||
"../util/lifted.rkt")
|
|
||||||
|
|
||||||
|
|
||||||
@(define-runtime-path root ".")
|
|
||||||
@(define rosette-eval (rosette-log-evaluator (logfile root "bools-log")))
|
|
||||||
|
|
||||||
@(define bools (select '(boolean? false? true false boolean=? not nand nor implies xor)))
|
|
||||||
|
|
||||||
@(define nums (select (remove* '(expt) '(number? complex? real? rational? integer? exact-integer? exact-nonnegative-integer? exact-positive-integer? inexact-real? fixnum? flonum? double-flonum? single-flonum? zero? positive? negative? even? odd? exact? inexact? inexact->exact exact->inexact real->single-flonum real->double-flonum + - * / quotient remainder quotient/ modulo add1 sub1 abs max min gcd lcm round floor ceiling truncate numerator denominator rationalize = < <= > >= sqrt integer-sqrt integer-sqrt/ expt exp log sin cos tan asin acos atan make-rectangular make-polar real-part imag-part magnitude angle bitwise-ior bitwise-and bitwise-xor bitwise-not bitwise-bit-set? bitwise-bit-field arithmetic-shift integer-length random random-seed make-pseudo-random-generator pseudo-random-generator? current-pseudo-random-generator pseudo-random-generator->vector vector->pseudo-random-generator vector->pseudo-random-generator! pseudo-random-generator-vector? number->string string->number real->decimal-string integer-bytes->integer integer->integer-bytes floating-point-bytes->real real->floating-point-bytes system-big-endian? pi pi.f degrees->radians radians->degrees sqr sgn conjugate sinh cosh tanh exact-round exact-floor exact-ceiling exact-truncate order-of-magnitude nan? infinite?))))
|
|
||||||
|
|
||||||
|
|
||||||
@title[#:tag "sec:bools+ints+reals"]{Booleans, Integers, and Reals}
|
|
||||||
|
|
||||||
@declare-exporting[rosette/base/base #:use-sources (rosette/base/base)]
|
|
||||||
|
|
||||||
Rosette lifts the following operations on booleans, integers, and reals:
|
|
||||||
@tabular[#:style (style #f (list (attributes '((id . "lifted")(class . "boxed")))))
|
|
||||||
(list (list @elem{Booleans} @bools)
|
|
||||||
(list @elem{Integers and Reals} @nums))]
|
|
||||||
|
|
||||||
Lifted boolean operations retain their Racket semantics on both concrete and symbolic values.
|
|
||||||
In particular, Rosette extends the intepretation of these operations to work on symbolic values in (logically) the
|
|
||||||
same way that they work on concrete values.
|
|
||||||
|
|
||||||
@examples[#:eval rosette-eval
|
|
||||||
(define-symbolic b boolean?)
|
|
||||||
(boolean? b)
|
|
||||||
(boolean? #t)
|
|
||||||
(boolean? #f)
|
|
||||||
(boolean? 1)
|
|
||||||
(code:line (not b) (code:comment "Produces a logical negation of b."))]
|
|
||||||
|
|
||||||
Lifted numeric operations, in contrast, match their Racket semantics
|
|
||||||
only when applied to concrete values. Their symbolic semantics depends on the
|
|
||||||
current @tech["reasoning precision"], as determined by the @racket[current-bitwidth]
|
|
||||||
parameter. In particular, if this parameter is set to @racket[#f], operations on symbolic numbers
|
|
||||||
retain their infinite-precision Racket semantics. However, because infinite-precision
|
|
||||||
reasoning is not efficiently (or at all) decidable for arbitrary numeric operations,
|
|
||||||
programs may need to set @racket[current-bitwidth] to a small positive integer @var[k].
|
|
||||||
With this setting, symbolic numbers are treated as signed @var[k]-bit integers. See
|
|
||||||
@secref{sec:reasoning-precision} for details and examples.
|
|
||||||
|
|
||||||
@section[#:tag "sec:extra-bools"]{Additional Logical Operators}
|
|
||||||
|
|
||||||
In addition to lifting Racket's operations on booleans,
|
|
||||||
Rosette supports the following logical operations:
|
|
||||||
conjunction (@racket[&&]), disjunction (@racket[||]),
|
|
||||||
implication (@racket[=>]), bi-implication (@racket[<=>]),
|
|
||||||
and negation (@racket[!]). These operations have their usual
|
|
||||||
logical meaning; e.g., unlike Racket's shortcircuiting
|
|
||||||
@racket[and] operator, the logical @racket[&&] operator
|
|
||||||
evaluates all of its arguments before taking their
|
|
||||||
conjunction.
|
|
||||||
|
|
||||||
@(rosette-eval '(clear-vc!))
|
|
||||||
|
|
||||||
@defproc[(! [v boolean?]) boolean?]{
|
|
||||||
Returns the negation of the given boolean value.
|
|
||||||
@examples[#:eval rosette-eval
|
|
||||||
(! #f)
|
|
||||||
(! #t)
|
|
||||||
(define-symbolic b boolean?)
|
|
||||||
(code:line (! (if b #f 3)) (code:comment "This typechecks only when b is true,"))
|
|
||||||
(code:line (vc) (code:comment "so Rosette emits a corresponding assertion."))]
|
|
||||||
}
|
|
||||||
|
|
||||||
@(rosette-eval '(clear-vc!))
|
|
||||||
|
|
||||||
@defproc*[([(&& [v boolean?] ...) boolean?]
|
|
||||||
[(|| [v boolean?] ...) boolean?])]{
|
|
||||||
Returns the logical conjunction or disjunction of zero or more boolean values.
|
|
||||||
@examples[#:eval rosette-eval
|
|
||||||
(&&)
|
|
||||||
(||)
|
|
||||||
(code:line (&& #f (begin (displayln "hello") #t)) (code:comment "No shortcircuiting."))
|
|
||||||
(define-symbolic a b boolean?)
|
|
||||||
(code:line (&& a (if b #t 1)) (code:comment "This typechecks only when b is true,"))
|
|
||||||
(code:line (vc) (code:comment "so Rosette emits a corresponding assertion."))]
|
|
||||||
}
|
|
||||||
|
|
||||||
@(rosette-eval '(clear-vc!))
|
|
||||||
|
|
||||||
@defproc*[([(=> [x boolean?] [y boolean?]) boolean?]
|
|
||||||
[(<=> [x boolean?] [y boolean?]) boolean?])]{
|
|
||||||
Returns the logical implication or bi-implication of two boolean values.
|
|
||||||
@examples[#:eval rosette-eval
|
|
||||||
(code:line (=> #f (begin (displayln "hello") #f)) (code:comment "No shortcircuiting."))
|
|
||||||
(define-symbolic a b boolean?)
|
|
||||||
(code:line (<=> a (if b #t 1)) (code:comment "This typechecks only when b is true,"))
|
|
||||||
(code:line (vc) (code:comment "so Rosette emits a corresponding assertion."))]
|
|
||||||
}
|
|
||||||
|
|
||||||
@section[#:tag "sec:quantifiers"]{Quantifiers}
|
|
||||||
|
|
||||||
Rosette also provides constructs for creating universally
|
|
||||||
(@racket[forall]) and existentially (@racket[exists])
|
|
||||||
quantified formulas. These differ from the usual logical
|
|
||||||
quantifiers in that the evaluation of a quantified formula's
|
|
||||||
body may have side effects (e.g., generate assertions). When
|
|
||||||
there are no side effects, however, these constructs have
|
|
||||||
their usual logical meaning.
|
|
||||||
|
|
||||||
@(rosette-eval '(clear-vc!))
|
|
||||||
@(rosette-eval '(current-bitwidth #f))
|
|
||||||
@defproc*[([(forall [vs (listof constant?)] [body boolean?]) boolean?]
|
|
||||||
[(exists [vs (listof constant?)] [body boolean?]) boolean?])]{
|
|
||||||
|
|
||||||
Returns a universally or existentially @deftech{quantified formula}, where the
|
|
||||||
symbolic constants @racket[vs] are treated as quantified variables.
|
|
||||||
Each constant in @racket[vs] must have a non-@racket[function?] @racket[solvable?] type.
|
|
||||||
The @racket[body] argument is a boolean value, which is usually a symbolic
|
|
||||||
boolean expression over the quantified variables @racket[vs] and,
|
|
||||||
optionally, over free symbolic (Skolem) constants. Any assertions and assumptions emitted during
|
|
||||||
the evaluation of @racket[body] are added to the current verification condition @racket[(vc)].
|
|
||||||
This may be the desired behavior in some circumstances but not in others, so to avoid
|
|
||||||
surprises, it is best to handle side effects separately and call quantifiers
|
|
||||||
with pure bodies, as shown below.
|
|
||||||
|
|
||||||
@examples[#:eval rosette-eval
|
|
||||||
(current-bitwidth #f)
|
|
||||||
(define-symbolic x y integer?)
|
|
||||||
(code:line
|
|
||||||
(exists (list x y) (= x y)) (code:comment "Pure body expression."))
|
|
||||||
(define-symbolic b boolean?)
|
|
||||||
(code:line
|
|
||||||
(forall (list b x y)
|
|
||||||
(= (+ (if b x 'x) 1) y)) (code:comment "Body emits a type assertion."))
|
|
||||||
(vc)
|
|
||||||
(clear-vc!)
|
|
||||||
(code:comment "To avoid surprises, capture assertions and assumptions using with-vc,")
|
|
||||||
(code:comment "and handle as desired, e.g.:")
|
|
||||||
(define out (with-vc (= (+ (if b x 'x) 1) y)))
|
|
||||||
out
|
|
||||||
(define out-val (result-value out))
|
|
||||||
(define out-vc (result-state out))
|
|
||||||
(forall (list b x y)
|
|
||||||
(=> (&& (vc-assumes out-vc) (vc-asserts out-vc)) out-val))
|
|
||||||
(vc)
|
|
||||||
]
|
|
||||||
|
|
||||||
The usual lexical scoping rules apply to quantified symbolics; if @racket[body] is
|
|
||||||
a quantified formula over a variable @var[v] in @racket[vs], then the
|
|
||||||
innermost quantification of @var[v] shadows any enclosing quantifications.
|
|
||||||
Quantified symbolics are not bound in a @racket[model], unless they also appear
|
|
||||||
freely in some formulas.
|
|
||||||
|
|
||||||
@examples[#:eval rosette-eval
|
|
||||||
(define-symbolic x y integer?)
|
|
||||||
(code:line
|
|
||||||
(define f
|
|
||||||
(forall (list x)
|
|
||||||
(exists (list y)
|
|
||||||
(= x (+ x y))))) (code:comment "x and y are not free in f,"))
|
|
||||||
(code:line
|
|
||||||
(solve (assert f)) (code:comment "so they are not bound in the model."))
|
|
||||||
(code:line
|
|
||||||
(define g
|
|
||||||
(forall (list x)
|
|
||||||
(= x (+ x y)))) (code:comment "y is free in g,"))
|
|
||||||
(code:line
|
|
||||||
(solve (assert g)) (code:comment "so it is bound in the model."))
|
|
||||||
(code:line
|
|
||||||
(define h
|
|
||||||
(exists (list x)
|
|
||||||
(forall (list x)
|
|
||||||
(= x (+ x x))))) (code:comment "The body of h refers to the innermost x,"))
|
|
||||||
(code:line
|
|
||||||
(solve (assert h)) (code:comment "so h is unsatisfiable."))
|
|
||||||
]
|
|
||||||
|
|
||||||
When executing queries over assertions that contain quantified formulas,
|
|
||||||
the @racket[current-bitwidth] parameter must be set to @racket[#f].
|
|
||||||
Quantified formulas may not appear in any assertion or assumption that is passed
|
|
||||||
to a @racket[synthesize] query.
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@(kill-evaluator rosette-eval)
|
|
||||||
|
|
@ -1,238 +0,0 @@
|
||||||
;; This file was created by make-log-based-eval
|
|
||||||
((define-symbolic b boolean?) ((3) 0 () 0 () () (c values c (void))) #"" #"")
|
|
||||||
((boolean? b) ((3) 0 () 0 () () (q values #t)) #"" #"")
|
|
||||||
((boolean? #t) ((3) 0 () 0 () () (q values #t)) #"" #"")
|
|
||||||
((boolean? #f) ((3) 0 () 0 () () (q values #t)) #"" #"")
|
|
||||||
((boolean? 1) ((3) 0 () 0 () () (q values #f)) #"" #"")
|
|
||||||
((not b)
|
|
||||||
((3)
|
|
||||||
1
|
|
||||||
(((lib "rosette/guide/scribble/util/lifted.rkt")
|
|
||||||
.
|
|
||||||
deserialize-info:opaque-v0))
|
|
||||||
0
|
|
||||||
()
|
|
||||||
()
|
|
||||||
(c values c (0 (u . "(! b)\n"))))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
((clear-vc!) ((3) 0 () 0 () () (c values c (void))) #"" #"")
|
|
||||||
((! #f) ((3) 0 () 0 () () (q values #t)) #"" #"")
|
|
||||||
((! #t) ((3) 0 () 0 () () (q values #f)) #"" #"")
|
|
||||||
((define-symbolic b boolean?) ((3) 0 () 0 () () (c values c (void))) #"" #"")
|
|
||||||
((! (if b #f 3)) ((3) 0 () 0 () () (q values #t)) #"" #"")
|
|
||||||
((vc)
|
|
||||||
((3)
|
|
||||||
1
|
|
||||||
(((lib "rosette/guide/scribble/util/lifted.rkt")
|
|
||||||
.
|
|
||||||
deserialize-info:opaque-v0))
|
|
||||||
0
|
|
||||||
()
|
|
||||||
()
|
|
||||||
(c values c (0 (u . "(vc #t b)\n"))))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
((clear-vc!) ((3) 0 () 0 () () (c values c (void))) #"" #"")
|
|
||||||
((&&) ((3) 0 () 0 () () (q values #t)) #"" #"")
|
|
||||||
((||) ((3) 0 () 0 () () (q values #f)) #"" #"")
|
|
||||||
((&& #f (begin (displayln "hello") #t))
|
|
||||||
((3) 0 () 0 () () (q values #f))
|
|
||||||
#"hello\n"
|
|
||||||
#"")
|
|
||||||
((define-symbolic a b boolean?) ((3) 0 () 0 () () (c values c (void))) #"" #"")
|
|
||||||
((&& a (if b #t 1))
|
|
||||||
((3)
|
|
||||||
1
|
|
||||||
(((lib "rosette/guide/scribble/util/lifted.rkt")
|
|
||||||
.
|
|
||||||
deserialize-info:opaque-v0))
|
|
||||||
0
|
|
||||||
()
|
|
||||||
()
|
|
||||||
(c values c (0 (u . "a\n"))))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
((vc)
|
|
||||||
((3)
|
|
||||||
1
|
|
||||||
(((lib "rosette/guide/scribble/util/lifted.rkt")
|
|
||||||
.
|
|
||||||
deserialize-info:opaque-v0))
|
|
||||||
0
|
|
||||||
()
|
|
||||||
()
|
|
||||||
(c values c (0 (u . "(vc #t b)\n"))))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
((clear-vc!) ((3) 0 () 0 () () (c values c (void))) #"" #"")
|
|
||||||
((=> #f (begin (displayln "hello") #f))
|
|
||||||
((3) 0 () 0 () () (q values #t))
|
|
||||||
#"hello\n"
|
|
||||||
#"")
|
|
||||||
((define-symbolic a b boolean?) ((3) 0 () 0 () () (c values c (void))) #"" #"")
|
|
||||||
((<=> a (if b #t 1))
|
|
||||||
((3)
|
|
||||||
1
|
|
||||||
(((lib "rosette/guide/scribble/util/lifted.rkt")
|
|
||||||
.
|
|
||||||
deserialize-info:opaque-v0))
|
|
||||||
0
|
|
||||||
()
|
|
||||||
()
|
|
||||||
(c values c (0 (u . "a\n"))))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
((vc)
|
|
||||||
((3)
|
|
||||||
1
|
|
||||||
(((lib "rosette/guide/scribble/util/lifted.rkt")
|
|
||||||
.
|
|
||||||
deserialize-info:opaque-v0))
|
|
||||||
0
|
|
||||||
()
|
|
||||||
()
|
|
||||||
(c values c (0 (u . "(vc #t b)\n"))))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
((clear-vc!) ((3) 0 () 0 () () (c values c (void))) #"" #"")
|
|
||||||
((current-bitwidth #f) ((3) 0 () 0 () () (c values c (void))) #"" #"")
|
|
||||||
((current-bitwidth #f) ((3) 0 () 0 () () (c values c (void))) #"" #"")
|
|
||||||
((define-symbolic x y integer?) ((3) 0 () 0 () () (c values c (void))) #"" #"")
|
|
||||||
((exists (list x y) (= x y))
|
|
||||||
((3)
|
|
||||||
1
|
|
||||||
(((lib "rosette/guide/scribble/util/lifted.rkt")
|
|
||||||
.
|
|
||||||
deserialize-info:opaque-v0))
|
|
||||||
0
|
|
||||||
()
|
|
||||||
()
|
|
||||||
(c values c (0 (u . "(exists (x y) (= x y))\n"))))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
((define-symbolic b boolean?) ((3) 0 () 0 () () (c values c (void))) #"" #"")
|
|
||||||
((forall (list b x y) (= (+ (if b x 'x) 1) y))
|
|
||||||
((3)
|
|
||||||
1
|
|
||||||
(((lib "rosette/guide/scribble/util/lifted.rkt")
|
|
||||||
.
|
|
||||||
deserialize-info:opaque-v0))
|
|
||||||
0
|
|
||||||
()
|
|
||||||
()
|
|
||||||
(c values c (0 (u . "(forall (b x y) (= y (+ 1 x)))\n"))))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
((vc)
|
|
||||||
((3)
|
|
||||||
1
|
|
||||||
(((lib "rosette/guide/scribble/util/lifted.rkt")
|
|
||||||
.
|
|
||||||
deserialize-info:opaque-v0))
|
|
||||||
0
|
|
||||||
()
|
|
||||||
()
|
|
||||||
(c values c (0 (u . "(vc #t b)\n"))))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
((clear-vc!) ((3) 0 () 0 () () (c values c (void))) #"" #"")
|
|
||||||
((define out (with-vc (= (+ (if b x 'x) 1) y)))
|
|
||||||
((3) 0 () 0 () () (c values c (void)))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
(out
|
|
||||||
((3)
|
|
||||||
1
|
|
||||||
(((lib "rosette/guide/scribble/util/lifted.rkt")
|
|
||||||
.
|
|
||||||
deserialize-info:opaque-v0))
|
|
||||||
0
|
|
||||||
()
|
|
||||||
()
|
|
||||||
(c values c (0 (u . "(normal (= y (+ 1 x)) (vc #t b))\n"))))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
((define out-val (result-value out))
|
|
||||||
((3) 0 () 0 () () (c values c (void)))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
((define out-vc (result-state out))
|
|
||||||
((3) 0 () 0 () () (c values c (void)))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
((forall
|
|
||||||
(list b x y)
|
|
||||||
(=> (&& (vc-assumes out-vc) (vc-asserts out-vc)) out-val))
|
|
||||||
((3)
|
|
||||||
1
|
|
||||||
(((lib "rosette/guide/scribble/util/lifted.rkt")
|
|
||||||
.
|
|
||||||
deserialize-info:opaque-v0))
|
|
||||||
0
|
|
||||||
()
|
|
||||||
()
|
|
||||||
(c values c (0 (u . "(forall (b x y) (|| (! b) (= y (+ 1 x))))\n"))))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
((vc)
|
|
||||||
((3)
|
|
||||||
1
|
|
||||||
(((lib "rosette/guide/scribble/util/lifted.rkt")
|
|
||||||
.
|
|
||||||
deserialize-info:opaque-v0))
|
|
||||||
0
|
|
||||||
()
|
|
||||||
()
|
|
||||||
(c values c (0 (u . "(vc #t #t)\n"))))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
((define-symbolic x y integer?) ((3) 0 () 0 () () (c values c (void))) #"" #"")
|
|
||||||
((define f (forall (list x) (exists (list y) (= x (+ x y)))))
|
|
||||||
((3) 0 () 0 () () (c values c (void)))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
((solve (assert f))
|
|
||||||
((3)
|
|
||||||
1
|
|
||||||
(((lib "rosette/guide/scribble/util/lifted.rkt")
|
|
||||||
.
|
|
||||||
deserialize-info:opaque-v0))
|
|
||||||
0
|
|
||||||
()
|
|
||||||
()
|
|
||||||
(c values c (0 (u . "(model)\n"))))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
((define g (forall (list x) (= x (+ x y))))
|
|
||||||
((3) 0 () 0 () () (c values c (void)))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
((solve (assert g))
|
|
||||||
((3)
|
|
||||||
1
|
|
||||||
(((lib "rosette/guide/scribble/util/lifted.rkt")
|
|
||||||
.
|
|
||||||
deserialize-info:opaque-v0))
|
|
||||||
0
|
|
||||||
()
|
|
||||||
()
|
|
||||||
(c values c (0 (u . "(model\n [y 0])\n"))))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
((define h (exists (list x) (forall (list x) (= x (+ x x)))))
|
|
||||||
((3) 0 () 0 () () (c values c (void)))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
((solve (assert h))
|
|
||||||
((3)
|
|
||||||
1
|
|
||||||
(((lib "rosette/guide/scribble/util/lifted.rkt")
|
|
||||||
.
|
|
||||||
deserialize-info:opaque-v0))
|
|
||||||
0
|
|
||||||
()
|
|
||||||
()
|
|
||||||
(c values c (0 (u . "(unsat)\n"))))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
|
|
@ -1,54 +0,0 @@
|
||||||
;; This file was created by make-log-based-eval
|
|
||||||
((define v1 (box 1)) ((3) 0 () 0 () () (c values c (void))) #"" #"")
|
|
||||||
((define v2 (box 1)) ((3) 0 () 0 () () (c values c (void))) #"" #"")
|
|
||||||
((eq? v1 v2) ((3) 0 () 0 () () (q values #f)) #"" #"")
|
|
||||||
((equal? v1 v2) ((3) 0 () 0 () () (q values #t)) #"" #"")
|
|
||||||
((define v3 (box-immutable 1)) ((3) 0 () 0 () () (c values c (void))) #"" #"")
|
|
||||||
((define v4 (box-immutable 1)) ((3) 0 () 0 () () (c values c (void))) #"" #"")
|
|
||||||
((eq? v3 v4) ((3) 0 () 0 () () (q values #t)) #"" #"")
|
|
||||||
((equal? v1 v3) ((3) 0 () 0 () () (q values #t)) #"" #"")
|
|
||||||
((define-symbolic x integer?) ((3) 0 () 0 () () (c values c (void))) #"" #"")
|
|
||||||
((define-symbolic b boolean?) ((3) 0 () 0 () () (c values c (void))) #"" #"")
|
|
||||||
((define v1 (box x)) ((3) 0 () 0 () () (c values c (void))) #"" #"")
|
|
||||||
((define v2 (if b v1 (box 3))) ((3) 0 () 0 () () (c values c (void))) #"" #"")
|
|
||||||
((define sol (solve (assert (= 4 (unbox v2)))))
|
|
||||||
((3) 0 () 0 () () (c values c (void)))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
(sol
|
|
||||||
((3)
|
|
||||||
1
|
|
||||||
(((lib "rosette/guide/scribble/util/lifted.rkt")
|
|
||||||
.
|
|
||||||
deserialize-info:opaque-v0))
|
|
||||||
0
|
|
||||||
()
|
|
||||||
()
|
|
||||||
(c values c (0 (u . "(model\n [x 4]\n [b #t])\n"))))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
((evaluate v1 sol)
|
|
||||||
((3)
|
|
||||||
1
|
|
||||||
(((lib "rosette/guide/scribble/util/lifted.rkt")
|
|
||||||
.
|
|
||||||
deserialize-info:opaque-v0))
|
|
||||||
0
|
|
||||||
()
|
|
||||||
()
|
|
||||||
(c values c (0 (u . "'#&4\n"))))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
((evaluate v2 sol)
|
|
||||||
((3)
|
|
||||||
1
|
|
||||||
(((lib "rosette/guide/scribble/util/lifted.rkt")
|
|
||||||
.
|
|
||||||
deserialize-info:opaque-v0))
|
|
||||||
0
|
|
||||||
()
|
|
||||||
()
|
|
||||||
(c values c (0 (u . "'#&4\n"))))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
((evaluate (eq? v1 v2) sol) ((3) 0 () 0 () () (q values #t)) #"" #"")
|
|
||||||
|
|
@ -1,46 +0,0 @@
|
||||||
#lang scribble/manual
|
|
||||||
|
|
||||||
@(require (for-label rosette/base/form/define rosette/query/query racket
|
|
||||||
(only-in rosette/base/base assert))
|
|
||||||
scribble/core scribble/html-properties scribble/examples racket/sandbox racket/runtime-path
|
|
||||||
"../util/lifted.rkt")
|
|
||||||
|
|
||||||
@(define box-ops (select '(box? box box-immutable unbox set-box! box-cas!)))
|
|
||||||
|
|
||||||
@(define-runtime-path root ".")
|
|
||||||
@(define rosette-eval (rosette-log-evaluator (logfile root "boxes-log")))
|
|
||||||
|
|
||||||
@title[#:tag "sec:box"]{Boxes}
|
|
||||||
|
|
||||||
A box is a single (im)mutable storage cell, which behaves like a one-element (im)mutable @seclink["sec:vec"]{vector}.
|
|
||||||
Like vectors, immutable boxes are treated as transparent immutable values: they are @racket[eq?] when their
|
|
||||||
contents are @racket[eq?]. Mutable boxes are references rather than values, so they are @racket[eq?] only when
|
|
||||||
they point to the same box object. Boxes can be concrete or symbolic, and they can contain both symbolic and concrete values.
|
|
||||||
|
|
||||||
@examples[#:eval rosette-eval
|
|
||||||
(define v1 (box 1))
|
|
||||||
(define v2 (box 1))
|
|
||||||
(eq? v1 v2)
|
|
||||||
(equal? v1 v2)
|
|
||||||
(define v3 (box-immutable 1))
|
|
||||||
(define v4 (box-immutable 1))
|
|
||||||
(eq? v3 v4)
|
|
||||||
(equal? v1 v3)
|
|
||||||
]
|
|
||||||
|
|
||||||
@examples[#:eval rosette-eval
|
|
||||||
(define-symbolic x integer?)
|
|
||||||
(define-symbolic b boolean?)
|
|
||||||
(code:line (define v1 (box x)) (code:comment "v1 is a box with symbolic contents."))
|
|
||||||
(code:line (define v2 (if b v1 (box 3))) (code:comment "v2 is a symbolic box."))
|
|
||||||
(define sol (solve (assert (= 4 (unbox v2)))))
|
|
||||||
sol
|
|
||||||
(evaluate v1 sol)
|
|
||||||
(evaluate v2 sol)
|
|
||||||
(evaluate (eq? v1 v2) sol)]
|
|
||||||
|
|
||||||
Lifted box operations are shown below.
|
|
||||||
@tabular[#:style (style #f (list (attributes '((id . "lifted")(class . "boxed")))))
|
|
||||||
(list (list @box-ops))]
|
|
||||||
|
|
||||||
@(kill-evaluator rosette-eval)
|
|
||||||
|
|
@ -1,46 +0,0 @@
|
||||||
#lang scribble/manual
|
|
||||||
|
|
||||||
|
|
||||||
@(require (for-label racket)
|
|
||||||
(for-label rosette/base/form/define rosette/base/core/type))
|
|
||||||
|
|
||||||
@title[#:tag "ch:built-in-datatypes" #:style 'toc]{Built-In Datatypes}
|
|
||||||
|
|
||||||
The @seclink["ch:syntactic-forms"]{previous chapter} describes the
|
|
||||||
Racket syntax forms that are @tech[#:key "lifted constructs"]{lifted} by Rosette to
|
|
||||||
work on symbolic values.
|
|
||||||
This chapter describes the lifted datatypes and their corresponding operations. Most
|
|
||||||
lifted operations retain their Racket semantics, with the exception of
|
|
||||||
equality predicates (Section @seclink["sec:equality"]{4.1}) and
|
|
||||||
numeric operations (Section @seclink["sec:bools+ints+reals"]{4.2}).
|
|
||||||
|
|
||||||
Rosette distinguishes between two kinds of built-in datatypes:
|
|
||||||
@deftech[#:key "solvable type"]{solvable} and @deftech[#:key "unsolvable type"]{unsolvable}.
|
|
||||||
Solvable types are (efficiently) supported by SMT solvers, and they include booleans,
|
|
||||||
integers, reals, bitvectors, and uninterpreted functions. All other built-in types are
|
|
||||||
unsolvable---that is, not as well supported by SMT solvers.
|
|
||||||
|
|
||||||
Every lifted type is equipped with a predicate (e.g., @racket[boolean?]) that
|
|
||||||
recognizes values of that type. Solvable types are themselves recognized by
|
|
||||||
the @racket[solvable?] predicate. Lifted types include both concrete Racket
|
|
||||||
values and symbolic Rosette values, but only solvable types include
|
|
||||||
@tech[#:key "symbolic constant"]{symbolic constants},
|
|
||||||
as introduced by @seclink["sec:symbolic-constants"]{@code{define-symbolic[*]}}.
|
|
||||||
|
|
||||||
@(table-of-contents)
|
|
||||||
@include-section["equality.scrbl"]
|
|
||||||
@include-section["bools+ints+reals.scrbl"]
|
|
||||||
@include-section["bitvectors.scrbl"]
|
|
||||||
@include-section["uninterpreted.scrbl"]
|
|
||||||
@include-section["procedures.scrbl"]
|
|
||||||
@include-section["pairs.scrbl"]
|
|
||||||
@include-section["vectors.scrbl"]
|
|
||||||
@include-section["boxes.scrbl"]
|
|
||||||
@include-section["solvers+solutions.scrbl"]
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,114 +0,0 @@
|
||||||
;; This file was created by make-log-based-eval
|
|
||||||
((struct point (x y) #:transparent)
|
|
||||||
((3) 0 () 0 () () (c values c (void)))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
((struct pt (x y)) ((3) 0 () 0 () () (c values c (void))) #"" #"")
|
|
||||||
((struct pnt (x y) #:mutable #:transparent)
|
|
||||||
((3) 0 () 0 () () (c values c (void)))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
((eq? (point 1 2) (point 1 2)) ((3) 0 () 0 () () (q values #t)) #"" #"")
|
|
||||||
((eq? (pt 1 2) (pt 1 2)) ((3) 0 () 0 () () (q values #f)) #"" #"")
|
|
||||||
((eq? (pnt 1 2) (pnt 1 2)) ((3) 0 () 0 () () (q values #f)) #"" #"")
|
|
||||||
((define-symbolic b boolean?) ((3) 0 () 0 () () (c values c (void))) #"" #"")
|
|
||||||
((define p (if b (point 1 2) (point 3 4)))
|
|
||||||
((3) 0 () 0 () () (c values c (void)))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
((point-x p)
|
|
||||||
((3)
|
|
||||||
1
|
|
||||||
(((lib "rosette/guide/scribble/util/lifted.rkt")
|
|
||||||
.
|
|
||||||
deserialize-info:opaque-v0))
|
|
||||||
0
|
|
||||||
()
|
|
||||||
()
|
|
||||||
(c values c (0 (u . "(ite b 1 3)\n"))))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
((point-y p)
|
|
||||||
((3)
|
|
||||||
1
|
|
||||||
(((lib "rosette/guide/scribble/util/lifted.rkt")
|
|
||||||
.
|
|
||||||
deserialize-info:opaque-v0))
|
|
||||||
0
|
|
||||||
()
|
|
||||||
()
|
|
||||||
(c values c (0 (u . "(ite b 2 4)\n"))))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
((define sol (solve (assert (= (point-x p) 3))))
|
|
||||||
((3) 0 () 0 () () (c values c (void)))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
((evaluate p sol)
|
|
||||||
((3)
|
|
||||||
1
|
|
||||||
(((lib "rosette/guide/scribble/util/lifted.rkt")
|
|
||||||
.
|
|
||||||
deserialize-info:opaque-v0))
|
|
||||||
0
|
|
||||||
()
|
|
||||||
()
|
|
||||||
(c values c (0 (u . "(point 3 4)\n"))))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
((define-generics viewable (view viewable))
|
|
||||||
((3) 0 () 0 () () (c values c (void)))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
((struct
|
|
||||||
square
|
|
||||||
(side)
|
|
||||||
#:methods
|
|
||||||
gen:viewable
|
|
||||||
((define (view self) (square-side self))))
|
|
||||||
((3) 0 () 0 () () (c values c (void)))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
((struct
|
|
||||||
circle
|
|
||||||
(radius)
|
|
||||||
#:transparent
|
|
||||||
#:methods
|
|
||||||
gen:viewable
|
|
||||||
((define (view self) (circle-radius self))))
|
|
||||||
((3) 0 () 0 () () (c values c (void)))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
((define-symbolic b boolean?) ((3) 0 () 0 () () (c values c (void))) #"" #"")
|
|
||||||
((define p (if b (square 2) (circle 3)))
|
|
||||||
((3) 0 () 0 () () (c values c (void)))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
((view p)
|
|
||||||
((3)
|
|
||||||
1
|
|
||||||
(((lib "rosette/guide/scribble/util/lifted.rkt")
|
|
||||||
.
|
|
||||||
deserialize-info:opaque-v0))
|
|
||||||
0
|
|
||||||
()
|
|
||||||
()
|
|
||||||
(c values c (0 (u . "(ite b 2 3)\n"))))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
((define sol (solve (assert (= (view p) 3))))
|
|
||||||
((3) 0 () 0 () () (c values c (void)))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
((evaluate p sol)
|
|
||||||
((3)
|
|
||||||
1
|
|
||||||
(((lib "rosette/guide/scribble/util/lifted.rkt")
|
|
||||||
.
|
|
||||||
deserialize-info:opaque-v0))
|
|
||||||
0
|
|
||||||
()
|
|
||||||
()
|
|
||||||
(c values c (0 (u . "(circle 3)\n"))))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
|
|
@ -1,89 +0,0 @@
|
||||||
#lang scribble/manual
|
|
||||||
|
|
||||||
@(require (for-label
|
|
||||||
rosette/base/form/define rosette/query/query
|
|
||||||
rosette/base/core/term (only-in rosette/base/base assert)
|
|
||||||
racket racket/generic)
|
|
||||||
scribble/core scribble/html-properties scribble/examples racket/sandbox racket/runtime-path
|
|
||||||
"../util/lifted.rkt")
|
|
||||||
|
|
||||||
@(define-runtime-path root ".")
|
|
||||||
@(define rosette-eval (rosette-log-evaluator (logfile root "defined-datatypes-log")))
|
|
||||||
@(define prop-facilities (select '(make-struct-type-property struct-type-property? struct-type-property-accessor-procedure?)))
|
|
||||||
@(define props (select '(prop:arity-string prop:blame prop:chaperone-contract prop:chaperone-unsafe-undefined prop:checked-procedure prop:contract prop:contracted prop:custom-print-quotable prop:custom-write prop:dict prop:dict/contract prop:equal+hash prop:evt prop:exn:missing-module prop:exn:srclocs prop:flat-contract prop:impersonator-of prop:input-port prop:legacy-match-expander prop:liberal-define-context prop:match-expander prop:output-port prop:place-location prop:procedure prop:provide-pre-transformer prop:provide-transformer prop:rename-transformer prop:require-transformer prop:sequence prop:serializable prop:set!-transformer prop:stream prop:struct-auto-info prop:struct-info)))
|
|
||||||
@(define generics-facilities (select '(define-generics raise-support-error exn:fail:support define/generic generic-instance/c impersonate-generics chaperone-generics redirect-generics )))
|
|
||||||
@(define generics (select '(gen:custom-write gen:dict gen:equal+hash gen:set gen:stream)))
|
|
||||||
|
|
||||||
|
|
||||||
@title[#:tag "ch:programmer-defined-datatypes" #:style 'toc]{Structures}
|
|
||||||
|
|
||||||
In addition to @tech[#:key "lifted constructs"]{lifting} many
|
|
||||||
@seclink["ch:built-in-datatypes"]{built-in datatypes}
|
|
||||||
to work with symbolic values, Rosette also lifts Racket's
|
|
||||||
@racketlink[struct]{structures}.
|
|
||||||
As in Racket, a structure is an instance of a @deftech{structure type}---a
|
|
||||||
record datatype with zero or more fields.
|
|
||||||
Structure types are defined using the @racket[struct] syntax. Defining a
|
|
||||||
structure type in this way also defines the necessary procedures for
|
|
||||||
creating instances of that type and for accessing their fields.
|
|
||||||
|
|
||||||
@examples[#:eval rosette-eval #:label #f
|
|
||||||
(struct point (x y) #:transparent) (code:comment "Immutable transparent type.")
|
|
||||||
(struct pt (x y)) (code:comment "Opaque immutable type.")
|
|
||||||
(struct pnt (x y) #:mutable #:transparent) (code:comment "Mutable transparent type.")]
|
|
||||||
|
|
||||||
Rosette structures can be concrete or symbolic. Their semantics matches that of Racket,
|
|
||||||
with one important exception: immutable transparent structures are treated as values
|
|
||||||
rather than references. This @seclink["sec:equality"]{means} that two such structures are
|
|
||||||
@racket[eq?] if they belong to the same type and their corresponding field values are @racket[eq?].
|
|
||||||
Structures of opaque or mutable types are treated as references. Two such structures are
|
|
||||||
@racket[eq?] only if the point to the same instance of the same type.
|
|
||||||
|
|
||||||
@examples[#:eval rosette-eval #:label #f
|
|
||||||
(code:line (eq? (point 1 2) (point 1 2)) (code:comment "point structures are values."))
|
|
||||||
(code:line (eq? (pt 1 2) (pt 1 2)) (code:comment "pt structures are references."))
|
|
||||||
(code:line (eq? (pnt 1 2) (pnt 1 2)) (code:comment "pnt structures are references."))]
|
|
||||||
|
|
||||||
Like @tech[#:key "unsolvable type"]{unsolvable built-in datatypes},
|
|
||||||
symbolic structures cannot be created using @racket[define-symbolic]. Instead,
|
|
||||||
they are created implicitly, by, for example, using an @racket[if] expression
|
|
||||||
together with a symbolic value.
|
|
||||||
|
|
||||||
|
|
||||||
@examples[#:eval rosette-eval #:label #f
|
|
||||||
(define-symbolic b boolean?)
|
|
||||||
(code:line (define p (if b (point 1 2) (point 3 4))) (code:comment "p holds a symbolic structure."))
|
|
||||||
(point-x p)
|
|
||||||
(point-y p)
|
|
||||||
(define sol (solve (assert (= (point-x p) 3))))
|
|
||||||
(evaluate p sol)]
|
|
||||||
|
|
||||||
As well as lifting the @racket[struct] syntax, Rosette also lifts the following structure
|
|
||||||
properties, generic interfaces, and facilities for defining new properties and interfaces:
|
|
||||||
@tabular[#:style (style #f (list (attributes '((id . "lifted")(class . "boxed")))))
|
|
||||||
(list (list @elem{Defining Properties} @elem{@prop-facilities})
|
|
||||||
(list @elem{Lifted Properties} @elem{@props})
|
|
||||||
(list @elem{Defining Generics} @elem{@generics-facilities})
|
|
||||||
(list @elem{Lifted Generics} @elem{@generics} ))]
|
|
||||||
|
|
||||||
Lifted generics work as expected with symbolic values:
|
|
||||||
|
|
||||||
@examples[#:eval rosette-eval #:label #f
|
|
||||||
(define-generics viewable (view viewable))
|
|
||||||
|
|
||||||
(struct square (side)
|
|
||||||
#:methods gen:viewable
|
|
||||||
[(define (view self) (square-side self))])
|
|
||||||
|
|
||||||
(struct circle (radius)
|
|
||||||
#:transparent
|
|
||||||
#:methods gen:viewable
|
|
||||||
[(define (view self) (circle-radius self))])
|
|
||||||
|
|
||||||
(define-symbolic b boolean?)
|
|
||||||
(code:line (define p (if b (square 2) (circle 3))) (code:comment "p holds a symbolic structure."))
|
|
||||||
(view p)
|
|
||||||
(define sol (solve (assert (= (view p) 3))))
|
|
||||||
(evaluate p sol)]
|
|
||||||
|
|
||||||
@(kill-evaluator rosette-eval)
|
|
||||||
|
|
@ -1,81 +0,0 @@
|
||||||
#lang scribble/manual
|
|
||||||
|
|
||||||
@(require (for-label rosette/base/form/define racket)
|
|
||||||
(for-label (only-in rosette/base/base function? distinct? ~> bv))
|
|
||||||
scribble/core scribble/html-properties scribble/examples racket/sandbox
|
|
||||||
"../util/lifted.rkt")
|
|
||||||
|
|
||||||
|
|
||||||
@(define rosette-eval (rosette-evaluator))
|
|
||||||
|
|
||||||
@title[#:tag "sec:equality"]{Equality}
|
|
||||||
|
|
||||||
@declare-exporting[rosette/base/base #:use-sources (rosette/base/base)]
|
|
||||||
|
|
||||||
Rosette supports two generic equality predicates, @racket[eq?] and @racket[equal?].
|
|
||||||
The @racket[equal?] predicate follows the Racket semantics, extended to work with symbolic values.
|
|
||||||
In particular, two values are @racket[equal?] only when they are @racket[eq?], unless a more permissive
|
|
||||||
notion of @racket[equal?] is specified for a particular datatype.
|
|
||||||
|
|
||||||
@examples[#:eval rosette-eval
|
|
||||||
(equal? 1 #t)
|
|
||||||
(equal? 1 1.0)
|
|
||||||
(equal? (list 1) (list 1.0))
|
|
||||||
(equal? (box 1) (box 1))
|
|
||||||
(equal? (list (box 1)) (list (box 1)))
|
|
||||||
(define-symbolic n integer?)
|
|
||||||
(equal? n 1)
|
|
||||||
(equal? (box n) (box 1))
|
|
||||||
(define-symbolic f g (~> integer? integer?))
|
|
||||||
(code:line (equal? f g) (code:comment "f and g are different procedures."))]
|
|
||||||
@(kill-evaluator rosette-eval)
|
|
||||||
@(set! rosette-eval (rosette-evaluator))
|
|
||||||
|
|
||||||
The @racket[eq?] predicate follows the Racket semantics for opaque or mutable datatypes,
|
|
||||||
such as procedures or vectors, but not for transparent immutable datatypes, such as
|
|
||||||
lists, or transparent solvable types, such as reals.
|
|
||||||
Rosette treats these transparent types as @emph{value types},
|
|
||||||
while Racket does not. Racket's @racket[eq?] may therefore return @racket[#f] when
|
|
||||||
given two instances of such a transparent type, regardless of their contents.
|
|
||||||
Rosette's @racket[eq?], in contrast, returns true when given two
|
|
||||||
transparent solvable values that are @racket[equal?],
|
|
||||||
or two transparent immutable values with @racket[eq?] contents.
|
|
||||||
|
|
||||||
@examples[#:eval rosette-eval
|
|
||||||
(eq? 1 #t)
|
|
||||||
(code:line (eq? 1 1.0) (code:comment "equal? transparent solvable values."))
|
|
||||||
(code:line (eq? (list 1) (list 1.0)) (code:comment "Transparent immutable values with eq? contents."))
|
|
||||||
(code:line (eq? (box 1) (box 1)) (code:comment "But boxes are mutable, so eq? follows Racket,"))
|
|
||||||
(eq? (list (box 1)) (list (box 1)))
|
|
||||||
(define-symbolic n integer?)
|
|
||||||
(eq? n 1)
|
|
||||||
(eq? (box n) (box 1))
|
|
||||||
(define-symbolic f g (~> integer? integer?))
|
|
||||||
(code:line (eq? f g) (code:comment "and procedures are opaque, so eq? follows Racket."))
|
|
||||||
(eq? f f)]
|
|
||||||
|
|
||||||
In addition to lifting Racket's equality predicates, Rosette also provides a @racket[distinct?] predicate
|
|
||||||
that returns true iff all of its arguments are distinct from each other. Invoking this predicate
|
|
||||||
on arbitrary values has the effect of performing O(@var[N]@superscript{2}) equality
|
|
||||||
comparisons. But when applied to symbolic values of a primitive
|
|
||||||
@tech[#:key "solvable type"]{solvable} type, @racket[distinct?] will produce a compact
|
|
||||||
symbolic value that can be directly solved by the underlying solver.
|
|
||||||
|
|
||||||
@(rosette-eval '(clear-vc!))
|
|
||||||
@defproc[(distinct? [v any/c] ...) boolean?]{
|
|
||||||
Returns true iff all of the given values @racket[v] are distinct---i.e., pairwise un-@racket[equal?]
|
|
||||||
to each other. If all values @racket[v] are of the same primitive (non-@racket[function?])
|
|
||||||
@tech[#:key "solvable type"]{solvable} type, this predicate produces a compact
|
|
||||||
constraint that can be more efficiently solved by the underlying solver. Otherwise, it performs, O(@var[N]@superscript{2}) inequality comparisons.
|
|
||||||
|
|
||||||
@examples[#:eval rosette-eval
|
|
||||||
(distinct?)
|
|
||||||
(distinct? 1)
|
|
||||||
(distinct? (list 1 2) (list 3) (list 1 2))
|
|
||||||
(define-symbolic x y z integer?)
|
|
||||||
(distinct? 3 z x y 2)
|
|
||||||
(define-symbolic b boolean?)
|
|
||||||
(distinct? 3 (bv 3 4) (list 1) (list x) y 2)]
|
|
||||||
}
|
|
||||||
|
|
||||||
@(kill-evaluator rosette-eval)
|
|
||||||
|
|
@ -1,83 +0,0 @@
|
||||||
;; This file was created by make-log-based-eval
|
|
||||||
((define-symbolic x y z n integer?)
|
|
||||||
((3) 0 () 0 () () (c values c (void)))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
((define xs (take (list x y z) n))
|
|
||||||
((3) 0 () 0 () () (c values c (void)))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
((define sol (solve (assert (null? xs))))
|
|
||||||
((3) 0 () 0 () () (c values c (void)))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
((evaluate xs sol)
|
|
||||||
((3)
|
|
||||||
1
|
|
||||||
(((lib "rosette/guide/scribble/util/lifted.rkt")
|
|
||||||
.
|
|
||||||
deserialize-info:opaque-v0))
|
|
||||||
0
|
|
||||||
()
|
|
||||||
()
|
|
||||||
(c values c (0 (u . "'()\n"))))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
((define sol
|
|
||||||
(solve
|
|
||||||
(begin
|
|
||||||
(assert (= (length xs) 2))
|
|
||||||
(assert (not (equal? xs (reverse xs))))
|
|
||||||
(assert (equal? xs (sort xs <))))))
|
|
||||||
((3) 0 () 0 () () (c values c (void)))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
((evaluate xs sol)
|
|
||||||
((3)
|
|
||||||
1
|
|
||||||
(((lib "rosette/guide/scribble/util/lifted.rkt")
|
|
||||||
.
|
|
||||||
deserialize-info:opaque-v0))
|
|
||||||
0
|
|
||||||
()
|
|
||||||
()
|
|
||||||
(c values c (0 (u . "'(-1 0)\n"))))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
((define-symbolic b boolean?) ((3) 0 () 0 () () (c values c (void))) #"" #"")
|
|
||||||
((define p (if b (cons 1 2) (cons 4 #f)))
|
|
||||||
((3) 0 () 0 () () (c values c (void)))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
((define sol (solve (assert (boolean? (cdr p)))))
|
|
||||||
((3) 0 () 0 () () (c values c (void)))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
((evaluate p sol)
|
|
||||||
((3)
|
|
||||||
1
|
|
||||||
(((lib "rosette/guide/scribble/util/lifted.rkt")
|
|
||||||
.
|
|
||||||
deserialize-info:opaque-v0))
|
|
||||||
0
|
|
||||||
()
|
|
||||||
()
|
|
||||||
(c values c (0 (u . "'(4 . #f)\n"))))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
((define sol (solve (assert (odd? (car p)))))
|
|
||||||
((3) 0 () 0 () () (c values c (void)))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
((evaluate p sol)
|
|
||||||
((3)
|
|
||||||
1
|
|
||||||
(((lib "rosette/guide/scribble/util/lifted.rkt")
|
|
||||||
.
|
|
||||||
deserialize-info:opaque-v0))
|
|
||||||
0
|
|
||||||
()
|
|
||||||
()
|
|
||||||
(c values c (0 (u . "'(1 . 2)\n"))))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
|
|
@ -1,193 +0,0 @@
|
||||||
#lang scribble/manual
|
|
||||||
|
|
||||||
@(require (for-label
|
|
||||||
rosette/base/form/define rosette/query/query
|
|
||||||
rosette/base/core/term
|
|
||||||
(only-in rosette/base/base assert vc clear-vc! define-symbolic
|
|
||||||
length-bv list-ref-bv list-set-bv
|
|
||||||
take-bv take-right-bv
|
|
||||||
drop-bv drop-right-bv
|
|
||||||
list-tail-bv split-at-bv split-at-right-bv
|
|
||||||
union? bitvector bitvector? bv?
|
|
||||||
bitvector->natural integer->bitvector)
|
|
||||||
racket)
|
|
||||||
scribble/core scribble/html-properties scribble/examples racket/sandbox racket/runtime-path
|
|
||||||
"../util/lifted.rkt")
|
|
||||||
|
|
||||||
|
|
||||||
@(define-runtime-path root ".")
|
|
||||||
@(define rosette-eval (rosette-log-evaluator (logfile root "pairs-log")))
|
|
||||||
|
|
||||||
@(define pairs:constructors+selectors (select '(pair? null? cons car cdr null list? list list* build-list)))
|
|
||||||
@(define list-operations (select '(length list-ref list-tail append reverse)))
|
|
||||||
@(define list-iteration (select '(map andmap ormap for-each foldl foldr)))
|
|
||||||
@(define list-filtering (select '(filter remove remq remv remove* remq* remv* sort)))
|
|
||||||
@(define list-searching (select '(member memv memq memf findf assoc assv assq assf)))
|
|
||||||
@(define more-pair-ops (select '(caar cadr cdar cddr caaar caadr cadar caddr cdaar cdadr cddar cdddr caaaar caaadr caadar caaddr cadaar cadadr caddar cadddr cdaaar cdaadr cdadar cdaddr cddaar cddadr cdddar cddddr)))
|
|
||||||
@(define more-list-ops (select '(empty cons? empty? first rest second third fourth fifth sixth seventh eighth ninth tenth last last-pair make-list take drop split-at takef dropf splitf-at take-right drop-right split-at-right takef-right dropf-right splitf-at-right add-between append* flatten remove-duplicates filter-map count partition range append-map filter-not shuffle permutations in-permutations argmin argmax list-set)))
|
|
||||||
|
|
||||||
@title[#:tag "sec:pair"]{Pairs and Lists}
|
|
||||||
|
|
||||||
A pair combines two values, and a list is either the
|
|
||||||
constant @racket[null] or a pair whose second
|
|
||||||
element is a list. Pairs and lists are transparent immutable values, and they may
|
|
||||||
be concrete or symbolic.
|
|
||||||
Two pairs or two lists are @racket[eq?] (resp. @racket[equal?])
|
|
||||||
if their corresponding elements are @racket[eq?] (resp. @racket[equal?]).
|
|
||||||
|
|
||||||
As values of @tech[#:key "unsolvable type"]{unsolvable types}, symbolic pairs
|
|
||||||
and lists cannot be created
|
|
||||||
via @seclink["sec:symbolic-constants"]{@code{define-symbolic[*]}}.
|
|
||||||
Instead, they are created by applying pair- or list-producing procedures to symbolic inputs,
|
|
||||||
or by controlling the application of such procedures with symbolic values. This
|
|
||||||
pattern for creating non-primitive symbolic values generalizes to all unsolvable datatypes.
|
|
||||||
|
|
||||||
@examples[#:eval rosette-eval
|
|
||||||
(define-symbolic x y z n integer?)
|
|
||||||
(code:line (define xs (take (list x y z) n)) (code:comment "(1) xs is a symbolic list."))
|
|
||||||
(define sol (solve (assert (null? xs))))
|
|
||||||
(evaluate xs sol)
|
|
||||||
(define sol
|
|
||||||
(solve (begin
|
|
||||||
(assert (= (length xs) 2))
|
|
||||||
(assert (not (equal? xs (reverse xs))))
|
|
||||||
(assert (equal? xs (sort xs <))))))
|
|
||||||
(evaluate xs sol)]
|
|
||||||
|
|
||||||
@examples[#:eval rosette-eval
|
|
||||||
(define-symbolic b boolean?)
|
|
||||||
(code:line (define p (if b (cons 1 2) (cons 4 #f))) (code:comment "(2) p is a symbolic pair."))
|
|
||||||
(define sol (solve (assert (boolean? (cdr p)))))
|
|
||||||
(evaluate p sol)
|
|
||||||
(define sol (solve (assert (odd? (car p)))))
|
|
||||||
(evaluate p sol)
|
|
||||||
]
|
|
||||||
|
|
||||||
@section{Lifted Operations on Pairs and Lists}
|
|
||||||
|
|
||||||
Rosette lifts the following operations on pairs and lists:
|
|
||||||
@tabular[#:style (style #f (list (attributes '((id . "lifted")(class . "boxed")))))
|
|
||||||
(list (list @elem{Pair Operations} @pairs:constructors+selectors)
|
|
||||||
(list @elem{List Operations} @list-operations)
|
|
||||||
(list @elem{List Iteration} @list-iteration)
|
|
||||||
(list @elem{List Filtering} @list-filtering)
|
|
||||||
(list @elem{List Searching} @list-searching)
|
|
||||||
(list @elem{Additional Pair Operations} @more-pair-ops)
|
|
||||||
(list @elem{Additional List Operations} @more-list-ops))]
|
|
||||||
|
|
||||||
@(kill-evaluator rosette-eval)
|
|
||||||
@(set! rosette-eval (rosette-evaluator))
|
|
||||||
|
|
||||||
@section{Additional Operations on Pairs and Lists}
|
|
||||||
|
|
||||||
Rosette provides the following procedures for operating on lists using @seclink["sec:bitvectors"]{bitvector} indices and lengths. These procedures produce symbolic values that avoid @racketlink[bitvector->natural]{casting} their bitvector arguments to integers, leading to @seclink["sec:notes"]{more efficiently solvable queries}.
|
|
||||||
|
|
||||||
@declare-exporting[rosette/base/base #:use-sources (rosette/base/base)]
|
|
||||||
|
|
||||||
@defproc[(length-bv [lst list?] [t (or/c bitvector? union?)]) bv?]{
|
|
||||||
Equivalent to @racket[(integer->bitvector (length lst) t)] but avoids the @racket[integer->bitvector] cast for better solving performance.
|
|
||||||
|
|
||||||
@examples[#:eval rosette-eval
|
|
||||||
(define-symbolic b boolean?)
|
|
||||||
(define xs (if b '(1 2) '(3 4 5 6)))
|
|
||||||
xs
|
|
||||||
(integer->bitvector (length xs) (bitvector 4))
|
|
||||||
(length-bv xs (bitvector 4))]
|
|
||||||
}
|
|
||||||
|
|
||||||
@defproc[(list-ref-bv [lst list?] [pos bv?]) any/c]{
|
|
||||||
Equivalent to @racket[(list-ref lst (bitvector->natural pos))] but avoids the @racket[bitvector->natural] cast for better solving performance.
|
|
||||||
|
|
||||||
@examples[#:eval rosette-eval
|
|
||||||
(define-symbolic p (bitvector 1))
|
|
||||||
(define xs '(1 2 3 4))
|
|
||||||
(code:comment "Uses a cast and generates a redundant assertion on the range of p:")
|
|
||||||
(list-ref xs (bitvector->natural p))
|
|
||||||
(vc)
|
|
||||||
(clear-vc!)
|
|
||||||
(code:comment "No cast and no redundant range assertion:")
|
|
||||||
(list-ref-bv xs p)
|
|
||||||
(vc)
|
|
||||||
(code:comment "But the range assertion is generated when needed:")
|
|
||||||
(define-symbolic q (bitvector 4))
|
|
||||||
(list-ref-bv xs q)
|
|
||||||
(vc)]
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@(rosette-eval '(clear-vc!))
|
|
||||||
@defproc[(list-set-bv [lst list?] [pos bv?] [val any/c]) list?]{
|
|
||||||
Equivalent to @racket[(list-set lst (bitvector->natural pos) val)] but avoids the @racket[bitvector->natural] cast for better solving performance.
|
|
||||||
|
|
||||||
@examples[#:eval rosette-eval
|
|
||||||
(define-symbolic p (bitvector 1))
|
|
||||||
(define xs '(1 2 3 4))
|
|
||||||
(code:comment "Uses a cast and generates a redundant assertion on the range of p:")
|
|
||||||
(list-set xs (bitvector->natural p) 5)
|
|
||||||
(vc)
|
|
||||||
(clear-vc!)
|
|
||||||
(code:comment "No cast and no redundant range assertion:")
|
|
||||||
(list-set-bv xs p 5)
|
|
||||||
(vc)
|
|
||||||
(code:comment "But the range assertion is generated when needed:")
|
|
||||||
(define-symbolic q (bitvector 4))
|
|
||||||
(list-set-bv xs q 5)
|
|
||||||
(vc)]
|
|
||||||
}
|
|
||||||
|
|
||||||
@(rosette-eval '(clear-vc!))
|
|
||||||
@defproc*[([(take-bv [lst any/c] [pos bv?]) list?]
|
|
||||||
[(take-right-bv [lst any/c] [pos bv?]) any/c]
|
|
||||||
[(drop-bv [lst any/c] [pos bv?]) any/c]
|
|
||||||
[(drop-right-bv [lst any/c] [pos bv?]) list?]
|
|
||||||
[(list-tail-bv [lst any/c] [pos bv?]) any/c])]{
|
|
||||||
|
|
||||||
Equivalent to @racket[take], @racket[take-right],
|
|
||||||
@racket[drop], @racket[drop-right], or @racket[list-tail]
|
|
||||||
applied to @racket[lst] and
|
|
||||||
@racket[(bitvector->natural pos)], but avoids the
|
|
||||||
@racket[bitvector->natural] cast for better solving
|
|
||||||
performance.
|
|
||||||
|
|
||||||
@examples[#:eval rosette-eval
|
|
||||||
(define-symbolic p (bitvector 1))
|
|
||||||
(define xs (cons 1 (cons 2 (cons 3 4))))
|
|
||||||
(code:comment "Uses a cast and generates a redundant assertion on the range of p:")
|
|
||||||
(take xs (bitvector->natural p))
|
|
||||||
(vc)
|
|
||||||
(clear-vc!)
|
|
||||||
(code:comment "No cast and no redundant range assertion:")
|
|
||||||
(take-bv xs p)
|
|
||||||
(vc)
|
|
||||||
(code:comment "But the range assertion is generated when needed:")
|
|
||||||
(define-symbolic q (bitvector 4))
|
|
||||||
(take-bv xs q)
|
|
||||||
(vc)]
|
|
||||||
}
|
|
||||||
|
|
||||||
@(rosette-eval '(clear-vc!))
|
|
||||||
@defproc*[([(split-at-bv [lst any/c] [pos bv?]) (list? any/c)]
|
|
||||||
[(split-at-right-bv [lst any/c] [pos bv?]) (list? any/c)])]{
|
|
||||||
|
|
||||||
Equivalent to
|
|
||||||
@racket[(split-at lst (bitvector->natural pos))] or
|
|
||||||
@racket[(split-at-right lst (bitvector->natural pos))], but
|
|
||||||
avoids the @racket[bitvector->natural] cast for better
|
|
||||||
solving performance.
|
|
||||||
|
|
||||||
@examples[#:eval rosette-eval
|
|
||||||
(define-symbolic p (bitvector 1))
|
|
||||||
(define xs (cons 1 2))
|
|
||||||
(code:comment "Uses a cast and generates a redundant assertion on the range of p:")
|
|
||||||
(split-at xs (bitvector->natural p))
|
|
||||||
(vc)
|
|
||||||
(clear-vc!)
|
|
||||||
(code:comment "No cast and no redundant range assertion:")
|
|
||||||
(split-at-bv xs p)
|
|
||||||
(vc)
|
|
||||||
(code:comment "But the range assertion is generated when needed:")
|
|
||||||
(define-symbolic q (bitvector 4))
|
|
||||||
(split-at-bv xs q)
|
|
||||||
(vc)]}
|
|
||||||
|
|
||||||
@(kill-evaluator rosette-eval)
|
|
||||||
|
|
@ -1,40 +0,0 @@
|
||||||
;; This file was created by make-log-based-eval
|
|
||||||
((require (only-in racket string->symbol))
|
|
||||||
((3) 0 () 0 () () (c values c (void)))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
((define-symbolic b boolean?) ((3) 0 () 0 () () (c values c (void))) #"" #"")
|
|
||||||
((define-symbolic x integer?) ((3) 0 () 0 () () (c values c (void))) #"" #"")
|
|
||||||
((define p (if b * -)) ((3) 0 () 0 () () (c values c (void))) #"" #"")
|
|
||||||
((define sol (synthesize #:forall (list x) #:guarantee (assert (= x (p x 1)))))
|
|
||||||
((3) 0 () 0 () () (c values c (void)))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
((evaluate p sol)
|
|
||||||
((3)
|
|
||||||
1
|
|
||||||
(((lib "rosette/guide/scribble/util/lifted.rkt")
|
|
||||||
.
|
|
||||||
deserialize-info:opaque-v0))
|
|
||||||
0
|
|
||||||
()
|
|
||||||
()
|
|
||||||
(c values c (0 (u . "*\n"))))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
((define sol (synthesize #:forall (list x) #:guarantee (assert (= x (p x 0)))))
|
|
||||||
((3) 0 () 0 () () (c values c (void)))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
((evaluate p sol)
|
|
||||||
((3)
|
|
||||||
1
|
|
||||||
(((lib "rosette/guide/scribble/util/lifted.rkt")
|
|
||||||
.
|
|
||||||
deserialize-info:opaque-v0))
|
|
||||||
0
|
|
||||||
()
|
|
||||||
()
|
|
||||||
(c values c (0 (u . "-\n"))))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
|
|
@ -1,531 +0,0 @@
|
||||||
#lang scribble/manual
|
|
||||||
|
|
||||||
@(require (for-label
|
|
||||||
rosette/solver/solver rosette/solver/solution
|
|
||||||
rosette/solver/smt/z3 rosette/solver/smt/cvc4
|
|
||||||
rosette/solver/smt/boolector
|
|
||||||
rosette/solver/smt/bitwuzla
|
|
||||||
rosette/solver/smt/cvc5
|
|
||||||
rosette/solver/smt/stp
|
|
||||||
rosette/solver/smt/yices
|
|
||||||
rosette/base/form/define rosette/query/query
|
|
||||||
rosette/base/core/term (only-in rosette/base/base bv?)
|
|
||||||
(only-in rosette/base/base assert)
|
|
||||||
racket)
|
|
||||||
scribble/core scribble/html-properties scribble/examples racket/sandbox racket/runtime-path
|
|
||||||
"../util/lifted.rkt")
|
|
||||||
|
|
||||||
@(define-runtime-path root ".")
|
|
||||||
@(define rosette-eval (rosette-log-evaluator (logfile root "solvers-log")))
|
|
||||||
|
|
||||||
@title[#:tag "sec:solvers-and-solutions"]{Solvers and Solutions}
|
|
||||||
|
|
||||||
@declare-exporting[rosette/query/query
|
|
||||||
rosette/solver/solver
|
|
||||||
rosette/solver/solution
|
|
||||||
rosette/solver/smt/z3
|
|
||||||
rosette/solver/smt/cvc4
|
|
||||||
rosette/solver/smt/boolector
|
|
||||||
rosette/solver/smt/bitwuzla
|
|
||||||
rosette/solver/smt/cvc5
|
|
||||||
rosette/solver/smt/stp
|
|
||||||
rosette/solver/smt/yices
|
|
||||||
#:use-sources
|
|
||||||
(rosette/query/finitize
|
|
||||||
rosette/query/query
|
|
||||||
rosette/solver/solver
|
|
||||||
rosette/solver/solution
|
|
||||||
rosette/solver/smt/z3
|
|
||||||
rosette/solver/smt/cvc4
|
|
||||||
rosette/solver/smt/boolector
|
|
||||||
rosette/solver/smt/bitwuzla
|
|
||||||
rosette/solver/smt/cvc5
|
|
||||||
rosette/solver/smt/stp
|
|
||||||
rosette/solver/smt/yices)]
|
|
||||||
|
|
||||||
A @deftech{solver} is an automatic reasoning engine, used to answer
|
|
||||||
@seclink["sec:queries"]{queries} about Rosette programs. The result of
|
|
||||||
a solver invocation is a @deftech{solution}, containing either
|
|
||||||
a @tech{binding} of symbolic constants to concrete values, or
|
|
||||||
an @link["https://en.wikipedia.org/wiki/Unsatisfiable_core"]{unsatisfiable core}.
|
|
||||||
Solvers and solutions may not be symbolic. Two solvers (resp. solutions) are @racket[eq?]/@racket[equal?]
|
|
||||||
if they refer to the same object.
|
|
||||||
|
|
||||||
@section{The Solver Interface}
|
|
||||||
|
|
||||||
A solver contains a stack of assertions (boolean terms) to satisfy and a set of objectives (numeric terms) to optimize.
|
|
||||||
The assertion stack is partitioned into levels, with each level containing
|
|
||||||
a set of assertions. The bottom (0) assertion level cannot be removed, but more levels
|
|
||||||
can be created and removed using the @racket[solver-push] and @racket[solver-pop] procedures.
|
|
||||||
The @racket[solver-assert] procedure adds assertions to the top level of the assertion stack, while
|
|
||||||
the @racket[solver-minimize] and @racket[solver-maximize] procedures add new terms to the current set of optimization objectives.
|
|
||||||
The @racket[solver-check] procedure checks the satisfiability of all assertions in the assertion stack,
|
|
||||||
optimizing the resulting solution (if any) with respect to the provided objectives.
|
|
||||||
|
|
||||||
@defparam[current-solver solver solver?]{
|
|
||||||
The @racket[current-solver] parameter holds the solver object used for
|
|
||||||
answering solver-aided queries. Rosette's default solver is @racket[z3], although
|
|
||||||
new (SMT) solvers can be added as well. Rosette will work with any solver that implements the
|
|
||||||
@racket[gen:solver] generic interface.
|
|
||||||
@examples[#:eval rosette-eval
|
|
||||||
(current-solver)]
|
|
||||||
}
|
|
||||||
|
|
||||||
@defthing[gen:solver solver?]{
|
|
||||||
A @hyperlink["https://docs.racket-lang.org/reference/struct-generics.html"]{generic interface}
|
|
||||||
that specifies the procedures provided by a solver. These include
|
|
||||||
@racket[solver-assert],
|
|
||||||
@racket[solver-push],
|
|
||||||
@racket[solver-pop],
|
|
||||||
@racket[solver-clear],
|
|
||||||
@racket[solver-minimize],
|
|
||||||
@racket[solver-maximize],
|
|
||||||
@racket[solver-check],
|
|
||||||
@racket[solver-debug],
|
|
||||||
@racket[solver-shutdown], and
|
|
||||||
@racket[solver-features].
|
|
||||||
A solver may support a subset of this interface, which loosely follows
|
|
||||||
the @hyperlink["http://smtlib.cs.uiowa.edu/papers/smt-lib-reference-v2.5-r2015-06-28.pdf"]{SMTLib solver interface}.
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@defproc[(solver? [v any/c]) boolean?]{
|
|
||||||
Returns true if @racket[v] is a concrete value that implements the @racket[gen:solver] interface.}
|
|
||||||
|
|
||||||
@defproc[(solver-assert [solver solver?] [constraints (listof boolean?)]) void?]{
|
|
||||||
Takes as input a list of boolean terms or values and
|
|
||||||
adds them to the current (top) level in the assertion stack.}
|
|
||||||
@defproc[(solver-push [solver solver?]) void?]{
|
|
||||||
Pushes a new level onto the solver's assertion stack. Subsequent calls to
|
|
||||||
@racket[solver-assert] will add assertions to this level.}
|
|
||||||
|
|
||||||
@defproc[(solver-pop [solver solver?] [levels integer?]) void?]{
|
|
||||||
Pops the given number of levels off the solver's assertion stack,
|
|
||||||
removing all the assertions at the popped levels. The number of @racket[levels] to
|
|
||||||
pop must be a positive integer that is no greater than the number of preceding
|
|
||||||
calls to @racket[solver-push].}
|
|
||||||
|
|
||||||
@defproc[(solver-clear [solver solver?]) void?]{
|
|
||||||
Clears the assertion stack of all levels and all assertions,
|
|
||||||
and removes all objectives from the current set of objectives to optimize.}
|
|
||||||
|
|
||||||
@defproc*[([(solver-minimize [solver solver?] [objs (listof (or/c integer? real? bv?))]) void?]
|
|
||||||
[(solver-maximize [solver solver?] [objs (listof (or/c integer? real? bv?))]) void?])]{
|
|
||||||
Adds the given optimization objectives to the given solver. These objectives take the form of
|
|
||||||
numeric terms whose value is to be minimized or maximized by subsequent calls to @racket[solver-check],
|
|
||||||
while satisfying all the boolean terms asserted via @racket[solver-assert].}
|
|
||||||
|
|
||||||
@defproc[(solver-check [solver solver?]) solution?]{
|
|
||||||
Searches for a binding from symbolic constants to concrete values that satisfies all
|
|
||||||
constraints (boolean terms) added to the solver via @racket[solver-assert].
|
|
||||||
If such a binding---or, a @racket[model]---exists,
|
|
||||||
it is returned in the form of a satisfiable (@racket[sat?]) solution, which optimizes
|
|
||||||
the objective terms added to the solver via @racket[solver-minimize] and @racket[solver-maximize].
|
|
||||||
Otherwise, an unsatisfiable (@racket[unsat?]) solution is returned, but without
|
|
||||||
computing an unsatisfiable @racket[core] (i.e., calling @racket[core] on the
|
|
||||||
resulting solution produces @racket[#f]).
|
|
||||||
}
|
|
||||||
|
|
||||||
@defproc[(solver-debug [solver solver?]) solution?]{
|
|
||||||
Searches for an unsatisfiable core of all constraints (boolean terms)
|
|
||||||
added to the solver via @racket[solver-assert] @emph{after} the most recent call to
|
|
||||||
@racket[clear] or @racket[solver-check] (if any).
|
|
||||||
If the constraints are satisfiable, or the given solver does
|
|
||||||
not support core extraction, an error is thrown. Otherwise, the result is an
|
|
||||||
@racket[unsat?] solution with a unsatisfiable @racket[core], expressed as a
|
|
||||||
list of boolean terms.
|
|
||||||
}
|
|
||||||
|
|
||||||
@defproc[(solver-shutdown [solver solver?]) void?]{
|
|
||||||
Terminates the current solving process (if any),
|
|
||||||
clears all added constraints, and releases all system resources associated
|
|
||||||
with the given solver instance. The solver must be able to reacquire these resources
|
|
||||||
if needed. That is, the solver should behave as though its state was merely cleared
|
|
||||||
(via @racket[solver-clear]) after a shutdown call.
|
|
||||||
}
|
|
||||||
|
|
||||||
@defproc[(solver-features [solver solver?]) (listof symbol?)]{
|
|
||||||
Returns the list of features supported by the solver.
|
|
||||||
The possible features, which correspond roughly to SMTLib @emph{logics},
|
|
||||||
extended with some additional options, are:
|
|
||||||
|
|
||||||
@itemize[
|
|
||||||
@item{@racket['qf_bv] (quantifier-free fixed-width bitvectors)}
|
|
||||||
@item{@racket['qf_uf] (quantifier-free uninterpreted functions and equality)}
|
|
||||||
@item{@racket['qf_lia] (quantifier-free linear integer arithmetic)}
|
|
||||||
@item{@racket['qf_nia] (quantifier-free non-linear integer arithmetic)}
|
|
||||||
@item{@racket['qf_lra] (quantifier-free linear real arithmetic)}
|
|
||||||
@item{@racket['qf_nra] (quantifier-free non-linear real arithmetic)}
|
|
||||||
@item{@racket['quantifiers] (quantified versions of the supported quantifier-free logics)}
|
|
||||||
@item{@racket['optimize] (support for objective function optimization)}
|
|
||||||
@item{@racket['unsat-cores] (unsatisfiable core generation)}
|
|
||||||
]
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@defproc[(solver-options [solver solver?]) (hash/c symbol? any/c)]{
|
|
||||||
Returns the options the given solver is configured with
|
|
||||||
(as specified by the @racket[#:options] argument to solver constructors).
|
|
||||||
}
|
|
||||||
|
|
||||||
@defparam[output-smt on? (or/c boolean? path-string? output-port?)]{
|
|
||||||
Enables verbose output of generated SMT constraints.
|
|
||||||
|
|
||||||
When the @racket[output-smt] parameter is @racket[#t] or a @racket[path-string?],
|
|
||||||
Rosette will log the SMT encoding of all solver queries to temporary files.
|
|
||||||
A new temporary file is created for each solver process Rosette spawns.
|
|
||||||
Note that a single solver-aided query may spawn multiple solver processes,
|
|
||||||
and Rosette may reuse a solver process across several solver-aided queries.
|
|
||||||
When @racket[output-smt] is @racket[#t], the temporary files are created
|
|
||||||
in the system's temporary directory; otherwise,
|
|
||||||
the temporary files are created in the given path (which must be a directory).
|
|
||||||
The path to each temporary file is printed to @racket[current-error-port]
|
|
||||||
when it is first created.
|
|
||||||
|
|
||||||
When the @racket[output-smt] parameter is an @racket[output-port?],
|
|
||||||
Rosette will log the SMT encoding to that output port.
|
|
||||||
For example, setting @racket[output-smt] to @racket[(current-output-port)]
|
|
||||||
will print the SMT encoding to standard output.
|
|
||||||
All solvers will log to the same output port,
|
|
||||||
so several separate encodings may be interleaved when multiple solvers are in use.
|
|
||||||
|
|
||||||
Default value is @racket[#f].
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@section{Supported Solvers}
|
|
||||||
|
|
||||||
Rosette supports several SMT solvers.
|
|
||||||
The @racket[current-solver] parameter controls the solver used for answering solver-aided queries.
|
|
||||||
Each supported solver is contained in a separate module
|
|
||||||
(e.g., @racketmodname[rosette/solver/smt/z3]),
|
|
||||||
which exports a constructor (e.g., @racket[z3])
|
|
||||||
to create a new solver instance.
|
|
||||||
|
|
||||||
@subsection{Z3}
|
|
||||||
|
|
||||||
@defmodule[rosette/solver/smt/z3 #:no-declare]
|
|
||||||
|
|
||||||
@defproc*[([(z3 [#:path path (or/c path-string? #f) #f]
|
|
||||||
[#:logic logic (or/c symbol? #f) #f]
|
|
||||||
[#:options options (hash/c symbol? any/c) (hash)]) solver?]
|
|
||||||
[(z3? [v any/c]) boolean?])]{
|
|
||||||
|
|
||||||
Returns a @racket[solver?] wrapper for the @hyperlink["https://github.com/Z3Prover/z3/"]{Z3} solver from Microsoft Research.
|
|
||||||
Rosette automatically installs a version of Z3;
|
|
||||||
the optional @racket[path] argument overrides this version with a path to a new Z3 binary.
|
|
||||||
|
|
||||||
The optional @racket[logic] argument specifies an SMT logic for the solver to use (e.g., @racket['QF_BV]).
|
|
||||||
Specifying a logic can improve solving performance, but Rosette makes no effort to check that
|
|
||||||
emitted constraints fall within the chosen logic. The default is @racket[#f],
|
|
||||||
which uses Z3's default logic.
|
|
||||||
|
|
||||||
The @racket[options] argument provides additional options that are sent to Z3
|
|
||||||
via the @tt{set-option} SMT command.
|
|
||||||
For example, setting @racket[options] to @racket[(hash ':smt.relevancy 0)]
|
|
||||||
will send the command @tt{(set-option :smt.relevancy 0)} to Z3 prior to solving.
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@subsection{CVC4}
|
|
||||||
|
|
||||||
@defmodule[rosette/solver/smt/cvc4 #:no-declare]
|
|
||||||
|
|
||||||
@defproc*[([(cvc4 [#:path path (or/c path-string? #f) #f]
|
|
||||||
[#:logic logic (or/c symbol? #f) #f]
|
|
||||||
[#:options options (hash/c symbol? any/c) (hash)]) solver?]
|
|
||||||
[(cvc4? [v any/c]) boolean?])]{
|
|
||||||
|
|
||||||
Returns a @racket[solver?] wrapper for the @hyperlink["http://cvc4.cs.stanford.edu/web/"]{CVC4} solver from NYU and UIowa.
|
|
||||||
|
|
||||||
To use this solver, download and install CVC4 (version 1.8 or later),
|
|
||||||
and either add the @tt{cvc4} executable to your @tt{PATH}
|
|
||||||
or pass the path to the executable as the optional @racket[path] argument.
|
|
||||||
|
|
||||||
The optional @racket[logic] argument specifies an SMT logic for the solver to use (e.g., @racket['QF_BV]).
|
|
||||||
Specifying a logic can improve solving performance, but Rosette makes no effort to check that
|
|
||||||
emitted constraints fall within the chosen logic. The default is @racket[#f],
|
|
||||||
which uses CVC4's default logic.
|
|
||||||
|
|
||||||
The @racket[options] argument provides additional options that are sent to CVC4
|
|
||||||
via the @tt{set-option} SMT command.
|
|
||||||
For example, setting @racket[options] to @racket[(hash ':bv-propagate 'true)]
|
|
||||||
will send the command @tt{(set-option :bv-propagate true)} to CVC4 prior to solving.
|
|
||||||
}
|
|
||||||
|
|
||||||
@defproc[(cvc4-available?) boolean?]{
|
|
||||||
Returns true if the CVC4 solver is available for use (i.e., Rosette can locate a @tt{cvc4} binary).
|
|
||||||
If this returns @racket[#f], @racket[(cvc4)] will not succeed
|
|
||||||
without its optional @racket[path] argument.}
|
|
||||||
|
|
||||||
|
|
||||||
@subsection{Boolector}
|
|
||||||
|
|
||||||
@defmodule[rosette/solver/smt/boolector #:no-declare]
|
|
||||||
|
|
||||||
@defproc*[([(boolector [#:path path (or/c path-string? #f) #f]
|
|
||||||
[#:logic logic (or/c symbol? #f) #f]
|
|
||||||
[#:options options (hash/c symbol? any/c) (hash)]) solver?]
|
|
||||||
[(boolector? [v any/c]) boolean?])]{
|
|
||||||
|
|
||||||
Returns a @racket[solver?] wrapper for the @hyperlink["http://fmv.jku.at/boolector/"]{Boolector} solver from JKU.
|
|
||||||
|
|
||||||
To use this solver, download and install Boolector (version 2.4.1 or later),
|
|
||||||
and either add the @tt{boolector} executable to your @tt{PATH}
|
|
||||||
or pass the path to the executable as the optional @racket[path] argument.
|
|
||||||
|
|
||||||
The optional @racket[logic] argument specifies an SMT logic for the solver to use (e.g., @racket['QF_BV]).
|
|
||||||
Specifying a logic can improve solving performance, but Rosette makes no effort to check that
|
|
||||||
emitted constraints fall within the chosen logic. The default is @racket[#f],
|
|
||||||
which uses Boolector's default logic.
|
|
||||||
|
|
||||||
The @racket[options] argument provides additional options that are sent to Boolector
|
|
||||||
via the @tt{set-option} SMT command.
|
|
||||||
For example, setting @racket[options] to @racket[(hash ':seed 5)]
|
|
||||||
will send the command @tt{(set-option :seed 5)} to Boolector prior to solving.
|
|
||||||
}
|
|
||||||
|
|
||||||
@defproc[(boolector-available?) boolean?]{
|
|
||||||
Returns true if the Boolector solver is available for use (i.e., Rosette can locate a @tt{boolector} binary).
|
|
||||||
If this returns @racket[#f], @racket[(boolector)] will not succeed
|
|
||||||
without its optional @racket[path] argument.}
|
|
||||||
|
|
||||||
@subsection{Bitwuzla}
|
|
||||||
|
|
||||||
@defmodule[rosette/solver/smt/bitwuzla #:no-declare]
|
|
||||||
|
|
||||||
@defproc*[([(bitwuzla [#:path path (or/c path-string? #f) #f]
|
|
||||||
[#:logic logic (or/c symbol? #f) #f]
|
|
||||||
[#:options options (hash/c symbol? any/c) (hash)]) solver?]
|
|
||||||
[(bitwuzla? [v any/c]) boolean?])]{
|
|
||||||
|
|
||||||
Returns a @racket[solver?] wrapper for the @hyperlink["https://bitwuzla.github.io/"]{Bitwuzla} solver.
|
|
||||||
|
|
||||||
To use this solver, download prebuilt Bitwuzla or build it yourself,
|
|
||||||
and ensure the executable is on your @tt{PATH} or pass the path to the
|
|
||||||
executable as the optional @racket[path] argument.
|
|
||||||
Rosette currently tests Bitwuzla at commit
|
|
||||||
@tt{93a3d930f622b4cef0063215e63b7c3bd10bd663}.
|
|
||||||
|
|
||||||
The optional @racket[logic] argument specifies an SMT logic for the solver to use (e.g., @racket['QF_BV]).
|
|
||||||
Specifying a logic can improve solving performance, but Rosette makes no effort to check that
|
|
||||||
emitted constraints fall within the chosen logic. The default is @racket[#f],
|
|
||||||
which uses Bitwuzla's default logic.
|
|
||||||
|
|
||||||
The @racket[options] argument provides additional options that are sent to Bitwuzla
|
|
||||||
via the @tt{set-option} SMT command.
|
|
||||||
For example, setting @racket[options] to @racket[(hash ':seed 5)]
|
|
||||||
will send the command @tt{(set-option :seed 5)} to Bitwuzla prior to solving.
|
|
||||||
}
|
|
||||||
|
|
||||||
@defproc[(bitwuzla-available?) boolean?]{
|
|
||||||
Returns true if the Bitwuzla solver is available for use (i.e., Rosette can locate a @tt{bitwuzla} binary).
|
|
||||||
If this returns @racket[#f], @racket[(bitwuzla)] will not succeed
|
|
||||||
without its optional @racket[path] argument.}
|
|
||||||
|
|
||||||
@subsection{CVC5}
|
|
||||||
|
|
||||||
@defmodule[rosette/solver/smt/cvc5 #:no-declare]
|
|
||||||
|
|
||||||
@defproc*[([(cvc5 [#:path path (or/c path-string? #f) #f]
|
|
||||||
[#:logic logic (or/c symbol? #f) #f]
|
|
||||||
[#:options options (hash/c symbol? any/c) (hash)]) solver?]
|
|
||||||
[(cvc5? [v any/c]) boolean?])]{
|
|
||||||
|
|
||||||
Returns a @racket[solver?] wrapper for the @hyperlink["https://cvc5.github.io/"]{CVC5} solver.
|
|
||||||
|
|
||||||
To use this solver, download prebuilt CVC5 or build it yourself,
|
|
||||||
and ensure the executable is on your @tt{PATH} or pass the path to the
|
|
||||||
executable as the optional @racket[path] argument.
|
|
||||||
Rosette currently tests CVC5 at version 1.0.7.
|
|
||||||
|
|
||||||
The optional @racket[logic] argument specifies an SMT logic for the solver to use (e.g., @racket['QF_BV]).
|
|
||||||
Specifying a logic can improve solving performance, but Rosette makes no effort to check that
|
|
||||||
emitted constraints fall within the chosen logic. The default is @racket[#f],
|
|
||||||
which uses CVC5's default logic.
|
|
||||||
|
|
||||||
The @racket[options] argument provides additional options that are sent to CVC5
|
|
||||||
via the @tt{set-option} SMT command.
|
|
||||||
For example, setting @racket[options] to @racket[(hash ':seed 5)]
|
|
||||||
will send the command @tt{(set-option :seed 5)} to CVC5 prior to solving.
|
|
||||||
}
|
|
||||||
|
|
||||||
@defproc[(cvc5-available?) boolean?]{
|
|
||||||
Returns true if the CVC5 solver is available for use (i.e., Rosette can locate a @tt{cvc5} binary).
|
|
||||||
If this returns @racket[#f], @racket[(cvc5)] will not succeed
|
|
||||||
without its optional @racket[path] argument.}
|
|
||||||
|
|
||||||
@subsection{STP}
|
|
||||||
|
|
||||||
@defmodule[rosette/solver/smt/stp #:no-declare]
|
|
||||||
|
|
||||||
@defproc*[([(stp [#:path path (or/c path-string? #f) #f]
|
|
||||||
[#:logic logic (or/c symbol? #f) #f]
|
|
||||||
[#:options options (hash/c symbol? any/c) (hash)]) solver?]
|
|
||||||
[(stp? [v any/c]) boolean?])]{
|
|
||||||
|
|
||||||
Returns a @racket[solver?] wrapper for the @hyperlink["https://stp.github.io/"]{STP} solver.
|
|
||||||
|
|
||||||
To use this solver, download prebuilt STP or build it yourself,
|
|
||||||
and ensure the executable is on your @tt{PATH} or pass the path to the
|
|
||||||
executable as the optional @racket[path] argument.
|
|
||||||
Rosette currently tests STP at commit
|
|
||||||
@tt{0510509a85b6823278211891cbb274022340fa5c}.
|
|
||||||
Note that as of December 2023, the STP version on Mac Homebrew is too old to be
|
|
||||||
supported by Rosette.
|
|
||||||
|
|
||||||
The optional @racket[logic] argument specifies an SMT logic for the solver to use (e.g., @racket['QF_BV]).
|
|
||||||
Specifying a logic can improve solving performance, but Rosette makes no effort to check that
|
|
||||||
emitted constraints fall within the chosen logic. The default is @racket[#f],
|
|
||||||
which uses STP's default logic.
|
|
||||||
|
|
||||||
The @racket[options] argument provides additional options that are sent to STP
|
|
||||||
via the @tt{set-option} SMT command.
|
|
||||||
For example, setting @racket[options] to @racket[(hash ':seed 5)]
|
|
||||||
will send the command @tt{(set-option :seed 5)} to STP prior to solving.
|
|
||||||
}
|
|
||||||
|
|
||||||
@defproc[(stp-available?) boolean?]{
|
|
||||||
Returns true if the STP solver is available for use (i.e., Rosette can locate a @tt{stp} binary).
|
|
||||||
If this returns @racket[#f], @racket[(stp)] will not succeed
|
|
||||||
without its optional @racket[path] argument.}
|
|
||||||
|
|
||||||
@subsection{Yices2}
|
|
||||||
|
|
||||||
@defmodule[rosette/solver/smt/yices #:no-declare]
|
|
||||||
|
|
||||||
@defproc*[([(yices [#:path path (or/c path-string? #f) #f]
|
|
||||||
[#:logic logic (or/c symbol? #f) 'QF_BV]
|
|
||||||
[#:options options (hash/c symbol? any/c) (hash)]) solver?]
|
|
||||||
[(yices? [v any/c]) boolean?])]{
|
|
||||||
|
|
||||||
Returns a @racket[solver?] wrapper for the @hyperlink["https://yices.csl.sri.com/"]{Yices2} solver.
|
|
||||||
|
|
||||||
To use this solver, download prebuilt Yices2 or build it yourself,
|
|
||||||
and ensure the executable is on your @tt{PATH} or pass the path to the
|
|
||||||
executable as the optional @racket[path] argument.
|
|
||||||
Rosette specifically uses the @tt{yices-smt2} executable, which is the Yices2
|
|
||||||
solver with its SMTLIB2 frontend enabled.
|
|
||||||
Note that just building (without installing) Yices2 will produce an executable
|
|
||||||
named @tt{yices_smt2}. Running the installation step produces an executable
|
|
||||||
with the correct name. However, it is safe to skip the installation step and
|
|
||||||
simply rename or symlink the @tt{yices_smt2} executable to @tt{yices-smt2}.
|
|
||||||
Rosette currently tests Yices2 at commit
|
|
||||||
@tt{e27cf308cffb0ecc6cc7165c10e81ca65bc303b3}.
|
|
||||||
|
|
||||||
The optional @racket[logic] argument specifies an SMT logic for the solver to use (e.g., @racket['QF_BV]).
|
|
||||||
Specifying a logic can improve solving performance, but Rosette makes no effort to check that
|
|
||||||
emitted constraints fall within the chosen logic. Yices2 expects a logic to be
|
|
||||||
set; Rosette defaults to @racket['QF_BV].
|
|
||||||
|
|
||||||
The @racket[options] argument provides additional options that are sent to Yices2
|
|
||||||
via the @tt{set-option} SMT command.
|
|
||||||
For example, setting @racket[options] to @racket[(hash ':seed 5)]
|
|
||||||
will send the command @tt{(set-option :seed 5)} to Yices2 prior to solving.
|
|
||||||
}
|
|
||||||
|
|
||||||
@defproc[(yices-available?) boolean?]{
|
|
||||||
Returns true if the Yices2 solver is available for use (i.e., Rosette can locate a @tt{yices-smt2} binary).
|
|
||||||
If this returns @racket[#f], @racket[(yices)] will not succeed
|
|
||||||
without its optional @racket[path] argument.}
|
|
||||||
|
|
||||||
|
|
||||||
@section{Solutions}
|
|
||||||
|
|
||||||
A solution to a set of formulas may be satisfiable (@racket[sat?]), unsatisfiable (@racket[unsat?]),
|
|
||||||
or unknown (@racket[unknown?]).
|
|
||||||
A satisfiable solution can be used as a procedure: when applied to a bound symbolic constant, it returns
|
|
||||||
a concrete value for that constant; when applied to any other value, it returns
|
|
||||||
the value itself.
|
|
||||||
The solver returns an @racket[unknown?] solution if it cannot determine whether
|
|
||||||
the given constraints are satisfiable or not.
|
|
||||||
|
|
||||||
A solution supports the following operations:
|
|
||||||
|
|
||||||
@defproc[(solution? [v any/c]) boolean?]{
|
|
||||||
Returns true if @racket[v] is a solution.}
|
|
||||||
|
|
||||||
@defproc[(sat? [v any/c]) boolean?]{
|
|
||||||
Returns true if @racket[v] is a satisfiable solution.}
|
|
||||||
|
|
||||||
@defproc[(unsat? [v any/c]) boolean?]{
|
|
||||||
Returns true if @racket[v] is an unsatisfiable solution.}
|
|
||||||
|
|
||||||
@defproc[(unknown? [v any/c]) boolean?]{
|
|
||||||
Returns true if @racket[v] is an unknown solution.}
|
|
||||||
|
|
||||||
@defproc*[([(sat) sat?]
|
|
||||||
[(sat [binding (hash/c constant? any/c #:immutable #t)]) sat?])]{
|
|
||||||
Returns a satisfiable solution that holds the given binding from symbolic
|
|
||||||
constants to values, or that holds the empty binding. The provided hash must
|
|
||||||
bind every symbolic constant in its keyset to a concrete value of the same type.
|
|
||||||
}
|
|
||||||
|
|
||||||
@defproc*[([(unsat) unsat?]
|
|
||||||
[(unsat [constraints (listof boolean?)]) unsat?])]{
|
|
||||||
Returns an unsatisfiable solution. The @racket[constraints] list, if provided,
|
|
||||||
consist of boolean values that are collectively unsatisfiable. If no constraints
|
|
||||||
are provided, applying @racket[core] to the resulting solution produces @racket[#f],
|
|
||||||
indicating that there is no satisfying solution but
|
|
||||||
core extraction was not performed. (Core extraction is an expensive
|
|
||||||
operation that is not supported by all solvers; those that do support it
|
|
||||||
do not compute a core unless explicitly asked for one via @racket[solver-debug].)}
|
|
||||||
|
|
||||||
@defproc[(unknown) unknown?]{
|
|
||||||
Returns an unknown solution.}
|
|
||||||
|
|
||||||
@defproc[(model [sol sat?]) (hash/c constant? any/c #:immutable #t)]{
|
|
||||||
Returns the binding stored in the given satisfiable solution. The binding is an immutable
|
|
||||||
hashmap from symbolic constants to values.
|
|
||||||
}
|
|
||||||
|
|
||||||
@defproc[(core [sol unsat?]) (or/c (listof (and/c constant? boolean?)) #f)]{
|
|
||||||
Returns the unsatisfiable core stored in the given satisfiable solution. If the solution is
|
|
||||||
@racket[unsat?] and a core was computed, the result is a list of boolean values that
|
|
||||||
are collectively unsatisfiable. Otherwise, the result is @racket[#f].
|
|
||||||
}
|
|
||||||
|
|
||||||
@defproc[(evaluate [v any/c] [sol sat?]) any/c]{
|
|
||||||
Given a Rosette value and a satisfiable solution, @racket[evaluate] produces a
|
|
||||||
new value obtained by replacing every symbolic constant @var[c] in @racket[v]
|
|
||||||
with @racket[(sol #, @var[c])] and simplifying the result.
|
|
||||||
|
|
||||||
@examples[#:eval rosette-eval
|
|
||||||
(define-symbolic a b boolean?)
|
|
||||||
(define-symbolic x y integer?)
|
|
||||||
(define sol
|
|
||||||
(solve (begin (assert a)
|
|
||||||
(assert (= x 1))
|
|
||||||
(assert (= y 2)))))
|
|
||||||
(sat? sol)
|
|
||||||
(evaluate (list 4 5 x) sol)
|
|
||||||
(define vec (vector a))
|
|
||||||
(evaluate vec sol)
|
|
||||||
(code:line (eq? vec (evaluate vec sol)) (code:comment "Evaluation produces a new vector."))
|
|
||||||
(evaluate (+ x y) sol)
|
|
||||||
(evaluate (and a b) sol)
|
|
||||||
]}
|
|
||||||
|
|
||||||
@defproc[(complete-solution [sol solution?] [consts (listof constant?)]) solution?]{
|
|
||||||
|
|
||||||
Given a solution @racket[sol] and a list of symbolic
|
|
||||||
constants @racket[consts], returns a solution that is
|
|
||||||
complete with respect to the given list. In particular, if
|
|
||||||
@racket[sol] is satisfiable, the returned solution is also
|
|
||||||
satisfiable, and it extends the @racket[sol] model with
|
|
||||||
default bindings for all constants in @racket[consts] that
|
|
||||||
are not bound by @racket[sol]. Otherwise, @racket[sol]
|
|
||||||
itself is returned.
|
|
||||||
|
|
||||||
@examples[#:eval rosette-eval
|
|
||||||
(define-symbolic a boolean?)
|
|
||||||
(define-symbolic x integer?)
|
|
||||||
(define sol (solve (assert a)))
|
|
||||||
(code:line sol (code:comment "No binding for x."))
|
|
||||||
(complete-solution sol (list a x))
|
|
||||||
(complete-solution (solve (assert #f)) (list a x))
|
|
||||||
]}
|
|
||||||
|
|
||||||
@(kill-evaluator rosette-eval)
|
|
||||||
|
|
||||||
|
|
@ -1,101 +0,0 @@
|
||||||
;; This file was created by make-log-based-eval
|
|
||||||
((current-solver)
|
|
||||||
((3)
|
|
||||||
1
|
|
||||||
(((lib "rosette/guide/scribble/util/lifted.rkt")
|
|
||||||
.
|
|
||||||
deserialize-info:opaque-v0))
|
|
||||||
0
|
|
||||||
()
|
|
||||||
()
|
|
||||||
(c values c (0 (u . "#<z3>\n"))))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
((define-symbolic a b boolean?) ((3) 0 () 0 () () (c values c (void))) #"" #"")
|
|
||||||
((define-symbolic x y integer?) ((3) 0 () 0 () () (c values c (void))) #"" #"")
|
|
||||||
((define sol (solve (begin (assert a) (assert (= x 1)) (assert (= y 2)))))
|
|
||||||
((3) 0 () 0 () () (c values c (void)))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
((sat? sol) ((3) 0 () 0 () () (q values #t)) #"" #"")
|
|
||||||
((evaluate (list 4 5 x) sol)
|
|
||||||
((3)
|
|
||||||
1
|
|
||||||
(((lib "rosette/guide/scribble/util/lifted.rkt")
|
|
||||||
.
|
|
||||||
deserialize-info:opaque-v0))
|
|
||||||
0
|
|
||||||
()
|
|
||||||
()
|
|
||||||
(c values c (0 (u . "'(4 5 1)\n"))))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
((define vec (vector a)) ((3) 0 () 0 () () (c values c (void))) #"" #"")
|
|
||||||
((evaluate vec sol)
|
|
||||||
((3)
|
|
||||||
1
|
|
||||||
(((lib "rosette/guide/scribble/util/lifted.rkt")
|
|
||||||
.
|
|
||||||
deserialize-info:opaque-v0))
|
|
||||||
0
|
|
||||||
()
|
|
||||||
()
|
|
||||||
(c values c (0 (u . "'#(#t)\n"))))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
((eq? vec (evaluate vec sol)) ((3) 0 () 0 () () (q values #f)) #"" #"")
|
|
||||||
((evaluate (+ x y) sol) ((3) 0 () 0 () () (q values 3)) #"" #"")
|
|
||||||
((evaluate (and a b) sol)
|
|
||||||
((3)
|
|
||||||
1
|
|
||||||
(((lib "rosette/guide/scribble/util/lifted.rkt")
|
|
||||||
.
|
|
||||||
deserialize-info:opaque-v0))
|
|
||||||
0
|
|
||||||
()
|
|
||||||
()
|
|
||||||
(c values c (0 (u . "b\n"))))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
((define-symbolic a boolean?) ((3) 0 () 0 () () (c values c (void))) #"" #"")
|
|
||||||
((define-symbolic x integer?) ((3) 0 () 0 () () (c values c (void))) #"" #"")
|
|
||||||
((define sol (solve (assert a)))
|
|
||||||
((3) 0 () 0 () () (c values c (void)))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
(sol
|
|
||||||
((3)
|
|
||||||
1
|
|
||||||
(((lib "rosette/guide/scribble/util/lifted.rkt")
|
|
||||||
.
|
|
||||||
deserialize-info:opaque-v0))
|
|
||||||
0
|
|
||||||
()
|
|
||||||
()
|
|
||||||
(c values c (0 (u . "(model\n [a #t])\n"))))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
((complete-solution sol (list a x))
|
|
||||||
((3)
|
|
||||||
1
|
|
||||||
(((lib "rosette/guide/scribble/util/lifted.rkt")
|
|
||||||
.
|
|
||||||
deserialize-info:opaque-v0))
|
|
||||||
0
|
|
||||||
()
|
|
||||||
()
|
|
||||||
(c values c (0 (u . "(model\n [a #t]\n [x 0])\n"))))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
((complete-solution (solve (assert #f)) (list a x))
|
|
||||||
((3)
|
|
||||||
1
|
|
||||||
(((lib "rosette/guide/scribble/util/lifted.rkt")
|
|
||||||
.
|
|
||||||
deserialize-info:opaque-v0))
|
|
||||||
0
|
|
||||||
()
|
|
||||||
()
|
|
||||||
(c values c (0 (u . "(unsat)\n"))))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
|
|
@ -1,54 +0,0 @@
|
||||||
#lang rosette/safe
|
|
||||||
|
|
||||||
(struct point (x y) #:transparent)
|
|
||||||
(eq? (point 1 2) (point 1 2))
|
|
||||||
(struct pt (x y))
|
|
||||||
(eq? (pt 1 2) (pt 1 2))
|
|
||||||
(struct pnt (x y) #:mutable #:transparent)
|
|
||||||
(eq? (pnt 1 2) (pnt 1 2))
|
|
||||||
|
|
||||||
(define-symbolic b boolean?)
|
|
||||||
(define p (if b (point 1 2) (point 3 4)))
|
|
||||||
(point-x p)
|
|
||||||
(point-y p)
|
|
||||||
(define sol (solve (assert (= (point-x p) 3))))
|
|
||||||
(evaluate p sol)
|
|
||||||
|
|
||||||
(define-generics viewable (view viewable))
|
|
||||||
|
|
||||||
(struct square (side)
|
|
||||||
#:methods gen:viewable
|
|
||||||
[(define (view self) (square-side self))])
|
|
||||||
|
|
||||||
(struct circle (radius)
|
|
||||||
#:transparent
|
|
||||||
#:methods gen:viewable
|
|
||||||
[(define (view self) (circle-radius self))])
|
|
||||||
|
|
||||||
(define q (if b (square 2) (circle 3)))
|
|
||||||
(view q)
|
|
||||||
(define sol2 (solve (assert (= (view q) 3))))
|
|
||||||
(evaluate q sol2)
|
|
||||||
|
|
||||||
#|(define-values (prop:foo foo? foo-value) (make-struct-type-property 'foo))
|
|
||||||
|
|
||||||
(struct point (x y) #:transparent #:property prop:foo 3)
|
|
||||||
|
|
||||||
(define-symbolic b boolean?)
|
|
||||||
(define p (if b (point 1 2) (point 3 4)))
|
|
||||||
(foo? p)
|
|
||||||
(foo-value p)
|
|
||||||
|
|
||||||
(eq? (point 1 2) (point 1 2))
|
|
||||||
|
|
||||||
(evaluate p (solve (assert (= (point-x p) 3))))
|
|
||||||
|
|
||||||
(struct pt (x y))
|
|
||||||
(eq? (pt 1 2) (pt 1 2))
|
|
||||||
|
|
||||||
(struct farm (x)
|
|
||||||
#:methods gen:equal+hash
|
|
||||||
[(define (equal-proc self f rec) (and (rec (farm-x self) (farm-x f))))
|
|
||||||
(define (hash-proc self rec) 1)
|
|
||||||
(define (hash2-proc self rec) 2)])|#
|
|
||||||
|
|
||||||
|
|
@ -1,193 +0,0 @@
|
||||||
;; This file was created by make-log-based-eval
|
|
||||||
((current-bitwidth #f) ((3) 0 () 0 () () (c values c (void))) #"" #"")
|
|
||||||
((define-symbolic f (~> integer? boolean?))
|
|
||||||
((3) 0 () 0 () () (c values c (void)))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
((f 1)
|
|
||||||
((3)
|
|
||||||
1
|
|
||||||
(((lib "rosette/guide/scribble/util/lifted.rkt")
|
|
||||||
.
|
|
||||||
deserialize-info:opaque-v0))
|
|
||||||
0
|
|
||||||
()
|
|
||||||
()
|
|
||||||
(c values c (0 (u . "(app f 1)\n"))))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
((define-symbolic x real?) ((3) 0 () 0 () () (c values c (void))) #"" #"")
|
|
||||||
((f x)
|
|
||||||
((3)
|
|
||||||
1
|
|
||||||
(((lib "rosette/guide/scribble/util/lifted.rkt")
|
|
||||||
.
|
|
||||||
deserialize-info:opaque-v0))
|
|
||||||
0
|
|
||||||
()
|
|
||||||
()
|
|
||||||
(c values c (0 (u . "(app f (real->integer x))\n"))))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
((vc)
|
|
||||||
((3)
|
|
||||||
1
|
|
||||||
(((lib "rosette/guide/scribble/util/lifted.rkt")
|
|
||||||
.
|
|
||||||
deserialize-info:opaque-v0))
|
|
||||||
0
|
|
||||||
()
|
|
||||||
()
|
|
||||||
(c values c (0 (u . "(vc #t (int? x))\n"))))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
((define sol (solve (assert (not (equal? (f x) (f 1))))))
|
|
||||||
((3) 0 () 0 () () (c values c (void)))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
((define g (evaluate f sol)) ((3) 0 () 0 () () (c values c (void))) #"" #"")
|
|
||||||
(g
|
|
||||||
((3)
|
|
||||||
1
|
|
||||||
(((lib "rosette/guide/scribble/util/lifted.rkt")
|
|
||||||
.
|
|
||||||
deserialize-info:opaque-v0))
|
|
||||||
0
|
|
||||||
()
|
|
||||||
()
|
|
||||||
(c values c (0 (u . "(fv integer?~>boolean?)\n"))))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
((evaluate x sol) ((3) 0 () 0 () () (q values 0)) #"" #"")
|
|
||||||
((fv? f) ((3) 0 () 0 () () (q values #t)) #"" #"")
|
|
||||||
((fv? g) ((3) 0 () 0 () () (q values #t)) #"" #"")
|
|
||||||
((g 2) ((3) 0 () 0 () () (q values #t)) #"" #"")
|
|
||||||
((g x)
|
|
||||||
((3)
|
|
||||||
1
|
|
||||||
(((lib "rosette/guide/scribble/util/lifted.rkt")
|
|
||||||
.
|
|
||||||
deserialize-info:opaque-v0))
|
|
||||||
0
|
|
||||||
()
|
|
||||||
()
|
|
||||||
(c values c (0 (u . "(ite (= 1 (real->integer x)) #f #t)\n"))))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
((define t (~> integer? real? boolean? (bitvector 4)))
|
|
||||||
((3) 0 () 0 () () (c values c (void)))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
(t
|
|
||||||
((3)
|
|
||||||
1
|
|
||||||
(((lib "rosette/guide/scribble/util/lifted.rkt")
|
|
||||||
.
|
|
||||||
deserialize-info:opaque-v0))
|
|
||||||
0
|
|
||||||
()
|
|
||||||
()
|
|
||||||
(c values c (0 (u . "integer?~>real?~>boolean?~>(bitvector 4)\n"))))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
((~> t integer?)
|
|
||||||
((3)
|
|
||||||
0
|
|
||||||
()
|
|
||||||
0
|
|
||||||
()
|
|
||||||
()
|
|
||||||
(q
|
|
||||||
exn
|
|
||||||
"function: expected a list of primitive solvable types\n domain: '(integer?~>real?~>boolean?~>(bitvector 4))"))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
((define-symbolic b boolean?) ((3) 0 () 0 () () (c values c (void))) #"" #"")
|
|
||||||
((~> integer? (if b boolean? real?))
|
|
||||||
((3)
|
|
||||||
0
|
|
||||||
()
|
|
||||||
0
|
|
||||||
()
|
|
||||||
()
|
|
||||||
(q
|
|
||||||
exn
|
|
||||||
"function: expected a primitive solvable type\n range: (union [b boolean?] [(! b) real?])"))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
((~> real?)
|
|
||||||
((3)
|
|
||||||
0
|
|
||||||
()
|
|
||||||
0
|
|
||||||
()
|
|
||||||
()
|
|
||||||
(q
|
|
||||||
exn
|
|
||||||
"~>: arity mismatch;\n the expected number of arguments does not match the given number\n expected: at least 2\n given: 1"))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
((define t0? (~> integer? real? boolean? (bitvector 4)))
|
|
||||||
((3) 0 () 0 () () (c values c (void)))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
((define t1? (~> integer? real?))
|
|
||||||
((3) 0 () 0 () () (c values c (void)))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
((function? t0?) ((3) 0 () 0 () () (q values #t)) #"" #"")
|
|
||||||
((function? t1?) ((3) 0 () 0 () () (q values #t)) #"" #"")
|
|
||||||
((define-symbolic b boolean?) ((3) 0 () 0 () () (c values c (void))) #"" #"")
|
|
||||||
((function? (if b t0? t1?)) ((3) 0 () 0 () () (q values #f)) #"" #"")
|
|
||||||
((function? integer?) ((3) 0 () 0 () () (q values #f)) #"" #"")
|
|
||||||
((function? 3) ((3) 0 () 0 () () (q values #f)) #"" #"")
|
|
||||||
((clear-vc!) ((3) 0 () 0 () () (c values c (void))) #"" #"")
|
|
||||||
((define-symbolic f (~> boolean? boolean?))
|
|
||||||
((3) 0 () 0 () () (c values c (void)))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
((fv? f) ((3) 0 () 0 () () (q values #t)) #"" #"")
|
|
||||||
((fv? (lambda (x) x)) ((3) 0 () 0 () () (q values #f)) #"" #"")
|
|
||||||
((define-symbolic b boolean?) ((3) 0 () 0 () () (c values c (void))) #"" #"")
|
|
||||||
((fv? (if b f 1))
|
|
||||||
((3)
|
|
||||||
1
|
|
||||||
(((lib "rosette/guide/scribble/util/lifted.rkt")
|
|
||||||
.
|
|
||||||
deserialize-info:opaque-v0))
|
|
||||||
0
|
|
||||||
()
|
|
||||||
()
|
|
||||||
(c values c (0 (u . "b\n"))))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
((define sol (solve (begin (assert (not (f #t))) (assert (f #f)))))
|
|
||||||
((3) 0 () 0 () () (c values c (void)))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
((define g (evaluate f sol)) ((3) 0 () 0 () () (c values c (void))) #"" #"")
|
|
||||||
(g
|
|
||||||
((3)
|
|
||||||
1
|
|
||||||
(((lib "rosette/guide/scribble/util/lifted.rkt")
|
|
||||||
.
|
|
||||||
deserialize-info:opaque-v0))
|
|
||||||
0
|
|
||||||
()
|
|
||||||
()
|
|
||||||
(c values c (0 (u . "(fv boolean?~>boolean?)\n"))))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
((fv? g) ((3) 0 () 0 () () (q values #t)) #"" #"")
|
|
||||||
((verify (assert (equal? (g b) (not b))))
|
|
||||||
((3)
|
|
||||||
1
|
|
||||||
(((lib "rosette/guide/scribble/util/lifted.rkt")
|
|
||||||
.
|
|
||||||
deserialize-info:opaque-v0))
|
|
||||||
0
|
|
||||||
()
|
|
||||||
()
|
|
||||||
(c values c (0 (u . "(unsat)\n"))))
|
|
||||||
#""
|
|
||||||
#"")
|
|
||||||
|
|
@ -1,109 +0,0 @@
|
||||||
#lang scribble/manual
|
|
||||||
|
|
||||||
@(require (for-label
|
|
||||||
rosette/base/form/define rosette/query/query rosette/solver/solution
|
|
||||||
rosette/base/core/term (only-in rosette/query/finitize current-bitwidth)
|
|
||||||
(only-in rosette/base/core/union union?)
|
|
||||||
(only-in rosette/base/core/function ~> function? fv?)
|
|
||||||
(only-in rosette/base/base bv bitvector assert vc clear-vc!))
|
|
||||||
(for-label racket) racket/runtime-path
|
|
||||||
scribble/core scribble/html-properties scribble/examples racket/sandbox
|
|
||||||
"../util/lifted.rkt")
|
|
||||||
|
|
||||||
@(define-runtime-path root ".")
|
|
||||||
@(define rosette-eval (rosette-log-evaluator (logfile root "uninterpreted-log")))
|
|
||||||
|
|
||||||
@title[#:tag "sec:UF"]{Uninterpreted Functions}
|
|
||||||
|
|
||||||
@declare-exporting[rosette/base/base #:use-sources (rosette/base/core/function
|
|
||||||
rosette/query/finitize
|
|
||||||
rosette/base/base)]
|
|
||||||
|
|
||||||
In Rosette, functions are special kinds of @seclink["sec:proc"]{procedures} that are pure
|
|
||||||
(have no side effects) and total (defined on every input value).
|
|
||||||
A function type is recognized by the @racket[function?] predicate, and all
|
|
||||||
function types are @tech[#:key "solvable type"]{solvable}. The type of a
|
|
||||||
function specifies the function's domain and range, which are given as @racket[solvable?] non-@racket[function?] types. A value of a function type is recognized by
|
|
||||||
the @racket[fv?] (function value) predicate. Because
|
|
||||||
function types are solvable, they can be used in the @seclink["sec:symbolic-constants"]{@code{define-symbolic[*]}} form
|
|
||||||
to introduce a symbolic function constant. These symbolic function constants are
|
|
||||||
technically @deftech[#:key "uninterpreted function"]{uninterpreted functions}---they have
|
|
||||||
no fixed meaning. Their meaning (or interpretation) is determined by the underlying solver
|
|
||||||
as the result of a @seclink["sec:queries"]{solver-aided query}.
|
|
||||||
|
|
||||||
@examples[#:eval rosette-eval
|
|
||||||
(current-bitwidth #f)
|
|
||||||
(code:comment "An uninterpreted function from integers to booleans:")
|
|
||||||
(define-symbolic f (~> integer? boolean?))
|
|
||||||
(code:line (f 1) (code:comment "No built-in interpretation for 1."))
|
|
||||||
(define-symbolic x real?)
|
|
||||||
(code:line (f x) (code:comment "This typechecks when x is an integer,"))
|
|
||||||
(code:line (vc) (code:comment "so Rosette emits the corresponding assertion."))
|
|
||||||
(define sol (solve (assert (not (equal? (f x) (f 1))))))
|
|
||||||
(code:line (define g (evaluate f sol)) (code:comment "An interpretation of f."))
|
|
||||||
g
|
|
||||||
(evaluate x sol)
|
|
||||||
(code:line (fv? f) (code:comment "f is a function value,"))
|
|
||||||
(code:line (fv? g) (code:comment "and so is g."))
|
|
||||||
(code:line (g 2) (code:comment "We can apply g to concrete values"))
|
|
||||||
(code:line (g x) (code:comment "and to symbolic values."))]
|
|
||||||
|
|
||||||
@defproc[(~> [d (and/c solvable? (not/c function?))] ...+
|
|
||||||
[r (and/c solvable? (not/c function?))]) function?]{
|
|
||||||
|
|
||||||
Returns a type predicate for recognizing functions that take as input
|
|
||||||
values of types @racket[d...+] and produce values of type @racket[r].
|
|
||||||
The domain and range arguments must be concrete @racket[solvable?] types that are
|
|
||||||
not themselves functions. Note that @racket[~>] expects at least one domain
|
|
||||||
type to be given, disallowing zero-argument functions.
|
|
||||||
|
|
||||||
@examples[#:eval rosette-eval
|
|
||||||
(define t (~> integer? real? boolean? (bitvector 4)))
|
|
||||||
t
|
|
||||||
(eval:error (~> t integer?))
|
|
||||||
(define-symbolic b boolean?)
|
|
||||||
(eval:error (~> integer? (if b boolean? real?)))
|
|
||||||
(eval:error (~> real?))]
|
|
||||||
}
|
|
||||||
|
|
||||||
@defproc[(function? [v any/c]) boolean?]{
|
|
||||||
|
|
||||||
Returns true if @racket[v] is a concrete type predicate that recognizes function values.
|
|
||||||
|
|
||||||
@examples[#:eval rosette-eval
|
|
||||||
(define t0? (~> integer? real? boolean? (bitvector 4)))
|
|
||||||
(define t1? (~> integer? real?))
|
|
||||||
(function? t0?)
|
|
||||||
(function? t1?)
|
|
||||||
(define-symbolic b boolean?)
|
|
||||||
(code:line (function? (if b t0? t1?)) (code:comment "Not a concrete type."))
|
|
||||||
(code:line (function? integer?) (code:comment "Not a function type."))
|
|
||||||
(code:line (function? 3) (code:comment "Not a type."))]
|
|
||||||
}
|
|
||||||
|
|
||||||
@(rosette-eval '(clear-vc!))
|
|
||||||
|
|
||||||
@defproc[(fv? [v any/c]) boolean?]{
|
|
||||||
|
|
||||||
Returns true if @racket[v] is a concrete or symbolic function value.
|
|
||||||
|
|
||||||
@examples[#:eval rosette-eval
|
|
||||||
(define-symbolic f (~> boolean? boolean?))
|
|
||||||
(fv? f)
|
|
||||||
(fv? (lambda (x) x))
|
|
||||||
(define-symbolic b boolean?)
|
|
||||||
(fv? (if b f 1))
|
|
||||||
(define sol
|
|
||||||
(solve
|
|
||||||
(begin
|
|
||||||
(assert (not (f #t)))
|
|
||||||
(assert (f #f)))))
|
|
||||||
(define g (evaluate f sol))
|
|
||||||
(code:line g (code:comment "g implements logical negation."))
|
|
||||||
(fv? g)
|
|
||||||
(code:comment "Verify that g implements logical negation:")
|
|
||||||
(verify (assert (equal? (g b) (not b))))]
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@(kill-evaluator rosette-eval)
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue