<commit_before>use std::io::Cursor;

use byteorder::BigEndian;

pub fn encode<'a, I>(iter: I) -> Encode<I>
where
    I: Iterator<Item = &'a [u8]>,
{
    Encode {
        iter: iter,
        cur: Cursor::new(vec!()),
    }
}

pub fn decode<R>(buf: R) -> Decode<R>
where
    R: std::io::Read,
{
    Decode {
        buf: buf,
    }
}

pub struct Encode<I> {
    iter: I,
    cur: Cursor<Vec<u8>>,
}

impl<'a, I> Encode<I>
where
    I: Iterator<Item = &'a [u8]>,
{
    fn is_empty(&self) -> bool {
        self.cur.position() == self.cur.get_ref().len() as u64
    }

    fn pull(&mut self) -> std::io::Result<()> {
        use std::io::Write as _;
        use byteorder::WriteBytesExt as _;

        let data = match self.iter.next() {
            Some(data) => data,
            None => return Ok(()),
        };

        self.cur.get_mut().clear();
        self.cur.set_position(0);

        self.cur.write_u64::<BigEndian>(data.len() as u64)?;
        self.cur.write_all(data)?;
        self.cur.set_position(0);

        Ok(())
    }
}

impl<'a, I> std::io::Read for Encode<I>
where
    I: Iterator<Item = &'a [u8]>,
{
    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
        if self.is_empty() {
            self.pull()?;
        }

        self.cur.read(buf)
    }
}

pub struct Decode<R> {
    buf: R,
}

impl<R: std::io::Read> Decode<R> {

    fn read_len(&mut self) -> std::io::Result<Option<usize>> {
        use byteorder::ReadBytesExt as _;

        let mut buf = [0; 8];
        match self.buf.read(&mut buf[..])? {
            8 => (),
            0 => return Ok(None),
            _ => return Err(SizeTagError.into()),
        }

        let len = (&buf[..]).read_u64::<BigEndian>()? as usize;
        Ok(Some(len))
    }
}

impl<R: std::io::Read> Iterator for Decode<R> {

    type Item = std::io::Result<Vec<u8>>;

    fn next(&mut self) -> Option<std::io::Result<Vec<u8>>> {
        let len = match self.read_len() {
            Ok(Some(len)) => len,
            Ok(None) => return None,
            Err(error) => return Some(Err(error)),
        };

        let mut buf = vec!(0; len);
        match self.buf.read_exact(&mut buf[..]) {
            Ok(()) => (),
            Err(error) => return Some(Err(error)),
        }

        Some(Ok(buf))
    }
}

#[derive(Debug)]
struct SizeTagError;

impl std::fmt::Display for SizeTagError {

    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(fmt, "incorrect length of the size tag")
    }
}

impl std::error::Error for SizeTagError {
}

impl From<SizeTagError> for std::io::Error {

    fn from(error: SizeTagError) -> std::io::Error {
        std::io::Error::new(std::io::ErrorKind::InvalidData, error)
    }
}

#[cfg(test)]
pub mod tests {

    use std::io::Read as _;

    use super::*;

    #[test]
    pub fn test_encode_empty() {
        let mut stream = encode(std::iter::empty());

        let mut output = vec!();
        stream.read_to_end(&mut output).unwrap();

        assert!(output.is_empty());
    }

    #[test]
    pub fn test_encode_single_chunk() {
        let mut stream = encode(vec!(&b"foo"[..]).into_iter());

        let mut output = vec!();
        stream.read_to_end(&mut output).unwrap();

        assert_eq!(output, b"\x00\x00\x00\x00\x00\x00\x00\x03foo");
    }

    #[test]
    pub fn test_encode_single_empty_chunk() {
        let mut stream = encode(vec!(&b""[..]).into_iter());

        let mut output = vec!();
        stream.read_to_end(&mut output).unwrap();

        assert_eq!(output, b"\x00\x00\x00\x00\x00\x00\x00\x00");
    }

    #[test]
    pub fn test_encode_multiple_empty_chunks() {
        let mut stream = encode(vec!(&b""[..], &b""[..]).into_iter());

        let mut output = vec!();
        stream.read_to_end(&mut output).unwrap();

        assert_eq!(&output[..8], b"\x00\x00\x00\x00\x00\x00\x00\x00");
        assert_eq!(&output[8..], b"\x00\x00\x00\x00\x00\x00\x00\x00");
    }

    #[test]
    pub fn test_decode_empty() {
        let mut iter = decode(&b""[..]);
        assert!(iter.next().is_none());
    }

    #[test]
    pub fn test_decode_single_chunk() {
        let mut iter = decode(&b"\x00\x00\x00\x00\x00\x00\x00\x03foo"[..])
            .map(Result::unwrap);

        assert_eq!(iter.next(), Some(b"foo".to_vec()));
        assert_eq!(iter.next(), None);
    }

    #[test]
    pub fn test_decode_empty_chunk() {
        let mut iter = decode(&b"\x00\x00\x00\x00\x00\x00\x00\x00"[..])
            .map(Result::unwrap);

        assert_eq!(iter.next(), Some(b"".to_vec()));
        assert_eq!(iter.next(), None);
    }

    #[test]
    pub fn test_decode_incorrect_size_tag() {
        let mut iter = decode(&b"\x12\x34\x56"[..]);

        let error = iter.next().unwrap().unwrap_err();
        assert_eq!(error.kind(), std::io::ErrorKind::InvalidData);
    }

    #[test]
    pub fn test_decode_short_input() {
        let mut iter = decode(&b"\x00\x00\x00\x00\x00\x00\x00\x42foo"[..]);

        let error = iter.next().unwrap().unwrap_err();
        assert_eq!(error.kind(), std::io::ErrorKind::UnexpectedEof);
    }

    #[test]
    pub fn test_encode_and_decode_multiple_chunks() {
        let chunks = vec! {
            &b"foo"[..],
            &b"bar"[..],
            &b"baz"[..],
        };

        let mut iter = decode(encode(chunks.into_iter()))
            .map(Result::unwrap);

        assert_eq!(iter.next(), Some(b"foo".to_vec()));
        assert_eq!(iter.next(), Some(b"bar".to_vec()));
        assert_eq!(iter.next(), Some(b"baz".to_vec()));
        assert_eq!(iter.next(), None);
    }

    #[test]
    pub fn test_encode_and_decode_multiple_empty_chunks() {
        let chunks = vec!(&b""[..], &b""[..], &b""[..]);

        let mut iter = decode(encode(chunks.into_iter()))
            .map(Result::unwrap);

        assert_eq!(iter.next(), Some(b"".to_vec()));
        assert_eq!(iter.next(), Some(b"".to_vec()));
        assert_eq!(iter.next(), Some(b"".to_vec()));
        assert_eq!(iter.next(), None);
    }
}
<commit_msg>Add missing license header.<commit_after>// Copyright 2020 Google LLC
//
// Use of this source code is governed by an MIT-style license that can be found
// in the LICENSE file or at https://opensource.org/licenses/MIT.

use std::io::Cursor;

use byteorder::BigEndian;

pub fn encode<'a, I>(iter: I) -> Encode<I>
where
    I: Iterator<Item = &'a [u8]>,
{
    Encode {
        iter: iter,
        cur: Cursor::new(vec!()),
    }
}

pub fn decode<R>(buf: R) -> Decode<R>
where
    R: std::io::Read,
{
    Decode {
        buf: buf,
    }
}

pub struct Encode<I> {
    iter: I,
    cur: Cursor<Vec<u8>>,
}

impl<'a, I> Encode<I>
where
    I: Iterator<Item = &'a [u8]>,
{
    fn is_empty(&self) -> bool {
        self.cur.position() == self.cur.get_ref().len() as u64
    }

    fn pull(&mut self) -> std::io::Result<()> {
        use std::io::Write as _;
        use byteorder::WriteBytesExt as _;

        let data = match self.iter.next() {
            Some(data) => data,
            None => return Ok(()),
        };

        self.cur.get_mut().clear();
        self.cur.set_position(0);

        self.cur.write_u64::<BigEndian>(data.len() as u64)?;
        self.cur.write_all(data)?;
        self.cur.set_position(0);

        Ok(())
    }
}

impl<'a, I> std::io::Read for Encode<I>
where
    I: Iterator<Item = &'a [u8]>,
{
    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
        if self.is_empty() {
            self.pull()?;
        }

        self.cur.read(buf)
    }
}

pub struct Decode<R> {
    buf: R,
}

impl<R: std::io::Read> Decode<R> {

    fn read_len(&mut self) -> std::io::Result<Option<usize>> {
        use byteorder::ReadBytesExt as _;

        let mut buf = [0; 8];
        match self.buf.read(&mut buf[..])? {
            8 => (),
            0 => return Ok(None),
            _ => return Err(SizeTagError.into()),
        }

        let len = (&buf[..]).read_u64::<BigEndian>()? as usize;
        Ok(Some(len))
    }
}

impl<R: std::io::Read> Iterator for Decode<R> {

    type Item = std::io::Result<Vec<u8>>;

    fn next(&mut self) -> Option<std::io::Result<Vec<u8>>> {
        let len = match self.read_len() {
            Ok(Some(len)) => len,
            Ok(None) => return None,
            Err(error) => return Some(Err(error)),
        };

        let mut buf = vec!(0; len);
        match self.buf.read_exact(&mut buf[..]) {
            Ok(()) => (),
            Err(error) => return Some(Err(error)),
        }

        Some(Ok(buf))
    }
}

#[derive(Debug)]
struct SizeTagError;

impl std::fmt::Display for SizeTagError {

    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(fmt, "incorrect length of the size tag")
    }
}

impl std::error::Error for SizeTagError {
}

impl From<SizeTagError> for std::io::Error {

    fn from(error: SizeTagError) -> std::io::Error {
        std::io::Error::new(std::io::ErrorKind::InvalidData, error)
    }
}

#[cfg(test)]
pub mod tests {

    use std::io::Read as _;

    use super::*;

    #[test]
    pub fn test_encode_empty() {
        let mut stream = encode(std::iter::empty());

        let mut output = vec!();
        stream.read_to_end(&mut output).unwrap();

        assert!(output.is_empty());
    }

    #[test]
    pub fn test_encode_single_chunk() {
        let mut stream = encode(vec!(&b"foo"[..]).into_iter());

        let mut output = vec!();
        stream.read_to_end(&mut output).unwrap();

        assert_eq!(output, b"\x00\x00\x00\x00\x00\x00\x00\x03foo");
    }

    #[test]
    pub fn test_encode_single_empty_chunk() {
        let mut stream = encode(vec!(&b""[..]).into_iter());

        let mut output = vec!();
        stream.read_to_end(&mut output).unwrap();

        assert_eq!(output, b"\x00\x00\x00\x00\x00\x00\x00\x00");
    }

    #[test]
    pub fn test_encode_multiple_empty_chunks() {
        let mut stream = encode(vec!(&b""[..], &b""[..]).into_iter());

        let mut output = vec!();
        stream.read_to_end(&mut output).unwrap();

        assert_eq!(&output[..8], b"\x00\x00\x00\x00\x00\x00\x00\x00");
        assert_eq!(&output[8..], b"\x00\x00\x00\x00\x00\x00\x00\x00");
    }

    #[test]
    pub fn test_decode_empty() {
        let mut iter = decode(&b""[..]);
        assert!(iter.next().is_none());
    }

    #[test]
    pub fn test_decode_single_chunk() {
        let mut iter = decode(&b"\x00\x00\x00\x00\x00\x00\x00\x03foo"[..])
            .map(Result::unwrap);

        assert_eq!(iter.next(), Some(b"foo".to_vec()));
        assert_eq!(iter.next(), None);
    }

    #[test]
    pub fn test_decode_empty_chunk() {
        let mut iter = decode(&b"\x00\x00\x00\x00\x00\x00\x00\x00"[..])
            .map(Result::unwrap);

        assert_eq!(iter.next(), Some(b"".to_vec()));
        assert_eq!(iter.next(), None);
    }

    #[test]
    pub fn test_decode_incorrect_size_tag() {
        let mut iter = decode(&b"\x12\x34\x56"[..]);

        let error = iter.next().unwrap().unwrap_err();
        assert_eq!(error.kind(), std::io::ErrorKind::InvalidData);
    }

    #[test]
    pub fn test_decode_short_input() {
        let mut iter = decode(&b"\x00\x00\x00\x00\x00\x00\x00\x42foo"[..]);

        let error = iter.next().unwrap().unwrap_err();
        assert_eq!(error.kind(), std::io::ErrorKind::UnexpectedEof);
    }

    #[test]
    pub fn test_encode_and_decode_multiple_chunks() {
        let chunks = vec! {
            &b"foo"[..],
            &b"bar"[..],
            &b"baz"[..],
        };

        let mut iter = decode(encode(chunks.into_iter()))
            .map(Result::unwrap);

        assert_eq!(iter.next(), Some(b"foo".to_vec()));
        assert_eq!(iter.next(), Some(b"bar".to_vec()));
        assert_eq!(iter.next(), Some(b"baz".to_vec()));
        assert_eq!(iter.next(), None);
    }

    #[test]
    pub fn test_encode_and_decode_multiple_empty_chunks() {
        let chunks = vec!(&b""[..], &b""[..], &b""[..]);

        let mut iter = decode(encode(chunks.into_iter()))
            .map(Result::unwrap);

        assert_eq!(iter.next(), Some(b"".to_vec()));
        assert_eq!(iter.next(), Some(b"".to_vec()));
        assert_eq!(iter.next(), Some(b"".to_vec()));
        assert_eq!(iter.next(), None);
    }
}
<|endoftext|>


<commit_before>extern crate llvm_sys as llvm;

// use std::mem;
use std::ffi::CString;
use std::ptr;
use std::rc::Rc;
use std;
use std::collections::HashMap;

use self::llvm::core::*;
use self::llvm::prelude::*;

// use parser;
// use lexer;
use node;
use types::{Type, Sign};
use error;

pub struct Codegen {
    context: LLVMContextRef,
    module: LLVMModuleRef,
    builder: LLVMBuilderRef,
    global_varmap: HashMap<String, (Type, LLVMValueRef)>,
    cur_func: Option<LLVMValueRef>,
}

impl Codegen {
    pub unsafe fn new(mod_name: &str) -> Codegen {
        let c_mod_name = CString::new(mod_name).unwrap();
        Codegen {
            context: LLVMContextCreate(),
            module: LLVMModuleCreateWithNameInContext(c_mod_name.as_ptr(), LLVMContextCreate()),
            builder: LLVMCreateBuilderInContext(LLVMContextCreate()),
            global_varmap: HashMap::new(),
            cur_func: None,
        }
    }

    pub unsafe fn type_to_llvmty(&mut self, ty: &Type) -> LLVMTypeRef {
        match ty {
            &Type::Void => LLVMVoidType(),
            &Type::Char(_) => LLVMInt8Type(),
            &Type::Short(_) => LLVMInt16Type(),
            &Type::Int(_) => LLVMInt32Type(),
            &Type::Long(_) => LLVMInt32Type(),
            &Type::LLong(_) => LLVMInt64Type(),
            &Type::Float => LLVMFloatType(),
            &Type::Double => LLVMDoubleType(),
            &Type::Ptr(ref elemty) => LLVMPointerType(self.type_to_llvmty(&**elemty), 0),
            &Type::Array(ref elemty, ref size) => {
                LLVMArrayType(self.type_to_llvmty(&**elemty), *size as u32)
            }
            &Type::Func(ref ret_type, ref param_types, ref is_vararg) => {
                LLVMFunctionType(self.type_to_llvmty(&**ret_type),
                                 || -> *mut LLVMTypeRef {
                                     let mut param_llvm_types: Vec<LLVMTypeRef> = Vec::new();
                                     for param_type in &*param_types {
                                         param_llvm_types.push(self.type_to_llvmty(&param_type));
                                     }
                                     param_llvm_types.as_mut_slice().as_mut_ptr()
                                 }(),
                                 (*param_types).len() as u32,
                                 if *is_vararg { 1 } else { 0 })
            }
        }
    }

    pub unsafe fn run(&mut self, node: Vec<node::AST>) {
        for ast in node {
            self.gen(&ast);
        }

        // e.g. create func
        // let int_t = LLVMInt32TypeInContext(self.context);
        // let func_t = LLVMFunctionType(/* ret type = */
        //                               int_t,
        //                               /* param types = */
        //                               ptr::null_mut(),
        //                               /* param count = */
        //                               0,
        //                               /* is vararg? = */
        //                               0);
        // let main_func =
        //     LLVMAddFunction(self.module, CString::new("main").unwrap().as_ptr(), func_t);
        //
        // // create basic block 'entry'
        // let bb_entry = LLVMAppendBasicBlock(main_func, CString::new("entry").unwrap().as_ptr());
        //
        // LLVMPositionBuilderAtEnd(self.builder, bb_entry); // SetInsertPoint in C++
        //
        // LLVMBuildRet(self.builder, self.make_int(0, false));

        LLVMDumpModule(self.module);
    }

    pub unsafe fn write_llvmir_to_file(&mut self, filename: &str) {
        let mut errmsg = ptr::null_mut();
        if LLVMPrintModuleToFile(self.module,
                                 CString::new(filename).unwrap().as_ptr(),
                                 &mut errmsg) != 0 {
            let err = CString::from_raw(errmsg)
                .to_owned()
                .into_string()
                .unwrap();
            println!("write_to_file failed: {}", err);
            std::process::exit(-1);
        }
    }

    pub unsafe fn gen(&mut self, ast: &node::AST) -> (LLVMValueRef, Option<Type>) {
        match ast {
            &node::AST::FuncDef(ref functy, ref param_names, ref name, ref body) => {
                self.gen_func_def(functy, param_names, name, body)
            }
            &node::AST::VariableDecl(ref ty, ref name, ref init) => {
                self.gen_var_decl(ty, name, init)
            }
            &node::AST::Block(ref block) => self.gen_block(block),
            &node::AST::BinaryOp(ref lhs, ref rhs, ref op) => {
                self.gen_binary_op(&**lhs, &**rhs, &*op)
            }
            // &node::AST::Variable(ref name) => self.gen_
            &node::AST::FuncCall(ref f, ref args) => self.gen_func_call(&*f, args),
            &node::AST::Return(ref ret) => {
                let (retval, _) = self.gen(ret);
                self.gen_return(retval)
            }
            &node::AST::Int(ref n) => self.make_int(*n as u64, false),
            &node::AST::Char(ref c) => self.make_char(*c),
            &node::AST::String(ref s) => self.make_const_str(s),
            _ => {
                error::error_exit(0,
                                  format!("codegen: unknown ast (given {:?})", ast).as_str())
            }
        }
    }

    pub unsafe fn gen_func_def(&mut self,
                               functy: &Type,
                               param_names: &Vec<String>,
                               name: &String,
                               body: &Rc<node::AST>)
                               -> (LLVMValueRef, Option<Type>) {
        let func_t = self.type_to_llvmty(functy);
        let func = LLVMAddFunction(self.module,
                                   CString::new(name.as_str()).unwrap().as_ptr(),
                                   func_t);
        self.global_varmap
            .insert(name.to_string(), (functy.clone(), func));
        self.cur_func = Some(func);

        let bb_entry = LLVMAppendBasicBlock(func, CString::new("entry").unwrap().as_ptr());
        LLVMPositionBuilderAtEnd(self.builder, bb_entry);

        self.gen(&**body);

        self.cur_func = None;

        (func, None)
    }

    // TODO: only global var decl supported
    unsafe fn gen_var_decl(&mut self,
                           ty: &Type,
                           name: &String,
                           init: &Option<Rc<node::AST>>)
                           -> (LLVMValueRef, Option<Type>) {
        // if self.cur_func.is_none() { // is global
        // }

        let gvar = match *ty {
            Type::Func(_, _, _) => {
                LLVMAddFunction(self.module,
                                CString::new(name.as_str()).unwrap().as_ptr(),
                                self.type_to_llvmty(ty))
            }
            _ => {
                LLVMAddGlobal(self.module,
                              self.type_to_llvmty(ty),
                              CString::new(name.as_str()).unwrap().as_ptr())
            }
        };
        self.global_varmap
            .insert(name.to_string(), (ty.clone(), gvar));
        (gvar, None)
    }

    unsafe fn gen_block(&mut self, block: &Vec<node::AST>) -> (LLVMValueRef, Option<Type>) {
        for stmt in block {
            self.gen(stmt);
        }
        (ptr::null_mut(), None)
    }

    unsafe fn gen_binary_op(&mut self,
                            lhsast: &node::AST,
                            rhsast: &node::AST,
                            op: &node::CBinOps)
                            -> (LLVMValueRef, Option<Type>) {
        // logical operators
        match *op {
            node::CBinOps::LAnd => {}
            node::CBinOps::LOr => {}
            _ => {}
        }

        // normal binary operators
        let (lhs, lhsty) = self.gen(lhsast);
        let (rhs, _rhsty) = self.gen(rhsast);
        match lhsty.unwrap() {
            Type::Int(_) => {
                return (self.gen_int_binary_op(lhs, rhs, op), Some(Type::Int(Sign::Signed)))
            }
            _ => {}
        }
        (ptr::null_mut(), Some(Type::Void))
    }

    unsafe fn gen_int_binary_op(&mut self,
                                lhs: LLVMValueRef,
                                rhs: LLVMValueRef,
                                op: &node::CBinOps)
                                -> LLVMValueRef {
        match *op {
            node::CBinOps::Add => {
                LLVMBuildAdd(self.builder,
                             lhs,
                             rhs,
                             CString::new("add").unwrap().as_ptr())
            }
            node::CBinOps::Sub => {
                LLVMBuildAdd(self.builder,
                             lhs,
                             rhs,
                             CString::new("sub").unwrap().as_ptr())
            }
            node::CBinOps::Mul => {
                LLVMBuildMul(self.builder,
                             lhs,
                             rhs,
                             CString::new("mul").unwrap().as_ptr())
            }
            node::CBinOps::Div => {
                LLVMBuildSDiv(self.builder,
                              lhs,
                              rhs,
                              CString::new("div").unwrap().as_ptr())
            }
            node::CBinOps::Rem => {
                LLVMBuildSRem(self.builder,
                              lhs,
                              rhs,
                              CString::new("rem").unwrap().as_ptr())
            }
            _ => ptr::null_mut(),
        }
    }

    unsafe fn gen_func_call(&mut self,
                            fast: &node::AST,
                            args: &Vec<node::AST>)
                            -> (LLVMValueRef, Option<Type>) {
        let args_val = &mut Vec::new();
        for arg in &*args {
            args_val.push(self.gen(arg).0);
        }
        let args_val_ptr = args_val.as_mut_slice().as_mut_ptr();

        let maybe_func = match *fast {
            node::AST::Variable(ref name) => self.global_varmap.get(name),
            _ => None,
        };
        if maybe_func.is_none() {
            error::error_exit(0, "gen_func_call: not found func");
        }

        let func_retty = maybe_func.unwrap().0.clone();
        let func = maybe_func.unwrap().1;
        (LLVMBuildCall(self.builder,
                       func,
                       args_val_ptr,
                       args_val.len() as u32,
                       CString::new("funccall").unwrap().as_ptr()),
         Some(func_retty))
    }

    unsafe fn gen_return(&mut self, retval: LLVMValueRef) -> (LLVMValueRef, Option<Type>) {
        (LLVMBuildRet(self.builder, retval), None)
    }

    pub unsafe fn make_int(&mut self, n: u64, is_unsigned: bool) -> (LLVMValueRef, Option<Type>) {
        (LLVMConstInt(LLVMInt32Type(), n, if is_unsigned { 1 } else { 0 }),
         Some(Type::Int(Sign::Signed)))
    }
    pub unsafe fn make_char(&mut self, n: i32) -> (LLVMValueRef, Option<Type>) {
        (LLVMConstInt(LLVMInt8Type(), n as u64, 0), Some(Type::Char(Sign::Signed)))
    }
    pub unsafe fn make_float(&mut self, f: f64) -> (LLVMValueRef, Option<Type>) {
        (LLVMConstReal(LLVMFloatType(), f), Some(Type::Float))
    }
    pub unsafe fn make_double(&mut self, f: f64) -> (LLVMValueRef, Option<Type>) {
        (LLVMConstReal(LLVMDoubleType(), f), Some(Type::Double))
    }
    pub unsafe fn make_const_str(&mut self, s: &String) -> (LLVMValueRef, Option<Type>) {
        (LLVMConstBitCast(LLVMBuildGlobalStringPtr(self.builder,
                                                   CString::new(s.as_str()).unwrap().as_ptr(),
                                                   CString::new("str").unwrap().as_ptr()),
                          self.type_to_llvmty(&Type::Ptr(Rc::new(Type::Char(Sign::Signed))))),
         Some(Type::Ptr(Rc::new(Type::Char(Sign::Signed)))))
    }
}
<commit_msg>global variable decl, load supported<commit_after>extern crate llvm_sys as llvm;

// use std::mem;
use std::ffi::CString;
use std::ptr;
use std::rc::Rc;
use std;
use std::collections::HashMap;

use self::llvm::core::*;
use self::llvm::prelude::*;

// use parser;
// use lexer;
use node;
use types::{Type, Sign};
use error;

pub struct Codegen {
    context: LLVMContextRef,
    module: LLVMModuleRef,
    builder: LLVMBuilderRef,
    global_varmap: HashMap<String, (Type, LLVMValueRef)>,
    local_varmap: HashMap<String, (Type, LLVMValueRef)>,
    cur_func: Option<LLVMValueRef>,
}

impl Codegen {
    pub unsafe fn new(mod_name: &str) -> Codegen {
        let c_mod_name = CString::new(mod_name).unwrap();
        Codegen {
            context: LLVMContextCreate(),
            module: LLVMModuleCreateWithNameInContext(c_mod_name.as_ptr(), LLVMContextCreate()),
            builder: LLVMCreateBuilderInContext(LLVMContextCreate()),
            global_varmap: HashMap::new(),
            local_varmap: HashMap::new(),
            cur_func: None,
        }
    }

    pub unsafe fn type_to_llvmty(&mut self, ty: &Type) -> LLVMTypeRef {
        match ty {
            &Type::Void => LLVMVoidType(),
            &Type::Char(_) => LLVMInt8Type(),
            &Type::Short(_) => LLVMInt16Type(),
            &Type::Int(_) => LLVMInt32Type(),
            &Type::Long(_) => LLVMInt32Type(),
            &Type::LLong(_) => LLVMInt64Type(),
            &Type::Float => LLVMFloatType(),
            &Type::Double => LLVMDoubleType(),
            &Type::Ptr(ref elemty) => LLVMPointerType(self.type_to_llvmty(&**elemty), 0),
            &Type::Array(ref elemty, ref size) => {
                LLVMArrayType(self.type_to_llvmty(&**elemty), *size as u32)
            }
            &Type::Func(ref ret_type, ref param_types, ref is_vararg) => {
                LLVMFunctionType(self.type_to_llvmty(&**ret_type),
                                 || -> *mut LLVMTypeRef {
                                     let mut param_llvm_types: Vec<LLVMTypeRef> = Vec::new();
                                     for param_type in &*param_types {
                                         param_llvm_types.push(self.type_to_llvmty(&param_type));
                                     }
                                     param_llvm_types.as_mut_slice().as_mut_ptr()
                                 }(),
                                 (*param_types).len() as u32,
                                 if *is_vararg { 1 } else { 0 })
            }
        }
    }

    pub unsafe fn run(&mut self, node: Vec<node::AST>) {
        for ast in node {
            self.gen(&ast);
        }

        // e.g. create func
        // let int_t = LLVMInt32TypeInContext(self.context);
        // let func_t = LLVMFunctionType(/* ret type = */
        //                               int_t,
        //                               /* param types = */
        //                               ptr::null_mut(),
        //                               /* param count = */
        //                               0,
        //                               /* is vararg? = */
        //                               0);
        // let main_func =
        //     LLVMAddFunction(self.module, CString::new("main").unwrap().as_ptr(), func_t);
        //
        // // create basic block 'entry'
        // let bb_entry = LLVMAppendBasicBlock(main_func, CString::new("entry").unwrap().as_ptr());
        //
        // LLVMPositionBuilderAtEnd(self.builder, bb_entry); // SetInsertPoint in C++
        //
        // LLVMBuildRet(self.builder, self.make_int(0, false));

        LLVMDumpModule(self.module);
    }

    pub unsafe fn write_llvmir_to_file(&mut self, filename: &str) {
        let mut errmsg = ptr::null_mut();
        if LLVMPrintModuleToFile(self.module,
                                 CString::new(filename).unwrap().as_ptr(),
                                 &mut errmsg) != 0 {
            let err = CString::from_raw(errmsg)
                .to_owned()
                .into_string()
                .unwrap();
            println!("write_to_file failed: {}", err);
            std::process::exit(-1);
        }
    }

    pub unsafe fn gen(&mut self, ast: &node::AST) -> (LLVMValueRef, Option<Type>) {
        match ast {
            &node::AST::FuncDef(ref functy, ref param_names, ref name, ref body) => {
                self.gen_func_def(functy, param_names, name, body)
            }
            &node::AST::VariableDecl(ref ty, ref name, ref init) => {
                self.gen_var_decl(ty, name, init)
            }
            &node::AST::Block(ref block) => self.gen_block(block),
            &node::AST::BinaryOp(ref lhs, ref rhs, ref op) => {
                self.gen_binary_op(&**lhs, &**rhs, &*op)
            }
            &node::AST::Variable(ref name) => self.gen_var(name),
            &node::AST::FuncCall(ref f, ref args) => self.gen_func_call(&*f, args),
            &node::AST::Return(ref ret) => {
                let (retval, _) = self.gen(ret);
                self.gen_return(retval)
            }
            &node::AST::Int(ref n) => self.make_int(*n as u64, false),
            &node::AST::Float(ref f) => self.make_double(*f),
            &node::AST::Char(ref c) => self.make_char(*c),
            &node::AST::String(ref s) => self.make_const_str(s),
            _ => {
                error::error_exit(0,
                                  format!("codegen: unknown ast (given {:?})", ast).as_str())
            }
        }
    }

    pub unsafe fn gen_func_def(&mut self,
                               functy: &Type,
                               param_names: &Vec<String>,
                               name: &String,
                               body: &Rc<node::AST>)
                               -> (LLVMValueRef, Option<Type>) {
        let func_t = self.type_to_llvmty(functy);
        let func = LLVMAddFunction(self.module,
                                   CString::new(name.as_str()).unwrap().as_ptr(),
                                   func_t);
        self.global_varmap
            .insert(name.to_string(), (functy.clone(), func));
        self.cur_func = Some(func);

        let bb_entry = LLVMAppendBasicBlock(func, CString::new("entry").unwrap().as_ptr());
        LLVMPositionBuilderAtEnd(self.builder, bb_entry);

        self.gen(&**body);

        self.cur_func = None;

        (func, None)
    }

    // TODO: only global var decl supported
    unsafe fn gen_var_decl(&mut self,
                           ty: &Type,
                           name: &String,
                           init: &Option<Rc<node::AST>>)
                           -> (LLVMValueRef, Option<Type>) {
        // is global
        if self.cur_func.is_none() {
            return self.gen_global_var_decl(ty, name, init);
        } else {
            let m = LLVMBuildAlloca(self.builder,
                                    self.type_to_llvmty(ty),
                                    CString::new(name.as_str()).unwrap().as_ptr());
            self.local_varmap
                .insert(name.to_string(), (ty.clone(), m));

            if init.is_some() {}
        }
        (ptr::null_mut(), None)
    }

    unsafe fn gen_global_var_decl(&mut self,
                                  ty: &Type,
                                  name: &String,
                                  init: &Option<Rc<node::AST>>)
                                  -> (LLVMValueRef, Option<Type>) {
        let gvar = match *ty {
            Type::Func(_, _, _) => {
                LLVMAddFunction(self.module,
                                CString::new(name.as_str()).unwrap().as_ptr(),
                                self.type_to_llvmty(ty))
            }
            _ => {
                LLVMAddGlobal(self.module,
                              self.type_to_llvmty(ty),
                              CString::new(name.as_str()).unwrap().as_ptr())
            }
        };
        self.global_varmap
            .insert(name.to_string(), (ty.clone(), gvar));

        if init.is_some() {
            LLVMSetInitializer(gvar, self.gen(&*init.clone().unwrap()).0);
        }

        (gvar, None)

    }

    unsafe fn gen_block(&mut self, block: &Vec<node::AST>) -> (LLVMValueRef, Option<Type>) {
        for stmt in block {
            self.gen(stmt);
        }
        (ptr::null_mut(), None)
    }

    unsafe fn gen_binary_op(&mut self,
                            lhsast: &node::AST,
                            rhsast: &node::AST,
                            op: &node::CBinOps)
                            -> (LLVMValueRef, Option<Type>) {
        // logical operators
        match *op {
            node::CBinOps::LAnd => {}
            node::CBinOps::LOr => {}
            _ => {}
        }

        // normal binary operators
        let (lhs, lhsty) = self.gen(lhsast);
        let (rhs, _rhsty) = self.gen(rhsast);
        match lhsty.unwrap() {
            Type::Int(_) => {
                return (self.gen_int_binary_op(lhs, rhs, op), Some(Type::Int(Sign::Signed)))
            }
            _ => {}
        }
        (ptr::null_mut(), Some(Type::Void))
    }

    unsafe fn gen_int_binary_op(&mut self,
                                lhs: LLVMValueRef,
                                rhs: LLVMValueRef,
                                op: &node::CBinOps)
                                -> LLVMValueRef {
        match *op {
            node::CBinOps::Add => {
                LLVMBuildAdd(self.builder,
                             lhs,
                             rhs,
                             CString::new("add").unwrap().as_ptr())
            }
            node::CBinOps::Sub => {
                LLVMBuildAdd(self.builder,
                             lhs,
                             rhs,
                             CString::new("sub").unwrap().as_ptr())
            }
            node::CBinOps::Mul => {
                LLVMBuildMul(self.builder,
                             lhs,
                             rhs,
                             CString::new("mul").unwrap().as_ptr())
            }
            node::CBinOps::Div => {
                LLVMBuildSDiv(self.builder,
                              lhs,
                              rhs,
                              CString::new("div").unwrap().as_ptr())
            }
            node::CBinOps::Rem => {
                LLVMBuildSRem(self.builder,
                              lhs,
                              rhs,
                              CString::new("rem").unwrap().as_ptr())
            }
            _ => ptr::null_mut(),
        }
    }

    unsafe fn gen_var(&mut self, name: &String) -> (LLVMValueRef, Option<Type>) {
        if !self.global_varmap.contains_key(name.as_str()) {
            error::error_exit(0,
                              format!("gen_var: not found variable '{}'", name).as_str());
        }
        let gvar = self.global_varmap.get(name.as_str()).unwrap().1;
        (LLVMBuildLoad(self.builder, gvar, CString::new("var").unwrap().as_ptr()), None)
    }

    unsafe fn gen_func_call(&mut self,
                            fast: &node::AST,
                            args: &Vec<node::AST>)
                            -> (LLVMValueRef, Option<Type>) {
        let args_val = &mut Vec::new();
        for arg in &*args {
            args_val.push(self.gen(arg).0);
        }
        let args_val_ptr = args_val.as_mut_slice().as_mut_ptr();

        let maybe_func = match *fast {
            node::AST::Variable(ref name) => self.global_varmap.get(name),
            _ => None, // for func ptr
        };
        if maybe_func.is_none() {
            error::error_exit(0, "gen_func_call: not found func");
        }

        let func_retty = maybe_func.unwrap().0.clone();
        let func = maybe_func.unwrap().1;
        (LLVMBuildCall(self.builder,
                       func,
                       args_val_ptr,
                       args_val.len() as u32,
                       CString::new("funccall").unwrap().as_ptr()),
         Some(func_retty))
    }

    unsafe fn gen_return(&mut self, retval: LLVMValueRef) -> (LLVMValueRef, Option<Type>) {
        (LLVMBuildRet(self.builder, retval), None)
    }

    pub unsafe fn make_int(&mut self, n: u64, is_unsigned: bool) -> (LLVMValueRef, Option<Type>) {
        (LLVMConstInt(LLVMInt32Type(), n, if is_unsigned { 1 } else { 0 }),
         Some(Type::Int(Sign::Signed)))
    }
    pub unsafe fn make_char(&mut self, n: i32) -> (LLVMValueRef, Option<Type>) {
        (LLVMConstInt(LLVMInt8Type(), n as u64, 0), Some(Type::Char(Sign::Signed)))
    }
    pub unsafe fn make_float(&mut self, f: f64) -> (LLVMValueRef, Option<Type>) {
        (LLVMConstReal(LLVMFloatType(), f), Some(Type::Float))
    }
    pub unsafe fn make_double(&mut self, f: f64) -> (LLVMValueRef, Option<Type>) {
        (LLVMConstReal(LLVMDoubleType(), f), Some(Type::Double))
    }
    pub unsafe fn make_const_str(&mut self, s: &String) -> (LLVMValueRef, Option<Type>) {
        (LLVMConstBitCast(LLVMBuildGlobalStringPtr(self.builder,
                                                   CString::new(s.as_str()).unwrap().as_ptr(),
                                                   CString::new("str").unwrap().as_ptr()),
                          self.type_to_llvmty(&Type::Ptr(Rc::new(Type::Char(Sign::Signed))))),
         Some(Type::Ptr(Rc::new(Type::Char(Sign::Signed)))))
    }
}
<|endoftext|>


<commit_before>extern crate chrono;

use chrono::prelude::*;
use regex::{Regex, Captures};

#[derive(Debug)]
pub enum Command {
    BadCommand,
    OneTimeEvent(OneTimeEventImpl),
    RepetitiveEvent(RepetitiveEventImpl),
}

#[derive(Debug)]
pub struct OneTimeEventImpl{
    pub event_time : DateTime<Utc>,
    pub event_text : String,
}

#[derive(Debug)]
pub struct RepetitiveEventImpl{
    pub event_start_time : DateTime<Utc>,
    pub event_wait_time : chrono::Duration,
    pub event_text : String,
}

const MOMENT_DAY_REGEX: &str =
    r"(?:(?P<m_day>[\d]+))?(?:-(?P<m_month>[\d]+))?(?:-(?P<m_year>[\d]+))?";

const MOMENT_TIME_REGEX: &str =
    r"(?P<m_hour>[\d]+)(?:.(?P<m_minute>[\d]+))?";

// const MOMENT_REGEX: &str =
    // MOM

const DURATION_REGEX: &str =
    r"(:?(?P<d_day>[\d]*)[D|d|Д|д])?(:?(?P<d_hour>[\d]*)[H|h|Ч|ч])?(:?(?P<d_minute>[\d]*)[M|m|М|м])?(:?(?P<d_second>[\d]*)[S|s|С|с])?";

pub fn parse_command(command_line : String, user_timezone: i32) -> Command {
    let command_line = String::from(command_line.trim());
    let mut result;
    result = try_parse_for(&command_line);
    if result.is_some() {
        return result.unwrap();
    }

    result = try_parse_at(&command_line, user_timezone);
    if result.is_some() {
        return result.unwrap();
    }

    result = try_parse_rep(&command_line, user_timezone);
    if result.is_some() {
        return result.unwrap();
    }

    warn!("parse_command: line {} doesn't match any regex", command_line);
    Command::BadCommand
}

fn try_parse_at(command_line : &String, user_timezone: i32) -> Option<Command>{
    // let reg = String::from(r"^(at|в)\s*") + MOMENT_REGEX + r" (?P<main_text>.*)";
    let reg = String::from(r"^") + MOMENT_DAY_REGEX + r"\s*(at|в)\s*" + MOMENT_TIME_REGEX + r" (?P<main_text>.*)";
    let time_format = Regex::new(&reg[..]).unwrap();

    let date_captures = time_format.captures(command_line);

    if date_captures.is_none() {
        return None;
    }
    let date_captures = date_captures.unwrap();
    let text = date_captures.name("main_text").unwrap().as_str();

    if let Some(t) = get_datetime_from_capture(&date_captures, user_timezone){
        return Some(Command::OneTimeEvent
                (OneTimeEventImpl
                    { event_text : String::from(text)
                    , event_time : t}
                ));
    }
    None
}

fn try_parse_for(command_line : &String) -> Option<Command>{
    let reg = String::from("^") + DURATION_REGEX + r"(?P<divider> )(?P<main_text>.*)";
    let reg = Regex::new(&reg[..]).unwrap();

    let capture = reg.captures(command_line);
    if capture.is_none(){
        return None;
    }
    let capture = capture.unwrap();

    let text = capture.name("main_text").unwrap().as_str();
    let dt = get_duration_from_capture(&capture);
    if dt.is_none() {
        return None;
    }

    Some(Command::OneTimeEvent(OneTimeEventImpl
            { event_text : String::from(text)
            , event_time : Utc::now() + dt.unwrap() }
        ))
}

fn try_parse_rep(command_line: &String, user_timezone: i32) -> Option<Command> {
    let reg = String::from("^") + r"rep\s*" + MOMENT_DAY_REGEX
                                + r"\s+"    + MOMENT_TIME_REGEX
                                + r"\s+"    + DURATION_REGEX
                                + r"(?P<divider> )(?P<main_text>.*)";
    let reg = Regex::new(&reg[..]).unwrap();

    let capture = reg.captures(command_line);
    if capture.is_none(){
        return None;
    }
    let capture = capture.unwrap();

    let text = capture.name("main_text").unwrap().as_str();
    let time = get_datetime_from_capture(&capture, user_timezone);
    let dt = get_duration_from_capture(&capture);
    if time.is_none() || dt.is_none() {
        return None;
    }

    Some(Command::RepetitiveEvent(RepetitiveEventImpl
        {
            event_start_time : time.unwrap(),
            event_wait_time : dt.unwrap(),
            event_text : String::from(text),
        }
    ))
}

fn get_duration_from_capture(cap: &Captures) -> Option<chrono::Duration>{
    let day:    i64 = cap.name("d_day").map_or    (0, |c| c.as_str().parse().unwrap() );
    let hour:   i64 = cap.name("d_hour").map_or   (0, |c| c.as_str().parse().unwrap() );
    let minute: i64 = cap.name("d_minute").map_or (0, |c| c.as_str().parse().unwrap() );
    let second: i64 = cap.name("d_second").map_or (0, |c| c.as_str().parse().unwrap() );

    if day == 0 && hour == 0 && minute == 0 && second == 0 {
        return None;
    }

    Some(chrono::Duration::seconds(day * (60*60*24)
                                 + hour * (60*60)
                                 + minute * 60
                                 + second))
}

fn get_datetime_from_capture(cap: &Captures, tz: i32) -> Option<DateTime<Utc>>{
    println!("UTC now: {}", Utc::now());
    let dt = chrono::Duration::seconds((tz as i64) * 60 * 60);
    let now = Utc::now() - dt;

    let day     = cap.name("m_day").map_or(now.day(),       |c| c.as_str().parse().unwrap());
    let month   = cap.name("m_month").map_or(now.month(),   |c| c.as_str().parse().unwrap());
    let year    = cap.name("m_year").map_or(now.year(),     |c| c.as_str().parse().unwrap());
    let minute  = cap.name("m_minute").map_or(now.minute(), |c| c.as_str().parse().unwrap());

    let hour    = cap.name("m_hour").unwrap().as_str().parse().unwrap();

    use chrono::offset::LocalResult::Single;
    if let Single(date) = Utc.ymd_opt(year, month, day) {
        if let Some(datetime) = date.and_hms_opt(hour, minute, 0){
            return Some(datetime + dt);
        }
    }
    None
}

//-------- TESTS ---------------------------------------------------------------

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

    #[test]
    fn parse_for_general() {
        let command = String::from("1d2h3m4s");
        let text = "some text";
        let command_text = command + " " + text;
        let dt = chrono::Duration::seconds((1 as i64) * (60*60*24)  // days
                                        + (2 as i64) * (60*60)      // hours
                                        + (3 as i64) * 60           // minutes
                                        + (4 as i64)                // seconds
                                        );
        let result = try_parse_for(&command_text);
        assert!(result.is_some());
        match result.unwrap(){
            OneTimeEvent(res) => {
                assert_eq!(res.event_text, text);
                assert!(time_moment_eq(res.event_time, Utc::now() + dt));
            },
            _ => panic!("Wrong command type")
        };
    }

    #[test]
    fn parse_for_hm() {
        let command = String::from("2h30m");
        let text = "some text";
        let command_text = command + " " + text;
        let dt = chrono::Duration::seconds((0 as i64) * (60*60*24)  // days
                                        + (2 as i64) * (60*60)      // hours
                                        + (30 as i64) * 60           // minutes
                                        + (0 as i64)                // seconds
                                        );
        let result = try_parse_for(&command_text);
        assert!(result.is_some());
        match result.unwrap(){
            OneTimeEvent(res) => {
                assert_eq!(res.event_text, text);
                assert!(time_moment_eq(res.event_time, Utc::now() + dt));
            },
            _ => panic!("Wrong command type")
        };
    }

    #[test]
    // #[ignore]
    fn parse_for_negative() {
        let command = String::from("1d-2h3m4s");
        let text = "some text";
        let command_text = command + " " + text;
        let result = try_parse_for(&command_text);
        assert!(result.is_none());
    }

    #[test]
    // #[ignore]
    fn parse_for_misspell() {
        let command = String::from("1d2j3m4s");
        let text = "some text";
        let command_text = command + " " + text;
        let result = try_parse_for(&command_text);
        assert!(result.is_none());
    }

    #[test]
    fn parse_at_tests() {
        {
            let command = String::from("24-10 at 18.30");
            let text = "some text";
            let command_text = command + " " + text;
            let now = Utc::now();
            let t = Utc.ymd(now.year(), 10, 24).and_hms(18-3, 30, 0);

            let result = try_parse_at(&command_text, -3);
            assert!(result.is_some());
            match result.unwrap(){
                OneTimeEvent(res) => {
                    assert_eq!(res.event_text, text);
                    assert_eq!(res.event_time, t);
                },
                _ => panic!("Wrong command type")
            };
        }

        {
            let command = String::from("24 at 18.30");
            let text = "some text";
            let command_text = command + " " + text;
            let now = Utc::now();
            let t = Utc.ymd(now.year(), now.month(), 24).and_hms(18-3, 30, 0);

            let result = try_parse_at(&command_text, -3);
            assert!(result.is_some());
            match result.unwrap(){
                OneTimeEvent(res) => {
                    assert_eq!(res.event_text, text);
                    assert_eq!(res.event_time, t);
                },
                _ => panic!("Wrong command type")
            };
        }
        {
            let command = String::from("at 18.30");
            let text = "some text";
            let command_text = command + " " + text;
            let now = Utc::now();
            let t = Utc.ymd(now.year(), now.month(), now.day()).and_hms(18, 30, 0);

            let result = try_parse_at(&command_text, 0);
            assert!(result.is_some());
            match result.unwrap(){
                OneTimeEvent(res) => {
                    assert_eq!(res.event_text, text);
                    assert_eq!(res.event_time, t);
                },
                _ => panic!("Wrong command type")
            };
        }
        {
            let command = String::from("at 18");
            let text = "some text";
            let command_text = command + " " + text;
            let now = Utc::now();
            let t = Utc.ymd(now.year(), now.month(), now.day()).and_hms(18, now.minute(), 0);

            let result = try_parse_at(&command_text, 0);
            assert!(result.is_some());
            match result.unwrap(){
                OneTimeEvent(res) => {
                    assert_eq!(res.event_text, text);
                    assert_eq!(res.event_time, t);
                },
                _ => panic!("Wrong command type")
            };
        }
    }

    #[test]
    fn parse_wrong_at_tests() {
        {
            let command = String::from("30-02 at 18");
            let text = "some text";
            let command_text = command + " " + text;

            let result = try_parse_at(&command_text, -3);
            assert!(result.is_none());
        }
        {
            let command = String::from("30-20 at 18");
            let text = "some text";
            let command_text = command + " " + text;

            let result = try_parse_at(&command_text, -3);
            assert!(result.is_none());
        }
        {
            let command = String::from("at 25");
            let text = "some text";
            let command_text = command + " " + text;

            let result = try_parse_at(&command_text, -3);
            assert!(result.is_none());
        }
        {
            let command = String::from("at 23.60");
            let text = "some text";
            let command_text = command + " " + text;

            let result = try_parse_at(&command_text, -3);
            assert!(result.is_none());
        }
    }

    #[test]
    fn parse_rep_general() {
        let command = String::from("rep 06-10 10.00 5m");
        let text = "test rep";
        let command_text = command + " " + text;
        let now = Utc::now();
        let t = Utc.ymd(now.year(), 10, 6).and_hms(10-3, 0, 0);
        let dt = chrono::Duration::seconds((0 as i64) * (60*60*24)  // days
                                        + (0 as i64) * (60*60)      // hours
                                        + (5 as i64) * 60           // minutes
                                        + (0 as i64)                // seconds
                                        );
        let result = try_parse_rep(&command_text, -3);
        assert!(result.is_some());
        match result.unwrap(){
            RepetitiveEvent(res) => {
                assert_eq!(res.event_start_time, t);
                assert_eq!(res.event_wait_time, dt);
                assert_eq!(res.event_text, text);
            },
            _ => panic!("Wrong command type")
        };
    }

    fn time_moment_eq(t1: DateTime<Utc>, t2: DateTime<Utc>) -> bool{
        t1.signed_duration_since(t2).num_milliseconds().abs() < 100
    }
}<commit_msg>[#43] at command: set minutes to zero when specify only hours<commit_after>extern crate chrono;

use chrono::prelude::*;
use regex::{Regex, Captures};

#[derive(Debug)]
pub enum Command {
    BadCommand,
    OneTimeEvent(OneTimeEventImpl),
    RepetitiveEvent(RepetitiveEventImpl),
}

#[derive(Debug)]
pub struct OneTimeEventImpl{
    pub event_time : DateTime<Utc>,
    pub event_text : String,
}

#[derive(Debug)]
pub struct RepetitiveEventImpl{
    pub event_start_time : DateTime<Utc>,
    pub event_wait_time : chrono::Duration,
    pub event_text : String,
}

const MOMENT_DAY_REGEX: &str =
    r"(?:(?P<m_day>[\d]+))?(?:-(?P<m_month>[\d]+))?(?:-(?P<m_year>[\d]+))?";

const MOMENT_TIME_REGEX: &str =
    r"(?P<m_hour>[\d]+)(?:.(?P<m_minute>[\d]+))?";

// const MOMENT_REGEX: &str =
    // MOM

const DURATION_REGEX: &str =
    r"(:?(?P<d_day>[\d]*)[D|d|Д|д])?(:?(?P<d_hour>[\d]*)[H|h|Ч|ч])?(:?(?P<d_minute>[\d]*)[M|m|М|м])?(:?(?P<d_second>[\d]*)[S|s|С|с])?";

pub fn parse_command(command_line : String, user_timezone: i32) -> Command {
    let command_line = String::from(command_line.trim());
    let mut result;
    result = try_parse_for(&command_line);
    if result.is_some() {
        return result.unwrap();
    }

    result = try_parse_at(&command_line, user_timezone);
    if result.is_some() {
        return result.unwrap();
    }

    result = try_parse_rep(&command_line, user_timezone);
    if result.is_some() {
        return result.unwrap();
    }

    warn!("parse_command: line {} doesn't match any regex", command_line);
    Command::BadCommand
}

fn try_parse_at(command_line : &String, user_timezone: i32) -> Option<Command>{
    // let reg = String::from(r"^(at|в)\s*") + MOMENT_REGEX + r" (?P<main_text>.*)";
    let reg = String::from(r"^") + MOMENT_DAY_REGEX + r"\s*(at|в)\s*" + MOMENT_TIME_REGEX + r" (?P<main_text>.*)";
    let time_format = Regex::new(&reg[..]).unwrap();

    let date_captures = time_format.captures(command_line);

    if date_captures.is_none() {
        return None;
    }
    let date_captures = date_captures.unwrap();
    let text = date_captures.name("main_text").unwrap().as_str();

    if let Some(t) = get_datetime_from_capture(&date_captures, user_timezone){
        return Some(Command::OneTimeEvent
                (OneTimeEventImpl
                    { event_text : String::from(text)
                    , event_time : t}
                ));
    }
    None
}

fn try_parse_for(command_line : &String) -> Option<Command>{
    let reg = String::from("^") + DURATION_REGEX + r"(?P<divider> )(?P<main_text>.*)";
    let reg = Regex::new(&reg[..]).unwrap();

    let capture = reg.captures(command_line);
    if capture.is_none(){
        return None;
    }
    let capture = capture.unwrap();

    let text = capture.name("main_text").unwrap().as_str();
    let dt = get_duration_from_capture(&capture);
    if dt.is_none() {
        return None;
    }

    Some(Command::OneTimeEvent(OneTimeEventImpl
            { event_text : String::from(text)
            , event_time : Utc::now() + dt.unwrap() }
        ))
}

fn try_parse_rep(command_line: &String, user_timezone: i32) -> Option<Command> {
    let reg = String::from("^") + r"rep\s*" + MOMENT_DAY_REGEX
                                + r"\s+"    + MOMENT_TIME_REGEX
                                + r"\s+"    + DURATION_REGEX
                                + r"(?P<divider> )(?P<main_text>.*)";
    let reg = Regex::new(&reg[..]).unwrap();

    let capture = reg.captures(command_line);
    if capture.is_none(){
        return None;
    }
    let capture = capture.unwrap();

    let text = capture.name("main_text").unwrap().as_str();
    let time = get_datetime_from_capture(&capture, user_timezone);
    let dt = get_duration_from_capture(&capture);
    if time.is_none() || dt.is_none() {
        return None;
    }

    Some(Command::RepetitiveEvent(RepetitiveEventImpl
        {
            event_start_time : time.unwrap(),
            event_wait_time : dt.unwrap(),
            event_text : String::from(text),
        }
    ))
}

fn get_duration_from_capture(cap: &Captures) -> Option<chrono::Duration>{
    let day:    i64 = cap.name("d_day").map_or    (0, |c| c.as_str().parse().unwrap() );
    let hour:   i64 = cap.name("d_hour").map_or   (0, |c| c.as_str().parse().unwrap() );
    let minute: i64 = cap.name("d_minute").map_or (0, |c| c.as_str().parse().unwrap() );
    let second: i64 = cap.name("d_second").map_or (0, |c| c.as_str().parse().unwrap() );

    if day == 0 && hour == 0 && minute == 0 && second == 0 {
        return None;
    }

    Some(chrono::Duration::seconds(day * (60*60*24)
                                 + hour * (60*60)
                                 + minute * 60
                                 + second))
}

fn get_datetime_from_capture(cap: &Captures, tz: i32) -> Option<DateTime<Utc>>{
    println!("UTC now: {}", Utc::now());
    let dt = chrono::Duration::seconds((tz as i64) * 60 * 60);
    let now = Utc::now() - dt;

    let day     = cap.name("m_day").map_or(now.day(),       |c| c.as_str().parse().unwrap());
    let month   = cap.name("m_month").map_or(now.month(),   |c| c.as_str().parse().unwrap());
    let year    = cap.name("m_year").map_or(now.year(),     |c| c.as_str().parse().unwrap());
    let minute  = cap.name("m_minute").map_or(0,            |c| c.as_str().parse().unwrap());

    let hour    = cap.name("m_hour").unwrap().as_str().parse().unwrap();

    use chrono::offset::LocalResult::Single;
    if let Single(date) = Utc.ymd_opt(year, month, day) {
        if let Some(datetime) = date.and_hms_opt(hour, minute, 0){
            return Some(datetime + dt);
        }
    }
    None
}

//-------- TESTS ---------------------------------------------------------------

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

    #[test]
    fn parse_for_general() {
        let command = String::from("1d2h3m4s");
        let text = "some text";
        let command_text = command + " " + text;
        let dt = chrono::Duration::seconds((1 as i64) * (60*60*24)  // days
                                        + (2 as i64) * (60*60)      // hours
                                        + (3 as i64) * 60           // minutes
                                        + (4 as i64)                // seconds
                                        );
        let result = try_parse_for(&command_text);
        assert!(result.is_some());
        match result.unwrap(){
            OneTimeEvent(res) => {
                assert_eq!(res.event_text, text);
                assert!(time_moment_eq(res.event_time, Utc::now() + dt));
            },
            _ => panic!("Wrong command type")
        };
    }

    #[test]
    fn parse_for_hm() {
        let command = String::from("2h30m");
        let text = "some text";
        let command_text = command + " " + text;
        let dt = chrono::Duration::seconds((0 as i64) * (60*60*24)  // days
                                        + (2 as i64) * (60*60)      // hours
                                        + (30 as i64) * 60           // minutes
                                        + (0 as i64)                // seconds
                                        );
        let result = try_parse_for(&command_text);
        assert!(result.is_some());
        match result.unwrap(){
            OneTimeEvent(res) => {
                assert_eq!(res.event_text, text);
                assert!(time_moment_eq(res.event_time, Utc::now() + dt));
            },
            _ => panic!("Wrong command type")
        };
    }

    #[test]
    // #[ignore]
    fn parse_for_negative() {
        let command = String::from("1d-2h3m4s");
        let text = "some text";
        let command_text = command + " " + text;
        let result = try_parse_for(&command_text);
        assert!(result.is_none());
    }

    #[test]
    // #[ignore]
    fn parse_for_misspell() {
        let command = String::from("1d2j3m4s");
        let text = "some text";
        let command_text = command + " " + text;
        let result = try_parse_for(&command_text);
        assert!(result.is_none());
    }

    #[test]
    fn parse_at_tests() {
        {
            let command = String::from("24-10 at 18.30");
            let text = "some text";
            let command_text = command + " " + text;
            let now = Utc::now();
            let t = Utc.ymd(now.year(), 10, 24).and_hms(18-3, 30, 0);

            let result = try_parse_at(&command_text, -3);
            assert!(result.is_some());
            match result.unwrap(){
                OneTimeEvent(res) => {
                    assert_eq!(res.event_text, text);
                    assert_eq!(res.event_time, t);
                },
                _ => panic!("Wrong command type")
            };
        }

        {
            let command = String::from("24 at 18.30");
            let text = "some text";
            let command_text = command + " " + text;
            let now = Utc::now();
            let t = Utc.ymd(now.year(), now.month(), 24).and_hms(18-3, 30, 0);

            let result = try_parse_at(&command_text, -3);
            assert!(result.is_some());
            match result.unwrap(){
                OneTimeEvent(res) => {
                    assert_eq!(res.event_text, text);
                    assert_eq!(res.event_time, t);
                },
                _ => panic!("Wrong command type")
            };
        }
        {
            let command = String::from("at 18.30");
            let text = "some text";
            let command_text = command + " " + text;
            let now = Utc::now();
            let t = Utc.ymd(now.year(), now.month(), now.day()).and_hms(18, 30, 0);

            let result = try_parse_at(&command_text, 0);
            assert!(result.is_some());
            match result.unwrap(){
                OneTimeEvent(res) => {
                    assert_eq!(res.event_text, text);
                    assert_eq!(res.event_time, t);
                },
                _ => panic!("Wrong command type")
            };
        }
        {
            let command = String::from("at 18");
            let text = "some text";
            let command_text = command + " " + text;
            let now = Utc::now();
            let t = Utc.ymd(now.year(), now.month(), now.day()).and_hms(18, now.minute(), 0);

            let result = try_parse_at(&command_text, 0);
            assert!(result.is_some());
            match result.unwrap(){
                OneTimeEvent(res) => {
                    assert_eq!(res.event_text, text);
                    assert_eq!(res.event_time, t);
                },
                _ => panic!("Wrong command type")
            };
        }
    }

    #[test]
    fn parse_wrong_at_tests() {
        {
            let command = String::from("30-02 at 18");
            let text = "some text";
            let command_text = command + " " + text;

            let result = try_parse_at(&command_text, -3);
            assert!(result.is_none());
        }
        {
            let command = String::from("30-20 at 18");
            let text = "some text";
            let command_text = command + " " + text;

            let result = try_parse_at(&command_text, -3);
            assert!(result.is_none());
        }
        {
            let command = String::from("at 25");
            let text = "some text";
            let command_text = command + " " + text;

            let result = try_parse_at(&command_text, -3);
            assert!(result.is_none());
        }
        {
            let command = String::from("at 23.60");
            let text = "some text";
            let command_text = command + " " + text;

            let result = try_parse_at(&command_text, -3);
            assert!(result.is_none());
        }
    }

    #[test]
    fn parse_rep_general() {
        let command = String::from("rep 06-10 10.00 5m");
        let text = "test rep";
        let command_text = command + " " + text;
        let now = Utc::now();
        let t = Utc.ymd(now.year(), 10, 6).and_hms(10-3, 0, 0);
        let dt = chrono::Duration::seconds((0 as i64) * (60*60*24)  // days
                                        + (0 as i64) * (60*60)      // hours
                                        + (5 as i64) * 60           // minutes
                                        + (0 as i64)                // seconds
                                        );
        let result = try_parse_rep(&command_text, -3);
        assert!(result.is_some());
        match result.unwrap(){
            RepetitiveEvent(res) => {
                assert_eq!(res.event_start_time, t);
                assert_eq!(res.event_wait_time, dt);
                assert_eq!(res.event_text, text);
            },
            _ => panic!("Wrong command type")
        };
    }

    fn time_moment_eq(t1: DateTime<Utc>, t2: DateTime<Utc>) -> bool{
        t1.signed_duration_since(t2).num_milliseconds().abs() < 100
    }
}<|endoftext|>


<commit_before>use std::collections::BTreeMap;
use std::string::ToString;
use std::default::Default;
use rustc_serialize::json::{Json,ToJson};

use parse::*;
use parse::Presence::*;
use record;
use record::{Record, PartialRecord};

// basic three-part Date type, YYYY-MM-DD
// individual components may be 0, which will present as None
// only minimal range sanity checks are done
#[derive(Clone, PartialEq, Debug)]
pub struct Date {
    pub y: Option<u16>,
    pub m: Option<u8>,
    pub d: Option<u8>,
}

impl Default for Date {
    fn default() -> Date {
        Date { y: None, m: None, d: None }
    }
}

impl ToString for Date {
    fn to_string(&self) -> String {
        format!("{:04}-{:02}-{:02}",
            match self.y {
                Some(n) => n,
                None    => 0,
            },
            match self.m {
                Some(n) => n,
                None    => 0,
            },
            match self.d {
                Some(n) => n,
                None    => 0,
            },
        )
    }
}

impl ToJson for Date {
    fn to_json(&self) -> Json {
        Json::String(self.to_string())
    }
}

impl FromJson for Date {
    fn from_json(json: &Json) -> Result<Date,ParseError> {
        match *json {
            Json::String(ref v) => {
                let (ok, err): (Vec<Result<u16,_>>,Vec<_>) = v.split('-').map(|ref s| s.parse::<u16>()).partition(|ref r| match **r { Ok(_) => true, Err(_) => false });
                if let false = err.is_empty() {
                    return Err(ParseError::InvalidStructure("Date".to_string()));
                }

                let dv: Vec<Option<u16>> = ok.into_iter().map(|n| match n.ok().unwrap() {
                    0  => None,
                    nn => Some(nn),
                }).collect();

                if let false = dv.len() == 3 {
                    return Err(ParseError::InvalidStructure("Date".to_string()));
                }

                Ok(Date {
                    y: dv[0],
                    m: try!(match dv[1] {
                        Some(n) if n > 12 => Err(ParseError::InvalidStructure("Date".to_string())),
                        Some(n) => Ok(Some(n as u8)),
                        None => Ok(None),
                    }),
                    d: try!(match dv[2] {
                        Some(n) if n > 31 => Err(ParseError::InvalidStructure("Date".to_string())),
                        Some(n) => Ok(Some(n as u8)),
                        None => Ok(None),
                    }),
                })
            },
            _ => Err(ParseError::InvalidJsonType("Date".to_string())),
        }
    }
}

// marker trait for contact types
pub trait ContactType: ToString + ToJson + FromJson + Default { }

make_prop_enum_type!(EmailType, "EmailType", Other,
    Personal => "personal",
    Work     => "work",
    Other    => "other"
);
impl ContactType for EmailType { }

make_prop_enum_type!(PhoneType, "PhoneType", Other,
    Home   => "home",
    Work   => "work",
    Mobile => "mobile",
    Fax    => "fax",
    Pager  => "pager",
    Other  => "other"
);
impl ContactType for PhoneType { }

make_prop_enum_type!(OnlineType, "OnlineType", Other,
    Uri      => "uri",
    Username => "username",
    Other    => "other"
);
impl ContactType for OnlineType { }

make_prop_enum_type!(AddressType, "AddressType", Other,
    Home    => "home",
    Work    => "work",
    Billing => "billing",
    Postal  => "postal",
    Other   => "other"
);
impl ContactType for AddressType { }

// ContactInformation is a type, a value and an optional label
#[derive(Clone, PartialEq, Debug)]
pub struct ContactInformation<T: ContactType> {
    pub typ:   T,
    pub value: String,
    pub label: Option<String>,
}

impl<T> Default for ContactInformation<T> where T: ContactType {
    fn default() -> ContactInformation<T> {
        ContactInformation { typ: T::default(), value: "".to_string(), label: None }
    }
}

impl<T> ToJson for ContactInformation<T> where T: ContactType {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::<String,Json>::new();
        self.typ.to_json_field(&mut d, "type");
        self.value.to_json_field(&mut d, "value");
        self.label.to_json_field(&mut d, "label");
        Json::Object(d)
    }
}

impl<T> FromJson for ContactInformation<T> where T: ContactType {
    fn from_json(json: &Json) -> Result<ContactInformation<T>,ParseError> {
        match *json {
            Json::Object(ref o) => {
                let mut ci = ContactInformation::<T>::default();
                ci.typ   = try!(FromJsonField::from_json_field(o, "type"));
                ci.value = try!(FromJsonField::from_json_field(o, "value"));
                ci.label = try!(FromJsonField::from_json_field(o, "label"));
                Ok(ci)
            },
            _ => Err(ParseError::InvalidJsonType("ContactInformation".to_string())),
        }
    }
}

make_prop_type!(Address, "Address",
    typ:      AddressType    => "type",
    label:    Option<String> => "label",
    street:   String         => "street",
    locality: String         => "locality",
    region:   String         => "region",
    postcode: String         => "postcode",
    country:  String         => "country"
);

make_prop_type!(File, "File",
    url:  String => "url",
    typ:  Option<String> => "type",
    name: Option<String> => "name",
    size: Option<String> => "size"
);

make_record_type!(Contact, PartialContact, "Contact",
    is_flagged:          bool                                => "isFlagged",
    avatar:              Option<File>                        => "avatar",
    prefix:              String                              => "prefix",
    first_name:          String                              => "firstName",
    last_name:           String                              => "lastName",
    suffix:              String                              => "suffix",
    nickname:            String                              => "nickname",
    birthday:            Date                                => "birthday",
    anniversary:         Date                                => "anniversary",
    company:             String                              => "company",
    department:          String                              => "department",
    job_title:           String                              => "jobTitle",
    emails:              Vec<ContactInformation<EmailType>>  => "emails",
    default_email_index: u64                                 => "defaultEmailIndex",
    phones:              Vec<ContactInformation<PhoneType>>  => "phones",
    online:              Vec<ContactInformation<OnlineType>> => "online",
    addresses:           Vec<Address>                        => "addresses",
    notes:               String                              => "notes"
);
<commit_msg>Rename contact date type, clashes with real dates I want for calendars<commit_after>use std::collections::BTreeMap;
use std::string::ToString;
use std::default::Default;
use rustc_serialize::json::{Json,ToJson};

use parse::*;
use parse::Presence::*;
use record;
use record::{Record, PartialRecord};

// basic three-part Date type, YYYY-MM-DD
// individual components may be 0, which will present as None
// only minimal range sanity checks are done
#[derive(Clone, PartialEq, Debug)]
pub struct OptionDate {
    pub y: Option<u16>,
    pub m: Option<u8>,
    pub d: Option<u8>,
}

impl Default for OptionDate {
    fn default() -> OptionDate {
        OptionDate { y: None, m: None, d: None }
    }
}

impl ToString for OptionDate {
    fn to_string(&self) -> String {
        format!("{:04}-{:02}-{:02}",
            match self.y {
                Some(n) => n,
                None    => 0,
            },
            match self.m {
                Some(n) => n,
                None    => 0,
            },
            match self.d {
                Some(n) => n,
                None    => 0,
            },
        )
    }
}

impl ToJson for OptionDate {
    fn to_json(&self) -> Json {
        Json::String(self.to_string())
    }
}

impl FromJson for OptionDate {
    fn from_json(json: &Json) -> Result<OptionDate,ParseError> {
        match *json {
            Json::String(ref v) => {
                let (ok, err): (Vec<Result<u16,_>>,Vec<_>) = v.split('-').map(|ref s| s.parse::<u16>()).partition(|ref r| match **r { Ok(_) => true, Err(_) => false });
                if let false = err.is_empty() {
                    return Err(ParseError::InvalidStructure("OptionDate".to_string()));
                }

                let dv: Vec<Option<u16>> = ok.into_iter().map(|n| match n.ok().unwrap() {
                    0  => None,
                    nn => Some(nn),
                }).collect();

                if let false = dv.len() == 3 {
                    return Err(ParseError::InvalidStructure("OptionDate".to_string()));
                }

                Ok(OptionDate {
                    y: dv[0],
                    m: try!(match dv[1] {
                        Some(n) if n > 12 => Err(ParseError::InvalidStructure("OptionDate".to_string())),
                        Some(n) => Ok(Some(n as u8)),
                        None => Ok(None),
                    }),
                    d: try!(match dv[2] {
                        Some(n) if n > 31 => Err(ParseError::InvalidStructure("OptionDate".to_string())),
                        Some(n) => Ok(Some(n as u8)),
                        None => Ok(None),
                    }),
                })
            },
            _ => Err(ParseError::InvalidJsonType("OptionDate".to_string())),
        }
    }
}

// marker trait for contact types
pub trait ContactType: ToString + ToJson + FromJson + Default { }

make_prop_enum_type!(EmailType, "EmailType", Other,
    Personal => "personal",
    Work     => "work",
    Other    => "other"
);
impl ContactType for EmailType { }

make_prop_enum_type!(PhoneType, "PhoneType", Other,
    Home   => "home",
    Work   => "work",
    Mobile => "mobile",
    Fax    => "fax",
    Pager  => "pager",
    Other  => "other"
);
impl ContactType for PhoneType { }

make_prop_enum_type!(OnlineType, "OnlineType", Other,
    Uri      => "uri",
    Username => "username",
    Other    => "other"
);
impl ContactType for OnlineType { }

make_prop_enum_type!(AddressType, "AddressType", Other,
    Home    => "home",
    Work    => "work",
    Billing => "billing",
    Postal  => "postal",
    Other   => "other"
);
impl ContactType for AddressType { }

// ContactInformation is a type, a value and an optional label
#[derive(Clone, PartialEq, Debug)]
pub struct ContactInformation<T: ContactType> {
    pub typ:   T,
    pub value: String,
    pub label: Option<String>,
}

impl<T> Default for ContactInformation<T> where T: ContactType {
    fn default() -> ContactInformation<T> {
        ContactInformation { typ: T::default(), value: "".to_string(), label: None }
    }
}

impl<T> ToJson for ContactInformation<T> where T: ContactType {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::<String,Json>::new();
        self.typ.to_json_field(&mut d, "type");
        self.value.to_json_field(&mut d, "value");
        self.label.to_json_field(&mut d, "label");
        Json::Object(d)
    }
}

impl<T> FromJson for ContactInformation<T> where T: ContactType {
    fn from_json(json: &Json) -> Result<ContactInformation<T>,ParseError> {
        match *json {
            Json::Object(ref o) => {
                let mut ci = ContactInformation::<T>::default();
                ci.typ   = try!(FromJsonField::from_json_field(o, "type"));
                ci.value = try!(FromJsonField::from_json_field(o, "value"));
                ci.label = try!(FromJsonField::from_json_field(o, "label"));
                Ok(ci)
            },
            _ => Err(ParseError::InvalidJsonType("ContactInformation".to_string())),
        }
    }
}

make_prop_type!(Address, "Address",
    typ:      AddressType    => "type",
    label:    Option<String> => "label",
    street:   String         => "street",
    locality: String         => "locality",
    region:   String         => "region",
    postcode: String         => "postcode",
    country:  String         => "country"
);

make_prop_type!(File, "File",
    url:  String => "url",
    typ:  Option<String> => "type",
    name: Option<String> => "name",
    size: Option<String> => "size"
);

make_record_type!(Contact, PartialContact, "Contact",
    is_flagged:          bool                                => "isFlagged",
    avatar:              Option<File>                        => "avatar",
    prefix:              String                              => "prefix",
    first_name:          String                              => "firstName",
    last_name:           String                              => "lastName",
    suffix:              String                              => "suffix",
    nickname:            String                              => "nickname",
    birthday:            OptionDate                          => "birthday",
    anniversary:         OptionDate                          => "anniversary",
    company:             String                              => "company",
    department:          String                              => "department",
    job_title:           String                              => "jobTitle",
    emails:              Vec<ContactInformation<EmailType>>  => "emails",
    default_email_index: u64                                 => "defaultEmailIndex",
    phones:              Vec<ContactInformation<PhoneType>>  => "phones",
    online:              Vec<ContactInformation<OnlineType>> => "online",
    addresses:           Vec<Address>                        => "addresses",
    notes:               String                              => "notes"
);
<|endoftext|>


<commit_before>use serialize::json::{Json, ToJson, Object};
use regex::Regex;
use std::collections::VecDeque;

pub struct Context {
    data: Json,
    default: Json,
    array_index_matcher: Regex,
}

#[inline]
fn parse_json_visitor<'a>(path_stack: &mut VecDeque<&'a str>, path: &'a str) {
    for p in (*path).split('/') {
        match p {
            "." | "" => {
                continue;
            }
            ".." => {
                path_stack.pop_back();
            }
            _ => {
                for dot_p in p.split('.') {
//                    if dot_p != "this" {
                        path_stack.push_back(dot_p);
//                    }
                }
            }
        }
    }
}

impl Context {
    pub fn null() -> Context {
        Context {
            data: Json::Null,
            default: Json::Null,
            array_index_matcher: Regex::new(r"\[\d+\]$").unwrap(),
        }
    }

    pub fn wraps<T: ToJson>(e: &T) -> Context {
        Context {
            data: e.to_json(),
            default: Json::Null,
            array_index_matcher: Regex::new(r"\[\d+\]$").unwrap(),
        }
    }

    pub fn navigate(&self, path: &str, relative_path: &str) -> &Json {
        let mut path_stack :VecDeque<&str> = VecDeque::new();
        parse_json_visitor(&mut path_stack, path);
        parse_json_visitor(&mut path_stack, relative_path);

        let paths :Vec<&str> = path_stack.iter().map(|x| *x).collect();
        let mut data: &Json = &self.data;
        for p in paths.iter() {
            match self.array_index_matcher.find(*p) {
                Some((s, _)) => {
                    let arr = &p[..s];
                    let idx = &p[s+1 .. p.len()-1];

                    let root = if arr == "this" {
                        Some(data)
                    } else {
                        data.find(arr)
                    };

                    data = match root {
                        Some(d) => {
                            if let Json::Array(ref l) = *d {
                                match idx.parse::<usize>() {
                                    Ok(idx_u) => l.get(idx_u).unwrap(),
                                    Err(_) => &self.default
                                }
                            } else {
                                &self.default
                            }
                        },
                        None => &self.default
                    };
                },
                None => {
                    data = match data.find(*p) {
                        Some(d) => d,
                        None => {
                            println!("{}", *p);
                            if *p == "this" {
                                data
                            } else {
                                &self.default
                            }
                        }
                    };
                }
            }
        }
        data
    }
}

pub trait JsonRender {
    fn render(&self) -> String;
}

pub trait JsonTruthy {
    fn is_truthy(&self) -> bool;
}

impl JsonRender for Json {
    fn render(&self) -> String {
        if let Json::String(_) = *self {
            self.as_string().unwrap_or("").to_string()
        } else {
            format!("{}", *self)
        }
    }
}

impl JsonTruthy for Json {
    fn is_truthy(&self) -> bool {
        match *self {
            Json::I64(i) => i != 0,
            Json::U64(i) => i != 0,
            Json::F64(i) => i != 0.0 || ! i.is_nan(),
            Json::Boolean (ref i) => *i,
            Json::Null => false,
            Json::String (ref i) => i.len() > 0,
            Json::Array (ref i) => i.len() > 0,
            Json::Object (ref i) => i.len() > 0
        }
    }
}

#[cfg(test)]
mod test {
    use context::{JsonRender, Context};
    use std::collections::BTreeMap;
    use serialize::json::{Json, ToJson};

    #[test]
    fn test_json_render() {
        let raw = "
Hello world
\n Json {
            let mut m = BTreeMap::new();
            m.insert("city".to_string(), self.city.to_json());
            m.insert("country".to_string(), self.country.to_json());
            Json::Object(m)
        }
    }

    struct Person {
        name: String,
        age: i16,
        addr: Address,
        titles: Vec<String>
    }

    impl ToJson for Person {
        fn to_json(&self) -> Json {
            let mut m = BTreeMap::new();
            m.insert("name".to_string(), self.name.to_json());
            m.insert("age".to_string(), self.age.to_json());
            m.insert("addr".to_string(), self.addr.to_json());
            m.insert("titles".to_string(), self.titles.to_json());
            Json::Object(m)
        }
    }

    #[test]
    fn test_render() {
        let v = "hello";
        let ctx = Context::wraps(&v.to_string());
        let this = "this".to_string();
        assert_eq!(ctx.navigate(&this, &this).render(), v.to_string());
    }

    #[test]
    fn test_navigation() {
        let addr = Address {
            city: "Beijing".to_string(),
            country: "China".to_string(),
        };

        let person = Person {
            name: "Ning Sun".to_string(),
            age: 27,
            addr: addr,
            titles: vec!["programmer".to_string(),
                         "cartographier".to_string()]
        };

        let ctx = Context::wraps(&person);
        let this = "this".to_string();
        let that = "./name/../addr/country".to_string();

        assert_eq!(ctx.navigate(&this, &that).render(), "China".to_string());

        let v = true;
        let ctx2 = Context::wraps(&v);
        assert_eq!(ctx2.navigate(&"this".to_string(), &"this".to_string()).render(), "true".to_string());

        let this2 = "this".to_string();
        let that2 = "titles[0]".to_string();
        assert_eq!(ctx.navigate(&this2, &that2).render(), "programmer".to_string());
    }

    #[test]
    fn test_this() {
        let mut map_with_this = BTreeMap::new();
        map_with_this.insert("this".to_string(), "hello".to_json());
        map_with_this.insert("age".to_string(), 5usize.to_json());
        let ctx1 = Context::wraps(&map_with_this);

        let mut map_without_this = BTreeMap::new();
        map_without_this.insert("age".to_string(), 4usize.to_json());
        let ctx2 = Context::wraps(&map_without_this);

        let this = "this".to_owned();
        assert_eq!(ctx1.navigate(&this, &this).render(), "hello".to_owned());
        assert_eq!(ctx2.navigate(&this, &"age".to_owned()).render(), "4".to_owned());
    }
}
<commit_msg>(feat) added merge_hash for context<commit_after>use serialize::json::{Json, ToJson, Object};
use regex::Regex;
use std::collections::{VecDeque, BTreeMap};

#[derive(Debug)]
pub struct Context {
    data: Json,
    default: Json,
    array_index_matcher: Regex,
}

#[inline]
fn parse_json_visitor<'a>(path_stack: &mut VecDeque<&'a str>, path: &'a str) {
    for p in (*path).split('/') {
        match p {
            "." | "" => {
                continue;
            }
            ".." => {
                path_stack.pop_back();
            }
            _ => {
                for dot_p in p.split('.') {
//                    if dot_p != "this" {
                        path_stack.push_back(dot_p);
//                    }
                }
            }
        }
    }
}

fn merge_json(base: &Json, addition: &Object) -> Json {
    let mut base_map = match base {
        &Json::Object(ref m) => {
            m.clone()
        }
        _ => {
            let mut map: BTreeMap<String, Json> = BTreeMap::new();
            map.insert("this".to_owned(), base.clone());
            map
        }
    };

    for (k, v) in addition.iter() {
        base_map.insert(k.clone(), v.clone());
    }

    Json::Object(base_map)
}

impl Context {
    pub fn null() -> Context {
        Context {
            data: Json::Null,
            default: Json::Null,
            array_index_matcher: Regex::new(r"\[\d+\]$").unwrap(),
        }
    }

    pub fn wraps<T: ToJson>(e: &T) -> Context {
        Context {
            data: e.to_json(),
            default: Json::Null,
            array_index_matcher: Regex::new(r"\[\d+\]$").unwrap(),
        }
    }

    pub fn merge_hash(&self, hash: &Object) -> Context {
        let new_data = merge_json(&self.data, hash);
        Context {
            data: new_data,
            default: Json::Null,
            array_index_matcher: Regex::new(r"\[\d+\]$").unwrap()
        }
    }

    pub fn navigate(&self, path: &str, relative_path: &str) -> &Json {
        let mut path_stack :VecDeque<&str> = VecDeque::new();
        parse_json_visitor(&mut path_stack, path);
        parse_json_visitor(&mut path_stack, relative_path);

        let paths :Vec<&str> = path_stack.iter().map(|x| *x).collect();
        let mut data: &Json = &self.data;
        for p in paths.iter() {
            match self.array_index_matcher.find(*p) {
                Some((s, _)) => {
                    let arr = &p[..s];
                    let idx = &p[s+1 .. p.len()-1];

                    let root = if arr == "this" {
                        Some(data)
                    } else {
                        data.find(arr)
                    };

                    data = match root {
                        Some(d) => {
                            if let Json::Array(ref l) = *d {
                                match idx.parse::<usize>() {
                                    Ok(idx_u) => l.get(idx_u).unwrap(),
                                    Err(_) => &self.default
                                }
                            } else {
                                &self.default
                            }
                        },
                        None => &self.default
                    };
                },
                None => {
                    data = match data.find(*p) {
                        Some(d) => d,
                        None => {
                            if *p == "this" {
                                data
                            } else {
                                &self.default
                            }
                        }
                    };
                }
            }
        }
        data
    }
}

pub trait JsonRender {
    fn render(&self) -> String;
}

pub trait JsonTruthy {
    fn is_truthy(&self) -> bool;
}

impl JsonRender for Json {
    fn render(&self) -> String {
        if let Json::String(_) = *self {
            self.as_string().unwrap_or("").to_string()
        } else {
            format!("{}", *self)
        }
    }
}

impl JsonTruthy for Json {
    fn is_truthy(&self) -> bool {
        match *self {
            Json::I64(i) => i != 0,
            Json::U64(i) => i != 0,
            Json::F64(i) => i != 0.0 || ! i.is_nan(),
            Json::Boolean (ref i) => *i,
            Json::Null => false,
            Json::String (ref i) => i.len() > 0,
            Json::Array (ref i) => i.len() > 0,
            Json::Object (ref i) => i.len() > 0
        }
    }
}

#[cfg(test)]
mod test {
    use context::{JsonRender, Context};
    use std::collections::BTreeMap;
    use serialize::json::{Json, ToJson};

    #[test]
    fn test_json_render() {
        let raw = "
Hello world
\n Json {
            let mut m = BTreeMap::new();
            m.insert("city".to_string(), self.city.to_json());
            m.insert("country".to_string(), self.country.to_json());
            Json::Object(m)
        }
    }

    struct Person {
        name: String,
        age: i16,
        addr: Address,
        titles: Vec<String>
    }

    impl ToJson for Person {
        fn to_json(&self) -> Json {
            let mut m = BTreeMap::new();
            m.insert("name".to_string(), self.name.to_json());
            m.insert("age".to_string(), self.age.to_json());
            m.insert("addr".to_string(), self.addr.to_json());
            m.insert("titles".to_string(), self.titles.to_json());
            Json::Object(m)
        }
    }

    #[test]
    fn test_render() {
        let v = "hello";
        let ctx = Context::wraps(&v.to_string());
        let this = "this".to_string();
        assert_eq!(ctx.navigate(&this, &this).render(), v.to_string());
    }

    #[test]
    fn test_navigation() {
        let addr = Address {
            city: "Beijing".to_string(),
            country: "China".to_string(),
        };

        let person = Person {
            name: "Ning Sun".to_string(),
            age: 27,
            addr: addr,
            titles: vec!["programmer".to_string(),
                         "cartographier".to_string()]
        };

        let ctx = Context::wraps(&person);
        let this = "this".to_string();
        let that = "./name/../addr/country".to_string();

        assert_eq!(ctx.navigate(&this, &that).render(), "China".to_string());

        let v = true;
        let ctx2 = Context::wraps(&v);
        assert_eq!(ctx2.navigate(&"this".to_string(), &"this".to_string()).render(), "true".to_string());

        let this2 = "this".to_string();
        let that2 = "titles[0]".to_string();
        assert_eq!(ctx.navigate(&this2, &that2).render(), "programmer".to_string());
    }

    #[test]
    fn test_this() {
        let mut map_with_this = BTreeMap::new();
        map_with_this.insert("this".to_string(), "hello".to_json());
        map_with_this.insert("age".to_string(), 5usize.to_json());
        let ctx1 = Context::wraps(&map_with_this);

        let mut map_without_this = BTreeMap::new();
        map_without_this.insert("age".to_string(), 4usize.to_json());
        let ctx2 = Context::wraps(&map_without_this);

        let this = "this".to_owned();
        assert_eq!(ctx1.navigate(&this, &this).render(), "hello".to_owned());
        assert_eq!(ctx2.navigate(&this, &"age".to_owned()).render(), "4".to_owned());
    }

    #[test]
    fn test_merge_hash() {
        let mut map = BTreeMap::new();
        map.insert("age".to_string(), 4usize.to_json());
        let ctx1 = Context::wraps(&map);

        let s = "hello".to_owned();
        let ctx2 = Context::wraps(&s);

        let mut hash = BTreeMap::new();
        hash.insert("tag".to_owned(), "h1".to_json());

        let ctx_a1 = ctx1.merge_hash(&hash);
        assert_eq!(ctx_a1.navigate(".", "age").render(), "4".to_owned());
        assert_eq!(ctx_a1.navigate(".", "tag").render(), "h1".to_owned());

        let ctx_a2 = ctx2.merge_hash(&hash);
        assert_eq!(ctx_a2.navigate(".", "this").render(), "hello".to_owned());
        assert_eq!(ctx_a2.navigate(".", "tag").render(), "h1".to_owned());
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::default::Default;
use std::rc::Rc;
use std::mem::replace;
use joker::word::Name;
use joker::track::span;
use easter::stmt::Stmt;
use easter::id::Id;
use result::Result;
use parser::{Parser, Strict};

pub trait WithContext {
    fn with_labels<F>(&mut self, mut labels: Vec<Id>, label_type: LabelType, op: F) -> Result<Stmt>
      where F: FnOnce(&mut Self) -> Result<Stmt>;
    fn allow_in<F, T>(&mut self, allow_in: bool, parse: F) -> Result<T>
      where F: FnOnce(&mut Self) -> Result<T>;
}

impl<I: Iterator<Item=char>> WithContext for Parser<I> {
    fn with_labels<F>(&mut self, mut labels: Vec<Id>, label_type: LabelType, op: F) -> Result<Stmt>
      where F: FnOnce(&mut Self) -> Result<Stmt>
    {
        let mut label_strings = Vec::new();
        for id in labels.iter() {
            let label = Rc::new(id.name.clone());
            self.context.labels.insert(label.clone(), label_type);
            label_strings.push(label);
        }
        let result = op(self);
        for label in label_strings {
            self.context.labels.remove(&label);
        }
        let mut body = result?;
        labels.reverse();
        for id in labels {
            let location = span(&id, &body);
            body = Stmt::Label(location, id, Box::new(body));
        }
        Ok(body)
    }

    fn allow_in<F, T>(&mut self, allow_in: bool, parse: F) -> Result<T>
      where F: FnOnce(&mut Self) -> Result<T>
    {
        let allow_in = replace(&mut self.context.allow_in, allow_in);
        let result = parse(self);
        replace(&mut self.context.allow_in, allow_in);
        result
    }
}

#[derive(Debug, Eq, PartialEq, Copy, Clone)]
pub enum LabelType {
    Statement,
    Iteration
}

#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
pub enum Goal {
    Script(Strict),
    Module,
    Unknown
}

impl Default for Goal {
    fn default() -> Goal { Goal::Unknown }
}

pub trait Mode {
    fn strict(&self) -> Strict;
    fn definitely_script(&self) -> bool;
    fn definitely_strict(&self) -> bool;
    fn definitely_sloppy(&self) -> bool;
    fn definitely_module(&self) -> bool;
}

impl Mode for Goal {
    fn strict(&self) -> Strict {
        match *self {
            Goal::Script(strict) => strict,
            Goal::Module         => Strict::Yes,
            Goal::Unknown        => Strict::Unknown
        }
    }

    fn definitely_script(&self) -> bool {
        match *self {
            Goal::Script(_) => true,
            _ => false
        }
    }

    fn definitely_strict(&self) -> bool {
        self.strict().definitely()
    }

    fn definitely_sloppy(&self) -> bool {
        self.strict().definitely_not()
    }

    fn definitely_module(&self) -> bool { *self == Goal::Module }
}

#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Context {
    pub function: Option<Strict>,
    pub iteration: bool,
    pub switch: bool,
    pub allow_in: bool,
    pub labels: HashMap<Rc<Name>, LabelType>
}

impl Context {
    pub fn new() -> Context {
        Context {
            function: None,
            iteration: false,
            switch: false,
            allow_in: true,
            labels: HashMap::new()
        }
    }

    pub fn new_function(inherited: Strict) -> Context {
        Context {
            function: Some(inherited),
            iteration: false,
            switch: false,
            allow_in: true,
            labels: HashMap::new()
        }
    }
}
<commit_msg>Remove `mut` specifier in trait definition as per warning<commit_after>use std::collections::HashMap;
use std::default::Default;
use std::rc::Rc;
use std::mem::replace;
use joker::word::Name;
use joker::track::span;
use easter::stmt::Stmt;
use easter::id::Id;
use result::Result;
use parser::{Parser, Strict};

pub trait WithContext {
    fn with_labels<F>(&mut self, labels: Vec<Id>, label_type: LabelType, op: F) -> Result<Stmt>
      where F: FnOnce(&mut Self) -> Result<Stmt>;
    fn allow_in<F, T>(&mut self, allow_in: bool, parse: F) -> Result<T>
      where F: FnOnce(&mut Self) -> Result<T>;
}

impl<I: Iterator<Item=char>> WithContext for Parser<I> {
    fn with_labels<F>(&mut self, mut labels: Vec<Id>, label_type: LabelType, op: F) -> Result<Stmt>
      where F: FnOnce(&mut Self) -> Result<Stmt>
    {
        let mut label_strings = Vec::new();
        for id in labels.iter() {
            let label = Rc::new(id.name.clone());
            self.context.labels.insert(label.clone(), label_type);
            label_strings.push(label);
        }
        let result = op(self);
        for label in label_strings {
            self.context.labels.remove(&label);
        }
        let mut body = result?;
        labels.reverse();
        for id in labels {
            let location = span(&id, &body);
            body = Stmt::Label(location, id, Box::new(body));
        }
        Ok(body)
    }

    fn allow_in<F, T>(&mut self, allow_in: bool, parse: F) -> Result<T>
      where F: FnOnce(&mut Self) -> Result<T>
    {
        let allow_in = replace(&mut self.context.allow_in, allow_in);
        let result = parse(self);
        replace(&mut self.context.allow_in, allow_in);
        result
    }
}

#[derive(Debug, Eq, PartialEq, Copy, Clone)]
pub enum LabelType {
    Statement,
    Iteration
}

#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
pub enum Goal {
    Script(Strict),
    Module,
    Unknown
}

impl Default for Goal {
    fn default() -> Goal { Goal::Unknown }
}

pub trait Mode {
    fn strict(&self) -> Strict;
    fn definitely_script(&self) -> bool;
    fn definitely_strict(&self) -> bool;
    fn definitely_sloppy(&self) -> bool;
    fn definitely_module(&self) -> bool;
}

impl Mode for Goal {
    fn strict(&self) -> Strict {
        match *self {
            Goal::Script(strict) => strict,
            Goal::Module         => Strict::Yes,
            Goal::Unknown        => Strict::Unknown
        }
    }

    fn definitely_script(&self) -> bool {
        match *self {
            Goal::Script(_) => true,
            _ => false
        }
    }

    fn definitely_strict(&self) -> bool {
        self.strict().definitely()
    }

    fn definitely_sloppy(&self) -> bool {
        self.strict().definitely_not()
    }

    fn definitely_module(&self) -> bool { *self == Goal::Module }
}

#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Context {
    pub function: Option<Strict>,
    pub iteration: bool,
    pub switch: bool,
    pub allow_in: bool,
    pub labels: HashMap<Rc<Name>, LabelType>
}

impl Context {
    pub fn new() -> Context {
        Context {
            function: None,
            iteration: false,
            switch: false,
            allow_in: true,
            labels: HashMap::new()
        }
    }

    pub fn new_function(inherited: Strict) -> Context {
        Context {
            function: Some(inherited),
            iteration: false,
            switch: false,
            allow_in: true,
            labels: HashMap::new()
        }
    }
}
<|endoftext|>