aboutsummaryrefslogtreecommitdiff
path: root/primrose/src
diff options
context:
space:
mode:
Diffstat (limited to 'primrose/src')
-rw-r--r--primrose/src/analysis.rs336
-rw-r--r--primrose/src/bounded_ops.rs25
-rw-r--r--primrose/src/description.rs95
-rw-r--r--primrose/src/generator.rs378
-rw-r--r--primrose/src/inference.rs120
-rw-r--r--primrose/src/lib.rs20
-rw-r--r--primrose/src/lib_spec_processor.rs227
-rw-r--r--primrose/src/library/eager_sorted_vector.rs337
-rw-r--r--primrose/src/library/eager_unique_vector.rs339
-rw-r--r--primrose/src/library/hashset.rs199
-rw-r--r--primrose/src/library/lazy_sorted_vector.rs365
-rw-r--r--primrose/src/library/lazy_unique_vector.rs366
-rw-r--r--primrose/src/library/list.rs319
-rw-r--r--primrose/src/library/mod.rs8
-rw-r--r--primrose/src/library/treeset.rs263
-rw-r--r--primrose/src/library/vector.rs287
-rw-r--r--primrose/src/main.rs24
-rw-r--r--primrose/src/parser.rs234
-rw-r--r--primrose/src/proptest/mod.rs68
-rw-r--r--primrose/src/proptest/strategies.rs37
-rw-r--r--primrose/src/run_matching.rs86
-rw-r--r--primrose/src/spec_map.rs18
-rw-r--r--primrose/src/tools/mod.rs43
-rw-r--r--primrose/src/traits/container_constructor.rs5
-rw-r--r--primrose/src/traits/mod.rs21
-rw-r--r--primrose/src/type_check.rs349
-rw-r--r--primrose/src/types.rs280
27 files changed, 4849 insertions, 0 deletions
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