<commit_before>//! QR decomposition

use ndarray::*;
use num_traits::Zero;

use super::convert::*;
use super::error::*;
use super::layout::*;

use lapack_traits::LapackScalar;

pub trait QR<Q, R> {
    fn qr(self) -> Result<(Q, R)>;
}

impl<A, S, Sq, Sr> QR<ArrayBase<Sq, Ix2>, ArrayBase<Sr, Ix2>> for ArrayBase<S, Ix2>
where
    A: LapackScalar + Copy + Zero,
    S: DataMut<Elem = A>,
    Sq: DataOwned<Elem = A> + DataMut,
    Sr: DataOwned<Elem = A> + DataMut,
{
    fn qr(mut self) -> Result<(ArrayBase<Sq, Ix2>, ArrayBase<Sr, Ix2>)> {
        (&mut self).qr()
    }
}

fn take_slice<A, S1, S2>(a: &ArrayBase<S1, Ix2>, n: usize, m: usize) -> ArrayBase<S2, Ix2>
where
    A: Copy,
    S1: Data<Elem = A>,
    S2: DataMut<Elem = A> + DataOwned,
{
    let av = a.slice(s![..n as isize, ..m as isize]);
    let mut a = unsafe { ArrayBase::uninitialized((n, m)) };
    a.assign(&av);
    a
}

fn take_slice_upper<A, S1, S2>(a: &ArrayBase<S1, Ix2>, n: usize, m: usize) -> ArrayBase<S2, Ix2>
where
    A: Copy + Zero,
    S1: Data<Elem = A>,
    S2: DataMut<Elem = A> + DataOwned,
{
    let av = a.slice(s![..n as isize, ..m as isize]);
    let mut a = unsafe { ArrayBase::uninitialized((n, m)) };
    for ((i, j), val) in a.indexed_iter_mut() {
        *val = if i <= j { av[(i, j)] } else { A::zero() };
    }
    a
}

impl<'a, A, S, Sq, Sr> QR<ArrayBase<Sq, Ix2>, ArrayBase<Sr, Ix2>> for &'a mut ArrayBase<S, Ix2>
where
    A: LapackScalar
        + Copy
        + Zero,
    S: DataMut<Elem = A>,
    Sq: DataOwned<Elem = A>
        + DataMut,
    Sr: DataOwned<Elem = A>
        + DataMut,
{
    fn qr(mut self) -> Result<(ArrayBase<Sq, Ix2>, ArrayBase<Sr, Ix2>)> {
        let n = self.rows();
        let m = self.cols();
        let k = ::std::cmp::min(n, m);
        let l = self.layout()?;
        let r = A::qr(l, self.as_allocated_mut()?)?;
        let r: Array2<_> = into_matrix(l, r)?;
        let q = self;
        Ok((take_slice(q, n, k), take_slice_upper(&r, k, m)))
    }
}

impl<'a, A, S, Sq, Sr> QR<ArrayBase<Sq, Ix2>, ArrayBase<Sr, Ix2>> for &'a ArrayBase<S, Ix2>
    where A: LapackScalar + Copy + Zero,
          S: Data<Elem = A>,
          Sq: DataOwned<Elem = A> + DataMut,
          Sr: DataOwned<Elem = A> + DataMut
{
    fn qr(self) -> Result<(ArrayBase<Sq, Ix2>, ArrayBase<Sr, Ix2>)> {
        let n = self.rows();
        let m = self.cols();
        let k = ::std::cmp::min(n, m);
        let l = self.layout()?;
        let mut q = self.to_owned();
        let r = A::qr(l, q.as_allocated_mut()?)?;
        let r: Array2<_> = into_matrix(l, r)?;
        Ok((take_slice(&q, n, k), take_slice_upper(&r, k, m)))
    }
}
<commit_msg>Use explicit form in QR<commit_after>//! QR decomposition

use ndarray::*;
use num_traits::Zero;

use super::convert::*;
use super::error::*;
use super::layout::*;
use super::triangular::*;

use lapack_traits::{LapackScalar, UPLO};

pub trait QR {
    type Q;
    type R;
    fn qr(&self) -> Result<(Self::Q, Self::R)>;
}

pub trait QRInto: Sized {
    type Q;
    type R;
    fn qr_into(self) -> Result<(Self::Q, Self::R)>;
}

pub trait QRSquare: Sized {
    type Q;
    type R;
    fn qr_square(&self) -> Result<(Self::Q, Self::R)>;
}

pub trait QRSquareInto: Sized {
    type R;
    fn qr_square_into(self) -> Result<(Self, Self::R)>;
}

impl<A, S> QRSquareInto for ArrayBase<S, Ix2>
where
    A: LapackScalar + Copy + Zero,
    S: DataMut<Elem = A>,
{
    type R = Array2<A>;

    fn qr_square_into(mut self) -> Result<(Self, Self::R)> {
        let l = self.square_layout()?;
        let r = A::qr(l, self.as_allocated_mut()?)?;
        let r: Array2<_> = into_matrix(l, r)?;
        Ok((self, r.into_triangular(UPLO::Upper)))
    }
}

impl<A, S> QRSquare for ArrayBase<S, Ix2>
where
    A: LapackScalar + Copy + Zero,
    S: Data<Elem = A>,
{
    type Q = Array2<A>;
    type R = Array2<A>;

    fn qr_square(&self) -> Result<(Self::Q, Self::R)> {
        let a = self.to_owned();
        a.qr_square_into()
    }
}

impl<A, S> QRInto for ArrayBase<S, Ix2>
where
    A: LapackScalar + Copy + Zero,
    S: DataMut<Elem = A>,
{
    type Q = Array2<A>;
    type R = Array2<A>;

    fn qr_into(mut self) -> Result<(Self::Q, Self::R)> {
        let n = self.rows();
        let m = self.cols();
        let k = ::std::cmp::min(n, m);
        let l = self.layout()?;
        let r = A::qr(l, self.as_allocated_mut()?)?;
        let r: Array2<_> = into_matrix(l, r)?;
        let q = self;
        Ok((take_slice(&q, n, k), take_slice_upper(&r, k, m)))
    }
}

impl<A, S> QR for ArrayBase<S, Ix2>
where
    A: LapackScalar + Copy + Zero,
    S: Data<Elem = A>,
{
    type Q = Array2<A>;
    type R = Array2<A>;

    fn qr(&self) -> Result<(Self::Q, Self::R)> {
        let a = self.to_owned();
        a.qr_into()
    }
}

fn take_slice<A, S1, S2>(a: &ArrayBase<S1, Ix2>, n: usize, m: usize) -> ArrayBase<S2, Ix2>
where
    A: Copy,
    S1: Data<Elem = A>,
    S2: DataMut<Elem = A> + DataOwned,
{
    let av = a.slice(s![..n as isize, ..m as isize]);
    let mut a = unsafe { ArrayBase::uninitialized((n, m)) };
    a.assign(&av);
    a
}

fn take_slice_upper<A, S1, S2>(a: &ArrayBase<S1, Ix2>, n: usize, m: usize) -> ArrayBase<S2, Ix2>
where
    A: Copy + Zero,
    S1: Data<Elem = A>,
    S2: DataMut<Elem = A> + DataOwned,
{
    let av = a.slice(s![..n as isize, ..m as isize]);
    let mut a = unsafe { ArrayBase::uninitialized((n, m)) };
    for ((i, j), val) in a.indexed_iter_mut() {
        *val = if i <= j { av[(i, j)] } else { A::zero() };
    }
    a
}
<|endoftext|>


<commit_before>extern crate docopt;
extern crate flac;
extern crate hound;
extern crate nom;
extern crate rustc_serialize;

use docopt::Docopt;
use flac::Stream;
use std::env;
use std::error::Error;

const USAGE: &'static str = "
Usage: decode <input> <output>
       decode <input>... <dir>
       decode --help

Options:
  -h, --help   Show this message.
";

#[derive(RustcDecodable)]
struct Arguments {
  arg_input: Vec<String>,
  arg_output: Option<String>,
  arg_dir: Option<String>,
}

fn decode_file(input_file: &str, output_file: &str)
               -> Result<(), hound::Error> {
  let mut stream = try! {
    Stream::from_file(input_file).map_err(hound::Error::IoError)
  };
  let info       = stream.info();
  let frames_len = stream.frames.len();

  let spec = hound::WavSpec {
    channels: info.channels as u16,
    sample_rate: info.sample_rate,
    bits_per_sample: info.bits_per_sample as u16,
  };

  let mut output = try!(hound::WavWriter::create(output_file, spec));

  for _ in 0..frames_len {
    if let Some(buffer) = stream.next_frame() {
      let buffer_size = buffer.len();
      let block_size  = buffer_size / 2;
      let left        = &buffer[0..block_size];
      let right       = &buffer[block_size..buffer_size];

      for i in 0..block_size {
        try!(output.write_sample(left[i]));
        try!(output.write_sample(right[i]));
      }
    } else {
      break;
    }
  }

  output.finalize()
}

fn main() {
  let args: Arguments = Docopt::new(USAGE)
    .and_then(|d| d.argv(env::args()).decode())
    .unwrap_or_else(|e| e.exit());

  if let Some(ref output_file) = args.arg_output {
    let input_file = &args.arg_input[0];

    if let Err(e) = decode_file(input_file, output_file) {
      println!("{:?}", e);
    } else {
      println!("decoded: {} -> {}", input_file, output_file);
    }
  }
}
<commit_msg>Add `to_output_file` function<commit_after>extern crate docopt;
extern crate flac;
extern crate hound;
extern crate nom;
extern crate rustc_serialize;

use docopt::Docopt;
use flac::Stream;

use std::env;
use std::error::Error;
use std::io;
use std::path::{Path, PathBuf};

const USAGE: &'static str = "
Usage: decode <input> <output>
       decode <input>... <dir>
       decode --help

Options:
  -h, --help   Show this message.
";

#[derive(RustcDecodable)]
struct Arguments {
  arg_input: Vec<String>,
  arg_output: Option<String>,
  arg_dir: Option<String>,
}

fn decode_file(input_file: &str, output_file: &str)
               -> Result<(), hound::Error> {
  let mut stream = try! {
    Stream::from_file(input_file).map_err(hound::Error::IoError)
  };
  let info       = stream.info();
  let frames_len = stream.frames.len();

  let spec = hound::WavSpec {
    channels: info.channels as u16,
    sample_rate: info.sample_rate,
    bits_per_sample: info.bits_per_sample as u16,
  };

  let mut output = try!(hound::WavWriter::create(output_file, spec));

  for _ in 0..frames_len {
    if let Some(buffer) = stream.next_frame() {
      let buffer_size = buffer.len();
      let block_size  = buffer_size / 2;
      let left        = &buffer[0..block_size];
      let right       = &buffer[block_size..buffer_size];

      for i in 0..block_size {
        try!(output.write_sample(left[i]));
        try!(output.write_sample(right[i]));
      }
    } else {
      break;
    }
  }

  output.finalize()
}

fn to_output_file(buffer: &mut PathBuf, path: &Path, directory: &str)
                  -> Result<(), hound::Error> {
  buffer.push(directory);

  path.file_name().map(|name| {
    buffer.push(name);
    buffer.set_extension("wav");
  }).ok_or_else(|| {
    let kind    = io::ErrorKind::NotFound;
    let message = "no file name found";
    let error   = io::Error::new(kind, message);

    hound::Error::IoError(error)
  })
}

fn main() {
  let args: Arguments = Docopt::new(USAGE)
    .and_then(|d| d.argv(env::args()).decode())
    .unwrap_or_else(|e| e.exit());

  if let Some(ref output_file) = args.arg_output {
    let input_file = &args.arg_input[0];

    if let Err(e) = decode_file(input_file, output_file) {
      println!("{:?}", e);
    } else {
      println!("decoded: {} -> {}", input_file, output_file);
    }
  }
}
<|endoftext|>


<commit_before>extern crate iron;
extern crate "handlebars-iron" as hbs;
extern crate "rustc-serialize" as serialize;

use iron::prelude::*;
use iron::{status};
use hbs::{Template, HandlebarsEngine};
use serialize::json::{ToJson, Json};
use std::collections::BTreeMap;

/// this function is for making example data
fn make_data() -> BTreeMap<String, Json> {
    let mut data = BTreeMap::new();

    data.insert("year".to_string(), "2015".to_json());

    let mut teams = Vec::new();

    for v in vec![("Jiangsu", 43u16), ("Beijing", 27u16), ("Guangzhou", 22u16), ("Shandong", 12u16)].iter() {
        let (name, score) = *v;
        let mut t = BTreeMap::new();
        t.insert("name".to_string(), name.to_json());
        t.insert("score".to_string(), score.to_json());
        teams.push(t)
    }

    data.insert("teams".to_string(), teams.to_json());
    data
}

/// the handler
fn hello_world(_: &mut Request) -> IronResult<Response> {
    let mut resp = Response::new();

    let data = make_data();
    resp.set_mut(Template::new("index", data)).set_mut(status::Ok);
    Ok(resp)
}

fn main() {
    let mut chain = Chain::new(hello_world);
    chain.link_after(HandlebarsEngine::new("./examples/templates/", ".hbs"));
    Iron::new(chain).listen("localhost:3000").unwrap();
}
<commit_msg>(fix) ironup: listen->http<commit_after>extern crate iron;
extern crate "handlebars-iron" as hbs;
extern crate "rustc-serialize" as serialize;

use iron::prelude::*;
use iron::{status};
use hbs::{Template, HandlebarsEngine};
use serialize::json::{ToJson, Json};
use std::collections::BTreeMap;

/// this function is for making example data
fn make_data() -> BTreeMap<String, Json> {
    let mut data = BTreeMap::new();

    data.insert("year".to_string(), "2015".to_json());

    let mut teams = Vec::new();

    for v in vec![("Jiangsu", 43u16), ("Beijing", 27u16), ("Guangzhou", 22u16), ("Shandong", 12u16)].iter() {
        let (name, score) = *v;
        let mut t = BTreeMap::new();
        t.insert("name".to_string(), name.to_json());
        t.insert("score".to_string(), score.to_json());
        teams.push(t)
    }

    data.insert("teams".to_string(), teams.to_json());
    data
}

/// the handler
fn hello_world(_: &mut Request) -> IronResult<Response> {
    let mut resp = Response::new();

    let data = make_data();
    resp.set_mut(Template::new("index", data)).set_mut(status::Ok);
    Ok(resp)
}

fn main() {
    let mut chain = Chain::new(hello_world);
    chain.link_after(HandlebarsEngine::new("./examples/templates/", ".hbs"));
    Iron::new(chain).http("localhost:3000").unwrap();
}
<|endoftext|>


<commit_before>extern crate tokio_proto;
extern crate serde_json;
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate macro_attr;

use varlink::server::{Proto, VarlinkService};
use tokio_proto::TcpServer;
use std::sync::{Arc, RwLock};

extern crate varlink;

#[macro_use]
mod io_systemd_network {
    /*
    varlink_server! (r#"
    # Provides information about network state
	interface io.systemd.network

	type NetdevInfo (
	  ifindex: int,
	  ifname: string
	)

	type Netdev (
	  ifindex: int,
	  ifname: string
	)

	# Returns information about a network device
	method Info(ifindex: int) -> (info: NetdevInfo)

	# Lists all network devices
	method List() -> (netdevs: Netdev[])

	error UnknownNetworkDevice
	error InvalidParameter
    "#);
    */

    // will be autogenerated by the above macro in the future
    include!("io_systemd_network/mod.rs");

}

use io_systemd_network::*;

macro_attr! { #[derive(IoSystemdNetwork!)]
struct MyServer {
    pub state: Arc<RwLock<i64>>,
}}

impl Interface for MyServer {
    fn info(&self, i: i64) -> Result<InfoReply, Error> {
        // State example
        {
            let mut number = self.state.write().unwrap();

            *number += 1;

            println!("{}", *number);
        }

        match i {
            1 => {
                Ok(InfoReply {
                       info: NetdevInfo {
                           ifindex: 1,
                           ifname: "lo".into(),
                       },
                   })
            }
            2 => {
                Ok(InfoReply {
                       info: NetdevInfo {
                           ifindex: 2,
                           ifname: "eth0".into(),
                       },
                   })
            }
            _ => Err(Error::UnknownNetworkDevice),
        }
    }

    fn list(&self) -> Result<ListReply, Error> {
        // State example
        {
            let mut number = self.state.write().unwrap();

            *number -= 1;

            println!("{}", *number);
        }
        Ok(ListReply {
               netdevs: vec![Netdev {
                                 ifindex: 1,
                                 ifname: "lo".into(),
                             },
                             Netdev {
                                 ifindex: 2,
                                 ifname: "eth0".into(),
                             }],
           })
    }
}

fn main() {
    // Specify the localhost address
    let addr = "0.0.0.0:12345".parse().unwrap();

    // The builder requires a protocol and an address
    let server = TcpServer::new(Proto, addr);

    let state = Arc::new(RwLock::new(0));

    // We provide a way to *instantiate* the service for each new
    // connection; here, we just immediately return a new instance.
    server.serve(move || {
        println!("New Server");
        let iosystemdnetwork = MyServer { state: state.clone() };
        Ok(VarlinkService::new("org.varlink".into(),
                               "test service".into(),
                               "0.1".into(),
                               "http://varlink.org".into(),
                               vec![Box::new(iosystemdnetwork)]))
    });
}
<commit_msg>server: Tell where the server is listening<commit_after>extern crate tokio_proto;
extern crate serde_json;
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate macro_attr;

use varlink::server::{Proto, VarlinkService};
use tokio_proto::TcpServer;
use std::sync::{Arc, RwLock};

extern crate varlink;

#[macro_use]
mod io_systemd_network {
    /*
    varlink_server! (r#"
    # Provides information about network state
	interface io.systemd.network

	type NetdevInfo (
	  ifindex: int,
	  ifname: string
	)

	type Netdev (
	  ifindex: int,
	  ifname: string
	)

	# Returns information about a network device
	method Info(ifindex: int) -> (info: NetdevInfo)

	# Lists all network devices
	method List() -> (netdevs: Netdev[])

	error UnknownNetworkDevice
	error InvalidParameter
    "#);
    */

    // will be autogenerated by the above macro in the future
    include!("io_systemd_network/mod.rs");

}

use io_systemd_network::*;

macro_attr! { #[derive(IoSystemdNetwork!)]
struct MyServer {
    pub state: Arc<RwLock<i64>>,
}}

impl Interface for MyServer {
    fn info(&self, i: i64) -> Result<InfoReply, Error> {
        // State example
        {
            let mut number = self.state.write().unwrap();

            *number += 1;

            println!("{}", *number);
        }

        match i {
            1 => {
                Ok(InfoReply {
                       info: NetdevInfo {
                           ifindex: 1,
                           ifname: "lo".into(),
                       },
                   })
            }
            2 => {
                Ok(InfoReply {
                       info: NetdevInfo {
                           ifindex: 2,
                           ifname: "eth0".into(),
                       },
                   })
            }
            _ => Err(Error::UnknownNetworkDevice),
        }
    }

    fn list(&self) -> Result<ListReply, Error> {
        // State example
        {
            let mut number = self.state.write().unwrap();

            *number -= 1;

            println!("{}", *number);
        }
        Ok(ListReply {
               netdevs: vec![Netdev {
                                 ifindex: 1,
                                 ifname: "lo".into(),
                             },
                             Netdev {
                                 ifindex: 2,
                                 ifname: "eth0".into(),
                             }],
           })
    }
}

fn main() {
    // Specify the localhost address
    let addr = "0.0.0.0:12345".parse().unwrap();

    // The builder requires a protocol and an address
    let server = TcpServer::new(Proto, addr);

    let state = Arc::new(RwLock::new(0));

    println!("Listening on {}", addr);

    // We provide a way to *instantiate* the service for each new
    // connection; here, we just immediately return a new instance.
    server.serve(move || {
        println!("New Server");
        let iosystemdnetwork = MyServer { state: state.clone() };
        Ok(VarlinkService::new("org.varlink".into(),
                               "test service".into(),
                               "0.1".into(),
                               "http://varlink.org".into(),
                               vec![Box::new(iosystemdnetwork)]))
    });
}
<|endoftext|>


<commit_before>use atom::*;
use env::Env;
use errors::*;
use symbol::*;
use funcs::*;
use eval::*;
use self::Instruction::*;

#[derive (Debug, Clone)]
pub struct Frame {
    pub program: CompiledFunction,
    pub pc: usize
}

impl Frame {
    fn new(f: CompiledFunction) -> Frame {
        Frame {
            program: f,
            pc: 0
        }
    }

    fn current_instruction(&self) -> Option<&Instruction> {
        self.program.body.get(self.pc)
    }
}

#[derive (Debug)]
pub struct VirtualMachine {
    pub stack: Vec<Atom>,
    pub frames: Vec<Frame>,
    fp: usize,
    sp: usize,
}

#[derive (Debug, Clone, PartialEq)]
#[allow(non_camel_case_types)]
pub enum Instruction {
    CONST(Atom), // pushes the atom on the stack
    LOAD(InternedStr), // loads then pushes the value
    DEFINE(InternedStr), // sets the symbol to value on the top of the stack
    POP, // pops one off the stack
    // FUNCTION,
    JUMP_IFNOT(usize),
    JUMP(usize),
    RETURN, // pops the frame
    CALL(Function, usize), // calls a noncompiled or native function
    DCALL(usize) // calls the function at the top of the stack
}

fn compile_node(node: Atom, out: &mut Vec<Instruction>, env: &mut Env) -> Result<(), Error> {
    match node {
        Atom::Symbol(sym) => {
            out.push(Instruction::LOAD(sym))
        },
        Atom::List(ref list) => {
            for n in list.iter() {
                try!(compile(n.clone(), out, env));
            }
        },
        // handle constants
        _ => {
            out.push(Instruction::CONST(node.clone()));
        }
    }
    Ok(())
}

fn expand_quasiquote(node: &Atom, env: &Env) -> AtomResult {
    // println!("expand_quasiquote: {}", node);
    if !node.is_pair() {
        return Ok(Atom::list(vec![Atom::Form(Form::Quote), node.clone()]))
    }

    let list = try!(node.as_list());
    match list[0] {
        Atom::Form(Form::Unquote) => {
            try!(expect_arg_length(list, 2));
            return Ok(list[1].clone())
        },
        _ => ()
    }

    if list[0].is_pair() {
        match list[0] {
            Atom::List(ref sublist) => {
                match sublist[0] {
                    Atom::Form(Form::Splice) => {
                        let append = Atom::Function(Function::Native(Native::Append));
                        let rest = list[1..].iter().map(|n| n.clone()).collect();
                        return Ok(Atom::list(vec![append, sublist[1].clone(),
                                                  Atom::list(rest)]))
                    },
                    _ => ()
                }
            },
            _ => ()
        }
    }

    let rest = list[1..].iter().map(|n| n.clone()).collect();
    Ok(Atom::list(vec![Atom::Function(Function::Native(Native::Cons)),
                       try!(expand_quasiquote(&list[0], env)),
                       try!(expand_quasiquote(&Atom::list(rest), env))]))
}

fn macro_expand(node: Atom, env: &mut Env) -> Result<Atom, Error> {
    match node {
        Atom::List(ref list) if !list.is_empty() => {
            match list[0] {
                Atom::Symbol(ref sym) => {
                    if let Some(Atom::Function(Function::Macro(ref m))) = env.get(sym.as_ref()) {
                        return macro_expand(try!(eval_macro(m, &list[1..], env)), env)
                    }
                },
                Atom::Form(Form::Fn) => {
                    if list.len() < 3 {
                        return Err(Error::NotEnoughArguments)
                    }

                    let mut out = Vec::with_capacity(list.len());
                    out.push(list[0].clone());
                    out.push(list[1].clone());
                    for i in &list[2..] {
                        out.push(try!(macro_expand(i.clone(), env)));
                    }
                    return Ok(Atom::list(out))
                }
                _ => ()
            }
            return Ok(Atom::list(try!(list.iter().map(|n| macro_expand(n.clone(), env)).collect())))
        },
        _ => ()
    }

    Ok(node)
}

pub fn compile(node: Atom, out: &mut Vec<Instruction>, env: &mut Env) -> Result<(), Error> {
    // println!("compiling: {}", node);
    match node {
        Atom::List(ref list) => {
            if list.is_empty() {
                out.push(Instruction::CONST(node.clone()));
                return Ok(())
            }
            ()
        },
        _ => return compile_node(node, out, env)
    }

    let n = try!(macro_expand(node, env));

    let list = match n {
        Atom::List(ref list) => {
            if list.is_empty() {
                out.push(CONST(Atom::Nil));
                return Ok(())
            } else {
                list
            }
        },
        c @ _ => {
            out.push(CONST(c));
            return Ok(())
        }
    };

    if let Atom::List(_) = list[0] {
        for i in list.iter().skip(1) {
            try!(compile(i.clone(), out, env));
        }
        try!(compile(list[0].clone(), out, env));
        out.push(DCALL(list.len() - 1));
        return Ok(())
    }

    match list[0] {
        Atom::Form(f) => {
            match f {
                Form::If => {
                    match list.len() {
                        4 => {
                            try!(compile(list[1].clone(), out, env));
                            // predicate on stack
                            out.push(JUMP_IFNOT(0));
                            let else_jump_pos = out.len() -1;
                            try!(compile(list[2].clone(), out, env));

                            out.push(JUMP(0));
                            let out_jump = out.len() - 1;

                            out[else_jump_pos] = JUMP_IFNOT(out.len());

                            try!(compile(list[3].clone(), out , env));

                            out[out_jump] = JUMP(out.len());
                        },
                        3 => {
                            try!(compile(list[1].clone(), out, env));
                            // predicate on stack
                            out.push(JUMP_IFNOT(0));
                            let else_jump_pos = out.len() -1;
                            try!(compile(list[2].clone(), out, env));
                            out[else_jump_pos] = JUMP_IFNOT(out.len());
                        }
                        _ => {
                            return Err(invalid_arg("invalid number for forms for if"))
                        }
                    }
                    return Ok(())
                },
                Form::Let => {
                    let mut new_env = Env::new(Some(env.clone()));
                    let bind_list = try!(list[1].as_list());
                    let mut bindings = Vec::new();

                    for binding in bind_list.iter() {
                        let bind_exp = try!(binding.as_list());
                        try!(expect_arg_length(bind_exp, 2));
                        bindings.push(bind_exp[0].clone());
                        try!(compile(bind_exp[1].clone(), out, env));
                    }
                    let mut body = Vec::new();
                    try!(compile(list[2].clone(), &mut body, &mut new_env));
                    body.push(RETURN);

                    let arity = bindings.len();

                    let func = CompiledFunction {
                        body: body,
                        params: to_list(bindings),
                        env: new_env
                    };

                    out.push(CONST(Atom::Function(Function::Compiled(func))));
                    out.push(DCALL(arity));
                    return Ok(())
                },
                Form::Set => {
                },
                Form::Def => {
                    let sym = try!(list[1].as_symbol());
                    try!(compile(list[2].clone(), out, env));
                    out.push(DEFINE(*sym));
                    return Ok(())
                },
                Form::Do => {
                    if list.len() > 2 {
                        for i in 1..list.len()-1 {
                            try!(compile(list[i].clone(), out, env));
                            out.push(POP);
                        }
                    }
                    return compile(list[list.len()-1].clone(), out, env)
                },
                Form::Fn => {
                    let mut body = Vec::new();
                    let mut env = Env::new(Some(env.clone()));
                    try!(compile(list[2].clone(), &mut body, &mut env));
                    body.push(RETURN);
                    let func = CompiledFunction {
                        body: body,
                        params: try!(list[1].as_list()).clone(),
                        env: env
                    };
                    out.push(CONST(Atom::Function(Function::Compiled(func))));
                    return Ok(())
                },
                Form::Macro => {
                    try!(macro_form(&list[1..], env));
                    out.push(CONST(Atom::Nil));
                    return Ok(())
                },
                Form::Quote => {
                    out.push(CONST(list[1].clone()));
                    return Ok(())
                },
                Form::QuasiQuote => {
                    let expanded = try!(expand_quasiquote(&list[1], env));
                    // println!("expanded: {}", expanded);
                    try!(compile(expanded, out, env));
                    return Ok(())
                }
                _ => {
                    return Err(Error::NotImplemented)
                }
            }
        },
        Atom::Symbol(_) => {
            for n in list.iter().skip(1) {
                try!(compile(n.clone(), out, env));
            }
            try!(compile_node(list[0].clone(), out, env));
            out.push( DCALL(list.len() -1 ));
            return Ok(())
        }
        Atom::Function(ref func) => {
            for n in list.iter().skip(1) {
                try!(compile(n.clone(), out, env));
            }
            match *func {
                Function::Native(_) => {
                    out.push(Instruction::CALL(func.clone(), list.len() -1));
                    return Ok(())
                },
                Function::Compiled(_) => {
                    out.push(CALL(func.clone(), list.len() -1));
                    return Ok(())
                },
                _ => {
                    return Err(Error::NotImplemented)
                }
            }
        },
        _ => {
            return Err(Error::NotAFunction)
        }
    }

    Err(invalid_arg("bottom here"))
}

fn eval_native_borrow(v: &mut VirtualMachine, n: Native, len: usize) -> AtomResult {
    let &mut VirtualMachine { ref mut stack, ref mut frames, fp, .. } = v;
    eval_native(n, &stack[len..], &mut frames[fp].program.env)
}

impl VirtualMachine {
    pub fn new() -> VirtualMachine {
        VirtualMachine {
            stack: vec![],
            frames: vec![],
            sp: 0,
            fp: 0
        }
    }

    pub fn reset(&mut self) {
        self.stack.clear();
        self.frames.clear();
        self.sp = 0;
        self.fp = 0;
    }

    fn next_instruction(&self) -> Option<Instruction> {
        self.frames[self.fp].current_instruction().map(|n| n.clone())
    }

    fn current_frame(&mut self) -> &mut Frame {
        &mut self.frames[self.fp]
    }

    fn current_env(&mut self) -> &mut Env {
        &mut self.current_frame().program.env
    }

    pub fn run(&mut self) -> AtomResult {
        println!("running...");
        while let Some(instruction) = self.next_instruction() {
            match instruction {
                JUMP_IFNOT(addr) => {
                    if !try!(self.pop()).as_bool() {
                        self.current_frame().pc = addr;
                        continue;
                    }
                },
                JUMP(addr) => {
                    self.current_frame().pc = addr;
                    continue;
                }
                POP => {
                    try!(self.pop());
                }
                RETURN => {
                    if self.fp > 0 {
                        self.fp -= 1;
                        self.frames.pop();
                    } else {
                        return self.pop()
                    }
                },
                LOAD(sym) => {
                    let value = self.current_frame().program.env.get(sym.as_ref()).unwrap_or(Atom::Nil);
                    self.push(value);
                },
                CONST(ref atom) => {
                    self.push(atom.clone())
                },
                DEFINE(sym) => {
                    let v = try!(self.pop());
                    let a = try!(self.current_env().define(sym, v));
                    self.push(a);
                },
                DCALL(arity) => {
                    let func = try!(self.pop());
                    match func {
                        Atom::Function(Function::Compiled(mut f)) => {
                            f.env.bind_mut(&f.params, &self.stack[self.sp-arity..]);
                            for _ in 0..arity {
                                try!(self.pop());
                            }
                            self.frames.push(Frame::new(f.clone()));
                            self.fp += 1;
                            continue; // don't advance PC of the new frame
                        },
                        _ => {
                            // println!("not a function: {:?}", func);
                            return Err(Error::NotAFunction)
                        }
                    }
                },
                CALL(func, arity) => {
                    match func {
                        Function::Native(n) => {
                            let len = self.stack.len();
                            let r = try!(eval_native_borrow(self, n, len - arity));
                            for _ in 0..arity {
                                try!(self.pop());
                            }
                            self.push(r);
                        },
                        _ => {
                            return Err(Error::NotImplemented)
                        }
                    }
                },
            }
            self.current_frame().pc += 1;
        }
        self.pop()
    }

    fn push(&mut self, a: Atom)  {
        self.stack.push(a);
        self.sp += 1
    }

    fn pop(&mut self) -> AtomResult {
        if self.sp > 0 {
            self.sp -= 1;
            return self.stack.pop().ok_or(Error::RuntimeAssertion)
        } else {
            return Err(Error::RuntimeAssertion)
        }
    }
}

pub fn empty_frame() -> Frame {
    Frame::new(CompiledFunction{
        body: Vec::new(),
        params: empty_list(),
        env: Env::new(None)
    })
}
#[cfg(test)]
mod tests {
    use super::*;
    use parser::*;
    use atom::*;
    use env::*;

    fn run_expr(s: &str) -> Atom {
        let p = tokenize(s).unwrap();
        let mut out = Vec::new();
        let env = Env::new(None);
        compile(p, &mut out, &env).unwrap();
        let mut vm = VirtualMachine::new();
        let mut f = empty_frame();
        f.program.env = env;
        f.program.body = out;
        vm.frames.push(f);
        vm.run().unwrap()
    }

    #[test]
    fn test() {
        assert_eq!(Atom::from(6), run_expr("(+ (+ 2 2) 2)"));
        assert_eq!(Atom::symbol("a"), run_expr("(def a 1)"));
        assert_eq!(Atom::from(3), run_expr("(do (def foo (fn (x) (+ x 2))) (foo 1))"));
    }

    #[test]
    fn test_invoke_fn_inline() {
        assert_eq!(Atom::from(1), run_expr("((fn (x) 1) 0)"));
    }

    #[test]
    fn test_closure() {
        assert_eq!(Atom::from(2), run_expr("(do (def foo (fn (x) (fn () (* 2 x)))) (def foo2 (foo 1)) (foo2))"))
    }

    #[test]
    fn let_binding() {
        assert_eq!(Atom::from(-1), run_expr("(let* ((x 2) (y 3)) (- x y))"));
    }

    #[test]
    fn nested_let() {
        assert_eq!(Atom::from(0), run_expr("(do (let* ((x 1)) (let* ((x 0)) x)))"))
    }

    #[test]
    fn if_test() {
        assert_eq!(Atom::from(0), run_expr("(if false 1 0)"));
        assert_eq!(Atom::from(1), run_expr("(if true 1 0)"));
    }

    #[test]
    fn quote_test() {
        assert_eq!(Atom::list(vec![Atom::from(0), Atom::from(1)]), run_expr("'(0 1)"));
    }
}
<commit_msg>store instruction<commit_after>use atom::*;
use env::Env;
use errors::*;
use symbol::*;
use funcs::*;
use eval::*;
use self::Instruction::*;

#[derive (Debug, Clone)]
pub struct Frame {
    pub program: CompiledFunction,
    pub pc: usize
}

impl Frame {
    fn new(f: CompiledFunction) -> Frame {
        Frame {
            program: f,
            pc: 0
        }
    }

    fn current_instruction(&self) -> Option<&Instruction> {
        self.program.body.get(self.pc)
    }
}

#[derive (Debug)]
pub struct VirtualMachine {
    pub stack: Vec<Atom>,
    pub frames: Vec<Frame>,
    fp: usize,
    sp: usize,
}

#[derive (Debug, Clone, PartialEq)]
#[allow(non_camel_case_types)]
pub enum Instruction {
    CONST(Atom), // pushes the atom on the stack
    LOAD(InternedStr), // loads then pushes the value
    DEFINE(InternedStr), // sets the symbol to value on the top of the stack
    POP, // pops one off the stack
    STORE(InternedStr),
    // FUNCTION,
    JUMP_IFNOT(usize),
    JUMP(usize),
    RETURN, // pops the frame
    CALL(Function, usize), // calls a noncompiled or native function
    DCALL(usize) // calls the function at the top of the stack
}

fn compile_node(node: Atom, out: &mut Vec<Instruction>, env: &mut Env) -> Result<(), Error> {
    match node {
        Atom::Symbol(sym) => {
            out.push(Instruction::LOAD(sym))
        },
        Atom::List(ref list) => {
            for n in list.iter() {
                try!(compile(n.clone(), out, env));
            }
        },
        // handle constants
        _ => {
            out.push(Instruction::CONST(node.clone()));
        }
    }
    Ok(())
}

fn expand_quasiquote(node: &Atom, env: &Env) -> AtomResult {
    // println!("expand_quasiquote: {}", node);
    if !node.is_pair() {
        return Ok(Atom::list(vec![Atom::Form(Form::Quote), node.clone()]))
    }

    let list = try!(node.as_list());
    match list[0] {
        Atom::Form(Form::Unquote) => {
            try!(expect_arg_length(list, 2));
            return Ok(list[1].clone())
        },
        _ => ()
    }

    if list[0].is_pair() {
        match list[0] {
            Atom::List(ref sublist) => {
                match sublist[0] {
                    Atom::Form(Form::Splice) => {
                        let append = Atom::Function(Function::Native(Native::Append));
                        let rest = list[1..].iter().map(|n| n.clone()).collect();
                        return Ok(Atom::list(vec![append, sublist[1].clone(),
                                                  Atom::list(rest)]))
                    },
                    _ => ()
                }
            },
            _ => ()
        }
    }

    let rest = list[1..].iter().map(|n| n.clone()).collect();
    Ok(Atom::list(vec![Atom::Function(Function::Native(Native::Cons)),
                       try!(expand_quasiquote(&list[0], env)),
                       try!(expand_quasiquote(&Atom::list(rest), env))]))
}

fn macro_expand(node: Atom, env: &mut Env) -> Result<Atom, Error> {
    match node {
        Atom::List(ref list) if !list.is_empty() => {
            match list[0] {
                Atom::Symbol(ref sym) => {
                    if let Some(Atom::Function(Function::Macro(ref m))) = env.get(sym.as_ref()) {
                        return macro_expand(try!(eval_macro(m, &list[1..], env)), env)
                    }
                },
                Atom::Form(Form::Fn) => {
                    if list.len() < 3 {
                        return Err(Error::NotEnoughArguments)
                    }

                    let mut out = Vec::with_capacity(list.len());
                    out.push(list[0].clone());
                    out.push(list[1].clone());
                    for i in &list[2..] {
                        out.push(try!(macro_expand(i.clone(), env)));
                    }
                    return Ok(Atom::list(out))
                }
                _ => ()
            }
            return Ok(Atom::list(try!(list.iter().map(|n| macro_expand(n.clone(), env)).collect())))
        },
        _ => ()
    }

    Ok(node)
}

pub fn compile(node: Atom, out: &mut Vec<Instruction>, env: &mut Env) -> Result<(), Error> {
    // println!("compiling: {}", node);
    match node {
        Atom::List(ref list) => {
            if list.is_empty() {
                out.push(Instruction::CONST(node.clone()));
                return Ok(())
            }
            ()
        },
        _ => return compile_node(node, out, env)
    }

    let n = try!(macro_expand(node, env));

    let list = match n {
        Atom::List(ref list) => {
            if list.is_empty() {
                out.push(CONST(Atom::Nil));
                return Ok(())
            } else {
                list
            }
        },
        c @ _ => {
            out.push(CONST(c));
            return Ok(())
        }
    };

    if let Atom::List(_) = list[0] {
        for i in list.iter().skip(1) {
            try!(compile(i.clone(), out, env));
        }
        try!(compile(list[0].clone(), out, env));
        out.push(DCALL(list.len() - 1));
        return Ok(())
    }

    match list[0] {
        Atom::Form(f) => {
            match f {
                Form::If => {
                    match list.len() {
                        4 => {
                            try!(compile(list[1].clone(), out, env));
                            // predicate on stack
                            out.push(JUMP_IFNOT(0));
                            let else_jump_pos = out.len() -1;
                            try!(compile(list[2].clone(), out, env));

                            out.push(JUMP(0));
                            let out_jump = out.len() - 1;

                            out[else_jump_pos] = JUMP_IFNOT(out.len());

                            try!(compile(list[3].clone(), out , env));

                            out[out_jump] = JUMP(out.len());
                        },
                        3 => {
                            try!(compile(list[1].clone(), out, env));
                            // predicate on stack
                            out.push(JUMP_IFNOT(0));
                            let else_jump_pos = out.len() -1;
                            try!(compile(list[2].clone(), out, env));
                            out[else_jump_pos] = JUMP_IFNOT(out.len());
                        }
                        _ => {
                            return Err(invalid_arg("invalid number for forms for if"))
                        }
                    }
                    return Ok(())
                },
                Form::Let => {
                    let mut new_env = Env::new(Some(env.clone()));
                    let bind_list = try!(list[1].as_list());
                    let mut bindings = Vec::new();

                    for binding in bind_list.iter() {
                        let bind_exp = try!(binding.as_list());
                        try!(expect_arg_length(bind_exp, 2));
                        bindings.push(bind_exp[0].clone());
                        try!(compile(bind_exp[1].clone(), out, env));
                    }
                    let mut body = Vec::new();
                    try!(compile(list[2].clone(), &mut body, &mut new_env));
                    body.push(RETURN);

                    let arity = bindings.len();

                    let func = CompiledFunction {
                        body: body,
                        params: to_list(bindings),
                        env: new_env
                    };

                    out.push(CONST(Atom::Function(Function::Compiled(func))));
                    out.push(DCALL(arity));
                    return Ok(())
                },
                Form::Set => {
                    let sym = try!(list[1].as_symbol());
                    try!(compile(list[2].clone(), out, env));
                    out.push(STORE(*sym));
                    return Ok(())
                },
                Form::Def => {
                    let sym = try!(list[1].as_symbol());
                    try!(compile(list[2].clone(), out, env));
                    out.push(DEFINE(*sym));
                    return Ok(())
                },
                Form::Do => {
                    if list.len() > 2 {
                        for i in 1..list.len()-1 {
                            try!(compile(list[i].clone(), out, env));
                            out.push(POP);
                        }
                    }
                    return compile(list[list.len()-1].clone(), out, env)
                },
                Form::Fn => {
                    let mut body = Vec::new();
                    let mut env = Env::new(Some(env.clone()));
                    try!(compile(list[2].clone(), &mut body, &mut env));
                    body.push(RETURN);
                    let func = CompiledFunction {
                        body: body,
                        params: try!(list[1].as_list()).clone(),
                        env: env
                    };
                    out.push(CONST(Atom::Function(Function::Compiled(func))));
                    return Ok(())
                },
                Form::Macro => {
                    try!(macro_form(&list[1..], env));
                    out.push(CONST(Atom::Nil));
                    return Ok(())
                },
                Form::Quote => {
                    out.push(CONST(list[1].clone()));
                    return Ok(())
                },
                Form::QuasiQuote => {
                    let expanded = try!(expand_quasiquote(&list[1], env));
                    // println!("expanded: {}", expanded);
                    try!(compile(expanded, out, env));
                    return Ok(())
                }
                _ => {
                    return Err(Error::NotImplemented)
                }
            }
        },
        Atom::Symbol(_) => {
            for n in list.iter().skip(1) {
                try!(compile(n.clone(), out, env));
            }
            try!(compile_node(list[0].clone(), out, env));
            out.push( DCALL(list.len() -1 ));
            return Ok(())
        }
        Atom::Function(ref func) => {
            for n in list.iter().skip(1) {
                try!(compile(n.clone(), out, env));
            }
            match *func {
                Function::Native(_) => {
                    out.push(Instruction::CALL(func.clone(), list.len() -1));
                    return Ok(())
                },
                Function::Compiled(_) => {
                    out.push(CALL(func.clone(), list.len() -1));
                    return Ok(())
                },
                _ => {
                    return Err(Error::NotImplemented)
                }
            }
        },
        _ => {
            return Err(Error::NotAFunction)
        }
    }

    Err(invalid_arg("bottom here"))
}

fn eval_native_borrow(v: &mut VirtualMachine, n: Native, len: usize) -> AtomResult {
    let &mut VirtualMachine { ref mut stack, ref mut frames, fp, .. } = v;
    eval_native(n, &stack[len..], &mut frames[fp].program.env)
}

impl VirtualMachine {
    pub fn new() -> VirtualMachine {
        VirtualMachine {
            stack: vec![],
            frames: vec![],
            sp: 0,
            fp: 0
        }
    }

    pub fn reset(&mut self) {
        self.stack.clear();
        self.frames.clear();
        self.sp = 0;
        self.fp = 0;
    }

    fn next_instruction(&self) -> Option<Instruction> {
        self.frames[self.fp].current_instruction().map(|n| n.clone())
    }

    fn current_frame(&mut self) -> &mut Frame {
        &mut self.frames[self.fp]
    }

    fn current_env(&mut self) -> &mut Env {
        &mut self.current_frame().program.env
    }

    pub fn run(&mut self) -> AtomResult {
        println!("running...");
        while let Some(instruction) = self.next_instruction() {
            match instruction {
                JUMP_IFNOT(addr) => {
                    if !try!(self.pop()).as_bool() {
                        self.current_frame().pc = addr;
                        continue;
                    }
                },
                JUMP(addr) => {
                    self.current_frame().pc = addr;
                    continue;
                }
                POP => {
                    try!(self.pop());
                }
                RETURN => {
                    if self.fp > 0 {
                        self.fp -= 1;
                        self.frames.pop();
                    } else {
                        return self.pop()
                    }
                },
                LOAD(sym) => {
                    let value = self.current_frame().program.env.get(sym.as_ref()).unwrap_or(Atom::Nil);
                    self.push(value);
                },
                CONST(ref atom) => {
                    self.push(atom.clone())
                },
                DEFINE(sym) => {
                    let v = try!(self.pop());
                    let a = try!(self.current_env().define(sym, v));
                    self.push(a);
                },
                STORE(sym) => {
                    let v = try!(self.pop());
                    let a = try!(self.current_env().set(sym, v));
                    self.push(a);
                }
                DCALL(arity) => {
                    let func = try!(self.pop());
                    match func {
                        Atom::Function(Function::Compiled(mut f)) => {
                            f.env.bind_mut(&f.params, &self.stack[self.sp-arity..]);
                            for _ in 0..arity {
                                try!(self.pop());
                            }
                            self.frames.push(Frame::new(f.clone()));
                            self.fp += 1;
                            continue; // don't advance PC of the new frame
                        },
                        _ => {
                            // println!("not a function: {:?}", func);
                            return Err(Error::NotAFunction)
                        }
                    }
                },
                CALL(func, arity) => {
                    match func {
                        Function::Native(n) => {
                            let len = self.stack.len();
                            let r = try!(eval_native_borrow(self, n, len - arity));
                            for _ in 0..arity {
                                try!(self.pop());
                            }
                            self.push(r);
                        },
                        _ => {
                            return Err(Error::NotImplemented)
                        }
                    }
                },
            }
            self.current_frame().pc += 1;
        }
        self.pop()
    }

    fn push(&mut self, a: Atom)  {
        self.stack.push(a);
        self.sp += 1
    }

    fn pop(&mut self) -> AtomResult {
        if self.sp > 0 {
            self.sp -= 1;
            return self.stack.pop().ok_or(Error::RuntimeAssertion)
        } else {
            return Err(Error::RuntimeAssertion)
        }
    }
}

pub fn empty_frame() -> Frame {
    Frame::new(CompiledFunction{
        body: Vec::new(),
        params: empty_list(),
        env: Env::new(None)
    })
}
#[cfg(test)]
mod tests {
    use super::*;
    use parser::*;
    use atom::*;
    use env::*;

    fn run_expr(s: &str) -> Atom {
        let p = tokenize(s).unwrap();
        let mut out = Vec::new();
        let env = Env::new(None);
        compile(p, &mut out, &env).unwrap();
        let mut vm = VirtualMachine::new();
        let mut f = empty_frame();
        f.program.env = env;
        f.program.body = out;
        vm.frames.push(f);
        vm.run().unwrap()
    }

    #[test]
    fn test() {
        assert_eq!(Atom::from(6), run_expr("(+ (+ 2 2) 2)"));
        assert_eq!(Atom::symbol("a"), run_expr("(def a 1)"));
        assert_eq!(Atom::from(3), run_expr("(do (def foo (fn (x) (+ x 2))) (foo 1))"));
    }

    #[test]
    fn test_invoke_fn_inline() {
        assert_eq!(Atom::from(1), run_expr("((fn (x) 1) 0)"));
    }

    #[test]
    fn test_closure() {
        assert_eq!(Atom::from(2), run_expr("(do (def foo (fn (x) (fn () (* 2 x)))) (def foo2 (foo 1)) (foo2))"))
    }

    #[test]
    fn let_binding() {
        assert_eq!(Atom::from(-1), run_expr("(let* ((x 2) (y 3)) (- x y))"));
    }

    #[test]
    fn nested_let() {
        assert_eq!(Atom::from(0), run_expr("(do (let* ((x 1)) (let* ((x 0)) x)))"))
    }

    #[test]
    fn if_test() {
        assert_eq!(Atom::from(0), run_expr("(if false 1 0)"));
        assert_eq!(Atom::from(1), run_expr("(if true 1 0)"));
    }

    #[test]
    fn quote_test() {
        assert_eq!(Atom::list(vec![Atom::from(0), Atom::from(1)]), run_expr("'(0 1)"));
    }
}
<|endoftext|>


<commit_before>use atom::*;
use env::Env;
use errors::*;
use funcs::*;
use eval::*;
use opcodes::*;
use opcodes::Opcode::*;
use compiler;
use parser::tokenize;

#[derive (Debug, Clone)]
struct Frame {
    program: CompiledFunction,
    pc: usize,
}

impl Frame {
    fn new(f: CompiledFunction) -> Frame {
        Frame {
            program: f,
            pc: 0,
        }
    }

    fn current_instruction(&self) -> Option<&Opcode> {
        self.program.body.get(self.pc)
    }
}

#[derive (Debug)]
pub struct VirtualMachine {
    stack: Vec<Atom>,
    frames: Vec<Frame>,
    fp: usize,
    sp: usize,
    root: Env,
}

impl Default for VirtualMachine {
    fn default() -> VirtualMachine {
        use base_lib;
        let mut vm = VirtualMachine {
            stack: vec![],
            frames: vec![],
            fp: 0,
            sp: 0,
            root: Env::default(),
        };
        base_lib::library()
            .and_then(|n| vm.run_node(n))
            .expect("base library should always compile and run!");

        vm
    }
}

fn eval_native_borrow(v: &mut VirtualMachine, n: Native, len: usize) -> AtomResult {
    let &mut VirtualMachine { ref mut stack, ref mut frames, fp, .. } = v;
    eval_native(n, &stack[len..], &mut frames[fp].program.env)
}

fn recur_borrow(v: &mut VirtualMachine, len: usize) {
    let &mut VirtualMachine { ref mut stack, ref mut frames, fp, sp, .. } = v;
    let frame = &mut frames[fp];
    frame.program.env.bind_mut(&frame.program.params, &stack[sp - len..]);
    frame.pc = 0;
}

impl VirtualMachine {
    pub fn run_node(&mut self, node: Atom) -> AtomResult {
        let mut out = vec![];
        let source = try!(node.as_list()).clone();
        let mut e = self.root.clone();
        try!(compiler::compile(node, &mut out, &mut e));
        let frame = Frame::new(CompiledFunction {
            body: out,
            source: source,
            params: empty_list(),
            env: e,
        });
        self.frames.push(frame);
        self.run()
    }

    pub fn print_stack(&self) {
        for (i, item) in self.stack.iter().rev().enumerate() {
            println!("{}: {}", -(i as i32), item);
        }
    }

    pub fn eval_string(&mut self, s: &str) -> AtomResult {
        use parser;
        parser::tokenize(s).and_then(|a| self.run_node(a))
    }

    fn next_instruction(&self) -> Option<Opcode> {
        self.frames[self.fp].current_instruction().cloned()
    }

    fn current_frame(&mut self) -> &mut Frame {
        &mut self.frames[self.fp]
    }

    fn current_env(&mut self) -> &mut Env {
        &mut self.current_frame().program.env
    }

    pub fn run(&mut self) -> AtomResult {
        while let Some(instruction) = self.next_instruction() {
            trace!("{} - {}", self.current_frame().pc, instruction);
            match instruction {
                JUMP_IFNOT(addr) => {
                    if !try!(self.pop()).as_bool() {
                        self.current_frame().pc = addr;
                        continue;
                    }
                }
                EVAL => {
                    let e = try!(self.pop());
                    let eval_string = try!(e.as_string());
                    let tokens = try!(tokenize(eval_string));
                    self.fp += 1;
                    let value = self.run_node(tokens);
                    self.fp -= 1;
                    self.push(try!(value));
                }
                JUMP(addr) => {
                    self.current_frame().pc = addr;
                    continue;
                }
                POP => {
                    try!(self.pop());
                }
                RETURN => {
                    self.frames.pop();
                    if self.fp > 0 {
                        self.fp -= 1;
                    } else {
                        return self.pop();
                    }
                }
                LOAD(sym) => {
                    let value =
                        self.current_frame().program.env.get(sym.as_ref()).unwrap_or(Atom::Nil);
                    self.push(value);
                }
                CONST(ref atom) => self.push(atom.clone()),
                DEFINE(sym) => {
                    let v = try!(self.pop());
                    let a = try!(self.current_env().define(sym, v));
                    self.push(a);
                }
                STORE(sym) => {
                    let v = try!(self.pop());
                    let a = try!(self.current_env().set(sym, v));
                    self.push(a);
                }
                RECUR(arity) => {
                    recur_borrow(self, arity);
                    for _ in 0..arity {
                        try!(self.pop());
                    }
                    continue;
                }
                APPLY => {
                    let args = try!(self.pop());
                    let func = try!(self.pop());
                    let arg_list = try!(args.as_list());
                    match func {
                        Atom::Function(Function::Compiled(mut f)) => {
                            f.env.bind_mut(&f.params, arg_list);
                            self.frames.push(Frame::new(f.clone()));
                            self.fp += 1;
                            continue; // don't advance PC of the new frame
                        }
                        Atom::Function(Function::Native(f)) => {
                            let x = try!(eval_native(f,
                                                     arg_list,
                                                     &mut self.frames[self.fp].program.env));
                            self.push(x);
                        }
                        Atom::Function(Function::Proc(ref f)) => {
                            println!("compiled functions only!");
                            println!("{}", print_list(&f.body));
                            return Err(Error::NotImplemented);
                        }
                        _ => {
                            println!("attempted to call: {}", func);
                            return Err(Error::NotAFunction);
                        }
                    }
                }
                DCALL(arity) => {
                    let func = try!(self.pop());
                    match func {
                        Atom::Function(Function::Compiled(mut f)) => {
                            f.env.bind_mut(&f.params, &self.stack[self.sp - arity..]);
                            for _ in 0..arity {
                                try!(self.pop());
                            }
                            self.frames.push(Frame::new(f.clone()));
                            self.fp += 1;
                            continue; // don't advance PC of the new frame
                        }
                        Atom::Function(Function::Proc(ref f)) => {
                            println!("compiled functions only!");
                            println!("{}", print_list(&f.body));
                            return Err(Error::NotImplemented);
                        }
                        Atom::Function(Function::Native(n)) => {
                            let len = self.stack.len();
                            let r = try!(eval_native_borrow(self, n, len - arity));
                            for _ in 0..arity {
                                try!(self.pop());
                            }
                            self.push(r);
                        }
                        _ => {
                            println!("attempted to call: {}", func);
                            return Err(Error::NotAFunction);
                        }
                    }
                }
                CALL(func, arity) => {
                    match func {
                        Function::Native(n) => {
                            let len = self.stack.len();
                            let r = try!(eval_native_borrow(self, n, len - arity));
                            for _ in 0..arity {
                                try!(self.pop());
                            }
                            self.push(r);
                        }
                        _ => return Err(Error::NotImplemented),
                    }
                }
            }
            self.current_frame().pc += 1;
        }
        self.frames.pop().unwrap();
        self.pop()
    }

    fn push(&mut self, a: Atom) {
        self.stack.push(a);
        self.sp += 1
    }

    fn pop(&mut self) -> AtomResult {
        if self.sp > 0 {
            self.sp -= 1;
            self.stack.pop().ok_or(Error::RuntimeAssertion)
        } else {
            println!("stack = {:?}", &self.stack);
            println!("StackUnderflow!");
            Err(Error::RuntimeAssertion)
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use parser::*;
    use atom::*;
    fn run_expr(s: &str) -> Atom {
        let p = tokenize(s).unwrap();
        let mut vm = VirtualMachine::default();
        vm.run_node(p).unwrap()
    }

    #[test]
    fn test() {
        assert_eq!(Atom::from(6), run_expr("(+ (+ 2 2) 2)"));
        assert_eq!(Atom::symbol("a"), run_expr("(def a 1)"));
        assert_eq!(Atom::from(3),
                   run_expr("(do (def foo (fn (x) (+ x 2))) (foo 1))"));
    }

    #[test]
    fn test_invoke_fn_inline() {
        assert_eq!(Atom::from(1), run_expr("((fn (x) 1) 0)"));
    }

    #[test]
    fn test_closure() {
        assert_eq!(Atom::from(2),
                   run_expr("(do (def foo (fn (x) (fn () (* 2 x)))) (def foo2 (foo 1)) (foo2))"))
    }

    #[test]
    fn let_binding() {
        assert_eq!(Atom::from(-1), run_expr("(let* ((x 2) (y 3)) (- x y))"));
    }

    #[test]
    fn nested_let() {
        assert_eq!(Atom::from(0),
                   run_expr("(do (let* ((x 1)) (let* ((x 0)) x)))"))
    }

    #[test]
    fn if_test() {
        assert_eq!(Atom::from(0), run_expr("(if false 1 0)"));
        assert_eq!(Atom::from(1), run_expr("(if true 1 0)"));
    }

    #[test]
    fn quote_test() {
        assert_eq!(Atom::list(vec![Atom::from(0), Atom::from(1)]),
                   run_expr("'(0 1)"));
    }

    #[test]
    fn filter_test() {
        assert_eq!(Atom::list(vec![Atom::from(0)]),
                   run_expr("(filter (fn (x) (= 0 x)) '(1 1 1 1 0 1))"));
    }

    #[test]
    fn recur_test() {
        let s = include_str!("../test/recur.myco");
        assert_eq!(Atom::from(0), run_expr(s));
    }

    #[test]
    fn map_test() {
        assert_eq!(run_expr("'(1 2)"),
                   run_expr("(map (fn (x) (+ x 1)) '(0 1))"));

    }

    #[test]
    fn map_leak_test() {
        let mut vm = VirtualMachine::default();
        let l = Atom::list(vec![Atom::from(1), Atom::from(2)]);
        assert_eq!(l, vm.eval_string("(map (fn (x) (+ x 1)) '(0 1))").unwrap());
        assert_eq!(l, vm.eval_string("(map (fn (x) (+ x 1)) '(0 1))").unwrap());
    }

    #[test]
    fn reduce_test() {
        assert_eq!(Atom::from(0),
                   run_expr("(reduce (fn (acc i) (- acc i)) 5 '(1 1 1 1 1))"));
    }

    #[test]
    fn apply_test() {
        assert_eq!(Atom::from(0), run_expr("(apply (fn (x) 0) '())"));
        assert_eq!(Atom::from(3),
                   run_expr("(apply (fn (x y) (+ x y 1)) '(1 1))"));
        assert_eq!(Atom::from(1),
                   run_expr("(apply (fn (x & y) (count y)) '(1 1))"));
    }

    #[test]
    fn get_test() {
        assert_eq!(Atom::from(0), run_expr("(get '(0 2) 0)"));
        assert_eq!(Atom::from(0), run_expr("(get '(2 0) 1)"));
    }

    #[test]
    fn cond_test() {
        assert_eq!(Atom::from(0), run_expr("(cond (true 0))"));
        assert_eq!(Atom::from(0), run_expr("(cond (false 1) (true 0))"));
        assert_eq!(Atom::from(0),
                   run_expr("(cond (false 1) ((= 0 1) 2) (true 0))"));
    }

    #[test]
    fn map_native_test() {
        assert_eq!(Atom::list(vec![Atom::from(1), Atom::from(2)]),
                   run_expr("(map + '(1 2))"));
    }

    #[test]
    fn eval_test() {
        assert_eq!(Atom::from(0), run_expr("(eval \"0\")"));
    }

    #[test]
    fn run_suite() {
        let suite = include_str!("../test/suite.myco");
        assert_eq!(Atom::from(true), run_expr(suite));
    }
}
<commit_msg>add load test<commit_after>use atom::*;
use env::Env;
use errors::*;
use funcs::*;
use eval::*;
use opcodes::*;
use opcodes::Opcode::*;
use compiler;
use parser::tokenize;

#[derive (Debug, Clone)]
struct Frame {
    program: CompiledFunction,
    pc: usize,
}

impl Frame {
    fn new(f: CompiledFunction) -> Frame {
        Frame {
            program: f,
            pc: 0,
        }
    }

    fn current_instruction(&self) -> Option<&Opcode> {
        self.program.body.get(self.pc)
    }
}

#[derive (Debug)]
pub struct VirtualMachine {
    stack: Vec<Atom>,
    frames: Vec<Frame>,
    fp: usize,
    sp: usize,
    root: Env,
}

impl Default for VirtualMachine {
    fn default() -> VirtualMachine {
        use base_lib;
        let mut vm = VirtualMachine {
            stack: vec![],
            frames: vec![],
            fp: 0,
            sp: 0,
            root: Env::default(),
        };
        base_lib::library()
            .and_then(|n| vm.run_node(n))
            .expect("base library should always compile and run!");

        vm
    }
}

fn eval_native_borrow(v: &mut VirtualMachine, n: Native, len: usize) -> AtomResult {
    let &mut VirtualMachine { ref mut stack, ref mut frames, fp, .. } = v;
    eval_native(n, &stack[len..], &mut frames[fp].program.env)
}

fn recur_borrow(v: &mut VirtualMachine, len: usize) {
    let &mut VirtualMachine { ref mut stack, ref mut frames, fp, sp, .. } = v;
    let frame = &mut frames[fp];
    frame.program.env.bind_mut(&frame.program.params, &stack[sp - len..]);
    frame.pc = 0;
}

impl VirtualMachine {
    pub fn run_node(&mut self, node: Atom) -> AtomResult {
        let mut out = vec![];
        let source = try!(node.as_list()).clone();
        let mut e = self.root.clone();
        try!(compiler::compile(node, &mut out, &mut e));
        let frame = Frame::new(CompiledFunction {
            body: out,
            source: source,
            params: empty_list(),
            env: e,
        });
        self.frames.push(frame);
        self.run()
    }

    pub fn print_stack(&self) {
        for (i, item) in self.stack.iter().rev().enumerate() {
            println!("{}: {}", -(i as i32), item);
        }
    }

    pub fn eval_string(&mut self, s: &str) -> AtomResult {
        use parser;
        parser::tokenize(s).and_then(|a| self.run_node(a))
    }

    fn next_instruction(&self) -> Option<Opcode> {
        self.frames[self.fp].current_instruction().cloned()
    }

    fn current_frame(&mut self) -> &mut Frame {
        &mut self.frames[self.fp]
    }

    fn current_env(&mut self) -> &mut Env {
        &mut self.current_frame().program.env
    }

    pub fn run(&mut self) -> AtomResult {
        while let Some(instruction) = self.next_instruction() {
            trace!("{} - {}", self.current_frame().pc, instruction);
            match instruction {
                JUMP_IFNOT(addr) => {
                    if !try!(self.pop()).as_bool() {
                        self.current_frame().pc = addr;
                        continue;
                    }
                }
                EVAL => {
                    let e = try!(self.pop());
                    let eval_string = try!(e.as_string());
                    let tokens = try!(tokenize(eval_string));
                    self.fp += 1;
                    let value = self.run_node(tokens);
                    self.fp -= 1;
                    self.push(try!(value));
                }
                JUMP(addr) => {
                    self.current_frame().pc = addr;
                    continue;
                }
                POP => {
                    try!(self.pop());
                }
                RETURN => {
                    self.frames.pop();
                    if self.fp > 0 {
                        self.fp -= 1;
                    } else {
                        return self.pop();
                    }
                }
                LOAD(sym) => {
                    let value =
                        self.current_frame().program.env.get(sym.as_ref()).unwrap_or(Atom::Nil);
                    self.push(value);
                }
                CONST(ref atom) => self.push(atom.clone()),
                DEFINE(sym) => {
                    let v = try!(self.pop());
                    let a = try!(self.current_env().define(sym, v));
                    self.push(a);
                }
                STORE(sym) => {
                    let v = try!(self.pop());
                    let a = try!(self.current_env().set(sym, v));
                    self.push(a);
                }
                RECUR(arity) => {
                    recur_borrow(self, arity);
                    for _ in 0..arity {
                        try!(self.pop());
                    }
                    continue;
                }
                APPLY => {
                    let args = try!(self.pop());
                    let func = try!(self.pop());
                    let arg_list = try!(args.as_list());
                    match func {
                        Atom::Function(Function::Compiled(mut f)) => {
                            f.env.bind_mut(&f.params, arg_list);
                            self.frames.push(Frame::new(f.clone()));
                            self.fp += 1;
                            continue; // don't advance PC of the new frame
                        }
                        Atom::Function(Function::Native(f)) => {
                            let x = try!(eval_native(f,
                                                     arg_list,
                                                     &mut self.frames[self.fp].program.env));
                            self.push(x);
                        }
                        Atom::Function(Function::Proc(ref f)) => {
                            println!("compiled functions only!");
                            println!("{}", print_list(&f.body));
                            return Err(Error::NotImplemented);
                        }
                        _ => {
                            println!("attempted to call: {}", func);
                            return Err(Error::NotAFunction);
                        }
                    }
                }
                DCALL(arity) => {
                    let func = try!(self.pop());
                    match func {
                        Atom::Function(Function::Compiled(mut f)) => {
                            f.env.bind_mut(&f.params, &self.stack[self.sp - arity..]);
                            for _ in 0..arity {
                                try!(self.pop());
                            }
                            self.frames.push(Frame::new(f.clone()));
                            self.fp += 1;
                            continue; // don't advance PC of the new frame
                        }
                        Atom::Function(Function::Proc(ref f)) => {
                            println!("compiled functions only!");
                            println!("{}", print_list(&f.body));
                            return Err(Error::NotImplemented);
                        }
                        Atom::Function(Function::Native(n)) => {
                            let len = self.stack.len();
                            let r = try!(eval_native_borrow(self, n, len - arity));
                            for _ in 0..arity {
                                try!(self.pop());
                            }
                            self.push(r);
                        }
                        _ => {
                            println!("attempted to call: {}", func);
                            return Err(Error::NotAFunction);
                        }
                    }
                }
                CALL(func, arity) => {
                    match func {
                        Function::Native(n) => {
                            let len = self.stack.len();
                            let r = try!(eval_native_borrow(self, n, len - arity));
                            for _ in 0..arity {
                                try!(self.pop());
                            }
                            self.push(r);
                        }
                        _ => return Err(Error::NotImplemented),
                    }
                }
            }
            self.current_frame().pc += 1;
        }
        self.frames.pop().unwrap();
        self.pop()
    }

    fn push(&mut self, a: Atom) {
        self.stack.push(a);
        self.sp += 1
    }

    fn pop(&mut self) -> AtomResult {
        if self.sp > 0 {
            self.sp -= 1;
            self.stack.pop().ok_or(Error::RuntimeAssertion)
        } else {
            println!("stack = {:?}", &self.stack);
            println!("StackUnderflow!");
            Err(Error::RuntimeAssertion)
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use parser::*;
    use atom::*;
    fn run_expr(s: &str) -> Atom {
        let p = tokenize(s).unwrap();
        let mut vm = VirtualMachine::default();
        vm.run_node(p).unwrap()
    }

    #[test]
    fn test() {
        assert_eq!(Atom::from(6), run_expr("(+ (+ 2 2) 2)"));
        assert_eq!(Atom::symbol("a"), run_expr("(def a 1)"));
        assert_eq!(Atom::from(3),
                   run_expr("(do (def foo (fn (x) (+ x 2))) (foo 1))"));
    }

    #[test]
    fn test_invoke_fn_inline() {
        assert_eq!(Atom::from(1), run_expr("((fn (x) 1) 0)"));
    }

    #[test]
    fn test_closure() {
        assert_eq!(Atom::from(2),
                   run_expr("(do (def foo (fn (x) (fn () (* 2 x)))) (def foo2 (foo 1)) (foo2))"))
    }

    #[test]
    fn let_binding() {
        assert_eq!(Atom::from(-1), run_expr("(let* ((x 2) (y 3)) (- x y))"));
    }

    #[test]
    fn nested_let() {
        assert_eq!(Atom::from(0),
                   run_expr("(do (let* ((x 1)) (let* ((x 0)) x)))"))
    }

    #[test]
    fn if_test() {
        assert_eq!(Atom::from(0), run_expr("(if false 1 0)"));
        assert_eq!(Atom::from(1), run_expr("(if true 1 0)"));
    }

    #[test]
    fn quote_test() {
        assert_eq!(Atom::list(vec![Atom::from(0), Atom::from(1)]),
                   run_expr("'(0 1)"));
    }

    #[test]
    fn filter_test() {
        assert_eq!(Atom::list(vec![Atom::from(0)]),
                   run_expr("(filter (fn (x) (= 0 x)) '(1 1 1 1 0 1))"));
    }

    #[test]
    fn recur_test() {
        let s = include_str!("../test/recur.myco");
        assert_eq!(Atom::from(0), run_expr(s));
    }

    #[test]
    fn load_test() {
        assert_eq!(Atom::from(0), run_expr("(load \"test/recur.myco\")"));
    }

    #[test]
    fn map_test() {
        assert_eq!(run_expr("'(1 2)"),
                   run_expr("(map (fn (x) (+ x 1)) '(0 1))"));

    }

    #[test]
    fn map_leak_test() {
        let mut vm = VirtualMachine::default();
        let l = Atom::list(vec![Atom::from(1), Atom::from(2)]);
        assert_eq!(l, vm.eval_string("(map (fn (x) (+ x 1)) '(0 1))").unwrap());
        assert_eq!(l, vm.eval_string("(map (fn (x) (+ x 1)) '(0 1))").unwrap());
    }

    #[test]
    fn reduce_test() {
        assert_eq!(Atom::from(0),
                   run_expr("(reduce (fn (acc i) (- acc i)) 5 '(1 1 1 1 1))"));
    }

    #[test]
    fn apply_test() {
        assert_eq!(Atom::from(0), run_expr("(apply (fn (x) 0) '())"));
        assert_eq!(Atom::from(3),
                   run_expr("(apply (fn (x y) (+ x y 1)) '(1 1))"));
        assert_eq!(Atom::from(1),
                   run_expr("(apply (fn (x & y) (count y)) '(1 1))"));
    }

    #[test]
    fn get_test() {
        assert_eq!(Atom::from(0), run_expr("(get '(0 2) 0)"));
        assert_eq!(Atom::from(0), run_expr("(get '(2 0) 1)"));
    }

    #[test]
    fn cond_test() {
        assert_eq!(Atom::from(0), run_expr("(cond (true 0))"));
        assert_eq!(Atom::from(0), run_expr("(cond (false 1) (true 0))"));
        assert_eq!(Atom::from(0),
                   run_expr("(cond (false 1) ((= 0 1) 2) (true 0))"));
    }

    #[test]
    fn map_native_test() {
        assert_eq!(Atom::list(vec![Atom::from(1), Atom::from(2)]),
                   run_expr("(map + '(1 2))"));
    }

    #[test]
    fn eval_test() {
        assert_eq!(Atom::from(0), run_expr("(eval \"0\")"));
    }

    #[test]
    fn run_suite() {
        let suite = include_str!("../test/suite.myco");
        assert_eq!(Atom::from(true), run_expr(suite));
    }
}
<|endoftext|>


<commit_before>//! Virtual machine.

use std::rc::Rc;
use std::{error, result};
use std::fmt;
use std::collections::HashMap;
use std::error::Error as StdError;
use item::{Block, BlockItem, Stack, StackItem};

pub type Result<T> = result::Result<T, Error>;

#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub enum Error {
    TypeError,
    DivideByZero,
    StackUnderflow,
    UnknownMethod,
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.description())
    }
}

impl error::Error for Error {
    fn description(&self) -> &str {
        match *self {
            Error::DivideByZero => "Divided by zero",
            Error::TypeError => "Type error",
            Error::StackUnderflow => "Stack underflow",
            Error::UnknownMethod => "Unknown method",
        }
    }
}

pub enum Method {
    Builtin(Box<Fn(&mut Vm) -> Result<()>>),
    Block(Block),
}

pub struct Vm {
    pub stack: Stack,
    pub methods: HashMap<String, Rc<Method>>,
}

impl Vm {
    pub fn run(&mut self, item: &BlockItem) -> Result<()> {
        match *item {
            BlockItem::Literal(ref stack_item) =>
                self.stack.push(stack_item.clone()),
            BlockItem::Call(ref name) => {
                let method = match self.methods.get(&*name) {
                    Some(m) => m.clone(),
                    None => return Err(Error::UnknownMethod),
                };
                try!(match *method {
                    Method::Builtin(ref f) => (**f)(self),
                    Method::Block(ref b) => self.run_block(b),
                })
            },
        }
        Ok(())
    }

    pub fn run_block(&mut self, block: &Block) -> Result<()> {
        for item in block.iter() {
            try!(self.run(item));
        }
        Ok(())
    }

    pub fn builtin(&mut self, name: String, method: Box<Fn(&mut Vm)
                   -> Result<()>>) {
        self.methods.insert(name, Rc::new(Method::Builtin(method)));
    }

    pub fn pop_integer(&mut self) -> Result<i64> {
        match self.stack.pop() {
            Some(StackItem::Integer(i)) => Ok(i),
            Some(..) => Err(Error::TypeError),
            None => Err(Error::StackUnderflow),
        }
    }

    pub fn new() -> Vm {
        Vm {
            stack: Vec::new(),
            methods: HashMap::new(),
        }
    }

    pub fn new_with_builtins() -> Vm {
        let mut vm = Vm::new();
        vm.builtin("+".into(), Box::new(|vm| {
            let n2 = try!(vm.pop_integer());
            let n1 = try!(vm.pop_integer());
            vm.stack.push(StackItem::Integer(n1 + n2));
            Ok(())
        }));
        vm.builtin("-".into(), Box::new(|vm| {
            let n2 = try!(vm.pop_integer());
            let n1 = try!(vm.pop_integer());
            vm.stack.push(StackItem::Integer(n1 - n2));
            Ok(())
        }));
        vm.builtin("*".into(), Box::new(|vm| {
            let n2 = try!(vm.pop_integer());
            let n1 = try!(vm.pop_integer());
            vm.stack.push(StackItem::Integer(n1 * n2));
            Ok(())
        }));
        vm.builtin("/".into(), Box::new(|vm| {
            let n2 = try!(vm.pop_integer());
            let n1 = try!(vm.pop_integer());
            match n2 {
                0 => return Err(Error::DivideByZero),
                _ => vm.stack.push(StackItem::Integer(n1 / n2)),
            }
            Ok(())
        }));
        vm.builtin("fn".into(), Box::new(|vm| {
            let block = try!(vm.stack.pop().ok_or(Error::StackUnderflow));
            let name = try!(vm.stack.pop().ok_or(Error::StackUnderflow));
            match (name, block) {
                (StackItem::String(s), StackItem::Block(b)) =>
                    { vm.methods.insert(s, Rc::new(Method::Block(b))); },
                _ => return Err(Error::TypeError),
            }
            Ok(())
        }));
        vm.builtin("swap".into(), Box::new(|vm| {
            let a = try!(vm.stack.pop().ok_or(Error::StackUnderflow));
            let b = try!(vm.stack.pop().ok_or(Error::StackUnderflow));
            vm.stack.push(a);
            vm.stack.push(b);
            Ok(())
        }));
        vm.builtin("dup".into(), Box::new(|vm| {
            let a = try!(vm.stack.pop().ok_or(Error::StackUnderflow));
            vm.stack.push(a.clone());
            vm.stack.push(a);
            Ok(())
        }));
        vm.builtin("pop".into(), Box::new(|vm| {
            let _ = try!(vm.stack.pop().ok_or(Error::StackUnderflow));
            Ok(())
        }));
        vm.builtin("false".into(), Box::new(|vm| {
            vm.stack.push(StackItem::Boolean(false));
            Ok(())
        }));
        vm.builtin("true".into(), Box::new(|vm| {
            vm.stack.push(StackItem::Boolean(true));
            Ok(())
        }));
        vm.builtin("eq".into(), Box::new(|vm| {
            let a = try!(vm.stack.pop().ok_or(Error::StackUnderflow));
            let b = try!(vm.stack.pop().ok_or(Error::StackUnderflow));
            vm.stack.push(StackItem::Boolean(a == b));
            Ok(())
        }));
        vm.builtin("not".into(), Box::new(|vm| {
            let a = try!(vm.stack.pop().ok_or(Error::StackUnderflow));
            if let StackItem::Boolean(boolean) = a {
                vm.stack.push(StackItem::Boolean(!boolean));
            } else {
                return Err(Error::TypeError)
            }
            Ok(())
        }));
        vm.builtin("if".into(), Box::new(|vm| {
            let block = try!(vm.stack.pop().ok_or(Error::StackUnderflow));
            let condition = try!(vm.stack.pop().ok_or(Error::StackUnderflow));
            if let (StackItem::Block(block), StackItem::Boolean(condition)) =
                    (block, condition) {
                if condition {
                    try!(vm.run_block(&block));
                }
            } else {
                return Err(Error::TypeError);
            }
            Ok(())
        }));
        vm.builtin("while".into(), Box::new(|vm| {
            let action_block = try!(vm.stack.pop().ok_or(Error::StackUnderflow));
            let condition_block = try!(vm.stack.pop().ok_or(Error::StackUnderflow));
            if let (StackItem::Block(action_block), StackItem::Block(condition_block)) =
                    (action_block, condition_block) {
                loop {
                    try!(vm.run_block(&condition_block));
                    let condition = try!(vm.stack.pop().ok_or(Error::StackUnderflow));
                    if let StackItem::Boolean(condition) = condition {
                        if condition {
                            try!(vm.run_block(&action_block));
                        } else {
                            break;
                        }
                    } else {
                        return Err(Error::TypeError);
                    }
                }
            } else {
                return Err(Error::TypeError);
            }
            Ok(())
        }));
        vm
    }
}
<commit_msg>Improve ergonomics with defining builtins<commit_after>//! Virtual machine.

use std::rc::Rc;
use std::{error, result};
use std::fmt;
use std::collections::HashMap;
use std::error::Error as StdError;
use item::{Block, BlockItem, Stack, StackItem};

pub type Result<T> = result::Result<T, Error>;

#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub enum Error {
    TypeError,
    DivideByZero,
    StackUnderflow,
    UnknownMethod,
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.description())
    }
}

impl error::Error for Error {
    fn description(&self) -> &str {
        match *self {
            Error::DivideByZero => "Divided by zero",
            Error::TypeError => "Type error",
            Error::StackUnderflow => "Stack underflow",
            Error::UnknownMethod => "Unknown method",
        }
    }
}

pub enum Method {
    Builtin(Box<Fn(&mut Vm) -> Result<()>>),
    Block(Block),
}

pub struct Vm {
    pub stack: Stack,
    pub methods: HashMap<String, Rc<Method>>,
}

impl Vm {
    pub fn run(&mut self, item: &BlockItem) -> Result<()> {
        match *item {
            BlockItem::Literal(ref stack_item) =>
                self.stack.push(stack_item.clone()),
            BlockItem::Call(ref name) => {
                let method = match self.methods.get(&*name) {
                    Some(m) => m.clone(),
                    None => return Err(Error::UnknownMethod),
                };
                try!(match *method {
                    Method::Builtin(ref f) => (**f)(self),
                    Method::Block(ref b) => self.run_block(b),
                })
            },
        }
        Ok(())
    }

    pub fn run_block(&mut self, block: &Block) -> Result<()> {
        for item in block.iter() {
            try!(self.run(item));
        }
        Ok(())
    }

    pub fn builtin<S>(&mut self, name: S, method: Box<Fn(&mut Vm)
                   -> Result<()>>) where S: Into<String> {
        self.methods.insert(name.into(), Rc::new(Method::Builtin(method)));
    }

    pub fn pop_integer(&mut self) -> Result<i64> {
        match self.stack.pop() {
            Some(StackItem::Integer(i)) => Ok(i),
            Some(..) => Err(Error::TypeError),
            None => Err(Error::StackUnderflow),
        }
    }

    pub fn new() -> Vm {
        Vm {
            stack: Vec::new(),
            methods: HashMap::new(),
        }
    }

    pub fn new_with_builtins() -> Vm {
        let mut vm = Vm::new();
        vm.builtin("+", Box::new(|vm| {
            let n2 = try!(vm.pop_integer());
            let n1 = try!(vm.pop_integer());
            vm.stack.push(StackItem::Integer(n1 + n2));
            Ok(())
        }));
        vm.builtin("-", Box::new(|vm| {
            let n2 = try!(vm.pop_integer());
            let n1 = try!(vm.pop_integer());
            vm.stack.push(StackItem::Integer(n1 - n2));
            Ok(())
        }));
        vm.builtin("*", Box::new(|vm| {
            let n2 = try!(vm.pop_integer());
            let n1 = try!(vm.pop_integer());
            vm.stack.push(StackItem::Integer(n1 * n2));
            Ok(())
        }));
        vm.builtin("/", Box::new(|vm| {
            let n2 = try!(vm.pop_integer());
            let n1 = try!(vm.pop_integer());
            match n2 {
                0 => return Err(Error::DivideByZero),
                _ => vm.stack.push(StackItem::Integer(n1 / n2)),
            }
            Ok(())
        }));
        vm.builtin("fn", Box::new(|vm| {
            let block = try!(vm.stack.pop().ok_or(Error::StackUnderflow));
            let name = try!(vm.stack.pop().ok_or(Error::StackUnderflow));
            match (name, block) {
                (StackItem::String(s), StackItem::Block(b)) =>
                    { vm.methods.insert(s, Rc::new(Method::Block(b))); },
                _ => return Err(Error::TypeError),
            }
            Ok(())
        }));
        vm.builtin("swap", Box::new(|vm| {
            let a = try!(vm.stack.pop().ok_or(Error::StackUnderflow));
            let b = try!(vm.stack.pop().ok_or(Error::StackUnderflow));
            vm.stack.push(a);
            vm.stack.push(b);
            Ok(())
        }));
        vm.builtin("dup", Box::new(|vm| {
            let a = try!(vm.stack.pop().ok_or(Error::StackUnderflow));
            vm.stack.push(a.clone());
            vm.stack.push(a);
            Ok(())
        }));
        vm.builtin("pop", Box::new(|vm| {
            let _ = try!(vm.stack.pop().ok_or(Error::StackUnderflow));
            Ok(())
        }));
        vm.builtin("false", Box::new(|vm| {
            vm.stack.push(StackItem::Boolean(false));
            Ok(())
        }));
        vm.builtin("true", Box::new(|vm| {
            vm.stack.push(StackItem::Boolean(true));
            Ok(())
        }));
        vm.builtin("eq", Box::new(|vm| {
            let a = try!(vm.stack.pop().ok_or(Error::StackUnderflow));
            let b = try!(vm.stack.pop().ok_or(Error::StackUnderflow));
            vm.stack.push(StackItem::Boolean(a == b));
            Ok(())
        }));
        vm.builtin("not", Box::new(|vm| {
            let a = try!(vm.stack.pop().ok_or(Error::StackUnderflow));
            if let StackItem::Boolean(boolean) = a {
                vm.stack.push(StackItem::Boolean(!boolean));
            } else {
                return Err(Error::TypeError)
            }
            Ok(())
        }));
        vm.builtin("if", Box::new(|vm| {
            let block = try!(vm.stack.pop().ok_or(Error::StackUnderflow));
            let condition = try!(vm.stack.pop().ok_or(Error::StackUnderflow));
            if let (StackItem::Block(block), StackItem::Boolean(condition)) =
                    (block, condition) {
                if condition {
                    try!(vm.run_block(&block));
                }
            } else {
                return Err(Error::TypeError);
            }
            Ok(())
        }));
        vm.builtin("while", Box::new(|vm| {
            let action_block = try!(vm.stack.pop().ok_or(Error::StackUnderflow));
            let condition_block = try!(vm.stack.pop().ok_or(Error::StackUnderflow));
            if let (StackItem::Block(action_block), StackItem::Block(condition_block)) =
                    (action_block, condition_block) {
                loop {
                    try!(vm.run_block(&condition_block));
                    let condition = try!(vm.stack.pop().ok_or(Error::StackUnderflow));
                    if let StackItem::Boolean(condition) = condition {
                        if condition {
                            try!(vm.run_block(&action_block));
                        } else {
                            break;
                        }
                    } else {
                        return Err(Error::TypeError);
                    }
                }
            } else {
                return Err(Error::TypeError);
            }
            Ok(())
        }));
        vm
    }
}
<|endoftext|>