From 864eecf44c45c3db7563d79c1a61958d9adc86d5 Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Wed, 17 Apr 2019 19:32:10 -0400 Subject: [PATCH] Delete obsolete stuff --- Cargo.lock | 2 + old/canonical.rs | 1 - old/constrain.rs | 24 -- old/ena/bitvec.rs | 301 ------------------------ old/ena/mod.rs | 15 -- old/ena/snapshot_vec.rs | 370 ----------------------------- old/ena/unify/backing_vec.rs | 214 ----------------- old/ena/unify/mod.rs | 444 ----------------------------------- old/expr.rs | 44 ---- old/interpret.rs | 57 ----- old/lib.rs | 22 -- old/name.rs | 1 - old/parse.rs | 335 -------------------------- old/repl.rs | 63 ----- old/solve.rs | 317 ------------------------- old/typ.rs | 12 - old/unify.rs | 363 ---------------------------- oldtst/test_interpreter.rs | 38 --- oldtst/test_repl.rs | 41 ---- oldtst/test_solve.rs | 134 ----------- oldtst/test_unify.rs | 39 --- src/constrain.rs | 24 -- src/expr.rs | 55 ++--- src/interpret.rs | 57 ----- src/name.rs | 1 - src/parse.rs | 102 +------- src/repl.rs | 63 ----- src/solve.rs | 317 ------------------------- src/typ.rs | 12 - src/unify.rs | 363 ---------------------------- 30 files changed, 26 insertions(+), 3805 deletions(-) delete mode 100644 old/canonical.rs delete mode 100644 old/constrain.rs delete mode 100644 old/ena/bitvec.rs delete mode 100644 old/ena/mod.rs delete mode 100644 old/ena/snapshot_vec.rs delete mode 100644 old/ena/unify/backing_vec.rs delete mode 100644 old/ena/unify/mod.rs delete mode 100644 old/expr.rs delete mode 100644 old/interpret.rs delete mode 100644 old/lib.rs delete mode 100644 old/name.rs delete mode 100644 old/parse.rs delete mode 100644 old/repl.rs delete mode 100644 old/solve.rs delete mode 100644 old/typ.rs delete mode 100644 old/unify.rs delete mode 100644 oldtst/test_interpreter.rs delete mode 100644 oldtst/test_repl.rs delete mode 100644 oldtst/test_solve.rs delete mode 100644 oldtst/test_unify.rs delete mode 100644 src/constrain.rs delete mode 100644 src/interpret.rs delete mode 100644 src/name.rs delete mode 100644 src/repl.rs delete mode 100644 src/solve.rs delete mode 100644 src/typ.rs delete mode 100644 src/unify.rs diff --git a/Cargo.lock b/Cargo.lock index 8dbf51d0be..b090ed7949 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,3 +1,5 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. [[package]] name = "ansi_term" version = "0.11.0" diff --git a/old/canonical.rs b/old/canonical.rs deleted file mode 100644 index 9b31875b62..0000000000 --- a/old/canonical.rs +++ /dev/null @@ -1 +0,0 @@ -pub enum Annotation {} diff --git a/old/constrain.rs b/old/constrain.rs deleted file mode 100644 index 43492d1b1f..0000000000 --- a/old/constrain.rs +++ /dev/null @@ -1,24 +0,0 @@ -use typ::Type; - -// constrainDecls :: Can.Decls -> Constraint -> IO Constraint -// constrainDecls decls finalConstraint = -// case decls of -// Can.Declare def otherDecls -> -// Expr.constrainDef Map.empty def =<< constrainDecls otherDecls finalConstraint - -// Can.DeclareRec defs otherDecls -> -// Expr.constrainRecursiveDefs Map.empty defs =<< constrainDecls otherDecls finalConstraint - -// Can.SaveTheEnvironment -> -// return finalConstraint - - - -pub type ExpectedType = Type; - - -pub enum Constraint { - True, - Equal(Type, ExpectedType), - Batch(Vec), -} diff --git a/old/ena/bitvec.rs b/old/ena/bitvec.rs deleted file mode 100644 index 3677c8c5e5..0000000000 --- a/old/ena/bitvec.rs +++ /dev/null @@ -1,301 +0,0 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -/// A very simple BitVector type. -pub struct BitVector { - data: Vec, -} - -impl BitVector { - pub fn new(num_bits: usize) -> BitVector { - let num_words = u64s(num_bits); - BitVector { data: vec![0; num_words] } - } - - pub fn contains(&self, bit: usize) -> bool { - let (word, mask) = word_mask(bit); - (self.data[word] & mask) != 0 - } - - /// Returns true if the bit has changed. - pub fn insert(&mut self, bit: usize) -> bool { - let (word, mask) = word_mask(bit); - let data = &mut self.data[word]; - let value = *data; - let new_value = value | mask; - *data = new_value; - new_value != value - } - - pub fn insert_all(&mut self, all: &BitVector) -> bool { - assert!(self.data.len() == all.data.len()); - let mut changed = false; - for (i, j) in self.data.iter_mut().zip(&all.data) { - let value = *i; - *i = value | *j; - if value != *i { - changed = true; - } - } - changed - } - - pub fn grow(&mut self, num_bits: usize) { - let num_words = u64s(num_bits); - let extra_words = self.data.len() - num_words; - self.data.extend((0..extra_words).map(|_| 0)); - } - - /// Iterates over indexes of set bits in a sorted order - pub fn iter<'a>(&'a self) -> BitVectorIter<'a> { - BitVectorIter { - iter: self.data.iter(), - current: 0, - idx: 0, - } - } -} - -pub struct BitVectorIter<'a> { - iter: ::std::slice::Iter<'a, u64>, - current: u64, - idx: usize, -} - -impl<'a> Iterator for BitVectorIter<'a> { - type Item = usize; - fn next(&mut self) -> Option { - while self.current == 0 { - self.current = if let Some(&i) = self.iter.next() { - if i == 0 { - self.idx += 64; - continue; - } else { - self.idx = u64s(self.idx) * 64; - i - } - } else { - return None; - } - } - let offset = self.current.trailing_zeros() as usize; - self.current >>= offset; - self.current >>= 1; // shift otherwise overflows for 0b1000_0000_…_0000 - self.idx += offset + 1; - return Some(self.idx - 1); - } -} - -/// A "bit matrix" is basically a square matrix of booleans -/// represented as one gigantic bitvector. In other words, it is as if -/// you have N bitvectors, each of length N. Note that `elements` here is `N`/ -#[derive(Clone)] -pub struct BitMatrix { - elements: usize, - vector: Vec, -} - -impl BitMatrix { - // Create a new `elements x elements` matrix, initially empty. - pub fn new(elements: usize) -> BitMatrix { - // For every element, we need one bit for every other - // element. Round up to an even number of u64s. - let u64s_per_elem = u64s(elements); - BitMatrix { - elements: elements, - vector: vec![0; elements * u64s_per_elem], - } - } - - /// The range of bits for a given element. - fn range(&self, element: usize) -> (usize, usize) { - let u64s_per_elem = u64s(self.elements); - let start = element * u64s_per_elem; - (start, start + u64s_per_elem) - } - - pub fn add(&mut self, source: usize, target: usize) -> bool { - let (start, _) = self.range(source); - let (word, mask) = word_mask(target); - let mut vector = &mut self.vector[..]; - let v1 = vector[start + word]; - let v2 = v1 | mask; - vector[start + word] = v2; - v1 != v2 - } - - /// Do the bits from `source` contain `target`? - /// - /// Put another way, if the matrix represents (transitive) - /// reachability, can `source` reach `target`? - pub fn contains(&self, source: usize, target: usize) -> bool { - let (start, _) = self.range(source); - let (word, mask) = word_mask(target); - (self.vector[start + word] & mask) != 0 - } - - /// Returns those indices that are reachable from both `a` and - /// `b`. This is an O(n) operation where `n` is the number of - /// elements (somewhat independent from the actual size of the - /// intersection, in particular). - pub fn intersection(&self, a: usize, b: usize) -> Vec { - let (a_start, a_end) = self.range(a); - let (b_start, b_end) = self.range(b); - let mut result = Vec::with_capacity(self.elements); - for (base, (i, j)) in (a_start..a_end).zip(b_start..b_end).enumerate() { - let mut v = self.vector[i] & self.vector[j]; - for bit in 0..64 { - if v == 0 { - break; - } - if v & 0x1 != 0 { - result.push(base * 64 + bit); - } - v >>= 1; - } - } - result - } - - /// Add the bits from `read` to the bits from `write`, - /// return true if anything changed. - /// - /// This is used when computing transitive reachability because if - /// you have an edge `write -> read`, because in that case - /// `write` can reach everything that `read` can (and - /// potentially more). - pub fn merge(&mut self, read: usize, write: usize) -> bool { - let (read_start, read_end) = self.range(read); - let (write_start, write_end) = self.range(write); - let vector = &mut self.vector[..]; - let mut changed = false; - for (read_index, write_index) in (read_start..read_end).zip(write_start..write_end) { - let v1 = vector[write_index]; - let v2 = v1 | vector[read_index]; - vector[write_index] = v2; - changed = changed | (v1 != v2); - } - changed - } -} - -fn u64s(elements: usize) -> usize { - (elements + 63) / 64 -} - -fn word_mask(index: usize) -> (usize, u64) { - let word = index / 64; - let mask = 1 << (index % 64); - (word, mask) -} - -#[test] -fn bitvec_iter_works() { - let mut bitvec = BitVector::new(100); - bitvec.insert(1); - bitvec.insert(10); - bitvec.insert(19); - bitvec.insert(62); - bitvec.insert(63); - bitvec.insert(64); - bitvec.insert(65); - bitvec.insert(66); - bitvec.insert(99); - assert_eq!(bitvec.iter().collect::>(), - [1, 10, 19, 62, 63, 64, 65, 66, 99]); -} - -#[test] -fn bitvec_iter_works_2() { - let mut bitvec = BitVector::new(300); - bitvec.insert(1); - bitvec.insert(10); - bitvec.insert(19); - bitvec.insert(62); - bitvec.insert(66); - bitvec.insert(99); - bitvec.insert(299); - assert_eq!(bitvec.iter().collect::>(), - [1, 10, 19, 62, 66, 99, 299]); - -} - -#[test] -fn bitvec_iter_works_3() { - let mut bitvec = BitVector::new(319); - bitvec.insert(0); - bitvec.insert(127); - bitvec.insert(191); - bitvec.insert(255); - bitvec.insert(319); - assert_eq!(bitvec.iter().collect::>(), [0, 127, 191, 255, 319]); -} - -#[test] -fn union_two_vecs() { - let mut vec1 = BitVector::new(65); - let mut vec2 = BitVector::new(65); - assert!(vec1.insert(3)); - assert!(!vec1.insert(3)); - assert!(vec2.insert(5)); - assert!(vec2.insert(64)); - assert!(vec1.insert_all(&vec2)); - assert!(!vec1.insert_all(&vec2)); - assert!(vec1.contains(3)); - assert!(!vec1.contains(4)); - assert!(vec1.contains(5)); - assert!(!vec1.contains(63)); - assert!(vec1.contains(64)); -} - -#[test] -fn grow() { - let mut vec1 = BitVector::new(65); - assert!(vec1.insert(3)); - assert!(!vec1.insert(3)); - assert!(vec1.insert(5)); - assert!(vec1.insert(64)); - vec1.grow(128); - assert!(vec1.contains(3)); - assert!(vec1.contains(5)); - assert!(vec1.contains(64)); - assert!(!vec1.contains(126)); -} - -#[test] -fn matrix_intersection() { - let mut vec1 = BitMatrix::new(200); - - // (*) Elements reachable from both 2 and 65. - - vec1.add(2, 3); - vec1.add(2, 6); - vec1.add(2, 10); // (*) - vec1.add(2, 64); // (*) - vec1.add(2, 65); - vec1.add(2, 130); - vec1.add(2, 160); // (*) - - vec1.add(64, 133); - - vec1.add(65, 2); - vec1.add(65, 8); - vec1.add(65, 10); // (*) - vec1.add(65, 64); // (*) - vec1.add(65, 68); - vec1.add(65, 133); - vec1.add(65, 160); // (*) - - let intersection = vec1.intersection(2, 64); - assert!(intersection.is_empty()); - - let intersection = vec1.intersection(2, 65); - assert_eq!(intersection, &[10, 64, 160]); -} diff --git a/old/ena/mod.rs b/old/ena/mod.rs deleted file mode 100644 index 36b9093a0c..0000000000 --- a/old/ena/mod.rs +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! An implementation of union-find. See the `unify` module for more -//! details. - -pub mod snapshot_vec; -pub mod unify; diff --git a/old/ena/snapshot_vec.rs b/old/ena/snapshot_vec.rs deleted file mode 100644 index 4c1c2614a2..0000000000 --- a/old/ena/snapshot_vec.rs +++ /dev/null @@ -1,370 +0,0 @@ -// Copyright 2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! A utility class for implementing "snapshottable" things; a snapshottable data structure permits -//! you to take a snapshot (via `start_snapshot`) and then, after making some changes, elect either -//! to rollback to the start of the snapshot or commit those changes. -//! -//! This vector is intended to be used as part of an abstraction, not serve as a complete -//! abstraction on its own. As such, while it will roll back most changes on its own, it also -//! supports a `get_mut` operation that gives you an arbitrary mutable pointer into the vector. To -//! ensure that any changes you make this with this pointer are rolled back, you must invoke -//! `record` to record any changes you make and also supplying a delegate capable of reversing -//! those changes. - -use self::UndoLog::*; - -use std::fmt; -use std::mem; -use std::ops; - -#[derive(Debug)] -pub enum UndoLog { - /// New variable with given index was created. - NewElem(usize), - - /// Variable with given index was changed *from* the given value. - SetElem(usize, D::Value), - - /// Extensible set of actions - Other(D::Undo), -} - -pub struct SnapshotVec { - values: Vec, - undo_log: Vec>, - num_open_snapshots: usize, -} - -impl fmt::Debug for SnapshotVec - where D: SnapshotVecDelegate, - D: fmt::Debug, - D::Undo: fmt::Debug, - D::Value: fmt::Debug -{ - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.debug_struct("SnapshotVec") - .field("values", &self.values) - .field("undo_log", &self.undo_log) - .field("num_open_snapshots", &self.num_open_snapshots) - .finish() - } -} - -// Snapshots are tokens that should be created/consumed linearly. -pub struct Snapshot { - // Length of the undo log at the time the snapshot was taken. - length: usize, -} - -pub trait SnapshotVecDelegate { - type Value; - type Undo; - - fn reverse(values: &mut Vec, action: Self::Undo); -} - -// HACK(eddyb) manual impl avoids `Default` bound on `D`. -impl Default for SnapshotVec { - fn default() -> Self { - SnapshotVec { - values: Vec::new(), - undo_log: Vec::new(), - num_open_snapshots: 0, - } - } -} - -impl SnapshotVec { - pub fn new() -> Self { - Self::default() - } - - pub fn with_capacity(c: usize) -> SnapshotVec { - SnapshotVec { - values: Vec::with_capacity(c), - undo_log: Vec::new(), - num_open_snapshots: 0, - } - } - - fn in_snapshot(&self) -> bool { - self.num_open_snapshots > 0 - } - - pub fn record(&mut self, action: D::Undo) { - if self.in_snapshot() { - self.undo_log.push(Other(action)); - } - } - - pub fn len(&self) -> usize { - self.values.len() - } - - pub fn push(&mut self, elem: D::Value) -> usize { - let len = self.values.len(); - self.values.push(elem); - - if self.in_snapshot() { - self.undo_log.push(NewElem(len)); - } - - len - } - - pub fn get(&self, index: usize) -> &D::Value { - &self.values[index] - } - - /// Reserve space for new values, just like an ordinary vec. - pub fn reserve(&mut self, additional: usize) { - // This is not affected by snapshots or anything. - self.values.reserve(additional); - } - - /// Returns a mutable pointer into the vec; whatever changes you make here cannot be undone - /// automatically, so you should be sure call `record()` with some sort of suitable undo - /// action. - pub fn get_mut(&mut self, index: usize) -> &mut D::Value { - &mut self.values[index] - } - - /// Updates the element at the given index. The old value will saved (and perhaps restored) if - /// a snapshot is active. - pub fn set(&mut self, index: usize, new_elem: D::Value) { - let old_elem = mem::replace(&mut self.values[index], new_elem); - if self.in_snapshot() { - self.undo_log.push(SetElem(index, old_elem)); - } - } - - /// Updates all elements. Potentially more efficient -- but - /// otherwise equivalent to -- invoking `set` for each element. - pub fn set_all(&mut self, mut new_elems: impl FnMut(usize) -> D::Value) { - if !self.in_snapshot() { - for (slot, index) in self.values.iter_mut().zip(0..) { - *slot = new_elems(index); - } - } else { - for i in 0..self.values.len() { - self.set(i, new_elems(i)); - } - } - } - - pub fn update(&mut self, index: usize, op: OP) - where - OP: FnOnce(&mut D::Value), - D::Value: Clone, - { - if self.in_snapshot() { - let old_elem = self.values[index].clone(); - self.undo_log.push(SetElem(index, old_elem)); - } - op(&mut self.values[index]); - } - - pub fn start_snapshot(&mut self) -> Snapshot { - let length = self.undo_log.len(); - self.num_open_snapshots += 1; - Snapshot { length: length } - } - - pub fn actions_since_snapshot(&self, snapshot: &Snapshot) -> &[UndoLog] { - &self.undo_log[snapshot.length..] - } - - fn assert_open_snapshot(&self, snapshot: &Snapshot) { - // Failures here may indicate a failure to follow a stack discipline. - assert!(self.undo_log.len() >= snapshot.length); - assert!(self.num_open_snapshots > 0); - } - - pub fn rollback_to(&mut self, snapshot: Snapshot) { - debug!("rollback_to({})", snapshot.length); - - self.assert_open_snapshot(&snapshot); - - while self.undo_log.len() > snapshot.length { - match self.undo_log.pop().unwrap() { - NewElem(i) => { - self.values.pop(); - assert!(self.values.len() == i); - } - - SetElem(i, v) => { - self.values[i] = v; - } - - Other(u) => { - D::reverse(&mut self.values, u); - } - } - } - - self.num_open_snapshots -= 1; - } - - /// Commits all changes since the last snapshot. Of course, they - /// can still be undone if there is a snapshot further out. - pub fn commit(&mut self, snapshot: Snapshot) { - debug!("commit({})", snapshot.length); - - self.assert_open_snapshot(&snapshot); - - if self.num_open_snapshots == 1 { - // The root snapshot. It's safe to clear the undo log because - // there's no snapshot further out that we might need to roll back - // to. - assert!(snapshot.length == 0); - self.undo_log.clear(); - } - - self.num_open_snapshots -= 1; - } -} - -impl ops::Deref for SnapshotVec { - type Target = [D::Value]; - fn deref(&self) -> &[D::Value] { - &*self.values - } -} - -impl ops::DerefMut for SnapshotVec { - fn deref_mut(&mut self) -> &mut [D::Value] { - &mut *self.values - } -} - -impl ops::Index for SnapshotVec { - type Output = D::Value; - fn index(&self, index: usize) -> &D::Value { - self.get(index) - } -} - -impl ops::IndexMut for SnapshotVec { - fn index_mut(&mut self, index: usize) -> &mut D::Value { - self.get_mut(index) - } -} - -impl Extend for SnapshotVec { - fn extend(&mut self, iterable: T) - where - T: IntoIterator, - { - let initial_len = self.values.len(); - self.values.extend(iterable); - let final_len = self.values.len(); - - if self.in_snapshot() { - self.undo_log.extend((initial_len..final_len).map(|len| NewElem(len))); - } - } -} - -impl Clone for SnapshotVec -where - D::Value: Clone, - D::Undo: Clone, -{ - fn clone(&self) -> Self { - SnapshotVec { - values: self.values.clone(), - undo_log: self.undo_log.clone(), - num_open_snapshots: self.num_open_snapshots, - } - } -} - -impl Clone for UndoLog -where - D::Value: Clone, - D::Undo: Clone, -{ - fn clone(&self) -> Self { - match *self { - NewElem(i) => NewElem(i), - SetElem(i, ref v) => SetElem(i, v.clone()), - Other(ref u) => Other(u.clone()), - } - } -} - -impl SnapshotVecDelegate for i32 { - type Value = i32; - type Undo = (); - - fn reverse(_: &mut Vec, _: ()) {} -} - -#[test] -fn basic() { - let mut vec: SnapshotVec = SnapshotVec::default(); - assert!(!vec.in_snapshot()); - assert_eq!(vec.len(), 0); - vec.push(22); - vec.push(33); - assert_eq!(vec.len(), 2); - assert_eq!(*vec.get(0), 22); - assert_eq!(*vec.get(1), 33); - vec.set(1, 34); - assert_eq!(vec.len(), 2); - assert_eq!(*vec.get(0), 22); - assert_eq!(*vec.get(1), 34); - - let snapshot = vec.start_snapshot(); - assert!(vec.in_snapshot()); - - vec.push(44); - vec.push(55); - vec.set(1, 35); - assert_eq!(vec.len(), 4); - assert_eq!(*vec.get(0), 22); - assert_eq!(*vec.get(1), 35); - assert_eq!(*vec.get(2), 44); - assert_eq!(*vec.get(3), 55); - - vec.rollback_to(snapshot); - assert!(!vec.in_snapshot()); - - assert_eq!(vec.len(), 2); - assert_eq!(*vec.get(0), 22); - assert_eq!(*vec.get(1), 34); -} - -#[test] -#[should_panic] -fn out_of_order() { - let mut vec: SnapshotVec = SnapshotVec::default(); - vec.push(22); - let snapshot1 = vec.start_snapshot(); - vec.push(33); - let snapshot2 = vec.start_snapshot(); - vec.push(44); - vec.rollback_to(snapshot1); // bogus, but accepted - vec.rollback_to(snapshot2); // asserts -} - -#[test] -fn nested_commit_then_rollback() { - let mut vec: SnapshotVec = SnapshotVec::default(); - vec.push(22); - let snapshot1 = vec.start_snapshot(); - let snapshot2 = vec.start_snapshot(); - vec.set(0, 23); - vec.commit(snapshot2); - assert_eq!(*vec.get(0), 23); - vec.rollback_to(snapshot1); - assert_eq!(*vec.get(0), 22); -} diff --git a/old/ena/unify/backing_vec.rs b/old/ena/unify/backing_vec.rs deleted file mode 100644 index 3d4ffbcf43..0000000000 --- a/old/ena/unify/backing_vec.rs +++ /dev/null @@ -1,214 +0,0 @@ -// This is a fork of ena, whose copyright and license info is in ena/unify/mod.rs - -#[cfg(feature = "persistent")] -use dogged::DVec; -use ena::snapshot_vec as sv; -use std::ops; -use std::marker::PhantomData; -use std::fmt::Debug; - -use super::{VarValue, UnifyKey}; - -#[allow(dead_code)] // rustc BUG -#[allow(type_alias_bounds)] -type Key = ::Key; - -/// Largely internal trait implemented by the unification table -/// backing store types. The most common such type is `InPlace`, -/// which indicates a standard, mutable unification table. -pub trait UnificationStore: - ops::Index>> + Clone + Default -{ - type Key: UnifyKey; - type Value: Debug + Clone; - type Snapshot; - - fn start_snapshot(&mut self) -> Self::Snapshot; - - fn rollback_to(&mut self, snapshot: Self::Snapshot); - - fn commit(&mut self, snapshot: Self::Snapshot); - - fn reset_unifications( - &mut self, - value: impl FnMut(u32) -> VarValue, - ); - - fn len(&self) -> usize; - - fn push(&mut self, value: VarValue); - - fn reserve(&mut self, num_new_values: usize); - - fn update(&mut self, index: usize, op: F) - where F: FnOnce(&mut VarValue); - - fn tag() -> &'static str { - Self::Key::tag() - } -} - -/// Backing store for an in-place unification table. -/// Not typically used directly. -#[derive(Clone, Debug)] -pub struct InPlace { - values: sv::SnapshotVec> -} - -// HACK(eddyb) manual impl avoids `Default` bound on `K`. -impl Default for InPlace { - fn default() -> Self { - InPlace { values: sv::SnapshotVec::new() } - } -} - -impl UnificationStore for InPlace { - type Key = K; - type Value = K::Value; - type Snapshot = sv::Snapshot; - - #[inline] - fn start_snapshot(&mut self) -> Self::Snapshot { - self.values.start_snapshot() - } - - #[inline] - fn rollback_to(&mut self, snapshot: Self::Snapshot) { - self.values.rollback_to(snapshot); - } - - #[inline] - fn commit(&mut self, snapshot: Self::Snapshot) { - self.values.commit(snapshot); - } - - #[inline] - fn reset_unifications( - &mut self, - mut value: impl FnMut(u32) -> VarValue, - ) { - self.values.set_all(|i| value(i as u32)); - } - - #[inline] - fn len(&self) -> usize { - self.values.len() - } - - #[inline] - fn push(&mut self, value: VarValue) { - self.values.push(value); - } - - #[inline] - fn reserve(&mut self, num_new_values: usize) { - self.values.reserve(num_new_values); - } - - #[inline] - fn update(&mut self, index: usize, op: F) - where F: FnOnce(&mut VarValue) - { - self.values.update(index, op) - } -} - -impl ops::Index for InPlace - where K: UnifyKey -{ - type Output = VarValue; - fn index(&self, index: usize) -> &VarValue { - &self.values[index] - } -} - -#[derive(Copy, Clone, Debug)] -struct Delegate(PhantomData); - -impl sv::SnapshotVecDelegate for Delegate { - type Value = VarValue; - type Undo = (); - - fn reverse(_: &mut Vec>, _: ()) {} -} - -#[cfg(feature = "persistent")] -#[derive(Clone, Debug)] -pub struct Persistent { - values: DVec> -} - -// HACK(eddyb) manual impl avoids `Default` bound on `K`. -#[cfg(feature = "persistent")] -impl Default for Persistent { - fn default() -> Self { - Persistent { values: DVec::new() } - } -} - -#[cfg(feature = "persistent")] -impl UnificationStore for Persistent { - type Key = K; - type Value = K::Value; - type Snapshot = Self; - - #[inline] - fn start_snapshot(&mut self) -> Self::Snapshot { - self.clone() - } - - #[inline] - fn rollback_to(&mut self, snapshot: Self::Snapshot) { - *self = snapshot; - } - - #[inline] - fn commit(&mut self, _snapshot: Self::Snapshot) { - } - - #[inline] - fn reset_unifications( - &mut self, - mut value: impl FnMut(u32) -> VarValue, - ) { - // Without extending dogged, there isn't obviously a more - // efficient way to do this. But it's pretty dumb. Maybe - // dogged needs a `map`. - for i in 0 .. self.values.len() { - self.values[i] = value(i as u32); - } - } - - #[inline] - fn len(&self) -> usize { - self.values.len() - } - - #[inline] - fn push(&mut self, value: VarValue) { - self.values.push(value); - } - - #[inline] - fn reserve(&mut self, _num_new_values: usize) { - // not obviously relevant to DVec. - } - - #[inline] - fn update(&mut self, index: usize, op: F) - where F: FnOnce(&mut VarValue) - { - let p = &mut self.values[index]; - op(p); - } -} - -#[cfg(feature = "persistent")] -impl ops::Index for Persistent - where K: UnifyKey -{ - type Output = VarValue; - fn index(&self, index: usize) -> &VarValue { - &self.values[index] - } -} diff --git a/old/ena/unify/mod.rs b/old/ena/unify/mod.rs deleted file mode 100644 index 42526544f7..0000000000 --- a/old/ena/unify/mod.rs +++ /dev/null @@ -1,444 +0,0 @@ -// This is a fork of ena, whose copyright and license info is below. -// -// The fork was made primarily in order to support unifying type unions, which -// requires looking up the current values of keys in the middle of unification. -// This fork implements that by replacing the UnificationValue trait with -// FnOnce callbacks which accept the table as well as the values to unify. - -// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Union-find implementation. The main type is `UnificationTable`. -//! -//! You can define your own type for the *keys* in the table, but you -//! must implement `UnifyKey` for that type. The assumption is that -//! keys will be newtyped integers, hence we require that they -//! implement `Copy`. -//! -//! Keys can have values associated with them. The assumption is that -//! these values are cheaply cloneable (ideally, `Copy`), and some of -//! the interfaces are oriented around that assumption. If you just -//! want the classical "union-find" algorithm where you group things -//! into sets, use the `Value` type of `()`. -//! -//! When you have keys with non-trivial values, you must also define -//! how those values can be merged. -//! -//! The best way to see how it is used is to read the `tests.rs` file; -//! search for e.g. `UnitKey`. - -use std::marker; -use std::fmt::Debug; - -mod backing_vec; -pub use self::backing_vec::{InPlace, UnificationStore}; - -#[cfg(feature = "persistent")] -pub use self::backing_vec::Persistent; - - -#[cfg(test)] -mod tests; - -/// This trait is implemented by any type that can serve as a type -/// variable. We call such variables *unification keys*. For example, -/// this trait is implemented by `IntVid`, which represents integral -/// variables. -/// -/// Each key type has an associated value type `V`. For example, for -/// `IntVid`, this is `Option`, representing some -/// (possibly not yet known) sort of integer. -/// -/// Clients are expected to provide implementations of this trait; you -/// can see some examples in the `test` module. -pub trait UnifyKey: Copy + Clone + Debug + PartialEq { - type Value: Clone + Debug; - - fn index(&self) -> u32; - - fn from_index(u: u32) -> Self; - - fn tag() -> &'static str; - - /// If true, then `self` should be preferred as root to `other`. - /// Note that we assume a consistent partial ordering, so - /// returning true implies that `other.prefer_as_root_to(self)` - /// would return false. If there is no ordering between two keys - /// (i.e., `a.prefer_as_root_to(b)` and `b.prefer_as_root_to(a)` - /// both return false) then the rank will be used to determine the - /// root in an optimal way. - /// - /// NB. The only reason to implement this method is if you want to - /// control what value is returned from `find()`. In general, it - /// is better to let the unification table determine the root, - /// since overriding the rank can cause execution time to increase - /// dramatically. - #[allow(unused_variables)] - fn order_roots( - a: Self, - a_value: &Self::Value, - b: Self, - b_value: &Self::Value, - ) -> Option<(Self, Self)> { - None - } -} - - -/// Value of a unification key. We implement Tarjan's union-find -/// algorithm: when two keys are unified, one of them is converted -/// into a "redirect" pointing at the other. These redirects form a -/// DAG: the roots of the DAG (nodes that are not redirected) are each -/// associated with a value of type `V` and a rank. The rank is used -/// to keep the DAG relatively balanced, which helps keep the running -/// time of the algorithm under control. For more information, see -/// . -#[derive(PartialEq, Clone, Debug)] -pub struct VarValue { // FIXME pub - parent: K, // if equal to self, this is a root - value: K::Value, // value assigned (only relevant to root) - rank: u32, // max depth (only relevant to root) -} - -/// Table of unification keys and their values. You must define a key type K -/// that implements the `UnifyKey` trait. Unification tables can be used in two-modes: -/// -/// - in-place (`UnificationTable>` or `InPlaceUnificationTable`): -/// - This is the standard mutable mode, where the array is modified -/// in place. -/// - To do backtracking, you can employ the `snapshot` and `rollback_to` -/// methods. -/// - persistent (`UnificationTable>` or `PersistentUnificationTable`): -/// - In this mode, we use a persistent vector to store the data, so that -/// cloning the table is an O(1) operation. -/// - This implies that ordinary operations are quite a bit slower though. -/// - Requires the `persistent` feature be selected in your Cargo.toml file. -#[derive(Clone, Debug, Default)] -pub struct UnificationTable { - /// Indicates the current value of each key. - values: S, -} - -/// A unification table that uses an "in-place" vector. -#[allow(type_alias_bounds)] -pub type InPlaceUnificationTable = UnificationTable>; - -/// A unification table that uses a "persistent" vector. -#[cfg(feature = "persistent")] -#[allow(type_alias_bounds)] -pub type PersistentUnificationTable = UnificationTable>; - -/// At any time, users may snapshot a unification table. The changes -/// made during the snapshot may either be *committed* or *rolled back*. -pub struct Snapshot { - // Link snapshot to the unification store `S` of the table. - marker: marker::PhantomData, - snapshot: S::Snapshot, -} - -impl VarValue { - fn new_var(key: K, value: K::Value) -> VarValue { - VarValue::new(key, value, 0) - } - - fn new(parent: K, value: K::Value, rank: u32) -> VarValue { - VarValue { - parent: parent, // this is a root - value: value, - rank: rank, - } - } - - fn redirect(&mut self, to: K) { - self.parent = to; - } - - fn root(&mut self, rank: u32, value: K::Value) { - self.rank = rank; - self.value = value; - } - - fn parent(&self, self_key: K) -> Option { - self.if_not_self(self.parent, self_key) - } - - fn if_not_self(&self, key: K, self_key: K) -> Option { - if key == self_key { - None - } else { - Some(key) - } - } -} - -// We can't use V:LatticeValue, much as I would like to, -// because frequently the pattern is that V=Option for some -// other type parameter U, and we have no way to say -// Option:LatticeValue. - -impl UnificationTable { - pub fn new() -> Self { - Self::default() - } - - /// Starts a new snapshot. Each snapshot must be either - /// rolled back or committed in a "LIFO" (stack) order. - pub fn snapshot(&mut self) -> Snapshot { - Snapshot { - marker: marker::PhantomData::, - snapshot: self.values.start_snapshot(), - } - } - - /// Reverses all changes since the last snapshot. Also - /// removes any keys that have been created since then. - pub fn rollback_to(&mut self, snapshot: Snapshot) { - debug!("{}: rollback_to()", S::tag()); - self.values.rollback_to(snapshot.snapshot); - } - - /// Commits all changes since the last snapshot. Of course, they - /// can still be undone if there is a snapshot further out. - pub fn commit(&mut self, snapshot: Snapshot) { - debug!("{}: commit()", S::tag()); - self.values.commit(snapshot.snapshot); - } - - /// Creates a fresh key with the given value. - pub fn new_key(&mut self, value: S::Value) -> S::Key { - let len = self.values.len(); - let key: S::Key = UnifyKey::from_index(len as u32); - self.values.push(VarValue::new_var(key, value)); - debug!("{}: created new key: {:?}", S::tag(), key); - key - } - - /// Reserve memory for `num_new_keys` to be created. Does not - /// actually create the new keys; you must then invoke `new_key`. - pub fn reserve(&mut self, num_new_keys: usize) { - self.values.reserve(num_new_keys); - } - - /// Clears all unifications that have been performed, resetting to - /// the initial state. The values of each variable are given by - /// the closure. - pub fn reset_unifications( - &mut self, - mut value: impl FnMut(S::Key) -> S::Value, - ) { - self.values.reset_unifications(|i| { - let key = UnifyKey::from_index(i as u32); - let value = value(key); - VarValue::new_var(key, value) - }); - } - - /// Returns the number of keys created so far. - pub fn len(&self) -> usize { - self.values.len() - } - - /// Obtains the current value for a particular key. - /// Not for end-users; they can use `probe_value`. - fn value(&self, key: S::Key) -> &VarValue { - &self.values[key.index() as usize] - } - - /// Find the root node for `vid`. This uses the standard - /// union-find algorithm with path compression: - /// . - /// - /// NB. This is a building-block operation and you would probably - /// prefer to call `probe` below. - fn get_root_key(&mut self, vid: S::Key) -> S::Key { - let redirect = { - match self.value(vid).parent(vid) { - None => return vid, - Some(redirect) => redirect, - } - }; - - let root_key: S::Key = self.get_root_key(redirect); - if root_key != redirect { - // Path compression - self.update_value(vid, |value| value.parent = root_key); - } - - root_key - } - - fn update_value(&mut self, key: S::Key, op: OP) - where - OP: FnOnce(&mut VarValue), - { - self.values.update(key.index() as usize, op); - debug!("Updated variable {:?} to {:?}", key, self.value(key)); - } - - /// Either redirects `node_a` to `node_b` or vice versa, depending - /// on the relative rank. The value associated with the new root - /// will be `new_value`. - /// - /// NB: This is the "union" operation of "union-find". It is - /// really more of a building block. If the values associated with - /// your key are non-trivial, you would probably prefer to call - /// `unify_var_var` below. - fn unify_roots(&mut self, key_a: S::Key, key_b: S::Key, new_value: S::Value) { - debug!("unify(key_a={:?}, key_b={:?})", key_a, key_b); - - let rank_a = self.value(key_a).rank; - let rank_b = self.value(key_b).rank; - if let Some((new_root, redirected)) = - S::Key::order_roots( - key_a, - &self.value(key_a).value, - key_b, - &self.value(key_b).value, - ) { - // compute the new rank for the new root that they chose; - // this may not be the optimal choice. - let new_rank = if new_root == key_a { - debug_assert!(redirected == key_b); - if rank_a > rank_b { - rank_a - } else { - rank_b + 1 - } - } else { - debug_assert!(new_root == key_b); - debug_assert!(redirected == key_a); - if rank_b > rank_a { - rank_b - } else { - rank_a + 1 - } - }; - self.redirect_root(new_rank, redirected, new_root, new_value); - } else if rank_a > rank_b { - // a has greater rank, so a should become b's parent, - // i.e., b should redirect to a. - self.redirect_root(rank_a, key_b, key_a, new_value); - } else if rank_a < rank_b { - // b has greater rank, so a should redirect to b. - self.redirect_root(rank_b, key_a, key_b, new_value); - } else { - // If equal, redirect one to the other and increment the - // other's rank. - self.redirect_root(rank_a + 1, key_a, key_b, new_value); - } - } - - /// Internal method to redirect `old_root_key` (which is currently - /// a root) to a child of `new_root_key` (which will remain a - /// root). The rank and value of `new_root_key` will be updated to - /// `new_rank` and `new_value` respectively. - fn redirect_root( - &mut self, - new_rank: u32, - old_root_key: S::Key, - new_root_key: S::Key, - new_value: S::Value, - ) { - self.update_value(old_root_key, |old_root_value| { - old_root_value.redirect(new_root_key); - }); - self.update_value(new_root_key, |new_root_value| { - new_root_value.root(new_rank, new_value); - }); - } -} - -/// //////////////////////////////////////////////////////////////////////// -/// Public API - -impl<'tcx, S, K, V> UnificationTable -where - S: UnificationStore, - K: UnifyKey, - V: Debug + Clone, -{ - /// Unions two keys without the possibility of failure; only - /// applicable when unify values use `NoError` as their error - /// type. - pub fn union(&mut self, a_id: K1, b_id: K2, unify_values: F) - where - K1: Into, - K2: Into, - V: Debug + Clone, - F: FnOnce(&Self, &V, &V) -> V - { - let a_id = a_id.into(); - let b_id = b_id.into(); - - let root_a = self.get_root_key(a_id); - let root_b = self.get_root_key(b_id); - - if root_a == root_b { - return; - } - - let combined = unify_values(&self, &self.value(root_a).value, &self.value(root_b).value); - - self.unify_roots(root_a, root_b, combined); - } - - /// Unions a key and a value without the possibility of failure. - pub fn union_value(&mut self, id: K1, value: V, unify_values: F) - where - K1: Into, - V: Debug + Clone, - F: FnOnce(&Self, &V, &V) -> V - { - self.unify_var_value(id, value, unify_values); - } - - /// Given two keys, indicates whether they have been unioned together. - pub fn unioned(&mut self, a_id: K1, b_id: K2) -> bool - where - K1: Into, - K2: Into, - { - self.find(a_id) == self.find(b_id) - } - - /// Given a key, returns the (current) root key. - pub fn find(&mut self, id: K1) -> K - where - K1: Into, - { - let id = id.into(); - self.get_root_key(id) - } - - /// Sets the value of the key `a_id` to `b`, attempting to merge - /// with the previous value. - pub fn unify_var_value(&mut self, a_id: K1, b: V, unify_values: F) - where - K1: Into, - F: FnOnce(&Self, &V, &V) -> V - { - let a_id = a_id.into(); - let root_a = self.get_root_key(a_id); - let value = unify_values(&self, &self.value(root_a).value, &b); - self.update_value(root_a, |node| node.value = value); - } - - /// Returns the current value for the given key. If the key has - /// been union'd, this will give the value from the current root. - pub fn probe_value(&mut self, id: K1) -> V - where - K1: Into, - { - let id = id.into(); - let id = self.get_root_key(id); - self.value(id).value.clone() - } -} - diff --git a/old/expr.rs b/old/expr.rs deleted file mode 100644 index c4ea222347..0000000000 --- a/old/expr.rs +++ /dev/null @@ -1,44 +0,0 @@ -use name::Name; -use typ::Type; - -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] -pub enum Operator { - Plus, Minus, Star, Slash, DoubleSlash, -} - -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] -pub enum Builtin { - // Default - Negate, - Not, - - // String - // StringLength, -} - - -#[derive(Debug, PartialEq)] -pub enum Expr { - Int(i64), - Ratio(i64, u64), - - // Functions - CallOperator(Box, Operator, Box), - CallBuiltin(Builtin, Box), - CallLambda(Box, Box), -} - -#[derive(Debug, PartialEq)] -pub enum Pattern { - Name(Name), // `foo =` - As(Name, Box), // ` as foo` - Type(Type), - Symbol(String), - String(String), - Char(char), - Int(i64), - Float(f64), - Tuple(Vec), - Record(Vec<(Name, Option)>), // { a = 5, b : Int as x, c } -} - diff --git a/old/interpret.rs b/old/interpret.rs deleted file mode 100644 index 475c9b4115..0000000000 --- a/old/interpret.rs +++ /dev/null @@ -1,57 +0,0 @@ -use unify::Expr; -use unify::Literal; - -pub fn eval<'a>(expr: &'a Expr<'a>) -> &'a Literal<'a> { - match expr { - Expr::Literal(literal) => literal, - Expr::Assignment(_, subexpr) => eval(subexpr), - Expr::If(cond, if_true, if_false) => { - match eval(cond) { - Literal::Symbol("True") => eval(if_true), - Literal::Symbol("False") => eval(if_false), - _ => { - panic!("somehow an if-conditional did not evaluate to True or False!") - } - } - } - } -} - -pub fn literal_to_string<'a>(literal: &'a Literal<'a>) -> String { - match literal { - Literal::String(str) => format!("\"{}\"", str), - Literal::Char(character) => format!("'{}'", character), - Literal::Symbol(str) => str.to_string(), - Literal::HexOctalBinary(str) => str.to_string(), - Literal::Number(str) => str.to_string(), - Literal::Record(field_exprs) => { - let mut field_strings = Vec::new(); - - for (field, subexpr) in field_exprs { - let val = literal_to_string(eval(subexpr)); - - field_strings.push(format!("{} = {}", field, val)); - } - - format!("{{ {} }}", field_strings.join(", ")) - }, - Literal::Tuple(elem_exprs) => { - let mut elem_strings = Vec::new(); - - for elem_expr in elem_exprs { - elem_strings.push(literal_to_string(eval(elem_expr))); - } - - format!("({})", elem_strings.join(", ")) - }, - Literal::Array(elem_exprs) => { - let mut elem_strings = Vec::new(); - - for elem_expr in elem_exprs { - elem_strings.push(literal_to_string(eval(elem_expr))); - } - - format!("[{}]", elem_strings.join(", ")) - }, - } -} diff --git a/old/lib.rs b/old/lib.rs deleted file mode 100644 index 50a8919698..0000000000 --- a/old/lib.rs +++ /dev/null @@ -1,22 +0,0 @@ -#![feature(box_syntax, box_patterns)] - -// pub mod unify; -// pub mod interpret; -// pub mod repl; - -pub mod solve; -pub mod expr; -pub mod constrain; -pub mod canonical; -pub mod name; -pub mod typ; -pub mod parse; -mod ena; - -#[macro_use] -extern crate log; - -#[cfg(feature = "persistent")] -extern crate dogged; - -#[macro_use] extern crate combine; diff --git a/old/name.rs b/old/name.rs deleted file mode 100644 index 13a9337411..0000000000 --- a/old/name.rs +++ /dev/null @@ -1 +0,0 @@ -pub type Name = String; diff --git a/old/parse.rs b/old/parse.rs deleted file mode 100644 index bcfa5fd5ad..0000000000 --- a/old/parse.rs +++ /dev/null @@ -1,335 +0,0 @@ -use expr::Operator; -use expr::Expr; - -use std::char; -use std::iter; - -use combine::parser::char::{char, string, letter, alpha_num, spaces, digit, hex_digit, HexDigit}; -use combine::parser::repeat::{many, count_min_max}; -use combine::parser::item::{any, satisfy, satisfy_map, value}; -use combine::{choice, many1, parser, Parser, optional, between, unexpected_any}; -use combine::error::{Consumed, ParseError}; -use combine::stream::{Stream}; - - -pub const ERR_EMPTY_CHAR: &'static str = "EMPTY_CHAR"; - -pub fn expr() -> impl Parser -where I: Stream, - I::Error: ParseError -{ - // TODO change to expr() to reproduce rust compiler bug - expr_() -} - -// This macro allows recursive parsers -parser! { - #[inline(always)] - fn expr_[I]()(I) -> Expr - where [ I: Stream ] - { - choice(( - number_literal(), - ident(), - )).skip(spaces()).and( - // Optionally follow the expression with an operator, - // - // e.g. In the expression (1 + 2), the subexpression 1 - // is followed by the operator + and another subexpression, 2 - optional( - operator() - .skip(spaces()) - .and(expr() - ) - )).map(|(v1, maybe_op)| { - match maybe_op { - None => v1, - Some((op, v2)) => { - Expr::CallOperator(Box::new(v1), op, Box::new(v2)) - }, - } - }) - } -} - -pub fn operator() -> impl Parser -where I: Stream, - I::Error: ParseError -{ - choice(( - char('+').map(|_| Operator::Plus), - char('-').map(|_| Operator::Minus), - char('*').map(|_| Operator::Star), - )) -} - -pub fn ident() -> impl Parser -where I: Stream, - I::Error: ParseError -{ - char('.').map(|_| Expr::Int(1)) -} - -pub fn string_literal() -> impl Parser -where I: Stream, - I::Error: ParseError -{ - between(char('"'), char('"'), many(string_body())) - .map(|str| Expr::String(str)) -} - -pub fn char_literal() -> impl Parser -where I: Stream, - I::Error: ParseError -{ - between(char('\''), char('\''), char_body().expected(ERR_EMPTY_CHAR)) - .map(|ch| Expr::Char(ch)) -} - - -fn unicode_code_pt() -> impl Parser -where - I: Stream, - I::Error: ParseError, -{ - // You can put up to 6 hex digits inside \u{...} - // e.g. \u{00A0} or \u{101010} - // They must be no more than 10FFFF - let hex_code_pt = - count_min_max::, HexDigit>(1, 6, hex_digit()) - .then(|hex_digits| { - let hex_str:String = hex_digits.into_iter().collect(); - - match u32::from_str_radix(&hex_str, 16) { - Ok(code_pt) => { - if code_pt > 0x10FFFF { - unexpected_any("Invalid Unicode code point. It must be no more than \\u{10FFFF}.").right() - } else { - match char::from_u32(code_pt) { - Some(ch) => value(ch).left(), - None => unexpected_any("Invalid Unicode code point.").right() - } - } - }, - Err(_) => { - unexpected_any("Invalid hex code - Unicode code points must be specified using hexadecimal characters (the numbers 0-9 and letters A-F)").right() - } - } - }); - - char('u').with(between(char('{'), char('}'), hex_code_pt)) -} - -fn string_body() -> impl Parser -where - I: Stream, - I::Error: ParseError, -{ - parser(|input: &mut I| { - let (parsed_char, consumed) = try!(any().parse_lazy(input).into()); - let mut escaped = satisfy_map(|escaped_char| { - // NOTE! When modifying this, revisit char_body too! - // Their implementations are similar but not the same. - match escaped_char { - '"' => Some('"'), - '\\' => Some('\\'), - 't' => Some('\t'), - 'n' => Some('\n'), - 'r' => Some('\r'), - _ => None, - } - }); - - match parsed_char { - '\\' => { - consumed.combine(|_| { - // Try to parse basic backslash-escaped literals - // e.g. \t, \n, \r - escaped.parse_stream(input).or_else(|_| - // If we didn't find any of those, try \u{...} - unicode_code_pt().parse_stream(input) - ) - }) - }, - '"' => { - // We should never consume a double quote unless - // it's preceded by a backslash - Err(Consumed::Empty(I::Error::empty(input.position()).into())) - }, - _ => Ok((parsed_char, consumed)), - } - }) -} - -fn char_body() -> impl Parser -where - I: Stream, - I::Error: ParseError, -{ - parser(|input: &mut I| { - let (parsed_char, consumed) = try!(any().parse_lazy(input).into()); - let mut escaped = satisfy_map(|escaped_char| { - // NOTE! When modifying this, revisit string_body too! - // Their implementations are similar but not the same. - match escaped_char { - '\'' => Some('\''), - '\\' => Some('\\'), - 't' => Some('\t'), - 'n' => Some('\n'), - 'r' => Some('\r'), - _ => None, - } - }); - - match parsed_char { - '\\' => { - consumed.combine(|_| { - // Try to parse basic backslash-escaped literals - // e.g. \t, \n, \r - escaped.parse_stream(input).or_else(|_| - // If we didn't find any of those, try \u{...} - unicode_code_pt().parse_stream(input) - ) - }) - }, - '\'' => { - // We should never consume a single quote unless - // it's preceded by a backslash - Err(Consumed::Empty(I::Error::empty(input.position()).into())) - }, - _ => Ok((parsed_char, consumed)), - } - }) -} - -pub fn number_literal() -> impl Parser -where I: Stream, - I::Error: ParseError -{ - // Digits before the decimal point can be space-separated - // e.g. one million can be written as 1 000 000 - let digits_before_decimal = many1::, _>(digit().skip(optional(char(' ')))); - let digits_after_decimal = many1::, _>(digit()); - - optional(char('-')) - .and(digits_before_decimal) - .and(optional(char('.').with(digits_after_decimal))) - .map(|((maybe_minus, int_digits), decimals): ((Option, Vec), Option>)| { - let is_positive = maybe_minus.is_none(); - - // TODO check length of digits and make sure not to overflow - let int_str: String = int_digits.into_iter().collect(); - let int_val = int_str.parse::().unwrap(); - - match decimals { - None => { - if is_positive { - Expr::Int(int_val as i64) - } else { - Expr::Int(-int_val as i64) - } - }, - Some(nums) => { - let decimal_str: String = nums.into_iter().collect(); - // calculate numerator and denominator - // e.g. 123.45 == 12345 / 100 - let denom = (10 as i64).pow(decimal_str.len() as u32); - let decimal = decimal_str.parse::().unwrap(); - let numerator = (int_val * denom) + (decimal as i64); - - if is_positive { - Expr::Ratio(numerator, denom as u64) - } else { - Expr::Ratio(-numerator, denom as u64) - } - } - } - }) -} - -// pub fn parse_expr(state: &mut State) -> Result { - -// let digits = chomp_digits(state); - -// if digits.is_empty() { -// Err(Problem::InvalidNumber) -// } else { -// // TODO store these in a bigint, and handle overflow. -// let num = digits.parse::().unwrap(); - -// if decimal_point - - -// Ok(Expr::Int(num)) -// } -// } - -// enum Parsed { -// Expr(Expr), -// Malformed(Problem), -// NotFound -// } - - -// #[inline] -// fn number_parser() -> { -// let has_minus_sign = false; -// let decimal_point_index: usize = 0; -// let len: usize = 0; - -// for ch in state.text.chars() { -// if ch.is_ascii_digit() { -// len += 1; -// } else if ch == '-' { -// if has_minus_sign { -// if len == 1 { -// return Malformed(DoubleMinusSign); -// } else { -// // This second minus sign is a subtraction operator. -// // We've reached the end of the number! -// break; -// } -// } else { -// has_minus_sign = true; -// len += 1; -// } -// } else if ch == '.' { -// if len == 0 { -// return Malformed(NoDigitsBeforeDecimalPoint); -// } else if decimal_point_index != 0 { -// return Malformed(DoubleDecimalPoint); -// } else { -// // This might be a valid decimal number! -// decimal_point_index = len; - -// len += 1; -// } -// } -// } - -// state.col += len; - -// if decimal_point_index == 0 { -// // This is an integer. -// Expr(Expr::Int(parse_int(&state.text[..len]))) -// } else { -// // This is a decimal. -// let before_decimal_pt = &state.text[..decimal_point_index]; -// let after_decimal_pt = &state.text[(decimal_point_index + 1)..]; - -// let numerator_str = before_decimal_pt.to_owned(); -// numerator_str.push_str(after_decimal_pt); - -// let numerator = parse_int(&numerator_str); -// let denominator = 10 * after_decimal_pt.len() as u64; - -// Expr(Expr::Ratio(numerator, denominator)) -// } -// } - -// #[inline] -// fn parse_int(text: &str) -> i64 { -// // TODO parse as BigInt -// text.parse::().unwrap() -// } - diff --git a/old/repl.rs b/old/repl.rs deleted file mode 100644 index 96404959a9..0000000000 --- a/old/repl.rs +++ /dev/null @@ -1,63 +0,0 @@ -use interpret::{eval, literal_to_string}; -use unify::infer; -use unify::Expr; -use unify::Type; - -pub fn eval_and_print<'a>(expr: &Expr<'a>) -> String { - match infer(&expr) { - Ok(typ) => { - let lit = eval(expr); - - format!("{}\n: {}", literal_to_string(lit), type_to_string(true, &typ)) - }, - Err(_) => - "[TYPE MISMATCH!]".to_string() - } -} - -pub fn type_to_string<'a>(outermost: bool, typ: &'a Type<'a>) -> String { - match typ { - Type::Unbound => "*".to_string(), - Type::String => "String".to_string(), - Type::Char => "Char".to_string(), - Type::Int => "Int".to_string(), - Type::Float => "Float".to_string(), - Type::Number => "Int | Float".to_string(), - Type::Symbol(sym) => format!(":{}", sym), - Type::Array(elem_type) => { - let str = format!("Array {}", type_to_string(false, elem_type)); - - if outermost { - str - } else { - format!("({})", str) - } - }, - Type::Record(fields) => { - let field_strings = fields.into_iter().map(|(field, subtyp)| { - let typ_str = type_to_string(false, subtyp); - - format!("{} : {}", field, typ_str) - }); - - format!("{{ {} }}", field_strings.collect::>().join(", ")) - }, - Type::Tuple(elems) => { - let elem_strings = elems.into_iter().map(|subtyp| { type_to_string(false, subtyp) }); - let str = elem_strings.collect::>().join(", "); - - if outermost { - str - } else { - format!("({})", str) - } - } - Type::Assignment(_, assigned_typ) => type_to_string(outermost, assigned_typ), - Type::Union(set) => { - set.into_iter().collect::>>().into_iter().map(|typ_in_set| { - type_to_string(false, typ_in_set) - }).collect::>().join(" | ") - } - - } -} diff --git a/old/solve.rs b/old/solve.rs deleted file mode 100644 index 8fb99f8faa..0000000000 --- a/old/solve.rs +++ /dev/null @@ -1,317 +0,0 @@ -use std::collections::BTreeSet; -use std::collections::HashMap; -use constrain::Constraint; -use typ::Type; -use canonical::Annotation; -use name::Name; -use self::Variable::*; -use ena::unify::{UnificationTable, UnifyKey, InPlace}; - -type UTable = UnificationTable>; - -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] -enum Variable { - Wildcard, - RigidVar(Name), - FlexUnion(BTreeSet), - RigidUnion(BTreeSet), - Structure(FlatType), - Mismatch -} - -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] -enum FlatType { - Function(VarId, VarId), - - // Apply a higher-kinded type constructor by name. For example: - // "Apply the higher-kinded type constructor `Array` to the variable `Int` - // to form `Array Int`." - // ApplyTypeConstructor(CanonicalModuleName, Name, VarId) - Tuple2(VarId, VarId), - Tuple3(VarId, VarId, VarId), - // TupleN(Vec), // Last resort - allocates - // Record1 (Map.Map N.Name VarId) VarId, -} - -#[inline] -fn unify_rigid(named: &Variable, other: &Variable) -> Variable { - match other { - Wildcard => named.clone(), - RigidVar(_) => Mismatch, - FlexUnion(_) => Mismatch, - RigidUnion(_) => Mismatch, - Structure(_) => { panic!("TODO"); Mismatch } - Mismatch => other.clone() - } -} - -#[inline] -fn unify_rigid_union(utable: &mut UTable, rigid_union: &BTreeSet, var: &Variable, other: &Variable) -> Variable { - match other { - Wildcard => var.clone(), - RigidVar(_) => Mismatch, - FlexUnion(flex_union) => { - if rigid_union_fits_flex_union(utable, &rigid_union, &flex_union) { - var.clone() - } else { - Mismatch - } - }, - Structure(_) => { panic!("TODO"); Mismatch } - RigidUnion(_) => Mismatch, - Mismatch => other.clone() - } -} - -#[inline] -fn rigid_union_fits_flex_union(utable: &mut UTable, rigid_union: &BTreeSet, flex_union: &BTreeSet) -> bool { - if rigid_union.is_subset(&flex_union) { - // If the keys of the rigid one are a subset of the flex keys, we're done. - return true; - } - - let potentially_missing_flex_ids = flex_union.difference(rigid_union); - - // a flex union can conform to a rigid one, as long - // as the rigid union contains all the flex union's alternative types - let rigid_union_values: BTreeSet = - rigid_union.iter().map(|var_id| utable.probe_value(*var_id)).collect(); - - for flex_var_id in potentially_missing_flex_ids { - let flex_val = utable.probe_value(*flex_var_id); - - if !rigid_union_values.contains(&flex_val) { - return false; - } - } - - true -} - -#[inline] -fn unify_flex_union(utable: &mut UTable, flex_union: &BTreeSet, var: &Variable, other: &Variable) -> Variable { - match other { - Wildcard => var.clone(), - RigidVar(_) => Mismatch, - RigidUnion(rigid_union) => { - if rigid_union_fits_flex_union(utable, &rigid_union, &flex_union) { - other.clone() - } else { - Mismatch - } - }, - FlexUnion(other_union) => unify_flex_unions(&flex_union, &other_union), - Structure(_) => unify_flex_union_with_structure(&flex_union, other), - Mismatch => other.clone() - } -} - -#[inline] -fn unify_flex_unions(my_union: &BTreeSet, other_union: &BTreeSet) -> Variable { - let ids_in_common = my_union.intersection(other_union); - let unified_union: BTreeSet = ids_in_common.into_iter().map(|var_id| *var_id).collect(); - - // If they have no types in common, that's a mismatch. - if unified_union.len() == 0 { - Mismatch - } else { - FlexUnion(unified_union) - } -} - -fn unify_vars(utable: &mut UTable, first: &Variable, second: &Variable) -> Variable { - match first { - // wildcard types defer to whatever the other type happens to be. - Wildcard => second.clone(), - FlexUnion(union) => unify_flex_union(utable, &union, first, second), - RigidVar(Name) => unify_rigid(first, second), - RigidUnion(union) => unify_rigid_union(utable, &union, first, second), - Structure(flat_type) => unify_structure(utable, flat_type, first, second), - // Mismatches propagate. - Mismatch => first.clone() - } -} - -#[inline] -fn unify_structure(utable: &mut UTable, flat_type: &FlatType, var: &Variable, other: &Variable) -> Variable { - match other { - Wildcard => var.clone(), - RigidVar(_) => Mismatch, - FlexUnion(flex_union) => unify_flex_union_with_structure(&flex_union, var), - RigidUnion(_) => Mismatch, - Structure(other_flat_type) => unify_flat_types(utable, flat_type, other_flat_type), - Mismatch => other.clone() - } -} - -#[inline] -fn unify_flat_types(utable: &mut UTable, flat_type: &FlatType, other_flat_type: &FlatType) -> Variable { - match (flat_type, other_flat_type) { - (FlatType::Function(my_arg, my_return), - FlatType::Function(other_arg, other_return)) => { - let new_arg = unify_var_ids(utable, *my_arg, *other_arg); - let new_return = unify_var_ids(utable, *my_return, *other_return); - - // Propagate any mismatches. - if new_arg == Mismatch { - new_arg - } else if new_return == Mismatch { - new_return - } else { - let new_arg_id = utable.new_key(new_arg); - let new_return_id = utable.new_key(new_return); - - Structure(FlatType::Function(new_arg_id, new_return_id)) - } - }, - (FlatType::Function(_, __return), _) => Mismatch, - (_, FlatType::Function(_, __return)) => Mismatch, - (FlatType::Tuple2(my_first, my_second), - FlatType::Tuple2(other_first, other_second)) => { - let new_first = unify_var_ids(utable, *my_first, *other_first); - let new_second = unify_var_ids(utable, *my_second, *other_second); - - // Propagate any mismatches. - if new_first == Mismatch { - new_first - } else if new_second == Mismatch { - new_second - } else { - let new_first_id = utable.new_key(new_first); - let new_second_id = utable.new_key(new_second); - - Structure(FlatType::Tuple2(new_first_id, new_second_id)) - } - }, - (FlatType::Tuple2(_, _), _) => Mismatch, - (_, FlatType::Tuple2(_, _)) => Mismatch, - (FlatType::Tuple3(my_first, my_second, my_third), - FlatType::Tuple3(other_first, other_second, other_third)) => { - let new_first = unify_var_ids(utable, *my_first, *other_first); - let new_second = unify_var_ids(utable, *my_second, *other_second); - let new_third = unify_var_ids(utable, *my_third, *other_third); - - // Propagate any mismatches. - if new_first == Mismatch { - new_first - } else if new_second == Mismatch { - new_second - } else if new_third == Mismatch { - new_third - } else { - let new_first_id = utable.new_key(new_first); - let new_second_id = utable.new_key(new_second); - let new_third_id = utable.new_key(new_third); - - Structure(FlatType::Tuple3(new_first_id, new_second_id, new_third_id)) - } - }, - // (FlatType::Tuple3(_, _, _), _) => Mismatch, - // (_, FlatType::Tuple3(_, _, _)) => Mismatch, - } -} - -#[inline] -fn unify_flex_union_with_structure(flex_union: &BTreeSet, var: &Variable) -> Variable { - // TODO I guess iterate through the set, looking up Variables - - panic!("TODO"); - // if flex_union.contains(var) { - // Narrow the union to the one member type - var.clone() - // } else { - // Mismatch - // } -} - - -// Given a type, create a constraint variable for it and add it to the table. -// Return the VarId corresponding to the variable in the table. -fn type_to_var_id(utable: &mut UTable, typ: Type) -> VarId { - match typ { - Type::Call(box fn_type, box arg_type) => { - panic!("TODO"); - utable.new_key(Mismatch) - // let left_var_id = type_to_var_id(utable, left_type); - // let right_var_id = type_to_var_id(utable, right_type); - - // // TODO should we match on op to hardcode the types we expect? - // let flat_type = FlatType::Function(left_var_id, right_var_id); - - // utable.new_key(Structure(flat_type)) - } - } -} - -#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] -struct VarId(u32); - -impl UnifyKey for VarId { - type Value = Variable; - - fn index(&self) -> u32 { self.0 } - fn from_index(u: u32) -> VarId { VarId(u) } - - // tag is a static string that's only used in debugging - fn tag() -> &'static str { "VarId" } -} - -fn unify_var_ids(utable: &mut UTable, left_id: VarId, right_id: VarId) -> Variable { - let left_content = utable.probe_value(left_id); - let right_content = utable.probe_value(right_id); - - if left_content == right_content { - left_content - } else { - unify_vars(utable, &left_content, &right_content) - } -} - -pub type TypeError = String; - -pub fn solve_constraint(constraint: Constraint) -> Result> { - let mut utable: UTable = UnificationTable::new(); - - solve(&mut utable, constraint); - - Ok("TODO: actually gather errors etc".to_owned()) -} - -fn solve(utable: &mut UTable, constraint: Constraint) { - match constraint { - Constraint::True => {}, - - Constraint::Equal(actual_type, expectation) => { - let actual_var_id = type_to_var_id(utable, actual_type); - let expected_var_id = type_to_var_id(utable, expectation); - let answer = unify_var_ids(utable, actual_var_id, expected_var_id); - - panic!("Oh no! TYPE MISMATCH! (TODO: record errors as appropriate)"); - () - // match answer { - // Mismatch => { - // panic!("Oh no! TYPE MISMATCH! (TODO: record errors as appropriate)"); - // } - // do introduce rank pools vars - // return state - - // UF.modify var $ \(Descriptor content _ mark copy) -> - // Descriptor content rank mark copy - - // Unify.Err vars actualType expectedType -> - - // panic!("TODO xyz"); - // do introduce rank pools vars - // return $ addError state $ - // Error.BadExpr region category actualType $ - // Error.typeReplace expectation expectedType - // } - }, - - Constraint::Batch(_) => { - panic!("TODO"); - () - } - } -} - diff --git a/old/typ.rs b/old/typ.rs deleted file mode 100644 index 5f211e92e8..0000000000 --- a/old/typ.rs +++ /dev/null @@ -1,12 +0,0 @@ - - -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] -pub enum Type { - // Symbol(String), - // Int, - // Float, - // Number, - // TypeUnion(BTreeSet), - // Function(Box, Box), - Call(Box, Box), -} diff --git a/old/unify.rs b/old/unify.rs deleted file mode 100644 index d3056ca0be..0000000000 --- a/old/unify.rs +++ /dev/null @@ -1,363 +0,0 @@ -use std::collections::BTreeSet; -use std::collections::BTreeMap; -use self::Type::*; - -pub type Name<'a> = &'a str; - -pub type ModuleName<'a> = &'a str; - -#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] -pub enum Type<'a> { - Unbound, - String, - Char, - Int, - Float, - Number, - Symbol(&'a str), - Array(Box>), - Function(Box>, Box>), - Record(BTreeMap, Type<'a>>), - Tuple(Vec>), - Union(BTreeSet>), -} - -// CANONICAL IR - we have already done stuff like giving errors for -// duplicate field names - -#[derive(Debug, PartialEq)] -pub enum Expr<'a> { - // Variables - Declaration(&'a Pattern<'a>, Box<&'a Expr<'a>>, Box>), - LookupLocal(&'a Name<'a>), - LookupGlobal(&'a ModuleName<'a>, &'a Name<'a>), - - // Scalars - Symbol(&'a str), - String(&'a str), - Char(char), - HexOctalBinary(i64), // : Int - FractionalNumber(f64), // : Float - WholeNumber(i64), // : Int | Float - - // Collections - Array(Vec>), - Record(Vec<(&'a Name<'a>, &'a Expr<'a>)>), - Tuple(Vec<&'a Expr<'a>>), - LookupName(Name<'a>, Box<&'a Expr<'a>>), - // TODO add record update - - // Functions - Function(&'a Pattern<'a>, &'a Expr<'a>), - Call(Box<&'a Expr<'a>>, Box<&'a Expr<'a>>), - CallOperator(&'a Operator, Box<&'a Expr<'a>>, Box<&'a Expr<'a>>), - - // Conditionals - If(Box<&'a Expr<'a>> /* Conditional */, Box<&'a Expr<'a>> /* True branch */, Box<&'a Expr<'a>> /* False branch */), - Case(Box<&'a Expr<'a>>, Vec<(&'a Pattern<'a>, &'a Expr<'a>)>), -} - -#[derive(Debug, PartialEq)] -pub enum Operator { - Plus, Minus, Star, Caret, Percent, FloatDivision, IntDivision, - GT, GTE, LT, LTE, - EQ, NE, And, Or, - QuestionMark, Or -} - -#[derive(Debug, PartialEq)] -pub enum Pattern<'a> { - Name(&'a Name<'a>), // `foo =` - As(&'a Name<'a>, &'a Pattern<'a>), // ` as foo` - Type(&'a Type<'a>), - Symbol(&'a str), - String(&'a str), - Char(char), - WholeNumber(&'a str), - FractionalNumber(&'a str), - HexOctalBinary(&'a str), - Tuple(Vec>), - Record(Vec<(Name<'a>, Option>)>), // { a = 5, b : Int as x, c } -} - - -pub fn infer<'a>(expr: &Expr<'a>) -> Result, UnificationProblem> { - match expr { - Expr::String(_) => Ok(String), - Expr::Char(_) => Ok(Char), - Expr::HexOctalBinary(_) => Ok(Int), - Expr::FractionalNumber(_) => Ok(Float), - Expr::WholeNumber(_) => Ok(Number), - Expr::Symbol(sym) => Ok(Symbol(sym)), - Expr::Array(elem_exprs) => { - let elem_type; - - if elem_exprs.is_empty() { - elem_type = Unbound; - } else { - let mut unified_type = BTreeSet::new(); - - // Unify the types of all the elements - for elem_expr in elem_exprs { - unified_type.insert(infer(&elem_expr)?); - } - - if unified_type.len() == 1 { - // No point in storing a union of 1. - elem_type = unified_type.into_iter().next().unwrap() - } else { - elem_type = Union(unified_type) - } - } - - Ok(Array(Box::new(elem_type))) - }, - Expr::Record(fields) => { - let mut rec_type: BTreeMap<&'a Name<'a>, Type<'a>> = BTreeMap::new(); - - for (field, subexpr) in fields { - let field_type = infer(subexpr)?; - - rec_type.insert(&field, field_type); - } - - Ok(Record(rec_type)) - }, - Expr::Tuple(exprs) => { - let mut tuple_type: Vec> = Vec::new(); - - for subexpr in exprs { - let field_type = infer(subexpr)?; - - tuple_type.push(field_type); - } - - Ok(Tuple(tuple_type)) - }, - Expr::If(box cond, expr_if_true, expr_if_false) => { - let cond_type = infer(&cond)?; - - // if-conditionals must be of type Bool - if !matches_bool_type(&cond_type) { - return Err(UnificationProblem::IfConditionNotBool); - } - - // unify the true and false branches - let true_type = infer(&expr_if_true)?; - let false_type = infer(&expr_if_false)?; - - let mut unified_type = BTreeSet::new(); - - unified_type.insert(true_type); - unified_type.insert(false_type); - - if unified_type.len() == 1 { - // No point in storing a union of 1. - // - // We can't reuse true_type because it's been moved into the set - // but we can pull it back out of the set - Ok(unified_type.into_iter().next().unwrap()) - } else { - Ok(Union(unified_type)) - } - }, - Call(func, arg) => { - - }, - CallOperator(op, left_expr, right_expr) => { - let left = &(infer(left_expr)?); - let right = &(infer(right_expr)?); - - match op { - Operator::EQ | Operator::NE | Operator::And | Operator::Or => { - if types_match(left, right) { - conform_to_bool(left) - } else { - Err(UnificationProblem::TypeMismatch) - } - }, - Operator::Plus | Operator::Minus | Operator::Star - | Operator::GT | Operator::LT | Operator::GTE | Operator::LTE - | Operator::Caret | Operator::Percent => { - if types_match(left, right) { - conform_to_number(left) - } else { - Err(UnificationProblem::TypeMismatch) - } - }, - Operator::FloatDivision => { - if matches_float_type(left) && matches_float_type(right) { - Ok(&Float) - } else { - Err(UnificationProblem::TypeMismatch) - } - }, - Operator::IntDivision => { - if matches_int_type(left) && matches_int_type(right) { - Ok(&Int) - } else { - Err(UnificationProblem::TypeMismatch) - } - }, - Operator::CombineStrings => { - if matches_string_type(left) && matches_string_type(right) { - Ok(&String) - } else { - Err(UnificationProblem::TypeMismatch) - } - }, - Operator::QuestionMark => { - if types_match(left, right) { - conform_to_optional(left) - } else { - Err(UnificationProblem::TypeMismatch) - } - } - } - }, - Expr::Declaration(pattern, let_expr, in_expr) => { - // Think of this as a let..in even though syntactically it's not. - // We need to type-check the let-binding, but the type of the - // *expression* we're expaning is only affected by the in-block. - check_pattern(&pattern, &let_expr)?; - - infer(in_expr) - } - } -} - -fn types_match<'a>(first: &'a Type<'a>, second: &'a Type<'a>) -> bool { - match (first, second) { - (Type::Union(first_types), Type::Union(second_types)) => { - // If any type is not directly present in the other union, - // it must at least match *some* type in the other union - first_types.difference(second_types).into_iter().all(|not_in_second_type| { - second_types.iter().any(|second_type| types_match(second_type, not_in_second_type)) - }) && - second_types.difference(first_types).into_iter().all(|not_in_first_type| { - first_types.iter().any(|first_type| types_match(first_type, not_in_first_type)) - }) - }, - - // Happy path: try these first, since we expect them to succeed. - // These are sorted based on a vague guess of how often they will be used in practice. - (Type::Symbol(sym_one), Type::Symbol(sym_two)) => sym_one == sym_two, - (Type::String, Type::String) => true, - (Type::Unbound, _) | (_, Type::Unbound)=> true, - (Type::Array(box elem_type_one), Type::Array(box elem_type_two)) => { - types_match(elem_type_one, elem_type_two) - }, - (Type::Number, Type::Number) => true, - (Type::Number, other) => matches_number_type(other), - (other, Type::Number) => matches_number_type(other), - (Type::Int, Type::Int) => true, - (Type::Float, Type::Float) => true, - (Type::Tuple(first_elems), Type::Tuple(second_elems)) => { - // TODO verify that the elems and their types match up - // TODO write some scenarios to understand these better - - // like, what happens if you have a function that takes - // a lambda whose argument takes an open record, - // and you pass a lamba whose argument takes *fewer* fields? - // that should work! the function is gonna pass it a lambda that - // has more fields than it needs. - // I think there's an element of directionality here that I'm - // disregarding. Maybe this function shouldn't commute. - }, - (Type::Function(first_arg), Type::Function(second_arg)) => { - // TODO verify that the elems and their types match up - }, - (Type::Record(first_fields), Type::Record(second_fields)) => { - // TODO verify that the fields and their types match up - // TODO what should happen if one is a superset of the other? fail? - }, - (Type::Char, Type::Char) => true, - - // Unhappy path - expect these to fail, so check them last - (Type::Union(first_types), _) => { - first_types.iter().all(|typ| types_match(typ, second)) - }, - (_, Type::Union(second_types)) => { - second_types.iter().all(|typ| types_match(first, typ)) - }, - (Type::String, _) | (_, Type::String) => false, - (Type::Char, _) | (_, Type::Char) => false, - (Type::Int, _) | (_, Type::Int) => false, - (Type::Float, _) | (_, Type::Float) => false, - (Type::Symbol(_), _) | (_, Type::Symbol(_)) => false, - (Type::Array(_), _) | (_, Type::Array(_)) => false, - (Type::Record(_), _) | (_, Type::Record(_)) => false, - (Type::Tuple(_), _) | (_, Type::Tuple(_)) => false, - (Type::Function(_, _), _) | (_, Type::Function(_, _)) => false, - } -} - - -fn check_pattern<'a>(pattern: &'a Pattern<'a>, expr: &'a Expr<'a>) -> Result<(), UnificationProblem> { - let expr_type = infer(expr)?; - - panic!("TODO check the pattern's type against expr_type, then write some tests for funky record pattern cases - this is our first real unification! Next one will be field access, ooooo - gonna want lots of tests for that") -} - -const TRUE_SYMBOL_STR: &'static str = "True"; -const FALSE_SYMBOL_STR: &'static str = "False"; - -pub fn matches_string_type<'a>(candidate: &Type<'a>) -> bool { - match candidate { - Unbound | String => true, - Type::Union(types) => { - types.iter().all(|typ| matches_string_type(typ)) - }, - _ => Err(UnificationProblem::TypeMismatch) - } -} - -pub fn matches_bool_type<'a>(candidate: &Type<'a>) -> bool { - match candidate { - Type::Unbound => true, - Type::Symbol(str) => str == &TRUE_SYMBOL_STR || str == &FALSE_SYMBOL_STR, - Type::Union(types) => { - types.iter().all(|typ| matches_bool_type(typ)) - } - _ => false - } -} - -pub fn matches_number_type<'a>(candidate: &Type<'a>) -> bool { - match candidate { - Type::Unbound | Type::Int | Type::Float | Type::Number => true, - Type::Union(types) => { - types.iter().all(|typ| matches_number_type(typ)) - } - _ => false - } -} - -pub fn matches_int_type<'a>(candidate: &Type<'a>) -> bool { - match candidate { - Type::Unbound | Type::Int => true, - Type::Union(types) => { - types.iter().all(|typ| matches_int_type(typ)) - } - _ => false - } -} - -pub fn matches_float_type<'a>(candidate: &Type<'a>) -> bool { - match candidate { - Type::Unbound | Type::Float => true, - Type::Union(types) => { - types.iter().all(|typ| matches_float_type(typ)) - } - _ => false - } -} - -#[derive(Debug)] -pub enum UnificationProblem { - CannotUnifyAssignments, - NotMemberOfUnion, - TypeMismatch, - IfConditionNotBool, - SymbolMismatch -} - diff --git a/oldtst/test_interpreter.rs b/oldtst/test_interpreter.rs deleted file mode 100644 index 580684e60e..0000000000 --- a/oldtst/test_interpreter.rs +++ /dev/null @@ -1,38 +0,0 @@ -#[macro_use] extern crate pretty_assertions; - -extern crate roc; - -#[cfg(test)] -mod interpreter_tests { - use roc::interpret::literal_to_string; - use roc::unify::Expr::Literal; - use roc::unify::Literal::{String, Record}; - - #[test] - fn test_string_literal() { - let expected = "\"hi!\""; - let literal = String("hi!"); - - assert_eq!(expected, literal_to_string(&literal)); - } - - #[test] - fn test_record_literal() { - let str0 = &String("blah"); - let str1 = &String("foo"); - let str2 = &String("bar"); - - let x = ("x", &Literal(str1)); - let y = ("y", &Literal(str2)); - - let subrec = &Record(vec![x, y]); - let str_pair = ("string", &Literal(str0)); - let rec_pair = ("record", &Literal(subrec)); - let toprec = vec![str_pair, rec_pair]; - let literal = &Record(toprec); - - let expected = "{ string = \"blah\", record = { x = \"foo\", y = \"bar\" } }"; - - assert_eq!(expected, literal_to_string(literal)); - } -} diff --git a/oldtst/test_repl.rs b/oldtst/test_repl.rs deleted file mode 100644 index 83e178ecaa..0000000000 --- a/oldtst/test_repl.rs +++ /dev/null @@ -1,41 +0,0 @@ -#[macro_use] extern crate pretty_assertions; - -extern crate roc; - -#[cfg(test)] -mod repl_tests { - use roc::repl::eval_and_print; - use roc::unify::Expr::{Literal}; - use roc::unify::Literal::{String, Record}; - - #[test] - fn test_eval_and_print() { - let expected = "\"hi!\"\n: String"; - let literal = String("hi!"); - let expr = Literal(&literal); - - assert_eq!(expected, eval_and_print(&expr)); - } - - #[test] - fn test_record_literal() { - let expected = "{ string = \"abc\", record = { x = \"one\", y = \"two\" } }\n: { string : String, record : { x : String, y : String } }"; - - let str0 = &String("abc"); - let str1 = &String("one"); - let str2 = &String("two"); - - let x = ("x", &Literal(str1)); - let y = ("y", &Literal(str2)); - - let subrec = &Record(vec![x, y]); - let str_pair = ("string", &Literal(str0)); - let rec_pair = ("record", &Literal(subrec)); - let toprec = vec![str_pair, rec_pair]; - let literal = &Record(toprec); - - let expr = Literal(&literal); - - assert_eq!(expected, eval_and_print(&expr)); - } -} diff --git a/oldtst/test_solve.rs b/oldtst/test_solve.rs deleted file mode 100644 index 7819981525..0000000000 --- a/oldtst/test_solve.rs +++ /dev/null @@ -1,134 +0,0 @@ -#[macro_use] extern crate pretty_assertions; - -extern crate roc; - -#[cfg(test)] -mod tests { - use std::collections::HashMap; - use roc::solve::solve_constraint; - use roc::typ::Type::*; - use roc::constrain::Constraint::*; - use roc::expr::Expr::*; - - #[test] - fn test_solve_true() { - let expected = HashMap::new(); - - assert_eq!(Ok(expected), solve_constraint(True)); - } - - #[test] - fn test_solve_unify_basic() { - let expected = HashMap::new(); - - // TODO unify a function call. - // TODO to do this, will nee to introduce let-bindings to put stuff in the Name Map - // TODO since function calls are looked up by name. - let type_to_unify:Type = ... - let expected_type_to_unify:ExpectedType = ... - - assert_eq!(Ok(expected), solve_constraint(Unify(type_to_unify, expected_type_to_unify)); - } - -// #[test] -// fn test_negate_number() { -// expect_type(Type::Number, CallBuiltin(Negate, WholeNumber(5))); -// } - -// #[test] -// fn test_negate_float() { -// expect_type(Type::Float, CallBuiltin(Negate, FractinalNumber(3.1))); -// } - -// #[test] -// fn test_negate_int_twice() { -// expect_type(Type::Int, negate_twice(HexOctalBinary(0x12))); -// } - -// #[test] -// fn test_negate_number_twice() { -// expect_type(Type::Number, negate_twice(WholeNumber(5))); -// } - -// #[test] -// fn test_negate_float_twice() { -// expect_type(Type::Float, negate_twice(FractinalNumber(3.1))); -// } - - -// #[test] -// fn test_int_literal() { -// expect_type(Type::Int, HexOctalBinary(0x12)); -// } - -// #[test] -// fn test_float_literal() { -// expect_type(Type::Float, FractionalNumber(3.1)); -// } - -// #[test] -// fn test_number_literal() { -// expect_type(Type::Number, WholeNumber(5)); -// } - -// #[test] -// fn add_ints_returns_int() { -// expect_type(Type::Int, CallOperator(Plus, int(), int())); -// } - -// #[test] -// fn add_floats_returns_float() { -// expect_type(Type::Float, CallOperator(Plus, float(), float())); -// } - -// #[test] -// fn add_nums_returns_num() { -// expect_type(Type::Number, CallOperator(Plus, num(), num())); -// } - -// #[test] -// fn add_num_int_returns_int() { -// expect_type(Type::Int, CallOperator(Plus, num(), int())); -// expect_type(Type::Int, CallOperator(Plus, int(), num())); -// } - -// #[test] -// fn add_num_float_returns_float() { -// expect_type(Type::Float, CallOperator(Plus, num(), float())); -// expect_type(Type::Float, CallOperator(Plus, float(), num())); -// } - -// #[test] -// fn add_int_float_returns_mismatch() { -// expect_mismatch(CallOperator(Plus, int(), float())); -// } - -// fn expect_type<'a>(expected_type: Type<'a>, expr: Expr<'a>) { -// assert_eq!(expected_type, infer_type(expr).unwrap()); -// } - -// fn expect_mismatch<'a>(expr: Expr<'a>) { -// assert_eq!(Err(Problem::Mismatch), infer_type(expr)); -// } - -// #[inline] -// fn negate_twice(expr) { -// CallBuiltin(Negate, CallBuiltin(Negate, expr)) -// } - -// fn int<'a>() -> Box<&'a Expr<'a>> { Box::new(&HexOctalBinary(0x12)) } -// fn float<'a>() -> Box<&'a Expr<'a>> { Box::new(&FractionalNumber(3.1)) } -// fn num<'a>() -> Box<&'a Expr<'a>> { Box::new(&WholeNumber(5)) } - - // TODO test unions that ought to be equivalent, but only after - // a reduction of some sort, e.g. - // - // ((a|b)|c) vs (a|(b|c)) - // - // ((a|z)|(b|z)) vs (a|b|z) - // - // ideally, we fix these when constructing unions - // e.g. if a user puts this in as an annotation, reduce it immediately - // and when we're inferring unions, always infer them flat. - // This way we can avoid checking recursively. -} diff --git a/oldtst/test_unify.rs b/oldtst/test_unify.rs deleted file mode 100644 index ada3a9cf74..0000000000 --- a/oldtst/test_unify.rs +++ /dev/null @@ -1,39 +0,0 @@ -#[macro_use] extern crate pretty_assertions; - -extern crate roc; - -#[cfg(test)] -mod tests { - use roc::unify::Type; - use roc::unify::Expr::Literal; - use roc::unify::Literal::{String, Record}; - use roc::unify::infer; - - #[test] - fn test_infer_record_literals() { - let str0 = &String("doesn't matter"); - let str1 = &String("ignored"); - let str2 = &String("also ignored"); - - let x = ("x", &Literal(str1)); - let y = ("y", &Literal(str2)); - - let subrec = &Record(vec![x, y]); - let str_pair = ("string", &Literal(str0)); - let rec_pair = ("record", &Literal(subrec)); - let toprec = vec![str_pair, rec_pair]; - let literal = &Record(toprec); - - let expr = Literal(literal); - - let expected_type = Type::Record(vec![ - ("string", Type::String), - ("record", Type::Record(vec![ - ("x", Type::String), - ("y", Type::String) - ])) - ]); - - assert_eq!(expected_type, infer(&expr).unwrap()); - } -} diff --git a/src/constrain.rs b/src/constrain.rs deleted file mode 100644 index 43492d1b1f..0000000000 --- a/src/constrain.rs +++ /dev/null @@ -1,24 +0,0 @@ -use typ::Type; - -// constrainDecls :: Can.Decls -> Constraint -> IO Constraint -// constrainDecls decls finalConstraint = -// case decls of -// Can.Declare def otherDecls -> -// Expr.constrainDef Map.empty def =<< constrainDecls otherDecls finalConstraint - -// Can.DeclareRec defs otherDecls -> -// Expr.constrainRecursiveDefs Map.empty defs =<< constrainDecls otherDecls finalConstraint - -// Can.SaveTheEnvironment -> -// return finalConstraint - - - -pub type ExpectedType = Type; - - -pub enum Constraint { - True, - Equal(Type, ExpectedType), - Batch(Vec), -} diff --git a/src/expr.rs b/src/expr.rs index c4ea222347..fd76f25c0e 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -1,44 +1,21 @@ -use name::Name; -use typ::Type; + +#[derive(Debug, PartialEq)] +pub enum Expr { + // Literals + Int(i64), + Frac(i64, u64), + String(String), + Char(char), + + Var(String), + + // Functions + Func(String, Box), + Apply(Box, Box), + Operator(Box, Operator, Box), +} #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] pub enum Operator { Plus, Minus, Star, Slash, DoubleSlash, } - -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] -pub enum Builtin { - // Default - Negate, - Not, - - // String - // StringLength, -} - - -#[derive(Debug, PartialEq)] -pub enum Expr { - Int(i64), - Ratio(i64, u64), - - // Functions - CallOperator(Box, Operator, Box), - CallBuiltin(Builtin, Box), - CallLambda(Box, Box), -} - -#[derive(Debug, PartialEq)] -pub enum Pattern { - Name(Name), // `foo =` - As(Name, Box), // ` as foo` - Type(Type), - Symbol(String), - String(String), - Char(char), - Int(i64), - Float(f64), - Tuple(Vec), - Record(Vec<(Name, Option)>), // { a = 5, b : Int as x, c } -} - diff --git a/src/interpret.rs b/src/interpret.rs deleted file mode 100644 index 475c9b4115..0000000000 --- a/src/interpret.rs +++ /dev/null @@ -1,57 +0,0 @@ -use unify::Expr; -use unify::Literal; - -pub fn eval<'a>(expr: &'a Expr<'a>) -> &'a Literal<'a> { - match expr { - Expr::Literal(literal) => literal, - Expr::Assignment(_, subexpr) => eval(subexpr), - Expr::If(cond, if_true, if_false) => { - match eval(cond) { - Literal::Symbol("True") => eval(if_true), - Literal::Symbol("False") => eval(if_false), - _ => { - panic!("somehow an if-conditional did not evaluate to True or False!") - } - } - } - } -} - -pub fn literal_to_string<'a>(literal: &'a Literal<'a>) -> String { - match literal { - Literal::String(str) => format!("\"{}\"", str), - Literal::Char(character) => format!("'{}'", character), - Literal::Symbol(str) => str.to_string(), - Literal::HexOctalBinary(str) => str.to_string(), - Literal::Number(str) => str.to_string(), - Literal::Record(field_exprs) => { - let mut field_strings = Vec::new(); - - for (field, subexpr) in field_exprs { - let val = literal_to_string(eval(subexpr)); - - field_strings.push(format!("{} = {}", field, val)); - } - - format!("{{ {} }}", field_strings.join(", ")) - }, - Literal::Tuple(elem_exprs) => { - let mut elem_strings = Vec::new(); - - for elem_expr in elem_exprs { - elem_strings.push(literal_to_string(eval(elem_expr))); - } - - format!("({})", elem_strings.join(", ")) - }, - Literal::Array(elem_exprs) => { - let mut elem_strings = Vec::new(); - - for elem_expr in elem_exprs { - elem_strings.push(literal_to_string(eval(elem_expr))); - } - - format!("[{}]", elem_strings.join(", ")) - }, - } -} diff --git a/src/name.rs b/src/name.rs deleted file mode 100644 index 13a9337411..0000000000 --- a/src/name.rs +++ /dev/null @@ -1 +0,0 @@ -pub type Name = String; diff --git a/src/parse.rs b/src/parse.rs index bcfa5fd5ad..3b04de219c 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -2,11 +2,10 @@ use expr::Operator; use expr::Expr; use std::char; -use std::iter; -use combine::parser::char::{char, string, letter, alpha_num, spaces, digit, hex_digit, HexDigit}; +use combine::parser::char::{char, spaces, digit, hex_digit, HexDigit}; use combine::parser::repeat::{many, count_min_max}; -use combine::parser::item::{any, satisfy, satisfy_map, value}; +use combine::parser::item::{any, satisfy_map, value}; use combine::{choice, many1, parser, Parser, optional, between, unexpected_any}; use combine::error::{Consumed, ParseError}; use combine::stream::{Stream}; @@ -39,13 +38,13 @@ parser! { optional( operator() .skip(spaces()) - .and(expr() + .and(expr()) ) - )).map(|(v1, maybe_op)| { + ).map(|(v1, maybe_op)| { match maybe_op { None => v1, Some((op, v2)) => { - Expr::CallOperator(Box::new(v1), op, Box::new(v2)) + Expr::Operator(Box::new(v1), op, Box::new(v2)) }, } }) @@ -60,6 +59,7 @@ where I: Stream, char('+').map(|_| Operator::Plus), char('-').map(|_| Operator::Minus), char('*').map(|_| Operator::Star), + char('/').map(|_| Operator::Slash), )) } @@ -238,98 +238,12 @@ where I: Stream, let numerator = (int_val * denom) + (decimal as i64); if is_positive { - Expr::Ratio(numerator, denom as u64) + Expr::Frac(numerator, denom as u64) } else { - Expr::Ratio(-numerator, denom as u64) + Expr::Frac(-numerator, denom as u64) } } } }) } -// pub fn parse_expr(state: &mut State) -> Result { - -// let digits = chomp_digits(state); - -// if digits.is_empty() { -// Err(Problem::InvalidNumber) -// } else { -// // TODO store these in a bigint, and handle overflow. -// let num = digits.parse::().unwrap(); - -// if decimal_point - - -// Ok(Expr::Int(num)) -// } -// } - -// enum Parsed { -// Expr(Expr), -// Malformed(Problem), -// NotFound -// } - - -// #[inline] -// fn number_parser() -> { -// let has_minus_sign = false; -// let decimal_point_index: usize = 0; -// let len: usize = 0; - -// for ch in state.text.chars() { -// if ch.is_ascii_digit() { -// len += 1; -// } else if ch == '-' { -// if has_minus_sign { -// if len == 1 { -// return Malformed(DoubleMinusSign); -// } else { -// // This second minus sign is a subtraction operator. -// // We've reached the end of the number! -// break; -// } -// } else { -// has_minus_sign = true; -// len += 1; -// } -// } else if ch == '.' { -// if len == 0 { -// return Malformed(NoDigitsBeforeDecimalPoint); -// } else if decimal_point_index != 0 { -// return Malformed(DoubleDecimalPoint); -// } else { -// // This might be a valid decimal number! -// decimal_point_index = len; - -// len += 1; -// } -// } -// } - -// state.col += len; - -// if decimal_point_index == 0 { -// // This is an integer. -// Expr(Expr::Int(parse_int(&state.text[..len]))) -// } else { -// // This is a decimal. -// let before_decimal_pt = &state.text[..decimal_point_index]; -// let after_decimal_pt = &state.text[(decimal_point_index + 1)..]; - -// let numerator_str = before_decimal_pt.to_owned(); -// numerator_str.push_str(after_decimal_pt); - -// let numerator = parse_int(&numerator_str); -// let denominator = 10 * after_decimal_pt.len() as u64; - -// Expr(Expr::Ratio(numerator, denominator)) -// } -// } - -// #[inline] -// fn parse_int(text: &str) -> i64 { -// // TODO parse as BigInt -// text.parse::().unwrap() -// } - diff --git a/src/repl.rs b/src/repl.rs deleted file mode 100644 index 96404959a9..0000000000 --- a/src/repl.rs +++ /dev/null @@ -1,63 +0,0 @@ -use interpret::{eval, literal_to_string}; -use unify::infer; -use unify::Expr; -use unify::Type; - -pub fn eval_and_print<'a>(expr: &Expr<'a>) -> String { - match infer(&expr) { - Ok(typ) => { - let lit = eval(expr); - - format!("{}\n: {}", literal_to_string(lit), type_to_string(true, &typ)) - }, - Err(_) => - "[TYPE MISMATCH!]".to_string() - } -} - -pub fn type_to_string<'a>(outermost: bool, typ: &'a Type<'a>) -> String { - match typ { - Type::Unbound => "*".to_string(), - Type::String => "String".to_string(), - Type::Char => "Char".to_string(), - Type::Int => "Int".to_string(), - Type::Float => "Float".to_string(), - Type::Number => "Int | Float".to_string(), - Type::Symbol(sym) => format!(":{}", sym), - Type::Array(elem_type) => { - let str = format!("Array {}", type_to_string(false, elem_type)); - - if outermost { - str - } else { - format!("({})", str) - } - }, - Type::Record(fields) => { - let field_strings = fields.into_iter().map(|(field, subtyp)| { - let typ_str = type_to_string(false, subtyp); - - format!("{} : {}", field, typ_str) - }); - - format!("{{ {} }}", field_strings.collect::>().join(", ")) - }, - Type::Tuple(elems) => { - let elem_strings = elems.into_iter().map(|subtyp| { type_to_string(false, subtyp) }); - let str = elem_strings.collect::>().join(", "); - - if outermost { - str - } else { - format!("({})", str) - } - } - Type::Assignment(_, assigned_typ) => type_to_string(outermost, assigned_typ), - Type::Union(set) => { - set.into_iter().collect::>>().into_iter().map(|typ_in_set| { - type_to_string(false, typ_in_set) - }).collect::>().join(" | ") - } - - } -} diff --git a/src/solve.rs b/src/solve.rs deleted file mode 100644 index 8fb99f8faa..0000000000 --- a/src/solve.rs +++ /dev/null @@ -1,317 +0,0 @@ -use std::collections::BTreeSet; -use std::collections::HashMap; -use constrain::Constraint; -use typ::Type; -use canonical::Annotation; -use name::Name; -use self::Variable::*; -use ena::unify::{UnificationTable, UnifyKey, InPlace}; - -type UTable = UnificationTable>; - -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] -enum Variable { - Wildcard, - RigidVar(Name), - FlexUnion(BTreeSet), - RigidUnion(BTreeSet), - Structure(FlatType), - Mismatch -} - -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] -enum FlatType { - Function(VarId, VarId), - - // Apply a higher-kinded type constructor by name. For example: - // "Apply the higher-kinded type constructor `Array` to the variable `Int` - // to form `Array Int`." - // ApplyTypeConstructor(CanonicalModuleName, Name, VarId) - Tuple2(VarId, VarId), - Tuple3(VarId, VarId, VarId), - // TupleN(Vec), // Last resort - allocates - // Record1 (Map.Map N.Name VarId) VarId, -} - -#[inline] -fn unify_rigid(named: &Variable, other: &Variable) -> Variable { - match other { - Wildcard => named.clone(), - RigidVar(_) => Mismatch, - FlexUnion(_) => Mismatch, - RigidUnion(_) => Mismatch, - Structure(_) => { panic!("TODO"); Mismatch } - Mismatch => other.clone() - } -} - -#[inline] -fn unify_rigid_union(utable: &mut UTable, rigid_union: &BTreeSet, var: &Variable, other: &Variable) -> Variable { - match other { - Wildcard => var.clone(), - RigidVar(_) => Mismatch, - FlexUnion(flex_union) => { - if rigid_union_fits_flex_union(utable, &rigid_union, &flex_union) { - var.clone() - } else { - Mismatch - } - }, - Structure(_) => { panic!("TODO"); Mismatch } - RigidUnion(_) => Mismatch, - Mismatch => other.clone() - } -} - -#[inline] -fn rigid_union_fits_flex_union(utable: &mut UTable, rigid_union: &BTreeSet, flex_union: &BTreeSet) -> bool { - if rigid_union.is_subset(&flex_union) { - // If the keys of the rigid one are a subset of the flex keys, we're done. - return true; - } - - let potentially_missing_flex_ids = flex_union.difference(rigid_union); - - // a flex union can conform to a rigid one, as long - // as the rigid union contains all the flex union's alternative types - let rigid_union_values: BTreeSet = - rigid_union.iter().map(|var_id| utable.probe_value(*var_id)).collect(); - - for flex_var_id in potentially_missing_flex_ids { - let flex_val = utable.probe_value(*flex_var_id); - - if !rigid_union_values.contains(&flex_val) { - return false; - } - } - - true -} - -#[inline] -fn unify_flex_union(utable: &mut UTable, flex_union: &BTreeSet, var: &Variable, other: &Variable) -> Variable { - match other { - Wildcard => var.clone(), - RigidVar(_) => Mismatch, - RigidUnion(rigid_union) => { - if rigid_union_fits_flex_union(utable, &rigid_union, &flex_union) { - other.clone() - } else { - Mismatch - } - }, - FlexUnion(other_union) => unify_flex_unions(&flex_union, &other_union), - Structure(_) => unify_flex_union_with_structure(&flex_union, other), - Mismatch => other.clone() - } -} - -#[inline] -fn unify_flex_unions(my_union: &BTreeSet, other_union: &BTreeSet) -> Variable { - let ids_in_common = my_union.intersection(other_union); - let unified_union: BTreeSet = ids_in_common.into_iter().map(|var_id| *var_id).collect(); - - // If they have no types in common, that's a mismatch. - if unified_union.len() == 0 { - Mismatch - } else { - FlexUnion(unified_union) - } -} - -fn unify_vars(utable: &mut UTable, first: &Variable, second: &Variable) -> Variable { - match first { - // wildcard types defer to whatever the other type happens to be. - Wildcard => second.clone(), - FlexUnion(union) => unify_flex_union(utable, &union, first, second), - RigidVar(Name) => unify_rigid(first, second), - RigidUnion(union) => unify_rigid_union(utable, &union, first, second), - Structure(flat_type) => unify_structure(utable, flat_type, first, second), - // Mismatches propagate. - Mismatch => first.clone() - } -} - -#[inline] -fn unify_structure(utable: &mut UTable, flat_type: &FlatType, var: &Variable, other: &Variable) -> Variable { - match other { - Wildcard => var.clone(), - RigidVar(_) => Mismatch, - FlexUnion(flex_union) => unify_flex_union_with_structure(&flex_union, var), - RigidUnion(_) => Mismatch, - Structure(other_flat_type) => unify_flat_types(utable, flat_type, other_flat_type), - Mismatch => other.clone() - } -} - -#[inline] -fn unify_flat_types(utable: &mut UTable, flat_type: &FlatType, other_flat_type: &FlatType) -> Variable { - match (flat_type, other_flat_type) { - (FlatType::Function(my_arg, my_return), - FlatType::Function(other_arg, other_return)) => { - let new_arg = unify_var_ids(utable, *my_arg, *other_arg); - let new_return = unify_var_ids(utable, *my_return, *other_return); - - // Propagate any mismatches. - if new_arg == Mismatch { - new_arg - } else if new_return == Mismatch { - new_return - } else { - let new_arg_id = utable.new_key(new_arg); - let new_return_id = utable.new_key(new_return); - - Structure(FlatType::Function(new_arg_id, new_return_id)) - } - }, - (FlatType::Function(_, __return), _) => Mismatch, - (_, FlatType::Function(_, __return)) => Mismatch, - (FlatType::Tuple2(my_first, my_second), - FlatType::Tuple2(other_first, other_second)) => { - let new_first = unify_var_ids(utable, *my_first, *other_first); - let new_second = unify_var_ids(utable, *my_second, *other_second); - - // Propagate any mismatches. - if new_first == Mismatch { - new_first - } else if new_second == Mismatch { - new_second - } else { - let new_first_id = utable.new_key(new_first); - let new_second_id = utable.new_key(new_second); - - Structure(FlatType::Tuple2(new_first_id, new_second_id)) - } - }, - (FlatType::Tuple2(_, _), _) => Mismatch, - (_, FlatType::Tuple2(_, _)) => Mismatch, - (FlatType::Tuple3(my_first, my_second, my_third), - FlatType::Tuple3(other_first, other_second, other_third)) => { - let new_first = unify_var_ids(utable, *my_first, *other_first); - let new_second = unify_var_ids(utable, *my_second, *other_second); - let new_third = unify_var_ids(utable, *my_third, *other_third); - - // Propagate any mismatches. - if new_first == Mismatch { - new_first - } else if new_second == Mismatch { - new_second - } else if new_third == Mismatch { - new_third - } else { - let new_first_id = utable.new_key(new_first); - let new_second_id = utable.new_key(new_second); - let new_third_id = utable.new_key(new_third); - - Structure(FlatType::Tuple3(new_first_id, new_second_id, new_third_id)) - } - }, - // (FlatType::Tuple3(_, _, _), _) => Mismatch, - // (_, FlatType::Tuple3(_, _, _)) => Mismatch, - } -} - -#[inline] -fn unify_flex_union_with_structure(flex_union: &BTreeSet, var: &Variable) -> Variable { - // TODO I guess iterate through the set, looking up Variables - - panic!("TODO"); - // if flex_union.contains(var) { - // Narrow the union to the one member type - var.clone() - // } else { - // Mismatch - // } -} - - -// Given a type, create a constraint variable for it and add it to the table. -// Return the VarId corresponding to the variable in the table. -fn type_to_var_id(utable: &mut UTable, typ: Type) -> VarId { - match typ { - Type::Call(box fn_type, box arg_type) => { - panic!("TODO"); - utable.new_key(Mismatch) - // let left_var_id = type_to_var_id(utable, left_type); - // let right_var_id = type_to_var_id(utable, right_type); - - // // TODO should we match on op to hardcode the types we expect? - // let flat_type = FlatType::Function(left_var_id, right_var_id); - - // utable.new_key(Structure(flat_type)) - } - } -} - -#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] -struct VarId(u32); - -impl UnifyKey for VarId { - type Value = Variable; - - fn index(&self) -> u32 { self.0 } - fn from_index(u: u32) -> VarId { VarId(u) } - - // tag is a static string that's only used in debugging - fn tag() -> &'static str { "VarId" } -} - -fn unify_var_ids(utable: &mut UTable, left_id: VarId, right_id: VarId) -> Variable { - let left_content = utable.probe_value(left_id); - let right_content = utable.probe_value(right_id); - - if left_content == right_content { - left_content - } else { - unify_vars(utable, &left_content, &right_content) - } -} - -pub type TypeError = String; - -pub fn solve_constraint(constraint: Constraint) -> Result> { - let mut utable: UTable = UnificationTable::new(); - - solve(&mut utable, constraint); - - Ok("TODO: actually gather errors etc".to_owned()) -} - -fn solve(utable: &mut UTable, constraint: Constraint) { - match constraint { - Constraint::True => {}, - - Constraint::Equal(actual_type, expectation) => { - let actual_var_id = type_to_var_id(utable, actual_type); - let expected_var_id = type_to_var_id(utable, expectation); - let answer = unify_var_ids(utable, actual_var_id, expected_var_id); - - panic!("Oh no! TYPE MISMATCH! (TODO: record errors as appropriate)"); - () - // match answer { - // Mismatch => { - // panic!("Oh no! TYPE MISMATCH! (TODO: record errors as appropriate)"); - // } - // do introduce rank pools vars - // return state - - // UF.modify var $ \(Descriptor content _ mark copy) -> - // Descriptor content rank mark copy - - // Unify.Err vars actualType expectedType -> - - // panic!("TODO xyz"); - // do introduce rank pools vars - // return $ addError state $ - // Error.BadExpr region category actualType $ - // Error.typeReplace expectation expectedType - // } - }, - - Constraint::Batch(_) => { - panic!("TODO"); - () - } - } -} - diff --git a/src/typ.rs b/src/typ.rs deleted file mode 100644 index 5f211e92e8..0000000000 --- a/src/typ.rs +++ /dev/null @@ -1,12 +0,0 @@ - - -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] -pub enum Type { - // Symbol(String), - // Int, - // Float, - // Number, - // TypeUnion(BTreeSet), - // Function(Box, Box), - Call(Box, Box), -} diff --git a/src/unify.rs b/src/unify.rs deleted file mode 100644 index d3056ca0be..0000000000 --- a/src/unify.rs +++ /dev/null @@ -1,363 +0,0 @@ -use std::collections::BTreeSet; -use std::collections::BTreeMap; -use self::Type::*; - -pub type Name<'a> = &'a str; - -pub type ModuleName<'a> = &'a str; - -#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] -pub enum Type<'a> { - Unbound, - String, - Char, - Int, - Float, - Number, - Symbol(&'a str), - Array(Box>), - Function(Box>, Box>), - Record(BTreeMap, Type<'a>>), - Tuple(Vec>), - Union(BTreeSet>), -} - -// CANONICAL IR - we have already done stuff like giving errors for -// duplicate field names - -#[derive(Debug, PartialEq)] -pub enum Expr<'a> { - // Variables - Declaration(&'a Pattern<'a>, Box<&'a Expr<'a>>, Box>), - LookupLocal(&'a Name<'a>), - LookupGlobal(&'a ModuleName<'a>, &'a Name<'a>), - - // Scalars - Symbol(&'a str), - String(&'a str), - Char(char), - HexOctalBinary(i64), // : Int - FractionalNumber(f64), // : Float - WholeNumber(i64), // : Int | Float - - // Collections - Array(Vec>), - Record(Vec<(&'a Name<'a>, &'a Expr<'a>)>), - Tuple(Vec<&'a Expr<'a>>), - LookupName(Name<'a>, Box<&'a Expr<'a>>), - // TODO add record update - - // Functions - Function(&'a Pattern<'a>, &'a Expr<'a>), - Call(Box<&'a Expr<'a>>, Box<&'a Expr<'a>>), - CallOperator(&'a Operator, Box<&'a Expr<'a>>, Box<&'a Expr<'a>>), - - // Conditionals - If(Box<&'a Expr<'a>> /* Conditional */, Box<&'a Expr<'a>> /* True branch */, Box<&'a Expr<'a>> /* False branch */), - Case(Box<&'a Expr<'a>>, Vec<(&'a Pattern<'a>, &'a Expr<'a>)>), -} - -#[derive(Debug, PartialEq)] -pub enum Operator { - Plus, Minus, Star, Caret, Percent, FloatDivision, IntDivision, - GT, GTE, LT, LTE, - EQ, NE, And, Or, - QuestionMark, Or -} - -#[derive(Debug, PartialEq)] -pub enum Pattern<'a> { - Name(&'a Name<'a>), // `foo =` - As(&'a Name<'a>, &'a Pattern<'a>), // ` as foo` - Type(&'a Type<'a>), - Symbol(&'a str), - String(&'a str), - Char(char), - WholeNumber(&'a str), - FractionalNumber(&'a str), - HexOctalBinary(&'a str), - Tuple(Vec>), - Record(Vec<(Name<'a>, Option>)>), // { a = 5, b : Int as x, c } -} - - -pub fn infer<'a>(expr: &Expr<'a>) -> Result, UnificationProblem> { - match expr { - Expr::String(_) => Ok(String), - Expr::Char(_) => Ok(Char), - Expr::HexOctalBinary(_) => Ok(Int), - Expr::FractionalNumber(_) => Ok(Float), - Expr::WholeNumber(_) => Ok(Number), - Expr::Symbol(sym) => Ok(Symbol(sym)), - Expr::Array(elem_exprs) => { - let elem_type; - - if elem_exprs.is_empty() { - elem_type = Unbound; - } else { - let mut unified_type = BTreeSet::new(); - - // Unify the types of all the elements - for elem_expr in elem_exprs { - unified_type.insert(infer(&elem_expr)?); - } - - if unified_type.len() == 1 { - // No point in storing a union of 1. - elem_type = unified_type.into_iter().next().unwrap() - } else { - elem_type = Union(unified_type) - } - } - - Ok(Array(Box::new(elem_type))) - }, - Expr::Record(fields) => { - let mut rec_type: BTreeMap<&'a Name<'a>, Type<'a>> = BTreeMap::new(); - - for (field, subexpr) in fields { - let field_type = infer(subexpr)?; - - rec_type.insert(&field, field_type); - } - - Ok(Record(rec_type)) - }, - Expr::Tuple(exprs) => { - let mut tuple_type: Vec> = Vec::new(); - - for subexpr in exprs { - let field_type = infer(subexpr)?; - - tuple_type.push(field_type); - } - - Ok(Tuple(tuple_type)) - }, - Expr::If(box cond, expr_if_true, expr_if_false) => { - let cond_type = infer(&cond)?; - - // if-conditionals must be of type Bool - if !matches_bool_type(&cond_type) { - return Err(UnificationProblem::IfConditionNotBool); - } - - // unify the true and false branches - let true_type = infer(&expr_if_true)?; - let false_type = infer(&expr_if_false)?; - - let mut unified_type = BTreeSet::new(); - - unified_type.insert(true_type); - unified_type.insert(false_type); - - if unified_type.len() == 1 { - // No point in storing a union of 1. - // - // We can't reuse true_type because it's been moved into the set - // but we can pull it back out of the set - Ok(unified_type.into_iter().next().unwrap()) - } else { - Ok(Union(unified_type)) - } - }, - Call(func, arg) => { - - }, - CallOperator(op, left_expr, right_expr) => { - let left = &(infer(left_expr)?); - let right = &(infer(right_expr)?); - - match op { - Operator::EQ | Operator::NE | Operator::And | Operator::Or => { - if types_match(left, right) { - conform_to_bool(left) - } else { - Err(UnificationProblem::TypeMismatch) - } - }, - Operator::Plus | Operator::Minus | Operator::Star - | Operator::GT | Operator::LT | Operator::GTE | Operator::LTE - | Operator::Caret | Operator::Percent => { - if types_match(left, right) { - conform_to_number(left) - } else { - Err(UnificationProblem::TypeMismatch) - } - }, - Operator::FloatDivision => { - if matches_float_type(left) && matches_float_type(right) { - Ok(&Float) - } else { - Err(UnificationProblem::TypeMismatch) - } - }, - Operator::IntDivision => { - if matches_int_type(left) && matches_int_type(right) { - Ok(&Int) - } else { - Err(UnificationProblem::TypeMismatch) - } - }, - Operator::CombineStrings => { - if matches_string_type(left) && matches_string_type(right) { - Ok(&String) - } else { - Err(UnificationProblem::TypeMismatch) - } - }, - Operator::QuestionMark => { - if types_match(left, right) { - conform_to_optional(left) - } else { - Err(UnificationProblem::TypeMismatch) - } - } - } - }, - Expr::Declaration(pattern, let_expr, in_expr) => { - // Think of this as a let..in even though syntactically it's not. - // We need to type-check the let-binding, but the type of the - // *expression* we're expaning is only affected by the in-block. - check_pattern(&pattern, &let_expr)?; - - infer(in_expr) - } - } -} - -fn types_match<'a>(first: &'a Type<'a>, second: &'a Type<'a>) -> bool { - match (first, second) { - (Type::Union(first_types), Type::Union(second_types)) => { - // If any type is not directly present in the other union, - // it must at least match *some* type in the other union - first_types.difference(second_types).into_iter().all(|not_in_second_type| { - second_types.iter().any(|second_type| types_match(second_type, not_in_second_type)) - }) && - second_types.difference(first_types).into_iter().all(|not_in_first_type| { - first_types.iter().any(|first_type| types_match(first_type, not_in_first_type)) - }) - }, - - // Happy path: try these first, since we expect them to succeed. - // These are sorted based on a vague guess of how often they will be used in practice. - (Type::Symbol(sym_one), Type::Symbol(sym_two)) => sym_one == sym_two, - (Type::String, Type::String) => true, - (Type::Unbound, _) | (_, Type::Unbound)=> true, - (Type::Array(box elem_type_one), Type::Array(box elem_type_two)) => { - types_match(elem_type_one, elem_type_two) - }, - (Type::Number, Type::Number) => true, - (Type::Number, other) => matches_number_type(other), - (other, Type::Number) => matches_number_type(other), - (Type::Int, Type::Int) => true, - (Type::Float, Type::Float) => true, - (Type::Tuple(first_elems), Type::Tuple(second_elems)) => { - // TODO verify that the elems and their types match up - // TODO write some scenarios to understand these better - - // like, what happens if you have a function that takes - // a lambda whose argument takes an open record, - // and you pass a lamba whose argument takes *fewer* fields? - // that should work! the function is gonna pass it a lambda that - // has more fields than it needs. - // I think there's an element of directionality here that I'm - // disregarding. Maybe this function shouldn't commute. - }, - (Type::Function(first_arg), Type::Function(second_arg)) => { - // TODO verify that the elems and their types match up - }, - (Type::Record(first_fields), Type::Record(second_fields)) => { - // TODO verify that the fields and their types match up - // TODO what should happen if one is a superset of the other? fail? - }, - (Type::Char, Type::Char) => true, - - // Unhappy path - expect these to fail, so check them last - (Type::Union(first_types), _) => { - first_types.iter().all(|typ| types_match(typ, second)) - }, - (_, Type::Union(second_types)) => { - second_types.iter().all(|typ| types_match(first, typ)) - }, - (Type::String, _) | (_, Type::String) => false, - (Type::Char, _) | (_, Type::Char) => false, - (Type::Int, _) | (_, Type::Int) => false, - (Type::Float, _) | (_, Type::Float) => false, - (Type::Symbol(_), _) | (_, Type::Symbol(_)) => false, - (Type::Array(_), _) | (_, Type::Array(_)) => false, - (Type::Record(_), _) | (_, Type::Record(_)) => false, - (Type::Tuple(_), _) | (_, Type::Tuple(_)) => false, - (Type::Function(_, _), _) | (_, Type::Function(_, _)) => false, - } -} - - -fn check_pattern<'a>(pattern: &'a Pattern<'a>, expr: &'a Expr<'a>) -> Result<(), UnificationProblem> { - let expr_type = infer(expr)?; - - panic!("TODO check the pattern's type against expr_type, then write some tests for funky record pattern cases - this is our first real unification! Next one will be field access, ooooo - gonna want lots of tests for that") -} - -const TRUE_SYMBOL_STR: &'static str = "True"; -const FALSE_SYMBOL_STR: &'static str = "False"; - -pub fn matches_string_type<'a>(candidate: &Type<'a>) -> bool { - match candidate { - Unbound | String => true, - Type::Union(types) => { - types.iter().all(|typ| matches_string_type(typ)) - }, - _ => Err(UnificationProblem::TypeMismatch) - } -} - -pub fn matches_bool_type<'a>(candidate: &Type<'a>) -> bool { - match candidate { - Type::Unbound => true, - Type::Symbol(str) => str == &TRUE_SYMBOL_STR || str == &FALSE_SYMBOL_STR, - Type::Union(types) => { - types.iter().all(|typ| matches_bool_type(typ)) - } - _ => false - } -} - -pub fn matches_number_type<'a>(candidate: &Type<'a>) -> bool { - match candidate { - Type::Unbound | Type::Int | Type::Float | Type::Number => true, - Type::Union(types) => { - types.iter().all(|typ| matches_number_type(typ)) - } - _ => false - } -} - -pub fn matches_int_type<'a>(candidate: &Type<'a>) -> bool { - match candidate { - Type::Unbound | Type::Int => true, - Type::Union(types) => { - types.iter().all(|typ| matches_int_type(typ)) - } - _ => false - } -} - -pub fn matches_float_type<'a>(candidate: &Type<'a>) -> bool { - match candidate { - Type::Unbound | Type::Float => true, - Type::Union(types) => { - types.iter().all(|typ| matches_float_type(typ)) - } - _ => false - } -} - -#[derive(Debug)] -pub enum UnificationProblem { - CannotUnifyAssignments, - NotMemberOfUnion, - TypeMismatch, - IfConditionNotBool, - SymbolMismatch -} -