diff --git a/Cargo.toml b/Cargo.toml index 178d52b..b4dc744 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,3 +4,5 @@ version = "0.1.0" authors = ["James Harton "] [dependencies] +im = "11.0.1" +wrc = "0.3.0" diff --git a/src/context.rs b/src/context.rs new file mode 100644 index 0000000..422ed5c --- /dev/null +++ b/src/context.rs @@ -0,0 +1,184 @@ +use static_strings::StaticStringId; +use static_strings::StaticStrings; +use std::collections::HashMap; +use traits::Trait; +use traits::TraitId; +use types::Type; +use types::TypeId; + +#[derive(Debug)] +pub struct Context { + pub strings: StaticStrings, + types: HashMap, + traits: HashMap, +} + +impl Context { + pub fn new() -> Self { + Context { + strings: StaticStrings::new(), + types: HashMap::new(), + traits: HashMap::new(), + } + } + + pub fn new_native_integer_type(&mut self, name: &str) -> TypeId { + let id = self.next_type_id(); + let name = self.strings.set(name); + + self.types + .insert(id.clone(), Type::integer(id.clone(), name)); + + id + } + + pub fn new_native_float_type(&mut self, name: &str) -> TypeId { + let id = self.next_type_id(); + let name = self.strings.set(name); + + self.types.insert(id.clone(), Type::float(id.clone(), name)); + + id + } + + pub fn new_native_buffer_type(&mut self, name: &str) -> TypeId { + let id = self.next_type_id(); + let name = self.strings.set(name); + + self.types + .insert(id.clone(), Type::buffer(id.clone(), name)); + + id + } + + pub fn new_type(&mut self, name: &str, fields: Vec<(&str, TypeId)>) -> TypeId { + let id = self.next_type_id(); + let name = self.strings.set(name); + + let fields = fields + .into_iter() + .map(|(n, i)| (self.strings.set(n), i)) + .collect(); + + self.types + .insert(id.clone(), Type::compound(id.clone(), name, fields)); + + id + } + + pub fn new_trait(&mut self, name: &str) -> TraitId { + let id = self.next_trait_id(); + let name = self.strings.set(name); + + self.traits.insert(id.clone(), Trait::new(id.clone(), name)); + + id + } + + pub fn get_type(&self, id: TypeId) -> Option<&Type> { + self.types.get(&id) + } + + pub fn get_trait(&self, id: TraitId) -> Option<&Trait> { + self.traits.get(&id) + } + + fn next_type_id(&self) -> TypeId { + TypeId::new(self.types.len() as u32) + } + + fn next_trait_id(&self) -> TraitId { + TraitId::new(self.traits.len() as u32) + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_new() { + Context::new(); + } + + #[test] + fn test_new_native_integer_type() { + let mut context = Context::new(); + let typeid = context.new_native_integer_type("Marty McFly"); + assert_eq!(typeid.clone(), TypeId::new(0)); + let ty = context.get_type(typeid).unwrap(); + assert_eq!(ty.name(), context.strings.try("Marty McFly").unwrap()); + assert_eq!(ty.is_integer(), true); + assert_eq!(ty.is_float(), false); + assert_eq!(ty.is_buffer(), false); + assert_eq!(ty.is_compound(), false); + } + + #[test] + fn test_new_native_float_type() { + let mut context = Context::new(); + let typeid = context.new_native_float_type("Marty McFly"); + assert_eq!(typeid.clone(), TypeId::new(0)); + let ty = context.get_type(typeid).unwrap(); + assert_eq!(ty.name(), context.strings.try("Marty McFly").unwrap()); + assert_eq!(ty.is_integer(), false); + assert_eq!(ty.is_float(), true); + assert_eq!(ty.is_buffer(), false); + assert_eq!(ty.is_compound(), false); + } + + #[test] + fn test_new_native_buffer_type() { + let mut context = Context::new(); + let typeid = context.new_native_buffer_type("Marty McFly"); + assert_eq!(typeid.clone(), TypeId::new(0)); + let ty = context.get_type(typeid).unwrap(); + assert_eq!(ty.name(), context.strings.try("Marty McFly").unwrap()); + assert_eq!(ty.is_integer(), false); + assert_eq!(ty.is_float(), false); + assert_eq!(ty.is_buffer(), true); + assert_eq!(ty.is_compound(), false); + } + + #[test] + fn test_new_type() { + let mut context = Context::new(); + let int = context.new_native_integer_type("int"); + let flt = context.new_native_float_type("flt"); + let id = context.new_type("compound", vec![("a", int.clone()), ("b", flt.clone())]); + let ty = context.get_type(id).unwrap(); + + assert_eq!(ty.name(), context.strings.try("compound").unwrap()); + assert_eq!(ty.is_integer(), false); + assert_eq!(ty.is_float(), false); + assert_eq!(ty.is_buffer(), false); + assert_eq!(ty.is_compound(), true); + + let fields = ty.fields().unwrap(); + assert_eq!(fields[0], (context.strings.try("a").unwrap(), int)); + assert_eq!(fields[1], (context.strings.try("b").unwrap(), flt)); + } + + #[test] + fn test_new_trait() { + let mut context = Context::new(); + let id = context.new_trait("Marty"); + let tr = context.get_trait(id.clone()).unwrap(); + assert_eq!(tr.id(), id); + assert_eq!(tr.name(), context.strings.try("Marty").unwrap()); + } + + // #[test] + // fn test_debug() { + // let mut context = Context::new(); + // let int = context.new_native_integer_type("INTEGER"); + // let flt = context.new_native_float_type("FLOAT"); + // let buf = context.new_native_buffer_type("BUFFER"); + // context.new_type( + // "TUPLE", + // vec![("INTEGER", int), ("FLOAT", flt), ("BUFFER", buf)], + // ); + // context.new_trait("Integer"); + // assert_eq!("WAT", format!("{:?}", context)); + // } +} diff --git a/src/inner/mod.rs b/src/inner/mod.rs deleted file mode 100644 index 8257026..0000000 --- a/src/inner/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -mod storage; -mod tag; -mod value; -pub use self::value::Value; diff --git a/src/inner/storage.rs b/src/inner/storage.rs deleted file mode 100644 index b13c565..0000000 --- a/src/inner/storage.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub union Storage { - pub integer: i64, - pub float: f64, -} diff --git a/src/inner/tag.rs b/src/inner/tag.rs deleted file mode 100644 index 342ce47..0000000 --- a/src/inner/tag.rs +++ /dev/null @@ -1,5 +0,0 @@ -#[derive(Debug)] -pub enum Tag { - Integer, - Float, -} diff --git a/src/inner/value.rs b/src/inner/value.rs deleted file mode 100644 index 9ab111e..0000000 --- a/src/inner/value.rs +++ /dev/null @@ -1,90 +0,0 @@ -use std::convert::From; -use std::fmt; - -use super::storage::Storage; -use super::tag::Tag; - -pub struct Value { - tag: Tag, - storage: Storage, -} - -impl fmt::Debug for Value { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - unsafe { - match self { - &Value { - tag: Tag::Integer, - storage: Storage { integer: value }, - } => write!(f, "Value::Integer<{:?}>", value), - &Value { - tag: Tag::Float, - storage: Storage { float: value }, - } => write!(f, "Value::Float<{:?}>", value), - } - } - } -} - -impl From for Value { - fn from(i: i32) -> Self { - Value { - tag: Tag::Integer, - storage: Storage { integer: i as i64 }, - } - } -} - -impl From for Value { - fn from(i: i64) -> Self { - Value { - tag: Tag::Integer, - storage: Storage { integer: i }, - } - } -} - -impl From for Value { - fn from(f: f64) -> Self { - Value { - tag: Tag::Float, - storage: Storage { float: f }, - } - } -} - -impl From for Value { - fn from(f: f32) -> Self { - Value { - tag: Tag::Float, - storage: Storage { float: f as f64 }, - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn convert_from_i32() { - let i: i32 = 73; - let value = Value::from(i); - assert_eq!(format!("{:?}", value), "Value::Integer<73>"); - } - - #[test] - fn convert_from_i64() { - let i: i64 = 73; - let value = Value::from(i); - assert_eq!(format!("{:?}", value), "Value::Integer<73>"); - } - - #[test] - fn convert_from_f64() { - let i: f64 = 1.23; - let value = Value::from(i); - assert_eq!(format!("{:?}", value), "Value::Float<1.23>"); - } - -} diff --git a/src/lib.rs b/src/lib.rs index 8f1f264..cd4d009 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,9 +1,4 @@ -mod inner; - -#[cfg(test)] -mod tests { - #[test] - fn it_works() { - assert_eq!(2 + 2, 4); - } -} +mod context; +mod static_strings; +mod traits; +mod types; diff --git a/src/static_strings.rs b/src/static_strings.rs new file mode 100644 index 0000000..83cc09a --- /dev/null +++ b/src/static_strings.rs @@ -0,0 +1,72 @@ +use std::collections::HashMap; + +#[derive(Debug, Clone, Eq, PartialEq, Hash)] +pub struct StaticStringId(u32); + +impl StaticStringId { + pub fn new(id: u32) -> Self { + StaticStringId(id) + } +} + +#[derive(Debug)] +pub struct StaticStrings { + forward: HashMap, + backward: HashMap, +} + +impl StaticStrings { + pub fn new() -> Self { + StaticStrings { + forward: HashMap::new(), + backward: HashMap::new(), + } + } + + pub fn set(&mut self, value: &str) -> StaticStringId { + if self.forward.contains_key(value) { + return self.forward.get(value).unwrap().clone(); + } + let id = self.next_string_id(); + self.forward.insert(value.to_string(), id.clone()); + self.backward.insert(id.clone(), value.to_string()); + id + } + + pub fn get(&self, id: StaticStringId) -> Option<&String> { + self.backward.get(&id) + } + + pub fn try(&self, value: &str) -> Option { + match self.forward.get(value) { + Some(id) => Some(id.clone()), + None => None, + } + } + + fn next_string_id(&self) -> StaticStringId { + StaticStringId::new(self.forward.len() as u32) + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_set_returns_duplicate_ids() { + let mut strings = StaticStrings::new(); + let id0 = strings.set("Marty McFly"); + let id1 = strings.set("Doc Brown"); + let id2 = strings.set("Marty McFly"); + assert_ne!(id0, id1); + assert_eq!(id0, id2); + } + + #[test] + fn test_get() { + let mut strings = StaticStrings::new(); + let id0 = strings.set("Marty McFly"); + assert_eq!(strings.get(id0).unwrap(), "Marty McFly"); + } +} diff --git a/src/traits.rs b/src/traits.rs new file mode 100644 index 0000000..3e893a8 --- /dev/null +++ b/src/traits.rs @@ -0,0 +1,46 @@ +use static_strings::StaticStringId; + +#[derive(Debug)] +pub struct Trait { + name: StaticStringId, + id: TraitId, +} + +#[derive(Debug, Clone, Eq, PartialEq, Hash)] +pub struct TraitId(u32); + +impl TraitId { + pub fn new(id: u32) -> TraitId { + TraitId(id) + } +} + +impl Trait { + pub fn new(id: TraitId, name: StaticStringId) -> Self { + Trait { name: name, id: id } + } + + pub fn id(&self) -> TraitId { + self.id.clone() + } + + pub fn name(&self) -> StaticStringId { + self.name.clone() + } +} + +#[cfg(test)] +mod test { + use super::*; + use static_strings::StaticStrings; + + #[test] + fn test_new() { + let mut ss = StaticStrings::new(); + let s0 = ss.set("Marty McFly"); + let id = TraitId::new(13); + let t = Trait::new(id.clone(), s0.clone()); + assert_eq!(t.id(), id); + assert_eq!(t.name(), s0); + } +} diff --git a/src/types.rs b/src/types.rs new file mode 100644 index 0000000..c815789 --- /dev/null +++ b/src/types.rs @@ -0,0 +1,179 @@ +use static_strings::StaticStringId; + +#[derive(Debug)] +enum Inner { + Integer, + Float, + Buffer, + Compound(Vec<(StaticStringId, TypeId)>), +} + +#[derive(Debug)] +pub struct Type { + name: StaticStringId, + id: TypeId, + inner: Inner, +} + +#[derive(Debug, Clone, Eq, PartialEq, Hash)] +pub struct TypeId(u32); + +impl TypeId { + pub fn new(id: u32) -> TypeId { + TypeId(id) + } +} + +impl Type { + pub fn integer(id: TypeId, name: StaticStringId) -> Self { + Type { + name: name, + id: id, + inner: Inner::Integer, + } + } + + pub fn float(id: TypeId, name: StaticStringId) -> Self { + Type { + name: name, + id: id, + inner: Inner::Float, + } + } + + pub fn buffer(id: TypeId, name: StaticStringId) -> Self { + Type { + name: name, + id: id, + inner: Inner::Buffer, + } + } + + pub fn compound( + id: TypeId, + name: StaticStringId, + fields: Vec<(StaticStringId, TypeId)>, + ) -> Self { + Type { + name: name, + id: id, + inner: Inner::Compound(fields), + } + } + + pub fn id(&self) -> TypeId { + self.id.clone() + } + + pub fn name(&self) -> StaticStringId { + self.name.clone() + } + + pub fn is_integer(&self) -> bool { + match self.inner { + Inner::Integer => true, + _ => false, + } + } + + pub fn is_float(&self) -> bool { + match self.inner { + Inner::Float => true, + _ => false, + } + } + + pub fn is_buffer(&self) -> bool { + match self.inner { + Inner::Buffer => true, + _ => false, + } + } + + pub fn is_compound(&self) -> bool { + match self.inner { + Inner::Compound(_) => true, + _ => false, + } + } + + pub fn fields(&self) -> Option> { + match self.inner { + Inner::Compound(ref fields) => Some(fields.clone()), + _ => None, + } + } +} + +#[cfg(test)] +mod test { + use super::*; + use static_strings::StaticStrings; + + #[test] + fn test_integer() { + let mut ss = StaticStrings::new(); + let s0 = ss.set("Marty McFly"); + let id = TypeId::new(13); + let t = Type::integer(id.clone(), s0.clone()); + assert_eq!(t.id(), id); + assert_eq!(t.name(), s0); + assert_eq!(t.is_integer(), true); + assert_eq!(t.is_float(), false); + assert_eq!(t.is_buffer(), false); + assert_eq!(t.is_compound(), false); + } + + #[test] + fn test_float() { + let mut ss = StaticStrings::new(); + let s0 = ss.set("Marty McFly"); + let id = TypeId::new(14); + let t = Type::float(id.clone(), s0.clone()); + assert_eq!(t.id(), id); + assert_eq!(t.name(), s0); + assert_eq!(t.is_integer(), false); + assert_eq!(t.is_float(), true); + assert_eq!(t.is_buffer(), false); + assert_eq!(t.is_compound(), false); + } + + #[test] + fn test_buffer() { + let mut ss = StaticStrings::new(); + let s0 = ss.set("Marty McFly"); + let id = TypeId::new(15); + let t = Type::buffer(id.clone(), s0.clone()); + assert_eq!(t.id(), id); + assert_eq!(t.name(), s0); + assert_eq!(t.is_integer(), false); + assert_eq!(t.is_float(), false); + assert_eq!(t.is_buffer(), true); + assert_eq!(t.is_compound(), false); + } + + #[test] + fn test_compound() { + let mut ss = StaticStrings::new(); + let s0 = ss.set("Marty McFly"); + let s1 = ss.set("Doc Brown"); + let s2 = ss.set("Biff Tannen"); + let id0 = TypeId::new(0); + let id1 = TypeId::new(1); + let id2 = TypeId::new(2); + let t = Type::compound( + id0.clone(), + s0.clone(), + vec![(s1.clone(), id1.clone()), (s2.clone(), id2.clone())], + ); + assert_eq!(t.id(), id0); + assert_eq!(t.name(), s0); + assert_eq!(t.is_integer(), false); + assert_eq!(t.is_float(), false); + assert_eq!(t.is_buffer(), false); + assert_eq!(t.is_compound(), true); + let fields = t.fields().unwrap(); + assert_eq!(fields[0], (s1, id1)); + assert_eq!(fields[1], (s2, id2)); + } +}