<commit_before>//! The implementation for Version 1 UUIDs.
//!
//! Note that you need feature `v1` in order to use these features.

use crate::prelude::*;
use atomic::Atomic;

/// The number of 100 ns ticks between the UUID epoch
/// `1582-10-15 00:00:00` and the Unix epoch `1970-01-01 00:00:00`.
const UUID_TICKS_BETWEEN_EPOCHS: u64 = 0x01B2_1DD2_1381_4000;

/// A thread-safe, stateful context for the v1 generator to help ensure
/// process-wide uniqueness.
#[derive(Debug)]
pub struct Context {
    count: Atomic<usize>,
}

/// Stores the number of nanoseconds from an epoch and a counter for ensuring
/// V1 ids generated on the same host are unique.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Timestamp {
    ticks: u64,
    counter: u16,
}

impl Timestamp {
    /// Construct a `Timestamp` from its raw component values: an RFC4122
    /// timestamp and counter.
    ///
    /// RFC4122, which defines the V1 UUID, specifies a 60-byte timestamp format
    /// as the number of 100-nanosecond intervals elapsed since 00:00:00.00,
    /// 15 Oct 1582, "the date of the Gregorian reform of the Christian
    /// calendar."
    ///
    /// The counter value is used to differentiate between ids generated by
    /// the same host computer in rapid succession (i.e. with the same observed
    /// time). See the [`ClockSequence`] trait for a generic interface to any
    /// counter generators that might be used.
    ///
    /// Internally, the timestamp is stored as a `u64`. For this reason, dates
    /// prior to October 1582 are not supported.
    ///
    /// [`ClockSequence`]: trait.ClockSequence.html
    pub const fn from_rfc4122(ticks: u64, counter: u16) -> Self {
        Timestamp { ticks, counter }
    }

    /// Construct a `Timestamp` from a unix timestamp and sequence-generating
    /// `context`.
    ///
    /// A unix timestamp represents the elapsed time since Jan 1 1970. Libc's
    /// `clock_gettime` and other popular implementations traditionally
    /// represent this duration as a `timespec`: a struct with `u64` and
    /// `u32` fields representing the seconds, and "subsecond" or fractional
    /// nanoseconds elapsed since the timestamp's second began,
    /// respectively.
    ///
    /// This constructs a `Timestamp` from the seconds and fractional
    /// nanoseconds of a unix timestamp, converting the duration since 1970
    /// into the number of 100-nanosecond intervals since 00:00:00.00, 15
    /// Oct 1582 specified by RFC4122 and used internally by `Timestamp`.
    ///
    /// The function is not guaranteed to produce monotonically increasing
    /// values however. There is a slight possibility that two successive
    /// equal time values could be supplied and the sequence counter wraps back
    /// over to 0.
    ///
    /// If uniqueness and monotonicity is required, the user is responsible for
    /// ensuring that the time value always increases between calls (including
    /// between restarts of the process and device).
    pub fn from_unix(
        context: impl ClockSequence,
        seconds: u64,
        subsec_nanos: u32,
    ) -> Self {
        let counter = context.generate_sequence(seconds, subsec_nanos);
        let ticks = UUID_TICKS_BETWEEN_EPOCHS
            + seconds * 10_000_000
            + u64::from(subsec_nanos) / 100;

        Timestamp { ticks, counter }
    }

    /// Returns the raw RFC4122 timestamp and counter values stored by the
    /// `Timestamp`.
    ///
    /// The timestamp (the first, `u64` element in the tuple) represents the
    /// number of 100-nanosecond intervals since 00:00:00.00, 15 Oct 1582.
    /// The counter is used to differentiate between ids generated on the
    /// same host computer with the same observed time.
    pub const fn to_rfc4122(&self) -> (u64, u16) {
        (self.ticks, self.counter)
    }

    /// Returns the timestamp converted to the seconds and fractional
    /// nanoseconds since Jan 1 1970.
    ///
    /// Internally, the time is stored in 100-nanosecond intervals,
    /// thus the maximum precision represented by the fractional nanoseconds
    /// value is less than its unit size (100 ns vs. 1 ns).
    pub const fn to_unix(&self) -> (u64, u32) {
        (
            (self.ticks - UUID_TICKS_BETWEEN_EPOCHS) / 10_000_000,
            ((self.ticks - UUID_TICKS_BETWEEN_EPOCHS) % 10_000_000) as u32
                * 100,
        )
    }

    /// Returns the timestamp converted into nanoseconds elapsed since Jan 1
    /// 1970. Internally, the time is stored in 100-nanosecond intervals,
    /// thus the maximum precision represented is less than the units it is
    /// measured in (100 ns vs. 1 ns). The value returned represents the
    /// same duration as [`Timestamp::to_unix`]; this provides it in nanosecond
    /// units for convenience.
    pub const fn to_unix_nanos(&self) -> u64 {
        (self.ticks - UUID_TICKS_BETWEEN_EPOCHS) * 100
    }
}

/// A trait that abstracts over generation of UUID v1 "Clock Sequence" values.
pub trait ClockSequence {
    /// Return a 16-bit number that will be used as the "clock sequence" in
    /// the UUID. The number must be different if the time has changed since
    /// the last time a clock sequence was requested.
    fn generate_sequence(&self, seconds: u64, subsec_nanos: u32) -> u16;
}

impl<'a, T: ClockSequence + ?Sized> ClockSequence for &'a T {
    fn generate_sequence(&self, seconds: u64, subsec_nanos: u32) -> u16 {
        (**self).generate_sequence(seconds, subsec_nanos)
    }
}

impl Uuid {
    /// Create a new UUID (version 1) using a time value + sequence +
    /// *NodeId*.
    ///
    /// When generating [`Timestamp`]s using a [`ClockSequence`], this function
    /// is only guaranteed to produce unique values if the following conditions
    /// hold:
    ///
    /// 1. The *NodeId* is unique for this process,
    /// 2. The *Context* is shared across all threads which are generating v1
    ///    UUIDs,
    /// 3. The [`ClockSequence`] implementation reliably returns unique
    ///    clock sequences (this crate provides [`Context`] for this
    ///    purpose. However you can create your own [`ClockSequence`]
    ///    implementation, if [`Context`] does not meet your needs).
    ///
    /// The NodeID must be exactly 6 bytes long.
    ///
    /// Note that usage of this method requires the `v1` feature of this crate
    /// to be enabled.
    ///
    /// # Examples
    ///
    /// A UUID can be created from a unix [`Timestamp`] with a
    /// [`ClockSequence`]:
    ///
    /// ```rust
    /// use uuid::v1::{Timestamp, Context};
    /// use uuid::Uuid;
    ///
    /// let context = Context::new(42);
    /// let ts = Timestamp::from_unix(&context, 1497624119, 1234);
    /// let uuid = Uuid::new_v1(ts, &[1, 2, 3, 4, 5, 6]);
    ///
    /// assert_eq!(
    ///     uuid.to_hyphenated().to_string(),
    ///     "f3b4958c-52a1-11e7-802a-010203040506"
    /// );
    /// ```
    ///
    /// The timestamp can also be created manually as per RFC4122:
    ///
    /// ```
    /// use uuid::v1::{Timestamp, Context};
    /// use uuid::Uuid;
    ///
    /// let context = Context::new(42);
    /// let ts = Timestamp::from_rfc4122(1497624119, 0);
    /// let uuid = Uuid::new_v1(ts, &[1, 2, 3, 4, 5, 6]).expect("failed to generate UUID");
    ///
    /// assert_eq!(
    ///     uuid.to_hyphenated().to_string(),
    ///     "5943ee37-0000-1000-8000-010203040506"
    /// );
    /// ```
    ///
    /// [`Timestamp`]: v1/struct.Timestamp.html
    /// [`ClockSequence`]: v1/struct.ClockSequence.html
    /// [`Context`]: v1/struct.Context.html
    pub fn new_v1(ts: Timestamp, node_id: &[u8; 6]) -> Self {
        let time_low = (ts.ticks & 0xFFFF_FFFF) as u32;
        let time_mid = ((ts.ticks >> 32) & 0xFFFF) as u16;
        let time_high_and_version =
            (((ts.ticks >> 48) & 0x0FFF) as u16) | (1 << 12);

        let mut d4 = [0; 8];

        {
            d4[0] = (((ts.counter & 0x3F00) >> 8) as u8) | 0x80;
            d4[1] = (ts.counter & 0xFF) as u8;
        }

        d4[2..].copy_from_slice(node_id);

        Uuid::from_fields(time_low, time_mid, time_high_and_version, &d4)
    }

    /// Returns an optional [`Timestamp`] storing the timestamp and
    /// counter portion parsed from a V1 UUID.
    ///
    /// Returns `None` if the supplied UUID is not V1.
    ///
    /// The V1 timestamp format defined in RFC4122 specifies a 60-bit
    /// integer representing the number of 100-nanosecond intervals
    /// since 00:00:00.00, 15 Oct 1582.
    ///
    /// [`Timestamp`] offers several options for converting the raw RFC4122
    /// value into more commonly-used formats, such as a unix timestamp.
    ///
    /// [`Timestamp`]: v1/struct.Timestamp.html
    pub const fn to_timestamp(&self) -> Option<Timestamp> {
        match self.get_version() {
            Some(Version::Mac) => {
                let ticks: u64 = ((self.as_bytes()[6] & 0x0F) as u64) << 56
                    | ((self.as_bytes()[7]) as u64) << 48
                    | ((self.as_bytes()[4]) as u64) << 40
                    | ((self.as_bytes()[5]) as u64) << 32
                    | ((self.as_bytes()[0]) as u64) << 24
                    | ((self.as_bytes()[1]) as u64) << 16
                    | ((self.as_bytes()[2]) as u64) << 8
                    | (self.as_bytes()[3] as u64);

                let counter: u16 = ((self.as_bytes()[8] & 0x3F) as u16) << 8
                    | (self.as_bytes()[9] as u16);

                Some(Timestamp::from_rfc4122(ticks, counter))
            },
            _ => None
        }
    }
}

impl Context {
    /// Creates a thread-safe, internally mutable context to help ensure
    /// uniqueness.
    ///
    /// This is a context which can be shared across threads. It maintains an
    /// internal counter that is incremented at every request, the value ends
    /// up in the clock_seq portion of the UUID (the fourth group). This
    /// will improve the probability that the UUID is unique across the
    /// process.
    pub const fn new(count: u16) -> Self {
        Self {
            count: Atomic::new(count as usize),
        }
    }
}

impl ClockSequence for Context {
    fn generate_sequence(&self, _: u64, _: u32) -> u16 {
        (self.count.fetch_add(1, atomic::Ordering::SeqCst) & 0xffff) as u16
    }
}

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

    #[cfg(target_arch = "wasm32")]
    use wasm_bindgen_test::*;

    use crate::std::string::ToString;

    #[test]
    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
    fn test_new_v1() {
        let time: u64 = 1_496_854_535;
        let time_fraction: u32 = 812_946_000;
        let node = [1, 2, 3, 4, 5, 6];
        let context = Context::new(0);

        {
            let uuid = Uuid::new_v1(
                Timestamp::from_unix(&context, time, time_fraction),
                &node,
            )
            .unwrap();

            assert_eq!(uuid.get_version(), Some(Version::Mac));
            assert_eq!(uuid.get_variant(), Some(Variant::RFC4122));
            assert_eq!(
                uuid.to_hyphenated().to_string(),
                "20616934-4ba2-11e7-8000-010203040506"
            );

            let ts = uuid.to_timestamp().unwrap().to_rfc4122();

            assert_eq!(ts.0 - 0x01B2_1DD2_1381_4000, 14_968_545_358_129_460);
            assert_eq!(ts.1, 0);
        };

        {
            let uuid2 = Uuid::new_v1(
                Timestamp::from_unix(&context, time, time_fraction),
                &node,
            )
            .unwrap();

            assert_eq!(
                uuid2.to_hyphenated().to_string(),
                "20616934-4ba2-11e7-8001-010203040506"
            );
            assert_eq!(uuid2.to_timestamp().unwrap().to_rfc4122().1, 1)
        };
    }
}
<commit_msg>fix up v1<commit_after>//! The implementation for Version 1 UUIDs.
//!
//! Note that you need feature `v1` in order to use these features.

use crate::prelude::*;
use atomic::Atomic;

/// The number of 100 ns ticks between the UUID epoch
/// `1582-10-15 00:00:00` and the Unix epoch `1970-01-01 00:00:00`.
const UUID_TICKS_BETWEEN_EPOCHS: u64 = 0x01B2_1DD2_1381_4000;

/// A thread-safe, stateful context for the v1 generator to help ensure
/// process-wide uniqueness.
#[derive(Debug)]
pub struct Context {
    count: Atomic<usize>,
}

/// Stores the number of nanoseconds from an epoch and a counter for ensuring
/// V1 ids generated on the same host are unique.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Timestamp {
    ticks: u64,
    counter: u16,
}

impl Timestamp {
    /// Construct a `Timestamp` from its raw component values: an RFC4122
    /// timestamp and counter.
    ///
    /// RFC4122, which defines the V1 UUID, specifies a 60-byte timestamp format
    /// as the number of 100-nanosecond intervals elapsed since 00:00:00.00,
    /// 15 Oct 1582, "the date of the Gregorian reform of the Christian
    /// calendar."
    ///
    /// The counter value is used to differentiate between ids generated by
    /// the same host computer in rapid succession (i.e. with the same observed
    /// time). See the [`ClockSequence`] trait for a generic interface to any
    /// counter generators that might be used.
    ///
    /// Internally, the timestamp is stored as a `u64`. For this reason, dates
    /// prior to October 1582 are not supported.
    ///
    /// [`ClockSequence`]: trait.ClockSequence.html
    pub const fn from_rfc4122(ticks: u64, counter: u16) -> Self {
        Timestamp { ticks, counter }
    }

    /// Construct a `Timestamp` from a unix timestamp and sequence-generating
    /// `context`.
    ///
    /// A unix timestamp represents the elapsed time since Jan 1 1970. Libc's
    /// `clock_gettime` and other popular implementations traditionally
    /// represent this duration as a `timespec`: a struct with `u64` and
    /// `u32` fields representing the seconds, and "subsecond" or fractional
    /// nanoseconds elapsed since the timestamp's second began,
    /// respectively.
    ///
    /// This constructs a `Timestamp` from the seconds and fractional
    /// nanoseconds of a unix timestamp, converting the duration since 1970
    /// into the number of 100-nanosecond intervals since 00:00:00.00, 15
    /// Oct 1582 specified by RFC4122 and used internally by `Timestamp`.
    ///
    /// The function is not guaranteed to produce monotonically increasing
    /// values however. There is a slight possibility that two successive
    /// equal time values could be supplied and the sequence counter wraps back
    /// over to 0.
    ///
    /// If uniqueness and monotonicity is required, the user is responsible for
    /// ensuring that the time value always increases between calls (including
    /// between restarts of the process and device).
    pub fn from_unix(
        context: impl ClockSequence,
        seconds: u64,
        subsec_nanos: u32,
    ) -> Self {
        let counter = context.generate_sequence(seconds, subsec_nanos);
        let ticks = UUID_TICKS_BETWEEN_EPOCHS
            + seconds * 10_000_000
            + u64::from(subsec_nanos) / 100;

        Timestamp { ticks, counter }
    }

    /// Returns the raw RFC4122 timestamp and counter values stored by the
    /// `Timestamp`.
    ///
    /// The timestamp (the first, `u64` element in the tuple) represents the
    /// number of 100-nanosecond intervals since 00:00:00.00, 15 Oct 1582.
    /// The counter is used to differentiate between ids generated on the
    /// same host computer with the same observed time.
    pub const fn to_rfc4122(&self) -> (u64, u16) {
        (self.ticks, self.counter)
    }

    /// Returns the timestamp converted to the seconds and fractional
    /// nanoseconds since Jan 1 1970.
    ///
    /// Internally, the time is stored in 100-nanosecond intervals,
    /// thus the maximum precision represented by the fractional nanoseconds
    /// value is less than its unit size (100 ns vs. 1 ns).
    pub const fn to_unix(&self) -> (u64, u32) {
        (
            (self.ticks - UUID_TICKS_BETWEEN_EPOCHS) / 10_000_000,
            ((self.ticks - UUID_TICKS_BETWEEN_EPOCHS) % 10_000_000) as u32
                * 100,
        )
    }

    /// Returns the timestamp converted into nanoseconds elapsed since Jan 1
    /// 1970. Internally, the time is stored in 100-nanosecond intervals,
    /// thus the maximum precision represented is less than the units it is
    /// measured in (100 ns vs. 1 ns). The value returned represents the
    /// same duration as [`Timestamp::to_unix`]; this provides it in nanosecond
    /// units for convenience.
    pub const fn to_unix_nanos(&self) -> u64 {
        (self.ticks - UUID_TICKS_BETWEEN_EPOCHS) * 100
    }
}

/// A trait that abstracts over generation of UUID v1 "Clock Sequence" values.
pub trait ClockSequence {
    /// Return a 16-bit number that will be used as the "clock sequence" in
    /// the UUID. The number must be different if the time has changed since
    /// the last time a clock sequence was requested.
    fn generate_sequence(&self, seconds: u64, subsec_nanos: u32) -> u16;
}

impl<'a, T: ClockSequence + ?Sized> ClockSequence for &'a T {
    fn generate_sequence(&self, seconds: u64, subsec_nanos: u32) -> u16 {
        (**self).generate_sequence(seconds, subsec_nanos)
    }
}

impl Uuid {
    /// Create a new UUID (version 1) using a time value + sequence +
    /// *NodeId*.
    ///
    /// When generating [`Timestamp`]s using a [`ClockSequence`], this function
    /// is only guaranteed to produce unique values if the following conditions
    /// hold:
    ///
    /// 1. The *NodeId* is unique for this process,
    /// 2. The *Context* is shared across all threads which are generating v1
    ///    UUIDs,
    /// 3. The [`ClockSequence`] implementation reliably returns unique
    ///    clock sequences (this crate provides [`Context`] for this
    ///    purpose. However you can create your own [`ClockSequence`]
    ///    implementation, if [`Context`] does not meet your needs).
    ///
    /// The NodeID must be exactly 6 bytes long.
    ///
    /// Note that usage of this method requires the `v1` feature of this crate
    /// to be enabled.
    ///
    /// # Examples
    ///
    /// A UUID can be created from a unix [`Timestamp`] with a
    /// [`ClockSequence`]:
    ///
    /// ```rust
    /// use uuid::v1::{Timestamp, Context};
    /// use uuid::Uuid;
    ///
    /// let context = Context::new(42);
    /// let ts = Timestamp::from_unix(&context, 1497624119, 1234);
    /// let uuid = Uuid::new_v1(ts, &[1, 2, 3, 4, 5, 6]);
    ///
    /// assert_eq!(
    ///     uuid.to_hyphenated().to_string(),
    ///     "f3b4958c-52a1-11e7-802a-010203040506"
    /// );
    /// ```
    ///
    /// The timestamp can also be created manually as per RFC4122:
    ///
    /// ```
    /// use uuid::v1::{Timestamp, Context};
    /// use uuid::Uuid;
    ///
    /// let context = Context::new(42);
    /// let ts = Timestamp::from_rfc4122(1497624119, 0);
    /// let uuid = Uuid::new_v1(ts, &[1, 2, 3, 4, 5, 6]);
    ///
    /// assert_eq!(
    ///     uuid.to_hyphenated().to_string(),
    ///     "5943ee37-0000-1000-8000-010203040506"
    /// );
    /// ```
    ///
    /// [`Timestamp`]: v1/struct.Timestamp.html
    /// [`ClockSequence`]: v1/struct.ClockSequence.html
    /// [`Context`]: v1/struct.Context.html
    pub const fn new_v1(ts: Timestamp, node_id: &[u8; 6]) -> Self {
        let time_low = (ts.ticks & 0xFFFF_FFFF) as u32;
        let time_mid = ((ts.ticks >> 32) & 0xFFFF) as u16;
        let time_high_and_version =
            (((ts.ticks >> 48) & 0x0FFF) as u16) | (1 << 12);

        let mut d4 = [0; 8];

        d4[0] = (((ts.counter & 0x3F00) >> 8) as u8) | 0x80;
        d4[1] = (ts.counter & 0xFF) as u8;
        d4[2] = node_id[0];
        d4[3] = node_id[1];
        d4[4] = node_id[2];
        d4[5] = node_id[3];
        d4[6] = node_id[4];
        d4[7] = node_id[5];

        Uuid::from_fields(time_low, time_mid, time_high_and_version, &d4)
    }

    /// Returns an optional [`Timestamp`] storing the timestamp and
    /// counter portion parsed from a V1 UUID.
    ///
    /// Returns `None` if the supplied UUID is not V1.
    ///
    /// The V1 timestamp format defined in RFC4122 specifies a 60-bit
    /// integer representing the number of 100-nanosecond intervals
    /// since 00:00:00.00, 15 Oct 1582.
    ///
    /// [`Timestamp`] offers several options for converting the raw RFC4122
    /// value into more commonly-used formats, such as a unix timestamp.
    ///
    /// [`Timestamp`]: v1/struct.Timestamp.html
    pub const fn to_timestamp(&self) -> Option<Timestamp> {
        match self.get_version() {
            Some(Version::Mac) => {
                let ticks: u64 = ((self.as_bytes()[6] & 0x0F) as u64) << 56
                    | ((self.as_bytes()[7]) as u64) << 48
                    | ((self.as_bytes()[4]) as u64) << 40
                    | ((self.as_bytes()[5]) as u64) << 32
                    | ((self.as_bytes()[0]) as u64) << 24
                    | ((self.as_bytes()[1]) as u64) << 16
                    | ((self.as_bytes()[2]) as u64) << 8
                    | (self.as_bytes()[3] as u64);

                let counter: u16 = ((self.as_bytes()[8] & 0x3F) as u16) << 8
                    | (self.as_bytes()[9] as u16);

                Some(Timestamp::from_rfc4122(ticks, counter))
            },
            _ => None
        }
    }
}

impl Context {
    /// Creates a thread-safe, internally mutable context to help ensure
    /// uniqueness.
    ///
    /// This is a context which can be shared across threads. It maintains an
    /// internal counter that is incremented at every request, the value ends
    /// up in the clock_seq portion of the UUID (the fourth group). This
    /// will improve the probability that the UUID is unique across the
    /// process.
    pub const fn new(count: u16) -> Self {
        Self {
            count: Atomic::new(count as usize),
        }
    }
}

impl ClockSequence for Context {
    fn generate_sequence(&self, _: u64, _: u32) -> u16 {
        (self.count.fetch_add(1, atomic::Ordering::SeqCst) & 0xffff) as u16
    }
}

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

    #[cfg(target_arch = "wasm32")]
    use wasm_bindgen_test::*;

    use crate::std::string::ToString;

    #[test]
    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
    fn test_new_v1() {
        let time: u64 = 1_496_854_535;
        let time_fraction: u32 = 812_946_000;
        let node = [1, 2, 3, 4, 5, 6];
        let context = Context::new(0);

        {
            let uuid = Uuid::new_v1(
                Timestamp::from_unix(&context, time, time_fraction),
                &node,
            );

            assert_eq!(uuid.get_version(), Some(Version::Mac));
            assert_eq!(uuid.get_variant(), Some(Variant::RFC4122));
            assert_eq!(
                uuid.to_hyphenated().to_string(),
                "20616934-4ba2-11e7-8000-010203040506"
            );

            let ts = uuid.to_timestamp().unwrap().to_rfc4122();

            assert_eq!(ts.0 - 0x01B2_1DD2_1381_4000, 14_968_545_358_129_460);
            assert_eq!(ts.1, 0);
        };

        {
            let uuid2 = Uuid::new_v1(
                Timestamp::from_unix(&context, time, time_fraction),
                &node,
            );

            assert_eq!(
                uuid2.to_hyphenated().to_string(),
                "20616934-4ba2-11e7-8001-010203040506"
            );
            assert_eq!(uuid2.to_timestamp().unwrap().to_rfc4122().1, 1)
        };
    }
}
<|endoftext|>


<commit_before>//! The following code generates a 1s long, 16-bit, 440Hz sinewave at a 44100Hz sample rate.
//! It then writes the generated samples into a 44100Hz WAV file at `out/sine.wav`.
//!
//! ```
//! use synthrs::wave::SineWave;
//! use synthrs::writer::write_wav;
//! use synthrs::synthesizer::{quantize_samples, make_samples};
//!
//! write_wav("out/sine.wav", 44100,
//!     quantize_samples::<i16>(make_samples(1.0, 44100, SineWave(440.0)))
//! ).ok().expect("failed");
//! ```
//!
//! See: `examples/simple.rs`

use std::num::{ Float, FloatMath, from_f64 };
use std::mem::size_of;

use music;
use reader;
use wave;

/// Quantizes a `f64` sample into `T`.
pub fn quantize<T>(input: f64) -> T where T: FromPrimitive {
    let quantization_levels = 2.0.powf(size_of::<T>() as f64 * 8.0) - 1.0;
    // Convert from [-1, 1] to take up full quantization range
    from_f64::<T>(input * (quantization_levels / 2.0)).expect("failed to quantize to given type")
}

/// Quantizes a `Vec<f64>` of samples into `Vec<T>`.
///
/// This creates a 16-bit SineWave at 440Hz:
///
/// ```
/// use synthrs::wave::SineWave;
/// use synthrs::synthesizer::{quantize_samples, make_samples};
///
/// quantize_samples::<i16>(make_samples(1.0, 44100, SineWave(440.0)));
/// ```
pub fn quantize_samples<T>(input: Vec<f64>) -> Vec<T> where T: FromPrimitive {
    input.iter().map(|s| { quantize::<T>(*s) }).collect()
}

/// Invokes the waveform function `f` at time `t` to return the amplitude at that time.
pub fn generate<F>(t: f64, f: &F) -> f64 where F: Fn<(f64, ), f64> {
    f.call((t, ))
}

pub fn make_samples<F>(length: f64, sample_rate: uint, waveform: F) -> Vec<f64> where F: Fn<(f64, ), f64> {
    let num_samples = (sample_rate as f64 * length).floor() as uint;
    let mut samples: Vec<f64> = Vec::with_capacity(num_samples);

    for i in range(0u, num_samples) {
        let t = i as f64 / sample_rate as f64;
        samples.push(generate(t, &waveform));
    }

    samples
}

/// Peak normalizes a `Vec<f64>` of samples such that the maximum and minimum amplitudes of the
/// `Vec<f64>` samples are within the range [-1.0, 1.0]
pub fn peak_normalize(samples: Vec<f64>) -> Vec<f64> {
    let peak = samples.iter().fold(0.0f64, |acc, &sample| {
        acc.max(sample)
    });

    samples.iter().map(|&sample| {
        sample / peak
    }).collect()
}

// This is really awful, is there a more elegant way to do this?
// TODO: Make the instrument a parameter (perhaps using an Instrument trait?)
pub fn make_samples_from_midi(sample_rate: uint, filename: &str) -> Vec<f64> {
    let song = reader::read_midi(filename).unwrap();
    let length = (60.0 * song.max_time as f64) / (song.bpm * song.time_unit as f64);

    let mut notes_on_for_ticks: Vec<Vec<(u8, u8, uint)>> = Vec::new();
    for _ in range(0, song.max_time) {
        let notes_on_for_tick: Vec<(u8, u8, uint)> = Vec::new();
        notes_on_for_ticks.push(notes_on_for_tick);
    }

    for track in song.tracks.iter() {
        for i in range(0, track.events.len()) {
            let event = track.events[i];
            if event.event_type == reader::MidiEventType::NoteOn {
                let start_tick = event.time;
                let note = event.value1;
                let velocity = event.value2.unwrap();

                let mut end_tick = song.max_time;
                for j in range(i, track.events.len()) {
                    let event_cursor = track.events[j];

                    // NoteOn with velocity 0 == NoteOff
                    if (event_cursor.event_type == reader::MidiEventType::NoteOff && event_cursor.value1 == note) ||
                       (event_cursor.event_type == reader::MidiEventType::NoteOn && event_cursor.value1 == note && event_cursor.value2.unwrap() == 0) {
                        end_tick = event_cursor.time;
                        break;
                    }
                }

                for tick in range(start_tick, end_tick) {
                    notes_on_for_ticks[tick].push((note as u8, velocity as u8, start_tick));
                }
            }
        }
    }

    let midi_frequency_function = |t: f64| -> f64 {
        let tick = (t * song.bpm * song.time_unit as f64 / 60.0) as uint;
        let mut out = 0.0;

        if tick < notes_on_for_ticks.len() {
            for &(note, velocity, start_tick) in notes_on_for_ticks[tick].iter() {
                let frequency = music::note_midi(440.0, note as uint);
                let loudness = (6.908 * (velocity as f64 / 255.0)).exp() / 1000.0;
                let start_t = start_tick as f64 * 60.0 / song.bpm as f64 / song.time_unit as f64;
                let relative_t = t - start_t;
                out += loudness * wave::SquareWave(frequency)(relative_t)
            }
        }

        out
    };

    let num_samples = (sample_rate as f64 * length).floor() as uint;
    let mut samples: Vec<f64> = Vec::with_capacity(num_samples);

    for i in range(0u, num_samples) {
        let t = i as f64 / sample_rate as f64;
        samples.push(midi_frequency_function(t));
    }

    peak_normalize(samples)
}
<commit_msg>Use butchered Karplus-Strong square wave as MIDI instrument<commit_after>//! The following code generates a 1s long, 16-bit, 440Hz sinewave at a 44100Hz sample rate.
//! It then writes the generated samples into a 44100Hz WAV file at `out/sine.wav`.
//!
//! ```
//! use synthrs::wave::SineWave;
//! use synthrs::writer::write_wav;
//! use synthrs::synthesizer::{quantize_samples, make_samples};
//!
//! write_wav("out/sine.wav", 44100,
//!     quantize_samples::<i16>(make_samples(1.0, 44100, SineWave(440.0)))
//! ).ok().expect("failed");
//! ```
//!
//! See: `examples/simple.rs`

use std::num::{ Float, FloatMath, from_f64 };
use std::mem::size_of;

use music;
use reader;
use wave;

/// Quantizes a `f64` sample into `T`.
pub fn quantize<T>(input: f64) -> T where T: FromPrimitive {
    let quantization_levels = 2.0.powf(size_of::<T>() as f64 * 8.0) - 1.0;
    // Convert from [-1, 1] to take up full quantization range
    from_f64::<T>(input * (quantization_levels / 2.0)).expect("failed to quantize to given type")
}

/// Quantizes a `Vec<f64>` of samples into `Vec<T>`.
///
/// This creates a 16-bit SineWave at 440Hz:
///
/// ```
/// use synthrs::wave::SineWave;
/// use synthrs::synthesizer::{quantize_samples, make_samples};
///
/// quantize_samples::<i16>(make_samples(1.0, 44100, SineWave(440.0)));
/// ```
pub fn quantize_samples<T>(input: Vec<f64>) -> Vec<T> where T: FromPrimitive {
    input.iter().map(|s| { quantize::<T>(*s) }).collect()
}

/// Invokes the waveform function `f` at time `t` to return the amplitude at that time.
pub fn generate<F>(t: f64, f: &F) -> f64 where F: Fn<(f64, ), f64> {
    f.call((t, ))
}

pub fn make_samples<F>(length: f64, sample_rate: uint, waveform: F) -> Vec<f64> where F: Fn<(f64, ), f64> {
    let num_samples = (sample_rate as f64 * length).floor() as uint;
    let mut samples: Vec<f64> = Vec::with_capacity(num_samples);

    for i in range(0u, num_samples) {
        let t = i as f64 / sample_rate as f64;
        samples.push(generate(t, &waveform));
    }

    samples
}

/// Peak normalizes a `Vec<f64>` of samples such that the maximum and minimum amplitudes of the
/// `Vec<f64>` samples are within the range [-1.0, 1.0]
pub fn peak_normalize(samples: Vec<f64>) -> Vec<f64> {
    let peak = samples.iter().fold(0.0f64, |acc, &sample| {
        acc.max(sample)
    });

    samples.iter().map(|&sample| {
        sample / peak
    }).collect()
}

// This is really awful, is there a more elegant way to do this?
// TODO: Make the instrument a parameter (perhaps using an Instrument trait?)
pub fn make_samples_from_midi(sample_rate: uint, filename: &str) -> Vec<f64> {
    let song = reader::read_midi(filename).unwrap();
    let length = (60.0 * song.max_time as f64) / (song.bpm * song.time_unit as f64);

    let mut notes_on_for_ticks: Vec<Vec<(u8, u8, uint)>> = Vec::new();
    for _ in range(0, song.max_time) {
        let notes_on_for_tick: Vec<(u8, u8, uint)> = Vec::new();
        notes_on_for_ticks.push(notes_on_for_tick);
    }

    for track in song.tracks.iter() {
        for i in range(0, track.events.len()) {
            let event = track.events[i];
            if event.event_type == reader::MidiEventType::NoteOn {
                let start_tick = event.time;
                let note = event.value1;
                let velocity = event.value2.unwrap();

                let mut end_tick = song.max_time;
                for j in range(i, track.events.len()) {
                    let event_cursor = track.events[j];

                    // NoteOn with velocity 0 == NoteOff
                    if (event_cursor.event_type == reader::MidiEventType::NoteOff && event_cursor.value1 == note) ||
                       (event_cursor.event_type == reader::MidiEventType::NoteOn && event_cursor.value1 == note && event_cursor.value2.unwrap() == 0) {
                        end_tick = event_cursor.time;
                        break;
                    }
                }

                for tick in range(start_tick, end_tick) {
                    notes_on_for_ticks[tick].push((note as u8, velocity as u8, start_tick));
                }
            }
        }
    }

    let midi_frequency_function = |t: f64| -> f64 {
        let tick = (t * song.bpm * song.time_unit as f64 / 60.0) as uint;
        let mut out = 0.0;

        if tick < notes_on_for_ticks.len() {
            for &(note, velocity, start_tick) in notes_on_for_ticks[tick].iter() {
                let frequency = music::note_midi(440.0, note as uint);
                let loudness = (6.908 * (velocity as f64 / 255.0)).exp() / 1000.0;
                let attack = 0.01;
                let decay = 1.0;
                let sharpness = 0.8;
                let start_t = start_tick as f64 * 60.0 / song.bpm as f64 / song.time_unit as f64;
                let relative_t = t - start_t;
                out += loudness * wave::KarplusStrong(wave::SquareWave(frequency), attack, decay, sharpness, 44100.0)(relative_t)
            }
        }

        out
    };

    let num_samples = (sample_rate as f64 * length).floor() as uint;
    let mut samples: Vec<f64> = Vec::with_capacity(num_samples);

    for i in range(0u, num_samples) {
        let t = i as f64 / sample_rate as f64;
        samples.push(midi_frequency_function(t));
    }

    peak_normalize(samples)
}
<|endoftext|>


<commit_before>use std::io::prelude::*;
use std::io;

struct InfixNotation<'a> {
    text: &'a str,
    index: usize
}

impl<'a> InfixNotation<'a> {
    fn new(string: &'a str) -> InfixNotation<'a> {
        InfixNotation {
            text: string.trim().clone(),
            index: 0
        }
    }
}

impl<'a> Iterator for InfixNotation<'a> {
    type Item = &'a str;
    fn next(&mut self) -> Option<&'a str> {
        let start_pos = self.index;
        if start_pos >= self.text.len() {
            return None;
        }
        let mut end_pos = self.index;
        for block in self.text.chars().skip(start_pos) {
            end_pos += 1;
            match block {
                '+' | '-' | '*' | '/' | '(' | ')' => {
                    end_pos -= 1;
                    break;
                },
                _ => continue
            }
        }
        if end_pos - start_pos == 0 {
            self.index = end_pos + 1;
        } else {
            self.index = end_pos;
        }
        Some(unsafe {
            self.text.slice_unchecked(start_pos, self.index)
        })
    }
}

fn is_digit(block: &str) -> bool {
    block.chars().nth(0).unwrap().is_digit(10)
}

fn get_priority(operator: &str) -> Option<i8> {
    match operator {
        "(" => Some(-1),
        "*" | "/" => Some(1),
        "+" | "-" => Some(2),
        _ => None
    }
}

fn can_pop(op1: &str, stack: &Vec<&str>) -> bool {
    if stack.len() == 0 {
        return false
    }
    let p1 = get_priority(op1).unwrap();
    let last = *stack.last().unwrap();
    let p2 = get_priority(last).unwrap();
    p1 >= 0 && p2 >= 0 && p1 >= p2
}

fn in2rpn(input: &str) -> String {
    let mut result = String::new();
    let it = InfixNotation::new(input);
    let mut func: Vec<&str> = Vec::new();
    for item in it {
        if is_digit(item) {
            result.push_str(&format!("{} ", item));
        } else {
            if item == ")" {
                while func.len() > 0 && *func.last().unwrap() != "(" {
                    result.push_str(&format!("{} ", func.pop().unwrap()));
                }
                func.pop();
            } else {
                while can_pop(item, &func) {
                    result.push_str(&format!("{} ", func.pop().unwrap()));
                }
                func.push(item);
            }
        }
    }
    while let Some(item) = func.pop() {
        result.push_str(&format!("{} ", item));
    }
    result
}

fn main() {
    print!("enter expression: ");
    io::stdout().flush()
        .ok()
        .expect( "[error] Can't flush to stdout!" );
    let mut buffer = String::new();
    io::stdin().read_line(&mut buffer)
        .ok()
        .expect( "[error] Can't read line from stdin!" );
    println!("result: {}", in2rpn(&buffer));
}
<commit_msg>tests and error check<commit_after>use std::io::prelude::*;
use std::io;

struct InfixNotation<'a> {
    text: &'a str,
    index: usize
}

impl<'a> InfixNotation<'a> {
    fn new(string: &'a str) -> InfixNotation<'a> {
        InfixNotation {
            text: string.trim().clone(),
            index: 0
        }
    }
}

impl<'a> Iterator for InfixNotation<'a> {
    type Item = &'a str;
    fn next(&mut self) -> Option<&'a str> {
        let start_pos = self.index;
        if start_pos >= self.text.len() {
            return None;
        }
        let mut end_pos = self.index;
        for block in self.text.chars().skip(start_pos) {
            end_pos += 1;
            match block {
                '+' | '-' | '*' | '/' | '(' | ')' => {
                    end_pos -= 1;
                    break;
                },
                _ => continue
            }
        }
        if end_pos - start_pos == 0 {
            self.index = end_pos + 1;
        } else {
            self.index = end_pos;
        }
        Some(unsafe {
            self.text.slice_unchecked(start_pos, self.index).trim()
        })
    }
}

fn is_digit(block: &str) -> bool {
    block.chars().nth(0).unwrap().is_digit(10)
}

fn get_priority(operator: &str) -> Option<i8> {
    match operator {
        "(" => Some(-1),
        "*" | "/" => Some(1),
        "+" | "-" => Some(2),
        _ => None
    }
}

fn can_pop(op1: &str, stack: &Vec<&str>) -> bool {
    if stack.len() == 0 {
        return false
    }
    let p1 = match get_priority(op1) {
        Some(value) => value,
        None => panic!("[error]: unknown operator '{}'", op1)
    };
    let last = match stack.last() {
        Some(value) => *value,
        None => panic!("[error]: function stack is empty")
    };
    let p2 = match get_priority(last) {
        Some(value) => value,
        None => panic!("[error]: unknown operator '{}'", last)
    };
    p1 >= 0 && p2 >= 0 && p1 >= p2
}

pub fn in2rpn(input: &str) -> String {
    let mut result = String::new();
    let it = InfixNotation::new(input);
    let mut func: Vec<&str> = Vec::new();
    for item in it {
        if is_digit(item) {
            result.push_str(&format!("{} ", item));
        } else {
            if item == ")" {
                while func.len() > 0 && *func.last().unwrap() != "(" {
                    let function = match func.pop() {
                        Some(value) => value,
                        None => panic!("[error]: function stack is empty")
                    };
                    result.push_str(&format!("{} ", function));
                }
                func.pop();
            } else {
                while can_pop(item, &func) {
                    let function = match func.pop() {
                        Some(value) => value,
                        None => panic!("[error]: function stack is empty")
                    };
                    result.push_str(&format!("{} ", function));
                }
                func.push(item);
            }
        }
    }
    while let Some(item) = func.pop() {
        result.push_str(&format!("{} ", item));
    }
    result
}

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

    #[test]
    fn space() {
        assert_eq!("1 2 3 * 4 / +", in2rpn(" 1  + 2   * 3 /  4 ").trim());
    }
    #[test]
    fn bracket() {
        assert_eq!("1 2 3 4 5 - / * +", in2rpn("(1+(2*(3/(4-5))))").trim());
    }
}

fn main() {
    print!("enter expression: ");
    io::stdout().flush()
        .ok()
        .expect( "[error] Can't flush to stdout!" );
    let mut buffer = String::new();
    io::stdin().read_line(&mut buffer)
        .ok()
        .expect( "[error] Can't read line from stdin!" );
    println!("result: {}", in2rpn(&buffer));
}
<|endoftext|>


<commit_before>use ast::{Expr, Literal, UnaryOperator, UnaryExpr, BinaryOperator, BinaryExpr, Grouping};

#[derive(Debug, PartialEq, Clone)]
pub enum Value {
    Nil,
    Boolean(bool),
    Number(f64),
    String(String),
}

impl Value {
    fn is_true(&self) -> bool {
        match self {
            &Value::Nil => false,
            &Value::Boolean(b) => b,
            _ => true,
        }
    }

    fn equals(&self, other: &Value) -> bool {
        match (self, other) {
            (&Value::Nil, &Value::Nil) => true,
            (&Value::Boolean(l), &Value::Boolean(r)) => l == r,
            (&Value::Number(l), &Value::Number(r)) => l == r,
            (&Value::String(ref l), &Value::String(ref r)) => l == r,
            _ => false,
        }
    }
}

#[derive(Debug)]
pub enum RuntimeError {
    UnaryMinusTypeMismatch(Value),
    BinaryOperatorTypeMismatch(BinaryOperator, Value, Value),
}

pub trait Interpret {
    fn interpret(&self) -> Result<Value, RuntimeError>;
}

impl Interpret for Expr {
    fn interpret(&self) -> Result<Value, RuntimeError> {
        match *self {
            Expr::Literal(ref l) => l.interpret(),
            Expr::Unary(ref u) => u.interpret(),
            Expr::Binary(ref b) => b.interpret(),
            Expr::Grouping(ref g) => g.interpret(),
        }
    }
}

impl Interpret for Literal {
    fn interpret(&self) -> Result<Value, RuntimeError> {
        match *self {
            Literal::NilLiteral => Ok(Value::Nil),
            Literal::BoolLiteral(b) => Ok(Value::Boolean(b)),
            Literal::StringLiteral(ref s) => Ok(Value::String(s.clone())),
            Literal::NumberLiteral(n) => Ok(Value::Number(n)),
        }
    }
}

impl Interpret for Grouping {
    fn interpret(&self) -> Result<Value, RuntimeError> {
        self.expr.interpret()
    }
}

impl Interpret for UnaryExpr {
    fn interpret(&self) -> Result<Value, RuntimeError> {
        let value = try!(self.right.interpret());
        match self.operator {
            UnaryOperator::Bang => Ok(Value::Boolean(!value.is_true())),
            UnaryOperator::Minus => {
                match value {
                    Value::Number(n) => Ok(Value::Number(-n)),
                    _ => Err(RuntimeError::UnaryMinusTypeMismatch(value)),
                }
            }
        }
    }
}

impl Interpret for BinaryExpr {
    fn interpret(&self) -> Result<Value, RuntimeError> {
        let left = try!(self.left.interpret());
        let right = try!(self.right.interpret());
        match (&self.operator, &left, &right) {
            (&BinaryOperator::Minus, &Value::Number(l), &Value::Number(r)) => {
                Ok(Value::Number(l - r))
            }
            (&BinaryOperator::Slash, &Value::Number(l), &Value::Number(r)) => {
                Ok(Value::Number(l / r))
            }
            (&BinaryOperator::Star, &Value::Number(l), &Value::Number(r)) => {
                Ok(Value::Number(l * r))
            }
            (&BinaryOperator::Plus, &Value::Number(l), &Value::Number(r)) => {
                Ok(Value::Number(l + r))
            }
            (&BinaryOperator::Plus, &Value::String(ref l), &Value::String(ref r)) => {
                let mut result = String::new();
                result.push_str(l);
                result.push_str(r);
                Ok(Value::String(result))
            }
            (&BinaryOperator::Greater, &Value::Number(l), &Value::Number(r)) => {
                Ok(Value::Boolean(l > r))
            }
            (&BinaryOperator::Less, &Value::Number(l), &Value::Number(r)) => {
                Ok(Value::Boolean(l < r))
            }
            (&BinaryOperator::GreaterEqual, &Value::Number(l), &Value::Number(r)) => {
                Ok(Value::Boolean(l >= r))
            }
            (&BinaryOperator::LessEqual, &Value::Number(l), &Value::Number(r)) => {
                Ok(Value::Boolean(l <= r))
            }
            (&BinaryOperator::NotEqual, l, r) => Ok(Value::Boolean(!l.equals(r))),
            (&BinaryOperator::Equal, l, r) => Ok(Value::Boolean(l.equals(r))),
            _ => {
                Err(RuntimeError::BinaryOperatorTypeMismatch(self.operator,
                                                             left.clone(),
                                                             right.clone()))
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use ast::*;
    use interpreter::{Interpret, Value};

    #[test]
    fn literal() {
        let string = String::from("abc");
        let expr = Expr::Literal(Literal::StringLiteral(string.clone()));
        assert_eq!(Value::String(string), expr.interpret().unwrap());
    }

    #[test]
    fn grouping() {
        let expr = Grouping { expr: Expr::Literal(Literal::NumberLiteral(45.67f64)) };
        assert_eq!(Value::Number(45.67f64), expr.interpret().unwrap());
    }

    #[test]
    fn unary() {
        let expr = UnaryExpr {
            operator: UnaryOperator::Bang,
            right: Expr::Literal(Literal::BoolLiteral(false)),
        };
        assert_eq!(Value::Boolean(true), expr.interpret().unwrap());
    }

    #[test]
    fn binary() {
        let expr = BinaryExpr {
            operator: BinaryOperator::Plus,
            left: Expr::Literal(Literal::NumberLiteral(1.0f64)),
            right: Expr::Literal(Literal::NumberLiteral(1.0f64)),
        };
        assert_eq!(Value::Number(2.0f64), expr.interpret().unwrap());
    }

}
<commit_msg>[clippy] removed equals, it was equivalent to what PartialEq derives<commit_after>use ast::{Expr, Literal, UnaryOperator, UnaryExpr, BinaryOperator, BinaryExpr, Grouping};

#[derive(Debug, PartialEq, Clone)]
pub enum Value {
    Nil,
    Boolean(bool),
    Number(f64),
    String(String),
}

impl Value {
    fn is_true(&self) -> bool {
        match self {
            &Value::Nil => false,
            &Value::Boolean(b) => b,
            _ => true,
        }
    }
}

#[derive(Debug)]
pub enum RuntimeError {
    UnaryMinusTypeMismatch(Value),
    BinaryOperatorTypeMismatch(BinaryOperator, Value, Value),
}

pub trait Interpret {
    fn interpret(&self) -> Result<Value, RuntimeError>;
}

impl Interpret for Expr {
    fn interpret(&self) -> Result<Value, RuntimeError> {
        match *self {
            Expr::Literal(ref l) => l.interpret(),
            Expr::Unary(ref u) => u.interpret(),
            Expr::Binary(ref b) => b.interpret(),
            Expr::Grouping(ref g) => g.interpret(),
        }
    }
}

impl Interpret for Literal {
    fn interpret(&self) -> Result<Value, RuntimeError> {
        match *self {
            Literal::NilLiteral => Ok(Value::Nil),
            Literal::BoolLiteral(b) => Ok(Value::Boolean(b)),
            Literal::StringLiteral(ref s) => Ok(Value::String(s.clone())),
            Literal::NumberLiteral(n) => Ok(Value::Number(n)),
        }
    }
}

impl Interpret for Grouping {
    fn interpret(&self) -> Result<Value, RuntimeError> {
        self.expr.interpret()
    }
}

impl Interpret for UnaryExpr {
    fn interpret(&self) -> Result<Value, RuntimeError> {
        let value = try!(self.right.interpret());
        match self.operator {
            UnaryOperator::Bang => Ok(Value::Boolean(!value.is_true())),
            UnaryOperator::Minus => {
                match value {
                    Value::Number(n) => Ok(Value::Number(-n)),
                    _ => Err(RuntimeError::UnaryMinusTypeMismatch(value)),
                }
            }
        }
    }
}

impl Interpret for BinaryExpr {
    fn interpret(&self) -> Result<Value, RuntimeError> {
        let left = try!(self.left.interpret());
        let right = try!(self.right.interpret());
        match (&self.operator, &left, &right) {
            (&BinaryOperator::Minus, &Value::Number(l), &Value::Number(r)) => {
                Ok(Value::Number(l - r))
            }
            (&BinaryOperator::Slash, &Value::Number(l), &Value::Number(r)) => {
                Ok(Value::Number(l / r))
            }
            (&BinaryOperator::Star, &Value::Number(l), &Value::Number(r)) => {
                Ok(Value::Number(l * r))
            }
            (&BinaryOperator::Plus, &Value::Number(l), &Value::Number(r)) => {
                Ok(Value::Number(l + r))
            }
            (&BinaryOperator::Plus, &Value::String(ref l), &Value::String(ref r)) => {
                let mut result = String::new();
                result.push_str(l);
                result.push_str(r);
                Ok(Value::String(result))
            }
            (&BinaryOperator::Greater, &Value::Number(l), &Value::Number(r)) => {
                Ok(Value::Boolean(l > r))
            }
            (&BinaryOperator::Less, &Value::Number(l), &Value::Number(r)) => {
                Ok(Value::Boolean(l < r))
            }
            (&BinaryOperator::GreaterEqual, &Value::Number(l), &Value::Number(r)) => {
                Ok(Value::Boolean(l >= r))
            }
            (&BinaryOperator::LessEqual, &Value::Number(l), &Value::Number(r)) => {
                Ok(Value::Boolean(l <= r))
            }
            (&BinaryOperator::NotEqual, l, r) => Ok(Value::Boolean(l != r)),
            (&BinaryOperator::Equal, l, r) => Ok(Value::Boolean(l == r)),
            _ => {
                Err(RuntimeError::BinaryOperatorTypeMismatch(self.operator,
                                                             left.clone(),
                                                             right.clone()))
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use ast::*;
    use interpreter::{Interpret, Value};

    #[test]
    fn literal() {
        let string = String::from("abc");
        let expr = Expr::Literal(Literal::StringLiteral(string.clone()));
        assert_eq!(Value::String(string), expr.interpret().unwrap());
    }

    #[test]
    fn grouping() {
        let expr = Grouping { expr: Expr::Literal(Literal::NumberLiteral(45.67f64)) };
        assert_eq!(Value::Number(45.67f64), expr.interpret().unwrap());
    }

    #[test]
    fn unary() {
        let expr = UnaryExpr {
            operator: UnaryOperator::Bang,
            right: Expr::Literal(Literal::BoolLiteral(false)),
        };
        assert_eq!(Value::Boolean(true), expr.interpret().unwrap());
    }

    #[test]
    fn binary() {
        let expr = BinaryExpr {
            operator: BinaryOperator::Plus,
            left: Expr::Literal(Literal::NumberLiteral(1.0f64)),
            right: Expr::Literal(Literal::NumberLiteral(1.0f64)),
        };
        assert_eq!(Value::Number(2.0f64), expr.interpret().unwrap());
    }

}
<|endoftext|>


<commit_before>use token::{Token, Kind, Tokenizer};
use ast::{Node, Operation, Var};
use parser::Parser;
use std::collections::HashMap;

// # Interpreter
//
// Represents the interpreter that is responsible for interpret
// the Abstracted Sintax Tree generated by the Parser

pub struct Interpreter {
    pub symbol_table: HashMap<String, Token>,
    pub imutable_table: HashMap<String, Token>
}
impl Interpreter {
    pub fn new() -> Self {
        Interpreter {
            symbol_table: HashMap::new(),
            imutable_table: HashMap::new()
        }
    }
    // eval_tree
    //
    // It visits each node evaluating the binary operations returning the
    // result as String
    //
    // Example:
    //   The expression 3 * 5 + 5 will produce the follow tree and the result 20.
    //   It evaluates the left side first and applies a post-order calc
    //
    //         +-+
    //         |-|
    //     +---------+
    //     |         |
    //     v         v
    //    +-+       +-+
    //    |*|       |5|
    //  +-----+     +-+
    //  |     |
    //  v     v
    // ---   ---
    // |3|   |5|
    // +-+   +-+
    pub fn eval(&mut self, tree: Node) -> String {
        self.eval_tree(tree).value
    }

    pub fn eval_tree(&mut self, tree: Node) -> Token {
        let Node{operation, token} = tree;
        match *operation.clone() {

            Operation::IfElse(conditional, lnode, rnode) => {
                let condition = self.eval_tree(conditional);

                if truthy(condition) {
                    self.eval_tree(lnode)
                } else {
                    self.eval_tree(rnode)
                }
            },

            Operation::Binary(lnode, operator, rnode) => {
                binary_operation(
                    self.eval_tree(lnode), operator, self.eval_tree(rnode)
                )
            },

            Operation::Comparison(lnode, operator, rnode) =>
                binary_comparison( self.eval_tree(lnode), operator, self.eval_tree(rnode)),

            Operation::DefineImut(l, r) => {
                let value = self.eval_tree(r);
                let Token{ kind, value: name } = l.token;

                self.imutable_table.insert(name, value.clone());
                value
            },

            Operation::DefineVar(l, r) => {
                let value = self.eval_tree(r);
                let Token{ kind, value: name } = l.token;

                self.symbol_table.insert(name, value.clone());
                value
            },

            Operation::ReAssign(l, r) => {
                let value = self.eval_tree(r);
                let Token{ value: name, ..} = l.token;

                if self.imutable_table.contains_key(&*name) {
                    panic!("Value error: invalid assign imutable var: {}", name)
                }

                if !self.symbol_table.contains_key(&*name) {
                    panic!("Value error: used before declared var: {}", name)
                }

                self.symbol_table.insert(name, value.clone());
                value
            },

            Operation::NegUnary(r) => {
                unary_operation("-", self.eval_tree(r))
            },

            Operation::Return(node) => self.eval_tree(node),

            Operation::Block(statements) => {
                statements.iter()
                    .map(|n| self.eval_tree(n.clone()))
                    .fold(token, |_, t| t)
            },

            Operation::Loop(conditional, block) => {
                let mut condition = self.eval_tree(conditional.clone());

                while truthy(condition) {
                    let _ = self.eval_tree(block.clone());
                    condition = self.eval_tree(conditional.clone());
                }

                return Token::build(Kind::Empty, String::from("nil"))
            },

            Operation::Identifier(name) =>
                match self.imutable_table.get(&*name) {
                    Some(value) => return value.clone(),
                    None =>
                        match self.symbol_table.get(&*name) {
                            Some(value) => return value.clone(),
                            None =>
                                panic!("Interpreter error: undeclared variable")
                        }
                },

            _ => token
        }
    }
}

// unary_operation
// Resolves the unary operations Example: --1 == 1, 1++-1==0
fn unary_operation(operator: &str, operand: Token) -> Token {
    let ioperand = if let Ok(val) = operand.value.parse::<i32>() { val } else {
        panic!("Sintax error: invalid unary operand {:?}", operand)
    };
    let result = match operator.as_ref() {
        "+" => ioperand,
        "-" => -(ioperand),
        _ => panic!("Sintax error: invalid unary operator {:?}", operator)
    };

    Token::build(operand.kind, result.to_string())
}

// binary_operation
// Resolve binary expression for the given left, operator and right operand
fn binary_operation(left: Token, operator: String, right: Token) -> Token {
    let operleft = if let Ok(val) = left.value.parse::<i32>() { val } else {
        panic!("Sintax error: invalid operand: {:?}", left)
    };
    let operright = if let Ok(val) = right.value.parse::<i32>() { val } else {
        panic!("Sintax error: invalid operand: {:?}", right)
    };
    let result = match operator.as_ref() {
        "+" => operleft + operright,
        "-" => operleft - operright,
        "*" => operleft * operright,
        "/" => operleft / operright,
        _ => panic!("Sintax error: invalid operator {:?}", operator)
    };

    Token::build(left.kind, result.to_string())
}

// binary_operation
// Resolve binary expression for the given left, operator and right operand
fn binary_comparison(left: Token, operator: String, right: Token) -> Token {
    let bleft = left.value.replace("true","1").replace("false","0");
    let bright = right.value.replace("true","1").replace("false","0");

    let operleft = if let Ok(val) = bleft.parse::<i32>() { val } else {
        panic!("Sintax error: invalid operand: {:?}", left)
    };
    let operright = if let Ok(val) = bright.parse::<i32>() { val } else {
        panic!("Sintax error: invalid operand: {:?}", right)
    };

    let result = match operator.as_ref() {
        "==" => operleft == operright,
        "!=" => operleft != operright,
        "<" => operleft < operright,
        ">" => operleft > operright,
        "or"|"||" => operleft == 1 || operright == 1,
        "and"|"&&" => operleft == 1 && operright == 1,
        _ => panic!("Sintax error: invalid operator {:?}", operator)
    };

    Token::build(left.kind, result.to_string())
}

fn truthy(condition: Token) -> bool {
    binary_comparison(
        condition,
        String::from("=="),
        Token::build(Kind::Comparison, String::from("true"))).value == "true"
}

#[test]
fn it_eval_tree_leaf() {
    let token = Token::build(Kind::Integer, String::from("10"));
    let leaf = Node::constant(token);

    assert_eq!("10", Interpreter::new().eval(leaf))
}

#[test]
fn it_eval_the_node_binary_operation() {
    // 3+5
    let left = Node::constant(Token::build(Kind::Integer, String::from("3")));
    let operator = Token::build(Kind::Operator, String::from("+"));
    let right = Node::constant(Token::build(Kind::Integer, String::from("5")));
    let node = Node::binary(left, operator, right);

    assert_eq!("8", Interpreter::new().eval(node))
}

#[test]
fn it_eval_complex_tree() {
    // 5+5*3
    let left = Node::constant(Token::build(Kind::Integer, String::from("3")));
    let operator = Token::build(Kind::Operator, String::from("*"));
    let right = Node::constant(Token::build(Kind::Integer, String::from("5")));
    let plusnode = Node::binary(left, operator, right);

    let operator = Token::build(Kind::Operator, String::from("+"));
    let sumright = Node::constant(Token::build(Kind::Integer, String::from("5")));
    let sumnode = Node::binary(plusnode, operator, sumright);

    assert_eq!("20", Interpreter::new().eval(sumnode))
}

#[test]
fn it_eval_unary_operations() {
    // 2 -- 2
    let rnode = Node::constant(Token::build(Kind::Integer, String::from("2")));
    let negative_op = Token::build(Kind::Operator, String::from("-"));
    let unarynode = Node::unary(negative_op, rnode);

    let operator = Token::build(Kind::Operator, String::from("-"));
    let left = Node::constant(Token::build(Kind::Integer, String::from("2")));
    let sumnode = Node::binary(left, operator, unarynode);

    assert_eq!("4", Interpreter::new().eval(sumnode))
}

#[test]
fn it_sums() {
    let text = "5+1";
    let tokenizer = Tokenizer::new(String::from(text));
    let mut parser = Parser::new(tokenizer);

    assert_eq!("6", Interpreter::new().eval(parser.parse()));
}

#[test]
fn it_substract() {
    let text = "5-1";
    let tokenizer = Tokenizer::new(String::from(text));
    let mut parser = Parser::new(tokenizer);

    assert_eq!("4", Interpreter::new().eval(parser.parse()));
}

#[test]
fn it_multiplies() {
    let text = "5*2";
    let tokenizer = Tokenizer::new(String::from(text));
    let mut parser = Parser::new(tokenizer);

    assert_eq!("10", Interpreter::new().eval(parser.parse()));
}

#[test]
fn it_divide() {
    let text = "4/2";
    let tokenizer = Tokenizer::new(String::from(text));
    let mut parser = Parser::new(tokenizer);

    assert_eq!("2", Interpreter::new().eval(parser.parse()));
}

#[test]
fn it_accepts_multiples_operation() {
    let text = "10+5-4-1";
    let tokenizer = Tokenizer::new(String::from(text));
    let mut parser = Parser::new(tokenizer);

    assert_eq!("10", Interpreter::new().eval(parser.parse()));
}

#[test]
fn it_respect_precedence() {
    let text = "1+1*2";
    let tokenizer = Tokenizer::new(String::from(text));
    let mut parser = Parser::new(tokenizer);

    assert_eq!("3", Interpreter::new().eval(parser.parse()));
}

#[test]
fn it_respects_grouped_expression() {
    let text = "4+(1+(1+1)*2)+1";
    let tokenizer = Tokenizer::new(String::from(text));
    let mut parser = Parser::new(tokenizer);

    assert_eq!("10", Interpreter::new().eval(parser.parse()));
}

#[test]
fn it_accept_unary_operations() {
    let text = "(4+-1)--2";
    let tokenizer = Tokenizer::new(String::from(text));
    let mut parser = Parser::new(tokenizer);

    assert_eq!("5", Interpreter::new().eval(parser.parse()));
}

#[test]
fn it_accept_binary_comparison() {
    let text = "4 == 2";
    let tokenizer = Tokenizer::new(String::from(text));
    let mut parser = Parser::new(tokenizer);

    assert_eq!("false", Interpreter::new().eval(parser.parse()));
}

#[test]
fn it_accept_composed_binary_comparison() {
    let text = "1 > 1 or 2 == 2 and 3 != 3";
    let tokenizer = Tokenizer::new(String::from(text));
    let mut parser = Parser::new(tokenizer);

    assert_eq!("false", Interpreter::new().eval(parser.parse()));
}

#[test]
fn it_eval_block_assigning_vars_to_symbol_table() {
    let text = "begin mut x = 10 end";
    let tokenizer = Tokenizer::new(String::from(text));
    let mut parser = Parser::new(tokenizer);
    let mut interpreter = Interpreter::new();
    let _ = interpreter.eval(parser.parse());

    assert_eq!("10", interpreter.symbol_table["x"].value);
}

#[test]
fn it_eval_block_retrieve_vars_from_symbol_table() {
    let text = "begin imut x = 10; mut y = x + 5 end";
    let tokenizer = Tokenizer::new(String::from(text));
    let mut parser = Parser::new(tokenizer);
    let mut interpreter = Interpreter::new();
    let _ = interpreter.eval(parser.parse());

    assert_eq!("10", interpreter.imutable_table["x"].value);
    assert_eq!("15", interpreter.symbol_table["y"].value);
}

<commit_msg>refactor: returning strings<commit_after>use token::{Token, Kind, Tokenizer};
use ast::{Node, Operation, Var};
use parser::Parser;
use std::collections::HashMap;

// # Interpreter
//
// Represents the interpreter that is responsible for interpret
// the Abstracted Sintax Tree generated by the Parser

pub struct Interpreter {
    pub symbol_table: HashMap<String, String>,
    pub imutable_table: HashMap<String, String>
}
impl Interpreter {
    pub fn new() -> Self {
        Interpreter {
            symbol_table: HashMap::new(),
            imutable_table: HashMap::new()
        }
    }
    // eval_tree
    //
    // It visits each node evaluating the binary operations returning the
    // result as String
    //
    // Example:
    //   The expression 3 * 5 + 5 will produce the follow tree and the result 20.
    //   It evaluates the left side first and applies a post-order calc
    //
    //         +-+
    //         |-|
    //     +---------+
    //     |         |
    //     v         v
    //    +-+       +-+
    //    |*|       |5|
    //  +-----+     +-+
    //  |     |
    //  v     v
    // ---   ---
    // |3|   |5|
    // +-+   +-+
    pub fn eval(&mut self, tree: Node) -> String {
        self.eval_tree(tree)
    }

    pub fn eval_tree(&mut self, tree: Node) -> String {
        let Node{operation, token} = tree;
        match *operation.clone() {
            Operation::IfElse(conditional, lnode, rnode) => {
                let condition = self.eval_tree(conditional);

                if truthy(condition) {
                    self.eval_tree(lnode)
                } else {
                    self.eval_tree(rnode)
                }
            },

            Operation::Binary(lnode, operator, rnode) => {
                binary_operation(
                    self.eval_tree(lnode), operator, self.eval_tree(rnode)
                )
            },

            Operation::Comparison(lnode, operator, rnode) =>
                binary_comparison(
                    self.eval_tree(lnode), operator, self.eval_tree(rnode)),

            Operation::DefineImut(l, r) => {
                let value = self.eval_tree(r);
                let Token{ kind, value: name } = l.token;

                self.imutable_table.insert(name, value.clone());
                value
            },

            Operation::DefineVar(l, r) => {
                let value = self.eval_tree(r);
                let Token{ kind, value: name } = l.token;

                self.symbol_table.insert(name, value.clone());
                value
            },

            Operation::ReAssign(l, r) => {
                let value = self.eval_tree(r);
                let Token{ value: name, ..} = l.token;

                if self.imutable_table.contains_key(&*name) {
                    panic!("Value error: invalid assign imutable var: {}", name)
                }

                if !self.symbol_table.contains_key(&*name) {
                    panic!("Value error: used before declared var: {}", name)
                }

                self.symbol_table.insert(name, value.clone());
                value
            },

            Operation::NegUnary(r) => {
                unary_operation("-", self.eval_tree(r))
            },

            Operation::Return(node) => self.eval_tree(node),

            Operation::Block(statements) => {
                statements.iter()
                    .map(|n| self.eval_tree(n.clone()))
                    .fold(String::new(), |_, t| t)
            },

            Operation::Loop(conditional, block) => {
                let mut condition = self.eval_tree(conditional.clone());

                while truthy(condition) {
                    let _ = self.eval_tree(block.clone());
                    condition = self.eval_tree(conditional.clone());
                }

                return String::from("nil")
            },

            Operation::Identifier(name) =>
                match self.imutable_table.get(&*name) {
                    Some(value) => return value.clone(),
                    None =>
                        match self.symbol_table.get(&*name) {
                            Some(value) => return value.clone(),
                            None =>
                                panic!("Interpreter error: undeclared variable")
                        }
                },

            _ => token.value
        }
    }
}

// unary_operation
// Resolves the unary operations Example: --1 == 1, 1++-1==0
fn unary_operation(operator: &str, operand: String) -> String {
    let ioperand = if let Ok(val) = operand.parse::<i32>() { val } else {
        panic!("Sintax error: invalid unary operand {:?}", operand)
    };
    let result = match operator.as_ref() {
        "+" => ioperand,
        "-" => -(ioperand),
        _ => panic!("Sintax error: invalid unary operator {:?}", operator)
    };

    result.to_string()
}

// binary_operation
// Resolve binary expression for the given left, operator and right operand
fn binary_operation(left: String, operator: String, right: String) -> String {
    let operleft = if let Ok(val) = left.parse::<i32>() { val } else {
        panic!("Sintax error: invalid operand: {:?}", left)
    };
    let operright = if let Ok(val) = right.parse::<i32>() { val } else {
        panic!("Sintax error: invalid operand: {:?}", right)
    };
    let result = match operator.as_ref() {
        "+" => operleft + operright,
        "-" => operleft - operright,
        "*" => operleft * operright,
        "/" => operleft / operright,
        _ => panic!("Sintax error: invalid operator {:?}", operator)
    };

    result.to_string()
}

// binary_operation
// Resolve binary expression for the given left, operator and right operand
fn binary_comparison(left: String, operator: String, right: String) -> String {
    let bleft = left.replace("true","1").replace("false","0");
    let bright = right.replace("true","1").replace("false","0");

    let operleft = if let Ok(val) = bleft.parse::<i32>() { val } else {
        panic!("Sintax error: invalid operand: {:?}", left)
    };
    let operright = if let Ok(val) = bright.parse::<i32>() { val } else {
        panic!("Sintax error: invalid operand: {:?}", right)
    };

    let result = match operator.as_ref() {
        "==" => operleft == operright,
        "!=" => operleft != operright,
        "<" => operleft < operright,
        ">" => operleft > operright,
        "or"|"||" => operleft == 1 || operright == 1,
        "and"|"&&" => operleft == 1 && operright == 1,
        _ => panic!("Sintax error: invalid operator {:?}", operator)
    };

    result.to_string()
}

fn truthy(condition: String) -> bool {
    binary_comparison(
        condition,
        String::from("=="),
        String::from("true")) == "true"
}

#[test]
fn it_eval_tree_leaf() {
    let token = Token::build(Kind::Integer, String::from("10"));
    let leaf = Node::constant(token);

    assert_eq!("10", Interpreter::new().eval(leaf))
}

#[test]
fn it_eval_the_node_binary_operation() {
    // 3+5
    let left = Node::constant(Token::build(Kind::Integer, String::from("3")));
    let operator = Token::build(Kind::Operator, String::from("+"));
    let right = Node::constant(Token::build(Kind::Integer, String::from("5")));
    let node = Node::binary(left, operator, right);

    assert_eq!("8", Interpreter::new().eval(node))
}

#[test]
fn it_eval_complex_tree() {
    // 5+5*3
    let left = Node::constant(Token::build(Kind::Integer, String::from("3")));
    let operator = Token::build(Kind::Operator, String::from("*"));
    let right = Node::constant(Token::build(Kind::Integer, String::from("5")));
    let plusnode = Node::binary(left, operator, right);

    let operator = Token::build(Kind::Operator, String::from("+"));
    let sumright = Node::constant(Token::build(Kind::Integer, String::from("5")));
    let sumnode = Node::binary(plusnode, operator, sumright);

    assert_eq!("20", Interpreter::new().eval(sumnode))
}

#[test]
fn it_eval_unary_operations() {
    // 2 -- 2
    let rnode = Node::constant(Token::build(Kind::Integer, String::from("2")));
    let negative_op = Token::build(Kind::Operator, String::from("-"));
    let unarynode = Node::unary(negative_op, rnode);

    let operator = Token::build(Kind::Operator, String::from("-"));
    let left = Node::constant(Token::build(Kind::Integer, String::from("2")));
    let sumnode = Node::binary(left, operator, unarynode);

    assert_eq!("4", Interpreter::new().eval(sumnode))
}

#[test]
fn it_sums() {
    let text = "5+1";
    let tokenizer = Tokenizer::new(String::from(text));
    let mut parser = Parser::new(tokenizer);

    assert_eq!("6", Interpreter::new().eval(parser.parse()));
}

#[test]
fn it_substract() {
    let text = "5-1";
    let tokenizer = Tokenizer::new(String::from(text));
    let mut parser = Parser::new(tokenizer);

    assert_eq!("4", Interpreter::new().eval(parser.parse()));
}

#[test]
fn it_multiplies() {
    let text = "5*2";
    let tokenizer = Tokenizer::new(String::from(text));
    let mut parser = Parser::new(tokenizer);

    assert_eq!("10", Interpreter::new().eval(parser.parse()));
}

#[test]
fn it_divide() {
    let text = "4/2";
    let tokenizer = Tokenizer::new(String::from(text));
    let mut parser = Parser::new(tokenizer);

    assert_eq!("2", Interpreter::new().eval(parser.parse()));
}

#[test]
fn it_accepts_multiples_operation() {
    let text = "10+5-4-1";
    let tokenizer = Tokenizer::new(String::from(text));
    let mut parser = Parser::new(tokenizer);

    assert_eq!("10", Interpreter::new().eval(parser.parse()));
}

#[test]
fn it_respect_precedence() {
    let text = "1+1*2";
    let tokenizer = Tokenizer::new(String::from(text));
    let mut parser = Parser::new(tokenizer);

    assert_eq!("3", Interpreter::new().eval(parser.parse()));
}

#[test]
fn it_respects_grouped_expression() {
    let text = "4+(1+(1+1)*2)+1";
    let tokenizer = Tokenizer::new(String::from(text));
    let mut parser = Parser::new(tokenizer);

    assert_eq!("10", Interpreter::new().eval(parser.parse()));
}

#[test]
fn it_accept_unary_operations() {
    let text = "(4+-1)--2";
    let tokenizer = Tokenizer::new(String::from(text));
    let mut parser = Parser::new(tokenizer);

    assert_eq!("5", Interpreter::new().eval(parser.parse()));
}

#[test]
fn it_accept_binary_comparison() {
    let text = "4 == 2";
    let tokenizer = Tokenizer::new(String::from(text));
    let mut parser = Parser::new(tokenizer);

    assert_eq!("false", Interpreter::new().eval(parser.parse()));
}

#[test]
fn it_accept_composed_binary_comparison() {
    let text = "1 > 1 or 2 == 2 and 3 != 3";
    let tokenizer = Tokenizer::new(String::from(text));
    let mut parser = Parser::new(tokenizer);

    assert_eq!("false", Interpreter::new().eval(parser.parse()));
}

#[test]
fn it_eval_block_assigning_vars_to_symbol_table() {
    let text = "begin mut x = 10 end";
    let tokenizer = Tokenizer::new(String::from(text));
    let mut parser = Parser::new(tokenizer);
    let mut interpreter = Interpreter::new();
    let _ = interpreter.eval(parser.parse());

    assert_eq!("10", interpreter.symbol_table["x"]);
}

#[test]
fn it_eval_block_retrieve_vars_from_symbol_table() {
    let text = "begin imut x = 10; mut y = x + 5 end";
    let tokenizer = Tokenizer::new(String::from(text));
    let mut parser = Parser::new(tokenizer);
    let mut interpreter = Interpreter::new();
    let _ = interpreter.eval(parser.parse());

    assert_eq!("10", interpreter.imutable_table["x"]);
    assert_eq!("15", interpreter.symbol_table["y"]);
}

<|endoftext|>


<commit_before>//! This module provides the [CommentWriter] which makes it possible
//! to add comments to the written cranelift ir.
//!
//! # Example
//!
//! ```clif
//! test compile
//! target x86_64
//!
//! function u0:0(i64, i64, i64) system_v {
//! ; symbol _ZN119_$LT$example..IsNotEmpty$u20$as$u20$mini_core..FnOnce$LT$$LP$$RF$$u27$a$u20$$RF$$u27$b$u20$$u5b$u16$u5d$$C$$RP$$GT$$GT$9call_once17he85059d5e6a760a0E
//! ; instance Instance { def: Item(DefId(0/0:29 ~ example[8787]::{{impl}}[0]::call_once[0])), substs: [ReErased, ReErased] }
//! ; sig ([IsNotEmpty, (&&[u16],)]; c_variadic: false)->(u8, u8)
//!
//! ; ssa {_2: NOT_SSA, _4: NOT_SSA, _0: NOT_SSA, _3: (empty), _1: NOT_SSA}
//! ; msg   loc.idx    param    pass mode            ssa flags  ty
//! ; ret    _0      = v0       ByRef                NOT_SSA    (u8, u8)
//! ; arg    _1      = v1       ByRef                NOT_SSA    IsNotEmpty
//! ; arg    _2.0    = v2       ByVal(types::I64)    NOT_SSA    &&[u16]
//!
//!     ss0 = explicit_slot 0 ; _1: IsNotEmpty size=0 align=1,8
//!     ss1 = explicit_slot 8 ; _2: (&&[u16],) size=8 align=8,8
//!     ss2 = explicit_slot 8 ; _4: (&&[u16],) size=8 align=8,8
//!     sig0 = (i64, i64, i64) system_v
//!     sig1 = (i64, i64, i64) system_v
//!     fn0 = colocated u0:6 sig1 ; Instance { def: Item(DefId(0/0:31 ~ example[8787]::{{impl}}[1]::call_mut[0])), substs: [ReErased, ReErased] }
//!
//! block0(v0: i64, v1: i64, v2: i64):
//!     v3 = stack_addr.i64 ss0
//!     v4 = stack_addr.i64 ss1
//!     store v2, v4
//!     v5 = stack_addr.i64 ss2
//!     jump block1
//!
//! block1:
//!     nop
//! ; _3 = &mut _1
//! ; _4 = _2
//!     v6 = load.i64 v4
//!     store v6, v5
//! ;
//! ; _0 = const mini_core::FnMut::call_mut(move _3, move _4)
//!     v7 = load.i64 v5
//!     call fn0(v0, v3, v7)
//!     jump block2
//!
//! block2:
//!     nop
//! ;
//! ; return
//!     return
//! }
//! ```

use std::fmt;

use cranelift_codegen::{
    entity::SecondaryMap,
    ir::{entities::AnyEntity, function::DisplayFunctionAnnotations},
    write::{FuncWriter, PlainWriter},
};

use rustc_session::config::OutputType;

use crate::prelude::*;

#[derive(Debug)]
pub(crate) struct CommentWriter {
    global_comments: Vec<String>,
    entity_comments: FxHashMap<AnyEntity, String>,
}

impl CommentWriter {
    pub(crate) fn new<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> Self {
        let global_comments = if cfg!(debug_assertions) {
            vec![
                format!("symbol {}", tcx.symbol_name(instance).name),
                format!("instance {:?}", instance),
                format!(
                    "sig {:?}",
                    tcx.normalize_erasing_late_bound_regions(
                        ParamEnv::reveal_all(),
                        &crate::abi::fn_sig_for_fn_abi(tcx, instance)
                    )
                ),
                String::new(),
            ]
        } else {
            vec![]
        };

        CommentWriter {
            global_comments,
            entity_comments: FxHashMap::default(),
        }
    }
}

#[cfg(debug_assertions)]
impl CommentWriter {
    pub(crate) fn add_global_comment<S: Into<String>>(&mut self, comment: S) {
        self.global_comments.push(comment.into());
    }

    pub(crate) fn add_comment<S: Into<String> + AsRef<str>, E: Into<AnyEntity>>(
        &mut self,
        entity: E,
        comment: S,
    ) {
        use std::collections::hash_map::Entry;
        match self.entity_comments.entry(entity.into()) {
            Entry::Occupied(mut occ) => {
                occ.get_mut().push('\n');
                occ.get_mut().push_str(comment.as_ref());
            }
            Entry::Vacant(vac) => {
                vac.insert(comment.into());
            }
        }
    }
}

impl FuncWriter for &'_ CommentWriter {
    fn write_preamble(
        &mut self,
        w: &mut dyn fmt::Write,
        func: &Function,
        reg_info: Option<&isa::RegInfo>,
    ) -> Result<bool, fmt::Error> {
        for comment in &self.global_comments {
            if !comment.is_empty() {
                writeln!(w, "; {}", comment)?;
            } else {
                writeln!(w, "")?;
            }
        }
        if !self.global_comments.is_empty() {
            writeln!(w, "")?;
        }

        self.super_preamble(w, func, reg_info)
    }

    fn write_entity_definition(
        &mut self,
        w: &mut dyn fmt::Write,
        _func: &Function,
        entity: AnyEntity,
        value: &dyn fmt::Display,
    ) -> fmt::Result {
        write!(w, "    {} = {}", entity, value)?;

        if let Some(comment) = self.entity_comments.get(&entity) {
            writeln!(w, " ; {}", comment.replace('\n', "\n; "))
        } else {
            writeln!(w, "")
        }
    }

    fn write_block_header(
        &mut self,
        w: &mut dyn fmt::Write,
        func: &Function,
        isa: Option<&dyn isa::TargetIsa>,
        block: Block,
        indent: usize,
    ) -> fmt::Result {
        PlainWriter.write_block_header(w, func, isa, block, indent)
    }

    fn write_instruction(
        &mut self,
        w: &mut dyn fmt::Write,
        func: &Function,
        aliases: &SecondaryMap<Value, Vec<Value>>,
        isa: Option<&dyn isa::TargetIsa>,
        inst: Inst,
        indent: usize,
    ) -> fmt::Result {
        PlainWriter.write_instruction(w, func, aliases, isa, inst, indent)?;
        if let Some(comment) = self.entity_comments.get(&inst.into()) {
            writeln!(w, "; {}", comment.replace('\n', "\n; "))?;
        }
        Ok(())
    }
}

#[cfg(debug_assertions)]
impl<M: Module> FunctionCx<'_, '_, M> {
    pub(crate) fn add_global_comment<S: Into<String>>(&mut self, comment: S) {
        self.clif_comments.add_global_comment(comment);
    }

    pub(crate) fn add_comment<S: Into<String> + AsRef<str>, E: Into<AnyEntity>>(
        &mut self,
        entity: E,
        comment: S,
    ) {
        self.clif_comments.add_comment(entity, comment);
    }
}

pub(crate) fn write_clif_file<'tcx>(
    tcx: TyCtxt<'tcx>,
    postfix: &str,
    isa: Option<&dyn cranelift_codegen::isa::TargetIsa>,
    instance: Instance<'tcx>,
    context: &cranelift_codegen::Context,
    mut clif_comments: &CommentWriter,
) {
    use std::io::Write;

    if !cfg!(debug_assertions)
        && !tcx
            .sess
            .opts
            .output_types
            .contains_key(&OutputType::LlvmAssembly)
    {
        return;
    }

    let value_ranges = isa.map(|isa| {
        context
            .build_value_labels_ranges(isa)
            .expect("value location ranges")
    });

    let clif_output_dir = tcx.output_filenames(LOCAL_CRATE).with_extension("clif");

    match std::fs::create_dir(&clif_output_dir) {
        Ok(()) => {}
        Err(err) if err.kind() == std::io::ErrorKind::AlreadyExists => {}
        res @ Err(_) => res.unwrap(),
    }

    let clif_file_name = clif_output_dir.join(format!(
        "{}.{}.clif",
        tcx.symbol_name(instance).name,
        postfix
    ));

    let mut clif = String::new();
    cranelift_codegen::write::decorate_function(
        &mut clif_comments,
        &mut clif,
        &context.func,
        &DisplayFunctionAnnotations {
            isa: Some(&*crate::build_isa(
                tcx.sess, true, /* PIC doesn't matter here */
            )),
            value_ranges: value_ranges.as_ref(),
        },
    )
    .unwrap();

    let res: std::io::Result<()> = try {
        let mut file = std::fs::File::create(clif_file_name)?;
        let target_triple = crate::target_triple(tcx.sess);
        writeln!(file, "test compile")?;
        writeln!(file, "set is_pic")?;
        writeln!(file, "set enable_simd")?;
        writeln!(file, "target {} haswell", target_triple)?;
        writeln!(file, "")?;
        file.write(clif.as_bytes())?;
    };
    if let Err(err) = res {
        tcx.sess.warn(&format!("err writing clif file: {}", err));
    }
}

impl<M: Module> fmt::Debug for FunctionCx<'_, '_, M> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        writeln!(f, "{:?}", self.instance.substs)?;
        writeln!(f, "{:?}", self.local_map)?;

        let mut clif = String::new();
        ::cranelift_codegen::write::decorate_function(
            &mut &self.clif_comments,
            &mut clif,
            &self.bcx.func,
            &DisplayFunctionAnnotations::default(),
        )
        .unwrap();
        writeln!(f, "\n{}", clif)
    }
}
<commit_msg>Replace write with write_all<commit_after>//! This module provides the [CommentWriter] which makes it possible
//! to add comments to the written cranelift ir.
//!
//! # Example
//!
//! ```clif
//! test compile
//! target x86_64
//!
//! function u0:0(i64, i64, i64) system_v {
//! ; symbol _ZN119_$LT$example..IsNotEmpty$u20$as$u20$mini_core..FnOnce$LT$$LP$$RF$$u27$a$u20$$RF$$u27$b$u20$$u5b$u16$u5d$$C$$RP$$GT$$GT$9call_once17he85059d5e6a760a0E
//! ; instance Instance { def: Item(DefId(0/0:29 ~ example[8787]::{{impl}}[0]::call_once[0])), substs: [ReErased, ReErased] }
//! ; sig ([IsNotEmpty, (&&[u16],)]; c_variadic: false)->(u8, u8)
//!
//! ; ssa {_2: NOT_SSA, _4: NOT_SSA, _0: NOT_SSA, _3: (empty), _1: NOT_SSA}
//! ; msg   loc.idx    param    pass mode            ssa flags  ty
//! ; ret    _0      = v0       ByRef                NOT_SSA    (u8, u8)
//! ; arg    _1      = v1       ByRef                NOT_SSA    IsNotEmpty
//! ; arg    _2.0    = v2       ByVal(types::I64)    NOT_SSA    &&[u16]
//!
//!     ss0 = explicit_slot 0 ; _1: IsNotEmpty size=0 align=1,8
//!     ss1 = explicit_slot 8 ; _2: (&&[u16],) size=8 align=8,8
//!     ss2 = explicit_slot 8 ; _4: (&&[u16],) size=8 align=8,8
//!     sig0 = (i64, i64, i64) system_v
//!     sig1 = (i64, i64, i64) system_v
//!     fn0 = colocated u0:6 sig1 ; Instance { def: Item(DefId(0/0:31 ~ example[8787]::{{impl}}[1]::call_mut[0])), substs: [ReErased, ReErased] }
//!
//! block0(v0: i64, v1: i64, v2: i64):
//!     v3 = stack_addr.i64 ss0
//!     v4 = stack_addr.i64 ss1
//!     store v2, v4
//!     v5 = stack_addr.i64 ss2
//!     jump block1
//!
//! block1:
//!     nop
//! ; _3 = &mut _1
//! ; _4 = _2
//!     v6 = load.i64 v4
//!     store v6, v5
//! ;
//! ; _0 = const mini_core::FnMut::call_mut(move _3, move _4)
//!     v7 = load.i64 v5
//!     call fn0(v0, v3, v7)
//!     jump block2
//!
//! block2:
//!     nop
//! ;
//! ; return
//!     return
//! }
//! ```

use std::fmt;

use cranelift_codegen::{
    entity::SecondaryMap,
    ir::{entities::AnyEntity, function::DisplayFunctionAnnotations},
    write::{FuncWriter, PlainWriter},
};

use rustc_session::config::OutputType;

use crate::prelude::*;

#[derive(Debug)]
pub(crate) struct CommentWriter {
    global_comments: Vec<String>,
    entity_comments: FxHashMap<AnyEntity, String>,
}

impl CommentWriter {
    pub(crate) fn new<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> Self {
        let global_comments = if cfg!(debug_assertions) {
            vec![
                format!("symbol {}", tcx.symbol_name(instance).name),
                format!("instance {:?}", instance),
                format!(
                    "sig {:?}",
                    tcx.normalize_erasing_late_bound_regions(
                        ParamEnv::reveal_all(),
                        &crate::abi::fn_sig_for_fn_abi(tcx, instance)
                    )
                ),
                String::new(),
            ]
        } else {
            vec![]
        };

        CommentWriter {
            global_comments,
            entity_comments: FxHashMap::default(),
        }
    }
}

#[cfg(debug_assertions)]
impl CommentWriter {
    pub(crate) fn add_global_comment<S: Into<String>>(&mut self, comment: S) {
        self.global_comments.push(comment.into());
    }

    pub(crate) fn add_comment<S: Into<String> + AsRef<str>, E: Into<AnyEntity>>(
        &mut self,
        entity: E,
        comment: S,
    ) {
        use std::collections::hash_map::Entry;
        match self.entity_comments.entry(entity.into()) {
            Entry::Occupied(mut occ) => {
                occ.get_mut().push('\n');
                occ.get_mut().push_str(comment.as_ref());
            }
            Entry::Vacant(vac) => {
                vac.insert(comment.into());
            }
        }
    }
}

impl FuncWriter for &'_ CommentWriter {
    fn write_preamble(
        &mut self,
        w: &mut dyn fmt::Write,
        func: &Function,
        reg_info: Option<&isa::RegInfo>,
    ) -> Result<bool, fmt::Error> {
        for comment in &self.global_comments {
            if !comment.is_empty() {
                writeln!(w, "; {}", comment)?;
            } else {
                writeln!(w, "")?;
            }
        }
        if !self.global_comments.is_empty() {
            writeln!(w, "")?;
        }

        self.super_preamble(w, func, reg_info)
    }

    fn write_entity_definition(
        &mut self,
        w: &mut dyn fmt::Write,
        _func: &Function,
        entity: AnyEntity,
        value: &dyn fmt::Display,
    ) -> fmt::Result {
        write!(w, "    {} = {}", entity, value)?;

        if let Some(comment) = self.entity_comments.get(&entity) {
            writeln!(w, " ; {}", comment.replace('\n', "\n; "))
        } else {
            writeln!(w, "")
        }
    }

    fn write_block_header(
        &mut self,
        w: &mut dyn fmt::Write,
        func: &Function,
        isa: Option<&dyn isa::TargetIsa>,
        block: Block,
        indent: usize,
    ) -> fmt::Result {
        PlainWriter.write_block_header(w, func, isa, block, indent)
    }

    fn write_instruction(
        &mut self,
        w: &mut dyn fmt::Write,
        func: &Function,
        aliases: &SecondaryMap<Value, Vec<Value>>,
        isa: Option<&dyn isa::TargetIsa>,
        inst: Inst,
        indent: usize,
    ) -> fmt::Result {
        PlainWriter.write_instruction(w, func, aliases, isa, inst, indent)?;
        if let Some(comment) = self.entity_comments.get(&inst.into()) {
            writeln!(w, "; {}", comment.replace('\n', "\n; "))?;
        }
        Ok(())
    }
}

#[cfg(debug_assertions)]
impl<M: Module> FunctionCx<'_, '_, M> {
    pub(crate) fn add_global_comment<S: Into<String>>(&mut self, comment: S) {
        self.clif_comments.add_global_comment(comment);
    }

    pub(crate) fn add_comment<S: Into<String> + AsRef<str>, E: Into<AnyEntity>>(
        &mut self,
        entity: E,
        comment: S,
    ) {
        self.clif_comments.add_comment(entity, comment);
    }
}

pub(crate) fn write_clif_file<'tcx>(
    tcx: TyCtxt<'tcx>,
    postfix: &str,
    isa: Option<&dyn cranelift_codegen::isa::TargetIsa>,
    instance: Instance<'tcx>,
    context: &cranelift_codegen::Context,
    mut clif_comments: &CommentWriter,
) {
    use std::io::Write;

    if !cfg!(debug_assertions)
        && !tcx
            .sess
            .opts
            .output_types
            .contains_key(&OutputType::LlvmAssembly)
    {
        return;
    }

    let value_ranges = isa.map(|isa| {
        context
            .build_value_labels_ranges(isa)
            .expect("value location ranges")
    });

    let clif_output_dir = tcx.output_filenames(LOCAL_CRATE).with_extension("clif");

    match std::fs::create_dir(&clif_output_dir) {
        Ok(()) => {}
        Err(err) if err.kind() == std::io::ErrorKind::AlreadyExists => {}
        res @ Err(_) => res.unwrap(),
    }

    let clif_file_name = clif_output_dir.join(format!(
        "{}.{}.clif",
        tcx.symbol_name(instance).name,
        postfix
    ));

    let mut clif = String::new();
    cranelift_codegen::write::decorate_function(
        &mut clif_comments,
        &mut clif,
        &context.func,
        &DisplayFunctionAnnotations {
            isa: Some(&*crate::build_isa(
                tcx.sess, true, /* PIC doesn't matter here */
            )),
            value_ranges: value_ranges.as_ref(),
        },
    )
    .unwrap();

    let res: std::io::Result<()> = try {
        let mut file = std::fs::File::create(clif_file_name)?;
        let target_triple = crate::target_triple(tcx.sess);
        writeln!(file, "test compile")?;
        writeln!(file, "set is_pic")?;
        writeln!(file, "set enable_simd")?;
        writeln!(file, "target {} haswell", target_triple)?;
        writeln!(file, "")?;
        file.write_all(clif.as_bytes())?;
    };
    if let Err(err) = res {
        tcx.sess.warn(&format!("err writing clif file: {}", err));
    }
}

impl<M: Module> fmt::Debug for FunctionCx<'_, '_, M> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        writeln!(f, "{:?}", self.instance.substs)?;
        writeln!(f, "{:?}", self.local_map)?;

        let mut clif = String::new();
        ::cranelift_codegen::write::decorate_function(
            &mut &self.clif_comments,
            &mut clif,
            &self.bcx.func,
            &DisplayFunctionAnnotations::default(),
        )
        .unwrap();
        writeln!(f, "\n{}", clif)
    }
}
<|endoftext|>