<commit_before>use std::collections::{BTreeMap, HashMap, HashSet};

use log::trace;

use super::types::{ConflictMap, ConflictReason};
use crate::core::resolver::Context;
use crate::core::{Dependency, PackageId};

/// This is a trie for storing a large number of sets designed to
/// efficiently see if any of the stored sets are a subset of a search set.
enum ConflictStoreTrie {
    /// One of the stored sets.
    Leaf(ConflictMap),
    /// A map from an element to a subtrie where
    /// all the sets in the subtrie contains that element.
    Node(BTreeMap<PackageId, ConflictStoreTrie>),
}

impl ConflictStoreTrie {
    /// Finds any known set of conflicts, if any,
    /// which are activated in `cx` and pass the `filter` specified?
    fn find_conflicting(
        &self,
        cx: &Context,
        must_contain: Option<PackageId>,
    ) -> Option<&ConflictMap> {
        match self {
            ConflictStoreTrie::Leaf(c) => {
                if must_contain.is_none() {
                    // `is_conflicting` checks that all the elements are active,
                    // but we have checked each one by the recursion of this function.
                    debug_assert!(cx.is_conflicting(None, c).is_some());
                    Some(c)
                } else {
                    // We did not find `must_contain`, so we need to keep looking.
                    None
                }
            }
            ConflictStoreTrie::Node(m) => {
                let mut out = None;
                for (&pid, store) in must_contain
                    .map(|f| m.range(..=f))
                    .unwrap_or_else(|| m.range(..))
                {
                    // If the key is active, then we need to check all of the corresponding subtrie.
                    if cx.is_active(pid).is_some() {
                        if let Some(o) =
                            store.find_conflicting(cx, must_contain.filter(|&f| f != pid))
                        {
                            assert!(out.replace(o).is_none());
                        }
                    }
                    // Else, if it is not active then there is no way any of the corresponding
                    // subtrie will be conflicting.
                }
                out
            }
        }
    }

    fn insert(&mut self, mut iter: impl Iterator<Item = PackageId>, con: ConflictMap) {
        if let Some(pid) = iter.next() {
            if let ConflictStoreTrie::Node(p) = self {
                p.entry(pid)
                    .or_insert_with(|| ConflictStoreTrie::Node(BTreeMap::new()))
                    .insert(iter, con);
            }
        // Else, we already have a subset of this in the `ConflictStore`.
        } else {
            // We are at the end of the set we are adding, there are three cases for what to do
            // next:
            // 1. `self` is a empty dummy Node inserted by `or_insert_with`
            //      in witch case we should replace it with `Leaf(con)`.
            // 2. `self` is a `Node` because we previously inserted a superset of
            //      the thing we are working on (I don't know if this happens in practice)
            //      but the subset that we are working on will
            //      always match any time the larger set would have
            //      in witch case we can replace it with `Leaf(con)`.
            // 3. `self` is a `Leaf` that is in the same spot in the structure as
            //      the thing we are working on. So it is equivalent.
            //      We can replace it with `Leaf(con)`.
            if cfg!(debug_assertions) {
                if let ConflictStoreTrie::Leaf(c) = self {
                    let a: Vec<_> = con.keys().collect();
                    let b: Vec<_> = c.keys().collect();
                    assert_eq!(a, b);
                }
            }
            *self = ConflictStoreTrie::Leaf(con)
        }
    }
}

pub(super) struct ConflictCache {
    // `con_from_dep` is a cache of the reasons for each time we
    // backtrack. For example after several backtracks we may have:
    //
    //  con_from_dep[`foo = "^1.0.2"`] = map!{
    //      `foo=1.0.1`: map!{`foo=1.0.1`: Semver},
    //      `foo=1.0.0`: map!{`foo=1.0.0`: Semver},
    //  };
    //
    // This can be read as "we cannot find a candidate for dep `foo = "^1.0.2"`
    // if either `foo=1.0.1` OR `foo=1.0.0` are activated".
    //
    // Another example after several backtracks we may have:
    //
    //  con_from_dep[`foo = ">=0.8.2, <=0.9.3"`] = map!{
    //      `foo=0.8.1`: map!{
    //          `foo=0.9.4`: map!{`foo=0.8.1`: Semver, `foo=0.9.4`: Semver},
    //      }
    //  };
    //
    // This can be read as "we cannot find a candidate for dep `foo = ">=0.8.2,
    // <=0.9.3"` if both `foo=0.8.1` AND `foo=0.9.4` are activated".
    //
    // This is used to make sure we don't queue work we know will fail. See the
    // discussion in https://github.com/rust-lang/cargo/pull/5168 for why this
    // is so important. The nested HashMaps act as a kind of btree, that lets us
    // look up which entries are still active without
    // linearly scanning through the full list.
    //
    // Also, as a final note, this map is **not** ever removed from. This remains
    // as a global cache which we never delete from. Any entry in this map is
    // unconditionally true regardless of our resolution history of how we got
    // here.
    con_from_dep: HashMap<Dependency, ConflictStoreTrie>,
    // `dep_from_pid` is an inverse-index of `con_from_dep`.
    // For every `PackageId` this lists the `Dependency`s that mention it in `dep_from_pid`.
    dep_from_pid: HashMap<PackageId, HashSet<Dependency>>,
}

impl ConflictCache {
    pub fn new() -> ConflictCache {
        ConflictCache {
            con_from_dep: HashMap::new(),
            dep_from_pid: HashMap::new(),
        }
    }
    /// Finds any known set of conflicts, if any,
    /// which are activated in `cx` and pass the `filter` specified?
    pub fn find_conflicting(
        &self,
        cx: &Context,
        dep: &Dependency,
        must_contain: Option<PackageId>,
    ) -> Option<&ConflictMap> {
        let out = self
            .con_from_dep
            .get(dep)?
            .find_conflicting(cx, must_contain);
        if cfg!(debug_assertions) {
            if let Some(f) = must_contain {
                if let Some(c) = &out {
                    assert!(c.contains_key(&f));
                }
            }
        }
        out
    }
    pub fn conflicting(&self, cx: &Context, dep: &Dependency) -> Option<&ConflictMap> {
        self.find_conflicting(cx, dep, None)
    }

    /// Adds to the cache a conflict of the form:
    /// `dep` is known to be unresolvable if
    /// all the `PackageId` entries are activated.
    pub fn insert(&mut self, dep: &Dependency, con: &ConflictMap) {
        if con.values().any(|c| *c == ConflictReason::PublicDependency) {
            // TODO: needs more info for back jumping
            // for now refuse to cache it.
            return;
        }
        self.con_from_dep
            .entry(dep.clone())
            .or_insert_with(|| ConflictStoreTrie::Node(BTreeMap::new()))
            .insert(con.keys().cloned(), con.clone());

        trace!(
            "{} = \"{}\" adding a skip {:?}",
            dep.package_name(),
            dep.version_req(),
            con
        );

        for c in con.keys() {
            self.dep_from_pid
                .entry(c.clone())
                .or_insert_with(HashSet::new)
                .insert(dep.clone());
        }
    }
    pub fn dependencies_conflicting_with(&self, pid: PackageId) -> Option<&HashSet<Dependency>> {
        self.dep_from_pid.get(&pid)
    }
}
<commit_msg>We have more then one conflict that matches, so pick one with the max back-jumping.<commit_after>use std::collections::{BTreeMap, HashMap, HashSet};

use log::trace;

use super::types::{ConflictMap, ConflictReason};
use crate::core::resolver::Context;
use crate::core::{Dependency, PackageId};

/// This is a trie for storing a large number of sets designed to
/// efficiently see if any of the stored sets are a subset of a search set.
enum ConflictStoreTrie {
    /// One of the stored sets.
    Leaf(ConflictMap),
    /// A map from an element to a subtrie where
    /// all the sets in the subtrie contains that element.
    Node(BTreeMap<PackageId, ConflictStoreTrie>),
}

impl ConflictStoreTrie {
    /// Finds any known set of conflicts, if any,
    /// which are activated in `cx` and contain `PackageId` specified.
    /// If more then one are activated, then it will return
    /// one that will allow for the most jump-back.
    fn find_conflicting(
        &self,
        cx: &Context,
        must_contain: Option<PackageId>,
    ) -> Option<(&ConflictMap, usize)> {
        match self {
            ConflictStoreTrie::Leaf(c) => {
                if must_contain.is_none() {
                    // `is_conflicting` checks that all the elements are active,
                    // but we have checked each one by the recursion of this function.
                    debug_assert!(cx.is_conflicting(None, c).is_some());
                    Some((c, 0))
                } else {
                    // We did not find `must_contain`, so we need to keep looking.
                    None
                }
            }
            ConflictStoreTrie::Node(m) => {
                let mut out = None;
                for (&pid, store) in must_contain
                    .map(|f| m.range(..=f))
                    .unwrap_or_else(|| m.range(..))
                {
                    // If the key is active, then we need to check all of the corresponding subtrie.
                    if let Some(age_this) = cx.is_active(pid) {
                        if let Some((o, age_o)) =
                            store.find_conflicting(cx, must_contain.filter(|&f| f != pid))
                        {
                            let age = if must_contain == Some(pid) {
                                // all the results will include `must_contain`
                                // so the age of must_contain is not relevant to find the best result.
                                age_o
                            } else {
                                std::cmp::max(age_this, age_o)
                            };
                            let out_age = out.get_or_insert((o, age)).1;
                            if out_age > age {
                                // we found one that can jump-back further so replace the out.
                                out = Some((o, age));
                            }
                        }
                    }
                    // Else, if it is not active then there is no way any of the corresponding
                    // subtrie will be conflicting.
                }
                out
            }
        }
    }

    fn insert(&mut self, mut iter: impl Iterator<Item = PackageId>, con: ConflictMap) {
        if let Some(pid) = iter.next() {
            if let ConflictStoreTrie::Node(p) = self {
                p.entry(pid)
                    .or_insert_with(|| ConflictStoreTrie::Node(BTreeMap::new()))
                    .insert(iter, con);
            }
        // Else, we already have a subset of this in the `ConflictStore`.
        } else {
            // We are at the end of the set we are adding, there are three cases for what to do
            // next:
            // 1. `self` is a empty dummy Node inserted by `or_insert_with`
            //      in witch case we should replace it with `Leaf(con)`.
            // 2. `self` is a `Node` because we previously inserted a superset of
            //      the thing we are working on (I don't know if this happens in practice)
            //      but the subset that we are working on will
            //      always match any time the larger set would have
            //      in witch case we can replace it with `Leaf(con)`.
            // 3. `self` is a `Leaf` that is in the same spot in the structure as
            //      the thing we are working on. So it is equivalent.
            //      We can replace it with `Leaf(con)`.
            if cfg!(debug_assertions) {
                if let ConflictStoreTrie::Leaf(c) = self {
                    let a: Vec<_> = con.keys().collect();
                    let b: Vec<_> = c.keys().collect();
                    assert_eq!(a, b);
                }
            }
            *self = ConflictStoreTrie::Leaf(con)
        }
    }
}

pub(super) struct ConflictCache {
    // `con_from_dep` is a cache of the reasons for each time we
    // backtrack. For example after several backtracks we may have:
    //
    //  con_from_dep[`foo = "^1.0.2"`] = map!{
    //      `foo=1.0.1`: map!{`foo=1.0.1`: Semver},
    //      `foo=1.0.0`: map!{`foo=1.0.0`: Semver},
    //  };
    //
    // This can be read as "we cannot find a candidate for dep `foo = "^1.0.2"`
    // if either `foo=1.0.1` OR `foo=1.0.0` are activated".
    //
    // Another example after several backtracks we may have:
    //
    //  con_from_dep[`foo = ">=0.8.2, <=0.9.3"`] = map!{
    //      `foo=0.8.1`: map!{
    //          `foo=0.9.4`: map!{`foo=0.8.1`: Semver, `foo=0.9.4`: Semver},
    //      }
    //  };
    //
    // This can be read as "we cannot find a candidate for dep `foo = ">=0.8.2,
    // <=0.9.3"` if both `foo=0.8.1` AND `foo=0.9.4` are activated".
    //
    // This is used to make sure we don't queue work we know will fail. See the
    // discussion in https://github.com/rust-lang/cargo/pull/5168 for why this
    // is so important. The nested HashMaps act as a kind of btree, that lets us
    // look up which entries are still active without
    // linearly scanning through the full list.
    //
    // Also, as a final note, this map is **not** ever removed from. This remains
    // as a global cache which we never delete from. Any entry in this map is
    // unconditionally true regardless of our resolution history of how we got
    // here.
    con_from_dep: HashMap<Dependency, ConflictStoreTrie>,
    // `dep_from_pid` is an inverse-index of `con_from_dep`.
    // For every `PackageId` this lists the `Dependency`s that mention it in `dep_from_pid`.
    dep_from_pid: HashMap<PackageId, HashSet<Dependency>>,
}

impl ConflictCache {
    pub fn new() -> ConflictCache {
        ConflictCache {
            con_from_dep: HashMap::new(),
            dep_from_pid: HashMap::new(),
        }
    }
    /// Finds any known set of conflicts, if any,
    /// which are activated in `cx` and contain `PackageId` specified.
    /// If more then one are activated, then it will return
    /// one that will allow for the most jump-back.
    pub fn find_conflicting(
        &self,
        cx: &Context,
        dep: &Dependency,
        must_contain: Option<PackageId>,
    ) -> Option<&ConflictMap> {
        let out = self
            .con_from_dep
            .get(dep)?
            .find_conflicting(cx, must_contain)
            .map(|(c, _)| c);
        if cfg!(debug_assertions) {
            if let Some(f) = must_contain {
                if let Some(c) = &out {
                    assert!(c.contains_key(&f));
                }
            }
        }
        out
    }
    pub fn conflicting(&self, cx: &Context, dep: &Dependency) -> Option<&ConflictMap> {
        self.find_conflicting(cx, dep, None)
    }

    /// Adds to the cache a conflict of the form:
    /// `dep` is known to be unresolvable if
    /// all the `PackageId` entries are activated.
    pub fn insert(&mut self, dep: &Dependency, con: &ConflictMap) {
        if con.values().any(|c| *c == ConflictReason::PublicDependency) {
            // TODO: needs more info for back jumping
            // for now refuse to cache it.
            return;
        }
        self.con_from_dep
            .entry(dep.clone())
            .or_insert_with(|| ConflictStoreTrie::Node(BTreeMap::new()))
            .insert(con.keys().cloned(), con.clone());

        trace!(
            "{} = \"{}\" adding a skip {:?}",
            dep.package_name(),
            dep.version_req(),
            con
        );

        for c in con.keys() {
            self.dep_from_pid
                .entry(c.clone())
                .or_insert_with(HashSet::new)
                .insert(dep.clone());
        }
    }
    pub fn dependencies_conflicting_with(&self, pid: PackageId) -> Option<&HashSet<Dependency>> {
        self.dep_from_pid.get(&pid)
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::collections::hash_map::Entry::*;
use std::ops::Deref;
use std::mem;

#[derive(Debug)]
struct PatchLoc(usize, u8);

#[derive(Debug)]
pub struct AssemblingBuffer {
    // instruction buffer while building the assembly
    ops: Vec<u8>,

    // label name -> target loc
    global_labels: HashMap<&'static str, usize>,
    // end of patch location -> name
    global_relocs: Vec<(PatchLoc, &'static str)>,

    // label id -> target loc
    dynamic_labels: HashMap<usize, usize>,
    // location to be resolved, loc, label id
    dynamic_relocs: Vec<(PatchLoc, usize)>,

    // labelname -> most recent patch location
    local_labels: HashMap<&'static str, usize>,
    // locations to be patched once this label gets seen. name -> Vec<locs>
    local_relocs: HashMap<&'static str, Vec<PatchLoc>>
}

impl AssemblingBuffer {
    pub fn new() -> AssemblingBuffer {
        AssemblingBuffer {
            ops: Vec::new(),
            global_labels: HashMap::new(),
            dynamic_labels: HashMap::new(),
            local_labels: HashMap::new(),
            global_relocs: Vec::new(),
            dynamic_relocs: Vec::new(),
            local_relocs: HashMap::new()
        }
    }

    pub fn push(&mut self, value: u8) {
        self.ops.push(value);
    }

    pub fn push_8(&mut self, value: i8) {
        self.ops.push(value as u8);
    }

    pub fn push_16(&mut self, value: i16) {
        self.ops.extend( unsafe { ::std::mem::transmute::<_, [u8; 2]>(value.to_le())}.into_iter() );
    }

    pub fn push_32(&mut self, value: i32) {
        self.ops.extend( unsafe { ::std::mem::transmute::<_, [u8; 4]>(value.to_le())}.into_iter() );
    }

    pub fn push_64(&mut self, value: i64) {
        self.ops.extend( unsafe { ::std::mem::transmute::<_, [u8; 8]>(value.to_le())}.into_iter() );
    }

    pub fn align(&mut self, to: usize) {
        if self.ops.len() % to != 0 {
            for _ in 0..(to - self.ops.len() % to) {
                self.ops.push(0x90)
            }
        }
    }

    fn patch_loc(ops: &mut [u8], loc: PatchLoc, target: usize) {
        let buf = &mut ops[loc.0 - loc.1 as usize .. loc.0];

        unsafe { match loc.1 {
            1 => buf.copy_from_slice(&mem::transmute::<_, [u8; 1]>( ((target - loc.0) as i8 ).to_le() )),
            2 => buf.copy_from_slice(&mem::transmute::<_, [u8; 2]>( ((target - loc.0) as i16).to_le() )),
            4 => buf.copy_from_slice(&mem::transmute::<_, [u8; 4]>( ((target - loc.0) as i32).to_le() )),
            8 => buf.copy_from_slice(&mem::transmute::<_, [u8; 8]>( ((target - loc.0) as i64).to_le() )),
            _ => panic!("invalid patch size")
        } }
    }

    pub fn global_label(&mut self, name: &'static str) {
        if let Some(name) = self.global_labels.insert(name, self.ops.len()) {
            panic!("Duplicate global label '{}'", name);
        }
    }

    pub fn global_reloc(&mut self, name: &'static str, size: u8) {
        self.global_relocs.push((PatchLoc(self.ops.len(), size), name));
    }

    pub fn dynamic_label(&mut self, id: usize) {
        if let Some(id) = self.dynamic_labels.insert(id, self.ops.len()) {
            panic!("Duplicate label '{}'", id);
        }
    }

    pub fn dynamic_reloc(&mut self, id: usize, size: u8) {
        self.dynamic_relocs.push((PatchLoc(self.ops.len(), size), id));
    }

    pub fn local_label(&mut self, name: &'static str) {
        if let Some(relocs) = self.local_relocs.remove(&name) {
            for loc in relocs {
                let len = self.ops.len();
                Self::patch_loc(&mut self.ops, loc, len);
            }
        }
        self.local_labels.insert(name, self.ops.len());
    }

    pub fn forward_reloc(&mut self, name: &'static str, size: u8) {
        match self.local_relocs.entry(name) {
            Occupied(mut o) => {
                o.get_mut().push(PatchLoc(self.ops.len(), size));
            },
            Vacant(v)   => {
                v.insert(vec![PatchLoc(self.ops.len(), size)]);
            }
        }
    }

    pub fn backward_reloc(&mut self, name: &'static str, size: u8) {
        if let Some(&target) = self.local_labels.get(&name) {
            let len = self.ops.len();
            Self::patch_loc(&mut self.ops, PatchLoc(len, size), target)
        } else {
            panic!("Unknown local label '{}'", name);
        }
    }

    pub fn encode_relocs(&mut self) {
        for (loc, name) in self.global_relocs.drain(..) {
            if let Some(&target) = self.global_labels.get(&name) {
                Self::patch_loc(&mut self.ops, loc, target)
            } else {
                panic!("Unkonwn global label '{}'", name);
            }
        }

        for (loc, id) in self.dynamic_relocs.drain(..) {
            if let Some(&target) = self.dynamic_labels.get(&id) {
                Self::patch_loc(&mut self.ops, loc, target)
            } else {
                panic!("Unkonwn dynamic label '{}'", id);
            }
        }

        if let Some(name) = self.local_relocs.keys().next() {
            panic!("Unknown local label '{}'", name);
        }
    }
}

impl Deref for AssemblingBuffer {
    type Target = Vec<u8>;
    fn deref(&self) -> &Vec<u8> {
        &self.ops
    }
}
<commit_msg>Mark run-time functions for inlining as this is required for cross-crate inlining<commit_after>use std::collections::HashMap;
use std::collections::hash_map::Entry::*;
use std::ops::Deref;
use std::mem;

#[derive(Debug)]
struct PatchLoc(usize, u8);

#[derive(Debug)]
pub struct AssemblingBuffer {
    // instruction buffer while building the assembly
    ops: Vec<u8>,

    // label name -> target loc
    global_labels: HashMap<&'static str, usize>,
    // end of patch location -> name
    global_relocs: Vec<(PatchLoc, &'static str)>,

    // label id -> target loc
    dynamic_labels: HashMap<usize, usize>,
    // location to be resolved, loc, label id
    dynamic_relocs: Vec<(PatchLoc, usize)>,

    // labelname -> most recent patch location
    local_labels: HashMap<&'static str, usize>,
    // locations to be patched once this label gets seen. name -> Vec<locs>
    local_relocs: HashMap<&'static str, Vec<PatchLoc>>
}

impl AssemblingBuffer {
    pub fn new() -> AssemblingBuffer {
        AssemblingBuffer {
            ops: Vec::new(),
            global_labels: HashMap::new(),
            dynamic_labels: HashMap::new(),
            local_labels: HashMap::new(),
            global_relocs: Vec::new(),
            dynamic_relocs: Vec::new(),
            local_relocs: HashMap::new()
        }
    }

    #[inline]
    pub fn push(&mut self, value: u8) {
        self.ops.push(value);
    }

    #[inline]
    pub fn push_8(&mut self, value: i8) {
        self.ops.push(value as u8);
    }

    #[inline]
    pub fn push_16(&mut self, value: i16) {
        self.ops.extend( unsafe { ::std::mem::transmute::<_, [u8; 2]>(value.to_le()) }.into_iter() );
    }

    #[inline]
    pub fn push_32(&mut self, value: i32) {
        self.ops.extend( unsafe { ::std::mem::transmute::<_, [u8; 4]>(value.to_le()) }.into_iter() );
    }

    #[inline]
    pub fn push_64(&mut self, value: i64) {
        self.ops.extend( unsafe { ::std::mem::transmute::<_, [u8; 8]>(value.to_le()) }.into_iter() );
    }

    #[inline]
    pub fn align(&mut self, to: usize) {
        if self.ops.len() % to != 0 {
            for _ in 0..(to - self.ops.len() % to) {
                self.ops.push(0x90)
            }
        }
    }

    #[inline]
    fn patch_loc(ops: &mut [u8], loc: PatchLoc, target: usize) {
        let buf = &mut ops[loc.0 - loc.1 as usize .. loc.0];

        unsafe { match loc.1 {
            1 => buf.copy_from_slice(&mem::transmute::<_, [u8; 1]>( ((target - loc.0) as i8 ).to_le() )),
            2 => buf.copy_from_slice(&mem::transmute::<_, [u8; 2]>( ((target - loc.0) as i16).to_le() )),
            4 => buf.copy_from_slice(&mem::transmute::<_, [u8; 4]>( ((target - loc.0) as i32).to_le() )),
            8 => buf.copy_from_slice(&mem::transmute::<_, [u8; 8]>( ((target - loc.0) as i64).to_le() )),
            _ => panic!("invalid patch size")
        } }
    }

    #[inline]
    pub fn global_label(&mut self, name: &'static str) {
        if let Some(name) = self.global_labels.insert(name, self.ops.len()) {
            panic!("Duplicate global label '{}'", name);
        }
    }

    #[inline]
    pub fn global_reloc(&mut self, name: &'static str, size: u8) {
        self.global_relocs.push((PatchLoc(self.ops.len(), size), name));
    }

    #[inline]
    pub fn dynamic_label(&mut self, id: usize) {
        if let Some(id) = self.dynamic_labels.insert(id, self.ops.len()) {
            panic!("Duplicate label '{}'", id);
        }
    }

    #[inline]
    pub fn dynamic_reloc(&mut self, id: usize, size: u8) {
        self.dynamic_relocs.push((PatchLoc(self.ops.len(), size), id));
    }

    #[inline]
    pub fn local_label(&mut self, name: &'static str) {
        if let Some(relocs) = self.local_relocs.remove(&name) {
            for loc in relocs {
                let len = self.ops.len();
                Self::patch_loc(&mut self.ops, loc, len);
            }
        }
        self.local_labels.insert(name, self.ops.len());
    }

    #[inline]
    pub fn forward_reloc(&mut self, name: &'static str, size: u8) {
        match self.local_relocs.entry(name) {
            Occupied(mut o) => {
                o.get_mut().push(PatchLoc(self.ops.len(), size));
            },
            Vacant(v)   => {
                v.insert(vec![PatchLoc(self.ops.len(), size)]);
            }
        }
    }

    #[inline]
    pub fn backward_reloc(&mut self, name: &'static str, size: u8) {
        if let Some(&target) = self.local_labels.get(&name) {
            let len = self.ops.len();
            Self::patch_loc(&mut self.ops, PatchLoc(len, size), target)
        } else {
            panic!("Unknown local label '{}'", name);
        }
    }

    pub fn encode_relocs(&mut self) {
        for (loc, name) in self.global_relocs.drain(..) {
            if let Some(&target) = self.global_labels.get(&name) {
                Self::patch_loc(&mut self.ops, loc, target)
            } else {
                panic!("Unkonwn global label '{}'", name);
            }
        }

        for (loc, id) in self.dynamic_relocs.drain(..) {
            if let Some(&target) = self.dynamic_labels.get(&id) {
                Self::patch_loc(&mut self.ops, loc, target)
            } else {
                panic!("Unkonwn dynamic label '{}'", id);
            }
        }

        if let Some(name) = self.local_relocs.keys().next() {
            panic!("Unknown local label '{}'", name);
        }
    }
}

impl Deref for AssemblingBuffer {
    type Target = Vec<u8>;
    fn deref(&self) -> &Vec<u8> {
        &self.ops
    }
}
<|endoftext|>


<commit_before>//! FIXME: write short doc here

use std::{
    env,
    ffi::OsStr,
    ops,
    path::{Path, PathBuf},
    process::Command,
};

use anyhow::{Context, Result};
use cargo_metadata::{BuildScript, CargoOpt, Message, MetadataCommand, PackageId};
use ra_arena::{Arena, Idx};
use ra_db::Edition;
use rustc_hash::FxHashMap;

/// `CargoWorkspace` represents the logical structure of, well, a Cargo
/// workspace. It pretty closely mirrors `cargo metadata` output.
///
/// Note that internally, rust analyzer uses a different structure:
/// `CrateGraph`. `CrateGraph` is lower-level: it knows only about the crates,
/// while this knows about `Packages` & `Targets`: purely cargo-related
/// concepts.
#[derive(Debug, Clone)]
pub struct CargoWorkspace {
    packages: Arena<PackageData>,
    targets: Arena<TargetData>,
    workspace_root: PathBuf,
}

impl ops::Index<Package> for CargoWorkspace {
    type Output = PackageData;
    fn index(&self, index: Package) -> &PackageData {
        &self.packages[index]
    }
}

impl ops::Index<Target> for CargoWorkspace {
    type Output = TargetData;
    fn index(&self, index: Target) -> &TargetData {
        &self.targets[index]
    }
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct CargoConfig {
    /// Do not activate the `default` feature.
    pub no_default_features: bool,

    /// Activate all available features
    pub all_features: bool,

    /// List of features to activate.
    /// This will be ignored if `cargo_all_features` is true.
    pub features: Vec<String>,

    /// Runs cargo check on launch to figure out the correct values of OUT_DIR
    pub load_out_dirs_from_check: bool,
}

impl Default for CargoConfig {
    fn default() -> Self {
        CargoConfig {
            no_default_features: false,
            all_features: true,
            features: Vec::new(),
            load_out_dirs_from_check: false,
        }
    }
}

pub type Package = Idx<PackageData>;

pub type Target = Idx<TargetData>;

#[derive(Debug, Clone)]
pub struct PackageData {
    pub version: String,
    pub name: String,
    pub manifest: PathBuf,
    pub targets: Vec<Target>,
    pub is_member: bool,
    pub dependencies: Vec<PackageDependency>,
    pub edition: Edition,
    pub features: Vec<String>,
    pub out_dir: Option<PathBuf>,
    pub proc_macro_dylib_path: Option<PathBuf>,
}

#[derive(Debug, Clone)]
pub struct PackageDependency {
    pub pkg: Package,
    pub name: String,
}

#[derive(Debug, Clone)]
pub struct TargetData {
    pub package: Package,
    pub name: String,
    pub root: PathBuf,
    pub kind: TargetKind,
    pub is_proc_macro: bool,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TargetKind {
    Bin,
    /// Any kind of Cargo lib crate-type (dylib, rlib, proc-macro, ...).
    Lib,
    Example,
    Test,
    Bench,
    Other,
}

impl TargetKind {
    fn new(kinds: &[String]) -> TargetKind {
        for kind in kinds {
            return match kind.as_str() {
                "bin" => TargetKind::Bin,
                "test" => TargetKind::Test,
                "bench" => TargetKind::Bench,
                "example" => TargetKind::Example,
                "proc-macro" => TargetKind::Lib,
                _ if kind.contains("lib") => TargetKind::Lib,
                _ => continue,
            };
        }
        TargetKind::Other
    }
}

impl PackageData {
    pub fn root(&self) -> &Path {
        self.manifest.parent().unwrap()
    }
}

impl CargoWorkspace {
    pub fn from_cargo_metadata(
        cargo_toml: &Path,
        cargo_features: &CargoConfig,
    ) -> Result<CargoWorkspace> {
        let _ = Command::new(cargo_binary())
            .arg("--version")
            .status()
            .context("failed to run `cargo --version`, is `cargo` in PATH?")?;

        let mut meta = MetadataCommand::new();
        meta.manifest_path(cargo_toml);
        if cargo_features.all_features {
            meta.features(CargoOpt::AllFeatures);
        } else if cargo_features.no_default_features {
            // FIXME: `NoDefaultFeatures` is mutual exclusive with `SomeFeatures`
            // https://github.com/oli-obk/cargo_metadata/issues/79
            meta.features(CargoOpt::NoDefaultFeatures);
        } else if !cargo_features.features.is_empty() {
            meta.features(CargoOpt::SomeFeatures(cargo_features.features.clone()));
        }
        if let Some(parent) = cargo_toml.parent() {
            meta.current_dir(parent);
        }
        let meta = meta.exec().with_context(|| {
            format!("Failed to run `cargo metadata --manifest-path {}`", cargo_toml.display())
        })?;

        let mut out_dir_by_id = FxHashMap::default();
        let mut proc_macro_dylib_paths = FxHashMap::default();
        if cargo_features.load_out_dirs_from_check {
            let resources = load_extern_resources(cargo_toml, cargo_features)?;
            out_dir_by_id = resources.out_dirs;
            proc_macro_dylib_paths = resources.proc_dylib_paths;
        }

        let mut pkg_by_id = FxHashMap::default();
        let mut packages = Arena::default();
        let mut targets = Arena::default();

        let ws_members = &meta.workspace_members;

        for meta_pkg in meta.packages {
            let cargo_metadata::Package { id, edition, name, manifest_path, version, .. } =
                meta_pkg;
            let is_member = ws_members.contains(&id);
            let edition = edition
                .parse::<Edition>()
                .with_context(|| format!("Failed to parse edition {}", edition))?;
            let pkg = packages.alloc(PackageData {
                name,
                version: version.to_string(),
                manifest: manifest_path,
                targets: Vec::new(),
                is_member,
                edition,
                dependencies: Vec::new(),
                features: Vec::new(),
                out_dir: out_dir_by_id.get(&id).cloned(),
                proc_macro_dylib_path: proc_macro_dylib_paths.get(&id).cloned(),
            });
            let pkg_data = &mut packages[pkg];
            pkg_by_id.insert(id, pkg);
            for meta_tgt in meta_pkg.targets {
                let is_proc_macro = meta_tgt.kind.as_slice() == ["proc-macro"];
                let tgt = targets.alloc(TargetData {
                    package: pkg,
                    name: meta_tgt.name,
                    root: meta_tgt.src_path.clone(),
                    kind: TargetKind::new(meta_tgt.kind.as_slice()),
                    is_proc_macro,
                });
                pkg_data.targets.push(tgt);
            }
        }
        let resolve = meta.resolve.expect("metadata executed with deps");
        for node in resolve.nodes {
            let source = match pkg_by_id.get(&node.id) {
                Some(&src) => src,
                // FIXME: replace this and a similar branch below with `.unwrap`, once
                // https://github.com/rust-lang/cargo/issues/7841
                // is fixed and hits stable (around 1.43-is probably?).
                None => {
                    log::error!("Node id do not match in cargo metadata, ignoring {}", node.id);
                    continue;
                }
            };
            for dep_node in node.deps {
                let pkg = match pkg_by_id.get(&dep_node.pkg) {
                    Some(&pkg) => pkg,
                    None => {
                        log::error!(
                            "Dep node id do not match in cargo metadata, ignoring {}",
                            dep_node.pkg
                        );
                        continue;
                    }
                };
                let dep = PackageDependency { name: dep_node.name, pkg };
                packages[source].dependencies.push(dep);
            }
            packages[source].features.extend(node.features);
        }

        Ok(CargoWorkspace { packages, targets, workspace_root: meta.workspace_root })
    }

    pub fn packages<'a>(&'a self) -> impl Iterator<Item = Package> + ExactSizeIterator + 'a {
        self.packages.iter().map(|(id, _pkg)| id)
    }

    pub fn target_by_root(&self, root: &Path) -> Option<Target> {
        self.packages()
            .filter_map(|pkg| self[pkg].targets.iter().find(|&&it| self[it].root == root))
            .next()
            .copied()
    }

    pub fn workspace_root(&self) -> &Path {
        &self.workspace_root
    }

    pub fn package_flag(&self, package: &PackageData) -> String {
        if self.is_unique(&*package.name) {
            package.name.clone()
        } else {
            format!("{}:{}", package.name, package.version)
        }
    }

    fn is_unique(&self, name: &str) -> bool {
        self.packages.iter().filter(|(_, v)| v.name == name).count() == 1
    }
}

#[derive(Debug, Clone, Default)]
pub struct ExternResources {
    out_dirs: FxHashMap<PackageId, PathBuf>,
    proc_dylib_paths: FxHashMap<PackageId, PathBuf>,
}

pub fn load_extern_resources(
    cargo_toml: &Path,
    cargo_features: &CargoConfig,
) -> Result<ExternResources> {
    let mut cmd = Command::new(cargo_binary());
    cmd.args(&["check", "--message-format=json", "--manifest-path"]).arg(cargo_toml);
    if cargo_features.all_features {
        cmd.arg("--all-features");
    } else if cargo_features.no_default_features {
        // FIXME: `NoDefaultFeatures` is mutual exclusive with `SomeFeatures`
        // https://github.com/oli-obk/cargo_metadata/issues/79
        cmd.arg("--no-default-features");
    } else {
        cmd.args(&cargo_features.features);
    }

    let output = cmd.output()?;

    let mut res = ExternResources::default();

    for message in cargo_metadata::parse_messages(output.stdout.as_slice()) {
        if let Ok(message) = message {
            match message {
                Message::BuildScriptExecuted(BuildScript { package_id, out_dir, .. }) => {
                    res.out_dirs.insert(package_id, out_dir);
                }

                Message::CompilerArtifact(message) => {
                    if message.target.kind.contains(&"proc-macro".to_string()) {
                        let package_id = message.package_id;
                        // Skip rmeta file
                        if let Some(filename) = message.filenames.iter().find(|name| is_dylib(name))
                        {
                            res.proc_dylib_paths.insert(package_id, filename.clone());
                        }
                    }
                }
                Message::CompilerMessage(_) => (),
                Message::Unknown => (),
            }
        }
    }
    Ok(res)
}

// FIXME: File a better way to know if it is a dylib
fn is_dylib(path: &Path) -> bool {
    match path.extension().and_then(OsStr::to_str).map(|it| it.to_string().to_lowercase()) {
        None => false,
        Some(ext) => matches!(ext.as_str(), "dll" | "dylib" | "so"),
    }
}

fn cargo_binary() -> String {
    env::var("CARGO").unwrap_or_else(|_| "cargo".to_string())
}
<commit_msg>Don't print cargo version to stdout, breaking everything<commit_after>//! FIXME: write short doc here

use std::{
    env,
    ffi::OsStr,
    ops,
    path::{Path, PathBuf},
    process::Command,
};

use anyhow::{Context, Result};
use cargo_metadata::{BuildScript, CargoOpt, Message, MetadataCommand, PackageId};
use ra_arena::{Arena, Idx};
use ra_db::Edition;
use rustc_hash::FxHashMap;

/// `CargoWorkspace` represents the logical structure of, well, a Cargo
/// workspace. It pretty closely mirrors `cargo metadata` output.
///
/// Note that internally, rust analyzer uses a different structure:
/// `CrateGraph`. `CrateGraph` is lower-level: it knows only about the crates,
/// while this knows about `Packages` & `Targets`: purely cargo-related
/// concepts.
#[derive(Debug, Clone)]
pub struct CargoWorkspace {
    packages: Arena<PackageData>,
    targets: Arena<TargetData>,
    workspace_root: PathBuf,
}

impl ops::Index<Package> for CargoWorkspace {
    type Output = PackageData;
    fn index(&self, index: Package) -> &PackageData {
        &self.packages[index]
    }
}

impl ops::Index<Target> for CargoWorkspace {
    type Output = TargetData;
    fn index(&self, index: Target) -> &TargetData {
        &self.targets[index]
    }
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct CargoConfig {
    /// Do not activate the `default` feature.
    pub no_default_features: bool,

    /// Activate all available features
    pub all_features: bool,

    /// List of features to activate.
    /// This will be ignored if `cargo_all_features` is true.
    pub features: Vec<String>,

    /// Runs cargo check on launch to figure out the correct values of OUT_DIR
    pub load_out_dirs_from_check: bool,
}

impl Default for CargoConfig {
    fn default() -> Self {
        CargoConfig {
            no_default_features: false,
            all_features: true,
            features: Vec::new(),
            load_out_dirs_from_check: false,
        }
    }
}

pub type Package = Idx<PackageData>;

pub type Target = Idx<TargetData>;

#[derive(Debug, Clone)]
pub struct PackageData {
    pub version: String,
    pub name: String,
    pub manifest: PathBuf,
    pub targets: Vec<Target>,
    pub is_member: bool,
    pub dependencies: Vec<PackageDependency>,
    pub edition: Edition,
    pub features: Vec<String>,
    pub out_dir: Option<PathBuf>,
    pub proc_macro_dylib_path: Option<PathBuf>,
}

#[derive(Debug, Clone)]
pub struct PackageDependency {
    pub pkg: Package,
    pub name: String,
}

#[derive(Debug, Clone)]
pub struct TargetData {
    pub package: Package,
    pub name: String,
    pub root: PathBuf,
    pub kind: TargetKind,
    pub is_proc_macro: bool,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TargetKind {
    Bin,
    /// Any kind of Cargo lib crate-type (dylib, rlib, proc-macro, ...).
    Lib,
    Example,
    Test,
    Bench,
    Other,
}

impl TargetKind {
    fn new(kinds: &[String]) -> TargetKind {
        for kind in kinds {
            return match kind.as_str() {
                "bin" => TargetKind::Bin,
                "test" => TargetKind::Test,
                "bench" => TargetKind::Bench,
                "example" => TargetKind::Example,
                "proc-macro" => TargetKind::Lib,
                _ if kind.contains("lib") => TargetKind::Lib,
                _ => continue,
            };
        }
        TargetKind::Other
    }
}

impl PackageData {
    pub fn root(&self) -> &Path {
        self.manifest.parent().unwrap()
    }
}

impl CargoWorkspace {
    pub fn from_cargo_metadata(
        cargo_toml: &Path,
        cargo_features: &CargoConfig,
    ) -> Result<CargoWorkspace> {
        let _ = Command::new(cargo_binary())
            .arg("--version")
            .output()
            .context("failed to run `cargo --version`, is `cargo` in PATH?")?;

        let mut meta = MetadataCommand::new();
        meta.manifest_path(cargo_toml);
        if cargo_features.all_features {
            meta.features(CargoOpt::AllFeatures);
        } else if cargo_features.no_default_features {
            // FIXME: `NoDefaultFeatures` is mutual exclusive with `SomeFeatures`
            // https://github.com/oli-obk/cargo_metadata/issues/79
            meta.features(CargoOpt::NoDefaultFeatures);
        } else if !cargo_features.features.is_empty() {
            meta.features(CargoOpt::SomeFeatures(cargo_features.features.clone()));
        }
        if let Some(parent) = cargo_toml.parent() {
            meta.current_dir(parent);
        }
        let meta = meta.exec().with_context(|| {
            format!("Failed to run `cargo metadata --manifest-path {}`", cargo_toml.display())
        })?;

        let mut out_dir_by_id = FxHashMap::default();
        let mut proc_macro_dylib_paths = FxHashMap::default();
        if cargo_features.load_out_dirs_from_check {
            let resources = load_extern_resources(cargo_toml, cargo_features)?;
            out_dir_by_id = resources.out_dirs;
            proc_macro_dylib_paths = resources.proc_dylib_paths;
        }

        let mut pkg_by_id = FxHashMap::default();
        let mut packages = Arena::default();
        let mut targets = Arena::default();

        let ws_members = &meta.workspace_members;

        for meta_pkg in meta.packages {
            let cargo_metadata::Package { id, edition, name, manifest_path, version, .. } =
                meta_pkg;
            let is_member = ws_members.contains(&id);
            let edition = edition
                .parse::<Edition>()
                .with_context(|| format!("Failed to parse edition {}", edition))?;
            let pkg = packages.alloc(PackageData {
                name,
                version: version.to_string(),
                manifest: manifest_path,
                targets: Vec::new(),
                is_member,
                edition,
                dependencies: Vec::new(),
                features: Vec::new(),
                out_dir: out_dir_by_id.get(&id).cloned(),
                proc_macro_dylib_path: proc_macro_dylib_paths.get(&id).cloned(),
            });
            let pkg_data = &mut packages[pkg];
            pkg_by_id.insert(id, pkg);
            for meta_tgt in meta_pkg.targets {
                let is_proc_macro = meta_tgt.kind.as_slice() == ["proc-macro"];
                let tgt = targets.alloc(TargetData {
                    package: pkg,
                    name: meta_tgt.name,
                    root: meta_tgt.src_path.clone(),
                    kind: TargetKind::new(meta_tgt.kind.as_slice()),
                    is_proc_macro,
                });
                pkg_data.targets.push(tgt);
            }
        }
        let resolve = meta.resolve.expect("metadata executed with deps");
        for node in resolve.nodes {
            let source = match pkg_by_id.get(&node.id) {
                Some(&src) => src,
                // FIXME: replace this and a similar branch below with `.unwrap`, once
                // https://github.com/rust-lang/cargo/issues/7841
                // is fixed and hits stable (around 1.43-is probably?).
                None => {
                    log::error!("Node id do not match in cargo metadata, ignoring {}", node.id);
                    continue;
                }
            };
            for dep_node in node.deps {
                let pkg = match pkg_by_id.get(&dep_node.pkg) {
                    Some(&pkg) => pkg,
                    None => {
                        log::error!(
                            "Dep node id do not match in cargo metadata, ignoring {}",
                            dep_node.pkg
                        );
                        continue;
                    }
                };
                let dep = PackageDependency { name: dep_node.name, pkg };
                packages[source].dependencies.push(dep);
            }
            packages[source].features.extend(node.features);
        }

        Ok(CargoWorkspace { packages, targets, workspace_root: meta.workspace_root })
    }

    pub fn packages<'a>(&'a self) -> impl Iterator<Item = Package> + ExactSizeIterator + 'a {
        self.packages.iter().map(|(id, _pkg)| id)
    }

    pub fn target_by_root(&self, root: &Path) -> Option<Target> {
        self.packages()
            .filter_map(|pkg| self[pkg].targets.iter().find(|&&it| self[it].root == root))
            .next()
            .copied()
    }

    pub fn workspace_root(&self) -> &Path {
        &self.workspace_root
    }

    pub fn package_flag(&self, package: &PackageData) -> String {
        if self.is_unique(&*package.name) {
            package.name.clone()
        } else {
            format!("{}:{}", package.name, package.version)
        }
    }

    fn is_unique(&self, name: &str) -> bool {
        self.packages.iter().filter(|(_, v)| v.name == name).count() == 1
    }
}

#[derive(Debug, Clone, Default)]
pub struct ExternResources {
    out_dirs: FxHashMap<PackageId, PathBuf>,
    proc_dylib_paths: FxHashMap<PackageId, PathBuf>,
}

pub fn load_extern_resources(
    cargo_toml: &Path,
    cargo_features: &CargoConfig,
) -> Result<ExternResources> {
    let mut cmd = Command::new(cargo_binary());
    cmd.args(&["check", "--message-format=json", "--manifest-path"]).arg(cargo_toml);
    if cargo_features.all_features {
        cmd.arg("--all-features");
    } else if cargo_features.no_default_features {
        // FIXME: `NoDefaultFeatures` is mutual exclusive with `SomeFeatures`
        // https://github.com/oli-obk/cargo_metadata/issues/79
        cmd.arg("--no-default-features");
    } else {
        cmd.args(&cargo_features.features);
    }

    let output = cmd.output()?;

    let mut res = ExternResources::default();

    for message in cargo_metadata::parse_messages(output.stdout.as_slice()) {
        if let Ok(message) = message {
            match message {
                Message::BuildScriptExecuted(BuildScript { package_id, out_dir, .. }) => {
                    res.out_dirs.insert(package_id, out_dir);
                }

                Message::CompilerArtifact(message) => {
                    if message.target.kind.contains(&"proc-macro".to_string()) {
                        let package_id = message.package_id;
                        // Skip rmeta file
                        if let Some(filename) = message.filenames.iter().find(|name| is_dylib(name))
                        {
                            res.proc_dylib_paths.insert(package_id, filename.clone());
                        }
                    }
                }
                Message::CompilerMessage(_) => (),
                Message::Unknown => (),
            }
        }
    }
    Ok(res)
}

// FIXME: File a better way to know if it is a dylib
fn is_dylib(path: &Path) -> bool {
    match path.extension().and_then(OsStr::to_str).map(|it| it.to_string().to_lowercase()) {
        None => false,
        Some(ext) => matches!(ext.as_str(), "dll" | "dylib" | "so"),
    }
}

fn cargo_binary() -> String {
    env::var("CARGO").unwrap_or_else(|_| "cargo".to_string())
}
<|endoftext|>


<commit_before>use std::convert::TryInto;

use crate::{
    cache::{DefinitionInfoId, DefinitionKind},
    parser::ast,
    types::pattern::{Case, DecisionTree, VariantTag},
    util::fmap,
};

use super::monomorphisation::{Context, Definition};
use crate::hir;

impl<'c> Context<'c> {
    pub fn monomorphise_match(&mut self, match_: &ast::Match<'c>) -> hir::Ast {
        let match_prelude = self.store_initial_value(match_);
        let decision_tree = self.monomorphise_tree(match_.decision_tree.as_ref().unwrap());
        let branches = fmap(&match_.branches, |branch| self.monomorphise(&branch.1));
        let result_type = self.convert_type(match_.typ.as_ref().unwrap());

        hir::Ast::Sequence(hir::Sequence {
            statements: vec![match_prelude, hir::Ast::Match(hir::Match { branches, decision_tree, result_type })],
        })
    }

    /// Compile the expression to match on and store it in the DefinitionInfoId expected
    /// by the first Case of the DecisionTree
    fn store_initial_value(&mut self, match_: &ast::Match<'c>) -> hir::Ast {
        let value = self.monomorphise(match_.expression.as_ref());

        if let Some(DecisionTree::Switch(id, _)) = &match_.decision_tree {
            let (def, new_id) = self.fresh_definition(value);
            let typ = self.follow_all_bindings(self.cache[*id].typ.as_ref().unwrap().as_monotype());
            self.definitions.insert((*id, typ), new_id.into());
            def
        } else {
            value
        }
    }

    fn monomorphise_tree(&mut self, tree: &DecisionTree) -> hir::DecisionTree {
        match tree {
            DecisionTree::Leaf(index) => hir::DecisionTree::Leaf(*index),
            DecisionTree::Switch(id_to_match_on, cases) => self.monomorphise_switch(*id_to_match_on, cases),
            DecisionTree::Fail => {
                unreachable!("Patterns should be verified to be complete before monomorphisation")
            },
        }
    }

    fn monomorphise_switch(&mut self, id_to_match_on: DefinitionInfoId, cases: &[Case]) -> hir::DecisionTree {
        let typ = self.cache[id_to_match_on].typ.as_ref().unwrap().as_monotype();

        let value = match self.lookup_definition(id_to_match_on, typ) {
            Some(Definition::Normal(variable)) => variable,
            _ => unreachable!(),
        };

        if cases.len() == 1 {
            // If there's only 1 case we must be destructuring a struct, no need to check a tag
            self.monomorphise_case_no_tag_value(&cases[0], value.definition_id)
        } else {
            let (cases, match_all_case) = self.split_cases(cases);

            let typ = typ.clone();
            let monomorphised_type = self.convert_type(&typ);

            let cases = fmap(cases, |case| self.monomorphise_case(case, value.clone()));
            let else_case =
                match_all_case.map(|case| Box::new(self.monomorphise_case_no_tag_value(case, value.definition_id)));

            let tag = self.extract_tag(value, &monomorphised_type);
            hir::DecisionTree::Switch { int_to_switch_on: Box::new(tag), cases, else_case }
        }
    }

    fn monomorphise_case(&mut self, case: &Case, match_value: hir::DefinitionInfo) -> (u32, hir::DecisionTree) {
        let tree = if case.fields.is_empty() {
            self.monomorphise_tree(&case.branch)
        } else {
            // variable = value = reinterpret match_value as variant_type
            let value = self.cast_to_variant_type(match_value, case);
            let variable = self.next_unique_id();
            let field_bindings = self.bind_patterns(variable, case);

            let mut tree = self.monomorphise_tree(&case.branch);

            for definition in field_bindings.into_iter().rev() {
                tree = hir::DecisionTree::Definition(definition, Box::new(tree));
            }

            let expr = Box::new(value);
            let cast_definition = hir::Definition { variable, expr };

            hir::DecisionTree::Definition(cast_definition, Box::new(tree))
        };

        let expected_tag_value = self.get_tag_value(case);
        (expected_tag_value as u32, tree)
    }

    fn monomorphise_case_no_tag_value(&mut self, case: &Case, match_value: hir::DefinitionId) -> hir::DecisionTree {
        let field_bindings = self.bind_patterns(match_value, case);

        let mut tree = self.monomorphise_tree(&case.branch);

        for definition in field_bindings.into_iter().rev() {
            tree = hir::DecisionTree::Definition(definition, Box::new(tree));
        }

        tree
    }

    fn extract_tag(&mut self, value: hir::DefinitionInfo, typ: &hir::Type) -> hir::Ast {
        use hir::types::*;
        match typ {
            Type::Primitive(PrimitiveType::Integer(_)) => value.into(),
            Type::Tuple(_) => self.extract(value.into(), 0),
            _ => unreachable!(),
        }
    }

    /// Groups the given cases into an optional match-all case and a list of the other cases.
    fn split_cases<'a>(&self, cases: &'a [Case]) -> (&'a [Case], Option<&'a Case>) {
        let last = cases.last().unwrap();
        if last.tag == None {
            (&cases[0..cases.len() - 1], Some(last))
        } else {
            (cases, None)
        }
    }

    fn get_tag_value(&self, case: &Case) -> u8 {
        match case.tag.as_ref().unwrap() {
            VariantTag::True => 1,
            VariantTag::False => 0,
            VariantTag::Unit => 0,
            VariantTag::Literal(literal) => match literal {
                ast::LiteralKind::Integer(x, _) => (*x).try_into().unwrap(), // TODO: larger tags
                ast::LiteralKind::Float(_) => todo!(),
                ast::LiteralKind::String(_) => todo!(),
                ast::LiteralKind::Char(x) => (*x).try_into().unwrap(),
                ast::LiteralKind::Bool(_) => unreachable!(),
                ast::LiteralKind::Unit => unreachable!(),
            },
            VariantTag::UserDefined(id) => {
                match &self.cache[*id].definition {
                    Some(DefinitionKind::TypeConstructor { tag: Some(tag), .. }) => *tag,
                    _ => dbg!(0), //unreachable!(),
                }
            },
        }
    }

    fn bind_patterns(&mut self, variant: hir::DefinitionId, case: &Case) -> Vec<hir::Definition> {
        match &case.tag {
            Some(VariantTag::UserDefined(id)) => {
                let info_type = self.cache.definition_infos[id.0].typ.as_ref().unwrap();
                // Skip the tag value for unions when extracting fields
                let start_index = if info_type.is_union_constructor(&self.cache) { 1 } else { 0 };

                let info_type = info_type.clone();

                // Note: should not use function_type for any bindings, it is from a generalized
                // info_type that makes it only useful for checking if it is a function or not.
                let function_type = self.convert_type(info_type.remove_forall()).into_function();

                if function_type.is_some() {
                    fmap(case.fields.iter().enumerate(), |(i, field_aliases)| {
                        let field_index = start_index + i as u32;
                        let variant_variable: hir::Variable = variant.into();
                        let field_variable = self.next_unique_id();

                        for field_alias in field_aliases {
                            let alias_type = self.cache[*field_alias].typ.as_ref().unwrap().as_monotype();
                            let field_type = self.follow_all_bindings(alias_type);
                            self.definitions.insert((*field_alias, field_type), field_variable.into());
                        }

                        hir::Definition {
                            variable: field_variable,
                            expr: Box::new(self.extract(variant_variable.into(), field_index)),
                        }
                    })
                } else {
                    vec![]
                }
            },
            None => {
                assert!(case.fields.len() <= 1);
                for field_aliases in &case.fields {
                    for field_alias in field_aliases {
                        let alias_type = self.cache[*field_alias].typ.as_ref().unwrap().as_monotype();
                        let field_type = self.follow_all_bindings(alias_type);
                        self.definitions.insert((*field_alias, field_type), variant.into());
                    }
                }
                // We've aliased everything this pattern was bound to and did not
                // need to create any new Extract instructions to do so, so there is
                // no need to return any new definitions to insert.
                vec![]
            },
            Some(VariantTag::True | VariantTag::False | VariantTag::Unit | VariantTag::Literal(_)) => vec![], // No fields to bind
        }
    }

    fn cast_to_variant_type(&mut self, value: hir::DefinitionInfo, case: &Case) -> hir::Ast {
        let value = value.into();
        match &case.tag {
            Some(VariantTag::UserDefined(id)) => {
                let mut elems = Vec::with_capacity(case.fields.len() + 1);

                let constructor = self.follow_all_bindings(self.cache[*id].typ.as_ref().unwrap().remove_forall());
                if constructor.is_union_constructor(&self.cache) {
                    elems.push(Self::tag_type());
                }

                for field_aliases in &case.fields {
                    let typ = self.cache[field_aliases[0]].typ.as_ref().unwrap().clone().into_monotype();
                    elems.push(self.convert_type(&typ));
                }

                // TODO: Add padding to cast to smaller type in case some backends need it
                hir::Ast::ReinterpretCast(hir::ReinterpretCast {
                    lhs: Box::new(value),
                    target_type: hir::Type::Tuple(elems),
                })
            },
            _ => value,
        }
    }
}
<commit_msg>Fix cast char to u8 (#97)<commit_after>use std::convert::TryInto;

use crate::{
    cache::{DefinitionInfoId, DefinitionKind},
    parser::ast,
    types::pattern::{Case, DecisionTree, VariantTag},
    util::fmap,
};

use super::monomorphisation::{Context, Definition};
use crate::hir;

impl<'c> Context<'c> {
    pub fn monomorphise_match(&mut self, match_: &ast::Match<'c>) -> hir::Ast {
        let match_prelude = self.store_initial_value(match_);
        let decision_tree = self.monomorphise_tree(match_.decision_tree.as_ref().unwrap());
        let branches = fmap(&match_.branches, |branch| self.monomorphise(&branch.1));
        let result_type = self.convert_type(match_.typ.as_ref().unwrap());

        hir::Ast::Sequence(hir::Sequence {
            statements: vec![match_prelude, hir::Ast::Match(hir::Match { branches, decision_tree, result_type })],
        })
    }

    /// Compile the expression to match on and store it in the DefinitionInfoId expected
    /// by the first Case of the DecisionTree
    fn store_initial_value(&mut self, match_: &ast::Match<'c>) -> hir::Ast {
        let value = self.monomorphise(match_.expression.as_ref());

        if let Some(DecisionTree::Switch(id, _)) = &match_.decision_tree {
            let (def, new_id) = self.fresh_definition(value);
            let typ = self.follow_all_bindings(self.cache[*id].typ.as_ref().unwrap().as_monotype());
            self.definitions.insert((*id, typ), new_id.into());
            def
        } else {
            value
        }
    }

    fn monomorphise_tree(&mut self, tree: &DecisionTree) -> hir::DecisionTree {
        match tree {
            DecisionTree::Leaf(index) => hir::DecisionTree::Leaf(*index),
            DecisionTree::Switch(id_to_match_on, cases) => self.monomorphise_switch(*id_to_match_on, cases),
            DecisionTree::Fail => {
                unreachable!("Patterns should be verified to be complete before monomorphisation")
            },
        }
    }

    fn monomorphise_switch(&mut self, id_to_match_on: DefinitionInfoId, cases: &[Case]) -> hir::DecisionTree {
        let typ = self.cache[id_to_match_on].typ.as_ref().unwrap().as_monotype();

        let value = match self.lookup_definition(id_to_match_on, typ) {
            Some(Definition::Normal(variable)) => variable,
            _ => unreachable!(),
        };

        if cases.len() == 1 {
            // If there's only 1 case we must be destructuring a struct, no need to check a tag
            self.monomorphise_case_no_tag_value(&cases[0], value.definition_id)
        } else {
            let (cases, match_all_case) = self.split_cases(cases);

            let typ = typ.clone();
            let monomorphised_type = self.convert_type(&typ);

            let cases = fmap(cases, |case| self.monomorphise_case(case, value.clone()));
            let else_case =
                match_all_case.map(|case| Box::new(self.monomorphise_case_no_tag_value(case, value.definition_id)));

            let tag = self.extract_tag(value, &monomorphised_type);
            hir::DecisionTree::Switch { int_to_switch_on: Box::new(tag), cases, else_case }
        }
    }

    fn monomorphise_case(&mut self, case: &Case, match_value: hir::DefinitionInfo) -> (u32, hir::DecisionTree) {
        let tree = if case.fields.is_empty() {
            self.monomorphise_tree(&case.branch)
        } else {
            // variable = value = reinterpret match_value as variant_type
            let value = self.cast_to_variant_type(match_value, case);
            let variable = self.next_unique_id();
            let field_bindings = self.bind_patterns(variable, case);

            let mut tree = self.monomorphise_tree(&case.branch);

            for definition in field_bindings.into_iter().rev() {
                tree = hir::DecisionTree::Definition(definition, Box::new(tree));
            }

            let expr = Box::new(value);
            let cast_definition = hir::Definition { variable, expr };

            hir::DecisionTree::Definition(cast_definition, Box::new(tree))
        };

        let expected_tag_value = self.get_tag_value(case);
        (expected_tag_value as u32, tree)
    }

    fn monomorphise_case_no_tag_value(&mut self, case: &Case, match_value: hir::DefinitionId) -> hir::DecisionTree {
        let field_bindings = self.bind_patterns(match_value, case);

        let mut tree = self.monomorphise_tree(&case.branch);

        for definition in field_bindings.into_iter().rev() {
            tree = hir::DecisionTree::Definition(definition, Box::new(tree));
        }

        tree
    }

    fn extract_tag(&mut self, value: hir::DefinitionInfo, typ: &hir::Type) -> hir::Ast {
        use hir::types::*;
        match typ {
            Type::Primitive(PrimitiveType::Integer(_)) => value.into(),
            Type::Tuple(_) => self.extract(value.into(), 0),
            _ => unreachable!(),
        }
    }

    /// Groups the given cases into an optional match-all case and a list of the other cases.
    fn split_cases<'a>(&self, cases: &'a [Case]) -> (&'a [Case], Option<&'a Case>) {
        let last = cases.last().unwrap();
        if last.tag == None {
            (&cases[0..cases.len() - 1], Some(last))
        } else {
            (cases, None)
        }
    }

    fn get_tag_value(&self, case: &Case) -> u8 {
        match case.tag.as_ref().unwrap() {
            VariantTag::True => 1,
            VariantTag::False => 0,
            VariantTag::Unit => 0,
            VariantTag::Literal(literal) => match literal {
                ast::LiteralKind::Integer(x, _) => (*x).try_into().unwrap(), // TODO: larger tags
                ast::LiteralKind::Float(_) => todo!(),
                ast::LiteralKind::String(_) => todo!(),
                ast::LiteralKind::Char(x) => {
                    let codepoint: u32 = (*x).into();
                    codepoint.try_into().unwrap()
                },
                ast::LiteralKind::Bool(_) => unreachable!(),
                ast::LiteralKind::Unit => unreachable!(),
            },
            VariantTag::UserDefined(id) => {
                match &self.cache[*id].definition {
                    Some(DefinitionKind::TypeConstructor { tag: Some(tag), .. }) => *tag,
                    _ => dbg!(0), //unreachable!(),
                }
            },
        }
    }

    fn bind_patterns(&mut self, variant: hir::DefinitionId, case: &Case) -> Vec<hir::Definition> {
        match &case.tag {
            Some(VariantTag::UserDefined(id)) => {
                let info_type = self.cache.definition_infos[id.0].typ.as_ref().unwrap();
                // Skip the tag value for unions when extracting fields
                let start_index = if info_type.is_union_constructor(&self.cache) { 1 } else { 0 };

                let info_type = info_type.clone();

                // Note: should not use function_type for any bindings, it is from a generalized
                // info_type that makes it only useful for checking if it is a function or not.
                let function_type = self.convert_type(info_type.remove_forall()).into_function();

                if function_type.is_some() {
                    fmap(case.fields.iter().enumerate(), |(i, field_aliases)| {
                        let field_index = start_index + i as u32;
                        let variant_variable: hir::Variable = variant.into();
                        let field_variable = self.next_unique_id();

                        for field_alias in field_aliases {
                            let alias_type = self.cache[*field_alias].typ.as_ref().unwrap().as_monotype();
                            let field_type = self.follow_all_bindings(alias_type);
                            self.definitions.insert((*field_alias, field_type), field_variable.into());
                        }

                        hir::Definition {
                            variable: field_variable,
                            expr: Box::new(self.extract(variant_variable.into(), field_index)),
                        }
                    })
                } else {
                    vec![]
                }
            },
            None => {
                assert!(case.fields.len() <= 1);
                for field_aliases in &case.fields {
                    for field_alias in field_aliases {
                        let alias_type = self.cache[*field_alias].typ.as_ref().unwrap().as_monotype();
                        let field_type = self.follow_all_bindings(alias_type);
                        self.definitions.insert((*field_alias, field_type), variant.into());
                    }
                }
                // We've aliased everything this pattern was bound to and did not
                // need to create any new Extract instructions to do so, so there is
                // no need to return any new definitions to insert.
                vec![]
            },
            Some(VariantTag::True | VariantTag::False | VariantTag::Unit | VariantTag::Literal(_)) => vec![], // No fields to bind
        }
    }

    fn cast_to_variant_type(&mut self, value: hir::DefinitionInfo, case: &Case) -> hir::Ast {
        let value = value.into();
        match &case.tag {
            Some(VariantTag::UserDefined(id)) => {
                let mut elems = Vec::with_capacity(case.fields.len() + 1);

                let constructor = self.follow_all_bindings(self.cache[*id].typ.as_ref().unwrap().remove_forall());
                if constructor.is_union_constructor(&self.cache) {
                    elems.push(Self::tag_type());
                }

                for field_aliases in &case.fields {
                    let typ = self.cache[field_aliases[0]].typ.as_ref().unwrap().clone().into_monotype();
                    elems.push(self.convert_type(&typ));
                }

                // TODO: Add padding to cast to smaller type in case some backends need it
                hir::Ast::ReinterpretCast(hir::ReinterpretCast {
                    lhs: Box::new(value),
                    target_type: hir::Type::Tuple(elems),
                })
            },
            _ => value,
        }
    }
}
<|endoftext|>


<commit_before>use std::cmp::Ordering;
use std::fmt;
use std::rc::Rc;
use std::hash::Hash;

use integeriser::{HashIntegeriser, Integeriser};
use util::integerisable::Integerisable1;
use util::tree::GornTree;

/// upside-down tree with a designated position (the *stack pointer*) and *nodes* of type `A`.
#[derive(Clone, Debug)]
pub struct TreeStack<A> {
    parent: Option<(usize, Rc<TreeStack<A>>)>,
    value: A,
    children: Vec<Option<Rc<TreeStack<A>>>>,
}

impl<A> TreeStack<A> {
    /// Creates a new `TreeStack<A>` with root label `a`.
    pub fn new(a: A) -> Self {
        TreeStack { value: a, children: Vec::new(), parent: None }
    }

    /// Applies a function `FnMut(&A) -> B` to every node in a `TreeStack<A>`.
    pub fn map<F, B>(&self, f: &mut F) -> TreeStack<B>
        where F: FnMut(&A) -> B,
    {
        let new_value = f(&self.value);
        let new_parent = match self.parent {
            Some((i, ref p)) => Some((i, Rc::new(p.map(f)))),
            None => None,
        };
        let new_children = self.children.iter().map(|o| o.clone().map(|v| Rc::new(v.map(f)))).collect();
        TreeStack { parent: new_parent, value: new_value, children: new_children }
    }

    /// Returns `True` if the stack pointer points to the bottom node.
    pub fn is_at_bottom(&self) -> bool {
        self.parent.is_none()
    }

    /// Returns a reference to label of the current node.
    pub fn current_symbol(&self) -> &A {
        &self.value
    }

    /// Replaces the current value by the given value.
    pub fn set(mut self, a: A) -> Self {
        self.value = a;
        self
    }

    /// Writes a value to the specified child position (if the child position is vacant) and returns the resulting `TreeStack` in an `Ok`.
    /// Returns the unmodified `TreeStack` in an `Err` otherwise.
    pub fn push(mut self, n: usize, a: A) -> Result<Self, Self> {
        if n >= self.children.len() {
            let len = n - self.children.len() + 1;
            let filler = &mut vec![None; len];
            self.children.append(filler);
        }

        if self.children[n].is_none() {
            Ok(TreeStack { value: a,
                           children: Vec::new(),
                           parent: Some((n, Rc::new(self))) })
        } else {
            Err(self)
        }
    }
}

impl<A: Clone + fmt::Display> fmt::Display for TreeStack<A> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let (tree, pointer) = self.to_tree();

        for (path, value) in tree.iter() {
            let mut line1 = String::from(" ");
            let mut line2 = String::from(if path.eq(&pointer) {
                "*"
            } else {
                " "
            });

            match path.last() {
                Some(child_num) => {
                    for _ in 0..path.len() - 1 {
                        line1.push_str("| ");
                        line2.push_str("| ");
                    }

                    line1.push_str("|");
                    line2.push_str(&format!("+-{}: {}", child_num, value));
                },
                None => line2.push_str(&value.to_string()),
            }

            write!(f, "{}\n{}\n", line1, line2)?
        }

        Ok(())
    }
}

impl<A: Clone> TreeStack<A> {
    /// Goes up to a specific child position (if this position is occupied) and returns the resulting `TreeStack` in an `Ok`.
    /// Returns the unmodified `TreeStack` in an `Err` otherwise.
    pub fn up(mut self, n: usize) -> Result<Self, Self> {
        match {
            if self.children.len() > n {
                self.children.push(None);
                self.children.swap_remove(n)
            } else {
                None
            }
        } {
            Some(ref tn) => Ok(TreeStack { value: tn.value.clone(),
                                           children: tn.children.clone(),
                                           parent: Some((n, Rc::new(self))) }),
            _ => Err(self),
        }
    }

    /// Goes down to the parent position (if there is one) and returns the resulting `TreeStack` in an `Ok`.
    /// Returns the unmodified `TreeStack` in an `Err` otherwise.
    pub fn down(mut self) -> Result<Self, Self> {
        match self.parent.take() {
            Some((n, pn)) => {
                let mut new_pch = pn.children.clone();
                new_pch[n] = Some(Rc::new(self));
                Ok(TreeStack { value: pn.value.clone(),
                               children: new_pch,
                               parent: pn.parent.clone() })
            },
            None => Err(self),
        }
    }

    pub fn to_tree(&self) -> (GornTree<A>, Vec<usize>) {
        let mut tree_map = GornTree::new();
        let mut curr_path = Vec::new();

        if let Some((num, ref parent)) = self.parent {
            let (parent_map, parent_path) = parent.to_tree();
            curr_path = parent_path;
            curr_path.push(num);

            for (path, value) in parent_map {
                tree_map.insert(path, value);
            }
        }

        tree_map.insert(curr_path.clone(), self.value.clone());

        for (num, maybe_child) in self.children.iter().enumerate() {
            if let &Some(ref child) = maybe_child {
                let (mut child_map, _) = child.to_tree();

                for (path, value) in child_map {
                    let mut new_path = curr_path.clone();
                    new_path.push(num);
                    new_path.append(&mut path.clone());
                    tree_map.insert(new_path, value);
                }
            }
        }

        (tree_map, curr_path)
    }
}

impl<A: Clone + Eq + Hash> Integerisable1 for TreeStack<A> {
    type AInt = TreeStack<usize>;
    type I = HashIntegeriser<A>;

    fn integerise(&self, integeriser: &mut Self::I) -> Self::AInt {
        self.map(&mut move |v| integeriser.integerise(v.clone()))
    }

    fn un_integerise(aint: &Self::AInt, integeriser: &Self::I) -> Self {
        aint.map(&mut |&v| integeriser.find_value(v).unwrap().clone())
    }
}

impl<A: PartialEq> PartialEq for TreeStack<A> {
    fn eq(&self, other: &Self) -> bool {
        let comp = |p1, p2| Rc::ptr_eq(p1, p2) || p1 == p2;
        self.value == other.value
            && match (&self.parent, &other.parent) {
                (&Some((i1, ref p1)), &Some((i2, ref p2))) => i1 == i2 && comp(p1, p2),
                (&None, &None)                             => true,
                _                                          => false,
            }
            && self.children == other.children
    }
}

impl<A: Eq> Eq for TreeStack<A> {}

impl<A: PartialOrd> PartialOrd for TreeStack<A> {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        match self.value.partial_cmp(&other.value) {
            None | Some(Ordering::Equal) =>
                match self.parent.partial_cmp(&other.parent) {
                    None | Some(Ordering::Equal) => self.children.partial_cmp(&other.children),
                    x => x,
                }
            x => x,
        }
    }
}

impl<A: Ord> Ord for TreeStack<A> {
    fn cmp(&self, other: &Self) -> Ordering {
        match self.value.cmp(&other.value) {
            Ordering::Equal =>
                match self.parent.cmp(&other.parent) {
                    Ordering::Equal => self.children.cmp(&other.children),
                    x => x,
                }
            x => x,
        }
    }
}

#[cfg(test)]
pub mod tests {
    use super::*;

    #[test]
    fn test_tree_stack() {
        let mut ts: TreeStack<u8> = TreeStack::new(0);
        assert_eq!(&0, ts.current_symbol());

        ts = ts.push(1, 1).unwrap();
        assert_eq!(&1, ts.current_symbol());

        ts = ts.down().unwrap();
        assert_eq!(&0, ts.current_symbol());

        ts = ts.push(2, 2).unwrap();
        assert_eq!(&2, ts.current_symbol());

        ts = ts.down().unwrap();
        ts = ts.up(1).unwrap();
        assert_eq!(&1, ts.current_symbol());

        ts = ts.push(1, 11).unwrap();
        assert_eq!(&11, ts.current_symbol());

        ts = ts.down().unwrap();
        ts = ts.down().unwrap();
        ts = ts.up(2).unwrap();
        ts = ts.push(1, 21).unwrap();
        assert_eq!(&21, ts.current_symbol());

        ts = ts.down().unwrap();
        ts = ts.down().unwrap();
        assert_eq!(&0, ts.current_symbol());
    }
}
<commit_msg>src/tree_stack_automaton/tree_stack.rs: added test_to_tree()<commit_after>use std::cmp::Ordering;
use std::fmt;
use std::rc::Rc;
use std::hash::Hash;

use integeriser::{HashIntegeriser, Integeriser};
use util::integerisable::Integerisable1;
use util::tree::GornTree;

/// upside-down tree with a designated position (the *stack pointer*) and *nodes* of type `A`.
#[derive(Clone, Debug)]
pub struct TreeStack<A> {
    parent: Option<(usize, Rc<TreeStack<A>>)>,
    value: A,
    children: Vec<Option<Rc<TreeStack<A>>>>,
}

impl<A> TreeStack<A> {
    /// Creates a new `TreeStack<A>` with root label `a`.
    pub fn new(a: A) -> Self {
        TreeStack { value: a, children: Vec::new(), parent: None }
    }

    /// Applies a function `FnMut(&A) -> B` to every node in a `TreeStack<A>`.
    pub fn map<F, B>(&self, f: &mut F) -> TreeStack<B>
        where F: FnMut(&A) -> B,
    {
        let new_value = f(&self.value);
        let new_parent = match self.parent {
            Some((i, ref p)) => Some((i, Rc::new(p.map(f)))),
            None => None,
        };
        let new_children = self.children.iter().map(|o| o.clone().map(|v| Rc::new(v.map(f)))).collect();
        TreeStack { parent: new_parent, value: new_value, children: new_children }
    }

    /// Returns `True` if the stack pointer points to the bottom node.
    pub fn is_at_bottom(&self) -> bool {
        self.parent.is_none()
    }

    /// Returns a reference to label of the current node.
    pub fn current_symbol(&self) -> &A {
        &self.value
    }

    /// Replaces the current value by the given value.
    pub fn set(mut self, a: A) -> Self {
        self.value = a;
        self
    }

    /// Writes a value to the specified child position (if the child position is vacant) and returns the resulting `TreeStack` in an `Ok`.
    /// Returns the unmodified `TreeStack` in an `Err` otherwise.
    pub fn push(mut self, n: usize, a: A) -> Result<Self, Self> {
        if n >= self.children.len() {
            let len = n - self.children.len() + 1;
            let filler = &mut vec![None; len];
            self.children.append(filler);
        }

        if self.children[n].is_none() {
            Ok(TreeStack { value: a,
                           children: Vec::new(),
                           parent: Some((n, Rc::new(self))) })
        } else {
            Err(self)
        }
    }
}

impl<A: Clone + fmt::Display> fmt::Display for TreeStack<A> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let (tree, pointer) = self.to_tree();

        for (path, value) in tree.iter() {
            let mut line1 = String::from(" ");
            let mut line2 = String::from(if path.eq(&pointer) {
                "*"
            } else {
                " "
            });

            match path.last() {
                Some(child_num) => {
                    for _ in 0..path.len() - 1 {
                        line1.push_str("| ");
                        line2.push_str("| ");
                    }

                    line1.push_str("|");
                    line2.push_str(&format!("+-{}: {}", child_num, value));
                },
                None => line2.push_str(&value.to_string()),
            }

            write!(f, "{}\n{}\n", line1, line2)?
        }

        Ok(())
    }
}

impl<A: Clone> TreeStack<A> {
    /// Goes up to a specific child position (if this position is occupied) and returns the resulting `TreeStack` in an `Ok`.
    /// Returns the unmodified `TreeStack` in an `Err` otherwise.
    pub fn up(mut self, n: usize) -> Result<Self, Self> {
        match {
            if self.children.len() > n {
                self.children.push(None);
                self.children.swap_remove(n)
            } else {
                None
            }
        } {
            Some(ref tn) => Ok(TreeStack { value: tn.value.clone(),
                                           children: tn.children.clone(),
                                           parent: Some((n, Rc::new(self))) }),
            _ => Err(self),
        }
    }

    /// Goes down to the parent position (if there is one) and returns the resulting `TreeStack` in an `Ok`.
    /// Returns the unmodified `TreeStack` in an `Err` otherwise.
    pub fn down(mut self) -> Result<Self, Self> {
        match self.parent.take() {
            Some((n, pn)) => {
                let mut new_pch = pn.children.clone();
                new_pch[n] = Some(Rc::new(self));
                Ok(TreeStack { value: pn.value.clone(),
                               children: new_pch,
                               parent: pn.parent.clone() })
            },
            None => Err(self),
        }
    }

    pub fn to_tree(&self) -> (GornTree<A>, Vec<usize>) {
        let mut tree_map = GornTree::new();
        let mut curr_path = Vec::new();

        if let Some((num, ref parent)) = self.parent {
            let (parent_map, parent_path) = parent.to_tree();
            curr_path = parent_path;
            curr_path.push(num);

            for (path, value) in parent_map {
                tree_map.insert(path, value);
            }
        }

        tree_map.insert(curr_path.clone(), self.value.clone());

        for (num, maybe_child) in self.children.iter().enumerate() {
            if let &Some(ref child) = maybe_child {
                let (mut child_map, _) = child.to_tree();

                for (path, value) in child_map {
                    let mut new_path = curr_path.clone();
                    new_path.push(num);
                    new_path.append(&mut path.clone());
                    tree_map.insert(new_path, value);
                }
            }
        }

        (tree_map, curr_path)
    }
}

impl<A: Clone + Eq + Hash> Integerisable1 for TreeStack<A> {
    type AInt = TreeStack<usize>;
    type I = HashIntegeriser<A>;

    fn integerise(&self, integeriser: &mut Self::I) -> Self::AInt {
        self.map(&mut move |v| integeriser.integerise(v.clone()))
    }

    fn un_integerise(aint: &Self::AInt, integeriser: &Self::I) -> Self {
        aint.map(&mut |&v| integeriser.find_value(v).unwrap().clone())
    }
}

impl<A: PartialEq> PartialEq for TreeStack<A> {
    fn eq(&self, other: &Self) -> bool {
        let comp = |p1, p2| Rc::ptr_eq(p1, p2) || p1 == p2;
        self.value == other.value
            && match (&self.parent, &other.parent) {
                (&Some((i1, ref p1)), &Some((i2, ref p2))) => i1 == i2 && comp(p1, p2),
                (&None, &None)                             => true,
                _                                          => false,
            }
            && self.children == other.children
    }
}

impl<A: Eq> Eq for TreeStack<A> {}

impl<A: PartialOrd> PartialOrd for TreeStack<A> {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        match self.value.partial_cmp(&other.value) {
            None | Some(Ordering::Equal) =>
                match self.parent.partial_cmp(&other.parent) {
                    None | Some(Ordering::Equal) => self.children.partial_cmp(&other.children),
                    x => x,
                }
            x => x,
        }
    }
}

impl<A: Ord> Ord for TreeStack<A> {
    fn cmp(&self, other: &Self) -> Ordering {
        match self.value.cmp(&other.value) {
            Ordering::Equal =>
                match self.parent.cmp(&other.parent) {
                    Ordering::Equal => self.children.cmp(&other.children),
                    x => x,
                }
            x => x,
        }
    }
}

#[cfg(test)]
pub mod tests {
    use super::*;

    #[test]
    fn test_tree_stack() {
        let mut ts: TreeStack<u8> = TreeStack::new(0);
        assert_eq!(&0, ts.current_symbol());

        ts = ts.push(1, 1).unwrap();
        assert_eq!(&1, ts.current_symbol());

        ts = ts.down().unwrap();
        assert_eq!(&0, ts.current_symbol());

        ts = ts.push(2, 2).unwrap();
        assert_eq!(&2, ts.current_symbol());

        ts = ts.down().unwrap();
        ts = ts.up(1).unwrap();
        assert_eq!(&1, ts.current_symbol());

        ts = ts.push(1, 11).unwrap();
        assert_eq!(&11, ts.current_symbol());

        ts = ts.down().unwrap();
        ts = ts.down().unwrap();
        ts = ts.up(2).unwrap();
        ts = ts.push(1, 21).unwrap();
        assert_eq!(&21, ts.current_symbol());

        ts = ts.down().unwrap();
        ts = ts.down().unwrap();
        assert_eq!(&0, ts.current_symbol());
    }

    #[test]
    fn test_to_tree() {
        let mut ts: TreeStack<char> = TreeStack::new('@');
        ts = ts.push(0, 'a').unwrap();
        ts = ts.push(0, 'b').unwrap();
        ts = ts.down().unwrap();
        ts = ts.down().unwrap();
        ts = ts.push(1, 'c').unwrap();
        ts = ts.down().unwrap();
        ts = ts.push(3, 'd').unwrap();
        ts = ts.push(1, 'e').unwrap();

        let mut tree_map = GornTree::new();
        tree_map.insert(vec![], '@');
        tree_map.insert(vec![0], 'a');
        tree_map.insert(vec![0, 0], 'b');
        tree_map.insert(vec![1], 'c');
        tree_map.insert(vec![3], 'd');
        tree_map.insert(vec![3, 1], 'e');

        assert_eq!((tree_map, vec![3, 1]), ts.to_tree());
    }
}
<|endoftext|>


<commit_before>extern crate rcalc;

use std::io;
use std::io::{ BufWriter, Write };
use std::path::PathBuf;
use std::fs::File;
use std::env;
use rcalc::{ Calculator, RuntimeItem, Value };

fn main() {
    let mut calculator = Calculator::new();
    let mut precision = None;
    let mut parallel_srcs = None;

    println!("Enter ? for help");

    loop {
        print!("> ");
        io::stdout().flush().expect("stdout error");

        match read_line().trim() {
            "" => (),
            "#q" => break,
            "#:" => {
                parallel_srcs = match parallel_srcs {
                    Some(..) => None,
                    None => Some(vec![]),
                }
            },
            "#!" => {
                if let Some(ref srcs) = parallel_srcs {
                    let item = calculator.calc_parallel(srcs.clone());
                    print_item(item, &precision);
                };
                parallel_srcs = None;
            },
            "#j" => {
                let mut buf = Vec::new();
                let _ = {
                    let mut writer = BufWriter::new(&mut buf);
                    calculator.write_json(&mut writer).expect("json output error");
                };
                println!("{}", String::from_utf8(buf).unwrap());
            },
            "#w" => {
                match write_json(&calculator) {
                    Result::Ok(ref path) => println!("Context written to '{}'", path),
                    Result::Err((ref path, ref error)) => println!("Error writing context to '{}': {:?}", path, error),
                };
            },
            "?" => {
                println!("#q - quit rcalc");
                println!("#: - enter or leave parallel mode");
                println!("#! - leave parallel mode and evaluate all input concurrently");
                println!("#j - print a JSON representation of the current value bindings");
                println!("#w - write a JSON representation of the current value bindings to '{}'", get_json_path());
                println!("#precision <DIGITS> - format floating point values with <DIGITS> decimal places");
            },
            line if line.starts_with("#precision ") => {
                if let Result::Ok(p) = line.split_whitespace().last().unwrap().parse::<usize>() {
                    precision = Some(p);
                } else {
                    precision = None;
                }
            },
            line => {
                match parallel_srcs {
                    Some(ref mut srcs) => srcs.push(line.to_string()),
                    None => {
                        match calculator.calc(line) {
                            Ok(item) => print_item(item, &precision),
                            Err(msg) => println!("{}", msg),
                        }
                    }
                }
            }
        }
    }
}

fn read_line() -> String {
    let mut buf = String::new();
    match io::stdin().read_line(&mut buf) {
        Ok(_) => buf,
        Err(e) => {
            println!("{:?}", e);
            "".to_string()
        },
    }
}

fn print_item(item: &RuntimeItem, precision: &Option<usize>) {
    if let &RuntimeItem::Value(ref v) = item {
        print_value(v, precision);
    } else {
        println!("Function OK");
    };
}

fn print_value(v: &Value, precision: &Option<usize>) {
    match v {
        &Value::Integer(n) => {
            println!("= {}", n);
            println!("  {:#x}", n);
            println!("  {:#b}", n);
        },
        _ => {
            match *precision {
                Some(n) => println!("= {val:.prec$}", val = v, prec = n),
                None => println!("= {}", v),
            }
        },
    }
}

fn write_json(calculator: &Calculator) -> Result<String, (String, ::std::io::Error)> {
    fn inner_write(calculator: &Calculator, path: &str) -> ::std::io::Result<()> {
        let mut writer = BufWriter::new(try!(File::create(&path)));
        calculator.write_json(&mut writer)
    }
    let path = get_json_path();
    match inner_write(calculator, &path) {
        Result::Ok(()) => Result::Ok(path),
        Result::Err(error) => Result::Err((path, error)),
    }
}

fn get_json_path() -> String {
    let mut pathbuf: PathBuf = match env::home_dir() {
        Some(path) => PathBuf::from(path),
        None => PathBuf::from(""),
    };
    pathbuf.push("rcalc.json");
    pathbuf.to_str().unwrap().to_string()
}
<commit_msg>Minor cosmetics for v0.2<commit_after>extern crate rcalc;

use std::io;
use std::io::{ BufWriter, Write };
use std::path::PathBuf;
use std::fs::File;
use std::env;
use rcalc::{ Calculator, RuntimeItem, Value };

fn main() {
    let mut calculator = Calculator::new();
    let mut precision = None;
    let mut parallel_srcs = None;

    println!("rust-calc v0.2");
    println!("http://github.com/smackem/rust-calc");
    println!("Enter ? for help");

    loop {
        print!("> ");
        io::stdout().flush().expect("stdout error");

        match read_line().trim() {
            "" => (),
            "#q" => break,
            "#:" => {
                parallel_srcs = match parallel_srcs {
                    Some(..) => None,
                    None => Some(vec![]),
                }
            },
            "#!" => {
                if let Some(ref srcs) = parallel_srcs {
                    let item = calculator.calc_parallel(srcs.clone());
                    print_item(item, &precision);
                };
                parallel_srcs = None;
            },
            "#j" => {
                let mut buf = Vec::new();
                let _ = {
                    let mut writer = BufWriter::new(&mut buf);
                    calculator.write_json(&mut writer).expect("json output error");
                };
                println!("{}", String::from_utf8(buf).unwrap());
            },
            "#w" => {
                match write_json(&calculator) {
                    Result::Ok(ref path) => println!("Context written to '{}'", path),
                    Result::Err((ref path, ref error)) => println!("Error writing context to '{}': {:?}", path, error),
                };
            },
            "?" => {
                println!("#: - enter or leave parallel mode");
                println!("#! - leave parallel mode and evaluate all input concurrently");
                println!("#j - print a JSON representation of the current value bindings");
                println!("#w - write a JSON representation of the current value bindings to '{}'", get_json_path());
                println!("#p <DIGITS> - format floating point values with <DIGITS> decimal places");
                println!("#q - quit rcalc");
                println!("");
                println!("anything else is parsed as a statement.");
                println!("see http://github.com/smackem/rust-calc");
            },
            line if line.starts_with("#p ") => {
                if let Result::Ok(p) = line.split_whitespace().last().unwrap().parse::<usize>() {
                    precision = Some(p);
                } else {
                    precision = None;
                }
            },
            line => {
                match parallel_srcs {
                    Some(ref mut srcs) => srcs.push(line.to_string()),
                    None => {
                        match calculator.calc(line) {
                            Ok(item) => print_item(item, &precision),
                            Err(msg) => println!("{}", msg),
                        }
                    }
                }
            }
        }
    }
}

fn read_line() -> String {
    let mut buf = String::new();
    match io::stdin().read_line(&mut buf) {
        Ok(_) => buf,
        Err(e) => {
            println!("{:?}", e);
            "".to_string()
        },
    }
}

fn print_item(item: &RuntimeItem, precision: &Option<usize>) {
    if let &RuntimeItem::Value(ref v) = item {
        print_value(v, precision);
    } else {
        println!("Function OK");
    };
}

fn print_value(v: &Value, precision: &Option<usize>) {
    match v {
        &Value::Integer(n) => {
            println!("= {}", n);
            println!("  {:#x}", n);
            println!("  {:#b}", n);
        },
        _ => {
            match *precision {
                Some(n) => println!("= {val:.prec$}", val = v, prec = n),
                None => println!("= {}", v),
            }
        },
    }
}

fn write_json(calculator: &Calculator) -> Result<String, (String, ::std::io::Error)> {
    fn inner_write(calculator: &Calculator, path: &str) -> ::std::io::Result<()> {
        let mut writer = BufWriter::new(try!(File::create(&path)));
        calculator.write_json(&mut writer)
    }
    let path = get_json_path();
    match inner_write(calculator, &path) {
        Result::Ok(()) => Result::Ok(path),
        Result::Err(error) => Result::Err((path, error)),
    }
}

fn get_json_path() -> String {
    let mut pathbuf: PathBuf = match env::home_dir() {
        Some(path) => PathBuf::from(path),
        None => PathBuf::from(""),
    };
    pathbuf.push("rcalc.json");
    pathbuf.to_str().unwrap().to_string()
}
<|endoftext|>