diff options
author | Aria <me@aria.rip> | 2023-10-05 15:04:40 +0100 |
---|---|---|
committer | Aria <me@aria.rip> | 2023-10-05 15:04:40 +0100 |
commit | 1a8fba28d02a8415118e22f6d3816264b3840c95 (patch) | |
tree | 886265fa0f87b4897f356db7c016407dc951c141 /primrose | |
parent | 171f808f49675c7960939c441aa36063d868af80 (diff) |
add original primrose source
Diffstat (limited to 'primrose')
103 files changed, 6319 insertions, 0 deletions
diff --git a/primrose/.gitignore b/primrose/.gitignore new file mode 100644 index 0000000..aeb024c --- /dev/null +++ b/primrose/.gitignore @@ -0,0 +1,21 @@ +# Generated by Cargo +# will have compiled files and executables +/target/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk + +/proptest-regressions/* + +# Racket +compiled/ + +# Rust parser code gen testing dir. +/gen_code/ +/racket_specs/gen_lib_spec/ +/racket_specs/gen_prop_spec/ +/racket_specs/gen_match/
\ No newline at end of file diff --git a/primrose/Cargo.toml b/primrose/Cargo.toml new file mode 100644 index 0000000..21b2ef6 --- /dev/null +++ b/primrose/Cargo.toml @@ -0,0 +1,78 @@ +[package] +name = "primrose" +version = "0.1.0" +authors = ["Xueying Qin <qinxy1995@gmail.com>"] +edition = "2018" +default-run = "main" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +peg = "0.8.0" +indicatif = "0.16.2" +rand = "0.8.5" +im = "10.2.0" +proptest = "1.0.0" + +[dev-dependencies] +criterion = "0.3.5" + +[[bench]] +name = "criterion_benchmark" +harness = false + +[[bin]] +name = "main" +path = "src/main.rs" + +[[bin]] +name = "btreeset1" +path = "mem_profiling/btreeset_1m.rs" + +[[bin]] +name = "btreeset128" +path = "mem_profiling/btreeset_128m.rs" + +[[bin]] +name = "btreeset256" +path = "mem_profiling/btreeset_256m.rs" + +[[bin]] +name = "btreeset512" +path = "mem_profiling/btreeset_512m.rs" + +[[bin]] +name = "hashset1" +path = "mem_profiling/hashset_1m.rs" + +[[bin]] +name = "hashset128" +path = "mem_profiling/hashset_128m.rs" + +[[bin]] +name = "hashset256" +path = "mem_profiling/hashset_256m.rs" + +[[bin]] +name = "hashset512" +path = "mem_profiling/hashset_512m.rs" + +[[bin]] +name = "uveclazy1" +path = "mem_profiling/uvec_lazy_1m.rs" + +[[bin]] +name = "uveclazy128" +path = "mem_profiling/uvec_lazy_128m.rs" + +[[bin]] +name = "uveclazy256" +path = "mem_profiling/uvec_lazy_256m.rs" + +[[bin]] +name = "uveclazy512" +path = "mem_profiling/uvec_lazy_512m.rs" + +#[[bin]] +#name = "unique" +#path = "gen_code/unique/unique0.rs"
\ No newline at end of file diff --git a/primrose/README.md b/primrose/README.md new file mode 100644 index 0000000..604468b --- /dev/null +++ b/primrose/README.md @@ -0,0 +1,143 @@ +# Getting Start + +This is the research artifact for the paper *"Primrose: Selecting Container Data Types by their Properties"*. + +For easy of evaluation, the artifact is provided as a VirtualBox virtual machine with all dependencies already pre-installed. +Below, we provide a guide on how to check that the claims made in the paper are supported by the artifact. +We encourage the evaluators to experiment themselves with the provided examples. + + +## Logging into the VM and locating the artifact +- user name: `user` +- password: `helloworld` +- artifact location: `/home/user/Documents/PrimroseAE/` + +# Overview of the artifact +This artifact supports the claims made in the paper by containing: +- The property specifications introduced in section 4 of the paper. +- An implementation of a parser and type checker of the property specifications accompanied by a type-inference algorithm. +- An implementation of the container library studied in section 5 of the paper. +- Library specifications for each container discussed in section 5. +- An implementation of the selection process described in section 6 of the paper. +- An implementation of the code generation described in section 6.4 of the paper. +- Property based testing as described in the section 7.1 of the paper. +- A script for measuring the solver times reported in section 7.2 of the paper. We do expect that runtimes measured in the virtual machine might differ from the numbers reported in the paper that have been measured outside the virtual machine. + +# Step-by-Step Instructions +## Walkthrough code related to features introduced in the paper +- The *syntactic properties* introduced in section 4.1 can be found in `./src/traits/mod.rs` +- The *semantic properties* introduced in section 4.2, can be found in: + - the `unique` property specification can be found in `./spec_code/example_unique.rs` + - the `ascending` property specification as well as the composition of `ascending` and `unique` can be found in `./spec_code/example_comp.rs`. +- The property specification of the stack example introduced in section 4.3 can be found in `./spec_code/example_stack.rs` +- The combinators used in property specifications are provided in `./racket_specs/combinators.rkt` +- All library specifications introduced in section 5 can be found in `./src/library/` +- All property based tests introduced in section 7 can also be found in `./src/library/` + +## Selecting of valid container implementations and Rust code generation via the `primrose` tool +- To run the *unique container example* from the paper: + - Make sure you are in the directory `PrimroseAE` + - Run command: `cargo run example_unique.rs unique 3` + - Generated code can be found in the directory `./gen_code/unique/` + - For this example we expect that four files each with a different container implementation are generated: + - `./gen_code/unique/unique0.rs` + - `./gen_code/unique/unique1.rs` + - `./gen_code/unique/unique2.rs` + - `./gen_code/unique/unique3.rs` + - To compile the generated Rust code, go to `Cargo.toml`, add at the end for the code you want to compile: + ``` + [[bin]] + name = "unique0" + path = "gen_code/unique/unique0.rs" + + [[bin]] + name = "unique1" + path = "gen_code/unique/unique1.rs" + + [[bin]] + name = "unique2" + path = "gen_code/unique/unique2.rs" + + [[bin]] + name = "unique2" + path = "gen_code/unique/unique3.rs" + ``` + - Then you can compile and execute the generated file with: + - `cargo run --bin unique0` + - `cargo run --bin unique1` + - `cargo run --bin unique2` + - `cargo run --bin unique3` + +- To run the *unique and ascending (strictly ascending) container example* from the paper: + - Make sure you are in the directory `PrimroseAE` + - Run command: `cargo run example_comp.rs comp 3` + - Generated code can be found under the directory `./gen_code/comp/` + - To compile the generated Rust code, add it to `Cargo.toml` as above and then execute it via `cargo run` +- To run the *stack example* from the paper: + - Make sure you are in the directory `PrimroseAE` + - Run command: `cargo run example_stack.rs stack 3` + - Generated code can be found under the directory `./gen_code/stack/` + - To compile the generated Rust code, add it to `Cargo.toml` as above and then execute it via `cargo run` + +## Running property based testing from section 7.1 +- Make sure you are in the directory `PrimroseAE` +- Run command: `cargo test` +- if you want to measure how long it takes to execute all tests: `./timetests.sh` + +## Producing solver efficiency benchmarks form section 7.2 +- Make sure you are in the directory `PrimroseAE` +- Run command: `./timeall.sh` +- **Please note:** we do not expect that the times measured inside the virtual machine and on different hardware will be exactly the same as the times presented in the paper. + +# Technical Overview of the Artifact + +## Overview of pre-installed dependencies +- Rust 1.67.0-nightly +- Racket 8.1 or later +- Rosette + - We used the Z3 backend in this project +- These dependencies are all pre-installed, to check they are installed correctly: + - Type command: `rustc --version` in terminal, you should get: + ``` + rustc 1.67.0-nightly (01fbc6ae7 2022-12-07) + ``` + - Type command: `racket --version` in terminal, you should get: + ``` + Welcome to Racket v8.6 [cs]. + +## Execution of the `primrose` tool with arbitrary property specification +- Make sure you are in the `PrimroseAE` directory +- Make sure the Rust program with embedded property specifications (`input.rs`) is provided in the directory `./spec_code/` +- Run the tool with command: + ``` + cargo run [input.rs] [output_dir_name] [model_size] + ``` +- For most properties, we recommend a model size of `3` +- The generated file will appear in the directory `[output_dir_name]` and can be compiled with `cargo` after an entry for it has been added at the end of `Cargo.toml` file: + ``` + [[bin]] + name = "name_you_like" + path = "path/to/file.rs" + ``` +- To execute the generated Rust code run: + ``` + cargo run --bin name_you_like + ``` + +## File structure of the `PrimroseAE` directory +- `./benches/`: containing the code producing the runtime performance benchmarks in section 2 +- `./gen_code/`: containing generated code with selected container implementations +- `./mem_profiling/`: containing scripts producing the memory consumption benchmarks in section 2 +- `./racket_specs/`: containing scripts for setting up and executing the selection process and generated code during the selection process +- `./scripts/`: containing the scripts for measuring the solver's selection time introduced in section 7.2 +- `./spec_code/`: containing source code with property specifications introduced in section 4. +- `./src/library/`: containing container implementations used in this paper, library specifications introduced in section 5 and property based tests introduced in section 7.1 +- `./src/proptest/`: containing code for setting up property based tests. +- `./src/tools/`: containing the code for generating dataset memory profiling. +- `./src/traits/`: container syntactic properties introduced in section 4. +- `./src/main.rs`: the entry point for executing the tool. +- All other files in the `./src/` directory are the detailed implementation of Primrose, including parsing, type checking and analyzing property specifications, extracting and processing library specifications, executing the selection and generating code. +- `./runall.sh` is the script for executing all examples +- `./timeall.sh` is the script running every script located in `./scripts/` measuring the solver's selection time introduced in section 7.2 +- `./timetests.sh` is the script measuring the time for running all property based tests reported in section 7.1 +- `./Cargo.toml`, `./Cargo.lock` and `rust-toolchain.toml` are Rust package and compiler configuration files. diff --git a/primrose/benches/criterion_benchmark.rs b/primrose/benches/criterion_benchmark.rs new file mode 100644 index 0000000..cb4c649 --- /dev/null +++ b/primrose/benches/criterion_benchmark.rs @@ -0,0 +1,220 @@ +use criterion::{black_box, criterion_group, criterion_main, Criterion}; +use std::time::Duration; +use std::mem::size_of; +use std::collections::{BTreeSet, HashSet}; +use std::vec::Vec; +use preprocess::traits::{Container}; +use preprocess::library::eager_unique_vector::{EagerUniqueVec}; +use preprocess::library::lazy_unique_vector::{LazyUniqueVec}; +use rand::{Rng,SeedableRng}; +use rand::rngs::StdRng; +use rand::seq::SliceRandom; +use rand::thread_rng; + +// one search at the +fn gen_dataset_1() -> Vec<u32> { // avoid duplication + let size = 1024 * 1024; // 1 MB + let amount = size / size_of::<u32>(); + let mut data: Vec<u32> = (1..amount as u32).collect(); //ensure no duplication + let mut rng = StdRng::seed_from_u64(222); + data.shuffle(&mut rng); + data +} + +fn gen_dataset_128() -> Vec<u32> { // avoid duplication + let size = 128 * 1024 * 1024; // 128 MB + let amount = size / size_of::<u32>(); + let mut data: Vec<u32> = (1..amount as u32).collect(); //ensure no duplication + let mut rng = StdRng::seed_from_u64(222); + data.shuffle(&mut rng); + data +} + +fn gen_dataset_256() -> Vec<u32> { // avoid duplication + let size = 256 * 1024 * 1024; // 256 MB + let amount = size / size_of::<u32>(); + let mut data: Vec<u32> = (1..amount as u32).collect(); //ensure no duplication + let mut rng = StdRng::seed_from_u64(222); + data.shuffle(&mut rng); + data +} + +fn gen_dataset_512() -> Vec<u32> { // avoid duplication + let size = 512 * 1024 * 1024; // 512 MB + let amount = size / size_of::<u32>(); + let mut data: Vec<u32> = (1..amount as u32).collect(); //ensure no duplication + let mut rng = StdRng::seed_from_u64(222); + data.shuffle(&mut rng); + data +} + +fn btreeset_insertion_1m(c: &mut Criterion) { + let s: &mut dyn Container<u32> = &mut BTreeSet::new(); + let data = gen_dataset_1(); + c.bench_function("btreeset insertion 1MB", |b| b.iter(|| { + for val in data.iter() { + s.insert(*val); + } + s.contains(&1024); + } + )); +} + +fn btreeset_insertion_128m(c: &mut Criterion) { + let s: &mut dyn Container<u32> = &mut BTreeSet::new(); + let data = gen_dataset_128(); + c.bench_function("btreeset insertion 128MB", |b| b.iter(|| { + for val in data.iter() { + s.insert(*val); + } + s.contains(&1024); + } + )); +} + +fn btreeset_insertion_256m(c: &mut Criterion) { + let s: &mut dyn Container<u32> = &mut BTreeSet::new(); + let data = gen_dataset_256(); + c.bench_function("btreeset insertion 256MB", |b| b.iter(|| { + for val in data.iter() { + s.insert(*val); + } + s.contains(&1024); + } + )); +} + +fn btreeset_insertion_512m(c: &mut Criterion) { + let s: &mut dyn Container<u32> = &mut BTreeSet::new(); + let data = gen_dataset_512(); + c.bench_function("btreeset insertion 512MB", |b| b.iter(|| { + for val in data.iter() { + s.insert(*val); + } + s.contains(&1024); + } + )); +} + +fn hashset_insertion_1m(c: &mut Criterion) { + let s: &mut dyn Container<u32> = &mut HashSet::new(); + let data = gen_dataset_1(); + c.bench_function("hashset insertion 1MB", |b| b.iter(|| { + for val in data.iter() { + s.insert(*val); + } + s.contains(&1024); + } + )); +} + +fn hashset_insertion_128m(c: &mut Criterion) { + let s: &mut dyn Container<u32> = &mut HashSet::new(); + let data = gen_dataset_128(); + c.bench_function("hashset insertion 128MB", |b| b.iter(|| { + for val in data.iter() { + s.insert(*val); + } + s.contains(&1024); + } + )); +} + +fn hashset_insertion_256m(c: &mut Criterion) { + let s: &mut dyn Container<u32> = &mut HashSet::new(); + let data = gen_dataset_256(); + c.bench_function("hashset insertion 256MB", |b| b.iter(|| { + for val in data.iter() { + s.insert(*val); + } + s.contains(&1024); + } + )); +} + +fn hashset_insertion_512m(c: &mut Criterion) { + let s: &mut dyn Container<u32> = &mut HashSet::new(); + let data = gen_dataset_512(); + c.bench_function("hashset insertion 512MB", |b| b.iter(|| { + for val in data.iter() { + s.insert(*val); + } + s.contains(&1024); + } + )); +} + +fn lazy_uniuqe_vec_insertion_1m(c: &mut Criterion) { + let s: &mut dyn Container<u32> = &mut LazyUniqueVec::new(); + let data = gen_dataset_1(); + c.bench_function("lazy unique vector insertion 1MB", |b| b.iter(|| { + for val in data.iter() { + s.insert(*val); + } + s.contains(&1024); + } + )); +} + +fn lazy_uniuqe_vec_insertion_128m(c: &mut Criterion) { + let s: &mut dyn Container<u32> = &mut LazyUniqueVec::new(); + let data = gen_dataset_128(); + c.bench_function("lazy unique vector insertion 128MB", |b| b.iter(|| { + for val in data.iter() { + s.insert(*val); + } + s.contains(&1024); + } + )); +} + +fn lazy_uniuqe_vec_insertion_256m(c: &mut Criterion) { + let s: &mut dyn Container<u32> = &mut LazyUniqueVec::new(); + let data = gen_dataset_256(); + c.bench_function("lazy unique vector insertion 256MB", |b| b.iter(|| { + for val in data.iter() { + s.insert(*val); + } + s.contains(&1024); + } + )); +} + +fn lazy_uniuqe_vec_insertion_512m(c: &mut Criterion) { + let s: &mut dyn Container<u32> = &mut LazyUniqueVec::new(); + let data = gen_dataset_512(); + c.bench_function("lazy unique vector insertion 512MB", |b| b.iter(|| { + for val in data.iter() { + s.insert(*val); + } + s.contains(&1024); + } + )); +} + + +criterion_group! { + name = insertion_1m; + config = Criterion::default().sample_size(10); + targets = btreeset_insertion_1m, hashset_insertion_1m, lazy_uniuqe_vec_insertion_1m +} + +criterion_group! { + name = insertion_128m; + config = Criterion::default().sample_size(10); + targets = btreeset_insertion_128m, hashset_insertion_128m, lazy_uniuqe_vec_insertion_128m +} + +criterion_group! { + name = insertion_256m; + config = Criterion::default().sample_size(10); + targets = btreeset_insertion_256m, hashset_insertion_256m, lazy_uniuqe_vec_insertion_256m +} + +criterion_group! { + name = insertion_512m; + config = Criterion::default().sample_size(10); + targets = btreeset_insertion_512m, hashset_insertion_512m, lazy_uniuqe_vec_insertion_512m +} + +criterion_main!(insertion_1m, insertion_128m, insertion_256m, insertion_512m); diff --git a/primrose/mem_profiling/btreeset_128m.rs b/primrose/mem_profiling/btreeset_128m.rs new file mode 100644 index 0000000..539d658 --- /dev/null +++ b/primrose/mem_profiling/btreeset_128m.rs @@ -0,0 +1,17 @@ +use std::collections::{BTreeSet}; +use primrose::traits::Container; +use primrose::tools::gen_dataset_128; + + +fn btreeset_insertion_128m() { + let s: &mut dyn Container<u32> = &mut BTreeSet::new(); + let data = gen_dataset_128(); + for val in data.iter() { + s.insert(*val); + } + println!("Contains 1024? {}", s.contains(&1024)); +} + +fn main() { + btreeset_insertion_128m(); +}
\ No newline at end of file diff --git a/primrose/mem_profiling/btreeset_1m.rs b/primrose/mem_profiling/btreeset_1m.rs new file mode 100644 index 0000000..f691dfb --- /dev/null +++ b/primrose/mem_profiling/btreeset_1m.rs @@ -0,0 +1,17 @@ +use std::collections::{BTreeSet}; +use primrose::traits::Container; +use primrose::tools::gen_dataset_1; + + +fn btreeset_insertion_1m() { + let s: &mut dyn Container<u32> = &mut BTreeSet::new(); + let data = gen_dataset_1(); + for val in data.iter() { + s.insert(*val); + } + println!("Contains 1024? {}", s.contains(&1024)); +} + +fn main() { + btreeset_insertion_1m(); +}
\ No newline at end of file diff --git a/primrose/mem_profiling/btreeset_256m.rs b/primrose/mem_profiling/btreeset_256m.rs new file mode 100644 index 0000000..2457507 --- /dev/null +++ b/primrose/mem_profiling/btreeset_256m.rs @@ -0,0 +1,17 @@ +use std::collections::{BTreeSet}; +use primrose::traits::Container; +use primrose::tools::gen_dataset_256; + + +fn btreeset_insertion_256m() { + let s: &mut dyn Container<u32> = &mut BTreeSet::new(); + let data = gen_dataset_256(); + for val in data.iter() { + s.insert(*val); + } + println!("Contains 1024? {}", s.contains(&1024)); +} + +fn main() { + btreeset_insertion_256m(); +}
\ No newline at end of file diff --git a/primrose/mem_profiling/btreeset_512m.rs b/primrose/mem_profiling/btreeset_512m.rs new file mode 100644 index 0000000..e0a1ffb --- /dev/null +++ b/primrose/mem_profiling/btreeset_512m.rs @@ -0,0 +1,17 @@ +use std::collections::{BTreeSet}; +use primrose::traits::Container; +use primrose::tools::gen_dataset_512; + + +fn btreeset_insertion_512m() { + let s: &mut dyn Container<u32> = &mut BTreeSet::new(); + let data = gen_dataset_512(); + for val in data.iter() { + s.insert(*val); + } + println!("Contains 1024? {}", s.contains(&1024)); +} + +fn main() { + btreeset_insertion_512m(); +}
\ No newline at end of file diff --git a/primrose/mem_profiling/hashset_128m.rs b/primrose/mem_profiling/hashset_128m.rs new file mode 100644 index 0000000..7a64ef4 --- /dev/null +++ b/primrose/mem_profiling/hashset_128m.rs @@ -0,0 +1,17 @@ +use std::collections::{HashSet}; +use primrose::traits::Container; +use primrose::tools::gen_dataset_128; + + +fn hashset_insertion_128m() { + let s: &mut dyn Container<u32> = &mut HashSet::new(); + let data = gen_dataset_128(); + for val in data.iter() { + s.insert(*val); + } + println!("Contains 1024? {}", s.contains(&1024)); +} + +fn main() { + hashset_insertion_128m(); +}
\ No newline at end of file diff --git a/primrose/mem_profiling/hashset_1m.rs b/primrose/mem_profiling/hashset_1m.rs new file mode 100644 index 0000000..fdec49a --- /dev/null +++ b/primrose/mem_profiling/hashset_1m.rs @@ -0,0 +1,17 @@ +use std::collections::{HashSet}; +use primrose::traits::Container; +use primrose::tools::gen_dataset_1; + + +fn hashset_insertion_1m() { + let s: &mut dyn Container<u32> = &mut HashSet::new(); + let data = gen_dataset_1(); + for val in data.iter() { + s.insert(*val); + } + println!("Contains 1024? {}", s.contains(&1024)); +} + +fn main() { + hashset_insertion_1m(); +}
\ No newline at end of file diff --git a/primrose/mem_profiling/hashset_256m.rs b/primrose/mem_profiling/hashset_256m.rs new file mode 100644 index 0000000..813b4bf --- /dev/null +++ b/primrose/mem_profiling/hashset_256m.rs @@ -0,0 +1,17 @@ +use std::collections::{HashSet}; +use primrose::traits::Container; +use primrose::tools::gen_dataset_256; + + +fn hashset_insertion_256m() { + let s: &mut dyn Container<u32> = &mut HashSet::new(); + let data = gen_dataset_256(); + for val in data.iter() { + s.insert(*val); + } + println!("Contains 1024? {}", s.contains(&1024)); +} + +fn main() { + hashset_insertion_256m(); +}
\ No newline at end of file diff --git a/primrose/mem_profiling/hashset_512m.rs b/primrose/mem_profiling/hashset_512m.rs new file mode 100644 index 0000000..475f7ca --- /dev/null +++ b/primrose/mem_profiling/hashset_512m.rs @@ -0,0 +1,17 @@ +use std::collections::{HashSet}; +use primrose::traits::Container; +use primrose::tools::gen_dataset_512; + + +fn hashset_insertion_512m() { + let s: &mut dyn Container<u32> = &mut HashSet::new(); + let data = gen_dataset_512(); + for val in data.iter() { + s.insert(*val); + } + println!("Contains 1024? {}", s.contains(&1024)); +} + +fn main() { + hashset_insertion_512m(); +}
\ No newline at end of file diff --git a/primrose/mem_profiling/uvec_lazy_128m.rs b/primrose/mem_profiling/uvec_lazy_128m.rs new file mode 100644 index 0000000..47837b0 --- /dev/null +++ b/primrose/mem_profiling/uvec_lazy_128m.rs @@ -0,0 +1,16 @@ +use primrose::library::lazy_unique_vector::{LazyUniqueVec}; +use primrose::traits::Container; +use primrose::tools::gen_dataset_128; + +fn vec_insertion_128m() { + let s: &mut dyn Container<u32> = &mut LazyUniqueVec::new(); + let data = gen_dataset_128(); + for val in data.iter() { + s.insert(*val); + } + println!("Contains 1024? {}", s.contains(&1024)); +} + +fn main() { + vec_insertion_128m(); +}
\ No newline at end of file diff --git a/primrose/mem_profiling/uvec_lazy_1m.rs b/primrose/mem_profiling/uvec_lazy_1m.rs new file mode 100644 index 0000000..e9d7efe --- /dev/null +++ b/primrose/mem_profiling/uvec_lazy_1m.rs @@ -0,0 +1,16 @@ +use primrose::library::lazy_unique_vector::{LazyUniqueVec}; +use primrose::traits::Container; +use primrose::tools::gen_dataset_1; + +fn vec_insertion_1m() { + let s: &mut dyn Container<u32> = &mut LazyUniqueVec::new(); + let data = gen_dataset_1(); + for val in data.iter() { + s.insert(*val); + } + println!("Contains 1024? {}", s.contains(&1024)); +} + +fn main() { + vec_insertion_1m(); +}
\ No newline at end of file diff --git a/primrose/mem_profiling/uvec_lazy_256m.rs b/primrose/mem_profiling/uvec_lazy_256m.rs new file mode 100644 index 0000000..8a6e47d --- /dev/null +++ b/primrose/mem_profiling/uvec_lazy_256m.rs @@ -0,0 +1,16 @@ +use primrose::library::lazy_unique_vector::{LazyUniqueVec}; +use primrose::traits::Container; +use primrose::tools::gen_dataset_256; + +fn vec_insertion_256m() { + let s: &mut dyn Container<u32> = &mut LazyUniqueVec::new(); + let data = gen_dataset_256(); + for val in data.iter() { + s.insert(*val); + } + println!("Contains 1024? {}", s.contains(&1024)); +} + +fn main() { + vec_insertion_256m(); +}
\ No newline at end of file diff --git a/primrose/mem_profiling/uvec_lazy_512m.rs b/primrose/mem_profiling/uvec_lazy_512m.rs new file mode 100644 index 0000000..4cbed46 --- /dev/null +++ b/primrose/mem_profiling/uvec_lazy_512m.rs @@ -0,0 +1,16 @@ +use primrose::library::lazy_unique_vector::{LazyUniqueVec}; +use primrose::traits::Container; +use primrose::tools::gen_dataset_512; + +fn vec_insertion_512m() { + let s: &mut dyn Container<u32> = &mut LazyUniqueVec::new(); + let data = gen_dataset_512(); + for val in data.iter() { + s.insert(*val); + } + println!("Contains 1024? {}", s.contains(&1024)); +} + +fn main() { + vec_insertion_512m(); +}
\ No newline at end of file diff --git a/primrose/mem_profiling/vec_baseline_128m.rs b/primrose/mem_profiling/vec_baseline_128m.rs new file mode 100644 index 0000000..a98f00d --- /dev/null +++ b/primrose/mem_profiling/vec_baseline_128m.rs @@ -0,0 +1,14 @@ +use primrose::tools::gen_dataset_128; + +fn vec_128m() { + let s = &mut Vec::new(); + let data = gen_dataset_128(); + for val in data.iter() { + s.push(*val); + } + println!("Contains 1024? {}", s.contains(&1024)); +} + +fn main() { + vec_128m(); +}
\ No newline at end of file diff --git a/primrose/mem_profiling/vec_baseline_1m.rs b/primrose/mem_profiling/vec_baseline_1m.rs new file mode 100644 index 0000000..825ac9e --- /dev/null +++ b/primrose/mem_profiling/vec_baseline_1m.rs @@ -0,0 +1,14 @@ +use primrose::tools::gen_dataset_1; + +fn vec_1m() { + let s = &mut Vec::new(); + let data = gen_dataset_1(); + for val in data.iter() { + s.push(*val); + } + println!("Contains 1024? {}", s.contains(&1024)); +} + +fn main() { + vec_1m(); +}
\ No newline at end of file diff --git a/primrose/mem_profiling/vec_baseline_256m.rs b/primrose/mem_profiling/vec_baseline_256m.rs new file mode 100644 index 0000000..53a57e2 --- /dev/null +++ b/primrose/mem_profiling/vec_baseline_256m.rs @@ -0,0 +1,14 @@ +use primrose::tools::gen_dataset_256; + +fn vec_256m() { + let s = &mut Vec::new(); + let data = gen_dataset_256(); + for val in data.iter() { + s.push(*val); + } + println!("Contains 1024? {}", s.contains(&1024)); +} + +fn main() { + vec_256m(); +}
\ No newline at end of file diff --git a/primrose/mem_profiling/vec_baseline_512m.rs b/primrose/mem_profiling/vec_baseline_512m.rs new file mode 100644 index 0000000..d20260b --- /dev/null +++ b/primrose/mem_profiling/vec_baseline_512m.rs @@ -0,0 +1,14 @@ +use primrose::tools::gen_dataset_512; + +fn vec_512m() { + let s = &mut Vec::new(); + let data = gen_dataset_512(); + for val in data.iter() { + s.push(*val); + } + println!("Contains 1024? {}", s.contains(&1024)); +} + +fn main() { + vec_512m(); +}
\ No newline at end of file diff --git a/primrose/racket_specs/combinators.rkt b/primrose/racket_specs/combinators.rkt new file mode 100644 index 0000000..a74296b --- /dev/null +++ b/primrose/racket_specs/combinators.rkt @@ -0,0 +1,78 @@ +#lang rosette +; Combinators +; list -> boolean +; we choose the list as our model for specifications +; The binary combinators +(define (for-all-unique-pairs l fn) + (foldl elem-and #t + (flatten + (map (lambda (a) + (map (lambda (b) (fn a b)) (remove a l))) l)))) + +(define (for-all-consecutive-pairs l fn) + (foldl elem-and #t + (map (lambda (p) (fn (first p) (second p))) (consecutive-pairs l)))) + +; The unary combinator +(define (for-all-elems l fn) + (foldl elem-and #t + (map (lambda (a) (fn a)) l))) + +; Helpers +; (elem-and a b) -> boolean +; Since the and operator in Racket is a syntax instead of a procedure, +; we need to create an and procedure which can be used as a parameter +; of a procedure +(define (elem-and a b) (and a b)) + +; (not-equal? a b) -> boolean? +(define (not-equal? a b) (not (equal? a b))) + +; (leq? a b) -> boolean? +(define (leq? . args) + (cond [(andmap string? args) (apply string<=? args)] + [(andmap char? args) (apply char<=? args)] + [else (apply <= args)])) + +; (geq? a b) -> boolean? +(define (geq? . args) + (cond [(andmap string? args) (apply string>=? args)] + [(andmap char? args) (apply char>=? args)] + [else (apply >= args)])) + +; (contains? elem lst) -> boolean? +(define (contains x l) + (cond + [(list? (member x l)) #t] + [else #f])) + +; (unique-count? elem lst) -> boolean +; Checking if the occurance of the elem in the lst is exactly once +(define (unique-count? x l) + (= 1 (count (lambda (y) (= x y)) l))) + +; (once? elem lst) -> boolean? +; The equalvent version of unique-count?, +; created to compare the performance of the solver +(define (once? x l) + (cond + [(empty? l) #f] + [else (or (and (= x (first l)) (not (contains x (rest l)))) + (and (not (= x (first l))) (once? x (rest l))))])) + +; (consecutive-pairs lst) -> list? +; Obtaining all consecutive pairs of elements of a given list +; Examples: +; > (consecutive-pairs '(1 2 3)) +; '((1 2) (2 3)) +; > (consecutive-pairs '(1)) +; '() +; > (consecutive-pairs null) +; '() +(define (consecutive-pairs l) + (cond + [(< (length l) 2) null] + [else (append (list (take l 2)) (consecutive-pairs (drop l 1)))])) + +; Export procedures +(provide for-all-unique-pairs for-all-consecutive-pairs for-all-elems elem-and not-equal? leq? geq? unique-count?)
\ No newline at end of file diff --git a/primrose/racket_specs/container-setup.rkt b/primrose/racket_specs/container-setup.rkt new file mode 100644 index 0000000..0d5cd79 --- /dev/null +++ b/primrose/racket_specs/container-setup.rkt @@ -0,0 +1,55 @@ +#lang rosette + +(define (check-spec-len prop pre spec xs) + (assume (and (prop xs) (pre xs))) + (assert (prop (car (spec xs))))) + +(define (check-spec-is-empty prop pre spec xs) + (assume (and (prop xs) (pre xs))) + (assert (prop (car (spec xs))))) + +(define (check-spec-first prop pre spec xs) + (assume (and (prop xs) (pre xs))) + (assert (prop (car (spec xs))))) + +(define (check-spec-last prop pre spec xs) + (assume (and (prop xs) (pre xs))) + (assert (prop (car (spec xs))))) + +(define (check-spec-contains prop pre spec xs x) + (assume (and (prop xs) (pre xs))) + (assert (prop (car (spec xs x))))) + +(define (check-spec-insert prop pre spec xs x) + (assume (and (prop xs) (pre xs))) + (assert (prop (spec xs x)))) + +(define (check-spec-remove prop pre spec xs x) + (assume (and (prop xs) (pre xs))) + (assert (prop (car (spec xs x))))) + +(define (check-spec-clear prop pre spec xs) + (assume (and (prop xs) (pre xs))) + (assert (prop (spec xs)))) + +(define (check-not-contradict prop pre xs) + (assert (and (prop xs) (pre xs) (> (length xs) 1)))) + +(define (check prop pres specs xs x) + (cond + [(or (unsat? (solve (check-not-contradict prop (first pres) xs))) + (unsat? (solve (check-not-contradict prop (second pres) xs))) + (unsat? (solve (check-not-contradict prop (third pres) xs))) + (unsat? (solve (check-not-contradict prop (fourth pres) xs))) + (unsat? (solve (check-not-contradict prop (fifth pres) xs))) + (unsat? (solve (check-not-contradict prop (sixth pres) xs))) + ) #f] + [else (and (unsat? (verify (check-spec-clear prop (first pres) (first specs) xs))) + (unsat? (verify (check-spec-contains prop (second pres) (second specs) xs x))) + (unsat? (verify (check-spec-insert prop (third pres) (third specs) xs x))) + (unsat? (verify (check-spec-is-empty prop (fourth pres) (fourth specs) xs))) + (unsat? (verify (check-spec-len prop (fifth pres) (fifth specs) xs))) + (unsat? (verify (check-spec-remove prop (sixth pres) (sixth specs) xs x))) + )])) + +(provide check)
\ No newline at end of file diff --git a/primrose/racket_specs/indexable-setup.rkt b/primrose/racket_specs/indexable-setup.rkt new file mode 100644 index 0000000..ddd6239 --- /dev/null +++ b/primrose/racket_specs/indexable-setup.rkt @@ -0,0 +1,31 @@ +#lang rosette + +(define (check-not-contradict prop pre xs) + (assert (and (prop xs) (pre xs) (> (length xs) 1)))) + +(define (check-spec-first prop pre spec xs) + (assume (and (prop xs) (pre xs))) + (assert (prop (car (spec xs))))) + +(define (check-spec-last prop pre spec xs) + (assume (and (prop xs) (pre xs))) + (assert (prop (car (spec xs))))) + + +(define (check-spec-nth prop pre spec xs n) + (assume (and (prop xs) (pre xs))) + (assert (prop (car (spec xs n))))) + + +(define (check prop pres specs xs n) + (cond + [(or (unsat? (solve (check-not-contradict prop (first pres) xs))) + (unsat? (solve (check-not-contradict prop (second pres) xs))) + (unsat? (solve (check-not-contradict prop (third pres) xs))) + ) #f] + [else (and (unsat? (verify (check-spec-first prop (first pres) (first specs) xs))) + (unsat? (verify (check-spec-last prop (second pres) (second specs) xs))) + (unsat? (verify (check-spec-nth prop (third pres) (third specs) xs n))) + )])) + +(provide check)
\ No newline at end of file diff --git a/primrose/racket_specs/stack-setup.rkt b/primrose/racket_specs/stack-setup.rkt new file mode 100644 index 0000000..336cf8d --- /dev/null +++ b/primrose/racket_specs/stack-setup.rkt @@ -0,0 +1,10 @@ +#lang rosette + +(define (check-push-pop prop pres xs) + (assume (and ((first pres) xs) ((second pres) xs))) + (assert (prop xs))) + +(define (check prop pres specs xs x) + (unsat? (verify (check-push-pop prop pres xs)))) + +(provide check)
\ No newline at end of file diff --git a/primrose/runall.sh b/primrose/runall.sh new file mode 100755 index 0000000..e2dfe9a --- /dev/null +++ b/primrose/runall.sh @@ -0,0 +1,9 @@ +./scripts/b_unique_con_3.sh +./scripts/b_asc_con_3.sh +./scripts/b_des_con_3.sh +./scripts/b_unique_asc_con_3.sh +./scripts/b_unique_con_ra_3.sh +./scripts/b_asc_con_ra_3.sh +./scripts/b_des_con_ra_3.sh +./scripts/b_unique_asc_con_ra_3.sh +./scripts/b_stack_con_3.sh
\ No newline at end of file diff --git a/primrose/rust-toolchain.toml b/primrose/rust-toolchain.toml new file mode 100644 index 0000000..271800c --- /dev/null +++ b/primrose/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "nightly"
\ No newline at end of file diff --git a/primrose/scripts/b_asc_con_3.sh b/primrose/scripts/b_asc_con_3.sh new file mode 100755 index 0000000..3befd9d --- /dev/null +++ b/primrose/scripts/b_asc_con_3.sh @@ -0,0 +1,9 @@ +#!/bin/bash +TIMEFORMAT=' +semantic property: ascending +syntactic property: Container +search time: %R seconds +' +time { +cargo run b_asc_con.rs gen_asc_con 3 +} diff --git a/primrose/scripts/b_asc_con_5.sh b/primrose/scripts/b_asc_con_5.sh new file mode 100755 index 0000000..32b3f61 --- /dev/null +++ b/primrose/scripts/b_asc_con_5.sh @@ -0,0 +1,9 @@ +#!/bin/bash +TIMEFORMAT=' +semantic property: ascending +syntactic property: Container +search time: %R seconds +' +time { +cargo run b_asc_con.rs gen_asc_con 5 +} diff --git a/primrose/scripts/b_asc_con_7.sh b/primrose/scripts/b_asc_con_7.sh new file mode 100755 index 0000000..843a7d1 --- /dev/null +++ b/primrose/scripts/b_asc_con_7.sh @@ -0,0 +1,9 @@ +#!/bin/bash +TIMEFORMAT=' +semantic property: ascending +syntactic property: Container +search time: %R seconds +' +time { +cargo run b_asc_con.rs gen_asc_con.rs 7 +} diff --git a/primrose/scripts/b_asc_con_9.sh b/primrose/scripts/b_asc_con_9.sh new file mode 100755 index 0000000..78134e5 --- /dev/null +++ b/primrose/scripts/b_asc_con_9.sh @@ -0,0 +1,9 @@ +#!/bin/bash +TIMEFORMAT=' +semantic property: ascending +syntactic property: Container +search time: %R seconds +' +time { +cargo run b_asc_con.rs gen_asc_con 9 +} diff --git a/primrose/scripts/b_asc_con_ra_3.sh b/primrose/scripts/b_asc_con_ra_3.sh new file mode 100755 index 0000000..8346f4c --- /dev/null +++ b/primrose/scripts/b_asc_con_ra_3.sh @@ -0,0 +1,9 @@ +#!/bin/bash +TIMEFORMAT=' +semantic property: ascending +syntactic property: Container, RandomAsscess +search time: %R seconds +' +time { +cargo run b_asc_con_ra.rs gen_asc_con_ra 3 +} diff --git a/primrose/scripts/b_asc_con_ra_5.sh b/primrose/scripts/b_asc_con_ra_5.sh new file mode 100755 index 0000000..9cb1cda --- /dev/null +++ b/primrose/scripts/b_asc_con_ra_5.sh @@ -0,0 +1,9 @@ +#!/bin/bash +TIMEFORMAT=' +semantic property: ascending +syntactic property: Container, RandomAsscess +search time: %R seconds +' +time { +cargo run b_asc_con_ra.rs gen_asc_con_ra 5 +} diff --git a/primrose/scripts/b_asc_con_ra_7.sh b/primrose/scripts/b_asc_con_ra_7.sh new file mode 100755 index 0000000..c797868 --- /dev/null +++ b/primrose/scripts/b_asc_con_ra_7.sh @@ -0,0 +1,9 @@ +#!/bin/bash +TIMEFORMAT=' +semantic property: ascending +syntactic property: Container, RandomAsscess +search time: %R seconds +' +time { +cargo run b_asc_con_ra.rs gen_asc_con_ra 7 +} diff --git a/primrose/scripts/b_asc_con_ra_9.sh b/primrose/scripts/b_asc_con_ra_9.sh new file mode 100755 index 0000000..f60be36 --- /dev/null +++ b/primrose/scripts/b_asc_con_ra_9.sh @@ -0,0 +1,9 @@ +#!/bin/bash +TIMEFORMAT=' +semantic property: ascending +syntactic property: Container, RandomAsscess +search time: %R seconds +' +time { +cargo run b_asc_con_ra.rs gen_asc_con_ra 9 +} diff --git a/primrose/scripts/b_des_con_3.sh b/primrose/scripts/b_des_con_3.sh new file mode 100755 index 0000000..c00c125 --- /dev/null +++ b/primrose/scripts/b_des_con_3.sh @@ -0,0 +1,9 @@ +#!/bin/bash +TIMEFORMAT=' +semantic property: descending +syntactic property: Container +search time: %R seconds +' +time { +cargo run b_des_con.rs gen_des_con 3 +} diff --git a/primrose/scripts/b_des_con_5.sh b/primrose/scripts/b_des_con_5.sh new file mode 100755 index 0000000..b870f59 --- /dev/null +++ b/primrose/scripts/b_des_con_5.sh @@ -0,0 +1,9 @@ +#!/bin/bash +TIMEFORMAT=' +semantic property: descending +syntactic property: Container +search time: %R seconds +' +time { +cargo run b_des_con.rs gen_des_con 5 +} diff --git a/primrose/scripts/b_des_con_7.sh b/primrose/scripts/b_des_con_7.sh new file mode 100755 index 0000000..ca11765 --- /dev/null +++ b/primrose/scripts/b_des_con_7.sh @@ -0,0 +1,9 @@ +#!/bin/bash +TIMEFORMAT=' +semantic property: descending +syntactic property: Container +search time: %R seconds +' +time { +cargo run b_des_con.rs gen_des_con 7 +} diff --git a/primrose/scripts/b_des_con_9.sh b/primrose/scripts/b_des_con_9.sh new file mode 100755 index 0000000..1d34895 --- /dev/null +++ b/primrose/scripts/b_des_con_9.sh @@ -0,0 +1,9 @@ +#!/bin/bash +TIMEFORMAT=' +semantic property: descending +syntactic property: Container +search time: %R seconds +' +time { +cargo run b_des_con.rs gen_des_con 9 +} diff --git a/primrose/scripts/b_des_con_ra_3.sh b/primrose/scripts/b_des_con_ra_3.sh new file mode 100755 index 0000000..fe7c0a0 --- /dev/null +++ b/primrose/scripts/b_des_con_ra_3.sh @@ -0,0 +1,9 @@ +#!/bin/bash +TIMEFORMAT=' +semantic property: descending +syntactic property: Container, RandomAccess +search time: %R seconds +' +time { +cargo run b_des_con_ra.rs gen_des_con_ra 3 +} diff --git a/primrose/scripts/b_des_con_ra_5.sh b/primrose/scripts/b_des_con_ra_5.sh new file mode 100755 index 0000000..3769ff5 --- /dev/null +++ b/primrose/scripts/b_des_con_ra_5.sh @@ -0,0 +1,9 @@ +#!/bin/bash +TIMEFORMAT=' +semantic property: descending +syntactic property: Container, RandomAccess +search time: %R seconds +' +time { +cargo run b_des_con_ra.rs gen_des_con_ra 5 +} diff --git a/primrose/scripts/b_des_con_ra_7.sh b/primrose/scripts/b_des_con_ra_7.sh new file mode 100755 index 0000000..9c3a7c9 --- /dev/null +++ b/primrose/scripts/b_des_con_ra_7.sh @@ -0,0 +1,9 @@ +#!/bin/bash +TIMEFORMAT=' +semantic property: descending +syntactic property: Container, RandomAccess +search time: %R seconds +' +time { +cargo run b_des_con_ra.rs gen_des_con_ra 7 +} diff --git a/primrose/scripts/b_des_con_ra_9.sh b/primrose/scripts/b_des_con_ra_9.sh new file mode 100755 index 0000000..5d70bf4 --- /dev/null +++ b/primrose/scripts/b_des_con_ra_9.sh @@ -0,0 +1,9 @@ +#!/bin/bash +TIMEFORMAT=' +semantic property: descending +syntactic property: Container, RandomAccess +search time: %R seconds +' +time { +cargo run b_des_con_ra.rs gen_des_con_ra 9 +} diff --git a/primrose/scripts/b_stack_con_3.sh b/primrose/scripts/b_stack_con_3.sh new file mode 100755 index 0000000..3317bd7 --- /dev/null +++ b/primrose/scripts/b_stack_con_3.sh @@ -0,0 +1,9 @@ +#!/bin/bash +TIMEFORMAT=' +semantic property: lifo +syntactic property: Container, Stack +search time: %R seconds +' +time { +cargo run b_stack_con.rs gen_stack_con 3 +} diff --git a/primrose/scripts/b_stack_con_5.sh b/primrose/scripts/b_stack_con_5.sh new file mode 100755 index 0000000..ff7c2ea --- /dev/null +++ b/primrose/scripts/b_stack_con_5.sh @@ -0,0 +1,9 @@ +#!/bin/bash +TIMEFORMAT=' +semantic property: lifo +syntactic property: Container, Stack +search time: %R seconds +' +time { +cargo run b_stack_con.rs gen_stack_con 5 +} diff --git a/primrose/scripts/b_stack_con_7.sh b/primrose/scripts/b_stack_con_7.sh new file mode 100755 index 0000000..f04d5f5 --- /dev/null +++ b/primrose/scripts/b_stack_con_7.sh @@ -0,0 +1,9 @@ +#!/bin/bash +TIMEFORMAT=' +semantic property: lifo +syntactic property: Container, Stack +search time: %R seconds +' +time { +cargo run b_stack_con.rs gen_stack_con 7 +} diff --git a/primrose/scripts/b_stack_con_9.sh b/primrose/scripts/b_stack_con_9.sh new file mode 100755 index 0000000..9347fb6 --- /dev/null +++ b/primrose/scripts/b_stack_con_9.sh @@ -0,0 +1,9 @@ +#!/bin/bash +TIMEFORMAT=' +semantic property: lifo +syntactic property: Container, Stack +search time: %R seconds +' +time { +cargo run b_stack_con.rs gen_stack_con 9 +} diff --git a/primrose/scripts/b_unique_asc_con_3.sh b/primrose/scripts/b_unique_asc_con_3.sh new file mode 100755 index 0000000..13fa6c3 --- /dev/null +++ b/primrose/scripts/b_unique_asc_con_3.sh @@ -0,0 +1,9 @@ +#!/bin/bash +TIMEFORMAT=' +semantic property: unique, ascending +syntactic property: Container +search time: %R seconds +' +time { +cargo run b_unique_asc_con.rs gen_unique_asc_con 3 +} diff --git a/primrose/scripts/b_unique_asc_con_5.sh b/primrose/scripts/b_unique_asc_con_5.sh new file mode 100755 index 0000000..cd8fd68 --- /dev/null +++ b/primrose/scripts/b_unique_asc_con_5.sh @@ -0,0 +1,9 @@ +#!/bin/bash +TIMEFORMAT=' +semantic property: unique, ascending +syntactic property: Container +search time: %R seconds +' +time { +cargo run b_unique_asc_con.rs gen_unique_asc_con 5 +} diff --git a/primrose/scripts/b_unique_asc_con_7.sh b/primrose/scripts/b_unique_asc_con_7.sh new file mode 100755 index 0000000..1a76def --- /dev/null +++ b/primrose/scripts/b_unique_asc_con_7.sh @@ -0,0 +1,9 @@ +#!/bin/bash +TIMEFORMAT=' +semantic property: unique, ascending +syntactic property: Container +search time: %R seconds +' +time { +cargo run b_unique_asc_con.rs gen_unique_asc_con 7 +} diff --git a/primrose/scripts/b_unique_asc_con_9.sh b/primrose/scripts/b_unique_asc_con_9.sh new file mode 100755 index 0000000..fba4841 --- /dev/null +++ b/primrose/scripts/b_unique_asc_con_9.sh @@ -0,0 +1,9 @@ +#!/bin/bash +TIMEFORMAT=' +semantic property: unique, ascending +syntactic property: Container +search time: %R seconds +' +time { +cargo run b_unique_asc_con.rs gen_unique_asc_con 9 +} diff --git a/primrose/scripts/b_unique_asc_con_ra_3.sh b/primrose/scripts/b_unique_asc_con_ra_3.sh new file mode 100755 index 0000000..7ef5b22 --- /dev/null +++ b/primrose/scripts/b_unique_asc_con_ra_3.sh @@ -0,0 +1,9 @@ +#!/bin/bash +TIMEFORMAT=' +semantic property: unique, ascending +syntactic property: Container, RandomAccess +search time: %R seconds +' +time { +cargo run b_unique_asc_con_ra.rs gen_unique_asc_con_ra 3 +} diff --git a/primrose/scripts/b_unique_asc_con_ra_5.sh b/primrose/scripts/b_unique_asc_con_ra_5.sh new file mode 100755 index 0000000..a32ee2f --- /dev/null +++ b/primrose/scripts/b_unique_asc_con_ra_5.sh @@ -0,0 +1,9 @@ +#!/bin/bash +TIMEFORMAT=' +semantic property: unique, ascending +syntactic property: Container, RandomAccess +search time: %R seconds +' +time { +cargo run b_unique_asc_con_ra.rs gen_unique_asc_con_ra 5 +} diff --git a/primrose/scripts/b_unique_asc_con_ra_7.sh b/primrose/scripts/b_unique_asc_con_ra_7.sh new file mode 100755 index 0000000..ea16baf --- /dev/null +++ b/primrose/scripts/b_unique_asc_con_ra_7.sh @@ -0,0 +1,9 @@ +#!/bin/bash +TIMEFORMAT=' +semantic property: unique, ascending +syntactic property: Container, RandomAccess +search time: %R seconds +' +time { +cargo run b_unique_asc_con_ra.rs gen_unique_asc_con_ra 7 +} diff --git a/primrose/scripts/b_unique_asc_con_ra_9.sh b/primrose/scripts/b_unique_asc_con_ra_9.sh new file mode 100755 index 0000000..8dff946 --- /dev/null +++ b/primrose/scripts/b_unique_asc_con_ra_9.sh @@ -0,0 +1,9 @@ +#!/bin/bash +TIMEFORMAT=' +semantic property: unique, ascending +syntactic property: Container, RandomAccess +search time: %R seconds +' +time { +cargo run b_unique_asc_con_ra.rs gen_unique_asc_con_ra 9 +} diff --git a/primrose/scripts/b_unique_con_3.sh b/primrose/scripts/b_unique_con_3.sh new file mode 100755 index 0000000..10cd4c4 --- /dev/null +++ b/primrose/scripts/b_unique_con_3.sh @@ -0,0 +1,9 @@ +#!/bin/bash +TIMEFORMAT=' +semantic property: unique +syntactic property: Container +search time: %R seconds +' +time { +cargo run b_unique_con.rs gen_unique_con 3 +} diff --git a/primrose/scripts/b_unique_con_5.sh b/primrose/scripts/b_unique_con_5.sh new file mode 100755 index 0000000..fbf8073 --- /dev/null +++ b/primrose/scripts/b_unique_con_5.sh @@ -0,0 +1,9 @@ +#!/bin/bash +TIMEFORMAT=' +semantic property: unique +syntactic property: Container +search time: %R seconds +' +time { +cargo run b_unique_con.rs gen_unique_con 5 +} diff --git a/primrose/scripts/b_unique_con_7.sh b/primrose/scripts/b_unique_con_7.sh new file mode 100755 index 0000000..c2bbf29 --- /dev/null +++ b/primrose/scripts/b_unique_con_7.sh @@ -0,0 +1,9 @@ +#!/bin/bash +TIMEFORMAT=' +semantic property: unique +syntactic property: Container +search time: %R seconds +' +time { +cargo run b_unique_con.rs gen_unique_con 7 +} diff --git a/primrose/scripts/b_unique_con_9.sh b/primrose/scripts/b_unique_con_9.sh new file mode 100755 index 0000000..dd16dfc --- /dev/null +++ b/primrose/scripts/b_unique_con_9.sh @@ -0,0 +1,9 @@ +#!/bin/bash +TIMEFORMAT=' +semantic property: unique +syntactic property: Container +search time: %R seconds +' +time { +cargo run b_unique_con.rs gen_unique_con 9 +} diff --git a/primrose/scripts/b_unique_con_ra_3.sh b/primrose/scripts/b_unique_con_ra_3.sh new file mode 100755 index 0000000..fbcac8d --- /dev/null +++ b/primrose/scripts/b_unique_con_ra_3.sh @@ -0,0 +1,9 @@ +#!/bin/bash +TIMEFORMAT=' +semantic property: unique +syntactic properties: Container, RandomAccess +search time: %R seconds +' +time { +cargo run b_unique_con_ra.rs gen_unique_con_ra 3 +} diff --git a/primrose/scripts/b_unique_con_ra_5.sh b/primrose/scripts/b_unique_con_ra_5.sh new file mode 100755 index 0000000..6845931 --- /dev/null +++ b/primrose/scripts/b_unique_con_ra_5.sh @@ -0,0 +1,9 @@ +#!/bin/bash +TIMEFORMAT=' +semantic property: unique +syntactic properties: Container, RandomAccess +search time: %R seconds +' +time { +cargo run b_unique_con_ra.rs gen_unique_con_ra 5 +} diff --git a/primrose/scripts/b_unique_con_ra_7.sh b/primrose/scripts/b_unique_con_ra_7.sh new file mode 100755 index 0000000..8b62c27 --- /dev/null +++ b/primrose/scripts/b_unique_con_ra_7.sh @@ -0,0 +1,9 @@ +#!/bin/bash +TIMEFORMAT=' +semantic property: unique +syntactic properties: Container, RandomAccess +search time: %R seconds +' +time { +cargo run b_unique_con_ra.rs gen_unique_con_ra 7 +} diff --git a/primrose/scripts/b_unique_con_ra_9.sh b/primrose/scripts/b_unique_con_ra_9.sh new file mode 100755 index 0000000..f756117 --- /dev/null +++ b/primrose/scripts/b_unique_con_ra_9.sh @@ -0,0 +1,9 @@ +#!/bin/bash +TIMEFORMAT=' +semantic property: unique +syntactic properties: Container, RandomAccess +search time: %R seconds +' +time { +cargo run b_unique_con_ra.rs gen_unique_con_ra 9 +} diff --git a/primrose/spec_code/b_asc_con.rs b/primrose/spec_code/b_asc_con.rs new file mode 100644 index 0000000..5189ddd --- /dev/null +++ b/primrose/spec_code/b_asc_con.rs @@ -0,0 +1,15 @@ +/*SPEC* +property ascending<T> { + \c -> ((for-all-consecutive-pairs c) leq?) +} +type AscendingCon<T> = {c impl (Container) | (ascending c)} +*ENDSPEC*/ + +fn main () { + let mut c = AscendingCon::<u32>::new(); + for x in 0..10 { + c.insert(x); + c.insert(x); + } + assert_eq!(c.len(), 20); +}
\ No newline at end of file diff --git a/primrose/spec_code/b_asc_con_ra.rs b/primrose/spec_code/b_asc_con_ra.rs new file mode 100644 index 0000000..af39947 --- /dev/null +++ b/primrose/spec_code/b_asc_con_ra.rs @@ -0,0 +1,15 @@ +/*SPEC* +property ascending<T> { + \c -> ((for-all-consecutive-pairs c) leq?) +} +type AscendingCon<T> = {c impl (Container, Indexable) | (ascending c)} +*ENDSPEC*/ + +fn main () { + let mut c = AscendingCon::<u32>::new(); + for x in 0..10 { + c.insert(x); + c.insert(x); + } + assert_eq!(c.len(), 20); +}
\ No newline at end of file diff --git a/primrose/spec_code/b_des_con.rs b/primrose/spec_code/b_des_con.rs new file mode 100644 index 0000000..f9532dd --- /dev/null +++ b/primrose/spec_code/b_des_con.rs @@ -0,0 +1,15 @@ +/*SPEC* +property descending<T> { + \c -> ((for-all-consecutive-pairs c) geq?) +} +type DescendingCon<T> = {c impl (Container) | (descending c)} +*ENDSPEC*/ + +fn main () { + let mut c = DescendingCon::<u32>::new(); + for x in 0..10 { + c.insert(x); + c.insert(x); + } + assert_eq!(c.len(), 20); +}
\ No newline at end of file diff --git a/primrose/spec_code/b_des_con_ra.rs b/primrose/spec_code/b_des_con_ra.rs new file mode 100644 index 0000000..5ed84c9 --- /dev/null +++ b/primrose/spec_code/b_des_con_ra.rs @@ -0,0 +1,15 @@ +/*SPEC* +property descending<T> { + \c -> ((for-all-consecutive-pairs c) geq?) +} +type DescendingCon<T> = {c impl (Container, Indexable) | (descending c)} +*ENDSPEC*/ + +fn main () { + let mut c = DescendingCon::<u32>::new(); + for x in 0..10 { + c.insert(x); + c.insert(x); + } + assert_eq!(c.len(), 20); +}
\ No newline at end of file diff --git a/primrose/spec_code/b_stack_con.rs b/primrose/spec_code/b_stack_con.rs new file mode 100644 index 0000000..d72a377 --- /dev/null +++ b/primrose/spec_code/b_stack_con.rs @@ -0,0 +1,16 @@ +/*SPEC* +property lifo<T> { + \c <: (Stack) -> (forall \x -> ((equal? (pop ((push c) x))) x)) +} + +type StackCon<S> = {c impl (Container, Stack) | (lifo c)} +*ENDSPEC*/ + +fn main () { + let mut c = StackCon::<u32>::new(); + for x in 0..10 { + c.insert(x); + c.insert(x); + } + assert_eq!(c.len(), 20); +}
\ No newline at end of file diff --git a/primrose/spec_code/b_unique_asc_con.rs b/primrose/spec_code/b_unique_asc_con.rs new file mode 100644 index 0000000..2aa0238 --- /dev/null +++ b/primrose/spec_code/b_unique_asc_con.rs @@ -0,0 +1,19 @@ +/*SPEC* +property unique<T> { + \c <: (Container) -> ((for-all-elems c) \a -> ((unique-count? a) c)) +} +property ascending<T> { + \c -> ((for-all-consecutive-pairs c) leq?) +} + +type StrictlyAscendingCon<S> = {c impl (Container) | ((unique c) and (ascending c))} +*ENDSPEC*/ + +fn main () { + let mut c = StrictlyAscendingCon::<u32>::new(); + for x in 0..10 { + c.insert(x); + c.insert(x); + } + assert_eq!(c.len(), 10); +}
\ No newline at end of file diff --git a/primrose/spec_code/b_unique_asc_con_ra.rs b/primrose/spec_code/b_unique_asc_con_ra.rs new file mode 100644 index 0000000..eb77993 --- /dev/null +++ b/primrose/spec_code/b_unique_asc_con_ra.rs @@ -0,0 +1,19 @@ +/*SPEC* +property unique<T> { + \c <: (Container) -> ((for-all-elems c) \a -> ((unique-count? a) c)) +} +property ascending<T> { + \c -> ((for-all-consecutive-pairs c) leq?) +} + +type StrictlyAscendingCon<S> = {c impl (Container, Indexable) | ((unique c) and (ascending c))} +*ENDSPEC*/ + +fn main () { + let mut c = StrictlyAscendingCon::<u32>::new(); + for x in 0..10 { + c.insert(x); + c.insert(x); + } + assert_eq!(c.len(), 10); +}
\ No newline at end of file diff --git a/primrose/spec_code/b_unique_con.rs b/primrose/spec_code/b_unique_con.rs new file mode 100644 index 0000000..acb9a30 --- /dev/null +++ b/primrose/spec_code/b_unique_con.rs @@ -0,0 +1,16 @@ +/*SPEC* +property unique<T> { + \c <: (Container) -> ((for-all-elems c) \a -> ((unique-count? a) c)) +} + +type UniqueCon<S> = {c impl (Container) | (unique c)} +*ENDSPEC*/ + +fn main () { + let mut c = UniqueCon::<u32>::new(); + for x in 0..10 { + c.insert(x); + c.insert(x); + } + assert_eq!(c.len(), 10); +}
\ No newline at end of file diff --git a/primrose/spec_code/b_unique_con_ra.rs b/primrose/spec_code/b_unique_con_ra.rs new file mode 100644 index 0000000..5bdf30c --- /dev/null +++ b/primrose/spec_code/b_unique_con_ra.rs @@ -0,0 +1,16 @@ +/*SPEC* +property unique<T> { + \c <: (Container) -> ((for-all-elems c) \a -> ((unique-count? a) c)) +} + +type UniqueCon<S> = {c impl (Container, Indexable) | (unique c)} +*ENDSPEC*/ + +fn main () { + let mut c = UniqueCon::<u32>::new(); + for x in 0..10 { + c.insert(x); + c.insert(x); + } + assert_eq!(c.len(), 10); +}
\ No newline at end of file diff --git a/primrose/spec_code/example_comp.rs b/primrose/spec_code/example_comp.rs new file mode 100644 index 0000000..00acce8 --- /dev/null +++ b/primrose/spec_code/example_comp.rs @@ -0,0 +1,20 @@ +/*SPEC* +property unique<T> { + \c <: (Container) -> ((for-all-elems c) \a -> ((unique-count? a) c)) +} +property ascending<T> { + \c -> ((for-all-consecutive-pairs c) leq?) +} + +type StrictlyAscendingCon<S> = {c impl (Container, Indexable) | ((unique c) and (ascending c))} +*ENDSPEC*/ + +fn main () { + let mut c = StrictlyAscendingCon::<u32>::new(); + for x in 0..10 { + c.insert(x); + c.insert(x); + //c.first(); + } + assert_eq!(c.len(), 10); +}
\ No newline at end of file diff --git a/primrose/spec_code/example_stack.rs b/primrose/spec_code/example_stack.rs new file mode 100644 index 0000000..af0b798 --- /dev/null +++ b/primrose/spec_code/example_stack.rs @@ -0,0 +1,17 @@ +/*SPEC* +property lifo<T> { + \c <: (Stack) -> (forall \x -> ((equal? (pop ((push c) x))) x)) +} + +type StackCon<S> = {c impl (Container, Stack) | (lifo c)} +*ENDSPEC*/ + +fn main () { + let mut c = StackCon::<u32>::new(); + for x in 0..10 { + c.insert(x); + c.insert(x); + //c.first(); + } + assert_eq!(c.len(), 20); +}
\ No newline at end of file diff --git a/primrose/spec_code/example_unique.rs b/primrose/spec_code/example_unique.rs new file mode 100644 index 0000000..34075f4 --- /dev/null +++ b/primrose/spec_code/example_unique.rs @@ -0,0 +1,17 @@ +/*SPEC* +property unique<T> { + \c <: (Container) -> ((for-all-elems c) \a -> ((unique-count? a) c)) +} + +type UniqueCon<S> = {c impl (Container) | (unique c)} +*ENDSPEC*/ + +fn main () { + let mut c = UniqueCon::<u32>::new(); + for x in 0..10 { + c.insert(x); + c.insert(x); + //c.first(); + } + assert_eq!(c.len(), 10); +}
\ No newline at end of file diff --git a/primrose/src/analysis.rs b/primrose/src/analysis.rs new file mode 100644 index 0000000..40fa0d9 --- /dev/null +++ b/primrose/src/analysis.rs @@ -0,0 +1,336 @@ +use crate::description::{Tag, InforMap}; +use crate::parser::{Prog, Block, Spec, Decl, Term, Refinement, Id, spec}; +use crate::spec_map::{PropSpecs}; + +use std::ops::Deref; +use std::env; +use std::fs; +use std::io::{Write, BufReader, BufRead, Error, ErrorKind}; + +type AnalyserError = String; +const LANGDECL: &str = "#lang rosette\n"; +const REQUIRE: &str = "(require \"../combinators.rkt\")\n"; +const EXTRAREQUIRE: &str = "(require \"../gen_lib_spec/ops.rkt\")\n"; +const GENPATH: &str = "./racket_specs/gen_prop_spec/"; + +// length can be adjusted +// set to 5 to speed up testing +const LISTMODEL: &str = +r#" +(define (generate-list n) + (define-symbolic* y integer? #:length n) + y) +(define-symbolic len (bitvector 32)) +(define ls (take-bv (generate-list 5) len)) +"#; + +fn gen_list_model(size: usize) -> String { + format!( +r#" +(define (generate-list n) + (define-symbolic* y integer? #:length n) + y) +(define-symbolic len (bitvector 32)) +(define ls (take-bv (generate-list {size}) len)) +"#) +} + +fn gen_symbolic(n: &str) -> String { + format!( +r#" +(define-symbolic {n} integer?) +"#) +} + +fn gen_symbolics(symbolics: &Vec<String>) -> String { + let provide = symbolics.join(" "); + let mut code = String::new(); + for s in symbolics.iter () { + code = code + &gen_symbolic(s); + } + let provide = format!( +r#" +(provide {provide} ls) +"#); + code = code + &provide; + code +} + +pub struct Analyser { + ctx: InforMap, + prop_specs: PropSpecs, +} + +impl Analyser { + pub fn new() -> Analyser { + Analyser { + ctx: InforMap::new(), + prop_specs: PropSpecs::new() + } + } + + pub fn get_ctx(&self) -> &InforMap { + &self.ctx + } + + pub fn get_prop_specs(&self) -> &PropSpecs { + &self.prop_specs + } + + + pub fn analyse_prog(&mut self, prog: Prog, model_size: usize) -> Result<(), AnalyserError> { + let specs: Vec<Spec> = + prog.iter() + .filter(| block | block.is_spec_block()) + .map(| block | block.extract_spec()) + .collect(); + self.analyse_specs(specs, model_size) + } + + pub fn analyse_specs(&mut self, specs: Vec<Spec>, model_size: usize) -> Result<(), AnalyserError> { + let concat_specs = specs.concat(); + let prop_decls: Vec<&Decl> = + concat_specs.iter() + .filter(| decl | decl.is_prop_decl()) + .collect(); + let contype_decls: Vec<&Decl> = + concat_specs.iter() + .filter(| decl | decl.is_contype_decl()) + .collect(); + match self.analyse_prop_decls(prop_decls, model_size) { + Ok(_) => match self.analyse_contype_decls(contype_decls.clone()) { + Ok(_) => self.analyse_bound_decls(contype_decls), + Err(e) => Err(e) + } + Err(e) => Err(e) + } + } + + pub fn analyse_prop_decls(&mut self, decls: Vec<&Decl>, model_size: usize) -> Result<(), AnalyserError> { + let mut result = Ok(()); + for decl in decls.into_iter() { + match self.analyse_prop_decl(decl, model_size) { + Ok(_) => continue, + Err(e) => result = Err(e) + } + } + result + } + + pub fn analyse_prop_decl(&mut self, decl: &Decl, model_size: usize) -> Result<(), AnalyserError> { + match decl { + Decl::PropertyDecl((id, _), term) => { + let mut mterm = term.clone(); + let mut cdr_added = Vec::<String>::new(); + let mut symbolics = Vec::<String>::new(); + let code = "(define ".to_string() + id + " " + &self.analyse_term(&mut mterm, true, false, &mut cdr_added, &mut symbolics) + ")\n" + "(provide " + id + ")"; + let filename = id.to_string() + ".rkt"; + let mut symbolics_provided = gen_symbolics(&vec!["n".to_string()]); + if (symbolics.len() != 0) { + symbolics_provided = gen_symbolics(&symbolics); + } + self.write_prop_spec_file(filename.clone(), code, symbolics_provided, model_size); + let prop_tag = Tag::Prop(Box::new(id.to_string())); + self.ctx.put(id.to_string(), prop_tag); + if (symbolics.len() == 0) { + self.prop_specs.insert(id.to_string(), (filename, vec!["n".to_string()])); + } else { + self.prop_specs.insert(id.to_string(), (filename, symbolics)); + } + Ok(()) + }, + _ => Err("Not a valid property declaration".to_string()) + } + } + + pub fn analyse_bound_decls(&mut self, decls: Vec<&Decl>) -> Result<(), AnalyserError> { + let mut result = Ok(()); + for decl in decls.into_iter() { + match self.analyse_bound_decl(decl) { + Ok(_) => continue, + Err(e) => result = Err(e) + } + } + result + } + + pub fn analyse_bound_decl(&mut self, decl: &Decl) -> Result<(), AnalyserError> { + match decl { + Decl::ConTypeDecl(con_ty, (_, ins, tags)) => { + let (c, t) = con_ty.get_con_elem().unwrap(); + let mut name = c.clone() + "Trait"; + let bound_tag = Tag::Bound((c.clone(), t), Box::new(ins.clone().into_iter().collect::<Vec<String>>())); + let immut_ctx = self.ctx.clone(); + // prevent generating existing name + let mut i: usize = 0; + while immut_ctx.contains(&name) { + name = name + &i.to_string(); + i = i+1; + } + let con_tag = immut_ctx.get_id(c.clone()).unwrap(); + match con_tag { + Tag::Con(elem_ty, _, tags) => { + self.ctx.update(c.clone(), Tag::Con(elem_ty.to_string(), name.clone(), Box::new(tags.to_vec()))); + }, + _ => { + return Err("Not a valid container declaration.".to_string()); + } + } + self.ctx.put(name, bound_tag); + Ok(()) + }, + _ => Err("Not a valid bound declaration".to_string()) + } + } + + pub fn analyse_contype_decls(&mut self, decls: Vec<&Decl>) -> Result<(), AnalyserError> { + let mut result = Ok(()); + for decl in decls.into_iter() { + match self.analyse_contype_decl(decl) { + Ok(_) => continue, + Err(e) => result = Err(e) + } + } + result + } + + pub fn analyse_contype_decl(&mut self, decl: &Decl) -> Result<(), AnalyserError> { + let mut tags = Vec::<Tag>::new(); + match decl { + Decl::ConTypeDecl(con_ty, (vid, ins, r)) => { + let (c, t) = con_ty.get_con_elem().unwrap(); + let i_tag = Tag::Bound((c.clone(), t.clone()), Box::new(ins.clone().into_iter().collect::<Vec<String>>())); + tags.push(i_tag); + match self.analyse_ref(r.deref(), vid) { + Ok(prop_tags) => { + let mut prop_tags_mut = prop_tags.clone(); + tags.append(&mut prop_tags_mut); + let con_tag = Tag::Con(t, String::new(), Box::new(tags)); + self.ctx.put(c, con_tag); + Ok(()) + }, + Err(e) => Err(e) + } + }, + _ => Err("Not a valid container type declaration".to_string()) + } + } + + fn analyse_ref<'a>(&self, r: &'a Refinement, vid: &'a Box<String>) -> Result<Vec<Tag>, AnalyserError> { + match r { + Refinement::Prop(term) => { + match term.deref() { + Term::AppTerm(term1, term2) => { + match self.retrive_ref_term(term1) { + Ok(t) => { + let tags = vec![t.clone()]; + Ok(tags) + }, + Err(e) => Err(e) + } + }, + _ => Err("Not a valid term for refining the type Con<T>".to_string()) + } + }, + Refinement::AndProps(r1, r2) => { + match self.analyse_ref(r1, vid) { + Ok(tags1) => { + match self.analyse_ref(r2, vid) { + Ok(tags2) => { + Ok([tags1, tags2].concat()) + }, + Err(e) => Err(e) + } + }, + Err(e) => Err(e) + } + } + } + } + + fn retrive_ref_term(&self, term: &Term) -> Result<&Tag, AnalyserError> { + match term { + Term::VarTerm(id) => { + match self.ctx.get_id(id.to_string()) { + Some(t) => { + match t { + Tag::Prop(_) => Ok(t), + _ => Err(id.to_string() + " does not have a valid property") + } + }, + _ => Err("Undefined variable: ".to_string() + id) + } + }, + _ => Err("Should be a varible term".to_string()) + } + } + + pub fn analyse_term(&self, term: &mut Term, is_outter_app: bool, is_quantifier: bool, cdr_added: &mut Vec<String>, symbolics: &mut Vec<String>) -> String { + match term { + Term::LitTerm(lit) => { + if (lit.to_string() == "true".to_string()) { + "#t".to_string() + } else { + "#f".to_string() + } + }, + Term::VarTerm(id) => { + id.to_string() + }, + Term::LambdaTerm((id, _), t) => { + if (is_quantifier) { + symbolics.push(id.to_string()); + "(list ".to_string() + id + ") " + &self.analyse_term(t, true, false, cdr_added, symbolics) + } else { + "(lambda (".to_string() + id + ") " + &self.analyse_term(t, true, false, cdr_added, symbolics) + ")" + } + + }, + Term::AppTerm(t1, t2) => { + // Temporary solution of cdr required to adjust model ops + if ((*t1.clone()).require_cdr() && !cdr_added.contains(&t1.to_string())) { + cdr_added.push(t1.to_string()); + *term = Term::AppTerm(Box::new(Term::VarTerm(Box::new("cdr".to_string()))), Box::new(term.clone())); + self.analyse_term(term, is_outter_app, is_quantifier, cdr_added, symbolics) + } else { + let mut result = String::new(); + match ((*t1.clone()).is_quantifier(), *t2.clone()) { + (_, Term::AppTerm(_, _)) => { + if (is_outter_app) { + "(".to_string() + &self.analyse_term(t1, false, false, cdr_added, symbolics) + " " + &self.analyse_term(t2, true, false, cdr_added, symbolics) + ")" + } else { + self.analyse_term(t1, false, false, cdr_added, symbolics) + " " + &self.analyse_term(t2, true, false, cdr_added, symbolics) + } + }, + (false, _) => { + if (is_outter_app) { + "(".to_string() + &self.analyse_term(t1, false, false, cdr_added, symbolics) + " " + &self.analyse_term(t2, false, false, cdr_added, symbolics) + ")" + } else { + self.analyse_term(t1, false, false, cdr_added, symbolics) + " " + &self.analyse_term(t2, false, false, cdr_added, symbolics) + } + }, + (true, _) => { + if (is_outter_app) { + "(".to_string() + &self.analyse_term(t1, false, false, cdr_added, symbolics) + " " + &self.analyse_term(t2, false, true, cdr_added, symbolics) + ")" + } else { + self.analyse_term(t1, false, false, cdr_added, symbolics) + " " + &self.analyse_term(t2, false, true, cdr_added, symbolics) + } + } + } + } + } + } + } + + fn write_prop_spec_file(&self, filename : String, contents: String, symbolics: String, model_size: usize) -> Result<(), Error> { + let mut output = fs::File::create(GENPATH.to_owned() + &filename)?; + write!(output, "{}", LANGDECL.to_string())?; + write!(output, "{}", REQUIRE.to_string())?; + write!(output, "{}", EXTRAREQUIRE.to_string())?; + let list_model = gen_list_model(model_size); + write!(output, "{}", list_model)?; + write!(output, "{}", contents)?; + write!(output, "{}", symbolics)?; + Ok(()) + } +} diff --git a/primrose/src/bounded_ops.rs b/primrose/src/bounded_ops.rs new file mode 100644 index 0000000..aae7410 --- /dev/null +++ b/primrose/src/bounded_ops.rs @@ -0,0 +1,25 @@ +use std::collections::HashMap; +use std::collections::hash_map::Iter; +use crate::types::{Type, TypeVar, Bounds}; + +type BoundName = String; +type OpName = String; +type OpInfo = (OpName, Type); +pub type BoundedOps = HashMap<BoundName, Vec<OpInfo>>; + +pub fn generate_bounded_ops() -> BoundedOps { + let mut ops = BoundedOps::new(); + let push = ("push".to_string(), Type::Fun( + Box::new(Type::Con(Box::new("Con".to_string()), + Box::new(Type::Var(TypeVar::new("T".to_string()))), + Box::new(Bounds::from(["Stack".to_string()])))), + Box::new(Type::Fun(Box::new(Type::Var(TypeVar::new("T".to_string()))), Box::new(Type::Con(Box::new("Con".to_string()), + Box::new(Type::Var(TypeVar::new("T".to_string()))), + Box::new(Bounds::from(["Stack".to_string()])))))))); + let pop = ("pop".to_string(), Type::Fun(Box::new(Type::Con(Box::new("Con".to_string()), + Box::new(Type::Var(TypeVar::new("T".to_string()))), + Box::new(Bounds::from(["Stack".to_string()])))), Box::new(Type::Var(TypeVar::new("T".to_string()))))); + ops.insert("Stack".to_string(), vec![push, pop]); + ops + +}
\ No newline at end of file diff --git a/primrose/src/description.rs b/primrose/src/description.rs new file mode 100644 index 0000000..9adf0be --- /dev/null +++ b/primrose/src/description.rs @@ -0,0 +1,95 @@ +use std::collections::HashMap; +use std::collections::hash_map::Iter; + +use crate::parser::{Id}; + +pub type Description = String; +type ElemTypeName = String; +type ConName = String; +type BoundName = String; + +#[derive(Eq, PartialEq, Clone, Debug)] +pub enum Tag { + Prop(Box<Description>), // analysis of a property + Bound((ConName, ElemTypeName), Box<Vec<Description>>), + Con(ElemTypeName, BoundName, Box<Vec<Tag>>) // analysis of a container type with refinements +} + +impl Tag { + pub fn is_prop_tag(&self) -> bool { + match self { + Tag::Prop(_) => true, + _ => false + } + } + + pub fn is_bound_tag(&self) -> bool { + match self { + Tag::Bound(_, _) => true, + _ => false + } + } + + pub fn is_con_tag(&self) -> bool { + match self { + Tag::Con(..) => true, + _ => false + } + } + + pub fn extract_prop_desc(&self) -> Description { + match self { + Tag::Prop(desc) => desc.to_string(), + _ => String::new() + } + } + + pub fn extract_bound_descs(&self) -> Vec<Description> { + match self { + Tag::Bound(_, descs) => descs.to_vec(), + _ => Vec::new() + } + } +} + +#[derive(Clone, Debug)] +pub struct InforMap { + infor_map: HashMap<Id, Tag>, +} + +impl InforMap { + pub fn new() -> InforMap { + InforMap { + infor_map: HashMap::new(), + } + } + + pub fn put(&mut self, id: Id, tag: Tag) -> bool { + if self.infor_map.contains_key(&id) { + false + } else { + self.infor_map.insert(id, tag); + true + } + } + + pub fn update(&mut self, id: Id, tag: Tag) { + self.infor_map.insert(id, tag); + } + + pub fn get_id(&self, id: Id) -> Option<&Tag> { + self.infor_map.get(&id) + } + + pub fn iter(&self) -> Iter<'_, Id, Tag> { + self.infor_map.iter() + } + + pub fn contains(&self, id: &Id) -> bool { + self.infor_map.contains_key(id) + } + + fn sz(&self) -> usize { + self.infor_map.len() + } +}
\ No newline at end of file diff --git a/primrose/src/generator.rs b/primrose/src/generator.rs new file mode 100644 index 0000000..aa18b6d --- /dev/null +++ b/primrose/src/generator.rs @@ -0,0 +1,378 @@ +use std::process::Command; +use std::env; +use std::fs; +use std::io::{Write, BufReader, BufRead, Error, ErrorKind}; +use indicatif::{ProgressBar, ProgressStyle}; + +use crate::parser::{Block, Spec, spec}; +use crate::type_check::{TypeChecker}; + +use crate::analysis::{Analyser}; +use crate::description::{Tag, Description, InforMap}; +use crate::lib_spec_processor::{process_lib_specs}; +use crate::spec_map::{PropSpecs, MatchSetup, ProvidedOps}; +use crate::run_matching::{LANGDECL, initialise_match_setup, gen_match_script, run_matching, cleanup_script, setup_dirs}; + +const CODEGEN: &str = "/*CODEGEN*/\n"; +const CODEGENEND: &str = "/*ENDCODEGEN*/\n"; + +const CODE: &str = "/*CODE*/"; +const CODEEND: &str = "/*ENDCODE*/"; + +const SPEC: &str = "/*SPEC*"; +const SPECEND: &str = "*ENDSPEC*/"; + +const LIB: &str = "./src/library/"; +const MATCHSCRIPT: &str = "./racket_specs/gen_match/match-script.rkt"; + +const IMPORT: &str = "use primrose::traits::container_constructor::ContainerConstructor;\n"; +const TRAITCRATE: &str = "primrose::traits::"; + +const OPS: &str = "./racket_specs/gen_lib_spec/ops.rkt"; + +type ErrorMessage = String; + +pub fn readfile(filename : String) -> String { + let contents = fs::read_to_string(filename) + .expect("Something went wrong reading the file"); + mark_src_blocks(contents) +} + +pub fn writefile(pathname: String, filename: String, contents: String) -> Result<(), Error> { + // Create the directory if it does not exist + Command::new("sh") + .arg("-c") + .arg("mkdir -p ".to_owned() + "./gen_code/" + &pathname) + .output() + .expect("Fail to create the library specification directory"); + + let path = "./gen_code/".to_string() + &pathname + "/"; + + let mut output = fs::File::create(path.to_owned() + &filename)?; + write!(output, "{}", contents)?; + + Ok(()) +} + +pub fn process_block(block: &Block) -> String { + match block { + Block::SpecBlock(_, _) => { + String::new() + }, + Block::CodeBlock(code, n) => { + code.to_string() + } + } +} + +fn process_bound_elem_ty(t: &str, elem_ty: &str) -> String { + return TRAITCRATE.to_string() + t + "<" + elem_ty + ">"; +} + +pub fn process_bound_decl(ctx: &InforMap) -> Result<String, ErrorMessage> { + let mut code = String::new(); + let match_setup = initialise_match_setup(); + for (id, tag) in ctx.iter() { + match tag { + Tag::Bound((c, t), decs) => { + let traits = decs.iter().map(|name| process_bound_elem_ty(name, t)).collect::<Vec<String>>().join(" + "); + code = code + &gen_trait_code(id, c, t, &traits); + }, + _ => continue + } + } + Ok(code) +} + +// Generate the code ro replace the container delcaration in property specification +pub fn process_con_decl(ctx: &InforMap, prop_specs: &PropSpecs) -> Result<Vec<String>, ErrorMessage> { + let mut code = String::new(); + // initialise a vector of generated container code + let mut gen_con_code: Vec<String> = Vec::new(); + let match_setup = initialise_match_setup(); + let cons = ctx.iter().filter(|(_, tag)| tag.is_con_tag()).collect::<Vec<(&String, &Tag)>>(); + if cons.len() > 1 { + for (id, tag) in ctx.iter() { + match tag { + Tag::Con(elem_ty, i_name, tags) => { + let prop_descs: Vec<Description> = + tags.iter() + .filter(| t | t.is_prop_tag()) + .map(| t | t.extract_prop_desc()) + .collect(); + let bounds: Vec<Description> = + tags.iter() + .filter(| t | t.is_bound_tag()) + .flat_map(| t | t.extract_bound_descs()) + .collect(); + let lookup_result = library_spec_lookup(id.to_string(), prop_descs, bounds, prop_specs, &match_setup); + match lookup_result { + Ok(struct_choices) => { + if struct_choices.is_empty() { + return Err("Unable to find a struct which matches the specification in the library".to_string()); + } else { + let opt = struct_choices.join(", "); + code = code + &gen_output_code(id, elem_ty, &struct_choices[0], i_name, &opt); + } + }, + Err(e) => { + return Err(e); + } + } + }, + _ => continue + } + } + gen_con_code.push(code); + Ok(gen_con_code) + } else { + for (id, tag) in ctx.iter() { + match tag { + Tag::Con(elem_ty, i_name, tags) => { + let prop_descs: Vec<Description> = + tags.iter() + .filter(| t | t.is_prop_tag()) + .map(| t | t.extract_prop_desc()) + .collect(); + let bounds: Vec<Description> = + tags.iter() + .filter(| t | t.is_bound_tag()) + .flat_map(| t | t.extract_bound_descs()) + .collect(); + let lookup_result = library_spec_lookup(id.to_string(), prop_descs, bounds, prop_specs, &match_setup); + match lookup_result { + Ok(struct_choices) => { + if struct_choices.is_empty() { + return Err("Unable to find a struct which matches the specification in the library".to_string()); + } else { + let opt = struct_choices.join(", "); + for struct_choice in struct_choices { + gen_con_code.push(gen_output_code(id, elem_ty, &struct_choice, i_name, &opt)); + } + } + }, + Err(e) => { + return Err(e); + } + } + }, + _ => continue + } + } + Ok(gen_con_code) + } +} + +fn write_provided_ops(provided_ops: &ProvidedOps) -> Result<(), Error> { + let ops_path = OPS; + let (code, ops) = provided_ops; + let mut output = fs::File::create(ops_path.to_owned())?; + write!(output, "{}", LANGDECL.to_string())?; + for i in 0..code.len() { + write!(output, "{}", code[i])?; + } + let ops_string = ops.join(" "); + let provide = "\n(provide ".to_string() + &ops_string + ")"; + write!(output, "{}", provide)?; + Ok(()) +} + +fn library_spec_lookup(id: String, properties: Vec<Description>, bounds: Vec<Description>, prop_specs: &PropSpecs, match_setup: &MatchSetup) -> Result<Vec<String>, ErrorMessage> { + let pb = ProgressBar::new_spinner(); + pb.enable_steady_tick(200); + pb.set_style( + ProgressStyle::default_spinner() + .tick_strings(&[ + "(>'-')>", + " ('-') ", + "<('-'<)", + " ('-') ", + "^('-')^", + " ('-') ", + "v('-')v", + " ('-') ", + " (^-^) ", + ]) + .template("{spinner:.magenta} {msg}"), + ); + pb.set_message("Finding library implementations for ".to_owned() + &id + "..."); + let lib_spec = process_lib_specs(LIB.to_string()).expect("Error: Unable to process library files"); // The specifications of library structs + let mut structs = Vec::new(); + // select library structs implement bounds decl in contype + let mut lib_spec_impls = lib_spec.clone(); + for (name, (_, impls, _)) in lib_spec.iter() { + if (!bounds.iter().all(|i| impls.keys().cloned().collect::<String>().contains(i))) { + lib_spec_impls.remove(name); + } + } + for (name, (lib_spec_dir, bound_ctx, provided_ops)) in lib_spec_impls.iter() { + match write_provided_ops(provided_ops) { + Ok(_) => { }, + Err(_) => { + return Err("Error, cannot obtain provided operations from the library specifiction".to_string()); + } + } + let mut is_match = false; + for p in &properties { + let mut is_partial_match = false; + for i in &bounds { + let (prop_file, symbolics) = prop_specs.get(p).expect(&("Error: No property specification found for: ".to_string() + &p)); + match gen_match_script(p.to_string(), match_setup.get(i).unwrap().to_string(), prop_file.to_string(), lib_spec_dir.to_string(), bound_ctx.get(i).unwrap().to_string(), symbolics) { + Ok(_) => { + let result = run_matching(MATCHSCRIPT.to_string()); + match result { + Ok(r) => { // true - match; false - not match + if (r) { + is_partial_match = true; + } else { + is_partial_match = false; + break; + } + }, + Err(e) => { + return Err(e); + } + } + }, + Err(e) => { + return Err(e.to_string()); + } + } + } + is_match = is_partial_match; + if (!is_match) { + break; + } + } + if (is_match) { + structs.push(name.to_string()); + } + } + pb.finish_with_message("Done. ".to_owned() + &structs.len().to_string() + " implementation(s) for " + &id + " found."); + cleanup_script(); + Ok(structs) +} + +pub fn process_src(filename : String, model_size: usize) -> Result<Vec<String>, ErrorMessage> { + setup_dirs(); + println!("{}", "Ready..."); + let f = readfile(filename); + match spec::prog(&f) { + Ok(blocks) => { + let mut tc = TypeChecker::new(); + match tc.check_prog(blocks.clone()) {// type checking + Ok(_) => { + // type checking ok + // run analyser + let mut analyser = Analyser::new(); + match analyser.analyse_prog(blocks.clone(), model_size) { + Ok(_) => { + let mut gen_code = Vec::new(); + // let mut result = String::new(); + // generate con types according to the information in con decl + match process_bound_decl(analyser.get_ctx()) { + Ok(code) => { + let bound = code.clone(); + match process_con_decl(analyser.get_ctx(), analyser.get_prop_specs()) { + Ok(gen_con_code) => { + for code in gen_con_code.iter() { + let mut result = bound.clone(); + result = CODEGEN.to_string() + IMPORT + &result + &code + CODEGENEND; + // generate rust source code + let code_blocks: Vec<&Block> = + blocks.iter() + .filter(| block | block.is_code_block()) + .collect(); + for block in code_blocks.iter() { + result = result + &process_block(block.to_owned()); + } + gen_code.push(result); + } + Ok(gen_code) + }, + Err(e) => Err(e) + } + }, + Err(e) => Err(e) + } + }, + Err(e) => Err(e) + } + }, + Err(e) => Err(e) + } + }, + _ => Err("Error, invalid source code.".to_string()) + } +} + +pub fn run(input: String, output_path: String, model_size: usize) -> Result<(), Error> { + match process_src(input, model_size) { + Ok(gen_code) => { + let mut i = 0; + while i < gen_code.len() { + let code = gen_code[i].clone(); + let output_file = output_path.clone() + &i.to_string() + ".rs"; + writefile(output_path.clone(), output_file, code); + i = i + 1; + } + Ok(()) + }, + Err(e) => Err(Error::new(ErrorKind::Other, e.to_string())) + } +} + +fn mark_src_blocks(src : String) -> String { + let mut trimed_src = src.trim(); + let mut result = String::new(); + while trimed_src.len() > 0 { + match trimed_src.find(SPEC) { + Some(n) => { + match trimed_src.find(SPECEND) { + Some(m) => { + if (n > 0) { + let code = &trimed_src[..n]; + result = result + CODE + &code + CODEEND; + } + let spec = &trimed_src[n..(m+SPECEND.len())]; + trimed_src = &trimed_src[(m+SPECEND.len())..].trim(); + result = result + &spec; + }, + None => { + result = result + CODE + trimed_src + CODEEND; + break; + } + } + }, + None => { + result = result + CODE + trimed_src + CODEEND; + break; + } + } + } + result +} + +pub fn gen_output_code(s: &str, elem_type: &str, chosen: &str, trait_name: &str, choices: &str) -> String { + format!( +r#"struct {s}<{elem_type}> {{ + elem_t: core::marker::PhantomData<{elem_type}>, +}} + +impl<{elem_type}: 'static + Ord + std::hash::Hash> ContainerConstructor for {s}<{elem_type}> {{ + type Impl = {chosen}<{elem_type}>; // All possible choices: {choices} + type Bound = dyn {trait_name}<{elem_type}>; + fn new() -> Box<Self::Bound> {{ + Box::new(Self::Impl::new()) + }} +}} +"#) +} + +pub fn gen_trait_code(trait_name: &str, s: &str, elem_type: &str, traits: &str) -> String { + format!( +r#" +trait {trait_name}<{elem_type}> : {traits} {{}} +impl<{elem_type}: 'static + Ord + std::hash::Hash> {trait_name}<{elem_type}> for <{s}<{elem_type}> as ContainerConstructor>::Impl {{}} +"#) +}
\ No newline at end of file diff --git a/primrose/src/inference.rs b/primrose/src/inference.rs new file mode 100644 index 0000000..6d55b09 --- /dev/null +++ b/primrose/src/inference.rs @@ -0,0 +1,120 @@ +use std::collections::{HashSet, HashMap}; +use std::hash::Hash; +use std::ops::{Deref, DerefMut}; +use std::fmt; +use std::result; + +use crate::parser::{Id, Term}; +use crate::types::{Name, Type, TypeVar, Types, TypeVarGen, Subst, TypeScheme}; +use crate::bounded_ops::{BoundedOps, generate_bounded_ops}; + +/// A type environment +#[derive(Clone, Debug)] +pub struct TypeEnv(HashMap<Id, TypeScheme>); + +impl Deref for TypeEnv { + type Target = HashMap<Id, TypeScheme>; + fn deref(&self) -> &Self::Target { + &self.0 + } +} +impl DerefMut for TypeEnv { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl Types for TypeEnv { + fn ftv(&self) -> HashSet<TypeVar> { + self.values().map(|x| x.clone()).collect::<Vec<TypeScheme>>().ftv() + } + + fn apply(&self, s: &Subst) -> TypeEnv { + TypeEnv(self.iter() + .map(|(k, v)| (k.clone(), v.apply(s))) + .collect()) + } +} + +impl TypeEnv { + pub fn new() -> TypeEnv { + TypeEnv(HashMap::new()) + } + + fn generalise(&self, ty: &Type) -> TypeScheme { + TypeScheme { + vars: ty.ftv().difference(&self.ftv()).cloned().collect(), + ty: ty.clone(), + } + } + + // Main type inference algorithm + fn ti(&self, term: &Term, tvg: &mut TypeVarGen) -> Result<(Subst, Type), InferenceError> { + // Get types of operations defined in traits + let bounded_ops = generate_bounded_ops(); + let (s, t) = (match term { + // Infer literal: currently only boolean + Term::LitTerm(_) => { + Ok((Subst::new(), Type::Bool())) + } + // Infer variable + Term::VarTerm(v) => { + match self.get(&v.to_string()) { + Some(s) => Ok((Subst::new(), s.instantiate(tvg))), + None => Err("unbound variable".to_string() + " " + &v.to_string()), + } + } + // Infer abstraction + Term::LambdaTerm((n, bounds), ref e) => { + let mut tv = Type::Var(tvg.next()); + let mut env = self.clone(); + if (!bounds.is_empty()) { + tv = Type::Con(Box::new("Con".to_string()), Box::new(tv), bounds.clone()); + for b in bounds.iter() { + if bounded_ops.contains_key(b) { + let ops_info = bounded_ops.get(b).unwrap(); + for (op_name, op_ty) in ops_info { + env.insert( + op_name.to_string(), + TypeScheme { + vars: Vec::new(), + ty: op_ty.clone(), + } + ); + } + } + } + } + env.remove(&n.to_string()); + + env.insert( + n.to_string(), + TypeScheme { + vars: Vec::new(), + ty: tv.clone(), + } + ); + let (s1, t1) = env.ti(e, tvg)?; + let result_ty = Type::Fun(Box::new(tv.apply(&s1)), Box::new(t1)); + Ok((s1.clone(), result_ty)) + } + // Infer application + Term::AppTerm(ref e1, ref e2) => { + let (s1, t1) = self.ti(e1, tvg)?; + let (s2, t2) = self.apply(&s1).ti(e2, tvg)?; + let tv = Type::Var(tvg.next()); + let s3 = t1.apply(&s2).mgu(&Type::Fun(Box::new(t2), Box::new(tv.clone())))?; + Ok((s3.compose(&s2.compose(&s1)), tv.apply(&s3))) + } + })?; + Ok((s, t)) + } + + // perform type inference on term + pub fn type_inference(&self, term: &Term, tvg: &mut TypeVarGen) -> Result<Type, InferenceError> { + let (s, t) = self.ti(term, tvg)?; + Ok(t.apply(&s)) + } +} + +pub type InferenceError = String;
\ No newline at end of file diff --git a/primrose/src/lib.rs b/primrose/src/lib.rs new file mode 100644 index 0000000..5286b39 --- /dev/null +++ b/primrose/src/lib.rs @@ -0,0 +1,20 @@ +#![allow(unused)] +#![feature(linked_list_cursors)] + +pub mod parser; +pub mod generator; +pub mod type_check; +pub mod description; +pub mod types; +pub mod analysis; +pub mod inference; +pub mod lib_spec_processor; +pub mod spec_map; +pub mod run_matching; +pub mod bounded_ops; + +pub mod library; +pub mod traits; + +pub mod proptest; +pub mod tools;
\ No newline at end of file diff --git a/primrose/src/lib_spec_processor.rs b/primrose/src/lib_spec_processor.rs new file mode 100644 index 0000000..30df5f1 --- /dev/null +++ b/primrose/src/lib_spec_processor.rs @@ -0,0 +1,227 @@ +use std::env; +use std::fs; +use std::io::{Write, BufReader, BufRead, Error, ErrorKind}; +use std::collections::BTreeMap; +use std::collections::btree_map::Iter; +//use std::collections::hash_map::Iter; +use std::collections::HashMap; + +use crate::spec_map::{LibSpecs, Bounds, ProvidedOps}; + +const LIBSPECNAME: &str = "/*LIBSPEC-NAME*"; +const LIBSPECNAMEEND: &str = "*ENDLIBSPEC-NAME*/"; +const LIBSPEC: &str = "/*LIBSPEC*"; +const LIBSPECEND: &str = "*ENDLIBSPEC*/"; +const LANGDECL: &str = "#lang rosette\n"; +const GENPATH: &str = "./racket_specs/gen_lib_spec/"; +const OPNAME: &str = "/*OPNAME*"; +const OPNAMEEND: &str = "*ENDOPNAME*/"; +const IMPL: &str = "/*IMPL*"; +const IMPLEND: &str = "*ENDIMPL*/"; + +type ErrorMessage = String; + +fn is_next_pragma_impl(src: &String) -> bool { + match src.find(IMPL) { + Some(impl_pos) => { + match src.find(LIBSPEC) { + Some(spec_pos) => { + impl_pos < spec_pos + }, + None => true + } + }, + None => false + } +} + +fn has_pragma_impl(src: &String) -> bool { + src.contains(IMPL) +} + +fn has_pragma_spec(src: &String) -> bool { + src.contains(LIBSPEC) +} + +pub fn read_lib_file(filename : String) -> Result<(String, String, Vec<String>, String, Bounds, ProvidedOps), ErrorMessage> { + let contents = fs::read_to_string(filename) + .expect("Something went wrong reading the file"); + let trimed_contents = contents.trim().to_string(); + + let name_pragmas: Vec<&str> = trimed_contents.matches(LIBSPECNAME).collect(); + let name_end_pragmas: Vec<&str> = trimed_contents.matches(LIBSPECNAMEEND).collect(); + let spec_pragmas: Vec<&str> = trimed_contents.matches(LIBSPEC).collect(); + let spec_end_pragmas : Vec<&str> = trimed_contents.matches(LIBSPECEND).collect(); + if ((name_pragmas.len() != 1) || (name_end_pragmas.len() != 1)) { + return Err("Error, invalid declaration of library specification name**.".to_string()); + } else if (spec_pragmas.len() != spec_end_pragmas.len()) { + return Err("Error, invalid declaration of library specification.".to_string()); + } else { + let mut specs = String::new(); + let v1: Vec<&str> = trimed_contents.split(LIBSPECNAME).collect(); + let s = v1.get(1).expect("Error, invalid declaration of library specification name."); + let v2: Vec<&str> = s.split(LIBSPECNAMEEND).collect(); + let s3 = v2.get(0).unwrap().trim().to_string(); + let v3: Vec<&str> = s3.split(" ").collect(); + let spec_name = v3.get(0).unwrap().trim().to_string(); + let struct_name = v3.get(1).unwrap().trim().to_string(); + let s1 = v1.get(0).expect("Unexpected error."); + let s2 = v2.get(1).expect("Unexpected error."); + // process interface blocks + let mut trimed_contents = String::new(); + trimed_contents.push_str(&s1); + trimed_contents.push_str(&s2); + if (!is_next_pragma_impl(&trimed_contents) && has_pragma_spec(&trimed_contents)) { + return Err("Specification without declared interface is not allowed".to_string()); + } else { + let mut interfaces = Vec::<String>::new(); + let mut interface_info = HashMap::<String, BTreeMap<String, (String, String, String)>>::new(); + let mut code = Vec::<String>::new(); + let mut provided_ops = Vec::<String>::new(); + while (has_pragma_impl(&trimed_contents)) { + let v4: Vec<&str> = trimed_contents.splitn(2, IMPL).collect(); + let s4 = v4.get(1).expect("Error, invalid interface declaration."); + let v5: Vec<&str> = s4.splitn(2, IMPLEND).collect(); + let mut interface_name = v5.get(0).unwrap().trim().to_string(); + trimed_contents = v5.get(1).unwrap().trim().to_string(); + let lib_specs = extract_lib_specs(trimed_contents); + match lib_specs { + Ok((rest, mut v, infos, mut ops)) => { + code.append(&mut v); + interface_info.insert(interface_name.clone(), infos); + interfaces.push(interface_name.clone()); + provided_ops.append(&mut ops); + trimed_contents = rest; + }, + Err(e) => { + return Err(e); + } + } + } + let (provide, interface_provide_map) = generate_provide(interface_info); + Ok((spec_name, struct_name, code.clone(), provide, interface_provide_map, (code, provided_ops))) + } + } +} + +pub fn generate_provide(interface_info: HashMap<String, BTreeMap<String, (String, String, String)>>) -> (String, Bounds) { + let mut interfaces = Vec::<String>::new(); + let mut provide = String::new(); + let mut interface_provide_map = Bounds::new(); + for (interface, infos) in interface_info.iter() { + let mut specs = Vec::<String>::new(); + let mut pres = Vec::<String>::new(); + for (key, value) in infos.iter() { + specs.push(value.0.clone()); + pres.push(value.1.clone()); + } + let specs_name = interface.to_lowercase() + "-specs"; + let pres_name = interface.to_lowercase() + "-pres"; + let interface_name = interface.to_lowercase(); + let specs_str = "\n(define ".to_string() + &specs_name + " (list " + &specs.join(" ") + "))\n"; + let pres_str = "(define ".to_string() + &pres_name + " (list " + &pres.join(" ") + "))\n"; + let interface_str = "(define ".to_string() + &interface_name + " (cons " + &specs_name + " " + &pres_name + "))\n"; + provide = provide + &specs_str + &pres_str + &interface_str; + interfaces.push(interface.to_lowercase()); + interface_provide_map.insert(interface.to_string(), interface_name); + } + let provide_str = "(provide ".to_string() + &interfaces.join(" ") + ")"; + + provide = provide + &provide_str; + (provide, interface_provide_map) +} + +pub fn extract_lib_specs(src: String) -> Result<(String, Vec<String>, BTreeMap<String, (String, String, String)>, Vec<String>), ErrorMessage> { + let mut result = Vec::<String>::new(); + let mut contents = src.trim(); + let mut op_infos = BTreeMap::<String, (String, String, String)>::new(); + let mut provided_ops = Vec::<String>::new(); + while (contents.len() > 0 && !is_next_pragma_impl(&contents.to_string())) { + if (contents.contains(LIBSPEC) && contents.contains(LIBSPECEND)) { + let v1: Vec<&str> = contents.splitn(2, LIBSPEC).collect(); + let s = v1.get(1).expect("Error, invalid specification."); + let v2: Vec<&str> = s.splitn(2, LIBSPECEND).collect(); + let spec = v2.get(0).unwrap().trim().to_string(); + let info = extract_op_info(spec.clone()).unwrap(); + op_infos.insert(info.0, (info.1.clone(), info.2, info.3)); + provided_ops.push(info.1); + let v3: Vec<&str> = spec.splitn(2, OPNAMEEND).collect(); + let code = v3.get(1).unwrap().trim_matches(|c| c == '\t' || c == ' ').to_string(); + result.push(code); + contents = v2.get(1).unwrap().trim(); + } else { + break; + } + } + Ok((contents.to_string(), result, op_infos, provided_ops)) +} + +pub fn extract_op_info(spec: String) -> Result<(String, String, String, String), ErrorMessage> { + let op_name_pragmas: Vec<&str> = spec.matches(OPNAME).collect(); + let op_name_end_pragmas : Vec<&str> = spec.matches(OPNAMEEND).collect(); + if (spec.chars().nth(0).unwrap() == '/' && op_name_pragmas.len()==1 && op_name_end_pragmas.len() == 1) { + let v1: Vec<&str> = spec.split(OPNAME).collect(); + let s = v1.get(1).expect("Error, invaild operation information declaration."); + let v2: Vec<&str> = s.split(OPNAMEEND).collect(); + let info_string = v2.get(0).expect("Error, invaild operation information declaration."); + let mut infos: Vec<&str> = info_string.trim().split(" ").collect(); + if (infos.len() == 4) { + let post = infos.pop().unwrap(); + let pre = infos.pop().unwrap(); + let op_spec = infos.pop().unwrap(); + let name = infos.pop().unwrap(); + Ok((name.to_string(), op_spec.to_string(), pre.to_string(), post.to_string())) + } else { + Err("Error, invaild operation information declaration.".to_string()) + } + } else { + Err("Error, invaild operation information declaration.".to_string()) + } +} + +pub fn write_lib_file(filename : String, contents: Vec<String>, provide: String) -> Result<(), Error> { + let path = GENPATH; + + let mut output = fs::File::create(path.to_owned() + &filename)?; + write!(output, "{}", LANGDECL.to_string())?; + for i in 0..contents.len() { + write!(output, "{}", contents[i])?; + } + write!(output, "{}", provide)?; + Ok(()) +} + +pub fn process_lib_spec(filename: String) -> Result<(String, String, Bounds, ProvidedOps), ErrorMessage> { + let result = read_lib_file(filename); + match result { + Ok((name, struct_name, specs, provide, interface_provide_map, provided_ops)) => { + let spec_name = name + ".rkt"; + let state = write_lib_file(spec_name.clone(), specs, provide); + if (!state.is_ok()) { + return Err("Unable to create lib specification file".to_string()); + } + Ok((spec_name, struct_name, interface_provide_map, provided_ops)) + }, + Err(e) => Err(e) + } +} + +pub fn process_lib_specs(dirname: String) -> Result<LibSpecs, ErrorMessage> { + let paths = fs::read_dir(dirname).unwrap(); + let files : Vec<String> = paths.into_iter() + .map(|path| path.unwrap().path().to_str().unwrap().to_string()) + .filter(|path| !path.contains("/mod.rs")) + .collect(); + let mut lib_specs = LibSpecs::new(); + for path in files { + match process_lib_spec(path) { + Ok((spec_name, struct_name, interface_provide_map, provided_ops)) => { + lib_specs.insert(struct_name, (spec_name, interface_provide_map, provided_ops)); + }, + Err(e) => { + return Err(e); + } + } + } + Ok(lib_specs) +}
\ No newline at end of file diff --git a/primrose/src/library/eager_sorted_vector.rs b/primrose/src/library/eager_sorted_vector.rs new file mode 100644 index 0000000..403a9b4 --- /dev/null +++ b/primrose/src/library/eager_sorted_vector.rs @@ -0,0 +1,337 @@ +/*LIBSPEC-NAME* +rust-eager-sorted-vec-spec primrose::library::eager_sorted_vector::EagerSortedVec +*ENDLIBSPEC-NAME*/ + +use std::vec::Vec; +use std::slice::Iter; +use std::ops::Deref; +use crate::traits::{Container, Stack, Indexable}; +use std::iter::FromIterator; + +use proptest::prelude::*; +use crate::proptest::strategies::{eager_sorted_vec}; +use crate::proptest::*; + +use im::conslist::{ConsList}; +use im::conslist; +use std::sync::Arc; + + +// A Sorted Vector +#[derive(Debug, Clone)] +pub struct EagerSortedVec<T> { + v: Vec<T>, +} + +impl<T: Ord> EagerSortedVec<T> { + pub fn from_vec(mut v: Vec<T>) -> EagerSortedVec<T> { + v.sort(); + EagerSortedVec{ v: v } + } + + pub fn new() -> EagerSortedVec<T> { + EagerSortedVec { v: Vec::new() } + } + + pub fn len(&mut self) -> usize { + self.v.len() + } + + pub fn contains(&mut self, x: &T) -> bool { + match self.v.binary_search(x) { + Ok(_) => true, + Err(_) => false, + } + } + + pub fn is_empty(&mut self) -> bool { + self.len() == 0 + } + + pub fn push(&mut self, value: T) { + let index = self.v.binary_search(&value).unwrap_or_else(|i| i); + self.v.insert(index, value); + } + + pub fn pop(&mut self) -> Option<T> { + self.v.pop() + } + + pub fn remove(&mut self, index: usize) -> T { + self.v.remove(index) + } + + pub fn clear(&mut self) { + self.v.clear() + } + + pub fn first(&mut self) -> Option<&T> { + self.v.first() + } + + pub fn last(&mut self) -> Option<&T> { + self.v.last() + } + + pub fn iter(&mut self) -> Iter<'_, T> { + self.v.iter() + } + + pub fn to_vec(self) -> Vec<T> { + self.v + } +} + + +/*IMPL* +Container +*ENDIMPL*/ +impl<T: Ord> Container<T> for EagerSortedVec<T> { + + /*LIBSPEC* + /*OPNAME* + len op-len pre-len post-len + *ENDOPNAME*/ + (define (op-len xs) (cons xs (length xs))) + (define (pre-len xs) (equal? xs (sort xs <))) + (define (post-len xs r) (equal? r (op-len xs))) + *ENDLIBSPEC*/ + fn len(&mut self) -> usize { + EagerSortedVec::len(self) + } + + /*LIBSPEC* + /*OPNAME* + contains op-contains pre-contains post-contains + *ENDOPNAME*/ + (define (op-contains xs x) + (cond + [(list? (member x xs)) (cons xs #t)] + [else (cons xs #f)])) + (define (pre-contains xs) (equal? xs (sort xs <))) + (define (post-contains xs x r) (equal? r (op-contains xs x))) + *ENDLIBSPEC*/ + fn contains(&mut self, x: &T) -> bool { + EagerSortedVec::contains(self, x) + } + + /*LIBSPEC* + /*OPNAME* + is-empty op-is-empty pre-is-empty post-is-empty + *ENDOPNAME*/ + (define (op-is-empty xs) (cons xs (null? xs))) + (define (pre-is-empty xs) (equal? xs (sort xs <))) + (define (post-is-empty xs r) (equal? r (op-is-empty xs))) + *ENDLIBSPEC*/ + fn is_empty(&mut self) -> bool { + EagerSortedVec::is_empty(self) + } + + /*LIBSPEC* + /*OPNAME* + clear op-clear pre-clear post-clear + *ENDOPNAME*/ + (define (op-clear xs) null) + (define (pre-clear xs) (equal? xs (sort xs <))) + (define (post-clear xs r) (equal? r (op-clear xs))) + *ENDLIBSPEC*/ + fn clear(&mut self) { + EagerSortedVec::clear(self); + } + + /*LIBSPEC* + /*OPNAME* + insert op-insert pre-insert post-insert + *ENDOPNAME*/ + (define (op-insert xs x) (sort (append xs (list x)) <)) + (define (pre-insert xs) (equal? xs (sort xs <))) + (define (post-insert xs x ys) (equal? ys (op-insert xs x))) + *ENDLIBSPEC*/ + fn insert(&mut self, elt: T) { + EagerSortedVec::push(self, elt); + } + + /*LIBSPEC* + /*OPNAME* + remove op-remove pre-remove post-remove + *ENDOPNAME*/ + (define (op-remove xs x) + (cond + [(list? (member x xs)) (cons (remove x xs) x)] + [else (cons xs null)])) + (define (pre-remove xs) (equal? xs (sort xs <))) + (define (post-remove xs r) (equal? r (op-remove xs))) + *ENDLIBSPEC*/ + fn remove(&mut self, elt: T) -> Option<T> { + match self.iter().position(|x| *x == elt) { + Some(index) => { + Some(self.remove(index)) + }, + None => None + } + } +} + +/*IMPL* +Indexable +*ENDIMPL*/ +impl<T: Ord> Indexable<T> for EagerSortedVec<T> { + /*LIBSPEC* + /*OPNAME* + first op-first pre-first post-first + *ENDOPNAME*/ + (define (op-first xs) + (cond + [(null? xs) (cons xs null)] + [else (cons xs (first xs))])) + (define (pre-first xs) (equal? xs (sort xs <))) + (define (post-first xs r) (equal? r (op-first xs))) + *ENDLIBSPEC*/ + fn first(&mut self) -> Option<&T> { + EagerSortedVec::first(self) + } + + /*LIBSPEC* + /*OPNAME* + last op-last pre-last post-last + *ENDOPNAME*/ + (define (op-last xs) + (cond + [(null? xs) (cons xs null)] + [else (cons xs (last xs))])) + (define (pre-last xs) (equal? xs (sort xs <))) + (define (post-last xs r) (equal? r (op-last xs))) + *ENDLIBSPEC*/ + fn last(&mut self) -> Option<&T> { + EagerSortedVec::last(self) + } + + /*LIBSPEC* + /*OPNAME* + nth op-nth pre-nth post-nth + *ENDOPNAME*/ + (define (op-nth xs n) + (cond + [(>= n (length xs)) (cons xs null)] + [(< n 0) (cons xs null)] + [else (cons xs (list-ref xs n))])) + (define (pre-nth xs) (equal? xs (sort xs <))) + (define (post-nth xs n r) (equal? r (op-nth xs n))) + *ENDLIBSPEC*/ + fn nth(&mut self, n: usize) -> Option<&T> { + EagerSortedVec::iter(self).nth(n) + } +} + +fn abstraction<T>(v: EagerSortedVec<T>) -> ConsList<T> +where T: Ord +{ + let list: ConsList<T> = ConsList::from(v.to_vec()); + list +} + +proptest! { + #![proptest_config(ProptestConfig { + cases: 100, .. ProptestConfig::default() + })] + + #[test] + fn test_eager_sorted_vec_len(ref mut v in eager_sorted_vec(".*", 0..100)) { + let abs_list = abstraction(v.clone()); + //pre + assert_eq!(abs_list, abs_list.sort()); + //post + assert_eq!(Container::<String>::len(v), abs_list.len()); + assert_eq!(abstraction(v.clone()), abs_list); + } + + #[test] + fn test_eager_sorted_vec_contains(ref mut v in eager_sorted_vec(".*", 0..100), a in ".*") { + let abs_list = abstraction(v.clone()); + //pre + assert_eq!(abs_list, abs_list.sort()); + //post + assert_eq!(Container::<String>::contains(v, &a), contains(&abs_list, &a)); + assert_eq!(abstraction(v.clone()), abs_list); + } + + #[test] + fn test_eager_sorted_vec_is_empty(ref mut v in eager_sorted_vec(".*", 0..100)) { + let abs_list = abstraction(v.clone()); + //pre + assert_eq!(abs_list, abs_list.sort()); + //post + assert_eq!(Container::<String>::is_empty(v), abs_list.is_empty()); + assert_eq!(abstraction(v.clone()), abs_list); + } + + #[test] + fn test_eager_sorted_vec_insert(ref mut v in eager_sorted_vec(".*", 0..100), a in ".*") { + let abs_list = abstraction(v.clone()); + //pre + assert_eq!(abs_list, abs_list.sort()); + //post + let after_list = abs_list.append(conslist![a.clone()]).sort(); + Container::<String>::insert(v, a.clone()); + assert_eq!(abstraction(v.clone()), after_list); + } + + #[test] + fn test_eager_sorted_vec_clear(ref mut v in eager_sorted_vec(".*", 0..100)) { + let abs_list = abstraction(v.clone()); + //pre + assert_eq!(abs_list, abs_list.sort()); + //post + let after_list = clear(&abs_list); + Container::<String>::clear(v); + assert_eq!(abstraction(v.clone()), after_list); + } + + #[test] + fn test_eager_sorted_vec_remove(ref mut v in eager_sorted_vec(".*", 0..100), a in ".*") { + let abs_list = abstraction(v.clone()); + //pre + assert_eq!(abs_list, abs_list.sort()); + //post + let (after_list, abs_elem) = remove(&abs_list, a.clone()); + let elem = Container::<String>::remove(v, a.clone()); + assert_eq!(abstraction(v.clone()), after_list); + assert_eq!(elem, abs_elem); + } + + #[test] + fn test_eager_sorted_vec_first(ref mut v in eager_sorted_vec(".*", 0..100)) { + let abs_list = abstraction(v.clone()); + //pre + assert_eq!(abs_list, abs_list.sort()); + //post + let elem = Indexable::<String>::first(v); + let abs_first = first(&abs_list); + assert_eq!(elem, abs_first); + assert_eq!(abstraction(v.clone()), abs_list); + } + + #[test] + fn test_eager_sorted_vec_last(ref mut v in eager_sorted_vec(".*", 0..100)) { + let abs_list = abstraction(v.clone()); + //pre + assert_eq!(abs_list, abs_list.sort()); + //post + let elem = Indexable::<String>::last(v); + let abs_last = last(&abs_list); + assert_eq!(elem, abs_last); + assert_eq!(abstraction(v.clone()), abs_list); + } + + #[test] + fn test_eager_sorted_vec_nth(ref mut v in eager_sorted_vec(".*", 0..100), n in 0usize..100) { + let abs_list = abstraction(v.clone()); + //pre + assert_eq!(abs_list, abs_list.sort()); + //post + let elem = Indexable::<String>::nth(v, n.clone()); + let abs_nth = nth(&abs_list, n); + assert_eq!(elem, abs_nth); + assert_eq!(abstraction(v.clone()), abs_list); + } +}
\ No newline at end of file diff --git a/primrose/src/library/eager_unique_vector.rs b/primrose/src/library/eager_unique_vector.rs new file mode 100644 index 0000000..0f4f389 --- /dev/null +++ b/primrose/src/library/eager_unique_vector.rs @@ -0,0 +1,339 @@ +/*LIBSPEC-NAME* +rust-eager-unique-vec-spec primrose::library::eager_unique_vector::EagerUniqueVec +*ENDLIBSPEC-NAME*/ + +use std::vec::Vec; +use std::slice::Iter; +use std::ops::Deref; +use crate::traits::{Container, Stack, Indexable}; +use std::iter::FromIterator; + +use proptest::prelude::*; +use crate::proptest::strategies::{eager_unique_vec}; +use crate::proptest::*; + +use im::conslist::{ConsList}; +use im::conslist; +use std::sync::Arc; + +// A Unique Vector +#[derive(Debug, Clone)] +pub struct EagerUniqueVec<T> { + v: Vec<T>, +} + +impl<T: PartialEq> EagerUniqueVec<T> { + pub fn from_vec(v: Vec<T>) -> EagerUniqueVec<T> { + let mut vec = Vec::<T>::new(); + for i in v { + if !vec.contains(&i) { + vec.push(i); + } + } + EagerUniqueVec{ v: vec } + } + + pub fn new() -> EagerUniqueVec<T> { + EagerUniqueVec { v: Vec::new() } + } + + pub fn len(&mut self) -> usize { + self.v.len() + } + + pub fn contains(&mut self, x: &T) -> bool { + self.v.contains(x) + } + + pub fn is_empty(&mut self) -> bool { + self.len() == 0 + } + + // Duplicated elements will be discarded + pub fn push(&mut self, value: T) { + if !self.contains(&value) { + self.v.push(value); + } + } + + pub fn pop(&mut self) -> Option<T> { + self.v.pop() + } + + pub fn remove(&mut self, index: usize) -> T { + self.v.remove(index) + } + + pub fn clear(&mut self) { + self.v.clear() + } + + pub fn first(&mut self) -> Option<&T> { + self.v.first() + } + + pub fn last(&mut self) -> Option<&T> { + self.v.last() + } + + pub fn iter(&mut self) -> Iter<'_, T> { + self.v.iter() + } + + pub fn to_vec(self) -> Vec<T> { + self.v + } +} + +/*IMPL* +Container +*ENDIMPL*/ +impl<T: PartialEq> Container<T> for EagerUniqueVec<T> { + + /*LIBSPEC* + /*OPNAME* + len op-len pre-len post-len + *ENDOPNAME*/ + (define (op-len xs) (cons xs (length xs))) + (define (pre-len xs) (equal? xs (remove-duplicates xs))) + (define (post-len xs r) (equal? r (op-len xs))) + *ENDLIBSPEC*/ + fn len(&mut self) -> usize { + EagerUniqueVec::len(self) + } + + /*LIBSPEC* + /*OPNAME* + contains op-contains pre-contains post-contains + *ENDOPNAME*/ + (define (op-contains xs x) + (cond + [(list? (member x xs)) (cons xs #t)] + [else (cons xs #f)])) + (define (pre-contains xs) (equal? xs (remove-duplicates xs))) + (define (post-contains xs x r) (equal? r (op-contains xs x))) + *ENDLIBSPEC*/ + fn contains(&mut self, x: &T) -> bool { + EagerUniqueVec::contains(self, x) // use fully qualified syntax to avoid function name collision + } + + /*LIBSPEC* + /*OPNAME* + is-empty op-is-empty pre-is-empty post-is-empty + *ENDOPNAME*/ + (define (op-is-empty xs) (cons xs (null? xs))) + (define (pre-is-empty xs) (equal? xs (remove-duplicates xs))) + (define (post-is-empty xs r) (equal? r (op-is-empty xs))) + *ENDLIBSPEC*/ + fn is_empty(&mut self) -> bool { + EagerUniqueVec::is_empty(self) + } + + /*LIBSPEC* + /*OPNAME* + clear op-clear pre-clear post-clear + *ENDOPNAME*/ + (define (op-clear xs) null) + (define (pre-clear xs) (equal? xs (remove-duplicates xs))) + (define (post-clear xs r) (equal? r (op-clear xs))) + *ENDLIBSPEC*/ + fn clear(&mut self) { + EagerUniqueVec::clear(self); + } + + /*LIBSPEC* + /*OPNAME* + insert op-insert pre-insert post-insert + *ENDOPNAME*/ + (define (op-insert xs x) (remove-duplicates (append xs (list x)))) + (define (pre-insert xs) (equal? xs (remove-duplicates xs))) + (define (post-insert xs x ys) (equal? ys (op-insert xs x))) + *ENDLIBSPEC*/ + fn insert(&mut self, elt: T) { + EagerUniqueVec::push(self, elt); + } + + /*LIBSPEC* + /*OPNAME* + remove op-remove pre-remove post-remove + *ENDOPNAME*/ + (define (op-remove xs x) + (cond + [(list? (member x xs)) (cons (remove x xs) x)] + [else (cons xs null)])) + (define (pre-remove xs) (equal? xs (remove-duplicates xs))) + (define (post-remove xs r) (equal? r (op-remove xs))) + *ENDLIBSPEC*/ + fn remove(&mut self, elt: T) -> Option<T> { + match self.iter().position(|x| *x == elt) { + Some(index) => { + Some(self.remove(index)) + }, + None => None + } + } +} + +/*IMPL* +Indexable +*ENDIMPL*/ +impl<T: PartialEq> Indexable<T> for EagerUniqueVec<T> { + /*LIBSPEC* + /*OPNAME* + first op-first pre-first post-first + *ENDOPNAME*/ + (define (op-first xs) + (cond + [(null? xs) (cons xs null)] + [else (cons xs (first xs))])) + (define (pre-first xs) #t) + (define (post-first xs r) (equal? r (op-first xs))) + *ENDLIBSPEC*/ + fn first(&mut self) -> Option<&T> { + EagerUniqueVec::first(self) + } + + /*LIBSPEC* + /*OPNAME* + last op-last pre-last post-last + *ENDOPNAME*/ + (define (op-last xs) + (cond + [(null? xs) (cons xs null)] + [else (cons xs (last xs))])) + (define (pre-last xs) #t) + (define (post-last xs r) (equal? r (op-last xs))) + *ENDLIBSPEC*/ + fn last(&mut self) -> Option<&T> { + EagerUniqueVec::last(self) + } + + /*LIBSPEC* + /*OPNAME* + nth op-nth pre-nth post-nth + *ENDOPNAME*/ + (define (op-nth xs n) + (cond + [(>= n (length xs)) (cons xs null)] + [(< n 0) (cons xs null)] + [else (cons xs (list-ref xs n))])) + (define (pre-nth xs) #t) + (define (post-nth xs n r) (equal? r (op-nth xs n))) + *ENDLIBSPEC*/ + fn nth(&mut self, n: usize) -> Option<&T> { + EagerUniqueVec::iter(self).nth(n) + } +} + +fn abstraction<T>(v: EagerUniqueVec<T>) -> ConsList<T> +where T: PartialEq +{ + let list: ConsList<T> = ConsList::from(v.to_vec()); + list +} + +proptest! { + #![proptest_config(ProptestConfig { + cases: 100, .. ProptestConfig::default() + })] + + #[test] + fn test_eager_unique_vec_len(ref mut v in eager_unique_vec(".*", 0..100)) { + let abs_list = abstraction(v.clone()); + //pre + assert_eq!(abs_list, unique(&abs_list)); + //post + assert_eq!(Container::<String>::len(v), abs_list.len()); + assert_eq!(abstraction(v.clone()), abs_list); + } + + #[test] + fn test_eager_unique_vec_contains(ref mut v in eager_unique_vec(".*", 0..100), a in ".*") { + let abs_list = abstraction(v.clone()); + //pre + assert_eq!(abs_list, unique(&abs_list)); + //post + assert_eq!(Container::<String>::contains(v, &a), contains(&abs_list, &a)); + assert_eq!(abstraction(v.clone()), abs_list); + } + + #[test] + fn test_eager_unique_vec_is_empty(ref mut v in eager_unique_vec(".*", 0..100)) { + let abs_list = abstraction(v.clone()); + //pre + assert_eq!(abs_list, unique(&abs_list)); + //post + assert_eq!(Container::<String>::is_empty(v), abs_list.is_empty()); + assert_eq!(abstraction(v.clone()), abs_list); + } + + #[test] + fn test_eager_unique_vec_insert(ref mut v in eager_unique_vec(".*", 0..100), a in ".*") { + let abs_list = abstraction(v.clone()); + //pre + assert_eq!(abs_list, unique(&abs_list)); + //post + let after_list = unique(&abs_list.append(conslist![a.clone()])); + Container::<String>::insert(v, a.clone()); + assert_eq!(abstraction(v.clone()), after_list); + } + + #[test] + fn test_eager_unique_vec_clear(ref mut v in eager_unique_vec(".*", 0..100)) { + let abs_list = abstraction(v.clone()); + //pre + assert_eq!(abs_list, unique(&abs_list)); + //post + let after_list = clear(&abs_list); + Container::<String>::clear(v); + assert_eq!(abstraction(v.clone()), after_list); + } + + #[test] + fn test_eager_unique_vec_remove(ref mut v in eager_unique_vec(".*", 0..100), a in ".*") { + let abs_list = abstraction(v.clone()); + //pre + assert_eq!(abs_list, unique(&abs_list)); + //post + let (after_list, abs_elem) = remove(&abs_list, a.clone()); + let elem = Container::<String>::remove(v, a.clone()); + assert_eq!(abstraction(v.clone()), after_list); + assert_eq!(elem, abs_elem); + } + + #[test] + fn test_eager_unique_vec_first(ref mut v in eager_unique_vec(".*", 0..100)) { + let abs_list = abstraction(v.clone()); + //pre + assert_eq!(abs_list, unique(&abs_list)); + //post + let elem = Indexable::<String>::first(v); + let abs_first = first(&abs_list); + assert_eq!(elem, abs_first); + assert_eq!(abstraction(v.clone()), abs_list); + } + + #[test] + fn test_eager_unique_vec_last(ref mut v in eager_unique_vec(".*", 0..100)) { + let abs_list = abstraction(v.clone()); + //pre + assert_eq!(abs_list, unique(&abs_list)); + //post + let elem = Indexable::<String>::last(v); + let abs_last = last(&abs_list); + assert_eq!(elem, abs_last); + assert_eq!(abstraction(v.clone()), abs_list); + } + + #[test] + fn test_eager_unique_vec_nth(ref mut v in eager_unique_vec(".*", 0..100), n in 0usize..100) { + let abs_list = abstraction(v.clone()); + //pre + assert_eq!(abs_list, unique(&abs_list)); + //post + let elem = Indexable::<String>::nth(v, n.clone()); + let abs_nth = nth(&abs_list, n); + assert_eq!(elem, abs_nth); + assert_eq!(abstraction(v.clone()), abs_list); + } +}
\ No newline at end of file diff --git a/primrose/src/library/hashset.rs b/primrose/src/library/hashset.rs new file mode 100644 index 0000000..a41b2e6 --- /dev/null +++ b/primrose/src/library/hashset.rs @@ -0,0 +1,199 @@ +/*LIBSPEC-NAME* +rust-hashset-spec std::collections::HashSet +*ENDLIBSPEC-NAME*/ + +use std::collections::HashSet; +use std::hash::Hash; +use crate::traits::Container; + +use crate::proptest::*; +use proptest::prelude::*; +use proptest::collection::hash_set; + +use im::conslist::{ConsList}; +use im::conslist; +use std::sync::Arc; +use std::iter::FromIterator; + +/*IMPL* +Container +*ENDIMPL*/ +impl<T: Ord + Hash> Container<T> for HashSet<T> { + + /*LIBSPEC* + /*OPNAME* + len op-len pre-len post-len + *ENDOPNAME*/ + (define (op-len xs) (cons xs (length xs))) + (define (pre-len xs) (equal? xs (remove-duplicates (sort xs <)))) + (define (post-len xs r) (equal? r (op-len xs))) + *ENDLIBSPEC*/ + fn len(&mut self) -> usize { + HashSet::len(self) + } + + /*LIBSPEC* + /*OPNAME* + contains op-contains pre-contains post-contains + *ENDOPNAME*/ + (define (op-contains xs x) + (cond + [(list? (member x xs)) (cons xs #t)] + [else (cons xs #f)])) + (define (pre-contains xs) (equal? xs (remove-duplicates (sort xs <)))) + (define (post-contains xs x r) (equal? r (op-contains xs x))) + *ENDLIBSPEC*/ + fn contains(&mut self, x: &T) -> bool { + HashSet::contains(self, x) + } + + /*LIBSPEC* + /*OPNAME* + is-empty op-is-empty pre-is-empty post-is-empty + *ENDOPNAME*/ + (define (op-is-empty xs) (cons xs (null? xs))) + (define (pre-is-empty xs) (equal? xs (remove-duplicates (sort xs <)))) + (define (post-is-empty xs r) (equal? r (op-is-empty xs))) + *ENDLIBSPEC*/ + fn is_empty(&mut self) -> bool { + HashSet::is_empty(self) + } + + /*LIBSPEC* + /*OPNAME* + clear op-clear pre-clear post-clear + *ENDOPNAME*/ + (define (op-clear xs) null) + (define (pre-clear xs) (equal? xs (remove-duplicates (sort xs <)))) + (define (post-clear xs r) (equal? r (op-clear xs))) + *ENDLIBSPEC*/ + fn clear(&mut self) { + HashSet::clear(self); + } + + /*LIBSPEC* + /*OPNAME* + insert op-insert pre-insert post-insert + *ENDOPNAME*/ + (define (op-insert xs x) (remove-duplicates (sort (append xs (list x)) <))) + (define (pre-insert xs) (equal? xs (remove-duplicates (sort xs <)))) + (define (post-insert xs x ys) (equal? ys (op-insert xs x))) + *ENDLIBSPEC*/ + fn insert(&mut self, elt: T) { + HashSet::insert(self, elt); + } + + /*LIBSPEC* + /*OPNAME* + remove op-remove pre-remove post-remove + *ENDOPNAME*/ + (define (op-remove xs x) + (cond + [(list? (member x xs)) (cons (remove x xs) x)] + [else (cons xs null)])) + (define (pre-remove xs) (equal? xs (remove-duplicates (sort xs <)))) + (define (post-remove xs r) (equal? r (op-remove xs))) + *ENDLIBSPEC*/ + fn remove(&mut self, elt: T) -> Option<T> { + match HashSet::remove(self, &elt) { + true => Some(elt), + false => None + } + } +} + +fn abstraction<T: Ord>(h: HashSet<T>) -> ConsList<T> { + let list: ConsList<T> = ConsList::from_iter(h); + list.sort() +} + +proptest!{ + #![proptest_config(ProptestConfig { + cases: 100, .. ProptestConfig::default() + })] + + #[test] + fn test_hashset_len(ref mut h in hash_set(".*", 0..100)) { + let abs_list = abstraction(h.clone()); + // pre: our list model is a sorted and unique list + assert_eq!(abs_list, unique(&abs_list.sort())); + //post + assert_eq!(Container::<String>::len(h), abs_list.len()); + assert_eq!(abstraction(h.clone()), abs_list); + } + + #[test] + fn test_hashset_contains(ref mut h in hash_set(".*", 0..100), a in ".*") { + let abs_list = abstraction(h.clone()); + //pre + assert_eq!(abs_list, unique(&abs_list.sort())); + //post + assert_eq!(Container::<String>::contains(h, &a), contains(&abs_list, &a)); + assert_eq!(abstraction(h.clone()), abs_list); + } + + #[test] + fn test_hashset_is_empty(ref mut h in hash_set(".*", 0..100)) { + let abs_list = abstraction(h.clone()); + //pre + assert_eq!(abs_list, unique(&abs_list.sort())); + //post + assert_eq!(Container::<String>::is_empty(h), abs_list.is_empty()); + assert_eq!(abstraction(h.clone()), abs_list); + } + + #[test] + fn test_hashset_insert(ref mut h in hash_set(".*", 0..100), a in ".*") { + let abs_list = abstraction(h.clone()); + //pre + assert_eq!(abs_list, unique(&abs_list.sort())); + //post + let after_list = unique(&abs_list.append(conslist![a.clone()]).sort()); + Container::<String>::insert(h, a.clone()); + assert_eq!(abstraction(h.clone()), after_list); + } + + #[test] + fn test_hash_clear(ref mut h in hash_set(".*", 0..100)) { + let abs_list = abstraction(h.clone()); + //pre + assert_eq!(abs_list, unique(&abs_list.sort())); + //post + let after_list = clear(&abs_list); + Container::<String>::clear(h); + assert_eq!(abstraction(h.clone()), after_list); + } + + #[test] + fn test_hashset_remove(ref mut h in hash_set(".*", 0..100), a in ".*") { + let abs_list = abstraction(h.clone()); + //pre + assert_eq!(abs_list, unique(&abs_list.sort())); + //post + let (after_list, abs_elem) = remove(&abs_list, a.clone()); + let elem = Container::<String>::remove(h, a.clone()); + assert_eq!(abstraction(h.clone()), after_list); + assert_eq!(elem, abs_elem); + } +} + +// #[cfg(test)] +// mod tests { +// use crate::traits::Container; +// use std::collections::HashSet; + +// #[test] +// fn test_hashset_container_trait() { +// let set : &mut dyn Container<u32> = &mut HashSet::<u32>::new(); +// assert_eq!(set.len(), 0); +// set.insert(1); +// set.insert(4); +// assert_eq!(set.len(), 2); +// assert_eq!(set.remove(9), None); +// assert_eq!(set.remove(1), Some(1)); +// assert_eq!(set.len(), 1); +// assert!(set.contains(&4)); +// set.clear(); +// assert_eq!(set.len(), 0); +// } +// }
\ No newline at end of file diff --git a/primrose/src/library/lazy_sorted_vector.rs b/primrose/src/library/lazy_sorted_vector.rs new file mode 100644 index 0000000..4409f44 --- /dev/null +++ b/primrose/src/library/lazy_sorted_vector.rs @@ -0,0 +1,365 @@ +/*LIBSPEC-NAME* +rust-lazy-sorted-vec-spec primrose::library::lazy_sorted_vector::LazySortedVec +*ENDLIBSPEC-NAME*/ + +use std::vec::Vec; +use std::slice::Iter; +use std::ops::Deref; +use crate::traits::{Container, Stack, Indexable}; +use std::iter::FromIterator; + +use proptest::prelude::*; +use crate::proptest::strategies::{lazy_sorted_vec}; +use crate::proptest::*; + +use im::conslist::{ConsList}; +use im::conslist; +use std::sync::Arc; + +// A Sorted Vector +#[derive(Debug, Clone)] +pub struct LazySortedVec<T> { + v: Vec<T>, + modified: bool +} + +impl<T: Ord> LazySortedVec<T> { + pub fn from_vec(mut v: Vec<T>) -> LazySortedVec<T> { + v.sort(); + LazySortedVec{ v: v, modified: false } + } + + pub fn new() -> LazySortedVec<T> { + LazySortedVec { v: Vec::new(), modified: false } + } + + pub fn len(&mut self) -> usize { + if (self.modified) { + self.v.sort(); + self.modified = false; + } + self.v.len() + } + + pub fn contains(&mut self, x: &T) -> bool { + if (self.modified) { + self.v.sort(); + self.modified = false; + } + self.v.binary_search(x).is_ok() + } + + pub fn is_empty(&mut self) -> bool { + if (self.modified) { + self.v.sort(); + self.modified = false; + } + self.len() == 0 + } + + pub fn push(&mut self, value: T) { + self.v.push(value); + self.modified = true; + } + + pub fn pop(&mut self) -> Option<T> { + if (self.modified) { + self.v.sort(); + self.modified = false; + } + self.v.pop() + } + + pub fn remove(&mut self, index: usize) -> T { + if (self.modified) { + self.v.sort(); + self.modified = false; + } + self.v.remove(index) + } + + pub fn clear(&mut self) { + self.v.clear() + } + + pub fn first(&mut self) -> Option<&T> { + if (self.modified) { + self.v.sort(); + self.modified = false; + } + self.v.first() + } + + pub fn last(&mut self) -> Option<&T> { + if (self.modified) { + self.v.sort(); + self.modified = false; + } + self.v.last() + } + + pub fn get(&mut self, index: usize) -> Option<&T> { + self.v.get(index) + } + + pub fn iter(&mut self) -> Iter<T> { + self.v.iter() + } + + pub fn to_vec(self) -> Vec<T> { + self.v + } +} + +/*IMPL* +Container +*ENDIMPL*/ +impl<T: Ord> Container<T> for LazySortedVec<T> { + + /*LIBSPEC* + /*OPNAME* + len op-len pre-len post-len + *ENDOPNAME*/ + (define (op-len xs) (cons xs (length xs))) + (define (pre-len xs) (equal? xs (sort xs <))) + (define (post-len xs r) (equal? r (op-len xs))) + *ENDLIBSPEC*/ + fn len(&mut self) -> usize { + LazySortedVec::len(self) + } + + /*LIBSPEC* + /*OPNAME* + contains op-contains pre-contains post-contains + *ENDOPNAME*/ + (define (op-contains xs x) + (cond + [(list? (member x xs)) (cons xs #t)] + [else (cons xs #f)])) + (define (pre-contains xs) (equal? xs (sort xs <))) + (define (post-contains xs x r) (equal? r (op-contains xs x))) + *ENDLIBSPEC*/ + fn contains(&mut self, x: &T) -> bool { + LazySortedVec::contains(self, x) + } + + /*LIBSPEC* + /*OPNAME* + is-empty op-is-empty pre-is-empty post-is-empty + *ENDOPNAME*/ + (define (op-is-empty xs) (cons xs (null? xs))) + (define (pre-is-empty xs) (equal? xs (sort xs <))) + (define (post-is-empty xs r) (equal? r (op-is-empty xs))) + *ENDLIBSPEC*/ + fn is_empty(&mut self) -> bool { + LazySortedVec::is_empty(self) + } + + /*LIBSPEC* + /*OPNAME* + clear op-clear pre-clear post-clear + *ENDOPNAME*/ + (define (op-clear xs) null) + (define (pre-clear xs) (equal? xs (sort xs <))) + (define (post-clear xs r) (equal? r (op-clear xs))) + *ENDLIBSPEC*/ + fn clear(&mut self) { + LazySortedVec::clear(self); + } + + /*LIBSPEC* + /*OPNAME* + insert op-insert pre-insert post-insert + *ENDOPNAME*/ + (define (op-insert xs x) (sort (append xs (list x)) <)) + (define (pre-insert xs) (equal? xs (sort xs <))) + (define (post-insert xs x ys) (equal? ys (op-insert xs x))) + *ENDLIBSPEC*/ + fn insert(&mut self, elt: T) { + LazySortedVec::push(self, elt); + } + + /*LIBSPEC* + /*OPNAME* + remove op-remove pre-remove post-remove + *ENDOPNAME*/ + (define (op-remove xs x) + (cond + [(list? (member x xs)) (cons (remove x xs) x)] + [else (cons xs null)])) + (define (pre-remove xs) (equal? xs (sort xs <))) + (define (post-remove xs r) (equal? r (op-remove xs))) + *ENDLIBSPEC*/ + fn remove(&mut self, elt: T) -> Option<T> { + match self.iter().position(|x| *x == elt) { + Some(index) => { + Some(self.remove(index)) + }, + None => None + } + } +} + +/*IMPL* +Indexable +*ENDIMPL*/ +impl<T: Ord> Indexable<T> for LazySortedVec<T> { + /*LIBSPEC* + /*OPNAME* + first op-first pre-first post-first + *ENDOPNAME*/ + (define (op-first xs) + (cond + [(null? xs) (cons xs null)] + [else (cons xs (first xs))])) + (define (pre-first xs) (equal? xs (sort xs <))) + (define (post-first xs r) (equal? r (op-first xs))) + *ENDLIBSPEC*/ + fn first(&mut self) -> Option<&T> { + LazySortedVec::first(self) + } + + /*LIBSPEC* + /*OPNAME* + last op-last pre-last post-last + *ENDOPNAME*/ + (define (op-last xs) + (cond + [(null? xs) (cons xs null)] + [else (cons xs (last xs))])) + (define (pre-last xs) (equal? xs (sort xs <))) + (define (post-last xs r) (equal? r (op-last xs))) + *ENDLIBSPEC*/ + fn last(&mut self) -> Option<&T> { + LazySortedVec::last(self) + } + + /*LIBSPEC* + /*OPNAME* + nth op-nth pre-nth post-nth + *ENDOPNAME*/ + (define (op-nth xs n) + (cond + [(>= n (length xs)) (cons xs null)] + [(< n 0) (cons xs null)] + [else (cons xs (list-ref xs n))])) + (define (pre-nth xs) (equal? xs (sort xs <))) + (define (post-nth xs n r) (equal? r (op-nth xs n))) + *ENDLIBSPEC*/ + fn nth(&mut self, n: usize) -> Option<&T> { + LazySortedVec::iter(self).nth(n) + } +} + +fn abstraction<T>(v: LazySortedVec<T>) -> ConsList<T> +where T: Ord +{ + let list: ConsList<T> = ConsList::from(v.to_vec()); + list.sort() +} + +proptest! { + #![proptest_config(ProptestConfig { + cases: 100, .. ProptestConfig::default() + })] + + #[test] + fn test_lazy_sorted_vec_len(ref mut v in lazy_sorted_vec(".*", 0..100)) { + let abs_list = abstraction(v.clone()); + //pre + assert_eq!(abs_list, abs_list.sort()); + //post + assert_eq!(Container::<String>::len(v), abs_list.len()); + assert_eq!(abstraction(v.clone()), abs_list); + } + + #[test] + fn test_lazy_sorted_vec_contains(ref mut v in lazy_sorted_vec(".*", 0..100), a in ".*") { + let abs_list = abstraction(v.clone()); + //pre + assert_eq!(abs_list, abs_list.sort()); + //post + assert_eq!(Container::<String>::contains(v, &a), contains(&abs_list, &a)); + assert_eq!(abstraction(v.clone()), abs_list); + } + + #[test] + fn test_lazy_sorted_vec_is_empty(ref mut v in lazy_sorted_vec(".*", 0..100)) { + let abs_list = abstraction(v.clone()); + //pre + assert_eq!(abs_list, abs_list.sort()); + //post + assert_eq!(Container::<String>::is_empty(v), abs_list.is_empty()); + assert_eq!(abstraction(v.clone()), abs_list); + } + + #[test] + fn test_lazy_sorted_vec_insert(ref mut v in lazy_sorted_vec(".*", 0..100), a in ".*") { + let abs_list = abstraction(v.clone()); + //pre + assert_eq!(abs_list, abs_list.sort()); + //post + let after_list = abs_list.append(conslist![a.clone()]).sort(); + Container::<String>::insert(v, a.clone()); + assert_eq!(abstraction(v.clone()), after_list); + } + + #[test] + fn test_lazy_sorted_vec_clear(ref mut v in lazy_sorted_vec(".*", 0..100)) { + let abs_list = abstraction(v.clone()); + //pre + assert_eq!(abs_list, abs_list.sort()); + //post + let after_list = clear(&abs_list); + Container::<String>::clear(v); + assert_eq!(abstraction(v.clone()), after_list); + } + + #[test] + fn test_lazy_sorted_vec_remove(ref mut v in lazy_sorted_vec(".*", 0..100), a in ".*") { + let abs_list = abstraction(v.clone()); + //pre + assert_eq!(abs_list, abs_list.sort()); + //post + let (after_list, abs_elem) = remove(&abs_list, a.clone()); + let elem = Container::<String>::remove(v, a.clone()); + assert_eq!(abstraction(v.clone()), after_list); + assert_eq!(elem, abs_elem); + } + + #[test] + fn test_lazy_sorted_vec_first(ref mut v in lazy_sorted_vec(".*", 0..100)) { + let abs_list = abstraction(v.clone()); + //pre + assert_eq!(abs_list, abs_list.sort()); + //post + let elem = Indexable::<String>::first(v); + let abs_first = first(&abs_list); + assert_eq!(elem, abs_first); + assert_eq!(abstraction(v.clone()), abs_list); + } + + #[test] + fn test_lazy_sorted_vec_last(ref mut v in lazy_sorted_vec(".*", 0..100)) { + let abs_list = abstraction(v.clone()); + //pre + assert_eq!(abs_list, abs_list.sort()); + //post + let elem = Indexable::<String>::last(v); + let abs_last = last(&abs_list); + assert_eq!(elem, abs_last); + assert_eq!(abstraction(v.clone()), abs_list); + } + + #[test] + fn test_lazy_sorted_vec_nth(ref mut v in lazy_sorted_vec(".*", 0..100), n in 0usize..100) { + let abs_list = abstraction(v.clone()); + //pre + assert_eq!(abs_list, abs_list.sort()); + //post + let elem = Indexable::<String>::nth(v, n.clone()); + let abs_nth = nth(&abs_list, n); + assert_eq!(elem, abs_nth); + assert_eq!(abstraction(v.clone()), abs_list); + } +}
\ No newline at end of file diff --git a/primrose/src/library/lazy_unique_vector.rs b/primrose/src/library/lazy_unique_vector.rs new file mode 100644 index 0000000..07175be --- /dev/null +++ b/primrose/src/library/lazy_unique_vector.rs @@ -0,0 +1,366 @@ +/*LIBSPEC-NAME* +rust-lazy-unique-vec-spec primrose::library::lazy_unique_vector::LazyUniqueVec +*ENDLIBSPEC-NAME*/ + +use std::vec::Vec; +use std::slice::Iter; +use std::ops::Deref; +use crate::traits::{Container, Stack, Indexable}; + +use proptest::prelude::*; +use crate::proptest::strategies::{lazy_unique_vec}; +use crate::proptest::*; + +use im::conslist::{ConsList}; +use im::conslist; +use std::sync::Arc; + +// A Unique and Ascending Vector +#[derive(Debug, Clone)] +pub struct LazyUniqueVec<T> { + v: Vec<T>, + modified: bool +} + +impl<T: Ord> LazyUniqueVec<T> { + pub fn new() -> LazyUniqueVec<T> { + LazyUniqueVec { + v: Vec::new(), + modified: false + } + } + + pub fn from_vec(mut v: Vec<T>) -> LazyUniqueVec<T> { + v.sort(); + v.dedup(); + LazyUniqueVec{ v: v, modified: false } + } + + pub fn len(&mut self) -> usize { + if (self.modified) { + self.v.sort(); + self.v.dedup(); + self.modified = false; + } + self.v.len() + } + + pub fn contains(&mut self, x: &T) -> bool { + if (self.modified) { + self.v.sort(); + self.v.dedup(); + self.modified = false; + } + self.v.binary_search(x).is_ok() + } + + pub fn is_empty(&mut self) -> bool { + if (self.modified) { + self.v.sort(); + self.v.dedup(); + self.modified = false; + } + self.len() == 0 + } + + // Duplicated elements will be discarded + pub fn push(&mut self, value: T) { + self.v.push(value); + self.modified = true; + } + + pub fn pop(&mut self) -> Option<T> { + if (self.modified) { + self.v.sort(); + self.v.dedup(); + self.modified = false; + } + self.v.pop() + } + + pub fn remove(&mut self, index: usize) -> T { + if (self.modified) { + self.v.sort(); + self.v.dedup(); + self.modified = false; + } + self.v.remove(index) + } + + pub fn clear(&mut self) { + self.v.clear() + } + + pub fn first(&mut self) -> Option<&T> { + if (self.modified) { + self.v.sort(); + self.v.dedup(); + self.modified = false; + } + self.v.first() + } + + pub fn last(&mut self) -> Option<&T> { + if (self.modified) { + self.v.sort(); + self.v.dedup(); + self.modified = false; + } + self.v.last() + } + + pub fn iter(&mut self) -> Iter<'_, T> { + if (self.modified) { + self.v.sort(); + self.v.dedup(); + self.modified = false; + } + self.v.iter() + } + + pub fn to_vec(self) -> Vec<T> { + self.v + } +} + +/*IMPL* +Container +*ENDIMPL*/ +impl<T: Ord> Container<T> for LazyUniqueVec<T> { + + /*LIBSPEC* + /*OPNAME* + len op-len pre-len post-len + *ENDOPNAME*/ + (define (op-len xs) (cons xs (length xs))) + (define (pre-len xs) (equal? xs (remove-duplicates (sort xs <)))) + (define (post-len xs r) (equal? r (op-len xs))) + *ENDLIBSPEC*/ + fn len(&mut self) -> usize { + LazyUniqueVec::len(self) + } + + /*LIBSPEC* + /*OPNAME* + contains op-contains pre-contains post-contains + *ENDOPNAME*/ + (define (op-contains xs x) + (cond + [(list? (member x xs)) (cons xs #t)] + [else (cons xs #f)])) + (define (pre-contains xs) (equal? xs (remove-duplicates (sort xs <)))) + (define (post-contains xs x r) (equal? r (op-contains xs x))) + *ENDLIBSPEC*/ + fn contains(&mut self, x: &T) -> bool { + LazyUniqueVec::contains(self, x) // use fully qualified syntax to avoid function name collision + } + + /*LIBSPEC* + /*OPNAME* + is-empty op-is-empty pre-is-empty post-is-empty + *ENDOPNAME*/ + (define (op-is-empty xs) (cons xs (null? xs))) + (define (pre-is-empty xs) (equal? xs (remove-duplicates (sort xs <)))) + (define (post-is-empty xs r) (equal? r (op-is-empty xs))) + *ENDLIBSPEC*/ + fn is_empty(&mut self) -> bool { + LazyUniqueVec::is_empty(self) + } + + /*LIBSPEC* + /*OPNAME* + clear op-clear pre-clear post-clear + *ENDOPNAME*/ + (define (op-clear xs) null) + (define (pre-clear xs) (equal? xs (remove-duplicates (sort xs <)))) + (define (post-clear xs r) (equal? r (op-clear xs))) + *ENDLIBSPEC*/ + fn clear(&mut self) { + LazyUniqueVec::clear(self); + } + + /*LIBSPEC* + /*OPNAME* + insert op-insert pre-insert post-insert + *ENDOPNAME*/ + (define (op-insert xs x) (remove-duplicates (sort (append xs (list x)) <))) + (define (pre-insert xs) (equal? xs (remove-duplicates (sort xs <)))) + (define (post-insert xs x ys) (equal? ys (op-insert xs x))) + *ENDLIBSPEC*/ + fn insert(&mut self, elt: T) { + LazyUniqueVec::push(self, elt); + } + + /*LIBSPEC* + /*OPNAME* + remove op-remove pre-remove post-remove + *ENDOPNAME*/ + (define (op-remove xs x) + (cond + [(list? (member x xs)) (cons (remove x xs) x)] + [else (cons xs null)])) + (define (pre-remove xs) (equal? xs (remove-duplicates (sort xs <)))) + (define (post-remove xs r) (equal? r (op-remove xs))) + *ENDLIBSPEC*/ + fn remove(&mut self, elt: T) -> Option<T> { + match self.iter().position(|x| *x == elt) { + Some(index) => { + Some(self.remove(index)) + }, + None => None + } + } +} + +/*IMPL* +Indexable +*ENDIMPL*/ +impl<T: Ord> Indexable<T> for LazyUniqueVec<T> { + /*LIBSPEC* + /*OPNAME* + first op-first pre-first post-first + *ENDOPNAME*/ + (define (op-first xs) + (cond + [(null? xs) (cons xs null)] + [else (cons xs (first xs))])) + (define (pre-first xs) (equal? xs (remove-duplicates (sort xs <)))) + (define (post-first xs r) (equal? r (op-first xs))) + *ENDLIBSPEC*/ + fn first(&mut self) -> Option<&T> { + LazyUniqueVec::first(self) + } + + /*LIBSPEC* + /*OPNAME* + last op-last pre-last post-last + *ENDOPNAME*/ + (define (op-last xs) + (cond + [(null? xs) (cons xs null)] + [else (cons xs (last xs))])) + (define (pre-last xs) (equal? xs (remove-duplicates (sort xs <)))) + (define (post-last xs r) (equal? r (op-last xs))) + *ENDLIBSPEC*/ + fn last(&mut self) -> Option<&T> { + LazyUniqueVec::last(self) + } + + /*LIBSPEC* + /*OPNAME* + nth op-nth pre-nth post-nth + *ENDOPNAME*/ + (define (op-nth xs n) + (cond + [(>= n (length xs)) (cons xs null)] + [(< n 0) (cons xs null)] + [else (cons xs (list-ref xs n))])) + (define (pre-nth xs) (equal? xs (remove-duplicates (sort xs <)))) + (define (post-nth n xs r) (equal? r (op-nth xs n))) + *ENDLIBSPEC*/ + fn nth(&mut self, n: usize) -> Option<&T> { + LazyUniqueVec::iter(self).nth(n) + } +} + +fn abstraction<T>(v: LazyUniqueVec<T>) -> ConsList<T> +where T: Ord +{ + let list: ConsList<T> = ConsList::from(v.to_vec()); + unique(&list.sort()) +} + +proptest! { + #![proptest_config(ProptestConfig { + cases: 100, .. ProptestConfig::default() + })] + + #[test] + fn test_lazy_unique_vec_len(ref mut v in lazy_unique_vec(".*", 0..100)) { + let abs_list = abstraction(v.clone()); + //pre + assert_eq!(abs_list, unique(&abs_list.sort())); + //post + assert_eq!(Container::<String>::len(v), abs_list.len()); + assert_eq!(abstraction(v.clone()), abs_list); + } + + #[test] + fn test_lazy_unique_vec_contains(ref mut v in lazy_unique_vec(".*", 0..100), a in ".*") { + let abs_list = abstraction(v.clone()); + //pre + assert_eq!(abs_list, unique(&abs_list.sort())); + //post + assert_eq!(Container::<String>::contains(v, &a), contains(&abs_list, &a)); + assert_eq!(abstraction(v.clone()), abs_list); + } + + #[test] + fn test_lazy_unique_vec_insert(ref mut v in lazy_unique_vec(".*", 0..100), a in ".*") { + let abs_list = abstraction(v.clone()); + //pre + assert_eq!(abs_list, unique(&abs_list.sort())); + //post + let after_list = unique(&abs_list.append(conslist![a.clone()]).sort()); + Container::<String>::insert(v, a.clone()); + assert_eq!(abstraction(v.clone()), after_list); + } + + #[test] + fn test_lazy_vec_is_empty(ref mut v in lazy_unique_vec(".*", 0..100)) { + let abs_list = abstraction(v.clone()); + //pre + assert_eq!(abs_list, unique(&abs_list.sort())); + //post + assert_eq!(Container::<String>::is_empty(v), abs_list.is_empty()); + assert_eq!(abstraction(v.clone()), abs_list); + } + + #[test] + fn test_lazy_unique_vec_remove(ref mut v in lazy_unique_vec(".*", 0..100), a in ".*") { + let abs_list = abstraction(v.clone()); + //pre + assert_eq!(abs_list, unique(&abs_list.sort())); + //post + let (after_list, abs_elem) = remove(&abs_list, a.clone()); + let elem = Container::<String>::remove(v, a.clone()); + assert_eq!(abstraction(v.clone()), after_list); + assert_eq!(elem, abs_elem); + } + + #[test] + fn test_lazy_unique_vec_first(ref mut v in lazy_unique_vec(".*", 0..100)) { + let abs_list = abstraction(v.clone()); + //pre + assert_eq!(abs_list, unique(&abs_list.sort())); + //post + let elem = Indexable::<String>::first(v); + let abs_first = first(&abs_list); + assert_eq!(elem, abs_first); + assert_eq!(abstraction(v.clone()), abs_list); + } + + #[test] + fn test_lazy_unique_vec_last(ref mut v in lazy_unique_vec(".*", 0..100)) { + let abs_list = abstraction(v.clone()); + //pre + assert_eq!(abs_list, unique(&abs_list.sort())); + //post + let elem = Indexable::<String>::last(v); + let abs_last = last(&abs_list); + assert_eq!(elem, abs_last); + assert_eq!(abstraction(v.clone()), abs_list); + } + + #[test] + fn test_lazy_unique_vec_nth(ref mut v in lazy_unique_vec(".*", 0..100), n in 0usize..100) { + let abs_list = abstraction(v.clone()); + //pre + assert_eq!(abs_list, unique(&abs_list.sort())); + //post + let elem = Indexable::<String>::nth(v, n.clone()); + let abs_nth = nth(&abs_list, n); + assert_eq!(elem, abs_nth); + assert_eq!(abstraction(v.clone()), abs_list); + } +}
\ No newline at end of file diff --git a/primrose/src/library/list.rs b/primrose/src/library/list.rs new file mode 100644 index 0000000..fe898b5 --- /dev/null +++ b/primrose/src/library/list.rs @@ -0,0 +1,319 @@ +/*LIBSPEC-NAME* +rust-linked-list-spec std::collections::LinkedList +*ENDLIBSPEC-NAME*/ + +use std::collections::LinkedList; +use std::cmp::Ordering; +use std::marker::PhantomData; +use std::iter::FromIterator; +// nightly features +use std::collections::linked_list::CursorMut; +use crate::traits::{Container, Stack, Indexable}; +use crate::proptest::*; +use proptest::prelude::*; +use proptest::collection::linked_list; + +use im::conslist::{ConsList}; +use im::conslist; +use std::sync::Arc; + +/*IMPL* +Container +*ENDIMPL*/ +impl<T: Ord> Container<T> for LinkedList<T> { + + /*LIBSPEC* + /*OPNAME* + len op-len pre-len post-len + *ENDOPNAME*/ + (define (op-len xs) (cons xs (length xs))) + (define (pre-len xs) #t) + (define (post-len xs r) (equal? r (op-len xs))) + *ENDLIBSPEC*/ + fn len(&mut self) -> usize { + LinkedList::len(self) + } + + /*LIBSPEC* + /*OPNAME* + contains op-contains pre-contains post-contains + *ENDOPNAME*/ + (define (op-contains xs x) + (cond + [(list? (member x xs)) (cons xs #t)] + [else (cons xs #f)])) + (define (pre-contains xs) #t) + (define (post-contains xs x r) (equal? r (op-contains xs x))) + *ENDLIBSPEC*/ + fn contains(&mut self, x: &T) -> bool { + LinkedList::contains(self, x) + } + + /*LIBSPEC* + /*OPNAME* + is-empty op-is-empty pre-is-empty post-is-empty + *ENDOPNAME*/ + (define (op-is-empty xs) (cons xs (null? xs))) + (define (pre-is-empty xs) #t) + (define (post-is-empty xs r) (equal? r (op-is-empty xs))) + *ENDLIBSPEC*/ + fn is_empty(&mut self) -> bool { + LinkedList::is_empty(self) + } + + /*LIBSPEC* + /*OPNAME* + clear op-clear pre-clear post-clear + *ENDOPNAME*/ + (define (op-clear xs) null) + (define (pre-clear xs) #t) + (define (post-clear xs r) (equal? r (op-clear xs))) + *ENDLIBSPEC*/ + fn clear(&mut self) { + LinkedList::clear(self); + } + + /*LIBSPEC* + /*OPNAME* + insert op-insert pre-insert post-insert + *ENDOPNAME*/ + (define (op-insert xs x) (append xs (list x))) + (define (pre-insert xs) #t) + (define (post-insert xs x ys) (equal? ys (op-insert xs x))) + *ENDLIBSPEC*/ + fn insert(&mut self, elt: T) { + LinkedList::push_back(self, elt); + } + + /*LIBSPEC* + /*OPNAME* + remove op-remove pre-remove post-remove + *ENDOPNAME*/ + (define (op-remove xs x) + (cond + [(list? (member x xs)) (cons (remove x xs) x)] + [else (cons xs null)])) + (define (pre-remove xs) #t) + (define (post-remove xs r) (equal? r (op-remove xs))) + *ENDLIBSPEC*/ + fn remove(&mut self, elt: T) -> Option<T> { + let mut c = self.cursor_front_mut(); + loop { + match c.current() { + Some(x) => { + match &elt.cmp(x) { + Ordering::Equal => { + return c.remove_current() + }, + _ => c.move_next() + } + }, + None => { // empty list + return None; + } + } + } + } +} + +/*IMPL* +Stack +*ENDIMPL*/ +impl<T> Stack<T> for LinkedList<T> { + /*LIBSPEC* + /*OPNAME* + push push pre-push post-push + *ENDOPNAME*/ + (define (push xs x) (append xs (list x))) + (define (pre-push xs) #t) + (define (post-push xs x ys) (equal? ys (push xs x))) + *ENDLIBSPEC*/ + fn push(&mut self, elt: T) { + LinkedList::push_back(self, elt); + } + + /*LIBSPEC* + /*OPNAME* + pop pop pre-pop post-pop + *ENDOPNAME*/ + (define (pop xs) + (cond + [(null? xs) (cons xs null)] + [else (cons (take xs (- (length xs) 1)) (last xs))])) + (define (pre-pop xs) #t) + (define (post-pop xs r) (equal? r (pop xs))) + *ENDLIBSPEC*/ + fn pop(&mut self) -> Option<T> { + LinkedList::pop_back(self) + } +} + +/*IMPL* +Indexable +*ENDIMPL*/ +impl<T> Indexable<T> for LinkedList<T> { + /*LIBSPEC* + /*OPNAME* + first op-first pre-first post-first + *ENDOPNAME*/ + (define (op-first xs) + (cond + [(null? xs) (cons xs null)] + [else (cons xs (first xs))])) + (define (pre-first xs) #t) + (define (post-first xs r) (equal? r (op-first xs))) + *ENDLIBSPEC*/ + fn first(&mut self) -> Option<&T> { + LinkedList::front(self) + } + + /*LIBSPEC* + /*OPNAME* + last op-last pre-last post-last + *ENDOPNAME*/ + (define (op-last xs) + (cond + [(null? xs) (cons xs null)] + [else (cons xs (last xs))])) + (define (pre-last xs) #t) + (define (post-last xs r) (equal? r (op-last xs))) + *ENDLIBSPEC*/ + fn last(&mut self) -> Option<&T> { + LinkedList::back(self) + } + + /*LIBSPEC* + /*OPNAME* + nth op-nth pre-nth post-nth + *ENDOPNAME*/ + (define (op-nth xs n) + (cond + [(>= n (length xs)) (cons xs null)] + [(< n 0) (cons xs null)] + [else (cons xs (list-ref xs n))])) + (define (pre-nth xs) #t) + (define (post-nth xs n r) (equal? r (op-nth xs n))) + *ENDLIBSPEC*/ + fn nth(&mut self, n: usize) -> Option<&T> { + LinkedList::iter(self).nth(n) + } +} + +struct Con<T> { + elem_t: PhantomData<T> +} + +pub trait Constructor { + type Impl: ?Sized; + type Interface: ?Sized; + fn new() -> Box<Self::Interface>; +} + +impl<T: 'static + Ord> Constructor for Con<T> { + type Impl = LinkedList::<T>; + type Interface = dyn Container<T>; + fn new() -> Box<Self::Interface> { + Box::new(Self::Impl::new()) + } +} + +fn abstraction<T>(l: LinkedList<T>) -> ConsList<T> { + let list: ConsList<T> = ConsList::from_iter(l); + list +} + +proptest! { + #![proptest_config(ProptestConfig { + cases: 100, .. ProptestConfig::default() + })] + + #[test] + fn test_list_len(ref mut l in linked_list(".*", 0..100)) { + let abs_list = abstraction(l.clone()); + assert_eq!(Container::<String>::len(l), abs_list.len()); + assert_eq!(abstraction(l.clone()), abs_list); + } + + #[test] + fn test_list_contains(ref mut l in linked_list(".*", 0..100), a in ".*") { + let abs_list = abstraction(l.clone()); + assert_eq!(Container::<String>::contains(l, &a), contains(&abs_list, &a)); + assert_eq!(abstraction(l.clone()), abs_list); + } + + #[test] + fn test_list_is_empty(ref mut l in linked_list(".*", 0..100)) { + let abs_list = abstraction(l.clone()); + assert_eq!(Container::<String>::is_empty(l), abs_list.is_empty()); + assert_eq!(abstraction(l.clone()), abs_list); + } + + #[test] + fn test_list_insert(ref mut l in linked_list(".*", 0..100), a in ".*") { + let abs_list = abstraction(l.clone()); + let after_list = abs_list.append(conslist![a.clone()]); + Container::<String>::insert(l, a.clone()); + assert_eq!(abstraction(l.clone()), after_list); + } + + #[test] + fn test_list_clear(ref mut l in linked_list(".*", 0..100)) { + let abs_list = abstraction(l.clone()); + let after_list = clear(&abs_list); + Container::<String>::clear(l); + assert_eq!(abstraction(l.clone()), after_list); + } + + #[test] + fn test_list_remove(ref mut l in linked_list(".*", 0..100), a in ".*") { + let abs_list = abstraction(l.clone()); + let (after_list, abs_elem) = remove(&abs_list, a.clone()); + let elem = Container::<String>::remove(l, a.clone()); + assert_eq!(abstraction(l.clone()), after_list); + assert_eq!(elem, abs_elem); + } + + #[test] + fn test_list_first(ref mut l in linked_list(".*", 0..100)) { + let abs_list = abstraction(l.clone()); + let elem = Indexable::<String>::first(l); + let abs_first = first(&abs_list); + assert_eq!(elem, abs_first); + assert_eq!(abstraction(l.clone()), abs_list); + } + + #[test] + fn test_list_last(ref mut l in linked_list(".*", 0..100)) { + let abs_list = abstraction(l.clone()); + let elem = Indexable::<String>::last(l); + let abs_last = last(&abs_list); + assert_eq!(elem, abs_last); + assert_eq!(abstraction(l.clone()), abs_list); + } + + #[test] + fn test_list_nth(ref mut l in linked_list(".*", 0..100), n in 0usize..100) { + let abs_list = abstraction(l.clone()); + let elem = Indexable::<String>::nth(l, n.clone()); + let abs_nth = nth(&abs_list, n); + assert_eq!(elem, abs_nth); + assert_eq!(abstraction(l.clone()), abs_list); + } + + #[test] + fn test_list_push(ref mut l in linked_list(".*", 0..100), a in ".*") { + let abs_list = abstraction(l.clone()); + let after_list = push(&abs_list, a.clone()); + Stack::<String>::push(l, a.clone()); + assert_eq!(abstraction(l.clone()), after_list); + } + + #[test] + fn test_list_pop(ref mut l in linked_list(".*", 0..100)) { + let abs_list = abstraction(l.clone()); + let (after_list, abs_elem) = pop(&abs_list); + let elem = Stack::<String>::pop(l); + assert_eq!(abstraction(l.clone()), after_list); + assert_eq!(elem.map(|x| Arc::new(x)), abs_elem); + } +}
\ No newline at end of file diff --git a/primrose/src/library/mod.rs b/primrose/src/library/mod.rs new file mode 100644 index 0000000..238a3f4 --- /dev/null +++ b/primrose/src/library/mod.rs @@ -0,0 +1,8 @@ +pub mod list; +pub mod vector; +pub mod treeset; +pub mod hashset; +pub mod eager_unique_vector; +pub mod lazy_unique_vector; +pub mod eager_sorted_vector; +pub mod lazy_sorted_vector;
\ No newline at end of file diff --git a/primrose/src/library/treeset.rs b/primrose/src/library/treeset.rs new file mode 100644 index 0000000..0e19f3d --- /dev/null +++ b/primrose/src/library/treeset.rs @@ -0,0 +1,263 @@ +/*LIBSPEC-NAME* +rust-btreeset-spec std::collections::BTreeSet +*ENDLIBSPEC-NAME*/ + +use std::collections::BTreeSet; +use std::iter::FromIterator; +use crate::traits::{Container, Indexable}; +use crate::proptest::*; + +use proptest::prelude::*; +use proptest::collection::btree_set; + +use im::conslist::{ConsList}; +use im::conslist; +use std::sync::Arc; + +/*IMPL* +Container +*ENDIMPL*/ +impl<T: Ord> Container<T> for BTreeSet<T> { + + /*LIBSPEC* + /*OPNAME* + len op-len pre-len post-len + *ENDOPNAME*/ + (define (op-len xs) (cons xs (length xs))) + (define (pre-len xs) (equal? xs (remove-duplicates (sort xs <)))) + (define (post-len xs r) (equal? r (op-len xs))) + *ENDLIBSPEC*/ + fn len(&mut self) -> usize { + BTreeSet::len(self) + } + + /*LIBSPEC* + /*OPNAME* + contains op-contains pre-contains post-contains + *ENDOPNAME*/ + (define (op-contains xs x) + (cond + [(list? (member x xs)) (cons xs #t)] + [else (cons xs #f)])) + (define (pre-contains xs) (equal? xs (remove-duplicates (sort xs <)))) + (define (post-contains xs x r) (equal? r (op-contains xs x))) + *ENDLIBSPEC*/ + fn contains(&mut self, x: &T) -> bool { + BTreeSet::contains(self, x) + } + + /*LIBSPEC* + /*OPNAME* + is-empty op-is-empty pre-is-empty post-is-empty + *ENDOPNAME*/ + (define (op-is-empty xs) (cons xs (null? xs))) + (define (pre-is-empty xs) (equal? xs (remove-duplicates (sort xs <)))) + (define (post-is-empty xs r) (equal? r (op-is-empty xs))) + *ENDLIBSPEC*/ + fn is_empty(&mut self) -> bool { + BTreeSet::is_empty(self) + } + + /*LIBSPEC* + /*OPNAME* + clear op-clear pre-clear post-clear + *ENDOPNAME*/ + (define (op-clear xs) null) + (define (pre-clear xs) (equal? xs (remove-duplicates (sort xs <)))) + (define (post-clear xs r) (equal? r (op-clear xs))) + *ENDLIBSPEC*/ + fn clear(&mut self) { + BTreeSet::clear(self); + } + + /*LIBSPEC* + /*OPNAME* + insert op-insert pre-insert post-insert + *ENDOPNAME*/ + (define (op-insert xs x) (remove-duplicates (sort (append xs (list x)) <))) + (define (pre-insert xs) (equal? xs (remove-duplicates (sort xs <)))) + (define (post-insert xs x ys) (equal? ys (op-insert xs x))) + *ENDLIBSPEC*/ + fn insert(&mut self, elt: T) { + BTreeSet::insert(self, elt); + } + + /*LIBSPEC* + /*OPNAME* + remove op-remove pre-remove post-remove + *ENDOPNAME*/ + (define (op-remove xs x) + (cond + [(list? (member x xs)) (cons (remove x xs) x)] + [else (cons xs null)])) + (define (pre-remove xs) (equal? xs (remove-duplicates (sort xs <)))) + (define (post-remove xs r) (equal? r (op-remove xs))) + *ENDLIBSPEC*/ + fn remove(&mut self, elt: T) -> Option<T> { + match BTreeSet::remove(self, &elt) { + true => Some(elt), + false => None + } + } +} + +/*IMPL* +Indexable +*ENDIMPL*/ +impl<T: Ord> Indexable<T> for BTreeSet<T> { + /*LIBSPEC* + /*OPNAME* + first op-first pre-first post-first + *ENDOPNAME*/ + (define (op-first xs) + (cond + [(null? xs) (cons xs null)] + [else (cons xs (first xs))])) + (define (pre-first xs) (equal? xs (remove-duplicates (sort xs <)))) + (define (post-first xs r) (equal? r (op-first xs))) + *ENDLIBSPEC*/ + fn first(&mut self) -> Option<&T> { + BTreeSet::first(self) + } + + /*LIBSPEC* + /*OPNAME* + last op-last pre-last post-last + *ENDOPNAME*/ + (define (op-last xs) + (cond + [(null? xs) (cons xs null)] + [else (cons xs (last xs))])) + (define (pre-last xs) (equal? xs (remove-duplicates (sort xs <)))) + (define (post-last xs r) (equal? r (op-last xs))) + *ENDLIBSPEC*/ + fn last(&mut self) -> Option<&T> { + BTreeSet::last(self) + } + + /*LIBSPEC* + /*OPNAME* + nth op-nth pre-nth post-nth + *ENDOPNAME*/ + (define (op-nth xs n) + (cond + [(>= n (length xs)) (cons xs null)] + [(< n 0) (cons xs null)] + [else (cons xs (list-ref xs n))])) + (define (pre-nth xs) (equal? xs (remove-duplicates (sort xs <)))) + (define (post-nth n xs r) (equal? r (op-nth xs n))) + *ENDLIBSPEC*/ + fn nth(&mut self, n: usize) -> Option<&T> { + BTreeSet::iter(self).nth(n) + } +} + +fn abstraction<T>(t: BTreeSet<T>) -> ConsList<T> { + let list: ConsList<T> = ConsList::from_iter(t); + list +} + +proptest!{ + #![proptest_config(ProptestConfig { + cases: 100, .. ProptestConfig::default() + })] + #[test] + fn test_btree_len(ref mut t in btree_set(".*", 0..100)) { + let abs_list = abstraction(t.clone()); + // pre: our list model is a sorted and unique list + assert_eq!(abs_list, unique(&abs_list.sort())); + //post + assert_eq!(Container::<String>::len(t), abs_list.len()); + assert_eq!(abstraction(t.clone()), abs_list); + } + + #[test] + fn test_btree_contains(ref mut t in btree_set(".*", 0..100), a in ".*") { + let abs_list = abstraction(t.clone()); + //pre + assert_eq!(abs_list, unique(&abs_list.sort())); + //post + assert_eq!(Container::<String>::contains(t, &a), contains(&abs_list, &a)); + assert_eq!(abstraction(t.clone()), abs_list); + } + + #[test] + fn test_btree_is_empty(ref mut t in btree_set(".*", 0..100)) { + let abs_list = abstraction(t.clone()); + //pre + assert_eq!(abs_list, unique(&abs_list.sort())); + //post + assert_eq!(Container::<String>::is_empty(t), abs_list.is_empty()); + assert_eq!(abstraction(t.clone()), abs_list); + } + + #[test] + fn test_btree_insert(ref mut t in btree_set(".*", 0..100), a in ".*") { + let abs_list = abstraction(t.clone()); + //pre + assert_eq!(abs_list, unique(&abs_list.sort())); + //post + let after_list = unique(&abs_list.append(conslist![a.clone()]).sort()); + Container::<String>::insert(t, a.clone()); + assert_eq!(abstraction(t.clone()), after_list); + } + + #[test] + fn test_btree_clear(ref mut t in btree_set(".*", 0..100)) { + let abs_list = abstraction(t.clone()); + //pre + assert_eq!(abs_list, unique(&abs_list.sort())); + //post + let after_list = clear(&abs_list); + Container::<String>::clear(t); + assert_eq!(abstraction(t.clone()), after_list); + } + + #[test] + fn test_btree_remove(ref mut t in btree_set(".*", 0..100), a in ".*") { + let abs_list = abstraction(t.clone()); + //pre + assert_eq!(abs_list, unique(&abs_list.sort())); + //post + let (after_list, abs_elem) = remove(&abs_list, a.clone()); + let elem = Container::<String>::remove(t, a.clone()); + assert_eq!(abstraction(t.clone()), after_list); + assert_eq!(elem, abs_elem); + } + + #[test] + fn test_btree_first(ref mut t in btree_set(".*", 0..100)) { + let abs_list = abstraction(t.clone()); + //pre + assert_eq!(abs_list, unique(&abs_list.sort())); + //post + let elem = Indexable::<String>::first(t); + let abs_first = first(&abs_list); + assert_eq!(elem, abs_first); + assert_eq!(abstraction(t.clone()), abs_list); + } + + #[test] + fn test_btree_last(ref mut t in btree_set(".*", 0..100)) { + let abs_list = abstraction(t.clone()); + //pre + assert_eq!(abs_list, unique(&abs_list.sort())); + //post + let elem = Indexable::<String>::last(t); + let abs_last = last(&abs_list); + assert_eq!(elem, abs_last); + assert_eq!(abstraction(t.clone()), abs_list); + } + + #[test] + fn test_btree_nth(ref mut t in btree_set(".*", 0..100), n in 0usize..100) { + let abs_list = abstraction(t.clone()); + //pre + assert_eq!(abs_list, unique(&abs_list.sort())); + //post + let elem = Indexable::<String>::nth(t, n.clone()); + let abs_nth = nth(&abs_list, n); + assert_eq!(elem, abs_nth); + assert_eq!(abstraction(t.clone()), abs_list); + } +}
\ No newline at end of file diff --git a/primrose/src/library/vector.rs b/primrose/src/library/vector.rs new file mode 100644 index 0000000..36f2a82 --- /dev/null +++ b/primrose/src/library/vector.rs @@ -0,0 +1,287 @@ +/*LIBSPEC-NAME* +rust-vec-spec std::vec::Vec +*ENDLIBSPEC-NAME*/ + +use std::vec::Vec; +use crate::traits::{Container, Stack, Indexable}; +use crate::proptest::*; + +use proptest::prelude::*; +use proptest::collection::vec; + +use im::conslist::{ConsList}; +use im::conslist; +use std::sync::Arc; + +/*IMPL* +Container +*ENDIMPL*/ +impl<T: PartialEq> Container<T> for Vec<T> { + + /*LIBSPEC* + /*OPNAME* + len op-len pre-len post-len + *ENDOPNAME*/ + (define (op-len xs) (cons xs (length xs))) + (define (pre-len xs) #t) + (define (post-len xs r) (equal? r (op-len xs))) + *ENDLIBSPEC*/ + fn len(&mut self) -> usize { + Vec::len(self) + } + + /*LIBSPEC* + /*OPNAME* + contains op-contains pre-contains post-contains + *ENDOPNAME*/ + (define (op-contains xs x) + (cond + [(list? (member x xs)) (cons xs #t)] + [else (cons xs #f)])) + (define (pre-contains xs) #t) + (define (post-contains xs x r) (equal? r (op-contains xs x))) + *ENDLIBSPEC*/ + fn contains(&mut self, x: &T) -> bool { + <[T]>::contains(self, x) // use fully qualified syntax to avoid function name collision + } + + /*LIBSPEC* + /*OPNAME* + is-empty op-is-empty pre-is-empty post-is-empty + *ENDOPNAME*/ + (define (op-is-empty xs) (cons xs (null? xs))) + (define (pre-is-empty xs) #t) + (define (post-is-empty xs r) (equal? r (op-is-empty xs))) + *ENDLIBSPEC*/ + fn is_empty(&mut self) -> bool { + Vec::is_empty(self) + } + + /*LIBSPEC* + /*OPNAME* + clear op-clear pre-clear post-clear + *ENDOPNAME*/ + (define (op-clear xs) null) + (define (pre-clear xs) #t) + (define (post-clear xs r) (equal? r (op-clear xs))) + *ENDLIBSPEC*/ + fn clear(&mut self) { + Vec::clear(self); + } + + /*LIBSPEC* + /*OPNAME* + insert op-insert pre-insert post-insert + *ENDOPNAME*/ + (define (op-insert xs x) (append xs (list x))) + (define (pre-insert xs) #t) + (define (post-insert xs x ys) (equal? ys (op-insert xs x))) + *ENDLIBSPEC*/ + fn insert(&mut self, elt: T) { + Vec::push(self, elt); + } + + /*LIBSPEC* + /*OPNAME* + remove op-remove pre-remove post-remove + *ENDOPNAME*/ + (define (op-remove xs x) + (cond + [(list? (member x xs)) (cons (remove x xs) x)] + [else (cons xs null)])) + (define (pre-remove xs) #t) + (define (post-remove xs r) (equal? r (op-remove xs))) + *ENDLIBSPEC*/ + fn remove(&mut self, elt: T) -> Option<T> { + match self.iter().position(|x| *x == elt) { + Some(index) => { + Some(self.remove(index)) + }, + None => None + } + } +} + +/*IMPL* +Stack +*ENDIMPL*/ +impl<T> Stack<T> for Vec<T> { + /*LIBSPEC* + /*OPNAME* + push push pre-push post-push + *ENDOPNAME*/ + (define (push xs x) (append xs (list x))) + (define (pre-push xs) #t) + (define (post-push xs x ys) (equal? ys (push xs x))) + *ENDLIBSPEC*/ + fn push(&mut self, elt: T) { + Vec::push(self, elt); + } + + /*LIBSPEC* + /*OPNAME* + pop pop pre-pop post-pop + *ENDOPNAME*/ + (define (pop xs) + (cond + [(null? xs) (cons xs null)] + [else (cons (take xs (- (length xs) 1)) (last xs))])) + (define (pre-pop xs) #t) + (define (post-pop xs r) (equal? r (pop xs))) + *ENDLIBSPEC*/ + fn pop(&mut self) -> Option<T> { + Vec::pop(self) + } +} + +/*IMPL* +Indexable +*ENDIMPL*/ +impl<T> Indexable<T> for Vec<T> { + /*LIBSPEC* + /*OPNAME* + first op-first pre-first post-first + *ENDOPNAME*/ + (define (op-first xs) + (cond + [(null? xs) (cons xs null)] + [else (cons xs (first xs))])) + (define (pre-first xs) #t) + (define (post-first xs r) (equal? r (op-first xs))) + *ENDLIBSPEC*/ + fn first(&mut self) -> Option<&T> { + <[T]>::first(self) + } + + /*LIBSPEC* + /*OPNAME* + last op-last pre-last post-last + *ENDOPNAME*/ + (define (op-last xs) + (cond + [(null? xs) (cons xs null)] + [else (cons xs (last xs))])) + (define (pre-last xs) #t) + (define (post-last xs r) (equal? r (op-last xs))) + *ENDLIBSPEC*/ + fn last(&mut self) -> Option<&T> { + <[T]>::last(self) + } + + /*LIBSPEC* + /*OPNAME* + nth op-nth pre-nth post-nth + *ENDOPNAME*/ + (define (op-nth xs n) + (cond + [(>= n (length xs)) (cons xs null)] + [(< n 0) (cons xs null)] + [else (cons xs (list-ref xs n))])) + (define (pre-nth xs) #t) + (define (post-nth xs n r) (equal? r (op-nth xs n))) + *ENDLIBSPEC*/ + fn nth(&mut self, n: usize) -> Option<&T> { + <[T]>::iter(self).nth(n) + } +} + +fn abstraction<T>(v: Vec<T>) -> ConsList<T> { + let list: ConsList<T> = ConsList::from(v); + list +} + +proptest! { + #![proptest_config(ProptestConfig { + cases: 100, .. ProptestConfig::default() + })] + + #[test] + fn test_vec_len(ref mut v in vec(".*", 0..100)) { + let abs_list = abstraction(v.clone()); + assert_eq!(Container::<String>::len(v), abs_list.len()); + assert_eq!(abstraction(v.clone()), abs_list); + } + + #[test] + fn test_vec_contains(ref mut v in vec(".*", 0..100), a in ".*") { + let abs_list = abstraction(v.clone()); + assert_eq!(Container::<String>::contains(v, &a), contains(&abs_list, &a)); + assert_eq!(abstraction(v.clone()), abs_list); + } + + #[test] + fn test_vec_is_empty(ref mut v in vec(".*", 0..100)) { + let abs_list = abstraction(v.clone()); + assert_eq!(Container::<String>::is_empty(v), abs_list.is_empty()); + assert_eq!(abstraction(v.clone()), abs_list); + } + + #[test] + fn test_vec_insert(ref mut v in vec(".*", 0..100), a in ".*") { + let abs_list = abstraction(v.clone()); + let after_list = abs_list.append(conslist![a.clone()]); + Container::<String>::insert(v, a.clone()); + assert_eq!(abstraction(v.clone()), after_list); + } + + #[test] + fn test_vec_clear(ref mut v in vec(".*", 0..100)) { + let abs_list = abstraction(v.clone()); + let after_list = clear(&abs_list); + Container::<String>::clear(v); + assert_eq!(abstraction(v.clone()), after_list); + } + + #[test] + fn test_vec_remove(ref mut v in vec(".*", 0..100), a in ".*") { + let abs_list = abstraction(v.clone()); + let (after_list, abs_elem) = remove(&abs_list, a.clone()); + let elem = Container::<String>::remove(v, a.clone()); + assert_eq!(abstraction(v.clone()), after_list); + assert_eq!(elem, abs_elem); + } + + #[test] + fn test_vec_first(ref mut v in vec(".*", 0..100)) { + let abs_list = abstraction(v.clone()); + let elem = Indexable::<String>::first(v); + let abs_first = first(&abs_list); + assert_eq!(elem, abs_first); + assert_eq!(abstraction(v.clone()), abs_list); + } + + #[test] + fn test_vec_last(ref mut v in vec(".*", 0..100)) { + let abs_list = abstraction(v.clone()); + let elem = Indexable::<String>::last(v); + let abs_last = last(&abs_list); + assert_eq!(elem, abs_last); + assert_eq!(abstraction(v.clone()), abs_list); + } + + #[test] + fn test_vec_nth(ref mut v in vec(".*", 0..100), n in 0usize..100) { + let abs_list = abstraction(v.clone()); + let elem = Indexable::<String>::nth(v, n.clone()); + let abs_nth = nth(&abs_list, n); + assert_eq!(elem, abs_nth); + assert_eq!(abstraction(v.clone()), abs_list); + } + + #[test] + fn test_vec_push(ref mut v in vec(".*", 0..100), a in ".*") { + let abs_list = abstraction(v.clone()); + let after_list = push(&abs_list, a.clone()); + Stack::<String>::push(v, a.clone()); + assert_eq!(abstraction(v.clone()), after_list); + } + + #[test] + fn test_vec_pop(ref mut v in vec(".*", 0..100)) { + let abs_list = abstraction(v.clone()); + let (after_list, abs_elem) = pop(&abs_list); + let elem = Stack::<String>::pop(v); + assert_eq!(abstraction(v.clone()), after_list); + assert_eq!(elem.map(|x| Arc::new(x)), abs_elem); + } +}
\ No newline at end of file diff --git a/primrose/src/main.rs b/primrose/src/main.rs new file mode 100644 index 0000000..911520f --- /dev/null +++ b/primrose/src/main.rs @@ -0,0 +1,24 @@ +use primrose::generator::{run}; +use std::env; +use std::io::{Error, ErrorKind}; + +fn main() -> Result<(), Error> { + let args: Vec<String> = env::args().collect(); + if args.len() == 1 { // skip the first arg + let _ = run("./spec_code/example_unique.rs".to_string(), "default".to_string(), 5); + Ok(()) + } else if args.len() == 4 { // skip the first arg + let model_size_input = match args.get(3).unwrap().parse::<u64>() { + Ok(val) => val, + Err(_) => { + println!("here"); + return Err(Error::new(ErrorKind::Other, "Invalid model size")); + } + }; + let model_size = model_size_input as usize; + let _ = run("./spec_code/".to_string() + args.get(1).unwrap(), args.get(2).unwrap().to_string(), model_size); + Ok(()) + } else { + Err(Error::new(ErrorKind::Other, "Invalid source code paths")) + } +}
\ No newline at end of file diff --git a/primrose/src/parser.rs b/primrose/src/parser.rs new file mode 100644 index 0000000..6d60a62 --- /dev/null +++ b/primrose/src/parser.rs @@ -0,0 +1,234 @@ +extern crate peg; +use peg::parser; + +use std::vec::Vec; +use std::iter::FromIterator; + +use crate::types::{Name, Type, TypeVar, Bounds}; + +pub type Id = String; + +pub type Literal = String; + +#[derive(Clone, Debug)] +pub enum Refinement { + Prop(Box<Term>), + AndProps(Box<Refinement>, Box<Refinement>), +} + +#[derive(Clone, Debug)] +pub enum Term { + LitTerm(Box<Literal>), + VarTerm(Box<Id>), + LambdaTerm((Box<Id>, Box<Bounds>), Box<Term>), + AppTerm(Box<Term>, Box<Term>), +} + +impl Term { + pub fn is_quantifier(&self) -> bool { + match self { + Term::VarTerm(id) => { + if id.to_string().eq("forall") { + true + } else { + false + } + }, + _ => false + } + } + + pub fn require_cdr(&self) -> bool { + match self { + Term::VarTerm(id) => { + if id.to_string().eq("pop") { + true + } else { + false + } + }, + _ => false + } + } +} + +impl ToString for Term { + fn to_string(&self) -> String { + match self { + Term::LitTerm(l) => l.to_string(), + Term::VarTerm(id) => id.to_string(), + Term::LambdaTerm((id, bounds), t) => id.to_string(), + Term::AppTerm(t1, t2) => t1.to_string() + &t2.to_string(), + } + } +} + +#[derive(Clone, Debug)] +pub enum Decl { + PropertyDecl((Box<Id>, Box<Type>), Box<Term>), + ConTypeDecl(Box<Type>, (Box<Id>, Box<Bounds>, Box<Refinement>)) +} + +impl Decl { + pub fn is_prop_decl(&self) -> bool { + match self { + Decl::PropertyDecl(_, _) => true, + _ => false + } + } + + pub fn is_contype_decl(&self) -> bool { + match self { + Decl::ConTypeDecl(_, _) => true, + _ => false + } + } + + pub fn get_name(&self) -> String { + match self { + Decl::ConTypeDecl(con_ty, _) => { + let (con, _) = con_ty.get_con_elem().unwrap(); + con + }, + Decl::PropertyDecl((id, _), _) => id.to_string() + } + } +} + +pub type Spec = Vec<Decl>; +pub type Code = String; + +#[derive(Clone, Debug)] +pub enum Block { + SpecBlock(Box<Spec>, usize), + CodeBlock(Box<Code>, usize) +} + +impl Block { + pub fn is_spec_block(&self) -> bool { + match self { + Block::SpecBlock(_, _) => true, + _ => false + } + } + + pub fn is_code_block(&self) -> bool { + match self { + Block::CodeBlock(_, _) => true, + _ => false + } + } + + pub fn extract_spec(&self) -> Spec { + match self { + Block::SpecBlock(spec, _) => spec.to_vec(), + _ => Vec::new() + } + } + + pub fn extract_code(&self) -> Code { + match self { + Block::CodeBlock(code, _) => code.to_string(), + _ => String::new() + } + } +} + +pub type Prog = Vec<Block>; + +parser!{ +pub grammar spec() for str { + pub rule id() -> Id + = s:$(!keyword() ([ 'a'..='z' | 'A'..='Z' | '_' ]['a'..='z' | 'A'..='Z' | '0'..='9' | '_' | '-' | '?' ]*)) + { s.into() } + + pub rule name() -> Name + = s:$(!keyword() ([ 'a'..='z' | 'A'..='Z' | '_' ]['a'..='z' | 'A'..='Z' | '0'..='9' ]*)) + { s.into() } + + pub rule keyword() -> () + = ("crate" / "super" / "self" / "Self" / "const" / "mut" / "true" / "false" / "pub" / "in" / "from" / "with" + / "f32"/ "i32" / "u32" / "bool" / "let" / "if" / "else" / "for" / "while" / "fn" / "do") + ![ 'a'..='z' | 'A'..='Z' | '0'..='9' | '_' | '-' | '?' ] + + pub rule literal() -> Literal + = s:$("true" / "false") + { s.into() } + + pub rule ty() -> Type + = precedence! { + n:name() "<" _ t:ty() _ ">" + { Type::Con(Box::new(n), Box::new(t), Box::new(Bounds::from(["Container".to_string()]))) } + -- + n:name() + { Type::Var(TypeVar::new(n)) } + } + + pub rule term() -> Term + = precedence!{ + lit: literal() { Term::LitTerm(Box::new(lit)) } + -- + v:id() { Term::VarTerm(Box::new(v)) } + -- + "\\" v:id() _ "->" _ t:term() { Term::LambdaTerm((Box::new(v), Box::new(Bounds::new())), Box::new(t)) } + -- + "\\" v:id() _ "<:" _ "(" _ b:bounds() _ ")" _ "->" _ t:term() { Term::LambdaTerm((Box::new(v), Box::new(b)), Box::new(t)) } + -- + "(" _ t1:term() __ t2:term() _ ")" { Term::AppTerm(Box::new(t1), Box::new(t2)) } + } + + pub rule refinement() -> Refinement + = precedence!{ + t:term() { Refinement::Prop(Box::new(t)) } + -- + "(" _ p1:refinement() __ "and" __ p2:refinement() _ ")" { Refinement::AndProps(Box::new(p1), Box::new(p2)) } + } + + pub rule bounds() -> Bounds + = l: ((_ n:name() _ {n}) ++ "," ) { Bounds::from_iter(l.iter().cloned()) } + + pub rule decl() -> Decl + = precedence! { + _ "property" __ p:id() _ "<" _ ty:ty() _ ">" _ "{" _ t:term() _ "}" _ + { + Decl::PropertyDecl((Box::new(p), Box::new(ty)), Box::new(t)) + } + -- + _ "type" __ ty:ty() _ "=" _ "{" _ c:id() _ "impl" __ "(" _ b:bounds() _ ")" _ "|" _ t:refinement() _ "}" _ + { + Decl::ConTypeDecl(Box::new(ty), (Box::new(c), Box::new(b), Box::new(t))) + } + } + + pub rule spec() -> Spec + = _ "/*SPEC*" _ decls: (d:decl() { d }) ** _ _ "*ENDSPEC*/" _ + { + decls + } + + pub rule code() -> Code + = _ "/*CODE*/" c:$((!"/*ENDCODE*/"!"/*SPEC*"!"*ENDSPEC*/"[_])*) "/*ENDCODE*/" _ { c.into() } + + pub rule block() -> Block + = precedence! { + _ p:position!() s:spec() _ + { + Block::SpecBlock(Box::new(s), p) + } + -- + _ p:position!() c:code() _ + { + Block::CodeBlock(Box::new(c), p) + } + } + + pub rule prog() -> Prog + = _ blocks: (b:block() { b }) ** _ _ + { + blocks + } + + rule _ = quiet!{[' ' | '\n' | '\t']*} + rule __ = quiet!{[' ' | '\n' | '\t']+} + +}}
\ No newline at end of file diff --git a/primrose/src/proptest/mod.rs b/primrose/src/proptest/mod.rs new file mode 100644 index 0000000..159a9f6 --- /dev/null +++ b/primrose/src/proptest/mod.rs @@ -0,0 +1,68 @@ +pub mod strategies; + +use im::conslist::{ConsList}; +use im::conslist; +use std::sync::Arc; + +pub fn contains<T: PartialEq>(list: &ConsList<T>, elem: &T) -> bool { + list.iter().find(|x| x.as_ref() == elem).is_some() +} + +pub fn clear<T>(list: &ConsList<T>) -> ConsList<T> { + ConsList::<T>::new() +} + +pub fn remove<T: PartialEq+Clone>(list: &ConsList<T>, a: T) -> (ConsList<T>, Option<T>) { + if contains(list, &a) { + let mut result = ConsList::<T>::new(); + let mut found = false; + for i in list.iter() { + if i.as_ref() == &a && !found { + found = true; + continue; + } else { + result = result.append(conslist![i.clone()]); + } + } + (result, Some(a)) + } else { + (list.clone(), None) + } +} + +pub fn first<T>(list: &ConsList<T>) -> Option<&T> { + list.head().map(|x| unsafe{&*Arc::into_raw(x)}) +} + +pub fn last<T>(list: &ConsList<T>) -> Option<&T> { + list.reverse().head().map(|x| unsafe{&*Arc::into_raw(x)}) +} + +pub fn nth<T>(list: &ConsList<T>, n: usize) -> Option<&T> { + list.iter().nth(n).map(|x| unsafe{&*Arc::into_raw(x)}) +} + +pub fn push<T>(list: &ConsList<T>, a: T) -> ConsList<T> { + list.append(conslist![a]) +} + +pub fn pop<T>(list: &ConsList<T>) -> (ConsList<T>, Option<Arc<T>>) { + if list.is_empty() { + (ConsList::<T>::new(), None) + } else { + let (elem, result) = list.reverse().uncons().unwrap(); + (result.reverse(), Some(elem)) + } +} + +pub fn unique<T: PartialEq>(list: &ConsList<T>) -> ConsList<T> { + let mut result = ConsList::<T>::new(); + for i in list.iter() { + if contains(&result, &i) { + continue; + } else { + result = result.append(conslist![i.clone()]); + } + } + result +}
\ No newline at end of file diff --git a/primrose/src/proptest/strategies.rs b/primrose/src/proptest/strategies.rs new file mode 100644 index 0000000..1f529ca --- /dev/null +++ b/primrose/src/proptest/strategies.rs @@ -0,0 +1,37 @@ +use proptest::strategy::*; +use proptest::prelude::*; +use std::ops::Range; + +use crate::library::eager_unique_vector::{EagerUniqueVec}; +use crate::library::eager_sorted_vector::{EagerSortedVec}; +use crate::library::lazy_unique_vector::{LazyUniqueVec}; +use crate::library::lazy_sorted_vector::{LazySortedVec}; +use proptest::collection::vec; +use proptest::prelude::*; +use im::conslist::{ConsList}; + + +pub fn eager_unique_vec<T: Strategy + 'static>(element: T, size: Range<usize>) -> impl Strategy<Value = EagerUniqueVec<T::Value>> +where <T as Strategy>::Value: PartialEq +{ + vec(element, size.clone()).prop_map(EagerUniqueVec::from_vec) +} + + +pub fn lazy_unique_vec<T: Strategy + 'static>(element: T, size: Range<usize>) -> impl Strategy<Value = LazyUniqueVec<T::Value>> +where <T as Strategy>::Value: Ord +{ + vec(element, size.clone()).prop_map(LazyUniqueVec::from_vec) +} + +pub fn eager_sorted_vec<T: Strategy + 'static>(element: T, size: Range<usize>) -> impl Strategy<Value = EagerSortedVec<T::Value>> +where <T as Strategy>::Value: Ord +{ + vec(element, size.clone()).prop_map(EagerSortedVec::from_vec) +} + +pub fn lazy_sorted_vec<T: Strategy + 'static>(element: T, size: Range<usize>) -> impl Strategy<Value = LazySortedVec<T::Value>> +where <T as Strategy>::Value: Ord +{ + vec(element, size.clone()).prop_map(LazySortedVec::from_vec) +} diff --git a/primrose/src/run_matching.rs b/primrose/src/run_matching.rs new file mode 100644 index 0000000..746c29a --- /dev/null +++ b/primrose/src/run_matching.rs @@ -0,0 +1,86 @@ +use std::process::Command; +use std::env; +use std::fs; +use std::io::{Write, BufReader, BufRead, Error, ErrorKind}; + +use crate::spec_map::{MatchSetup}; + +type ExecutionError = String; + +pub const LANGDECL: &str = "#lang rosette\n"; +const GENNAME: &str = "./racket_specs/gen_match/match-script.rkt"; +const LIBSPECPATH: &str = "../gen_lib_spec/"; +const PROPSPECPATH: &str = "../gen_prop_spec/"; +//const SETUP: &str = "(require \"../match-setup.rkt\")\n"; +const LIBDIR: &str = "./racket_specs/gen_lib_spec/"; +const PROPDIR: &str = "./racket_specs/gen_prop_spec/"; +const MATCHDIR: &str = "./racket_specs/gen_match/"; + +pub fn initialise_match_setup() -> MatchSetup { + let mut match_setup = MatchSetup::new(); + match_setup.insert("Container".to_string(), "../container-setup.rkt".to_string()); + match_setup.insert("Indexable".to_string(), "../indexable-setup.rkt".to_string()); + match_setup.insert("Stack".to_string(), "../stack-setup.rkt".to_string()); + match_setup +} + + +pub fn gen_match_script(prop: String, match_setup: String, prop_spec_file: String, lib_spec_file: String, interface_spec: String, symbolics: &Vec<String>) -> Result<String, Error> { + let mut output = fs::File::create(GENNAME.to_owned())?; + write!(output, "{}", LANGDECL.to_string())?; + let require_prop = "(require \"".to_string() + PROPSPECPATH + &prop_spec_file + "\")\n"; + write!(output, "{}", require_prop)?; + let require_lib = "(require \"".to_string() + LIBSPECPATH + &lib_spec_file + "\")\n"; + write!(output, "{}", require_lib)?; + write!(output, "{}", "(require \"".to_string() + &match_setup + "\")\n")?; + let s = symbolics.join(" "); + let code = "(check ".to_string() + &prop + " (cdr " + &interface_spec +") (car " + &interface_spec + ") ls " + &s + ")\n"; + write!(output, "{}", code)?; + Ok(GENNAME.to_string()) +} + +pub fn run_matching(filename: String) -> Result<bool, ExecutionError> { + let output = Command::new("sh") + .arg("-c") + .arg("racket ".to_owned() + &filename) + .output() + .expect("failed to execute process"); + let raw = output.stdout; + let result_str = String::from_utf8_lossy(&raw).to_string(); + let result = result_str.trim(); + if (result == "#t") { + Ok(true) + } else if (result == "#f"){ + Ok(false) + } else { + Err("Error: Not a valid output.".to_string()) + } +} + +pub fn cleanup_script() { + Command::new("sh") + .arg("-c") + .arg("rm -f ".to_owned() + GENNAME) + .output() + .expect("Fail to clean up"); +} + +pub fn setup_dirs() { + Command::new("sh") + .arg("-c") + .arg("mkdir -p ".to_owned() + PROPDIR) + .output() + .expect("Fail to create the property specification directory"); + + Command::new("sh") + .arg("-c") + .arg("mkdir -p ".to_owned() + LIBDIR) + .output() + .expect("Fail to create the library specification directory"); + + Command::new("sh") + .arg("-c") + .arg("mkdir -p ".to_owned() + MATCHDIR) + .output() + .expect("Fail to create the matching script directory"); +}
\ No newline at end of file diff --git a/primrose/src/spec_map.rs b/primrose/src/spec_map.rs new file mode 100644 index 0000000..c5d7656 --- /dev/null +++ b/primrose/src/spec_map.rs @@ -0,0 +1,18 @@ +use std::collections::HashMap; +use std::collections::hash_map::Iter; + +type StructName = String; +type LibSpecDir = String; +type BoundName = String; +type BoundProvide = String; +type MatchSetupDir = String; +pub type Bounds = HashMap<BoundName, BoundProvide>; +pub type ProvidedOps = (Vec<String>, Vec<String>); + +type PropertyName = String; +type PropSpecDir = String; +type PropSymbolics = Vec<String>; + +pub type LibSpecs = HashMap<StructName, (LibSpecDir, Bounds, ProvidedOps)>; +pub type PropSpecs = HashMap<PropertyName, (PropSpecDir, PropSymbolics)>; +pub type MatchSetup = HashMap<BoundName, MatchSetupDir>;
\ No newline at end of file diff --git a/primrose/src/tools/mod.rs b/primrose/src/tools/mod.rs new file mode 100644 index 0000000..3f75b33 --- /dev/null +++ b/primrose/src/tools/mod.rs @@ -0,0 +1,43 @@ +use std::mem::size_of; +use std::collections::{BTreeSet, HashSet}; +use std::vec::Vec; +use rand::{Rng,SeedableRng}; +use rand::rngs::StdRng; +use rand::seq::SliceRandom; +use rand::thread_rng; + +pub fn gen_dataset_1() -> Vec<u32> { + let size = 1024 * 1024; // 1 MB + let amount = size / size_of::<u32>(); + let mut data: Vec<u32> = (1..amount as u32).collect(); + let mut rng = StdRng::seed_from_u64(222); + data.shuffle(&mut rng); + data +} + +pub fn gen_dataset_128() -> Vec<u32> { + let size = 128 * 1024 * 1024; // 128 MB + let amount = size / size_of::<u32>(); + let mut data: Vec<u32> = (1..amount as u32).collect(); + let mut rng = StdRng::seed_from_u64(222); + data.shuffle(&mut rng); + data +} + +pub fn gen_dataset_256() -> Vec<u32> { + let size = 256 * 1024 * 1024; // 256 MB + let amount = size / size_of::<u32>(); + let mut data: Vec<u32> = (1..amount as u32).collect(); + let mut rng = StdRng::seed_from_u64(222); + data.shuffle(&mut rng); + data +} + +pub fn gen_dataset_512() -> Vec<u32> { + let size = 512 * 1024 * 1024; // 512 MB + let amount = size / size_of::<u32>(); + let mut data: Vec<u32> = (1..amount as u32).collect(); + let mut rng = StdRng::seed_from_u64(222); + data.shuffle(&mut rng); + data +}
\ No newline at end of file diff --git a/primrose/src/traits/container_constructor.rs b/primrose/src/traits/container_constructor.rs new file mode 100644 index 0000000..fa4e163 --- /dev/null +++ b/primrose/src/traits/container_constructor.rs @@ -0,0 +1,5 @@ +pub trait ContainerConstructor { + type Impl: ?Sized; + type Bound: ?Sized; + fn new() -> Box<Self::Bound>; +}
\ No newline at end of file diff --git a/primrose/src/traits/mod.rs b/primrose/src/traits/mod.rs new file mode 100644 index 0000000..5d98639 --- /dev/null +++ b/primrose/src/traits/mod.rs @@ -0,0 +1,21 @@ +pub mod container_constructor; + +pub trait Container<T> { + fn len(&mut self) -> usize; + fn contains(&mut self, x: &T) -> bool; + fn is_empty(&mut self) -> bool; + fn insert(&mut self, elt: T); + fn clear(&mut self); + fn remove(&mut self, elt: T) -> Option<T>; // remove first occurance +} + +pub trait Stack<T> { + fn push(&mut self, elt: T); + fn pop(&mut self) -> Option<T>; +} + +pub trait Indexable<T> { + fn first(&mut self) -> Option<&T>; + fn last(&mut self) -> Option<&T>; + fn nth(&mut self, n: usize) -> Option<&T>; +}
\ No newline at end of file diff --git a/primrose/src/type_check.rs b/primrose/src/type_check.rs new file mode 100644 index 0000000..7b5424b --- /dev/null +++ b/primrose/src/type_check.rs @@ -0,0 +1,349 @@ +use crate::parser::{Prog, Block, Spec, Decl, Term, Refinement, Id, spec}; +use crate::inference::{TypeEnv}; +use crate::generator::{readfile}; +use crate::types::{Type, TypeVar, TypeScheme, TypeVarGen, Bounds}; + +use std::ops::Deref; + +type TypeError = String; + +pub struct TypeChecker { + global_ctx : TypeEnv, + tvg: TypeVarGen +} + +impl TypeChecker { + pub fn new() -> TypeChecker { + TypeChecker { + global_ctx: TypeEnv::new(), + tvg: TypeVarGen::new() + } + } + + fn predefined(&mut self) { + // put for_all_unique_pair into context + let binary_fn1 = Type::Fun(Box::new(Type::Var(TypeVar::new("T".to_string()))), Box::new(Type::Fun(Box::new(Type::Var(TypeVar::new("T".to_string()))), Box::new(Type::Bool())))); + self.global_ctx.insert("for-all-unique-pairs".to_string(), + TypeScheme { + vars: Vec::new(), + ty: Type::Fun(Box::new(Type::Con(Box::new("Con".to_string()), + Box::new(Type::Var(TypeVar::new("T".to_string()))), + Box::new(Bounds::from(["Container".to_string()])))), + Box::new(Type::Fun(Box::new(binary_fn1), Box::new(Type::Bool())))) + } + ); + + // put for_all_unique_pair into context + let binary_fn2 = Type::Fun(Box::new(Type::Var(TypeVar::new("T".to_string()))), Box::new(Type::Fun(Box::new(Type::Var(TypeVar::new("T".to_string()))), Box::new(Type::Bool())))); + self.global_ctx.insert("for-all-consecutive-pairs".to_string(), + TypeScheme { + vars: Vec::new(), + ty: Type::Fun(Box::new(Type::Con(Box::new("Con".to_string()), + Box::new(Type::Var(TypeVar::new("T".to_string()))), + Box::new(Bounds::from(["Container".to_string()])))), + Box::new(Type::Fun(Box::new(binary_fn2), Box::new(Type::Bool())))) + } + ); + + let unary_fn = Type::Fun(Box::new(Type::Var(TypeVar::new("T".to_string()))), Box::new(Type::Bool())); + self.global_ctx.insert("for-all-elems".to_string(), + TypeScheme { + vars: Vec::new(), + ty: Type::Fun(Box::new(Type::Con(Box::new("Con".to_string()), + Box::new(Type::Var(TypeVar::new("T".to_string()))), + Box::new(Bounds::from(["Container".to_string()])))), + Box::new(Type::Fun(Box::new(unary_fn), Box::new(Type::Bool())))) + } + ); + + // put neq into context + let neq_fn = Type::Fun(Box::new(Type::Var(TypeVar::new("T".to_string()))), Box::new(Type::Fun(Box::new(Type::Var(TypeVar::new("T".to_string()))), Box::new(Type::Bool())))); + self.global_ctx.insert("neq".to_string(), + TypeScheme { + vars: Vec::new(), + ty: neq_fn + } + ); + + // put leq into context + let leq_fn = Type::Fun(Box::new(Type::Var(TypeVar::new("T".to_string()))), Box::new(Type::Fun(Box::new(Type::Var(TypeVar::new("T".to_string()))), Box::new(Type::Bool())))); + self.global_ctx.insert("leq?".to_string(), + TypeScheme { + vars: Vec::new(), + ty: leq_fn + } + ); + + let geq_fn = Type::Fun(Box::new(Type::Var(TypeVar::new("T".to_string()))), Box::new(Type::Fun(Box::new(Type::Var(TypeVar::new("T".to_string()))), Box::new(Type::Bool())))); + self.global_ctx.insert("geq?".to_string(), + TypeScheme { + vars: Vec::new(), + ty: geq_fn + } + ); + + let equal = Type::Fun(Box::new(Type::Var(TypeVar::new("T".to_string()))), Box::new(Type::Fun(Box::new(Type::Var(TypeVar::new("T".to_string()))), Box::new(Type::Bool())))); + self.global_ctx.insert("equal?".to_string(), + TypeScheme { + vars: Vec::new(), + ty: equal + } + ); + + let unique_count_fn = Type::Fun(Box::new(Type::Var(TypeVar::new("T".to_string()))), + Box::new(Type::Fun(Box::new(Type::Con(Box::new("Con".to_string()), + Box::new(Type::Var(TypeVar::new("T".to_string()))), + Box::new(Bounds::from(["Container".to_string()])))), Box::new(Type::Bool())))); + self.global_ctx.insert("unique-count?".to_string(), + TypeScheme { + vars: Vec::new(), + ty: unique_count_fn + } + ); + + // the forall quantifier + let forall = Type::Fun( + Box::new(Type::Fun(Box::new(Type::Var(TypeVar::new("T".to_string()))), Box::new(Type::Bool()))), + Box::new(Type::Bool())); + self.global_ctx.insert("forall".to_string(), + TypeScheme { + vars: Vec::new(), + ty: forall + } + ); + } + + pub fn get_ctx(&self) -> &TypeEnv { + &self.global_ctx + } + + pub fn check_prog(&mut self, prog: Prog) -> Result<(), TypeError> { + let specs: Vec<Spec> = + prog.iter() + .filter(| block | block.is_spec_block()) + .map(| block | block.extract_spec()) + .collect(); + self.predefined(); + self.check_specs(specs) + } + + pub fn check_specs(&mut self, specs: Vec<Spec>) -> Result<(), TypeError> { + let concat_specs = specs.concat(); + let prop_decls: Vec<&Decl> = + concat_specs.iter() + .filter(| decl | decl.is_prop_decl()) + .collect(); + let contype_decls: Vec<&Decl> = + concat_specs.iter() + .filter(| decl | decl.is_contype_decl()) + .collect(); + match self.check_prop_decls(prop_decls) { + Ok(_) => { + match self.check_contype_decls(contype_decls.clone()) { + Ok(_) => self.check_bound_decls(contype_decls), + Err(e) => Err(e) + } + } + Err(e) => Err(e) + } + } + + pub fn check_bound_decls(&mut self, decls: Vec<&Decl>) -> Result<(), TypeError> { + let mut result = Ok(()); + for decl in decls.into_iter() { + match self.check_bound_decl(decl) { + Ok(_) => continue, + Err(e) => { + result = Err(e); + break; + } + } + } + result + } + + pub fn check_bound_decl(&mut self, decl: &Decl) -> Result<(), TypeError> { + match decl { + Decl::ConTypeDecl(_, (_, ins, _)) => { + // Duplicate bound name checking + for i in ins.iter() { + match self.global_ctx.get(&i.to_string()) { + Some(_) => { + return Err("Duplicate bound name declaration".to_string()); + }, + None => continue, // TODO: check each bound is a valid rust trait + } + } + Ok(()) + }, + _ => Err("Not a valid bound declaration".to_string()) + } + } + + pub fn check_prop_decls(&mut self, decls: Vec<&Decl>) -> Result<(), TypeError> { + let mut result = Ok(()); + for decl in decls.into_iter() { + match self.check_prop_decl(decl) { + Ok(_) => continue, + Err(e) => { + result = Err(e); + break; + } + } + } + result + } + + pub fn check_prop_decl(&mut self, decl: &Decl) -> Result<(), TypeError> { + match decl { + Decl::PropertyDecl((id, ty), term) => { + // Duplicate property decl checking + match self.global_ctx.get(&id.to_string()) { + Some(_) => Err("Duplicate property declaration".to_string()), + None => { + // check well formedness + match self.global_ctx.type_inference(term, &mut self.tvg) { + Ok(ty) => { + // it should have type Con<T> -> Bool + match ty { + Type::Fun(ref t1, ref t2) => { + match (t1.deref(), t2.deref()) { + (Type::Con(n, t, _), Type::Bool()) => { + if n.to_string() == "Con".to_string() { + self.global_ctx.insert( + id.to_string(), + TypeScheme { + vars: Vec::new(), + ty: ty + } + ); + Ok(()) + } else { + Err("Not a valid property decl: input does not have basic container type Con<T>".to_string()) + } + }, + (_, Type::Bool()) => { + self.global_ctx.insert( + id.to_string(), + TypeScheme { + vars: Vec::new(), + ty: ty + } + ); + Ok(()) + }, + _ => Err("Not a valid property decl: return type should be Bool".to_string()) + } + }, + _ => Err("Not a valid property decl: should have type Con<T> -> Bool".to_string()) + } + } + Err(e) => Err(e) + } + }, + } + }, + _ => Err("Not a valid property declaration".to_string()) + } + } + + pub fn check_contype_decls(&mut self, decls: Vec<&Decl>) -> Result<(), TypeError> { + let mut result = Ok(()); + for decl in decls.into_iter() { + match self.check_contype_decl(decl) { + Ok(_) => continue, + Err(e) => { + result = Err(e); + break; + } + } + } + result + } + + pub fn check_contype_decl(&mut self, decl: &Decl) -> Result<(), TypeError> { + match decl { + Decl::ConTypeDecl(con_ty, (vid, ins, r)) => { + // Duplicate container type decl checking + match self.global_ctx.get(&con_ty.to_string()) { + Some(_) => Err("Duplicate container type declaration".to_string()), + None => { + let con = Type::Con(Box::new("Con".to_string()), Box::new(Type::Var(TypeVar::new("T".to_string()))), ins.clone()); + let mut local_ctx = self.global_ctx.clone(); + local_ctx.insert(vid.to_string(), + TypeScheme { + vars: Vec::new(), + ty: con + } + ); + match self.check_ref(&mut local_ctx, r) { + Ok(_) => { + self.global_ctx.insert(decl.get_name(), + TypeScheme{ + vars: Vec::new(), + ty: *con_ty.clone() + } + ); + Ok(()) + }, + Err(e) => Err(e) + } + } + } + }, + _ => Err("Not a valid container type declaration".to_string()) + } + } + + pub fn check_ref(&mut self, ctx: &mut TypeEnv, r: &Refinement) -> Result<(), TypeError> { + match r { + Refinement::Prop(t) => { + match ctx.type_inference(t.deref(), &mut self.tvg) { + Ok(t) => { + // t has to be boolean + if t.is_bool() { + Ok(()) + } else { + Err("The refinement has to be evaluated to a Bool type.".to_string()) + } + }, + Err(e) => Err(e) + } + }, + Refinement::AndProps(r1, r2) => { + match self.check_ref(ctx, r1) { + Ok(_) => { + match self.check_ref(ctx, r2) { + Ok(_) => Ok(()), + Err(e) => Err(e) + } + }, + Err(e) => Err(e) + } + } + } + } +} + + +// Helper functions of testing +// TODO; restructure them +pub fn check_prop_decl() -> Result<(), TypeError> { + let mut tc = TypeChecker::new(); + let f = readfile("./spec_code/example.rs".to_string()); + match spec::prog(&f) { + Ok(prog) => tc.check_prog(prog), + Err(e) => Err(e.to_string()) + } +} + +// #[cfg(test)] +// mod tests { +// use crate::type_check::{TypeChecker, check_prop_decl}; + +// #[test] +// fn test_dup_prop_decl() { +// assert!(check_prop_decl().is_ok()); +// } + +// }
\ No newline at end of file diff --git a/primrose/src/types.rs b/primrose/src/types.rs new file mode 100644 index 0000000..8578f5d --- /dev/null +++ b/primrose/src/types.rs @@ -0,0 +1,280 @@ +use std::borrow::Borrow; +use std::collections::{HashSet, HashMap}; +use std::hash::Hash; +use std::ops::{Deref, DerefMut}; +use std::fmt; +use std::result; + +pub type Name = String; + +// traits +pub type Bounds = HashSet<Name>; + +#[derive(Eq, PartialEq, Clone, Debug)] +pub enum Type { + Bool(), + Int(), + Var(TypeVar), + Con(Box<Name>, Box<Type>, Box<Bounds>), + Fun(Box<Type>, Box<Type>) +} + +impl Type { + pub fn is_var(&self) -> bool { + match self { + Type::Var(_) => true, + _ => false + } + } + + pub fn is_bool(&self) -> bool { + match self { + Type::Bool() => true, + _ => false + } + } + + pub fn get_con_elem(&self) -> Option<(String, String)> { + match self { + Type::Con(n, t, _) => Some((n.to_string(), t.to_string())), + _ => None + } + } +} + +impl ToString for Type { + fn to_string(&self) -> String { + match self { + Type::Bool() => "bool".to_string(), + Type::Int() => "int".to_string(), + Type::Var(tv) => tv.to_string(), + Type::Con(n, t, bounds) => n.to_string() + "<" + &t.to_string() + ">" + " <: (" + &bounds.clone().into_iter().collect::<Vec<String>>().join(", ") + ")", + Type::Fun(t1, t2) => t1.to_string() + "->" + &t2.to_string(), + } + } +} + +pub type UnificationError = String; + +trait Union { + fn union(&self, other: &Self) -> Self; +} + +impl<K, V> Union for HashMap<K, V> + where K: Clone + Eq + Hash, + V: Clone +{ + fn union(&self, other: &Self) -> Self { + let mut res = self.clone(); + for (key, value) in other { + res.entry(key.clone()).or_insert(value.clone()); + } + res + } +} + +impl Type { + // Most general unifier + pub fn mgu(&self, other: &Type) -> Result<Subst, UnificationError> { + match (self, other) { + // Unify function type + (&Type::Fun(ref in1, ref out1), &Type::Fun(ref in2, ref out2)) => { + let sub1 = in1.mgu(in2)?; + let sub2 = out1.apply(&sub1).mgu(&out2.apply(&sub1))?; + Ok(sub1.compose(&sub2)) + } + + // Unify con type + (&Type::Con(ref n1, ref t1, _), &Type::Con(ref n2, ref t2, _)) => { + if n1.to_string() != n2.to_string() { + Err("Cannot unify two different container".to_string()) + } else { + t1.mgu(t2) + } + } + + // Type variable biding + (&Type::Var(ref v), t) => v.bind(t), + (t, &Type::Var(ref v)) => v.bind(t), + + // Unify primitives + (&Type::Int(), &Type::Int()) | (&Type::Bool(), &Type::Bool()) => { + Ok(Subst::new()) + } + + // Otherwise, the types cannot be unified. + (t1, t2) => { + println!("{:?}", t1); + println!("{:?}", t2); + Err("types do not unify".to_string()) + } + } + } +} + +// A substitution is a mapping from type variables to types. +#[derive(Clone, Debug)] +pub struct Subst(HashMap<TypeVar, Type>); + +impl Deref for Subst { + type Target = HashMap<TypeVar, Type>; + fn deref(&self) -> &HashMap<TypeVar, Type> { + &self.0 + } +} +impl DerefMut for Subst { + fn deref_mut(&mut self) -> &mut HashMap<TypeVar, Type> { + &mut self.0 + } +} + +impl Subst { + pub fn new() -> Subst { + Subst(HashMap::new()) + } + + // composing substitutions + pub fn compose(&self, other: &Subst) -> Subst { + Subst(self.union(&other.iter() + .map(|(k, v)| (k.clone(), v.apply(self))) + .collect())) + } +} + +// Fresh variable generator +pub struct TypeVarGen { + supply: usize, +} + +impl TypeVarGen { + pub fn new() -> TypeVarGen { + TypeVarGen { supply: 0 } + } + pub fn next(&mut self) -> TypeVar { + let name = "#T".to_owned() + &self.supply.to_string(); + let v = TypeVar::new(name); + self.supply += 1; + v + } +} + +// Type variables/type names +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct TypeVar { + name: Name, +} + +impl TypeVar { + pub fn new(s: Name) -> TypeVar { + TypeVar{name: s} + } + /// Attempt to bind a type variable to a type, returning an appropriate substitution. + fn bind(&self, ty: &Type) -> Result<Subst, UnificationError> { + // Binding to itself + if let &Type::Var(ref u) = ty { + if u == self { + return Ok(Subst::new()); + } + } + + // Occurance check + if ty.ftv().contains(self) { + return Err("occur check fails".to_string()); + } + + let mut s = Subst::new(); + s.insert(self.clone(), ty.clone()); + Ok(s) + } +} + + +impl ToString for TypeVar { + fn to_string(&self) -> String { + self.name.to_string() + } +} + +pub trait Types { + fn ftv(&self) -> HashSet<TypeVar>; + fn apply(&self, s: &Subst) -> Self; +} + +impl<'a, T> Types for Vec<T> + where T: Types +{ + // Free type variables + fn ftv(&self) -> HashSet<TypeVar> { + self.iter() + .map(|x| x.ftv()) + .fold(HashSet::new(), |set, x| set.union(&x).cloned().collect()) + } + + // Apply a substitution to a vector of types + fn apply(&self, s: &Subst) -> Vec<T> { + self.iter().map(|x| x.apply(s)).collect() + } +} + +impl Types for Type { + fn ftv(&self) -> HashSet<TypeVar> { + match self { + &Type::Var(ref s) => [s.clone()].iter().cloned().collect(), + &Type::Int() | &Type::Bool() => HashSet::new(), + &Type::Fun(ref i, ref o) => i.ftv().union(&o.ftv()).cloned().collect(), + &Type::Con(_, ref s, _) => s.ftv().union(&HashSet::new()).cloned().collect(), + } + } + + // apply substitution + fn apply(&self, s: &Subst) -> Type { + match self { + &Type::Var(ref n) => s.get(n).cloned().unwrap_or(self.clone()), + &Type::Fun(ref t1, ref t2) => Type::Fun(Box::new(t1.apply(s)), Box::new(t2.apply(s))), + &Type::Con(ref n, ref t, ref bounds) => Type::Con(Box::new(n.to_string()), Box::new(t.apply(s)), bounds.clone()), + _ => self.clone(), + } + } +} + +// A type scheme is a type with an extra piece of information attached, to constraint the inference +#[derive(Clone, Debug)] +pub struct TypeScheme { + pub vars: Vec<TypeVar>, + pub ty: Type, +} + +impl Types for TypeScheme { + fn ftv(&self) -> HashSet<TypeVar> { + self.ty + .ftv() + .difference(&self.vars.iter().cloned().collect()) + .cloned() + .collect() + } + + fn apply(&self, s: &Subst) -> TypeScheme { + TypeScheme { + vars: self.vars.clone(), + ty: { + let mut sub = s.clone(); + for var in &self.vars { + sub.remove(var); + } + self.ty.apply(&sub) + }, + } + } +} + +impl TypeScheme { + /// Instantiates a typescheme into a type. + pub fn instantiate(&self, tvg: &mut TypeVarGen) -> Type { + let newvars = self.vars.iter().map(|_| Type::Var(tvg.next())); + self.ty.apply(&Subst(self.vars + .iter() + .cloned() + .zip(newvars) + .collect())) + } +}
\ No newline at end of file diff --git a/primrose/timeall.sh b/primrose/timeall.sh new file mode 100755 index 0000000..c1e2eb8 --- /dev/null +++ b/primrose/timeall.sh @@ -0,0 +1,36 @@ +./scripts/b_unique_con_3.sh +./scripts/b_unique_con_5.sh +./scripts/b_unique_con_7.sh +./scripts/b_unique_con_9.sh +./scripts/b_asc_con_3.sh +./scripts/b_asc_con_5.sh +./scripts/b_asc_con_7.sh +./scripts/b_asc_con_9.sh +./scripts/b_des_con_3.sh +./scripts/b_des_con_5.sh +./scripts/b_des_con_7.sh +./scripts/b_des_con_9.sh +./scripts/b_unique_asc_con_3.sh +./scripts/b_unique_asc_con_5.sh +./scripts/b_unique_asc_con_7.sh +./scripts/b_unique_asc_con_9.sh +./scripts/b_unique_con_ra_3.sh +./scripts/b_unique_con_ra_5.sh +./scripts/b_unique_con_ra_7.sh +./scripts/b_unique_con_ra_9.sh +./scripts/b_asc_con_ra_3.sh +./scripts/b_asc_con_ra_5.sh +./scripts/b_asc_con_ra_7.sh +./scripts/b_asc_con_ra_9.sh +./scripts/b_des_con_ra_3.sh +./scripts/b_des_con_ra_5.sh +./scripts/b_des_con_ra_7.sh +./scripts/b_des_con_ra_9.sh +./scripts/b_unique_asc_con_ra_3.sh +./scripts/b_unique_asc_con_ra_5.sh +./scripts/b_unique_asc_con_ra_7.sh +./scripts/b_unique_asc_con_ra_9.sh +./scripts/b_stack_con_3.sh +./scripts/b_stack_con_5.sh +./scripts/b_stack_con_7.sh +./scripts/b_stack_con_9.sh
\ No newline at end of file diff --git a/primrose/timetests.sh b/primrose/timetests.sh new file mode 100755 index 0000000..e702f23 --- /dev/null +++ b/primrose/timetests.sh @@ -0,0 +1,7 @@ +#!/bin/bash +TIMEFORMAT=' +Property based tests running time: %R seconds +' +time { +cargo test +} |