<commit_before>#[macro_use]
mod macros;

use coerce::{AsBool,AsNumber};

use french_press::ScopeManager;
use js_types::binding::Binding;
use js_types::js_fn::JsFnStruct;
use js_types::js_var::{JsVar, JsType, JsPtrEnum};
use js_types::js_var::JsType::*;

use jsrs_parser::lalr::parse_Stmt;
use jsrs_common::ast::*;
use jsrs_common::ast::Exp::*;
use jsrs_common::ast::BinOp::*;
use jsrs_common::ast::Stmt::*;

pub fn eval_string(string: &str, state: &mut ScopeManager) -> JsVar {
    match parse_Stmt(string) {
        Ok(stmt) => {
            let (v, _) = eval_stmt(&stmt, state);
            v
        }
        Err(_) => panic!("parse error"),
    }
}

pub fn eval_stmt(s: &Stmt, mut state: &mut ScopeManager) -> (JsVar, Option<JsVar>) {
    match *s {
        Assign(ref var_string, ref exp) => {
            // TODO: this is a hack to return the value properly, which should be changed once we
            // stop using HashMap to store state.
            let mut var = eval_exp(exp, state);
            let cloned = var.clone();
            var.binding = Binding::new(var_string.clone());
            match state.alloc(var, None) {
                Ok(_) => (),
                e @ Err(_) => println!("{:?}", e),
            }
            (cloned, None)
        },
        BareExp(ref exp) => (eval_exp(exp, &mut state), None),
        Decl(ref var_string, ref exp) => {
            let mut var = eval_exp(exp, state);
            var.binding = Binding::new(var_string.clone());
            // TODO: use value
            match state.alloc(var.clone(), None) {
                Ok(_) => (var, None),
                e @ Err(_) => panic!("{:?}", e),
            }
        },
        If(ref condition, ref if_block, ref else_block) => {
            if eval_exp(&condition, state).as_bool() {
                eval_stmt(&*if_block, state)
            } else {
                if let Some(ref block) = *else_block {
                    eval_stmt(&*block, state)
                } else {
                    (JsVar::new(JsUndef), None)
                }
            }
        },
        Ret(ref e) => {
            let v = eval_exp(&e, &mut state);
            (v.clone(), Some(v))
        }
        Seq(ref s1, ref s2) => {
            let _exp = eval_stmt(&*s1, &mut state);
            eval_stmt(&*s2, &mut state)
        },
        While(ref condition, ref block) => {
            let mut ret_val = None;
            loop {
                if eval_exp(&condition, state).as_bool() {
                    let (_, v) = eval_stmt(&*block, state);
                    ret_val = v;
                } else {
                    return (JsVar::new(JsUndef), ret_val);
                }
            }
        }
    }
}

pub fn eval_exp(e: &Exp, mut state: &mut ScopeManager) -> JsVar {
    match e {
        &BinExp(ref e1, ref op, ref e2) => {
            let val1 = eval_exp(e1, state);
            let val2 = eval_exp(e2, state);

            match *op {
                And => JsVar::new(JsBool(val1.as_bool() && val2.as_bool())),
                Or  => JsVar::new(JsBool(val1.as_bool() || val2.as_bool())),

                Ge  => JsVar::new(JsBool(val1.as_bool() >= val2.as_bool())),
                Gt  => JsVar::new(JsBool(val1.as_bool() >  val2.as_bool())),
                Le  => JsVar::new(JsBool(val1.as_bool() <= val2.as_bool())),
                Lt  => JsVar::new(JsBool(val1.as_bool() <  val2.as_bool())),
                Neq => JsVar::new(JsBool(val1.as_bool() != val2.as_bool())),
                Eql => JsVar::new(JsBool(val1.as_bool() == val2.as_bool())),

                Minus => JsVar::new(JsNum(val1.as_number() - val2.as_number())),
                Plus  => JsVar::new(JsNum(val1.as_number() + val2.as_number())),
                Slash => JsVar::new(JsNum(val1.as_number() / val2.as_number())),
                Star  => JsVar::new(JsNum(val1.as_number() * val2.as_number())),
            }
        }
        &Bool(b) => JsVar::new(JsBool(b)),
        &Call(ref fun_name, ref params) => {
            let fun_binding = eval_exp(fun_name, state);
            match state.load(&fun_binding.binding) {
                Ok((var, opt_ptr)) => {
                    if let Some(JsPtrEnum::JsFn(js_fn_struct)) = opt_ptr {
                        let (_, v) = eval_stmt(&js_fn_struct.stmt, state);
                        v.unwrap_or(JsVar::new(JsUndef))
                    } else {
                        panic!(format!("Invalid call object."))
                    }
                },
                _ => panic!("ReferenceError: {} is not defined")
            }
        },
        &Defun(ref opt_binding, ref params, ref body) => {
            if let &Some(ref binding) = opt_binding {
                let js_fun = JsFnStruct::new(opt_binding, params, &**body);
                let mut var = JsVar::new(JsPtr);
                var.binding = Binding::new(binding.clone());
                if let Err(_) = state.alloc(var, Some(JsPtrEnum::JsFn(js_fun))) {
                    panic!("error storing function into state");
                }
                JsVar::new(JsPtr) // Doesn't store any information anyway
            } else {
                panic!("functions without bindings are not yet supported.")
            }
        },
        &Float(f) => JsVar::new(JsType::JsNum(f)),
        &Neg(ref exp) => JsVar::new(JsNum(-eval_exp(exp, state).as_number())),
        &Pos(ref exp) => JsVar::new(JsNum(eval_exp(exp, state).as_number())),

        &PostDec(ref exp) => eval_float_post_op!(exp, f, f - 1.0, state),
        &PostInc(ref exp) => eval_float_post_op!(exp, f, f + 1.0, state),
        &PreDec(ref exp)  => eval_float_pre_op!(exp, f, f - 1.0, state),
        &PreInc(ref exp)  => eval_float_pre_op!(exp, f, f + 1.0, state),

        &NewObject(_, _) => unimplemented!(),
        &Object(_) => unimplemented!(),
        &Undefined => JsVar::new(JsUndef),
        &Var(ref var_binding) => {
            match state.load(&Binding::new(var_binding.clone())) {
                Ok((var, _)) => var,
                _ => panic!(format!("ReferenceError: {} is not defined", var_binding))
            }
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use std::collections::hash_set::HashSet;
    use french_press::init_gc;
    use js_types::js_var::JsType;
    use js_types::binding::Binding;

    #[test]
    fn test_eval_literals() {
        let mut state = init_gc();
        assert_eq!(JsType::JsNum(5.0f64), eval_string("5.0;\n", &mut state).t);
        assert_eq!(JsType::JsNum(0.0f64), eval_string("0.0;\n", &mut state).t);
        assert_eq!(JsType::JsUndef, eval_string("undefined;\n", &mut state).t);
    }

    //// TODO: handle `var` and no `var` separately
    //#[test]
    //fn test_store_state() {
    //    let mut state = HashMap::new();
    //    assert_eq!(JsUndefined, eval_string("var a = 1;\n", &mut state));
    //    assert_eq!(JsNumber(2.0f64), eval_string("a = 2;\n", &mut state));
    //    assert_eq!(JsUndefined, eval_string("var b = 3;\n", &mut state));
    //    assert_eq!(JsNumber(4.0f64), eval_string("c = 4;\n", &mut state));
    //}

    #[test]
    fn test_inc_dec() {
        let mut state = init_gc();
        //assert_eq!(JsType::JsNum(1.0f64), eval_string("var a = 1;\n", &mut state).t);
        //assert_eq!(&JsType::JsNum(1.0), state.load(&Binding::new("a")).unwrap());

        //assert_eq!(JsType::JsNum(1.0f64), eval_string("a++;\n", &mut state));
        //assert_eq!(&JsNumber(2.0f64), state.get("a").unwrap());

        //assert_eq!(JsNumber(3.0f64), eval_string("++a;\n", &mut state));
        //assert_eq!(&JsNumber(3.0f64), state.get("a").unwrap());

        //assert_eq!(JsNumber(3.0f64), eval_string("a--;\n", &mut state));
        //assert_eq!(&JsNumber(2.0f64), state.get("a").unwrap());

        //assert_eq!(JsNumber(1.0f64), eval_string("--a;\n", &mut state));
        //assert_eq!(&JsNumber(1.0f64), state.get("a").unwrap());
    }

    #[test]
    fn test_binexp() {
        let mut state = init_gc();
        assert_eq!(JsType::JsNum(6.0f64),  eval_string("2.0 + 4.0;\n", &mut state).t);
        assert_eq!(JsType::JsNum(0.5f64),  eval_string("2.0 / 4.0;\n", &mut state).t);
        assert_eq!(JsType::JsNum(-2.0f64), eval_string("2.0 - 4.0;\n", &mut state).t);
        assert_eq!(JsType::JsNum(8.0f64),  eval_string("2.0 * 4.0;\n", &mut state).t);
    }
}
<commit_msg>function arguments working!<commit_after>#[macro_use]
mod macros;

use coerce::{AsBool,AsNumber};

use french_press::ScopeManager;
use js_types::binding::Binding;
use js_types::js_fn::JsFnStruct;
use js_types::js_var::{JsVar, JsType, JsPtrEnum};
use js_types::js_var::JsType::*;

use jsrs_parser::lalr::parse_Stmt;
use jsrs_common::ast::*;
use jsrs_common::ast::Exp::*;
use jsrs_common::ast::BinOp::*;
use jsrs_common::ast::Stmt::*;

pub fn eval_string(string: &str, state: &mut ScopeManager) -> JsVar {
    match parse_Stmt(string) {
        Ok(stmt) => {
            let (v, _) = eval_stmt(&stmt, state);
            v
        }
        Err(_) => panic!("parse error"),
    }
}

pub fn eval_stmt(s: &Stmt, mut state: &mut ScopeManager) -> (JsVar, Option<JsVar>) {
    match *s {
        Assign(ref var_string, ref exp) => {
            // TODO: this is a hack to return the value properly, which should be changed once we
            // stop using HashMap to store state.
            let mut var = eval_exp(exp, state);
            let cloned = var.clone();
            var.binding = Binding::new(var_string.clone());
            match state.alloc(var, None) {
                Ok(_) => (),
                e @ Err(_) => println!("{:?}", e),
            }
            (cloned, None)
        },
        BareExp(ref exp) => (eval_exp(exp, &mut state), None),
        Decl(ref var_string, ref exp) => {
            let mut var = eval_exp(exp, state);
            var.binding = Binding::new(var_string.clone());
            // TODO: use value
            match state.alloc(var.clone(), None) {
                Ok(_) => (var, None),
                e @ Err(_) => panic!("{:?}", e),
            }
        },
        If(ref condition, ref if_block, ref else_block) => {
            if eval_exp(&condition, state).as_bool() {
                eval_stmt(&*if_block, state)
            } else {
                if let Some(ref block) = *else_block {
                    eval_stmt(&*block, state)
                } else {
                    (JsVar::new(JsUndef), None)
                }
            }
        },
        Ret(ref e) => {
            let v = eval_exp(&e, &mut state);
            (v.clone(), Some(v))
        }
        Seq(ref s1, ref s2) => {
            let _exp = eval_stmt(&*s1, &mut state);
            eval_stmt(&*s2, &mut state)
        },
        While(ref condition, ref block) => {
            let mut ret_val = None;
            loop {
                if eval_exp(&condition, state).as_bool() {
                    let (_, v) = eval_stmt(&*block, state);
                    ret_val = v;
                } else {
                    return (JsVar::new(JsUndef), ret_val);
                }
            }
        }
    }
}

pub fn eval_exp(e: &Exp, mut state: &mut ScopeManager) -> JsVar {
    match e {
        &BinExp(ref e1, ref op, ref e2) => {
            let val1 = eval_exp(e1, state);
            let val2 = eval_exp(e2, state);

            match *op {
                And => JsVar::new(JsBool(val1.as_bool() && val2.as_bool())),
                Or  => JsVar::new(JsBool(val1.as_bool() || val2.as_bool())),

                Ge  => JsVar::new(JsBool(val1.as_bool() >= val2.as_bool())),
                Gt  => JsVar::new(JsBool(val1.as_bool() >  val2.as_bool())),
                Le  => JsVar::new(JsBool(val1.as_bool() <= val2.as_bool())),
                Lt  => JsVar::new(JsBool(val1.as_bool() <  val2.as_bool())),
                Neq => JsVar::new(JsBool(val1.as_bool() != val2.as_bool())),
                Eql => JsVar::new(JsBool(val1.as_bool() == val2.as_bool())),

                Minus => JsVar::new(JsNum(val1.as_number() - val2.as_number())),
                Plus  => JsVar::new(JsNum(val1.as_number() + val2.as_number())),
                Slash => JsVar::new(JsNum(val1.as_number() / val2.as_number())),
                Star  => JsVar::new(JsNum(val1.as_number() * val2.as_number())),
            }
        }
        &Bool(b) => JsVar::new(JsBool(b)),
        &Call(ref fun_name, ref arg_exps) => {
            let fun_binding = eval_exp(fun_name, state);

            let mut args = Vec::new();

            for exp in arg_exps {
                args.push(eval_exp(exp, state));
            }

            match state.load(&fun_binding.binding) {
                Ok((_, opt_ptr)) => {
                    if let Some(JsPtrEnum::JsFn(js_fn_struct)) = opt_ptr {
                        state.push_scope();

                        for param in js_fn_struct.params.iter() {
                            let mut arg = if args.is_empty() {
                                JsVar::new(JsUndef)
                            } else {
                                args.remove(0)
                            };

                            arg.binding = Binding::new(param.to_owned());
                            state.alloc(arg, None).expect("Unable to store function argument in scope");
                        }

                        let (_, v) = eval_stmt(&js_fn_struct.stmt, state);
                        v.unwrap_or(JsVar::new(JsUndef))
                    } else {
                        panic!(format!("Invalid call object."))
                    }
                },
                _ => panic!("ReferenceError: {} is not defined")
            }
        },
        &Defun(ref opt_binding, ref params, ref body) => {
            if let &Some(ref binding) = opt_binding {
                let js_fun = JsFnStruct::new(opt_binding, params, &**body);
                let mut var = JsVar::new(JsPtr);
                var.binding = Binding::new(binding.clone());
                if let Err(_) = state.alloc(var, Some(JsPtrEnum::JsFn(js_fun))) {
                    panic!("error storing function into state");
                }
                JsVar::new(JsPtr) // Doesn't store any information anyway
            } else {
                panic!("functions without bindings are not yet supported.")
            }
        },
        &Float(f) => JsVar::new(JsType::JsNum(f)),
        &Neg(ref exp) => JsVar::new(JsNum(-eval_exp(exp, state).as_number())),
        &Pos(ref exp) => JsVar::new(JsNum(eval_exp(exp, state).as_number())),

        &PostDec(ref exp) => eval_float_post_op!(exp, f, f - 1.0, state),
        &PostInc(ref exp) => eval_float_post_op!(exp, f, f + 1.0, state),
        &PreDec(ref exp)  => eval_float_pre_op!(exp, f, f - 1.0, state),
        &PreInc(ref exp)  => eval_float_pre_op!(exp, f, f + 1.0, state),

        &NewObject(_, _) => unimplemented!(),
        &Object(_) => unimplemented!(),
        &Undefined => JsVar::new(JsUndef),
        &Var(ref var_binding) => {
            match state.load(&Binding::new(var_binding.clone())) {
                Ok((var, _)) => var,
                _ => panic!(format!("ReferenceError: {} is not defined", var_binding))
            }
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use std::collections::hash_set::HashSet;
    use french_press::init_gc;
    use js_types::js_var::JsType;
    use js_types::binding::Binding;

    #[test]
    fn test_eval_literals() {
        let mut state = init_gc();
        assert_eq!(JsType::JsNum(5.0f64), eval_string("5.0;\n", &mut state).t);
        assert_eq!(JsType::JsNum(0.0f64), eval_string("0.0;\n", &mut state).t);
        assert_eq!(JsType::JsUndef, eval_string("undefined;\n", &mut state).t);
    }

    //// TODO: handle `var` and no `var` separately
    //#[test]
    //fn test_store_state() {
    //    let mut state = HashMap::new();
    //    assert_eq!(JsUndefined, eval_string("var a = 1;\n", &mut state));
    //    assert_eq!(JsNumber(2.0f64), eval_string("a = 2;\n", &mut state));
    //    assert_eq!(JsUndefined, eval_string("var b = 3;\n", &mut state));
    //    assert_eq!(JsNumber(4.0f64), eval_string("c = 4;\n", &mut state));
    //}

    #[test]
    fn test_inc_dec() {
        let mut state = init_gc();
        //assert_eq!(JsType::JsNum(1.0f64), eval_string("var a = 1;\n", &mut state).t);
        //assert_eq!(&JsType::JsNum(1.0), state.load(&Binding::new("a")).unwrap());

        //assert_eq!(JsType::JsNum(1.0f64), eval_string("a++;\n", &mut state));
        //assert_eq!(&JsNumber(2.0f64), state.get("a").unwrap());

        //assert_eq!(JsNumber(3.0f64), eval_string("++a;\n", &mut state));
        //assert_eq!(&JsNumber(3.0f64), state.get("a").unwrap());

        //assert_eq!(JsNumber(3.0f64), eval_string("a--;\n", &mut state));
        //assert_eq!(&JsNumber(2.0f64), state.get("a").unwrap());

        //assert_eq!(JsNumber(1.0f64), eval_string("--a;\n", &mut state));
        //assert_eq!(&JsNumber(1.0f64), state.get("a").unwrap());
    }

    #[test]
    fn test_binexp() {
        let mut state = init_gc();
        assert_eq!(JsType::JsNum(6.0f64),  eval_string("2.0 + 4.0;\n", &mut state).t);
        assert_eq!(JsType::JsNum(0.5f64),  eval_string("2.0 / 4.0;\n", &mut state).t);
        assert_eq!(JsType::JsNum(-2.0f64), eval_string("2.0 - 4.0;\n", &mut state).t);
        assert_eq!(JsType::JsNum(8.0f64),  eval_string("2.0 * 4.0;\n", &mut state).t);
    }
}
<|endoftext|>


<commit_before> #![feature(buf_stream)]

#[macro_use]
extern crate log;
extern crate url;
extern crate capnp;

pub mod yak_capnp;

use std::net::TcpStream;
use std::io::{self,BufStream,Write};
use capnp::serialize_packed;
use capnp::{MessageBuilder, MessageReader, MallocMessageBuilder, ReaderOptions};

use url::{Url,SchemeType,UrlParser};

use yak_capnp::{client_request,client_response};

fn yak_url_scheme(_scheme: &str) -> SchemeType {
  SchemeType::Relative(0)
}

pub struct Client {
  connection: BufStream<TcpStream>
}

#[derive(Debug)]
pub enum YakError {
  UrlParseError(url::ParseError),
  InvalidUrl(Url),
  IoError(io::Error),
  CapnpError(capnp::Error),
  CapnpNotInSchema(capnp::NotInSchema),
  ProtocolError
}

#[derive(PartialEq,Eq,PartialOrd,Ord,Debug)]
pub struct Datum {
  pub content: Vec<u8>
}

impl Client {
  pub fn connect(loc: &str) -> Result<Client, YakError> {
    let mut p = UrlParser::new();
    p.scheme_type_mapper(yak_url_scheme);
    let url = try!(p.parse(loc));
    debug!("yak:url: {:?}", url);
    let addr = match (url.domain(), url.port()) {
      (Some(host), Some(port)) => (host, port),
      _ => return Err(YakError::InvalidUrl(url.clone()))
    };

    let sock = try!(TcpStream::connect(addr));
    debug!("connected:{:?}", sock);
    Ok(Client { connection: BufStream::new(sock) })
  }

  pub fn truncate(&mut self) -> Result<(), YakError> {
    let mut message = MallocMessageBuilder::new_default();
    {
      let mut rec = message.init_root::<client_request::Builder>();
      rec.set_truncate(())
    }
    serialize_packed::write_message(&mut self.connection, &mut message).unwrap();
    try!(self.connection.flush());
    debug!("Waiting for response");

    let message_reader = try!(serialize_packed::read_message(&mut self.connection, ReaderOptions::new()));
    let msg = try!(message_reader.get_root::<client_response::Reader>());
    debug!("Got response");

    Ok(())
  }

  pub fn write(&mut self, key: &[u8], val: &[u8]) -> Result<(), YakError> {
    let mut message = MallocMessageBuilder::new_default();
    {
      let mut rec = message.init_root::<client_request::Builder>();
      let mut req = rec.init_write();
      req.set_key(key);
      req.set_value(val);
    }
    serialize_packed::write_message(&mut self.connection, &mut message).unwrap();
    try!(self.connection.flush());
    debug!("Waiting for response");

    let message_reader = try!(serialize_packed::read_message(&mut self.connection, ReaderOptions::new()));
    let msg = try!(message_reader.get_root::<client_response::Reader>());
    debug!("Got response");

    Ok(())
  }

  pub fn read(&mut self, key: &[u8]) -> Result<Vec<Datum>, YakError> {
    let mut message = MallocMessageBuilder::new_default();
    {
      let mut rec = message.init_root::<client_request::Builder>();
      let mut req = rec.init_read();
      req.set_key(key)
    }
    serialize_packed::write_message(&mut self.connection, &mut message).unwrap();
    try!(self.connection.flush());
    debug!("Waiting for response");

    let message_reader = try!(serialize_packed::read_message(&mut self.connection, ReaderOptions::new()));
    let msg = try!(message_reader.get_root::<client_response::Reader>());
    match try!(msg.which()) {
      client_response::OkData(d) => {
        debug!("Got response Data: ");
        let mut data = Vec::with_capacity(try!(d).len() as usize);
        for it in try!(d).iter() {
          let val : Vec<u8> = try!(it.get_value()).iter().map(|v|v.clone()).collect();
          data.push(Datum { content: val });
        }

        Ok(data)
      },
      other => Err(YakError::ProtocolError)
    }

  }
}

impl From<url::ParseError> for YakError {
  fn from(err: url::ParseError) -> YakError {
    YakError::UrlParseError(err)
  }
}

impl From<io::Error> for YakError {
  fn from(err: io::Error) -> YakError {
    YakError::IoError(err)
  }
}

impl From<capnp::Error> for YakError {
  fn from(err: capnp::Error) -> YakError {
    YakError::CapnpError(err)
  }
}

impl From<capnp::NotInSchema> for YakError {
  fn from(err: capnp::NotInSchema) -> YakError {
    YakError::CapnpNotInSchema(err)
  }
}
<commit_msg>Extract encode functions for client messages.<commit_after> #![feature(buf_stream)]

#[macro_use]
extern crate log;
extern crate url;
extern crate capnp;

pub mod yak_capnp;

use std::net::TcpStream;
use std::io::{self,BufStream,Write};
use capnp::serialize_packed;
use capnp::{MessageBuilder, MessageReader, MallocMessageBuilder, ReaderOptions};

use url::{Url,SchemeType,UrlParser};

use yak_capnp::{client_request,client_response};

fn yak_url_scheme(_scheme: &str) -> SchemeType {
  SchemeType::Relative(0)
}

pub struct Client {
  connection: BufStream<TcpStream>
}

#[derive(Debug)]
pub enum YakError {
  UrlParseError(url::ParseError),
  InvalidUrl(Url),
  IoError(io::Error),
  CapnpError(capnp::Error),
  CapnpNotInSchema(capnp::NotInSchema),
  ProtocolError
}

#[derive(PartialEq,Eq,PartialOrd,Ord,Debug)]
pub struct Datum {
  pub content: Vec<u8>
}

impl Client {
  pub fn connect(loc: &str) -> Result<Client, YakError> {
    let mut p = UrlParser::new();
    p.scheme_type_mapper(yak_url_scheme);
    let url = try!(p.parse(loc));
    debug!("yak:url: {:?}", url);
    let addr = match (url.domain(), url.port()) {
      (Some(host), Some(port)) => (host, port),
      _ => return Err(YakError::InvalidUrl(url.clone()))
    };

    let sock = try!(TcpStream::connect(addr));
    debug!("connected:{:?}", sock);
    Ok(Client { connection: BufStream::new(sock) })
  }

  fn encode_truncate<B: MessageBuilder>(message: &mut B) {
    let mut rec = message.init_root::<client_request::Builder>();
    rec.set_truncate(())
  }

  pub fn truncate(&mut self) -> Result<(), YakError> {
    let mut message = MallocMessageBuilder::new_default();
    Self::encode_truncate(&mut message);
    try!(serialize_packed::write_message(&mut self.connection, &mut message));
    try!(self.connection.flush());
    debug!("Waiting for response");

    let message_reader = try!(serialize_packed::read_message(&mut self.connection, ReaderOptions::new()));
    let msg = try!(message_reader.get_root::<client_response::Reader>());
    debug!("Got response");

    Ok(())
  }

  fn encode_write<B: MessageBuilder>(message: &mut B, key: &[u8], val: &[u8]) {
    let mut rec = message.init_root::<client_request::Builder>();
    let mut req = rec.init_write();
    req.set_key(key);
    req.set_value(val);
  }

  pub fn write(&mut self, key: &[u8], val: &[u8]) -> Result<(), YakError> {
    let mut message = MallocMessageBuilder::new_default();
    Self::encode_write(&mut message, key, val);
    try!(serialize_packed::write_message(&mut self.connection, &mut message));
    try!(self.connection.flush());
    debug!("Waiting for response");

    let message_reader = try!(serialize_packed::read_message(&mut self.connection, ReaderOptions::new()));
    let msg = try!(message_reader.get_root::<client_response::Reader>());
    debug!("Got response");

    Ok(())
  }

  fn encode_read<B: MessageBuilder>(message: &mut B, key: &[u8]) {
      let mut rec = message.init_root::<client_request::Builder>();
      let mut req = rec.init_read();
      req.set_key(key)
  }

  pub fn read(&mut self, key: &[u8]) -> Result<Vec<Datum>, YakError> {
    let mut message = MallocMessageBuilder::new_default();
    Self::encode_read(&mut message, key);
    try!(serialize_packed::write_message(&mut self.connection, &mut message));
    try!(self.connection.flush());
    debug!("Waiting for response");

    let message_reader = try!(serialize_packed::read_message(&mut self.connection, ReaderOptions::new()));
    let msg = try!(message_reader.get_root::<client_response::Reader>());
    match try!(msg.which()) {
      client_response::OkData(d) => {
        debug!("Got response Data: ");
        let mut data = Vec::with_capacity(try!(d).len() as usize);
        for it in try!(d).iter() {
          let val : Vec<u8> = try!(it.get_value()).iter().map(|v|v.clone()).collect();
          data.push(Datum { content: val });
        }

        Ok(data)
      },
      other => Err(YakError::ProtocolError)
    }

  }
}

impl From<url::ParseError> for YakError {
  fn from(err: url::ParseError) -> YakError {
    YakError::UrlParseError(err)
  }
}

impl From<io::Error> for YakError {
  fn from(err: io::Error) -> YakError {
    YakError::IoError(err)
  }
}

impl From<capnp::Error> for YakError {
  fn from(err: capnp::Error) -> YakError {
    YakError::CapnpError(err)
  }
}

impl From<capnp::NotInSchema> for YakError {
  fn from(err: capnp::NotInSchema) -> YakError {
    YakError::CapnpNotInSchema(err)
  }
}
<|endoftext|>


<commit_before>#![doc(html_root_url="https://drakulix.github.io/zwreec/",
    html_logo_url="https://dl.dropboxusercontent.com/u/70410095/zwreec/logo.png")]
//! Twee-to-Zcode compile library.
//!
//! Zwreec is a compiler for [interactive fiction](http://en.wikipedia.org/wiki/Interactive_fiction)
//! in the Twee format (created by the [Twine software](http://en.wikipedia.org/wiki/Twine_(software)))
//! to [Z-Machine](http://en.wikipedia.org/wiki/Z-machine) instructions (Zcode)
//! which can be run with interpreters like [frotz](http://frotz.sourceforge.net).
//!
//! This library was developed as part of the course "Softwareprojekt
//! Übersetzerbau" in 2015.
//!
//! # Requirements and Usage
//!
//! The library uses a [fork of rustlex](https://github.com/Farthen/rustlex) to
//! to do lexical analysis. Since rustlex requires the Rust Nightly Builds, so
//! does zwreec. More precisely, this library was *only* developed and tested
//! against Rust 1.2.0-nightly, build 2015-06-01. You can install this specific
//! version using:
//!
//! ```sh
//! $ curl -s https://static.rust-lang.org/rustup.sh | sh -s -- --channel=nightly --date=2015-06-02
//! ```
//!
//! To use zwreec in your project you can add it as a dependency to your `Cargo.toml`.
//!
//! ```toml
//! [dependencies.zwreec]
//! git = "https:://github.com/Drakulix/zwreec"
//! ```
//!
//! Then you can use it in your crate root.
//!
//! ```rust
//! extern crate zwreec;
//! ```
//!
//! # Features
//!
//! Only rudimentary Twee features are supported right now. This is about to change in the upcoming weeks.
//! Check the github issues for more information on the currently supported features.
//!
//! # Example
//!
//! The following example is itself a full working command line compiler. It is a simpler
//! version of the [Reference Binary Implementation](#reference-binary-implementation).
//!
//! ```no_run
//! extern crate zwreec;
//!
//! use std::env;
//! use std::error::Error;
//! use std::fs::File;
//! use std::path::Path;
//!
//! fn main() {
//!     let mut args: Vec<String> = env::args().collect();
//!
//!     if args.len() != 2 { panic!("Need exactly one input file!"); }
//!
//!     let cfg = zwreec::config::Config::default_config();
//!     let mut input = match File::open(Path::new(&args[1])) {
//!         Ok(file) => file,
//!         Err(why) => { panic!("Couldn't open input: {}", Error::description(&why)); }
//!     };
//!     let mut output = match File::create(Path::new("a.z8")) {
//!         Ok(file) => file,
//!         Err(why) => { panic!("Couldn't open output: {}", Error::description(&why)); }
//!     };
//!
//!     zwreec::compile(cfg, &mut input, &mut output);
//! }
//! ```
//!
//! # Reference Binary Implementation
//!
//! Zwreecs [Github-Repository](https://github.com/Drakulix/zwreec) contains a
//! reference binary implementation that uses this library and provides a simple
//! command line interface to compile Twee files.
//!
//! To build the binary, you need the rust version as outlined in [Requirements and
//! Usage](#requirements-and-usage). Then you can build the binary using
//!
//! ```sh
//! $ cargo build
//! ```
//!
//! The resulting binary can be found at `target/debug/zwreec`.
//!
//! # Logging
//!
//! Zwreec uses the logging facade provided by
//! [log](../log/index.html). The reference binary implementation of zwreec also includes an
//! implementation of the `Log` trait.

extern crate rustlex_codegen as rustlex;
#[macro_use] extern crate log;
extern crate time;
extern crate term;
extern crate getopts;

#[macro_use] pub mod utils;
pub mod config;
pub mod frontend;
pub mod backend;

use config::{Config,TestCase};
use std::io::{Read,Write};

/// Compiles a Twee Input to Zcode
///
/// This is the main call into the Zwreec compiler. It will process `input: Read`
/// by calling the different parts of the compiler-chain, notably `frontend::lexer`
/// to generate a tokenstream, `frontend::parser` and `frontend::ast` to generate
/// the Abstract Syntax Tree and lastly `frontend::codegen` to generate the Zcode.
///
/// # Example
///
/// ```no_run
/// # use std::env;
/// # use std::fs::File;
/// # use std::path::Path;
/// # let mut args: Vec<String> = env::args().collect();
/// #
/// # if args.len() != 2 { panic!("Need exactly one input file!"); }
/// #
/// let cfg = zwreec::config::Config::default_config();
/// let mut input = File::open(Path::new(&args[1])).unwrap();
/// let mut output = File::create(Path::new("a.z8")).unwrap();
///
/// zwreec::compile(cfg, &mut input, &mut output);
/// ```
#[allow(unused_variables)]
pub fn compile<R: Read, W: Write>(cfg: Config, input: &mut R, output: &mut W) {
    // tokenize
    let tokens = frontend::lexer::lex(&cfg, input);

    //create parser
    let parser = frontend::parser::Parser::new(&cfg);

    //build up ast from tokens
    let ast = frontend::ast::AST::build(parser.parse(tokens.inspect(|ref token| {
        debug!("{:?}", token);
    })));
    ast.print(false);

    // create code
    frontend::codegen::generate_zcode(&cfg, ast, output);
}

/// Run internal library tests.
///
/// This function is used to circumvent certain parts of the compiler toolchain.
/// It currently only processes `TestCase::ZcodeBackend` which creates a Zcode
/// file using all available OP-Codes.
///
/// **Warning:** This function should be considered unstable and might be removed
/// in later versions.
///
/// # Example
///
/// ```
/// # use std::env;
/// # use std::fs::File;
/// # use std::path::Path;
/// let mut cfg = zwreec::config::Config::default_config();
/// cfg.test_cases.push(zwreec::config::TestCase::ZcodeBackend);
///
/// let mut input: Option<File> = None;
/// let mut output = Some(File::create(Path::new("a.z8")).unwrap());
///
/// zwreec::test_library(cfg, &mut input, &mut output);
/// ```
#[allow(unused_variables)]
pub fn test_library<R: Read, W: Write>(cfg: Config, input: &mut Option<R>, output: &mut Option<W>) {
    for case in cfg.test_cases {
        match case {
            TestCase::ZcodeBackend => {
                match output.as_mut() {
                     Some(o) => backend::zcode::temp_create_zcode_example(o),
                     None => error!("TestCase::ZcodeBackend requires output!"),
                }
            }
        }
    }
}
<commit_msg>update copy of readme<commit_after>#![doc(html_root_url="https://drakulix.github.io/zwreec/",
    html_logo_url="https://dl.dropboxusercontent.com/u/70410095/zwreec/logo.png")]
//! Twee-to-Zcode compile library.
//!
//! Zwreec is a compiler for [interactive fiction](http://en.wikipedia.org/wiki/Interactive_fiction)
//! in the Twee format (created by the [Twine software](http://en.wikipedia.org/wiki/Twine_(software)))
//! to [Z-Machine](http://en.wikipedia.org/wiki/Z-machine) instructions (Zcode)
//! which can be run with interpreters like [frotz](http://frotz.sourceforge.net).
//!
//! This library was developed as part of the course "Softwareprojekt
//! Übersetzerbau" in 2015.
//!
//! # Requirements and Usage
//!
//! The library uses a [fork of rustlex](https://github.com/Drakulix/rustlex)
//! to do lexical analysis.
//! You can install Rust stable or nightly as you like via the
//! [provided binaries](http://www.rust-lang.org/install.html)
//! or the rustup script:
//!
//! ```sh
//! $ curl -s https://static.rust-lang.org/rustup.sh | sh -s -- --channel=nightly --date=2015-06-02
//! ```
//!
//! Or install the Ubuntu/Debian Packages for
//! [Rust](http://ppa.launchpad.net/hansjorg/rust/ubuntu/pool/main/r/rust-nightly/) and
//! [Cargo](http://ppa.launchpad.net/hansjorg/rust/ubuntu/pool/main/c/cargo-nightly/).
//!
//! To use zwreec in your project you can add it as a dependency to your `Cargo.toml`.
//!
//! ```toml
//! [dependencies.zwreec]
//! git = "https:://github.com/Drakulix/zwreec"
//! ```
//!
//! Then you can use it in your crate root.
//!
//! ```rust
//! extern crate zwreec;
//! ```
//!
//! # Features
//!
//! Only rudimentary Twee features are supported right now. This is about to change in the upcoming weeks.
//! Check the github issues for more information on the currently supported features.
//!
//! # Example
//!
//! The following example is itself a full working command line compiler. It is a simpler
//! version of the [Reference Binary Implementation](#reference-binary-implementation).
//!
//! ```no_run
//! extern crate zwreec;
//!
//! use std::env;
//! use std::error::Error;
//! use std::fs::File;
//! use std::path::Path;
//!
//! fn main() {
//!     let mut args: Vec<String> = env::args().collect();
//!
//!     if args.len() != 2 { panic!("Need exactly one input file!"); }
//!
//!     let cfg = zwreec::config::Config::default_config();
//!     let mut input = match File::open(Path::new(&args[1])) {
//!         Ok(file) => file,
//!         Err(why) => { panic!("Couldn't open input: {}", Error::description(&why)); }
//!     };
//!     let mut output = match File::create(Path::new("a.z8")) {
//!         Ok(file) => file,
//!         Err(why) => { panic!("Couldn't open output: {}", Error::description(&why)); }
//!     };
//!
//!     zwreec::compile(cfg, &mut input, &mut output);
//! }
//! ```
//!
//! # Reference Binary Implementation
//!
//! Zwreecs [Github-Repository](https://github.com/Drakulix/zwreec) contains a
//! reference binary implementation that uses this library and provides a simple
//! command line interface to compile Twee files.
//!
//! To build the binary, you need the rust version as outlined in [Requirements and
//! Usage](#requirements-and-usage). Then you can build the binary using
//!
//! ```sh
//! $ cargo build
//! ```
//!
//! The resulting binary can be found at `target/debug/zwreec`.
//!
//! # Logging
//!
//! Zwreec uses the logging facade provided by
//! [log](../log/index.html). The reference binary implementation of zwreec also includes an
//! implementation of the `Log` trait.

extern crate rustlex_codegen as rustlex;
#[macro_use] extern crate log;
extern crate time;
extern crate term;
extern crate getopts;

#[macro_use] pub mod utils;
pub mod config;
pub mod frontend;
pub mod backend;

use config::{Config,TestCase};
use std::io::{Read,Write};

/// Compiles a Twee Input to Zcode
///
/// This is the main call into the Zwreec compiler. It will process `input: Read`
/// by calling the different parts of the compiler-chain, notably `frontend::lexer`
/// to generate a tokenstream, `frontend::parser` and `frontend::ast` to generate
/// the Abstract Syntax Tree and lastly `frontend::codegen` to generate the Zcode.
///
/// # Example
///
/// ```no_run
/// # use std::env;
/// # use std::fs::File;
/// # use std::path::Path;
/// # let mut args: Vec<String> = env::args().collect();
/// #
/// # if args.len() != 2 { panic!("Need exactly one input file!"); }
/// #
/// let cfg = zwreec::config::Config::default_config();
/// let mut input = File::open(Path::new(&args[1])).unwrap();
/// let mut output = File::create(Path::new("a.z8")).unwrap();
///
/// zwreec::compile(cfg, &mut input, &mut output);
/// ```
#[allow(unused_variables)]
pub fn compile<R: Read, W: Write>(cfg: Config, input: &mut R, output: &mut W) {
    // tokenize
    let tokens = frontend::lexer::lex(&cfg, input);

    //create parser
    let parser = frontend::parser::Parser::new(&cfg);

    //build up ast from tokens
    let ast = frontend::ast::AST::build(parser.parse(tokens.inspect(|ref token| {
        debug!("{:?}", token);
    })));
    ast.print(false);

    // create code
    frontend::codegen::generate_zcode(&cfg, ast, output);
}

/// Run internal library tests.
///
/// This function is used to circumvent certain parts of the compiler toolchain.
/// It currently only processes `TestCase::ZcodeBackend` which creates a Zcode
/// file using all available OP-Codes.
///
/// **Warning:** This function should be considered unstable and might be removed
/// in later versions.
///
/// # Example
///
/// ```
/// # use std::env;
/// # use std::fs::File;
/// # use std::path::Path;
/// let mut cfg = zwreec::config::Config::default_config();
/// cfg.test_cases.push(zwreec::config::TestCase::ZcodeBackend);
///
/// let mut input: Option<File> = None;
/// let mut output = Some(File::create(Path::new("a.z8")).unwrap());
///
/// zwreec::test_library(cfg, &mut input, &mut output);
/// ```
#[allow(unused_variables)]
pub fn test_library<R: Read, W: Write>(cfg: Config, input: &mut Option<R>, output: &mut Option<W>) {
    for case in cfg.test_cases {
        match case {
            TestCase::ZcodeBackend => {
                match output.as_mut() {
                     Some(o) => backend::zcode::temp_create_zcode_example(o),
                     None => error!("TestCase::ZcodeBackend requires output!"),
                }
            }
        }
    }
}
<|endoftext|>


<commit_before><commit_msg>Make the `dw!` macro also provide a `Display` implementation<commit_after><|endoftext|>


<commit_before>use std::io::prelude::*;
use std::fs::File;
use std::cell::RefCell;
use std::collections::{HashSet, HashMap};

use syntax::ast::*;
use syntax::print;
use syntax::ptr::P;
use syntax::ext;
use syntax::ext::base::ExtCtxt;
use syntax::ext::build::AstBuilder;
use syntax::parse::token::{str_to_ident, Token, IdentStyle};
use syntax::codemap::{ExpnId, ExpnInfo, CompilerExpansion, NameAndSpan, DUMMY_SP};

use rustc::session::{build_session,Session};
use rustc::session::config::{build_session_options,build_configuration, Input};
use rustc_driver::{handle_options,diagnostics_registry};
use rustc_driver::driver::{phase_1_parse_input, source_name};

use super::state::State;

const CRATE_SRC : &'static str =  "//Generated by hsm-gen, modifications will be lost when regenerating
                                   use super::shared_data::SharedData;
                                   use super::hsm_functions;";

pub struct HsmGenerator {
    sess        : Session,
    cfg         : CrateConfig,
    src_name    : String,
    krate       : RefCell<Crate>
}
impl HsmGenerator {
    pub fn new(prefix: bool) -> Self {
        let matches = match handle_options(vec!["".to_string(), "-".to_string()]) {
            Some(matches) => matches,
            None => unreachable!()
        };
        let descriptions = diagnostics_registry();
        let sopts = build_session_options(&matches);
        let sess = build_session(sopts, None, descriptions);
        let cfg = build_configuration(&sess);
        let input = Input::Str( if prefix {CRATE_SRC.to_string()} else {"".to_string()});
        let src_name = source_name(&input);
        let krate = RefCell::new(phase_1_parse_input(&sess, cfg.clone(), &input));

        HsmGenerator {
            sess    : sess,
            cfg     : cfg,
            src_name: src_name,
            krate   : krate
        }
    }

    fn ecfg(&self) -> ext::expand::ExpansionConfig {
        ext::expand::ExpansionConfig {
            crate_name      : self.src_name.clone(),
            features        : None, //Some(&self.sess.features.borrow()),
            recursion_limit : self.sess.recursion_limit.get(),
            trace_mac       : self.sess.opts.debugging_opts.trace_macros,
        }
    }
    fn extctxt(&self) -> ExtCtxt {
        let mut cx = ExtCtxt::new(&self.sess.parse_sess, self.cfg.clone(), self.ecfg());
        cx.backtrace = ExpnId::from_u32(0);
        cx.codemap().record_expansion(
            ExpnInfo {
                call_site: self.krate.borrow().span,
                callee: NameAndSpan {
                    name: "mattis".to_string(),
                    format: CompilerExpansion,
                    allow_internal_unstable: true,
                    span: None,
                },
            }
        );
        cx
    }

    pub fn print(&self, file: &str) {
        let src_nam = self.src_name.clone();
        let src = self.sess.codemap().get_filemap(&src_nam[..])
                            .src
                            .as_ref()
                            .unwrap()
                            .as_bytes()
                            .to_vec();
        let mut rdr = &src[..];
        let mut out = Vec::new();
        let ann = print::pprust::NoAnn;
        {
            let out_w: &mut Write = &mut out;
            print::pprust::print_crate( self.sess.codemap(),
                                        self.sess.diagnostic(),
                                        &self.krate.borrow(),
                                        src_nam,
                                        &mut rdr,
                                        box out_w,
                                        &ann,
                                        false).expect("Could not format crate");
        }
        // print!("{}", String::from_utf8(out).unwrap());
        let mut f = File::create(file).expect("Could not create file");
        f.write(&out).expect("Could not write to file");
    }

    pub fn create_event_enum(&self, hs: &HashSet<String>) {
        let cx = self.extctxt();
        let mut variants: Vec<P<Variant>> = Vec::new();
        for var_name in hs {
            let mut variant = cx.variant(
                DUMMY_SP,
                str_to_ident(var_name),
                Vec::new()
            );
            variant.node.vis = Visibility::Inherited;
            variants.push(P(variant));
        }
        let en = cx.item_enum(
            DUMMY_SP,
            str_to_ident("Events"),
            EnumDef{ variants: variants }
        ).map(|mut x| {
            x.vis = Visibility::Public;
            x.attrs.push(quote_attr!(&cx, #[derive(Debug, Clone)]));
            x
        });
        self.krate.borrow_mut().module.items.push(en);
    }

    pub fn create_hsm_objects(&self, states: &HashSet<String>) {
        let cx = self.extctxt();
        let events = str_to_ident("Events");
        let st_str = str_to_ident("StateStruct");
        let st     = str_to_ident("States");
        let shr_dat= str_to_ident("SharedData");
        let states: Vec<TokenTree> = states
            .iter()
            .map(|st_nam| vec![Token::Ident(str_to_ident(st_nam), IdentStyle::Plain)])
            .collect::<Vec<_>>()
            .join(&Token::Comma)
            .into_iter()
            .map(|t| TtToken(DUMMY_SP, t))
            .collect();

        let x = quote_item!(&cx,
            hsm_define_objects!($st_str, $st, $events, $shr_dat, (
                $states
            ));
        ).unwrap();
        self.krate.borrow_mut().module.items.push(x);
    }

    pub fn create_state_impls(&self, hm: &HashMap<String, State>) {
        let events = str_to_ident("Events");
        let states = str_to_ident("States");
        let shr_dat= str_to_ident("SharedData");
        for state in hm.values() {
            self.create_state_impl(state, &events, &states, &shr_dat);
        }
    }

    fn create_enter_exit_arm(&self, cx: &ExtCtxt, opt_func: &Option<String>, evt_type: &str) -> Option<Arm> {
        if let &Some(ref func_nam) = opt_func {
            let func_ident = str_to_ident(func_nam);
            let evt_ident = str_to_ident(evt_type);
            Some(cx.arm(DUMMY_SP,
                   vec!(quote_pat!(&cx, hsm::Event::$evt_ident)),
                   quote_expr!(&cx, {
                        hsm_functions::$func_ident(shr_data, evt);
                        hsm::Action::Ignore
                   })
            ))
        } else { None }
    }

    fn create_state_impl(&self, state: &State, events: &Ident, states: &Ident, shr_dat: &Ident) {
        let cx = self.extctxt();
        let state_ident = str_to_ident(state.name.as_ref().unwrap());
        let mut arms: Vec<Arm> = Vec::new();
        self.create_enter_exit_arm(&cx, &state.entry, "Enter").map(|x| arms.push(x));
        self.create_enter_exit_arm(&cx, &state.exit, "Exit").map(|x| arms.push(x));
        for (trigger, target) in state.transitions.iter() {
            let target_ident = str_to_ident(target);
            let trigger_ident = str_to_ident(trigger);
            let pat = if trigger == "_" {
                quote_pat!(&cx, $trigger_ident)
            } else {
                quote_pat!(&cx, hsm::Event($events::$trigger_ident))
            };
            let trans = quote_expr!(&cx, hsm::Action::Transition($states::$target_ident));
            arms.push(cx.arm(DUMMY_SP,
                             vec!(pat),
                             quote_expr!(&cx, hsm_delayed_transition!(probe, { $trans }))
            ));
        };
        if !state.transitions.contains_key("_") {
            arms.push(cx.arm(DUMMY_SP,
                             vec!(quote_pat!(&cx, _)),
                             quote_expr!(&cx, hsm::Action::Parent)
            ));
        };
        let match_expr = cx.expr_match(DUMMY_SP, quote_expr!(&cx, evt), arms);
        let x = quote_item!(&cx,
            impl hsm::State<$events, $states, $shr_dat> for $state_ident<$events, $states, $shr_dat> {
                fn handle_event(&mut self, shr_data: &mut $shr_dat, evt: hsm::Event<$events>, probe: bool) -> hsm::Action<$states> {
                    $match_expr
                }
            }
        ).unwrap();
        self.krate.borrow_mut().module.items.push(x);
    }

    pub fn create_function_stubs(&self, hm: &HashMap<String, State>) {
        let cx = self.extctxt();
        let events = str_to_ident("Events");
        let shr_dat= str_to_ident("SharedData");
        let mut functions = HashSet::new();
        hm.values().map(|s|
            s.entry.as_ref().map(|x|
                functions.insert(x.to_string())
            )
        ).count();
        for func in functions {
            let func_ident  = str_to_ident(&func);
            let x = quote_item!(&cx,
                fn $func_ident(shr_data: &mut $shr_dat, evt: hsm::Event<$events>) {
                    unimplemented!();
                }
            ).unwrap();
            self.krate.borrow_mut().module.items.push(x);
            // println!("{}", func);
        }
    }
}
<commit_msg>bugfixes<commit_after>use std::io::prelude::*;
use std::fs::File;
use std::cell::RefCell;
use std::collections::{HashSet, HashMap};

use syntax::ast::*;
use syntax::print;
use syntax::ptr::P;
use syntax::ext;
use syntax::ext::base::ExtCtxt;
use syntax::ext::build::AstBuilder;
use syntax::parse::token::{str_to_ident, Token, IdentStyle};
use syntax::codemap::{ExpnId, ExpnInfo, CompilerExpansion, NameAndSpan, DUMMY_SP};

use rustc::session::{build_session,Session};
use rustc::session::config::{build_session_options,build_configuration, Input};
use rustc_driver::{handle_options,diagnostics_registry};
use rustc_driver::driver::{phase_1_parse_input, source_name};

use super::state::State;

const CRATE_SRC : &'static str =  "//Generated by hsm-gen, modifications will be lost when regenerating
                                   use hsm;
                                   use super::shared_data::SharedData;
                                   use super::hsm_functions;";

pub struct HsmGenerator {
    sess        : Session,
    cfg         : CrateConfig,
    src_name    : String,
    krate       : RefCell<Crate>
}
impl HsmGenerator {
    pub fn new(prefix: bool) -> Self {
        let matches = match handle_options(vec!["".to_string(), "-".to_string()]) {
            Some(matches) => matches,
            None => unreachable!()
        };
        let descriptions = diagnostics_registry();
        let sopts = build_session_options(&matches);
        let sess = build_session(sopts, None, descriptions);
        let cfg = build_configuration(&sess);
        let input = Input::Str( if prefix {CRATE_SRC.to_string()} else {"".to_string()});
        let src_name = source_name(&input);
        let krate = RefCell::new(phase_1_parse_input(&sess, cfg.clone(), &input));

        HsmGenerator {
            sess    : sess,
            cfg     : cfg,
            src_name: src_name,
            krate   : krate
        }
    }

    fn ecfg(&self) -> ext::expand::ExpansionConfig {
        ext::expand::ExpansionConfig {
            crate_name      : self.src_name.clone(),
            features        : None, //Some(&self.sess.features.borrow()),
            recursion_limit : self.sess.recursion_limit.get(),
            trace_mac       : self.sess.opts.debugging_opts.trace_macros,
        }
    }
    fn extctxt(&self) -> ExtCtxt {
        let mut cx = ExtCtxt::new(&self.sess.parse_sess, self.cfg.clone(), self.ecfg());
        cx.backtrace = ExpnId::from_u32(0);
        cx.codemap().record_expansion(
            ExpnInfo {
                call_site: self.krate.borrow().span,
                callee: NameAndSpan {
                    name: "mattis".to_string(),
                    format: CompilerExpansion,
                    allow_internal_unstable: true,
                    span: None,
                },
            }
        );
        cx
    }

    pub fn print(&self, file: &str) {
        let src_nam = self.src_name.clone();
        let src = self.sess.codemap().get_filemap(&src_nam[..])
                            .src
                            .as_ref()
                            .unwrap()
                            .as_bytes()
                            .to_vec();
        let mut rdr = &src[..];
        let mut out = Vec::new();
        let ann = print::pprust::NoAnn;
        {
            let out_w: &mut Write = &mut out;
            print::pprust::print_crate( self.sess.codemap(),
                                        self.sess.diagnostic(),
                                        &self.krate.borrow(),
                                        src_nam,
                                        &mut rdr,
                                        box out_w,
                                        &ann,
                                        false).expect("Could not format crate");
        }
        // print!("{}", String::from_utf8(out).unwrap());
        let mut f = File::create(file).expect("Could not create file");
        f.write(&out).expect("Could not write to file");
    }

    pub fn create_event_enum(&self, hs: &HashSet<String>) {
        let cx = self.extctxt();
        let mut variants: Vec<P<Variant>> = Vec::new();
        for var_name in hs {
            let mut variant = cx.variant(
                DUMMY_SP,
                str_to_ident(var_name),
                Vec::new()
            );
            variant.node.vis = Visibility::Inherited;
            variants.push(P(variant));
        }
        let en = cx.item_enum(
            DUMMY_SP,
            str_to_ident("Events"),
            EnumDef{ variants: variants }
        ).map(|mut x| {
            x.vis = Visibility::Public;
            x.attrs.push(quote_attr!(&cx, #[derive(Debug, Clone)]));
            x
        });
        self.krate.borrow_mut().module.items.push(en);
    }

    pub fn create_hsm_objects(&self, states: &HashSet<String>) {
        let cx = self.extctxt();
        let events = str_to_ident("Events");
        let st_str = str_to_ident("StateStruct");
        let st     = str_to_ident("States");
        let shr_dat= str_to_ident("SharedData");
        let states: Vec<TokenTree> = states
            .iter()
            .map(|st_nam| vec![Token::Ident(str_to_ident(st_nam), IdentStyle::Plain)])
            .collect::<Vec<_>>()
            .join(&Token::Comma)
            .into_iter()
            .map(|t| TtToken(DUMMY_SP, t))
            .collect();

        let x = quote_item!(&cx,
            hsm_define_objects!($st_str, $st, $events, $shr_dat, (
                $states
            ));
        ).unwrap();
        self.krate.borrow_mut().module.items.push(x);
    }

    pub fn create_state_impls(&self, hm: &HashMap<String, State>) {
        let events = str_to_ident("Events");
        let states = str_to_ident("States");
        let shr_dat= str_to_ident("SharedData");
        for state in hm.values() {
            self.create_state_impl(state, &events, &states, &shr_dat);
        }
    }

    fn create_enter_exit_arm(&self, cx: &ExtCtxt, opt_func: &Option<String>, evt_type: &str) -> Option<Arm> {
        if let &Some(ref func_nam) = opt_func {
            let func_ident = str_to_ident(func_nam);
            let evt_ident = str_to_ident(evt_type);
            Some(cx.arm(DUMMY_SP,
                   vec!(quote_pat!(&cx, hsm::Event::$evt_ident)),
                   quote_expr!(&cx, {
                        hsm_functions::$func_ident(shr_data, evt);
                        hsm::Action::Ignore
                   })
            ))
        } else { None }
    }

    fn create_state_impl(&self, state: &State, events: &Ident, states: &Ident, shr_dat: &Ident) {
        let cx = self.extctxt();
        let state_ident = str_to_ident(state.name.as_ref().unwrap());
        let mut arms: Vec<Arm> = Vec::new();
        self.create_enter_exit_arm(&cx, &state.entry, "Enter").map(|x| arms.push(x));
        self.create_enter_exit_arm(&cx, &state.exit, "Exit").map(|x| arms.push(x));
        for (trigger, target) in state.transitions.iter() {
            let target_ident = str_to_ident(target);
            let trigger_ident = str_to_ident(trigger);
            let pat = if trigger == "_" {
                quote_pat!(&cx, $trigger_ident)
            } else {
                quote_pat!(&cx, hsm::Event::User($events::$trigger_ident))
            };
            let trans = quote_expr!(&cx, $states::$target_ident);
            arms.push(cx.arm(DUMMY_SP,
                             vec!(pat),
                             quote_expr!(&cx, hsm_delayed_transition!(probe, { $trans }))
            ));
        };
        if !state.transitions.contains_key("_") {
            arms.push(cx.arm(DUMMY_SP,
                             vec!(quote_pat!(&cx, _)),
                             quote_expr!(&cx, hsm::Action::Parent)
            ));
        };
        let match_expr = cx.expr_match(DUMMY_SP, quote_expr!(&cx, evt), arms);
        let x = quote_item!(&cx,
            impl hsm::State<$events, $states, $shr_dat> for $state_ident<$events, $states, $shr_dat> {
                fn handle_event(&mut self, shr_data: &mut $shr_dat, evt: hsm::Event<$events>, probe: bool) -> hsm::Action<$states> {
                    $match_expr
                }
            }
        ).unwrap();
        self.krate.borrow_mut().module.items.push(x);
    }

    pub fn create_function_stubs(&self, hm: &HashMap<String, State>) {
        let cx = self.extctxt();
        let events = str_to_ident("Events");
        let shr_dat= str_to_ident("SharedData");
        let mut functions = HashSet::new();
        hm.values().map(|s| {
            s.entry.as_ref().map(|x|
                functions.insert(x.to_string())
            );
            s.exit.as_ref().map(|x|
                functions.insert(x.to_string())
            )
        }).count();
        for func in functions {
            let func_ident  = str_to_ident(&func);
            let it = quote_item!(&cx,
                fn $func_ident(shr_data: &mut $shr_dat, evt: hsm::Event<$events>) {
                    unimplemented!();
                }
            ).unwrap().map(|mut x| {
                x.vis = Visibility::Public;
                x
            });
            self.krate.borrow_mut().module.items.push(it);
            // println!("{}", func);
        }
    }
}
<|endoftext|>


<commit_before>//! Functions to read and write debug registers.
//!
//! * The dr{0,1,2,3} registers are used to set break points.
//! * The dr6 register contains debug conditions that were sampled at the time
//!   the last debug exception.
//! * The dr7 register enables or disables breakpoints and sets breakpoint
//!   conditions.
//!
//! See Intel Vol. 3a Chapter 17, "Debug, Branch, Profile, TSC ... Features"

use bit_field::BitField;
use bitflags::bitflags;

/// Read dr0.
///
/// # Safety
/// Needs CPL 0.
pub unsafe fn dr0() -> usize {
    let ret: usize;
    asm!("mov dr0, {}", out(reg) ret, options(att_syntax));
    ret
}

/// Write dr0.
///
/// # Safety
/// Needs CPL 0.
pub unsafe fn dr0_write(val: usize) {
    asm!("mov {}, dr0", in(reg) val, options(att_syntax));
}

/// Read dr1.
///
/// # Safety
/// Needs CPL 0.
pub unsafe fn dr1() -> usize {
    let ret: usize;
    asm!("mov dr1, {}", out(reg) ret, options(att_syntax));
    ret
}

/// Write dr1.
///
/// # Safety
/// Needs CPL 0.
pub unsafe fn dr1_write(val: usize) {
    asm!("mov {}, dr1", in(reg) val, options(att_syntax));
}

/// Read dr2.
///
/// # Safety
/// Needs CPL 0.
pub unsafe fn dr2() -> usize {
    let ret: usize;
    asm!("mov dr2, {}", out(reg) ret, options(att_syntax));
    ret
}

/// Write dr2.
///
/// # Safety
/// Needs CPL 0.
pub unsafe fn dr2_write(val: usize) {
    asm!("mov {}, dr2", in(reg) val, options(att_syntax));
}

/// Read dr3.
///
/// # Safety
/// Needs CPL 0.
pub unsafe fn dr3() -> usize {
    let ret: usize;
    asm!("mov dr3, {}", out(reg) ret, options(att_syntax));
    ret
}

/// Write dr3.
///
/// # Safety
/// Needs CPL 0.
pub unsafe fn dr3_write(val: usize) {
    asm!("mov {}, dr3", in(reg) val, options(att_syntax));
}

bitflags! {
    /// Debug register 6 (dr6) flags.
    pub struct Dr6: usize {
        /// B0 breakpoint condition detected
        ///
        /// # Notes
        ///
        /// The flag is set if the condition described for the breakpoint by
        /// the LENn, and R/Wn flags in debug control register DR7 is true. They
        /// may or may not be set if the breakpoint is not enabled by the Ln or
        /// the Gn flags in register DR7. Therefore on a #DB, a debug handler
        /// should check only those B0-B3 bits which correspond to an enabled
        /// breakpoint.
        const B0 = 0b0001;

        /// B1 breakpoint condition detected
        ///
        /// # Notes
        ///
        /// The flag is set if the condition described for the breakpoint by
        /// the LENn, and R/Wn flags in debug control register DR7 is true. They
        /// may or may not be set if the breakpoint is not enabled by the Ln or
        /// the Gn flags in register DR7. Therefore on a #DB, a debug handler
        /// should check only those B0-B3 bits which correspond to an enabled
        /// breakpoint.
        const B1 = 0b0010;

        /// B2 breakpoint condition detected
        ///
        /// # Notes
        ///
        /// The flag is set if the condition described for the breakpoint by
        /// the LENn, and R/Wn flags in debug control register DR7 is true. They
        /// may or may not be set if the breakpoint is not enabled by the Ln or
        /// the Gn flags in register DR7. Therefore on a #DB, a debug handler
        /// should check only those B0-B3 bits which correspond to an enabled
        /// breakpoint.
        const B2 = 0b0100;

        /// B3 breakpoint condition detected
        ///
        /// # Notes
        ///
        /// The flag is set if the condition described for the breakpoint by
        /// the LENn, and R/Wn flags in debug control register DR7 is true. They
        /// may or may not be set if the breakpoint is not enabled by the Ln or
        /// the Gn flags in register DR7. Therefore on a #DB, a debug handler
        /// should check only those B0-B3 bits which correspond to an enabled
        /// breakpoint.
        const B3 = 0b1000;

        /// BD debug register access detected
        ///
        /// Indicates that the next instruction in the instruction stream
        /// accesses one of the debug registers.
        ///
        /// This flag is enabled when the GD (general detect) flag in debug
        /// control register DR7 is set.
        const BD = 1 << 13;

        /// BS single step
        ///
        /// Indicates (when set) that the debug exception was triggered by the
        /// single- step execution mode (enabled with the TF flag in the EFLAGS
        /// register).
        const BS = 1 << 14;

        /// BT task switch
        ///
        /// Indicates (when set) that the debug exception resulted from a task
        /// switch where the T flag (debug trap flag) in the TSS of the target
        /// task was set.
        const BT = 1 << 15;

        /// Enables (when set) advanced debugging of RTM transactional regions.
        const RTM = 1 << 16;
    }
}

/// Read dr6.
///
/// # Safety
/// Needs CPL 0.
pub unsafe fn dr6() -> Dr6 {
    let ret: usize;
    asm!("mov dr6, {}", out(reg) ret, options(att_syntax));
    Dr6::from_bits_truncate(ret)
}

/// Write dr6.
///
/// # Notes
///
/// Certain debug exceptions may clear bits 0-3. The remaining contents of the
/// DR6 register are never cleared by the processor. To avoid confusion in
/// identifying debug exceptions, debug handlers should clear the register
/// (except bit 16, which they should set) before returning to the interrupted
/// task).
///
/// # Safety
/// Needs CPL 0.
pub unsafe fn dr6_write(val: Dr6) {
    asm!("mov {}, dr6", in(reg) val.bits, options(att_syntax));
}

/// Specifies available hardware breakpoints.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Breakpoint {
    Dr0 = 0,
    Dr1 = 1,
    Dr2 = 2,
    Dr3 = 3,
}

/// Specifies the  breakpoint condition for a corresponding breakpoint.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum BreakCondition {
    /// 00 — Break on instruction execution only.
    Instructions = 0b00,
    /// 01 — Break on data writes only.
    DataWrites = 0b01,
    /// 10 — Break on I/O reads or writes.
    ///
    /// # Notes
    /// For this type to be available, the DE (debug extensions) flag in control
    /// register CR4 must be set.
    IoReadsWrites = 0b10,
    /// 11 — Break on data reads or writes but not instruction fetches.
    DataReadsWrites = 0b11,
}

/// Specify the size of the memory location at the address specified in the
/// corresponding breakpoint address register (DR0 through DR3).
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum BreakSize {
    /// 00 — 1-byte length.
    Bytes1 = 0b00,
    /// 01 — 2-byte length.
    Bytes2 = 0b01,
    /// 10 — 8 byte length (or undefined, on older processors).
    Bytes8 = 0b10,
    /// 11 — 4-byte length.
    Bytes4 = 0b11,
}

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct Dr7(pub usize);

impl Default for Dr7 {
    fn default() -> Self {
        Self(1 << 10)
    }
}

impl Dr7 {
    fn set_bp(&mut self, bp: Breakpoint, global: bool, val: bool) {
        let bp = bp as usize;
        assert!(bp < 4);
        let idx = if global { bp + 1 } else { bp };
        self.0.set_bit(idx, val);
    }

    fn set_bc(&mut self, bp: Breakpoint, bc: BreakCondition) {
        let idx = 16 + (bp as usize * 4);
        assert!(idx == 16 || idx == 20 || idx == 24 || idx == 28);
        self.0.set_bits(idx..=idx + 1, bc as usize);
    }

    fn set_bs(&mut self, bp: Breakpoint, bs: BreakSize) {
        let idx = 18 + (bp as usize * 4);
        assert!(idx == 18 || idx == 22 || idx == 26 || idx == 30);
        self.0.set_bits(idx..=idx + 1, bs as usize);
    }

    /// Enables the breakpoint condition for the associated breakpoint.
    ///
    /// - `bp`: The breakpoint to enable.
    /// - `global`: If true, the breakpoint is global (e.g., never reset on task
    ///   switches). If false, the CPU resets the flag (disables bp) on task
    ///   switch.
    pub fn enable_bp(&mut self, bp: Breakpoint, bc: BreakCondition, bs: BreakSize, global: bool) {
        assert!(
            !(bc == BreakCondition::Instructions && bs != BreakSize::Bytes1),
            "If bc is 00 (instruction execution), then the bs field should be 00"
        );
        self.set_bp(bp, global, true);
        self.set_bc(bp, bc);
        self.set_bs(bp, bs);
    }

    /// Disables the breakpoint condition for the associated breakpoint.
    ///
    /// - `bp`: The breakpoint to disable.
    /// - `global`: If true, the breakpoint is global (e.g., never reset on task
    ///   switches). If false, the CPU resets the flag (disables bp) on task
    ///   switch.
    pub fn disable_bp(&mut self, bp: Breakpoint, global: bool) {
        self.set_bp(bp, global, false);
    }

    /// Local exact breakpoint enable.
    ///
    /// This flag causes the processor to detect the exact instruction that
    /// caused a data breakpoint condition. This feature is not supported in the
    /// P6 family processors, later IA-32 processors, and Intel 64 processors.
    ///
    /// For backward and forward compatibility with other Intel processors,
    /// Intel recommends that the LE flag be set to 1 if exact breakpoints are
    /// required.
    pub fn enable_exact_local_bp(&mut self) {
        self.0.set_bit(8, true);
    }

    /// Global exact breakpoint enable.
    ///
    /// This flag causes the processor to detect the exact instruction that
    /// caused a data breakpoint condition. This feature is not supported in the
    /// P6 family processors, later IA-32 processors, and Intel 64 processors.
    ///
    /// For backward and forward compatibility with other Intel processors,
    /// Intel recommends that the GE flag be set to 1 if exact breakpoints are
    /// required.
    pub fn enable_exact_global_bp(&mut self) {
        self.0.set_bit(9, true);
    }

    /// Enables advanced debugging of RTM transactional regions.
    ///
    /// # Note
    /// This advanced debugging is enabled only if IA32_DEBUGCTL.RTM is also
    /// set.
    pub fn enable_rtm(&mut self) {
        self.0.set_bit(11, true);
    }

    /// Enables debug-register protection, which causes a debug exception to be
    /// generated prior to any MOV instruction that accesses a debug register.
    pub fn enable_general_detect(&mut self) {
        self.0.set_bit(13, true);
    }
}

/// Read dr7.
///
/// # Safety
/// Needs CPL 0.
pub unsafe fn dr7() -> Dr7 {
    let ret: usize;
    asm!("mov dr7, {}", out(reg) ret, options(att_syntax));
    Dr7(ret)
}

/// Write dr7.
///
/// # Safety
/// Needs CPL 0.
pub unsafe fn dr7_write(val: Dr7) {
    asm!("mov {}, dr7", in(reg) val.0, options(att_syntax));
}
<commit_msg>Some more functionality for debugregs.<commit_after>//! Functions to read and write debug registers.
//!
//! * The dr{0,1,2,3} registers are used to set break points.
//! * The dr6 register contains debug conditions that were sampled at the time
//!   the last debug exception.
//! * The dr7 register enables or disables breakpoints and sets breakpoint
//!   conditions.
//!
//! See Intel Vol. 3a Chapter 17, "Debug, Branch, Profile, TSC ... Features"

use bit_field::BitField;
use bitflags::bitflags;

/// An array list of all available breakpoint registers.
pub const BREAKPOINT_REGS: [Breakpoint; 4] = [
    Breakpoint::Dr0,
    Breakpoint::Dr1,
    Breakpoint::Dr2,
    Breakpoint::Dr3,
];

/// Write dr{0-3} register based on provided `reg` enum.
pub unsafe fn dr_write(reg: Breakpoint, val: usize) {
    match reg {
        Breakpoint::Dr0 => dr0_write(val),
        Breakpoint::Dr1 => dr1_write(val),
        Breakpoint::Dr2 => dr2_write(val),
        Breakpoint::Dr3 => dr3_write(val),
    }
}

/// Read dr{0-3} register based on provided `reg` enum.
pub unsafe fn dr(reg: Breakpoint) -> usize {
    match reg {
        Breakpoint::Dr0 => dr0(),
        Breakpoint::Dr1 => dr1(),
        Breakpoint::Dr2 => dr2(),
        Breakpoint::Dr3 => dr3(),
    }
}

/// Read dr0.
///
/// # Safety
/// Needs CPL 0.
pub unsafe fn dr0() -> usize {
    let ret: usize;
    asm!("mov dr0, {}", out(reg) ret, options(att_syntax));
    ret
}

/// Write dr0.
///
/// # Safety
/// Needs CPL 0.
pub unsafe fn dr0_write(val: usize) {
    asm!("mov {}, dr0", in(reg) val, options(att_syntax));
}

/// Read dr1.
///
/// # Safety
/// Needs CPL 0.
pub unsafe fn dr1() -> usize {
    let ret: usize;
    asm!("mov dr1, {}", out(reg) ret, options(att_syntax));
    ret
}

/// Write dr1.
///
/// # Safety
/// Needs CPL 0.
pub unsafe fn dr1_write(val: usize) {
    asm!("mov {}, dr1", in(reg) val, options(att_syntax));
}

/// Read dr2.
///
/// # Safety
/// Needs CPL 0.
pub unsafe fn dr2() -> usize {
    let ret: usize;
    asm!("mov dr2, {}", out(reg) ret, options(att_syntax));
    ret
}

/// Write dr2.
///
/// # Safety
/// Needs CPL 0.
pub unsafe fn dr2_write(val: usize) {
    asm!("mov {}, dr2", in(reg) val, options(att_syntax));
}

/// Read dr3.
///
/// # Safety
/// Needs CPL 0.
pub unsafe fn dr3() -> usize {
    let ret: usize;
    asm!("mov dr3, {}", out(reg) ret, options(att_syntax));
    ret
}

/// Write dr3.
///
/// # Safety
/// Needs CPL 0.
pub unsafe fn dr3_write(val: usize) {
    asm!("mov {}, dr3", in(reg) val, options(att_syntax));
}

bitflags! {
    /// Debug register 6 (dr6) flags.
    pub struct Dr6: usize {
        /// B0 breakpoint condition detected
        ///
        /// # Notes
        ///
        /// The flag is set if the condition described for the breakpoint by
        /// the LENn, and R/Wn flags in debug control register DR7 is true. They
        /// may or may not be set if the breakpoint is not enabled by the Ln or
        /// the Gn flags in register DR7. Therefore on a #DB, a debug handler
        /// should check only those B0-B3 bits which correspond to an enabled
        /// breakpoint.
        const B0 = 0b0001;

        /// B1 breakpoint condition detected
        ///
        /// # Notes
        ///
        /// The flag is set if the condition described for the breakpoint by
        /// the LENn, and R/Wn flags in debug control register DR7 is true. They
        /// may or may not be set if the breakpoint is not enabled by the Ln or
        /// the Gn flags in register DR7. Therefore on a #DB, a debug handler
        /// should check only those B0-B3 bits which correspond to an enabled
        /// breakpoint.
        const B1 = 0b0010;

        /// B2 breakpoint condition detected
        ///
        /// # Notes
        ///
        /// The flag is set if the condition described for the breakpoint by
        /// the LENn, and R/Wn flags in debug control register DR7 is true. They
        /// may or may not be set if the breakpoint is not enabled by the Ln or
        /// the Gn flags in register DR7. Therefore on a #DB, a debug handler
        /// should check only those B0-B3 bits which correspond to an enabled
        /// breakpoint.
        const B2 = 0b0100;

        /// B3 breakpoint condition detected
        ///
        /// # Notes
        ///
        /// The flag is set if the condition described for the breakpoint by
        /// the LENn, and R/Wn flags in debug control register DR7 is true. They
        /// may or may not be set if the breakpoint is not enabled by the Ln or
        /// the Gn flags in register DR7. Therefore on a #DB, a debug handler
        /// should check only those B0-B3 bits which correspond to an enabled
        /// breakpoint.
        const B3 = 0b1000;

        /// BD debug register access detected
        ///
        /// Indicates that the next instruction in the instruction stream
        /// accesses one of the debug registers.
        ///
        /// This flag is enabled when the GD (general detect) flag in debug
        /// control register DR7 is set.
        const BD = 1 << 13;

        /// BS single step
        ///
        /// Indicates (when set) that the debug exception was triggered by the
        /// single- step execution mode (enabled with the TF flag in the EFLAGS
        /// register).
        const BS = 1 << 14;

        /// BT task switch
        ///
        /// Indicates (when set) that the debug exception resulted from a task
        /// switch where the T flag (debug trap flag) in the TSS of the target
        /// task was set.
        const BT = 1 << 15;

        /// Enables (when set) advanced debugging of RTM transactional regions.
        const RTM = 1 << 16;
    }
}

/// Read dr6.
///
/// # Safety
/// Needs CPL 0.
pub unsafe fn dr6() -> Dr6 {
    let ret: usize;
    asm!("mov dr6, {}", out(reg) ret, options(att_syntax));
    Dr6::from_bits_truncate(ret)
}

/// Write dr6.
///
/// # Notes
///
/// Certain debug exceptions may clear bits 0-3. The remaining contents of the
/// DR6 register are never cleared by the processor. To avoid confusion in
/// identifying debug exceptions, debug handlers should clear the register
/// (except bit 16, which they should set) before returning to the interrupted
/// task).
///
/// # Safety
/// Needs CPL 0.
pub unsafe fn dr6_write(val: Dr6) {
    asm!("mov {}, dr6", in(reg) val.bits, options(att_syntax));
}

/// Specifies available hardware breakpoints.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Breakpoint {
    Dr0 = 0,
    Dr1 = 1,
    Dr2 = 2,
    Dr3 = 3,
}

/// Specifies the  breakpoint condition for a corresponding breakpoint.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum BreakCondition {
    /// 00 — Break on instruction execution only.
    Instructions = 0b00,
    /// 01 — Break on data writes only.
    DataWrites = 0b01,
    /// 10 — Break on I/O reads or writes.
    ///
    /// # Notes
    /// For this type to be available, the DE (debug extensions) flag in control
    /// register CR4 must be set.
    IoReadsWrites = 0b10,
    /// 11 — Break on data reads or writes but not instruction fetches.
    DataReadsWrites = 0b11,
}

/// Specify the size of the memory location at the address specified in the
/// corresponding breakpoint address register (DR0 through DR3).
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum BreakSize {
    /// 00 — 1-byte length.
    Bytes1 = 0b00,
    /// 01 — 2-byte length.
    Bytes2 = 0b01,
    /// 10 — 8 byte length (or undefined, on older processors).
    Bytes8 = 0b10,
    /// 11 — 4-byte length.
    Bytes4 = 0b11,
}

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct Dr7(pub usize);

impl Default for Dr7 {
    fn default() -> Self {
        Self(1 << 10)
    }
}

impl Dr7 {
    fn set_bp(&mut self, bp: Breakpoint, global: bool, val: bool) {
        let bp = bp as usize;
        assert!(bp < 4);
        let idx = if global { bp + 1 } else { bp };
        self.0.set_bit(idx, val);
    }

    fn set_bc(&mut self, bp: Breakpoint, bc: BreakCondition) {
        let idx = 16 + (bp as usize * 4);
        assert!(idx == 16 || idx == 20 || idx == 24 || idx == 28);
        self.0.set_bits(idx..=idx + 1, bc as usize);
    }

    fn set_bs(&mut self, bp: Breakpoint, bs: BreakSize) {
        let idx = 18 + (bp as usize * 4);
        assert!(idx == 18 || idx == 22 || idx == 26 || idx == 30);
        self.0.set_bits(idx..=idx + 1, bs as usize);
    }

    /// Enables the breakpoint condition for the associated breakpoint.
    ///
    /// - `bp`: The breakpoint to enable.
    /// - `global`: If true, the breakpoint is global (e.g., never reset on task
    ///   switches). If false, the CPU resets the flag (disables bp) on task
    ///   switch.
    pub fn enable_bp(&mut self, bp: Breakpoint, bc: BreakCondition, bs: BreakSize, global: bool) {
        assert!(
            !(bc == BreakCondition::Instructions && bs != BreakSize::Bytes1),
            "If bc is 00 (instruction execution), then the bs field should be 00"
        );
        self.set_bp(bp, global, true);
        self.set_bc(bp, bc);
        self.set_bs(bp, bs);
    }

    /// Disables the breakpoint condition for the associated breakpoint.
    ///
    /// - `bp`: The breakpoint to disable.
    /// - `global`: If true, the breakpoint is global (e.g., never reset on task
    ///   switches). If false, the CPU resets the flag (disables bp) on task
    ///   switch.
    pub fn disable_bp(&mut self, bp: Breakpoint, global: bool) {
        self.set_bp(bp, global, false);
    }

    /// Local exact breakpoint enable.
    ///
    /// This flag causes the processor to detect the exact instruction that
    /// caused a data breakpoint condition. This feature is not supported in the
    /// P6 family processors, later IA-32 processors, and Intel 64 processors.
    ///
    /// For backward and forward compatibility with other Intel processors,
    /// Intel recommends that the LE flag be set to 1 if exact breakpoints are
    /// required.
    pub fn enable_exact_local_bp(&mut self) {
        self.0.set_bit(8, true);
    }

    /// Global exact breakpoint enable.
    ///
    /// This flag causes the processor to detect the exact instruction that
    /// caused a data breakpoint condition. This feature is not supported in the
    /// P6 family processors, later IA-32 processors, and Intel 64 processors.
    ///
    /// For backward and forward compatibility with other Intel processors,
    /// Intel recommends that the GE flag be set to 1 if exact breakpoints are
    /// required.
    pub fn enable_exact_global_bp(&mut self) {
        self.0.set_bit(9, true);
    }

    /// Enables advanced debugging of RTM transactional regions.
    ///
    /// # Note
    /// This advanced debugging is enabled only if IA32_DEBUGCTL.RTM is also
    /// set.
    pub fn enable_rtm(&mut self) {
        self.0.set_bit(11, true);
    }

    /// Enables debug-register protection, which causes a debug exception to be
    /// generated prior to any MOV instruction that accesses a debug register.
    pub fn enable_general_detect(&mut self) {
        self.0.set_bit(13, true);
    }
}

/// Read dr7.
///
/// # Safety
/// Needs CPL 0.
pub unsafe fn dr7() -> Dr7 {
    let ret: usize;
    asm!("mov dr7, {}", out(reg) ret, options(att_syntax));
    Dr7(ret)
}

/// Write dr7.
///
/// # Safety
/// Needs CPL 0.
pub unsafe fn dr7_write(val: Dr7) {
    asm!("mov {}, dr7", in(reg) val.0, options(att_syntax));
}
<|endoftext|>


<commit_before><commit_msg>Implement demux get_caps().<commit_after><|endoftext|>