<commit_before>use nom::{
  IResult,
  bytes::complete::{
      tag,
      take_while1,
      take,
  },
  multi::{
      many0,
  },
  combinator::{
      map,
      peek,
  },
  branch::alt,
  error::{
      Error,
      ErrorKind,
  },
};
use std::fmt;

// This starts karma tokenizer stream
// TODO: make fancy but for now basic objective
// TODO: we probs want to stress test the round-trip feature (use a fuzzer to help this)
// 1. Convert inbound &str to a token stream and work on that (for structural concerns)
// 2. support 2 types of braces '"' and '['/']' to support single type and open/close
// 3. suppport 3 types of karma, up, down, side
// 4. support single character or multiple character karma token, for now '++', '--', '+-' '±'
#[derive(Debug, PartialEq, Clone)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
pub enum KarmaToken{
    // Alphanumberic
    Text(String),
    // Whitespace (with a copy in so we can reproduce the string exactly) (only because we care if
    // there is whitespace before the karma or not)
    Space(String),
    // Only the " mark
    // TODO: quotation escaping?
    Quote,
    // Only the [ mark
    OpenBrace,
    // Only the ] mark
    CloseBrace,
    // Karma (only complete chunk makes it in) ('++', '--', '+-', '±' for now)
    // For runs to the left of this, see KText()
    Karma(String),
    // Karma Run text, this contains any karma runs ie. "+++++++++" that is to the left of
    // an valid karma parse (to support c++--), It needs to be its own type becaue it is slightly
    // more restrictive than plain Text in downstream parsers
    KText(String),
}

impl <'a> fmt::Display for KarmaToken {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            KarmaToken::Text(t)    => write!(f, "{}", t),
            KarmaToken::Space(t)   => write!(f, "{}", t),
            KarmaToken::Karma(t)   => write!(f, "{}", t),
            KarmaToken::KText(t)   => write!(f, "{}", t),
            KarmaToken::Quote      => write!(f, "\""),
            KarmaToken::OpenBrace  => write!(f, "["),
            KarmaToken::CloseBrace => write!(f, "]"),
        }
    }
}

pub fn all_token(input: &str) -> IResult<&str, Vec<KarmaToken>> {
    many0(token)(input)
}

// TODO: maybe instead of parsing in only whole karma chunk could be greedy
// and start only if its a whole chunk (for some) but if there's more,
// continue accumulating. "a+++" -> Text("a"), Karma("+++") for eg. Then parse
// the rightmost for valid karma value to use
//
// Look into just capturing all value in a karma, then doing some sort of iterative
// parsing to try to fit things together, and if it comes up with something workable
// use it otherwise reject it.
//
// Rightmost parse with space/eof after, will become a token, the rejects get shuffled
// back into a Text
fn token(input: &str) -> IResult<&str, KarmaToken> {
    alt((
        symbols,
        space,
        karma_run,
        karma,
        text,
    ))(input)
}

fn symbols(input: &str) -> IResult<&str, KarmaToken> {
    alt((
        map(tag("\""),   |_| KarmaToken::Quote),
        map(tag("["),    |_| KarmaToken::OpenBrace),
        map(tag("]"),    |_| KarmaToken::CloseBrace),
    ))(input)
}

fn space(input: &str) -> IResult<&str, KarmaToken> {
    map(take_while1(|c:char| c.is_whitespace()), |s:&str| KarmaToken::Space(s.to_string()))(input)
}

fn karma(input: &str) -> IResult<&str, KarmaToken> {
    map(
        alt((tag("++"), tag("--"), tag("+-"), tag("±"))),
        |k:&str| KarmaToken::Karma(k.to_string())
    )(input)
}

fn karma_run(input: &str) -> IResult<&str, KarmaToken> {
    let (_, candidate) = take_while1(|c:char| is_karma_symbol(c))(input)?;

    // Let's try a list of karma
    let klist = ["++", "--", "+-", "±"];
    let mut longest = "";

    for k in klist.iter() {
        // Short circuit this check if
        // 1. karma is shorter than longest match, it won't win
        // 2. candidate is shorter than the karma to be checked
        if (longest.len() > k.len()) | (k.len() > candidate.len()) {
            continue;
        }

        // Try a rmatch approach first
        let v: Vec<_> = candidate.rmatch_indices(k).collect();
        println!("candidate: {:?}, k: {:?}", candidate, k);
        println!("rmatch: {:?}", v);
        println!("c-len: {:?}, k-len: {:?}, c-k-len: {:?}", candidate.len(), k.len(), candidate.len() - k.len());

        // Check if there is any possible match at all
        if !v.is_empty() {
            // There is, let's verify if the index of the match is eol
            let (idx, _) = v.get(0).unwrap();
            let offset = candidate.len() - k.len();

            if idx == &offset {
                if k.len() >= longest.len() {
                    longest = k;
                }
            }
        }
    }

    // If longest is empty, nothing matched
    if longest.is_empty() {
        Err(nom::Err::Error(Error::new(input, ErrorKind::Tag)))
    } else {
        // Returns the input with the text portion sliced off, also return the karma matched to
        // Memory layout is
        // [input_slice, longest, tmp_input] and we want to return
        // [input_slice, [longest + tmp_input] via slicing the original input
        let input_slice = &candidate[
            0..(candidate.len() - longest.len())
        ];
        let ret_slice = &input[
            input_slice.len()..
        ];

        // If text to be returned is empty, we only got the karma/other parse now
        if input_slice.is_empty() {
            Err(nom::Err::Error(Error::new(input, ErrorKind::Tag)))

        } else {
            println!("===");
            println!("Winning: {:?}, text: {:?}, input: {:?}", longest, &input_slice, &ret_slice);
            println!("===");

            Ok((&ret_slice, KarmaToken::KText(input_slice.to_string())))
        }
    }
}

fn is_karma_symbol(s: char) -> bool {
    let sym = vec!['+', '-', '±'];
    sym.contains(&s)
}

fn is_symbol(s: char) -> bool {
    let sym = vec!['"', '[', ']'];
    sym.contains(&s)
}

// Steps needed:
// 1. Take any non [whitespace, part of karma, or braces]
// 2. When take_while stops, do a peek parse of [whitespace/karma/braces]
// 3. [if fail] add the character to the string and resume take_while
// 4. [if succ] exit parser with a Text(ret)
fn text(input: &str) -> IResult<&str, KarmaToken> {
    let mut ret = "".to_string();
    let mut cur_input = input;

    // Bail out if empty input
    if input.is_empty() {
        return Err(nom::Err::Error(Error::new(input, ErrorKind::Eof)));
    }

    loop {
        println!("========");
        println!("1i: {:?}", cur_input);
        println!("1r: {:?}", ret);

        // Force forward progress
        let par: IResult<&str, &str> = take_while1(
            |c:char| !c.is_whitespace() & !is_symbol(c) & !is_karma_symbol(c)
        )(cur_input);
        println!("1p: {:?}", par);
        if let Ok((input, tok)) = par {
            cur_input = input;
            ret.push_str(tok);
        }

        println!("");
        println!("2i: {:?}", cur_input);
        println!("2r: {:?}", ret);

        // Check if it is a whitespace or symbol parse
        let par = peek(alt((
            space,
            symbols,
            karma_run,
            karma,
        )))(cur_input);
        println!("2p: {:?}", par);
        match par {
            Ok(_)  => break,
            Err(_) => {
                // Check if it hit eof, if so exit
                if cur_input.is_empty() {
                    break;
                }

                let (input, tok) = take(1usize)(cur_input)?;
                cur_input = input;
                ret.push_str(tok);

                println!("");
                println!("3i: {:?}", cur_input);
                println!("3r: {:?}", ret);
            },
        }
    }

    let stuff = Ok((cur_input, KarmaToken::Text(ret)));
    println!("========");
    println!("ret: {:?}", stuff);
    stuff
}

#[cfg(test)]
macro_rules! space {
    ($data:expr) => {
        KarmaToken::Space($data.to_string())
    }
}

#[cfg(test)]
macro_rules! text {
    ($data:expr) => {
        KarmaToken::Text($data.to_string())
    }
}

#[cfg(test)]
macro_rules! karma {
    ($data:expr) => {
        KarmaToken::Karma($data.to_string())
    }
}

#[cfg(test)]
macro_rules! ktext {
    ($data:expr) => {
        KarmaToken::KText($data.to_string())
    }
}

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

    #[test]
    fn test_rightmost_valid() {
        assert_eq!(
            karma_run("--++"),
            Ok(("++", ktext!("--")))
        );
    }

    #[test]
    fn test_rightmost_only_karma() {
        assert_eq!(
            karma_run("++"),
            Err(nom::Err::Error(Error::new("++", ErrorKind::Tag)))
        );
    }

    #[test]
    fn test_rightmost_text_before() {
        assert_eq!(
            karma_run("text++"),
            Err(nom::Err::Error(Error::new("text++", ErrorKind::TakeWhile1)))
        );
    }

    #[test]
    fn test_rightmost_text_after() {
        assert_eq!(
            karma_run("++text"),
            Err(nom::Err::Error(Error::new("++text", ErrorKind::Tag)))
        );
    }

    #[test]
    fn test_karma_run() {
        assert_eq!(
            all_token("++--++"),
            Ok(("", vec![ktext!("++--"), karma!("++")]))
        );
    }

    #[test]
    fn test_karma_incomplete_run() {
        assert_eq!(
            all_token("+++"),
            Ok(("", vec![ktext!("+"), karma!("++")]))
        );
    }

    // TODO: this test depends on us supporting "+-" but not "-+" find alternatives later
    #[test]
    fn test_karma_incomplete_not_valid_run() {
        assert_eq!(
            all_token("+-+"),
            Ok(("", vec![karma!("+-"), text!("+")]))
        );
    }

    #[test]
    fn test_quote() {
        assert_eq!(
            all_token("\"\"\""),
            Ok(("", vec![KarmaToken::Quote; 3]))
        );
    }

    #[test]
    fn test_brace() {
        assert_eq!(
            all_token("][]]"),
            Ok(("", vec![
                KarmaToken::CloseBrace,
                KarmaToken::OpenBrace,
                KarmaToken::CloseBrace,
                KarmaToken::CloseBrace
            ]))
        );
    }

    #[test]
    fn test_whitespace() {
        assert_eq!(
            all_token(" \t"),
            Ok(("", vec![space!(" \t")]))
        );
    }

    #[test]
    fn test_whitespace_block() {
        assert_eq!(
            all_token(" ]\t[  "),
            Ok(("", vec![
                space!(" "),
                KarmaToken::CloseBrace,
                space!("\t"),
                KarmaToken::OpenBrace,
                space!("  ")
            ]))
        );
    }

    #[test]
    fn test_text_plain() {
        assert_eq!(
            all_token("this藏test-d"),
            Ok(("", vec![text!("this藏test-d")]))
        );
    }

    #[test]
    fn test_text_starting_ending_hypen() {
        assert_eq!(
            all_token("-test-"),
            Ok(("", vec![text!("-test-")]))
        );
    }

    // TODO: this test depends on us supporting "+-" but not "-+" find alternatives later
    #[test]
    fn test_text_unsupported_karma() {
        assert_eq!(
            all_token("text-+"),
            Ok(("", vec![text!("text-+")]))
        );
    }

    #[test]
    fn test_text_block() {
        assert_eq!(
            all_token("this\t[\"藏  ++]"),
            Ok(("", vec![
                text!("this"),
                space!("\t"),
                KarmaToken::OpenBrace,
                KarmaToken::Quote,
                text!("藏"),
                space!("  "),
                karma!("++"),
                KarmaToken::CloseBrace,
            ]))
        );
    }

    #[test]
    fn test_roundtrip() {
        let text = "this\t[\"藏  ++]";
        let parse = all_token(text).unwrap().1;

        assert_eq!(parse.iter().map(|t| t.to_string()).collect::<Vec<String>>().join(""), text);
    }

    #[test]
    fn test_fuzz_one() {
        assert_eq!(
            all_token("±+\""),
            Ok(("", vec![karma!("±"), text!("+"), KarmaToken::Quote]))
        );
    }
}
<commit_msg>Clean out debugging println<commit_after>use nom::{
  IResult,
  bytes::complete::{
      tag,
      take_while1,
      take,
  },
  multi::{
      many0,
  },
  combinator::{
      map,
      peek,
  },
  branch::alt,
  error::{
      Error,
      ErrorKind,
  },
};
use std::fmt;

// This starts karma tokenizer stream
// TODO: make fancy but for now basic objective
// TODO: we probs want to stress test the round-trip feature (use a fuzzer to help this)
// 1. Convert inbound &str to a token stream and work on that (for structural concerns)
// 2. support 2 types of braces '"' and '['/']' to support single type and open/close
// 3. suppport 3 types of karma, up, down, side
// 4. support single character or multiple character karma token, for now '++', '--', '+-' '±'
#[derive(Debug, PartialEq, Clone)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
pub enum KarmaToken{
    // Alphanumberic
    Text(String),
    // Whitespace (with a copy in so we can reproduce the string exactly) (only because we care if
    // there is whitespace before the karma or not)
    Space(String),
    // Only the " mark
    // TODO: quotation escaping?
    Quote,
    // Only the [ mark
    OpenBrace,
    // Only the ] mark
    CloseBrace,
    // Karma (only complete chunk makes it in) ('++', '--', '+-', '±' for now)
    // For runs to the left of this, see KText()
    Karma(String),
    // Karma Run text, this contains any karma runs ie. "+++++++++" that is to the left of
    // an valid karma parse (to support c++--), It needs to be its own type becaue it is slightly
    // more restrictive than plain Text in downstream parsers
    KText(String),
}

impl <'a> fmt::Display for KarmaToken {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            KarmaToken::Text(t)    => write!(f, "{}", t),
            KarmaToken::Space(t)   => write!(f, "{}", t),
            KarmaToken::Karma(t)   => write!(f, "{}", t),
            KarmaToken::KText(t)   => write!(f, "{}", t),
            KarmaToken::Quote      => write!(f, "\""),
            KarmaToken::OpenBrace  => write!(f, "["),
            KarmaToken::CloseBrace => write!(f, "]"),
        }
    }
}

pub fn all_token(input: &str) -> IResult<&str, Vec<KarmaToken>> {
    many0(token)(input)
}

// TODO: maybe instead of parsing in only whole karma chunk could be greedy
// and start only if its a whole chunk (for some) but if there's more,
// continue accumulating. "a+++" -> Text("a"), Karma("+++") for eg. Then parse
// the rightmost for valid karma value to use
//
// Look into just capturing all value in a karma, then doing some sort of iterative
// parsing to try to fit things together, and if it comes up with something workable
// use it otherwise reject it.
//
// Rightmost parse with space/eof after, will become a token, the rejects get shuffled
// back into a Text
fn token(input: &str) -> IResult<&str, KarmaToken> {
    alt((
        symbols,
        space,
        karma_run,
        karma,
        text,
    ))(input)
}

fn symbols(input: &str) -> IResult<&str, KarmaToken> {
    alt((
        map(tag("\""),   |_| KarmaToken::Quote),
        map(tag("["),    |_| KarmaToken::OpenBrace),
        map(tag("]"),    |_| KarmaToken::CloseBrace),
    ))(input)
}

fn space(input: &str) -> IResult<&str, KarmaToken> {
    map(take_while1(|c:char| c.is_whitespace()), |s:&str| KarmaToken::Space(s.to_string()))(input)
}

fn karma(input: &str) -> IResult<&str, KarmaToken> {
    map(
        alt((tag("++"), tag("--"), tag("+-"), tag("±"))),
        |k:&str| KarmaToken::Karma(k.to_string())
    )(input)
}

fn karma_run(input: &str) -> IResult<&str, KarmaToken> {
    let (_, candidate) = take_while1(|c:char| is_karma_symbol(c))(input)?;

    // Let's try a list of karma
    let klist = ["++", "--", "+-", "±"];
    let mut longest = "";

    for k in klist.iter() {
        // Short circuit this check if
        // 1. karma is shorter than longest match, it won't win
        // 2. candidate is shorter than the karma to be checked
        if (longest.len() > k.len()) | (k.len() > candidate.len()) {
            continue;
        }

        // Try a rmatch approach first
        let v: Vec<_> = candidate.rmatch_indices(k).collect();

        // Check if there is any possible match at all
        if !v.is_empty() {
            // There is, let's verify if the index of the match is eol
            let (idx, _) = v.get(0).unwrap();
            let offset = candidate.len() - k.len();

            if idx == &offset {
                if k.len() >= longest.len() {
                    longest = k;
                }
            }
        }
    }

    // If longest is empty, nothing matched
    if longest.is_empty() {
        Err(nom::Err::Error(Error::new(input, ErrorKind::Tag)))
    } else {
        // Returns the input with the text portion sliced off, also return the karma matched to
        // Memory layout is
        // [input_slice, longest, tmp_input] and we want to return
        // [input_slice, [longest + tmp_input] via slicing the original input
        let input_slice = &candidate[
            0..(candidate.len() - longest.len())
        ];
        let ret_slice = &input[
            input_slice.len()..
        ];

        // If text to be returned is empty, we only got the karma/other parse now
        if input_slice.is_empty() {
            Err(nom::Err::Error(Error::new(input, ErrorKind::Tag)))

        } else {
            Ok((&ret_slice, KarmaToken::KText(input_slice.to_string())))
        }
    }
}

fn is_karma_symbol(s: char) -> bool {
    let sym = vec!['+', '-', '±'];
    sym.contains(&s)
}

fn is_symbol(s: char) -> bool {
    let sym = vec!['"', '[', ']'];
    sym.contains(&s)
}

// Steps needed:
// 1. Take any non [whitespace, part of karma, or braces]
// 2. When take_while stops, do a peek parse of [whitespace/karma/braces]
// 3. [if fail] add the character to the string and resume take_while
// 4. [if succ] exit parser with a Text(ret)
fn text(input: &str) -> IResult<&str, KarmaToken> {
    let mut ret = "".to_string();
    let mut cur_input = input;

    // Bail out if empty input
    if input.is_empty() {
        return Err(nom::Err::Error(Error::new(input, ErrorKind::Eof)));
    }

    loop {
        // Force forward progress
        let par: IResult<&str, &str> = take_while1(
            |c:char| !c.is_whitespace() & !is_symbol(c) & !is_karma_symbol(c)
        )(cur_input);
        if let Ok((input, tok)) = par {
            cur_input = input;
            ret.push_str(tok);
        }

        // Check if it is a whitespace or symbol parse
        let par = peek(alt((
            space,
            symbols,
            karma_run,
            karma,
        )))(cur_input);

        match par {
            Ok(_)  => break,
            Err(_) => {
                // Check if it hit eof, if so exit
                if cur_input.is_empty() {
                    break;
                }

                let (input, tok) = take(1usize)(cur_input)?;
                cur_input = input;
                ret.push_str(tok);
            },
        }
    }

    Ok((cur_input, KarmaToken::Text(ret)))
}

#[cfg(test)]
macro_rules! space {
    ($data:expr) => {
        KarmaToken::Space($data.to_string())
    }
}

#[cfg(test)]
macro_rules! text {
    ($data:expr) => {
        KarmaToken::Text($data.to_string())
    }
}

#[cfg(test)]
macro_rules! karma {
    ($data:expr) => {
        KarmaToken::Karma($data.to_string())
    }
}

#[cfg(test)]
macro_rules! ktext {
    ($data:expr) => {
        KarmaToken::KText($data.to_string())
    }
}

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

    #[test]
    fn test_rightmost_valid() {
        assert_eq!(
            karma_run("--++"),
            Ok(("++", ktext!("--")))
        );
    }

    #[test]
    fn test_rightmost_only_karma() {
        assert_eq!(
            karma_run("++"),
            Err(nom::Err::Error(Error::new("++", ErrorKind::Tag)))
        );
    }

    #[test]
    fn test_rightmost_text_before() {
        assert_eq!(
            karma_run("text++"),
            Err(nom::Err::Error(Error::new("text++", ErrorKind::TakeWhile1)))
        );
    }

    #[test]
    fn test_rightmost_text_after() {
        assert_eq!(
            karma_run("++text"),
            Err(nom::Err::Error(Error::new("++text", ErrorKind::Tag)))
        );
    }

    #[test]
    fn test_karma_run() {
        assert_eq!(
            all_token("++--++"),
            Ok(("", vec![ktext!("++--"), karma!("++")]))
        );
    }

    #[test]
    fn test_karma_incomplete_run() {
        assert_eq!(
            all_token("+++"),
            Ok(("", vec![ktext!("+"), karma!("++")]))
        );
    }

    // TODO: this test depends on us supporting "+-" but not "-+" find alternatives later
    #[test]
    fn test_karma_incomplete_not_valid_run() {
        assert_eq!(
            all_token("+-+"),
            Ok(("", vec![karma!("+-"), text!("+")]))
        );
    }

    #[test]
    fn test_quote() {
        assert_eq!(
            all_token("\"\"\""),
            Ok(("", vec![KarmaToken::Quote; 3]))
        );
    }

    #[test]
    fn test_brace() {
        assert_eq!(
            all_token("][]]"),
            Ok(("", vec![
                KarmaToken::CloseBrace,
                KarmaToken::OpenBrace,
                KarmaToken::CloseBrace,
                KarmaToken::CloseBrace
            ]))
        );
    }

    #[test]
    fn test_whitespace() {
        assert_eq!(
            all_token(" \t"),
            Ok(("", vec![space!(" \t")]))
        );
    }

    #[test]
    fn test_whitespace_block() {
        assert_eq!(
            all_token(" ]\t[  "),
            Ok(("", vec![
                space!(" "),
                KarmaToken::CloseBrace,
                space!("\t"),
                KarmaToken::OpenBrace,
                space!("  ")
            ]))
        );
    }

    #[test]
    fn test_text_plain() {
        assert_eq!(
            all_token("this藏test-d"),
            Ok(("", vec![text!("this藏test-d")]))
        );
    }

    #[test]
    fn test_text_starting_ending_hypen() {
        assert_eq!(
            all_token("-test-"),
            Ok(("", vec![text!("-test-")]))
        );
    }

    // TODO: this test depends on us supporting "+-" but not "-+" find alternatives later
    #[test]
    fn test_text_unsupported_karma() {
        assert_eq!(
            all_token("text-+"),
            Ok(("", vec![text!("text-+")]))
        );
    }

    #[test]
    fn test_text_block() {
        assert_eq!(
            all_token("this\t[\"藏  ++]"),
            Ok(("", vec![
                text!("this"),
                space!("\t"),
                KarmaToken::OpenBrace,
                KarmaToken::Quote,
                text!("藏"),
                space!("  "),
                karma!("++"),
                KarmaToken::CloseBrace,
            ]))
        );
    }

    #[test]
    fn test_roundtrip() {
        let text = "this\t[\"藏  ++]";
        let parse = all_token(text).unwrap().1;

        assert_eq!(parse.iter().map(|t| t.to_string()).collect::<Vec<String>>().join(""), text);
    }

    #[test]
    fn test_fuzz_one() {
        assert_eq!(
            all_token("±+\""),
            Ok(("", vec![karma!("±"), text!("+"), KarmaToken::Quote]))
        );
    }
}
<|endoftext|>


<commit_before>#![crate_id = "seax-svm"]
#![crate_type="lib"]
#![feature(box_syntax)]

/// Contains the Seax Virtual Machine (SVM) and miscellaneous
/// support code.
pub mod svm {
    use svm::slist::List;
    use svm::slist::Stack;
    use std::iter::IteratorExt;
    use std::fmt;

    /// Singly-linked list and stack implementations. `List<T>` is a
    /// singly-linked cons list with boxed items. `Stack<T>` is basically
    /// just a struct containing a boxed pointer to the head of a list,
    /// and some methods.
    pub mod slist {

        use svm::slist::List::{Cons,Nil};
        use std::fmt;

        /// A stack implementation wrapping a `List<T>`
        ///
        /// This is essentially just a struct containing a boxed pointer
        /// to the head of a `List<T>`, so that when items are pushed to or
        /// popped from the stack, the pointer is changed to point to the
        /// new head item. There may be saner ways of doing this.
        pub struct Stack<T> {
            /// The head item of the list.
            head: Box<List<T>>
        }

        impl<T> Stack<T> {
            /// Push an item to the top of the stack, returning a new stack
            pub fn push(self, it: T) -> Stack<T> {
                Stack { head: Box::new(self.head.prepend(it)) }
            }

            /// Peak at the top item of the stack.
            ///
            /// Returns Some<T> if there is an item on top of the stack,
            /// and None if the stack is empty.
            pub fn peek(&self) -> Option<&T> {
                match *self.head {
                    Nil => None,
                    Cons(ref it,_) => Some(it)
                }
            }

            /// Returns an empty stack.
            pub fn empty() -> Stack<T> {
                Stack { head: box Nil }
            }

            /// Wraps a list into a stack.
            pub fn new(l: List<T>) -> Stack<T> {
                Stack { head: box l }
            }

            /// Returns the length of the stack. This just calls
            /// `List::length()` on the wrapped list.
            pub fn length(&self) -> isize {
                self.head.length()
            }
        }

        /// Singly-linked cons list.
        ///
        /// This is used internally to represent list primitives in the
        /// machine.
        #[derive(PartialEq)]
        pub enum List<T> {
            Cons(T, Box<List<T>>),
            Nil,
        }

        /// Public implementation for List.
        impl<T> List<T> {

            /// Creates a new empty list
            pub fn new() -> List<T> {
                Nil
            }

            /// Prepends the given item to the list, returning the new head item.
            pub fn prepend(self, it: T) -> List<T> {
                Cons(it, box self)
            }

            /// Returns the length of the list.
            pub fn length (&self) -> isize {
                match *self {
                    Cons(_, ref tail) => 1 + tail.length(),
                    Nil => 0
                }
            }
        }

        impl<T> List<T> where T: fmt::Show {
            /// Return a string representation of the list
            fn to_string(&self) -> String {
                match *self {
                    Cons(ref head, ref tail) => format!("({:?}, {})", head, tail.to_string()),
                    Nil => format!("nil")
                }
            }
        }

        impl<T> fmt::Show for List<T> where T: fmt::Show {
            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
                write!(f, "{}", self.to_string())
            }
        }

        /// Convenience macro for making lists.
        ///
        /// Usage: `list!(1i32, 2i32, 3i32);` expands to
        /// `Cons(1i32, Box::new(Cons(2i32, Box::new(Cons(3i32, Box::new(Nil))))));`.
        macro_rules! list(
            ( $e:expr, $($rest:expr),+ ) => ( Cons($e, Box::new(list!( $( $rest ),+ )) ));
            ( $e:expr ) => ( Cons($e, Box::new(Nil)) );
            () => ( @Empty )
        );

        #[cfg(test)]
        mod tests {
            use super::{List, Stack};
            use super::List::{Cons,Nil};

            #[test]
            fn test_list_length() {
                let full_list: List<i32> = list!(1i32, 2i32, 3i32);
                let empty_list: List<i32> = List::new();
                assert_eq!(full_list.length(), 3);
                assert_eq!(empty_list.length(), 0);
            }

            #[test]
            fn test_list_to_string() {
                let l: List<i32> = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil))))));
                assert_eq!(l.to_string(), "(1i32, (2i32, (3i32, nil)))");
            }

            #[test]
            fn test_stack_length() {
                let full_stack: Stack<i32> = Stack::new(list!(1i32, 2i32, 3i32));
                let empty_stack: Stack<i32> = Stack::empty();
                assert_eq!(full_stack.length(), 3);
                assert_eq!(empty_stack.length(), 0);
            }

            #[test]
            fn test_stack_peek() {
                let full_stack: Stack<i32> = Stack::new(list!(1i32, 2i32, 3i32));
                let empty_stack: Stack<i32> = Stack::empty();
                assert_eq!(full_stack.peek(), Some(&1));
                assert_eq!(empty_stack.peek(), None);
            }

            #[test]
            fn test_stack_push() {
                let mut s: Stack<i32> = Stack::empty();
                assert_eq!(s.peek(), None);
                s = s.push(1);
                assert_eq!(s.peek(), Some(&1));
                s = s.push(6);
                assert_eq!(s.peek(), Some(&6));
            }

            #[test]
            fn test_stack_pop() {
                let mut s: Stack<i32> = Stack::empty();
                assert_eq!(s.peek(), None); // TODO: implement
                s = s.push(1);
                assert_eq!(s.peek(), Some(&1));
                s = s.push(6);
                assert_eq!(s.peek(), Some(&6));
            }

            #[test]
            fn test_list_macro() {
                let l: List<i32> = list!(1i32, 2i32, 3i32);
                assert_eq!(l.to_string(), "(1i32, (2i32, (3i32, nil)))")
            }
        }
    }

    /// SVM cell types.
    ///
    /// A cell in the VM can be either an atom (single item, either unsigned
    /// int, signed int, float, or string) or a pointer to a list cell.
    #[derive(PartialEq)]
    pub enum SVMCell {
        AtomCell(Atom),
        ListCell(Box<List<SVMCell>>)
    }

    impl fmt::Show for SVMCell {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            write!(f, "[{:?}]", self)
        }
    }

    /// SVM atom types.
    ///
    /// A VM atom can be either an unsigned int, signed int, float,
    /// char, or string.
    ///
    /// TODO: Strings could be implemented as char lists rather than
    /// Rust strings.
    #[derive(PartialEq)]
    pub enum Atom {
        UInt(usize),
        SInt(isize),
        Float(f64),
        Char(char),
        Str(String)
    }

    impl fmt::Show for Atom {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            match self {
                &Atom::UInt(value) => write!(f, "{}u", value),
                &Atom::SInt(value) => write!(f, "{}i", value),
                &Atom::Float(value) => write!(f, "{}f", value),
                &Atom::Char(value) => write!(f, "'{}'", value),
                &Atom::Str(ref value) => write!(f, "\"{}\"", value)
            }
        }
    }

    /// SVM instruction types
    pub enum SVMInstruction {
        /// `nil`
        ///
        /// Pushes an empty list (nil) onto the stack
        InstNIL,
        /// `ldc`: `L`oa`d` `C`onstant. Loads a constant (atom)
        InstLDC(Atom),
        /// `ld`: `L`oa`d`. Pushes a variable onto the stack.
        ///
        /// The variable is indicated by the argument, a pair.
        /// The pair's `car` specifies the level, the `cdr` the position.
        /// So `(1 . 3)` gives the current function's (level 1) third
        /// parameter.
        InstLD,
        /// `ldf`: `L`oa`d` `F`unction.
        ///
        ///  Takes one list argument representing a function and constructs
        ///  a closure (a pair containing the function and the current
        ///  environment) and pushes that onto the stack.
        InstLDF,
        /// `join`
        ///
        /// Pops a list reference from the dump and makes this the new value
        /// of `C`. This instruction occurs at the end of both alternatives of
        ///  a `sel`.
        InstJOIN,
        /// `ap`: `Ap`ply.
        ///
        /// Pops a closure and a list of parameter values from the stack.
        /// The closure is applied to the parameters by installing its
        /// environment as the current one, pushing the parameter list
        /// in front of that, clearing the stack, and setting `C` to the
        /// closure's function pointer. The previous values of `S`, `E`,
        ///  and the next value of `C` are saved on the dump.
        InstAP,
        /// `ret`: `Ret`urn.
        ///
        /// Pops one return value from the stack, restores
        /// `S`, `E`, and `C` from the dump, and pushes
        /// the return value onto the now-current stack.
        InstRET,
        /// `dum`: `Dum`my.
        ///
        /// Pops a dummy environment (an empty list) onto the `E` stack.
        InstDUM,
        /// `rap`: `R`ecursive `Ap`ply.
        /// Works like `ap`, only that it replaces an occurrence of a
        /// dummy environment with the current one, thus making recursive
        ///  functions possible.
        InstRAP,
        /// `sel`: `Sel`ect
        ///
        /// Expects two list arguments, and pops a value from the stack.
        /// The first list is executed if the popped value was non-nil,
        /// the second list otherwise. Before one of these list pointers
        ///  is made the new `C`, a pointer to the instruction following
        ///  `sel` is saved on the dump.
        InstSEL,
        /// `add`
        ///
        /// Pops two numbers off of the stack and adds them, pushing the
        /// result onto the stack. This will up-convert integers to floating
        /// point if necessary.
        ///
        /// TODO: figure out what happens when you try to add things that aren't
        /// numbers (maybe the compiler won't let this happen?).
        InstADD,
        /// `sub`: `Sub`tract
        ///
        /// Pops two numbers off of the stack and subtracts the first from the
        /// second, pushing the result onto the stack. This will up-convert
        /// integers to floating point if necessary.
        ///
        /// TODO: figure out what happens when you try to subtract things that
        /// aren't numbers (maybe the compiler won't let this happen?).
        InstSUB,
        /// `mul`: `Mul`tiply
        ///
        /// Pops two numbers off of the stack and multiplies them, pushing the
        /// result onto the stack. This will up-convert integers to floating
        /// point if necessary.
        ///
        /// TODO: figure out what happens when you try to multiply things that
        /// aren't numbers (maybe the compiler won't let this happen?).
        InstMUL,
        /// `div`: `Div`ide
        ///
        /// Pops two numbers off of the stack and divides the first by the second,
        /// pushing the result onto the stack. This performs integer division.
        ///
        /// TODO: figure out what happens when you try to divide things that
        /// aren't numbers (maybe the compiler won't let this happen?).
        InstDIV,
        /// `fdiv`: `F`loating-point `div`ide
        ///
        /// Pops two numbers off of the stack and divides the first by the second,
        /// pushing the result onto the stack. This performs float division.
        ///
        /// TODO: figure out what happens when you try to divide things that
        /// aren't numbers (maybe the compiler won't let this happen?).
        ///
        /// TODO: Not sure if there should be separate float and int divide words
        /// I guess the compiler can figure this out
        InstFDIV,
        /// `mod`: `Mod`ulo
        ///
        /// Pops two numbers off of the stack and divides the first by the second,
        /// pushing the remainder onto the stack.
        ///
        /// TODO: figure out what happens when you try to modulo things that
        /// aren't numbers (maybe the compiler won't let this happen?).
        InstMOD
        // TODO: add some hardcoded I/O instructions here so that you can
        //  do I/O without farming everything out to `stdio`
        // TODO: add `cons` and `cdr` words
    }

    /// Represents a SVM machine state
    pub struct State {
        stack: Stack<SVMCell>,
        env: Stack<SVMCell>,
        control: Stack<SVMCell>,
        dump: Stack<SVMCell>
    }

    impl State {

        /// Creates a new empty state
        fn new() -> State {
            State {
                stack: Stack::empty(),
                env: Stack::empty(),
                control: Stack::empty(),
                dump: Stack::empty()
            }
        }

        /// Evaluates an instruction.
        ///
        /// Evaluates an instruction against a state, returning a new state.
        pub fn eval(self, inst: SVMInstruction) -> State {
            match inst {
                SVMInstruction::InstNIL => State {
                    stack: self.stack.push(SVMCell::ListCell(box List::new())),
                    env: self.env,
                    control: self.control,
                    dump: self.dump
                },
                _ => { unimplemented!() }
            }
        }
    }

    /*
    /// Evaluates a program.
    ///
    /// Evaluates a program represented as an `Iterator` of `SVMInstruction`s.
    /// Returns the final machine state at the end of execution

    pub fn evalProgram(insts: Iterator<Item=SVMInstruction>) -> State {
        insts.fold(State::new(), |last_state: State, inst: SVMInstruction| last_state.eval(inst));
    }*/

    #[cfg(test)]
    mod tests {
        use super::State;
        use super::{SVMInstruction, SVMCell, Atom};
        use super::slist::List::{Cons,Nil};

        #[test]
        fn test_empty_state() {
            let state = State::new();
            assert_eq!(state.stack.length(), 0);
            assert_eq!(state.env.length(), 0);
            assert_eq!(state.control.length(), 0);
            assert_eq!(state.dump.length(), 0);
        }

        #[test]
        fn test_eval_nil () {
            let mut state = State::new();
            assert_eq!(state.stack.peek(), None);
            state = state.eval(SVMInstruction::InstNIL);
            assert_eq!(state.stack.peek(), Some(&SVMCell::ListCell(box Nil)));
        }

        #[test]
        fn test_eval_ldc () {
            let mut state = State::new();
            assert_eq!(state.stack.peek(), None);
            state = state.eval(SVMInstruction::InstLDC(Atom::SInt(1)));
            assert_eq!(state.stack.peek(), Some(&SVMCell::AtomCell(Atom::SInt(1))));
            state = state.eval(SVMInstruction::InstLDC(Atom::Char('a')));
            assert_eq!(state.stack.peek(), Some(&SVMCell::AtomCell(Atom::Char('a'))));
            state = state.eval(SVMInstruction::InstLDC(Atom::Float(1.0f64)));
            assert_eq!(state.stack.peek(), Some(&SVMCell::AtomCell(Atom::Float(1.0f64)))));

        }
    }

}
<commit_msg>Implement LDC instruction in SVM<commit_after>#![crate_id = "seax-svm"]
#![crate_type="lib"]
#![feature(box_syntax)]

/// Contains the Seax Virtual Machine (SVM) and miscellaneous
/// support code.
pub mod svm {
    use svm::slist::List;
    use svm::slist::Stack;
    use std::iter::IteratorExt;
    use std::fmt;

    /// Singly-linked list and stack implementations. `List<T>` is a
    /// singly-linked cons list with boxed items. `Stack<T>` is basically
    /// just a struct containing a boxed pointer to the head of a list,
    /// and some methods.
    pub mod slist {

        use svm::slist::List::{Cons,Nil};
        use std::fmt;

        /// A stack implementation wrapping a `List<T>`
        ///
        /// This is essentially just a struct containing a boxed pointer
        /// to the head of a `List<T>`, so that when items are pushed to or
        /// popped from the stack, the pointer is changed to point to the
        /// new head item. There may be saner ways of doing this.
        pub struct Stack<T> {
            /// The head item of the list.
            head: Box<List<T>>
        }

        impl<T> Stack<T> {
            /// Push an item to the top of the stack, returning a new stack
            pub fn push(self, it: T) -> Stack<T> {
                Stack { head: Box::new(self.head.prepend(it)) }
            }

            /// Peak at the top item of the stack.
            ///
            /// Returns Some<T> if there is an item on top of the stack,
            /// and None if the stack is empty.
            pub fn peek(&self) -> Option<&T> {
                match *self.head {
                    Nil => None,
                    Cons(ref it,_) => Some(it)
                }
            }

            /// Returns an empty stack.
            pub fn empty() -> Stack<T> {
                Stack { head: box Nil }
            }

            /// Wraps a list into a stack.
            pub fn new(l: List<T>) -> Stack<T> {
                Stack { head: box l }
            }

            /// Returns the length of the stack. This just calls
            /// `List::length()` on the wrapped list.
            pub fn length(&self) -> isize {
                self.head.length()
            }
        }

        /// Singly-linked cons list.
        ///
        /// This is used internally to represent list primitives in the
        /// machine.
        #[derive(PartialEq)]
        pub enum List<T> {
            Cons(T, Box<List<T>>),
            Nil,
        }

        /// Public implementation for List.
        impl<T> List<T> {

            /// Creates a new empty list
            pub fn new() -> List<T> {
                Nil
            }

            /// Prepends the given item to the list, returning the new head item.
            pub fn prepend(self, it: T) -> List<T> {
                Cons(it, box self)
            }

            /// Returns the length of the list.
            pub fn length (&self) -> isize {
                match *self {
                    Cons(_, ref tail) => 1 + tail.length(),
                    Nil => 0
                }
            }
        }

        impl<T> List<T> where T: fmt::Show {
            /// Return a string representation of the list
            fn to_string(&self) -> String {
                match *self {
                    Cons(ref head, ref tail) => format!("({:?}, {})", head, tail.to_string()),
                    Nil => format!("nil")
                }
            }
        }

        impl<T> fmt::Show for List<T> where T: fmt::Show {
            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
                write!(f, "{}", self.to_string())
            }
        }

        /// Convenience macro for making lists.
        ///
        /// Usage: `list!(1i32, 2i32, 3i32);` expands to
        /// `Cons(1i32, Box::new(Cons(2i32, Box::new(Cons(3i32, Box::new(Nil))))));`.
        macro_rules! list(
            ( $e:expr, $($rest:expr),+ ) => ( Cons($e, Box::new(list!( $( $rest ),+ )) ));
            ( $e:expr ) => ( Cons($e, Box::new(Nil)) );
            () => ( @Empty )
        );

        #[cfg(test)]
        mod tests {
            use super::{List, Stack};
            use super::List::{Cons,Nil};

            #[test]
            fn test_list_length() {
                let full_list: List<i32> = list!(1i32, 2i32, 3i32);
                let empty_list: List<i32> = List::new();
                assert_eq!(full_list.length(), 3);
                assert_eq!(empty_list.length(), 0);
            }

            #[test]
            fn test_list_to_string() {
                let l: List<i32> = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil))))));
                assert_eq!(l.to_string(), "(1i32, (2i32, (3i32, nil)))");
            }

            #[test]
            fn test_stack_length() {
                let full_stack: Stack<i32> = Stack::new(list!(1i32, 2i32, 3i32));
                let empty_stack: Stack<i32> = Stack::empty();
                assert_eq!(full_stack.length(), 3);
                assert_eq!(empty_stack.length(), 0);
            }

            #[test]
            fn test_stack_peek() {
                let full_stack: Stack<i32> = Stack::new(list!(1i32, 2i32, 3i32));
                let empty_stack: Stack<i32> = Stack::empty();
                assert_eq!(full_stack.peek(), Some(&1));
                assert_eq!(empty_stack.peek(), None);
            }

            #[test]
            fn test_stack_push() {
                let mut s: Stack<i32> = Stack::empty();
                assert_eq!(s.peek(), None);
                s = s.push(1);
                assert_eq!(s.peek(), Some(&1));
                s = s.push(6);
                assert_eq!(s.peek(), Some(&6));
            }

            #[test]
            fn test_stack_pop() {
                let mut s: Stack<i32> = Stack::empty();
                assert_eq!(s.peek(), None); // TODO: implement
                s = s.push(1);
                assert_eq!(s.peek(), Some(&1));
                s = s.push(6);
                assert_eq!(s.peek(), Some(&6));
            }

            #[test]
            fn test_list_macro() {
                let l: List<i32> = list!(1i32, 2i32, 3i32);
                assert_eq!(l.to_string(), "(1i32, (2i32, (3i32, nil)))")
            }
        }
    }

    /// SVM cell types.
    ///
    /// A cell in the VM can be either an atom (single item, either unsigned
    /// int, signed int, float, or string) or a pointer to a list cell.
    #[derive(PartialEq)]
    pub enum SVMCell {
        AtomCell(Atom),
        ListCell(Box<List<SVMCell>>)
    }

    impl fmt::Show for SVMCell {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            write!(f, "[{:?}]", self)
        }
    }

    /// SVM atom types.
    ///
    /// A VM atom can be either an unsigned int, signed int, float,
    /// char, or string.
    ///
    /// TODO: Strings could be implemented as char lists rather than
    /// Rust strings.
    #[derive(PartialEq)]
    pub enum Atom {
        UInt(usize),
        SInt(isize),
        Float(f64),
        Char(char),
        Str(String)
    }

    impl fmt::Show for Atom {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            match self {
                &Atom::UInt(value) => write!(f, "{}u", value),
                &Atom::SInt(value) => write!(f, "{}i", value),
                &Atom::Float(value) => write!(f, "{}f", value),
                &Atom::Char(value) => write!(f, "'{}'", value),
                &Atom::Str(ref value) => write!(f, "\"{}\"", value)
            }
        }
    }

    /// SVM instruction types
    pub enum SVMInstruction {
        /// `nil`
        ///
        /// Pushes an empty list (nil) onto the stack
        InstNIL,
        /// `ldc`: `L`oa`d` `C`onstant. Loads a constant (atom)
        InstLDC(Atom),
        /// `ld`: `L`oa`d`. Pushes a variable onto the stack.
        ///
        /// The variable is indicated by the argument, a pair.
        /// The pair's `car` specifies the level, the `cdr` the position.
        /// So `(1 . 3)` gives the current function's (level 1) third
        /// parameter.
        InstLD,
        /// `ldf`: `L`oa`d` `F`unction.
        ///
        ///  Takes one list argument representing a function and constructs
        ///  a closure (a pair containing the function and the current
        ///  environment) and pushes that onto the stack.
        InstLDF,
        /// `join`
        ///
        /// Pops a list reference from the dump and makes this the new value
        /// of `C`. This instruction occurs at the end of both alternatives of
        ///  a `sel`.
        InstJOIN,
        /// `ap`: `Ap`ply.
        ///
        /// Pops a closure and a list of parameter values from the stack.
        /// The closure is applied to the parameters by installing its
        /// environment as the current one, pushing the parameter list
        /// in front of that, clearing the stack, and setting `C` to the
        /// closure's function pointer. The previous values of `S`, `E`,
        ///  and the next value of `C` are saved on the dump.
        InstAP,
        /// `ret`: `Ret`urn.
        ///
        /// Pops one return value from the stack, restores
        /// `S`, `E`, and `C` from the dump, and pushes
        /// the return value onto the now-current stack.
        InstRET,
        /// `dum`: `Dum`my.
        ///
        /// Pops a dummy environment (an empty list) onto the `E` stack.
        InstDUM,
        /// `rap`: `R`ecursive `Ap`ply.
        /// Works like `ap`, only that it replaces an occurrence of a
        /// dummy environment with the current one, thus making recursive
        ///  functions possible.
        InstRAP,
        /// `sel`: `Sel`ect
        ///
        /// Expects two list arguments, and pops a value from the stack.
        /// The first list is executed if the popped value was non-nil,
        /// the second list otherwise. Before one of these list pointers
        ///  is made the new `C`, a pointer to the instruction following
        ///  `sel` is saved on the dump.
        InstSEL,
        /// `add`
        ///
        /// Pops two numbers off of the stack and adds them, pushing the
        /// result onto the stack. This will up-convert integers to floating
        /// point if necessary.
        ///
        /// TODO: figure out what happens when you try to add things that aren't
        /// numbers (maybe the compiler won't let this happen?).
        InstADD,
        /// `sub`: `Sub`tract
        ///
        /// Pops two numbers off of the stack and subtracts the first from the
        /// second, pushing the result onto the stack. This will up-convert
        /// integers to floating point if necessary.
        ///
        /// TODO: figure out what happens when you try to subtract things that
        /// aren't numbers (maybe the compiler won't let this happen?).
        InstSUB,
        /// `mul`: `Mul`tiply
        ///
        /// Pops two numbers off of the stack and multiplies them, pushing the
        /// result onto the stack. This will up-convert integers to floating
        /// point if necessary.
        ///
        /// TODO: figure out what happens when you try to multiply things that
        /// aren't numbers (maybe the compiler won't let this happen?).
        InstMUL,
        /// `div`: `Div`ide
        ///
        /// Pops two numbers off of the stack and divides the first by the second,
        /// pushing the result onto the stack. This performs integer division.
        ///
        /// TODO: figure out what happens when you try to divide things that
        /// aren't numbers (maybe the compiler won't let this happen?).
        InstDIV,
        /// `fdiv`: `F`loating-point `div`ide
        ///
        /// Pops two numbers off of the stack and divides the first by the second,
        /// pushing the result onto the stack. This performs float division.
        ///
        /// TODO: figure out what happens when you try to divide things that
        /// aren't numbers (maybe the compiler won't let this happen?).
        ///
        /// TODO: Not sure if there should be separate float and int divide words
        /// I guess the compiler can figure this out
        InstFDIV,
        /// `mod`: `Mod`ulo
        ///
        /// Pops two numbers off of the stack and divides the first by the second,
        /// pushing the remainder onto the stack.
        ///
        /// TODO: figure out what happens when you try to modulo things that
        /// aren't numbers (maybe the compiler won't let this happen?).
        InstMOD
        // TODO: add some hardcoded I/O instructions here so that you can
        //  do I/O without farming everything out to `stdio`
        // TODO: add `cons` and `cdr` words
    }

    /// Represents a SVM machine state
    pub struct State {
        stack: Stack<SVMCell>,
        env: Stack<SVMCell>,
        control: Stack<SVMCell>,
        dump: Stack<SVMCell>
    }

    impl State {

        /// Creates a new empty state
        fn new() -> State {
            State {
                stack: Stack::empty(),
                env: Stack::empty(),
                control: Stack::empty(),
                dump: Stack::empty()
            }
        }

        /// Evaluates an instruction.
        ///
        /// Evaluates an instruction against a state, returning a new state.
        pub fn eval(self, inst: SVMInstruction) -> State {
            match inst {
                SVMInstruction::InstNIL => State {
                    stack: self.stack.push(SVMCell::ListCell(box List::new())),
                    env: self.env,
                    control: self.control,
                    dump: self.dump
                },
                SVMInstruction::InstLDC(atom) => State {
                    stack: self.stack.push(SVMCell::AtomCell(atom)),
                    env: self.env,
                    control: self.control,
                    dump: self.dump
                },
                _ => { unimplemented!() }
            }
        }
    }

    /*
    /// Evaluates a program.
    ///
    /// Evaluates a program represented as an `Iterator` of `SVMInstruction`s.
    /// Returns the final machine state at the end of execution

    pub fn evalProgram(insts: Iterator<Item=SVMInstruction>) -> State {
        insts.fold(State::new(), |last_state: State, inst: SVMInstruction| last_state.eval(inst));
    }*/

    #[cfg(test)]
    mod tests {
        use super::State;
        use super::{SVMInstruction, SVMCell, Atom};
        use super::slist::List::{Cons,Nil};

        #[test]
        fn test_empty_state() {
            let state = State::new();
            assert_eq!(state.stack.length(), 0);
            assert_eq!(state.env.length(), 0);
            assert_eq!(state.control.length(), 0);
            assert_eq!(state.dump.length(), 0);
        }

        #[test]
        fn test_eval_nil () {
            let mut state = State::new();
            assert_eq!(state.stack.peek(), None);
            state = state.eval(SVMInstruction::InstNIL);
            assert_eq!(state.stack.peek(), Some(&SVMCell::ListCell(box Nil)));
        }

        #[test]
        fn test_eval_ldc () {
            let mut state = State::new();
            assert_eq!(state.stack.peek(), None);
            state = state.eval(SVMInstruction::InstLDC(Atom::SInt(1)));
            assert_eq!(state.stack.peek(), Some(&SVMCell::AtomCell(Atom::SInt(1))));
            state = state.eval(SVMInstruction::InstLDC(Atom::Char('a')));
            assert_eq!(state.stack.peek(), Some(&SVMCell::AtomCell(Atom::Char('a'))));
            state = state.eval(SVMInstruction::InstLDC(Atom::Float(1.0f64)));
            assert_eq!(state.stack.peek(), Some(&SVMCell::AtomCell(Atom::Float(1.0f64))));
        }
    }

}
<|endoftext|>


<commit_before>//! We use code generation heavily in rust-analyzer.
//!
//! Rather then doing it via proc-macros, we use old-school way of just dumping
//! the source code.
//!
//! This module's submodules define specific bits that we generate.

mod gen_syntax;
mod gen_parser_tests;
mod gen_assists_docs;

use std::{fs, mem, path::Path};

use crate::Result;

pub use self::{
    gen_assists_docs::generate_assists_docs, gen_parser_tests::generate_parser_tests,
    gen_syntax::generate_syntax,
};

const GRAMMAR_DIR: &str = "crates/ra_parser/src/grammar";
const OK_INLINE_TESTS_DIR: &str = "crates/ra_syntax/test_data/parser/inline/ok";
const ERR_INLINE_TESTS_DIR: &str = "crates/ra_syntax/test_data/parser/inline/err";

const SYNTAX_KINDS: &str = "crates/ra_parser/src/syntax_kind/generated.rs";
const AST_NODES: &str = "crates/ra_syntax/src/ast/generated/nodes.rs";
const AST_TOKENS: &str = "crates/ra_syntax/src/ast/generated/tokens.rs";

const ASSISTS_DIR: &str = "crates/ra_assists/src/handlers";
const ASSISTS_TESTS: &str = "crates/ra_assists/src/doc_tests/generated.rs";
const ASSISTS_DOCS: &str = "docs/user/assists.md";

#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum Mode {
    Overwrite,
    Verify,
}

/// A helper to update file on disk if it has changed.
/// With verify = false,
fn update(path: &Path, contents: &str, mode: Mode) -> Result<()> {
    match fs::read_to_string(path) {
        Ok(ref old_contents) if normalize(old_contents) == normalize(contents) => {
            return Ok(());
        }
        _ => (),
    }
    if mode == Mode::Verify {
        anyhow::bail!("`{}` is not up-to-date", path.display());
    }
    eprintln!("updating {}", path.display());
    fs::write(path, contents)?;
    return Ok(());

    fn normalize(s: &str) -> String {
        s.replace("\r\n", "\n")
    }
}

fn extract_comment_blocks(text: &str) -> Vec<Vec<String>> {
    do_extract_comment_blocks(text, false)
}

fn extract_comment_blocks_with_empty_lines(text: &str) -> Vec<Vec<String>> {
    do_extract_comment_blocks(text, true)
}

fn do_extract_comment_blocks(text: &str, allow_blocks_with_empty_lins: bool) -> Vec<Vec<String>> {
    let mut res = Vec::new();

    let prefix = "// ";
    let lines = text.lines().map(str::trim_start);

    let mut block = vec![];
    for line in lines {
        if line == "//" && allow_blocks_with_empty_lins {
            block.push(String::new());
            continue;
        }

        let is_comment = line.starts_with(prefix);
        if is_comment {
            block.push(line[prefix.len()..].to_string());
        } else if !block.is_empty() {
            res.push(mem::replace(&mut block, Vec::new()));
        }
    }
    if !block.is_empty() {
        res.push(mem::replace(&mut block, Vec::new()))
    }
    res
}
<commit_msg>Refucktor codegen<commit_after>//! We use code generation heavily in rust-analyzer.
//!
//! Rather then doing it via proc-macros, we use old-school way of just dumping
//! the source code.
//!
//! This module's submodules define specific bits that we generate.

mod gen_syntax;
mod gen_parser_tests;
mod gen_assists_docs;

use std::{mem, path::Path};

use crate::{not_bash::fs2, Result};

pub use self::{
    gen_assists_docs::generate_assists_docs, gen_parser_tests::generate_parser_tests,
    gen_syntax::generate_syntax,
};

const GRAMMAR_DIR: &str = "crates/ra_parser/src/grammar";
const OK_INLINE_TESTS_DIR: &str = "crates/ra_syntax/test_data/parser/inline/ok";
const ERR_INLINE_TESTS_DIR: &str = "crates/ra_syntax/test_data/parser/inline/err";

const SYNTAX_KINDS: &str = "crates/ra_parser/src/syntax_kind/generated.rs";
const AST_NODES: &str = "crates/ra_syntax/src/ast/generated/nodes.rs";
const AST_TOKENS: &str = "crates/ra_syntax/src/ast/generated/tokens.rs";

const ASSISTS_DIR: &str = "crates/ra_assists/src/handlers";
const ASSISTS_TESTS: &str = "crates/ra_assists/src/doc_tests/generated.rs";
const ASSISTS_DOCS: &str = "docs/user/assists.md";

#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum Mode {
    Overwrite,
    Verify,
}

/// A helper to update file on disk if it has changed.
/// With verify = false,
fn update(path: &Path, contents: &str, mode: Mode) -> Result<()> {
    match fs2::read_to_string(path) {
        Ok(ref old_contents) if normalize(old_contents) == normalize(contents) => {
            return Ok(());
        }
        _ => (),
    }
    if mode == Mode::Verify {
        anyhow::bail!("`{}` is not up-to-date", path.display());
    }
    eprintln!("updating {}", path.display());
    fs2::write(path, contents)?;
    return Ok(());

    fn normalize(s: &str) -> String {
        s.replace("\r\n", "\n")
    }
}

fn extract_comment_blocks(text: &str) -> Vec<Vec<String>> {
    do_extract_comment_blocks(text, false)
}

fn extract_comment_blocks_with_empty_lines(text: &str) -> Vec<Vec<String>> {
    do_extract_comment_blocks(text, true)
}

fn do_extract_comment_blocks(text: &str, allow_blocks_with_empty_lines: bool) -> Vec<Vec<String>> {
    let mut res = Vec::new();

    let prefix = "// ";
    let lines = text.lines().map(str::trim_start);

    let mut block = vec![];
    for line in lines {
        if line == "//" && allow_blocks_with_empty_lines {
            block.push(String::new());
            continue;
        }

        let is_comment = line.starts_with(prefix);
        if is_comment {
            block.push(line[prefix.len()..].to_string());
        } else if !block.is_empty() {
            res.push(mem::replace(&mut block, Vec::new()));
        }
    }
    if !block.is_empty() {
        res.push(mem::replace(&mut block, Vec::new()))
    }
    res
}
<|endoftext|>


<commit_before>#[doc(hidden)]
#[macro_export]
macro_rules! forward_to_deserialize_method {
    ($func:ident($($arg:ty),*)) => {
        #[inline]
        fn $func<__V>(self, $(_: $arg,)* visitor: __V) -> $crate::export::Result<__V::Value, Self::Error>
            where __V: $crate::de::Visitor<'de>
        {
            self.deserialize(visitor)
        }
    };
}

#[doc(hidden)]
#[macro_export]
macro_rules! forward_to_deserialize_helper {
    (bool) => {
        forward_to_deserialize_method!{deserialize_bool()}
    };
    (u8) => {
        forward_to_deserialize_method!{deserialize_u8()}
    };
    (u16) => {
        forward_to_deserialize_method!{deserialize_u16()}
    };
    (u32) => {
        forward_to_deserialize_method!{deserialize_u32()}
    };
    (u64) => {
        forward_to_deserialize_method!{deserialize_u64()}
    };
    (i8) => {
        forward_to_deserialize_method!{deserialize_i8()}
    };
    (i16) => {
        forward_to_deserialize_method!{deserialize_i16()}
    };
    (i32) => {
        forward_to_deserialize_method!{deserialize_i32()}
    };
    (i64) => {
        forward_to_deserialize_method!{deserialize_i64()}
    };
    (f32) => {
        forward_to_deserialize_method!{deserialize_f32()}
    };
    (f64) => {
        forward_to_deserialize_method!{deserialize_f64()}
    };
    (char) => {
        forward_to_deserialize_method!{deserialize_char()}
    };
    (str) => {
        forward_to_deserialize_method!{deserialize_str()}
    };
    (string) => {
        forward_to_deserialize_method!{deserialize_string()}
    };
    (unit) => {
        forward_to_deserialize_method!{deserialize_unit()}
    };
    (option) => {
        forward_to_deserialize_method!{deserialize_option()}
    };
    (seq) => {
        forward_to_deserialize_method!{deserialize_seq()}
    };
    (seq_fixed_size) => {
        forward_to_deserialize_method!{deserialize_seq_fixed_size(usize)}
    };
    (bytes) => {
        forward_to_deserialize_method!{deserialize_bytes()}
    };
    (byte_buf) => {
        forward_to_deserialize_method!{deserialize_byte_buf()}
    };
    (map) => {
        forward_to_deserialize_method!{deserialize_map()}
    };
    (unit_struct) => {
        forward_to_deserialize_method!{deserialize_unit_struct(&'static str)}
    };
    (newtype_struct) => {
        forward_to_deserialize_method!{deserialize_newtype_struct(&'static str)}
    };
    (tuple_struct) => {
        forward_to_deserialize_method!{deserialize_tuple_struct(&'static str, usize)}
    };
    (struct) => {
        forward_to_deserialize_method!{deserialize_struct(&'static str, &'static [&'static str])}
    };
    (struct_field) => {
        forward_to_deserialize_method!{deserialize_struct_field()}
    };
    (tuple) => {
        forward_to_deserialize_method!{deserialize_tuple(usize)}
    };
    (enum) => {
        forward_to_deserialize_method!{deserialize_enum(&'static str, &'static [&'static str])}
    };
    (ignored_any) => {
        forward_to_deserialize_method!{deserialize_ignored_any()}
    };
}

// Super explicit first paragraph because this shows up at the top level and
// trips up people who are just looking for basic Serialize / Deserialize
// documentation.
//
/// Helper macro when implementing the `Deserializer` part of a new data format
/// for Serde.
///
/// Some `Deserializer` implementations for self-describing formats do not care
/// what hint the `Visitor` gives them, they just want to blindly call the
/// `Visitor` method corresponding to the data they can tell is in the input.
/// This requires repetitive implementations of all the `Deserializer` trait
/// methods.
///
/// ```rust
/// # #[macro_use]
/// # extern crate serde;
/// #
/// # use serde::de::{value, Deserializer, Visitor};
/// #
/// # struct MyDeserializer;
/// #
/// # impl<'de> Deserializer<'de> for MyDeserializer {
/// #     type Error = value::Error;
/// #
/// #     fn deserialize<V>(self, _: V) -> Result<V::Value, Self::Error>
/// #         where V: Visitor<'de>
/// #     {
/// #         unimplemented!()
/// #     }
/// #
/// #[inline]
/// fn deserialize_bool<V>(self, visitor: V) -> Result<V::Value, Self::Error>
///     where V: Visitor<'de>
/// {
///     self.deserialize(visitor)
/// }
/// #
/// #     forward_to_deserialize! {
/// #         u8 u16 u32 u64 i8 i16 i32 i64 f32 f64 char str string unit option
/// #         seq seq_fixed_size bytes byte_buf map unit_struct newtype_struct
/// #         tuple_struct struct struct_field tuple enum ignored_any
/// #     }
/// # }
/// #
/// # fn main() {}
/// ```
///
/// The `forward_to_deserialize!` macro implements these simple forwarding
/// methods so that they forward directly to `Deserializer::deserialize`. You
/// can choose which methods to forward.
///
/// ```rust
/// # #[macro_use]
/// # extern crate serde;
/// #
/// # use serde::de::{value, Deserializer, Visitor};
/// #
/// # struct MyDeserializer;
/// #
/// impl<'de> Deserializer<'de> for MyDeserializer {
/// #   type Error = value::Error;
/// #
///     fn deserialize<V>(self, visitor: V) -> Result<V::Value, Self::Error>
///         where V: Visitor<'de>
///     {
///         /* ... */
/// #       let _ = visitor;
/// #       unimplemented!()
///     }
///
///     forward_to_deserialize! {
///         bool u8 u16 u32 u64 i8 i16 i32 i64 f32 f64 char str string unit option
///         seq seq_fixed_size bytes byte_buf map unit_struct newtype_struct
///         tuple_struct struct struct_field tuple enum ignored_any
///     }
/// }
/// #
/// # fn main() {}
/// ```
///
/// The macro assumes the convention that your `Deserializer` lifetime parameter
/// is called `'de`. It will not work if the `Deserializer` lifetime parameter
/// is called something different.
#[macro_export]
macro_rules! forward_to_deserialize {
    ($($func:ident)*) => {
        $(forward_to_deserialize_helper!{$func})*
    };
}
<commit_msg>Configurable forward_to_deserialize<commit_after>#[doc(hidden)]
#[macro_export]
macro_rules! forward_to_deserialize_method {
    ($func:ident<$l:tt, $v:ident>($($arg:ident : $ty:ty),*)) => {
        #[inline]
        fn $func<$v>(self, $($arg: $ty,)* visitor: $v) -> $crate::export::Result<$v::Value, Self::Error>
            where $v: $crate::de::Visitor<$l>
        {
            $(
                let _ = $arg;
            )*
            self.deserialize(visitor)
        }
    };
}

#[doc(hidden)]
#[macro_export]
macro_rules! forward_to_deserialize_helper {
    (bool<$l:tt, $v:ident>) => {
        forward_to_deserialize_method!{deserialize_bool<$l, $v>()}
    };
    (u8<$l:tt, $v:ident>) => {
        forward_to_deserialize_method!{deserialize_u8<$l, $v>()}
    };
    (u16<$l:tt, $v:ident>) => {
        forward_to_deserialize_method!{deserialize_u16<$l, $v>()}
    };
    (u32<$l:tt, $v:ident>) => {
        forward_to_deserialize_method!{deserialize_u32<$l, $v>()}
    };
    (u64<$l:tt, $v:ident>) => {
        forward_to_deserialize_method!{deserialize_u64<$l, $v>()}
    };
    (i8<$l:tt, $v:ident>) => {
        forward_to_deserialize_method!{deserialize_i8<$l, $v>()}
    };
    (i16<$l:tt, $v:ident>) => {
        forward_to_deserialize_method!{deserialize_i16<$l, $v>()}
    };
    (i32<$l:tt, $v:ident>) => {
        forward_to_deserialize_method!{deserialize_i32<$l, $v>()}
    };
    (i64<$l:tt, $v:ident>) => {
        forward_to_deserialize_method!{deserialize_i64<$l, $v>()}
    };
    (f32<$l:tt, $v:ident>) => {
        forward_to_deserialize_method!{deserialize_f32<$l, $v>()}
    };
    (f64<$l:tt, $v:ident>) => {
        forward_to_deserialize_method!{deserialize_f64<$l, $v>()}
    };
    (char<$l:tt, $v:ident>) => {
        forward_to_deserialize_method!{deserialize_char<$l, $v>()}
    };
    (str<$l:tt, $v:ident>) => {
        forward_to_deserialize_method!{deserialize_str<$l, $v>()}
    };
    (string<$l:tt, $v:ident>) => {
        forward_to_deserialize_method!{deserialize_string<$l, $v>()}
    };
    (unit<$l:tt, $v:ident>) => {
        forward_to_deserialize_method!{deserialize_unit<$l, $v>()}
    };
    (option<$l:tt, $v:ident>) => {
        forward_to_deserialize_method!{deserialize_option<$l, $v>()}
    };
    (seq<$l:tt, $v:ident>) => {
        forward_to_deserialize_method!{deserialize_seq<$l, $v>()}
    };
    (seq_fixed_size<$l:tt, $v:ident>) => {
        forward_to_deserialize_method!{deserialize_seq_fixed_size<$l, $v>(len: usize)}
    };
    (bytes<$l:tt, $v:ident>) => {
        forward_to_deserialize_method!{deserialize_bytes<$l, $v>()}
    };
    (byte_buf<$l:tt, $v:ident>) => {
        forward_to_deserialize_method!{deserialize_byte_buf<$l, $v>()}
    };
    (map<$l:tt, $v:ident>) => {
        forward_to_deserialize_method!{deserialize_map<$l, $v>()}
    };
    (unit_struct<$l:tt, $v:ident>) => {
        forward_to_deserialize_method!{deserialize_unit_struct<$l, $v>(name: &'static str)}
    };
    (newtype_struct<$l:tt, $v:ident>) => {
        forward_to_deserialize_method!{deserialize_newtype_struct<$l, $v>(name: &'static str)}
    };
    (tuple_struct<$l:tt, $v:ident>) => {
        forward_to_deserialize_method!{deserialize_tuple_struct<$l, $v>(name: &'static str, len: usize)}
    };
    (struct<$l:tt, $v:ident>) => {
        forward_to_deserialize_method!{deserialize_struct<$l, $v>(name: &'static str, fields: &'static [&'static str])}
    };
    (struct_field<$l:tt, $v:ident>) => {
        forward_to_deserialize_method!{deserialize_struct_field<$l, $v>()}
    };
    (tuple<$l:tt, $v:ident>) => {
        forward_to_deserialize_method!{deserialize_tuple<$l, $v>(len: usize)}
    };
    (enum<$l:tt, $v:ident>) => {
        forward_to_deserialize_method!{deserialize_enum<$l, $v>(name: &'static str, variants: &'static [&'static str])}
    };
    (ignored_any<$l:tt, $v:ident>) => {
        forward_to_deserialize_method!{deserialize_ignored_any<$l, $v>()}
    };
}

// Super explicit first paragraph because this shows up at the top level and
// trips up people who are just looking for basic Serialize / Deserialize
// documentation.
//
/// Helper macro when implementing the `Deserializer` part of a new data format
/// for Serde.
///
/// Some `Deserializer` implementations for self-describing formats do not care
/// what hint the `Visitor` gives them, they just want to blindly call the
/// `Visitor` method corresponding to the data they can tell is in the input.
/// This requires repetitive implementations of all the `Deserializer` trait
/// methods.
///
/// ```rust
/// # #[macro_use]
/// # extern crate serde;
/// #
/// # use serde::de::{value, Deserializer, Visitor};
/// #
/// # struct MyDeserializer;
/// #
/// # impl<'de> Deserializer<'de> for MyDeserializer {
/// #     type Error = value::Error;
/// #
/// #     fn deserialize<V>(self, _: V) -> Result<V::Value, Self::Error>
/// #         where V: Visitor<'de>
/// #     {
/// #         unimplemented!()
/// #     }
/// #
/// #[inline]
/// fn deserialize_bool<V>(self, visitor: V) -> Result<V::Value, Self::Error>
///     where V: Visitor<'de>
/// {
///     self.deserialize(visitor)
/// }
/// #
/// #     forward_to_deserialize! {
/// #         u8 u16 u32 u64 i8 i16 i32 i64 f32 f64 char str string unit option
/// #         seq seq_fixed_size bytes byte_buf map unit_struct newtype_struct
/// #         tuple_struct struct struct_field tuple enum ignored_any
/// #     }
/// # }
/// #
/// # fn main() {}
/// ```
///
/// The `forward_to_deserialize!` macro implements these simple forwarding
/// methods so that they forward directly to `Deserializer::deserialize`. You
/// can choose which methods to forward.
///
/// ```rust
/// # #[macro_use]
/// # extern crate serde;
/// #
/// # use serde::de::{value, Deserializer, Visitor};
/// #
/// # struct MyDeserializer;
/// #
/// impl<'de> Deserializer<'de> for MyDeserializer {
/// #   type Error = value::Error;
/// #
///     fn deserialize<V>(self, visitor: V) -> Result<V::Value, Self::Error>
///         where V: Visitor<'de>
///     {
///         /* ... */
/// #       let _ = visitor;
/// #       unimplemented!()
///     }
///
///     forward_to_deserialize! {
///         bool u8 u16 u32 u64 i8 i16 i32 i64 f32 f64 char str string unit option
///         seq seq_fixed_size bytes byte_buf map unit_struct newtype_struct
///         tuple_struct struct struct_field tuple enum ignored_any
///     }
/// }
/// #
/// # fn main() {}
/// ```
///
/// The macro assumes the convention that your `Deserializer` lifetime parameter
/// is called `'de` and that the `Visitor` type parameters on each method are
/// called `V`. A different type parameter and a different lifetime can be
/// specified explicitly if necessary.
///
/// ```rust
/// # #[macro_use] extern crate serde;
/// # use serde::de::{value, Deserializer, Visitor};
/// # pub struct MyDeserializer;
/// # impl<'q> Deserializer<'q> for MyDeserializer {
/// #   type Error = value::Error;
/// #    fn deserialize<V>(self, visitor: V) -> Result<V::Value, Self::Error>
/// #        where V: Visitor<'q> { unimplemented!() }
/// forward_to_deserialize! {
///     <W: Visitor<'q>>
///     bool u8 u16 u32 u64 i8 i16 i32 i64 f32 f64 char str string unit option
///     seq seq_fixed_size bytes byte_buf map unit_struct newtype_struct
///     tuple_struct struct struct_field tuple enum ignored_any
/// }
/// # }
/// # fn main() {}
/// ```
#[macro_export]
macro_rules! forward_to_deserialize {
    (<$visitor:ident: Visitor<$lifetime:tt>> $($func:ident)*) => {
        $(forward_to_deserialize_helper!{$func<$lifetime, $visitor>})*
    };
    ($($func:ident)*) => {
        $(forward_to_deserialize_helper!{$func<'de, V>})*
    };
}
<|endoftext|>


<commit_before>//! Helper module which helps to determine amount of threads to be used
//! during tests execution.
use std::env;
use std::thread;

#[allow(deprecated)]
pub fn get_concurrency() -> usize {
    match env::var("RUST_TEST_THREADS") {
        Ok(s) => {
            let opt_n: Option<usize> = s.parse().ok();
            match opt_n {
                Some(n) if n > 0 => n,
                _ => panic!("RUST_TEST_THREADS is `{}`, should be a positive integer.", s),
            }
        }
        Err(..) => thread::available_concurrency().map(|n| n.get()).unwrap_or(1),
    }
}
<commit_msg>make concurrency helper more pleasant to read<commit_after>//! Helper module which helps to determine amount of threads to be used
//! during tests execution.
use std::{env, num::NonZeroUsize, thread};

pub fn get_concurrency() -> usize {
    if let Ok(value) = env::var("RUST_TEST_THREADS") {
        match value.parse::<NonZeroUsize>().ok() {
            Some(n) => n.get(),
            _ => panic!("RUST_TEST_THREADS is `{}`, should be a positive integer.", value),
        }
    } else {
        thread::available_concurrency().map(|n| n.get()).unwrap_or(1)
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2017 Erik Nordstrøm <erik@nordstroem.no>
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

use self::Color::*;
use self::Rank::*;
use std::slice::Iter;
use std::mem;

extern crate rand;
use self::rand::Rng;

#[derive(Copy, Clone)]
pub enum Color
{
    Spades   = 1, // ♠,
    Hearts,       // ♥,
    Diamonds,     // ♦,
    Clubs,        // ♣
}

#[derive(Copy, Clone)]
pub enum Rank
{
    A = 1, Two, Three, Four, Five, Six,
    Seven, Eight, Nine, Ten, J, Q, K
}

impl Color
{
    // https://stackoverflow.com/a/21376984
    fn iterator () -> Iter<'static, Color>
    {
        static COLORS: [Color; 4] = [Spades, Hearts, Diamonds, Clubs];
        COLORS.into_iter()
    }
}

impl Rank
{
    fn iterator () -> Iter<'static, Rank>
    {
        static RANKS: [Rank; 13] =
            [ A, Two, Three, Four, Five, Six, Seven,
              Eight, Nine, Ten, J, Q, K ] ;
        RANKS.into_iter()
    }
}

pub struct Card
{
    pub color:     Color,
    pub rank:      Rank,
    pub id:        u8,
    pub facing_up: bool
}

pub fn shuffled_deck () -> Box<[Card; 52]>
{
    // https://stackoverflow.com/a/31361031
    let all_cards = unsafe
    {
        let mut _all_cards: [Card; 52] = mem::uninitialized();

        let mut ids_available: Vec<u8> = (0..52).collect();

        // https://stackoverflow.com/a/26035435
        let mut ids_avail_slice = ids_available.as_mut_slice();
        rand::thread_rng().shuffle(ids_avail_slice);
        let mut id_iter = ids_avail_slice.iter_mut();

        for color in Color::iterator()
        {
            for rank in Rank::iterator()
            {

                let curr_id = *(id_iter.next().unwrap());

                let card = Card
                {
                    color:     *color,
                    rank:      *rank,
                    id:        curr_id,
                    facing_up: false
                };

                _all_cards[curr_id as usize] = card;
            }
        }

        _all_cards
    };

    Box::new(all_cards)
}
<commit_msg>Whitespace.<commit_after>/*
 * Copyright (c) 2017 Erik Nordstrøm <erik@nordstroem.no>
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

use self::Color::*;
use self::Rank::*;
use std::slice::Iter;
use std::mem;

extern crate rand;
use self::rand::Rng;

#[derive(Copy, Clone)]
pub enum Color
{
    Spades   = 1, // ♠,
    Hearts,       // ♥,
    Diamonds,     // ♦,
    Clubs,        // ♣
}

#[derive(Copy, Clone)]
pub enum Rank
{
    A = 1, Two, Three, Four, Five, Six,
    Seven, Eight, Nine, Ten, J, Q, K
}

impl Color
{
    // https://stackoverflow.com/a/21376984
    fn iterator () -> Iter<'static, Color>
    {
        static COLORS: [Color; 4] = [Spades, Hearts, Diamonds, Clubs];
        COLORS.into_iter()
    }
}

impl Rank
{
    fn iterator () -> Iter<'static, Rank>
    {
        static RANKS: [Rank; 13] =
            [ A, Two, Three, Four, Five, Six, Seven,
              Eight, Nine, Ten, J, Q, K ] ;
        RANKS.into_iter()
    }
}

pub struct Card
{
    pub color:     Color,
    pub rank:      Rank,
    pub id:        u8,
    pub facing_up: bool
}

pub fn shuffled_deck () -> Box<[Card; 52]>
{
    // https://stackoverflow.com/a/31361031
    let all_cards = unsafe
    {
        let mut _all_cards: [Card; 52] = mem::uninitialized();

        let mut ids_available: Vec<u8> = (0..52).collect();

        // https://stackoverflow.com/a/26035435
        let mut ids_avail_slice = ids_available.as_mut_slice();
        rand::thread_rng().shuffle(ids_avail_slice);
        let mut id_iter = ids_avail_slice.iter_mut();

        for color in Color::iterator()
        {
            for rank in Rank::iterator()
            {
                let curr_id = *(id_iter.next().unwrap());

                let card = Card
                {
                    color:     *color,
                    rank:      *rank,
                    id:        curr_id,
                    facing_up: false
                };

                _all_cards[curr_id as usize] = card;
            }
        }

        _all_cards
    };

    Box::new(all_cards)
}
<|endoftext|>


<commit_before>use slack_api as slack;

use tokio::sync::mpsc;
use tokio_stream::wrappers::ReceiverStream;

use std::env;
use std::path::Path;
use std::result::Result;
use std::thread;

use chrono::prelude::{Utc, DateTime};

use karmator_rust::core::database;
use karmator_rust::core::cache;
use karmator_rust::core::signal;
use karmator_rust::core::bot;
use karmator_rust::bot::user_event;

// TODO:
// Fix voting for - @luisp++ case it should -> luisp++ for the database
// Fix command parsing for <@user> -> user for commands
// FIX self-voting on reacji since we now also upvote the owner of the message itself along with
//      the message

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let filename = env::var("SQLITE_FILE").map_err(|_| "SQLITE_FILE env var must be set")?;
    let token = env::var("SLACK_API_TOKEN").map_err(|_| "SLACK_API_TOKEN env var must be set")?;
    let backup = env::var("SQLITE_BACKUP_DIR").map_err(|_| "SQLITE_BACKUP_DIR env var must be set")?;
    let client = slack::default_client().map_err(|e| format!("Could not get default_client, {:?}", e))?;

    // Uptime of program start
    let start_time: DateTime<Utc> = Utc::now();

    // System Cache manager
    let cache = cache::Cache::new(&token, client.clone());

    // Shutdown Signal
    let (sql_shutdown_tx, signal) = signal::Signal::new();

    //*******************
    // Sql bits
    //*******************

    // Sql request/reply channels for the downstream handlers to talk to the
    // sqlite worker thread
    let (sql_tx, sql_rx) = mpsc::channel(32);

    // Launch the sqlite worker thread
    let sql_worker = thread::Builder::new().name("sqlite_worker".into()).spawn(move || {
        println!("INFO [Sql Worker]: Launching");

        let res = database::process_queries(
            Path::new(&filename),
            ReceiverStream::new(sql_rx),
        );
        eprintln!("ERROR [Sql Worker]: {:?}", res);

        // Worker died, signal the shutdown signal
        let _ = sql_shutdown_tx.send(true);

        println!("INFO [Sql Worker]: Exiting");
    })?;

    //*******************
    // Core bot eventloop
    //*******************

    bot::default_event_loop(
        &token,
        client.clone(),
        signal,
        |event, msg_id, tx| {
            let sql_tx2 = sql_tx.clone();
            let cache2 = cache.clone();

            tokio::spawn(async move {
                // TODO: check result
                let _ = user_event::process_user_message(
                    msg_id,
                    event,
                    tx,
                    sql_tx2,
                    start_time,
                    cache2,
                ).await;
            });
        },
        || {
            // Install an backup job for backing up the database
            let sql_tx3 = sql_tx.clone();
            let path = backup.clone();
            tokio::spawn(async move {
                let _ = database::backup(
                    path,
                    sql_tx3
                ).await;
            });
        }
    ).await?;

    //*******************
    // Sql bits
    //*******************

    // Force drop the sender (since all other sender clone should be dropped by now)
    drop(sql_tx);
    let res = sql_worker.join();
    println!("SYSTEM [Sql Worker]: Thread Join: {:?}", res);

    Ok(())
}
<commit_msg>Make backup optional<commit_after>use slack_api as slack;

use tokio::sync::mpsc;
use tokio_stream::wrappers::ReceiverStream;

use std::env;
use std::path::Path;
use std::result::Result;
use std::thread;

use chrono::prelude::{Utc, DateTime};

use karmator_rust::core::database;
use karmator_rust::core::cache;
use karmator_rust::core::signal;
use karmator_rust::core::bot;
use karmator_rust::bot::user_event;

// TODO:
// Fix voting for - @luisp++ case it should -> luisp++ for the database
// Fix command parsing for <@user> -> user for commands
// FIX self-voting on reacji since we now also upvote the owner of the message itself along with
//      the message

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let filename = env::var("SQLITE_FILE").map_err(|_| "SQLITE_FILE env var must be set")?;
    let token = env::var("SLACK_API_TOKEN").map_err(|_| "SLACK_API_TOKEN env var must be set")?;
    let backup = env::var("SQLITE_BACKUP_DIR");
    let client = slack::default_client().map_err(|e| format!("Could not get default_client, {:?}", e))?;

    // Uptime of program start
    let start_time: DateTime<Utc> = Utc::now();

    // System Cache manager
    let cache = cache::Cache::new(&token, client.clone());

    // Shutdown Signal
    let (sql_shutdown_tx, signal) = signal::Signal::new();

    //*******************
    // Sql bits
    //*******************

    // Sql request/reply channels for the downstream handlers to talk to the
    // sqlite worker thread
    let (sql_tx, sql_rx) = mpsc::channel(32);

    // Launch the sqlite worker thread
    let sql_worker = thread::Builder::new().name("sqlite_worker".into()).spawn(move || {
        println!("INFO [Sql Worker]: Launching");

        let res = database::process_queries(
            Path::new(&filename),
            ReceiverStream::new(sql_rx),
        );
        eprintln!("ERROR [Sql Worker]: {:?}", res);

        // Worker died, signal the shutdown signal
        let _ = sql_shutdown_tx.send(true);

        println!("INFO [Sql Worker]: Exiting");
    })?;

    //*******************
    // Setting up backup
    //*******************
    let backup_callback: Box<dyn Fn()> = match backup {
        Ok(b) => {
            // Install an backup job for backing up the database
            let sql_tx3 = sql_tx.clone();
            Box::new(move || {
                let sql_tx4 = sql_tx3.clone();
                let path = b.clone();
                tokio::spawn(async move {
                    let _ = database::backup(
                        path.clone(),
                        sql_tx4
                    ).await;
                });
            })
        },
        Err(_) => {
            println!("WARNING [Backup]: SQLITE_BACKUP_DIR env var must be set");
            Box::new(|| {})
        },
    };

    //*******************
    // Core bot eventloop
    //*******************
    bot::default_event_loop(
        &token,
        client.clone(),
        signal,
        |event, msg_id, tx| {
            let sql_tx2 = sql_tx.clone();
            let cache2 = cache.clone();

            tokio::spawn(async move {
                // TODO: check result
                let _ = user_event::process_user_message(
                    msg_id,
                    event,
                    tx,
                    sql_tx2,
                    start_time,
                    cache2,
                ).await;
            });
        },
        backup_callback,
    ).await?;

    //*******************
    // Sql bits
    //*******************

    // Force drop the sender (since all other sender clone should be dropped by now)
    drop(sql_tx);
    let res = sql_worker.join();
    println!("SYSTEM [Sql Worker]: Thread Join: {:?}", res);

    Ok(())
}
<|endoftext|>


<commit_before>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

extern crate libstratis;
#[macro_use]
extern crate log;
extern crate env_logger;
extern crate clap;
extern crate dbus;
extern crate term;
extern crate libc;

#[cfg(test)]
extern crate quickcheck;

use std::io::Write;
use std::env;
use std::error::Error;
use std::rc::Rc;
use std::cell::RefCell;
use std::process::exit;

use clap::{App, Arg};
use log::LogLevelFilter;
use env_logger::LogBuilder;
use dbus::WatchEvent;

use libstratis::engine::{Engine, SimEngine, StratEngine};
use libstratis::stratis::{StratisResult, StratisError, VERSION};

/// Try to write the error from the program to stderr, vehemently.
/// Return an error if stderr unavailable or writing was a failure.
fn write_err(err: StratisError) -> StratisResult<()> {
    let mut out = term::stderr().ok_or(StratisError::StderrNotFound)?;
    out.fg(term::color::RED)?;
    writeln!(out, "{}", err.description())?;
    out.reset()?;
    Ok(())
}

/// If writing a program error to stderr fails, panic.
fn write_or_panic(err: StratisError) -> () {
    if let Err(e) = write_err(err) {
        panic!("Unable to write to stderr: {}", e)
    }
}

fn run() -> StratisResult<()> {

    let matches = App::new("stratis")
        .version(VERSION)
        .about("Stratis storage management")
        .arg(Arg::with_name("debug")
                 .long("debug")
                 .help("Print additional output for debugging"))
        .arg(Arg::with_name("sim")
                 .long("sim")
                 .help("Use simulator engine"))
        .get_matches();

    let mut builder = LogBuilder::new();
    if matches.is_present("debug") {
        builder.filter(Some("stratisd"), LogLevelFilter::Debug);
        builder.filter(Some("libstratis"), LogLevelFilter::Debug);
    } else {
        if let Ok(s) = env::var("RUST_LOG") {
            builder.parse(&s);
        }
    };

    builder
        .init()
        .expect("This is the first and only initialization of the logger; it must succeed");

    let engine: Rc<RefCell<Engine>> = {
        if matches.is_present("sim") {
            info!("Using SimEngine");
            Rc::new(RefCell::new(SimEngine::default()))
        } else {
            info!("Using StratEngine");
            Rc::new(RefCell::new(StratEngine::initialize()?))
        }
    };

    let (dbus_conn, mut tree, dbus_context) = libstratis::dbus_api::connect(Rc::clone(&engine))?;

    // Get a list of fds to poll for
    let mut fds: Vec<_> = dbus_conn
        .watch_fds()
        .iter()
        .map(|w| w.to_pollfd())
        .collect();

    loop {
        // Poll them with a 10 s timeout
        let r = unsafe { libc::poll(fds.as_mut_ptr(), fds.len() as libc::c_ulong, 10000) };
        assert!(r >= 0);

        // And handle incoming events
        for pfd in fds.iter().filter(|pfd| pfd.revents != 0) {
            for item in dbus_conn.watch_handle(pfd.fd, WatchEvent::from_revents(pfd.revents)) {
                if let Err(r) = libstratis::dbus_api::handle(&dbus_conn,
                                                             &item,
                                                             &mut tree,
                                                             &dbus_context) {
                    write_or_panic(From::from(r));
                }
            }
        }

        // Ask the engine to check its pools
        engine.borrow_mut().check()
    }
}

fn main() {
    let error_code = match run() {
        Ok(_) => 0,
        Err(err) => {
            write_or_panic(err);
            1
        }
    };
    exit(error_code);
}
<commit_msg>Implement support for event-driven notifications in stratisd<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

extern crate libstratis;
#[macro_use]
extern crate log;
extern crate env_logger;
extern crate clap;
extern crate dbus;
extern crate term;
extern crate libc;

#[cfg(test)]
extern crate quickcheck;

use std::io::Write;
use std::env;
use std::error::Error;
use std::rc::Rc;
use std::cell::RefCell;
use std::process::exit;

use clap::{App, Arg};
use log::LogLevelFilter;
use env_logger::LogBuilder;
use dbus::WatchEvent;

use libstratis::engine::{Engine, SimEngine, StratEngine};
use libstratis::stratis::{StratisResult, StratisError, VERSION};

/// Try to write the error from the program to stderr, vehemently.
/// Return an error if stderr unavailable or writing was a failure.
fn write_err(err: StratisError) -> StratisResult<()> {
    let mut out = term::stderr().ok_or(StratisError::StderrNotFound)?;
    out.fg(term::color::RED)?;
    writeln!(out, "{}", err.description())?;
    out.reset()?;
    Ok(())
}

/// If writing a program error to stderr fails, panic.
fn write_or_panic(err: StratisError) -> () {
    if let Err(e) = write_err(err) {
        panic!("Unable to write to stderr: {}", e)
    }
}

fn run() -> StratisResult<()> {

    let matches = App::new("stratis")
        .version(VERSION)
        .about("Stratis storage management")
        .arg(Arg::with_name("debug")
                 .long("debug")
                 .help("Print additional output for debugging"))
        .arg(Arg::with_name("sim")
                 .long("sim")
                 .help("Use simulator engine"))
        .get_matches();

    let mut builder = LogBuilder::new();
    if matches.is_present("debug") {
        builder.filter(Some("stratisd"), LogLevelFilter::Debug);
        builder.filter(Some("libstratis"), LogLevelFilter::Debug);
    } else {
        if let Ok(s) = env::var("RUST_LOG") {
            builder.parse(&s);
        }
    };

    builder
        .init()
        .expect("This is the first and only initialization of the logger; it must succeed");

    let engine: Rc<RefCell<Engine>> = {
        if matches.is_present("sim") {
            info!("Using SimEngine");
            Rc::new(RefCell::new(SimEngine::default()))
        } else {
            info!("Using StratEngine");
            Rc::new(RefCell::new(StratEngine::initialize()?))
        }
    };

    let (dbus_conn, mut tree, dbus_context) = libstratis::dbus_api::connect(Rc::clone(&engine))?;

    let mut fds = Vec::new();

    let mut eventable = engine.borrow_mut().get_eventable()?;
    let engine_fds_end_idx = match eventable {
        Some(ref mut evt) => {
            fds.push(libc::pollfd {
                         fd: evt.get_pollable_fd(),
                         revents: 0,
                         events: libc::POLLIN,
                     });
            1
        }
        None => 0,
    };

    // Don't timeout if eventable lets us be event-driven
    let poll_timeout = if engine_fds_end_idx != 0 { -1 } else { 10000 };

    loop {
        // Unconditionally call evented() if engine has no eventable.
        // This looks like a bad idea, but the only engine that has
        // no eventable is the sim engine, and for that engine, evented()
        // is essentially a no-op.
        if engine_fds_end_idx == 0 {
            engine.borrow_mut().evented()?;
        }

        // Handle engine fd, if there is one.
        // If engine has no eventable, there is no engine fd; in that case
        // the body of the loop is executed zero times.
        for pfd in fds[..engine_fds_end_idx]
                .iter_mut()
                .filter(|pfd| pfd.revents != 0) {
            pfd.revents = 0;
            eventable
                .as_mut()
                .expect("index < engine_fds_end_idx <=> index == 0 <=> eventable.is_some()")
                .clear_event()?;
            engine.borrow_mut().evented()?;
        }

        // Iterate through D-Bus file descriptors
        for pfd in fds[engine_fds_end_idx..]
                .iter()
                .filter(|pfd| pfd.revents != 0) {
            for item in dbus_conn.watch_handle(pfd.fd, WatchEvent::from_revents(pfd.revents)) {
                if let Err(r) = libstratis::dbus_api::handle(&dbus_conn,
                                                             &item,
                                                             &mut tree,
                                                             &dbus_context) {
                    write_or_panic(From::from(r));
                }
            }
        }

        // Refresh list of dbus fds to poll for every time. This can change as
        // D-Bus clients come and go.
        fds.truncate(engine_fds_end_idx);
        fds.extend(dbus_conn.watch_fds().iter().map(|w| w.to_pollfd()));

        let r = unsafe { libc::poll(fds.as_mut_ptr(), fds.len() as libc::c_ulong, poll_timeout) };
        assert!(r >= 0);
    }
}

fn main() {
    let error_code = match run() {
        Ok(_) => 0,
        Err(err) => {
            write_or_panic(err);
            1
        }
    };
    exit(error_code);
}
<|endoftext|>