Beginnings of a type system.
This commit is contained in:
parent
37f3070326
commit
c2d775169f
10 changed files with 487 additions and 112 deletions
|
@ -4,3 +4,5 @@ version = "0.1.0"
|
|||
authors = ["James Harton <james@automat.nz>"]
|
||||
|
||||
[dependencies]
|
||||
im = "11.0.1"
|
||||
wrc = "0.3.0"
|
||||
|
|
184
src/context.rs
Normal file
184
src/context.rs
Normal file
|
@ -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<TypeId, Type>,
|
||||
traits: HashMap<TraitId, Trait>,
|
||||
}
|
||||
|
||||
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));
|
||||
// }
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
mod storage;
|
||||
mod tag;
|
||||
mod value;
|
||||
pub use self::value::Value;
|
|
@ -1,4 +0,0 @@
|
|||
pub union Storage {
|
||||
pub integer: i64,
|
||||
pub float: f64,
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
#[derive(Debug)]
|
||||
pub enum Tag {
|
||||
Integer,
|
||||
Float,
|
||||
}
|
|
@ -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<i32> for Value {
|
||||
fn from(i: i32) -> Self {
|
||||
Value {
|
||||
tag: Tag::Integer,
|
||||
storage: Storage { integer: i as i64 },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i64> for Value {
|
||||
fn from(i: i64) -> Self {
|
||||
Value {
|
||||
tag: Tag::Integer,
|
||||
storage: Storage { integer: i },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<f64> for Value {
|
||||
fn from(f: f64) -> Self {
|
||||
Value {
|
||||
tag: Tag::Float,
|
||||
storage: Storage { float: f },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<f32> 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>");
|
||||
}
|
||||
|
||||
}
|
13
src/lib.rs
13
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;
|
||||
|
|
72
src/static_strings.rs
Normal file
72
src/static_strings.rs
Normal file
|
@ -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<String, StaticStringId>,
|
||||
backward: HashMap<StaticStringId, String>,
|
||||
}
|
||||
|
||||
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<StaticStringId> {
|
||||
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");
|
||||
}
|
||||
}
|
46
src/traits.rs
Normal file
46
src/traits.rs
Normal file
|
@ -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);
|
||||
}
|
||||
}
|
179
src/types.rs
Normal file
179
src/types.rs
Normal file
|
@ -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<Vec<(StaticStringId, TypeId)>> {
|
||||
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));
|
||||
}
|
||||
}
|
Reference in a new issue