<commit_before>#![feature(rustc_private)]
extern crate llvm_sys as llvm;
extern crate libc;
use std::ptr;
use std::ffi;

use self::llvm::prelude::{LLVMContextRef, LLVMModuleRef, LLVMBuilderRef, LLVMValueRef, LLVMTypeRef};
use self::llvm::core::*;

use std::collections::{HashMap};
use std::mem;

use syntax::ast::{Block, Expr, Decl, TType, OptionalTypeExprTupleList};
use syntax::ptr::{B};
//FIXME this import is for integration testing purposes
use syntax::parse::*;//{Parser};
use syntax::parse::parser::{Parser};

macro_rules! c_str_ptr {
    ($s:expr) => {
        ffi::CString::new($s).unwrap().as_ptr()
    };
}

pub struct Context{
    context : LLVMContextRef,
    module : LLVMModuleRef,
    builder : LLVMBuilderRef,
    sym_tab : HashMap<String, LLVMValueRef>,
    bb_stack : Vec<*mut llvm::LLVMBasicBlock>
}

impl Context{
    fn new(module_name : &str) -> Self{
        unsafe{
            let llvm_context =  LLVMContextCreate();
            let llvm_module = LLVMModuleCreateWithNameInContext(c_str_ptr!(module_name),
                                                                llvm_context);
            let builder = LLVMCreateBuilderInContext(llvm_context);
            let sym_tab = HashMap::new();
            let bb_stack = Vec::new();

            Context {
                context : llvm_context,
                module : llvm_module,
                builder : builder,
                sym_tab : sym_tab,
                bb_stack : bb_stack
            }
        }
    }

    pub fn dump(&self){
        unsafe{
            LLVMDumpModule(self.module);
        }
    }
}

impl Drop for Context{
    fn drop(&mut self){
        unsafe{
            LLVMDisposeBuilder(self.builder);
            LLVMDisposeModule(self.module);
            LLVMContextDispose(self.context);
        }
    }
}

//TODO move these in a seperate file
type IRBuildingResult = Result<LLVMValueRef, String>;

trait IRBuilder{
    fn codegen(&self, ctxt : &mut Context) -> IRBuildingResult;
}

fn std_functions_call_factory(fn_name : &str,
                              args : &OptionalTypeExprTupleList,
                              ctxt : &mut Context) -> Option<LLVMValueRef>{
    unsafe{
        match fn_name {
            "print" =>{
                debug_assert!(args.is_some(), "No args passed to print()");
                let lst = args.as_ref().unwrap();
                debug_assert!(lst.len() == 1, "One arg should be passed to print()");
                debug_assert!(lst[0].0 == TType::TString);
                let str_arg = match &*lst[0].1 {
                    &Expr::StringExpr(ref value) => c_str_ptr!(&*(value.clone())),
                    _ => panic!("Expected a string expr")
                };

                let print_ty = LLVMIntTypeInContext(ctxt.context, 32);
                let mut pf_type_args_vec = Vec::new();
                pf_type_args_vec.push(LLVMPointerType(LLVMIntTypeInContext(ctxt.context, 8),
                                                      0));
                let proto = LLVMFunctionType(print_ty, pf_type_args_vec.as_mut_ptr(), 1, 1);
                let print_function = LLVMAddFunction(ctxt.module,
                                                     c_str_ptr!("printf"),
                                                     proto);
                let gstr = LLVMBuildGlobalStringPtr(ctxt.builder,
                                                    str_arg,
                                                    c_str_ptr!(".str"));
                let mut pf_args = Vec::new();
                pf_args.push(gstr);

                Some(LLVMBuildCall(ctxt.builder,
                                   print_function,
                                   pf_args.as_mut_ptr(),
                                   1,
                                   c_str_ptr!("call")))
            },
            _ => {None}
        }
    }
}

fn get_llvm_type_for_ttype(ty : &TType, ctxt : &mut Context) -> LLVMTypeRef{
    unsafe{
        match ty {
            &TType::TVoid => LLVMVoidTypeInContext(ctxt.context),
            &TType::TInt32 => LLVMIntTypeInContext(ctxt.context, 32),
            _ => panic!("Other TTypes not mapped yet to the corresponding LLVM types")
        }
    }
}

impl IRBuilder for Expr{
    fn codegen(&self, ctxt : &mut Context) -> IRBuildingResult{
        macro_rules! build_binary_instrs{
            ($fun : ident, $e1:ident, $e2:ident, $s : expr) => {{
                let ev1 = try!($e1.codegen(ctxt));
                let ev2 = try!($e2.codegen(ctxt));
                Ok($fun(ctxt.builder, ev1, ev2, $s.as_ptr() as *const i8))
            }}
        }
        unsafe{
            match self{
                &Expr::NumExpr(ref i) => {
                    let ty = LLVMIntTypeInContext(ctxt.context, 32);
                    Ok(LLVMConstInt(ty, *i as u64, 0))
                },
                &Expr::AddExpr(ref e1, ref e2) => {
                    build_binary_instrs!(LLVMBuildFAdd, e1, e2, "add_tmp")
                },
                &Expr::SubExpr(ref e1, ref e2) => {
                    build_binary_instrs!(LLVMBuildFSub, e1, e2, "sub_tmp")
                },
                &Expr::CallExpr(ref fn_name, ref optional_args) => {
                    //FIXME instead of directly passing to the factory
                    //fn_name can be checked in a map that records names of std functions
                    match std_functions_call_factory(&*fn_name, optional_args, ctxt) {
                        Some(call) => Ok(call),
                        _ => {
                            //user defined function call
                            let mut pf_args = Vec::new();
                            //FIXME pass args if present in the call
                            if optional_args.is_some() {

                            }
                            //pf_args.push(gstr);
                            let _fn = ctxt.sym_tab[&*fn_name];
                            Ok(LLVMBuildCall(ctxt.builder,
                                            _fn,
                                            pf_args.as_mut_ptr(),
                                            0,
                                            c_str_ptr!("")))
                        }
                    }
                },
                &Expr::LetExpr(ref decls, ref expr) => {
                    debug_assert!(!decls.is_empty(), "Declarations in a let block can't be empty");
                    debug_assert!(expr.is_some(), "Expr in a let block can't be empty");
                    for decl in &*decls {
                        match decl {
                            &Decl::FunDec(ref name, ref params, ref ty, ref body) => {
                                let llvm_ty = get_llvm_type_for_ttype(ty, ctxt);
                                let proto = LLVMFunctionType(llvm_ty, ptr::null_mut(), 0, 0);
                                let function = LLVMAddFunction(ctxt.module,
                                                               c_str_ptr!(&(*name.clone())),
                                                               proto);
                                let bb = LLVMAppendBasicBlockInContext(ctxt.context,
                                                                       function,
                                                                       c_str_ptr!("entry"));
                                ctxt.sym_tab.insert(name.clone(), function);
                                LLVMPositionBuilderAtEnd(ctxt.builder, bb);
                                //trans_expr(body, &mut ctxt);
                                let value_ref = try!(body.codegen(ctxt));
                                if *ty == TType::TVoid{
                                    LLVMBuildRetVoid(ctxt.builder);
                                }
                                else{
                                    LLVMBuildRet(ctxt.builder, value_ref);
                                }
                            },
                            _ => panic!("More decl types should be covered")
                        }

                    }
                    //trans_expr(&*expr.unwrap(), &mut ctxt);
                    //FIXME should the previous bb be popped here?
                    let bb = ctxt.bb_stack.pop().unwrap();
                    LLVMPositionBuilderAtEnd(ctxt.builder, bb);
                    let e = &expr.as_ref().unwrap();
                    let v = try!(e.codegen(ctxt));
                    Ok(v)
                }
                _ => Err("error".to_string())
            }
        }
    }
}

pub fn translate(expr : &Expr) -> Option<Context>{
    let mut ctxt = Context::new("main_mod");
    unsafe{
        //build outer embedding main() fn
        let ty = LLVMIntTypeInContext(ctxt.context, 32);
        let proto = LLVMFunctionType(ty, ptr::null_mut(), 0, 0);
        let function = LLVMAddFunction(ctxt.module,
                                       c_str_ptr!("main"),
                                       proto);
        let bb = LLVMAppendBasicBlockInContext(ctxt.context,
                                               function,
                                               c_str_ptr!("entry"));
        LLVMPositionBuilderAtEnd(ctxt.builder, bb);
        ctxt.bb_stack.push(bb);
        trans_expr(expr, &mut ctxt);
        LLVMBuildRet(ctxt.builder,
                     LLVMConstInt(LLVMIntTypeInContext(ctxt.context, 32), 0 as u64, 0));

        //add translated code as part of the block
    }
    Some(ctxt)
}

fn trans_expr(expr: &Expr, ctxt : &mut Context){
    let result = expr.codegen(ctxt); //ctxt is already &mut
    match result {
        Ok(_) => {},
        Err(msg) => panic!(msg)
    }
}

//#[test]
fn test_translate_std_print_call() {
    let ctxt = translate(&Expr::CallExpr("print".to_string(),
                                  Some(vec![(TType::TString,
                                             B(Expr::StringExpr("abhi".to_string())))])));
    assert_eq!(ctxt.is_some(), true);
    ctxt.unwrap().dump();
}

//#[test]
fn test_prsr_bcknd_intgrtion_prnt_call() {
    let mut p = Parser::new("print(\"Grrrr!\n\")".to_string());
    p.start_lexer();
    let tup = p.expr();
    let (ty, b_expr) = tup.unwrap();
    let ctxt = translate(&*b_expr);
    // let ctxt = translate();&Expr::CallExpr("print".to_string(),
    //                               Some(vec![(TType::TString,
    //                                          B(Expr::StringExpr("abhi".to_string())))])));
    assert_eq!(ctxt.is_some(), true);
    ctxt.unwrap().dump();
}

#[test]
fn test_translate_add_expr(){
    let mut p = Parser::new(String::from("let function foo() : int = 1+3 in foo() end"));
    p.start_lexer();
    let tup = p.expr();
    let (_ , b_expr) = tup.unwrap();
    let ctxt = translate(&*b_expr);
    ctxt.unwrap().dump();
}
#[test]
fn test_prsr_bcknd_intgrtion_let_blk() {
    let mut p = Parser::new("let function foo() = print(\"Grrrr!\n\") in foo() end".to_string());
    p.start_lexer();
    let tup = p.expr();
    let (ty, b_expr) = tup.unwrap();
    let ctxt = translate(&*b_expr);
    assert_eq!(ctxt.is_some(), true);
    ctxt.unwrap().dump();
}
<commit_msg>Add translation for if-then-else<commit_after>#![feature(rustc_private)]
extern crate llvm_sys as llvm;
extern crate libc;
use std::ptr;
use std::ffi;

use self::llvm::prelude::{LLVMContextRef, LLVMModuleRef, LLVMBuilderRef, LLVMValueRef, LLVMTypeRef};
use self::llvm::core::*;

use std::collections::{HashMap};
use std::mem;

use syntax::ast::{Block, Expr, Decl, TType, OptionalTypeExprTupleList};
use syntax::ptr::{B};
//FIXME this import is for integration testing purposes
use syntax::parse::*;//{Parser};
use syntax::parse::parser::{Parser};

macro_rules! c_str_ptr {
    ($s:expr) => {
        ffi::CString::new($s).unwrap().as_ptr()
    };
}

pub struct Context{
    context : LLVMContextRef,
    module : LLVMModuleRef,
    builder : LLVMBuilderRef,
    sym_tab : HashMap<String, LLVMValueRef>,
    bb_stack : Vec<*mut llvm::LLVMBasicBlock>
}

impl Context{
    fn new(module_name : &str) -> Self{
        unsafe{
            let llvm_context =  LLVMContextCreate();
            let llvm_module = LLVMModuleCreateWithNameInContext(c_str_ptr!(module_name),
                                                                llvm_context);
            let builder = LLVMCreateBuilderInContext(llvm_context);
            let sym_tab = HashMap::new();
            let bb_stack = Vec::new();

            Context {
                context : llvm_context,
                module : llvm_module,
                builder : builder,
                sym_tab : sym_tab,
                bb_stack : bb_stack
            }
        }
    }

    pub fn dump(&self){
        unsafe{
            LLVMDumpModule(self.module);
        }
    }
}

impl Drop for Context{
    fn drop(&mut self){
        unsafe{
            LLVMDisposeBuilder(self.builder);
            LLVMDisposeModule(self.module);
            LLVMContextDispose(self.context);
        }
    }
}

//TODO move these in a seperate file
type IRBuildingResult = Result<LLVMValueRef, String>;

trait IRBuilder{
    fn codegen(&self, ctxt : &mut Context) -> IRBuildingResult;
}

fn std_functions_call_factory(fn_name : &str,
                              args : &OptionalTypeExprTupleList,
                              ctxt : &mut Context) -> Option<LLVMValueRef>{
    unsafe{
        match fn_name {
            "print" =>{
                debug_assert!(args.is_some(), "No args passed to print()");
                let lst = args.as_ref().unwrap();
                debug_assert!(lst.len() == 1, "One arg should be passed to print()");
                debug_assert!(lst[0].0 == TType::TString);
                let str_arg = match &*lst[0].1 {
                    &Expr::StringExpr(ref value) => c_str_ptr!(&*(value.clone())),
                    _ => panic!("Expected a string expr")
                };

                let print_ty = LLVMIntTypeInContext(ctxt.context, 32);
                let mut pf_type_args_vec = Vec::new();
                pf_type_args_vec.push(LLVMPointerType(LLVMIntTypeInContext(ctxt.context, 8),
                                                      0));
                let proto = LLVMFunctionType(print_ty, pf_type_args_vec.as_mut_ptr(), 1, 1);
                let print_function = LLVMAddFunction(ctxt.module,
                                                     c_str_ptr!("printf"),
                                                     proto);
                let gstr = LLVMBuildGlobalStringPtr(ctxt.builder,
                                                    str_arg,
                                                    c_str_ptr!(".str"));
                let mut pf_args = Vec::new();
                pf_args.push(gstr);

                Some(LLVMBuildCall(ctxt.builder,
                                   print_function,
                                   pf_args.as_mut_ptr(),
                                   1,
                                   c_str_ptr!("call")))
            },
            _ => {None}
        }
    }
}

fn get_llvm_type_for_ttype(ty : &TType, ctxt : &mut Context) -> LLVMTypeRef{
    unsafe{
        match ty {
            &TType::TVoid => LLVMVoidTypeInContext(ctxt.context),
            &TType::TInt32 => LLVMIntTypeInContext(ctxt.context, 32),
            _ => panic!("Other TTypes not mapped yet to the corresponding LLVM types")
        }
    }
}

impl IRBuilder for Expr{
    fn codegen(&self, ctxt : &mut Context) -> IRBuildingResult{
        macro_rules! build_binary_instrs{
            ($fun : ident, $e1:ident, $e2:ident, $s : expr) => {{
                let ev1 = try!($e1.codegen(ctxt));
                let ev2 = try!($e2.codegen(ctxt));
                Ok($fun(ctxt.builder, ev1, ev2, $s.as_ptr() as *const i8))
            }}
        }
        unsafe{
            match self{
                &Expr::NumExpr(ref i) => {
                    let ty = LLVMIntTypeInContext(ctxt.context, 32);
                    Ok(LLVMConstInt(ty, *i as u64, 0))
                },
                &Expr::AddExpr(ref e1, ref e2) => {
                    build_binary_instrs!(LLVMBuildFAdd, e1, e2, "add_tmp")
                },
                &Expr::SubExpr(ref e1, ref e2) => {
                    build_binary_instrs!(LLVMBuildFSub, e1, e2, "sub_tmp")
                },
                &Expr::IfThenElseExpr(ref conditional_expr, ref then_expr, ref else_expr) => {
                    let cond_code = try!(conditional_expr.codegen(ctxt));
                    let zero = LLVMConstInt(LLVMIntTypeInContext(ctxt.context, 32), 0u64, 0);
                    let if_cond = LLVMBuildFCmp(ctxt.builder, llvm::LLVMRealPredicate::LLVMRealONE, cond_code, zero, "ifcond".as_ptr() as *const i8);
                    let bb = LLVMGetInsertBlock(ctxt.builder);
                    let function = LLVMGetBasicBlockParent(bb);
                    let then_block = LLVMAppendBasicBlockInContext(ctxt.context, function, "thencond".as_ptr() as *const i8);
                    let else_block = LLVMAppendBasicBlockInContext(ctxt.context, function, "elsecond".as_ptr() as *const i8);
                    let ifcont_block = LLVMAppendBasicBlockInContext(ctxt.context, function, "ifcont".as_ptr() as *const i8);
                    LLVMBuildCondBr(ctxt.builder, if_cond, then_block, else_block);

                    LLVMPositionBuilderAtEnd(ctxt.builder, then_block);
                    let then_code = try!(then_expr.codegen(ctxt));
                    LLVMBuildBr(ctxt.builder, ifcont_block);
                    let then_end = LLVMGetInsertBlock(ctxt.builder);

                    LLVMPositionBuilderAtEnd(ctxt.builder, else_block);
                    let else_code = try!(else_expr.codegen(ctxt));
                    LLVMBuildBr(ctxt.builder, ifcont_block);
                    let else_end = LLVMGetInsertBlock(ctxt.builder);

                    LLVMPositionBuilderAtEnd(ctxt.builder, ifcont_block);

                    let phi_node = LLVMBuildPhi(ctxt.builder, LLVMIntTypeInContext(ctxt.context, 32), "ifphi".as_ptr() as *const i8);
                    LLVMAddIncoming(phi_node, then_code as *mut LLVMValueRef, then_end as *mut *mut llvm::LLVMBasicBlock, 1);
                    panic!("asdad");
                    LLVMAddIncoming(phi_node, else_code as *mut LLVMValueRef, else_end as *mut *mut llvm::LLVMBasicBlock, 1);
                    Ok(phi_node)

                },
                &Expr::CallExpr(ref fn_name, ref optional_args) => {
                    //FIXME instead of directly passing to the factory
                    //fn_name can be checked in a map that records names of std functions
                    match std_functions_call_factory(&*fn_name, optional_args, ctxt) {
                        Some(call) => Ok(call),
                        _ => {
                            //user defined function call
                            let mut pf_args = Vec::new();
                            //FIXME pass args if present in the call
                            if optional_args.is_some() {

                            }
                            //pf_args.push(gstr);
                            let _fn = ctxt.sym_tab[&*fn_name];
                            Ok(LLVMBuildCall(ctxt.builder,
                                            _fn,
                                            pf_args.as_mut_ptr(),
                                            0,
                                            c_str_ptr!("")))
                        }
                    }
                },
                &Expr::LetExpr(ref decls, ref expr) => {
                    debug_assert!(!decls.is_empty(), "Declarations in a let block can't be empty");
                    debug_assert!(expr.is_some(), "Expr in a let block can't be empty");
                    for decl in &*decls {
                        match decl {
                            &Decl::FunDec(ref name, ref params, ref ty, ref body) => {
                                let llvm_ty = get_llvm_type_for_ttype(ty, ctxt);
                                let proto = LLVMFunctionType(llvm_ty, ptr::null_mut(), 0, 0);
                                let function = LLVMAddFunction(ctxt.module,
                                                               c_str_ptr!(&(*name.clone())),
                                                               proto);
                                let bb = LLVMAppendBasicBlockInContext(ctxt.context,
                                                                       function,
                                                                       c_str_ptr!("entry"));
                                ctxt.sym_tab.insert(name.clone(), function);
                                LLVMPositionBuilderAtEnd(ctxt.builder, bb);
                                //trans_expr(body, &mut ctxt);
                                let value_ref = try!(body.codegen(ctxt));
                                if *ty == TType::TVoid{
                                    LLVMBuildRetVoid(ctxt.builder);
                                }
                                else{
                                    LLVMBuildRet(ctxt.builder, value_ref);
                                }
                            },
                            _ => panic!("More decl types should be covered")
                        }

                    }
                    //trans_expr(&*expr.unwrap(), &mut ctxt);
                    //FIXME should the previous bb be popped here?
                    let bb = ctxt.bb_stack.pop().unwrap();
                    LLVMPositionBuilderAtEnd(ctxt.builder, bb);
                    let e = &expr.as_ref().unwrap();
                    let v = try!(e.codegen(ctxt));
                    Ok(v)
                }
                t => Err(format!("error: {:?}", t))
            }
        }
    }
}

pub fn translate(expr : &Expr) -> Option<Context>{
    let mut ctxt = Context::new("main_mod");
    unsafe{
        //build outer embedding main() fn
        let ty = LLVMIntTypeInContext(ctxt.context, 32);
        let proto = LLVMFunctionType(ty, ptr::null_mut(), 0, 0);
        let function = LLVMAddFunction(ctxt.module,
                                       c_str_ptr!("main"),
                                       proto);
        let bb = LLVMAppendBasicBlockInContext(ctxt.context,
                                               function,
                                               c_str_ptr!("entry"));
        LLVMPositionBuilderAtEnd(ctxt.builder, bb);
        ctxt.bb_stack.push(bb);
        trans_expr(expr, &mut ctxt);
        LLVMBuildRet(ctxt.builder,
                     LLVMConstInt(LLVMIntTypeInContext(ctxt.context, 32), 0 as u64, 0));

        //add translated code as part of the block
    }
    Some(ctxt)
}

fn trans_expr(expr: &Expr, ctxt : &mut Context){
    let result = expr.codegen(ctxt); //ctxt is already &mut
    match result {
        Ok(_) => {},
        Err(msg) => panic!(msg)
    }
}

//#[test]
fn test_translate_std_print_call() {
    let ctxt = translate(&Expr::CallExpr("print".to_string(),
                                  Some(vec![(TType::TString,
                                             B(Expr::StringExpr("abhi".to_string())))])));
    assert_eq!(ctxt.is_some(), true);
    ctxt.unwrap().dump();
}

//#[test]
fn test_prsr_bcknd_intgrtion_prnt_call() {
    let mut p = Parser::new("print(\"Grrrr!\n\")".to_string());
    p.start_lexer();
    let tup = p.expr();
    let (ty, b_expr) = tup.unwrap();
    let ctxt = translate(&*b_expr);
    // let ctxt = translate();&Expr::CallExpr("print".to_string(),
    //                               Some(vec![(TType::TString,
    //                                          B(Expr::StringExpr("abhi".to_string())))])));
    assert_eq!(ctxt.is_some(), true);
    ctxt.unwrap().dump();
}

#[test]
fn test_translate_add_expr(){
    let mut p = Parser::new(String::from("let function foo() : int = 1+3 in foo() end"));
    p.start_lexer();
    let tup = p.expr();
    let (_ , b_expr) = tup.unwrap();
    let ctxt = translate(&*b_expr);
    ctxt.unwrap().dump();
}
#[test]
fn test_prsr_bcknd_intgrtion_let_blk() {
    let mut p = Parser::new("let function foo() = print(\"Grrrr!\n\") in foo() end".to_string());
    p.start_lexer();
    let tup = p.expr();
    let (ty, b_expr) = tup.unwrap();
    let ctxt = translate(&*b_expr);
    assert_eq!(ctxt.is_some(), true);
    ctxt.unwrap().dump();
}

#[test]
fn test_prsr_bcknd_intgrtion_if_then_expr() {
    let mut p = Parser::new("let function foo() :int = if 1 then 1 else 1 in foo() end".to_string());
    p.start_lexer();
    let tup = p.expr();
    let (ty, b_expr) = tup.unwrap();
    let ctxt = translate(&*b_expr);
    assert_eq!(ctxt.is_some(), true);
    ctxt.unwrap().dump();
}
<|endoftext|>


<commit_before>extern crate llvm_sys;

use std::ptr;
use self::llvm_sys::core::*;
use self::llvm_sys::*;
use llvm::codegen::{Context, c_str_ptr};

// Generate LLVM IR for functions built into the language
pub unsafe fn generate_builtins(context: &mut Context) {
    generate_types(context);
    generate_print(context);

    // Generate a `main` function for the script to be housed
    let int32 = LLVMInt32TypeInContext(context.get_context());
    let fn_type = LLVMFunctionType(int32, ptr::null_mut(), 0, 0);
    let main = LLVMAddFunction(context.get_module(), c_str_ptr("main"), fn_type);
    let bb = LLVMAppendBasicBlockInContext(context.get_context(), main, c_str_ptr("entry"));

    // Goes to the end of the block
    LLVMPositionBuilderAtEnd(context.get_builder(), bb);

    // Adds a return statement
    LLVMBuildRet(context.get_builder(), LLVMConstInt(int32, 1, 1));

    // Reposition to the start of the block
    LLVMPositionBuilder(context.get_builder(), bb, LLVMGetFirstInstruction(bb));
}

unsafe fn generate_print(context: &mut Context) {
    // Types
    let void = LLVMVoidTypeInContext(context.get_context());
    let string_type_fields = vec![LLVMInt32TypeInContext(context.get_context()),
                  LLVMPointerType(LLVMInt8TypeInContext(context.get_context()), 0)];
    let int32_type = LLVMInt32TypeInContext(context.get_context());
    let string_type = LLVMStructTypeInContext(context.get_context(), string_type_fields.as_ptr() as *mut _, 2, 0);

    let args = vec![string_type];

    // 2nd arg is param types (*mut LLVMTypeRef) and 3rd is param count. 4 is variable # of args t/f?
    let fn_type = LLVMFunctionType(void, args.as_ptr() as *mut _, 1, 0);
    let print_fn = LLVMAddFunction(context.get_module(), c_str_ptr("print"), fn_type);

    // Name the param
    let param = LLVMGetFirstParam(print_fn);
    LLVMSetValueName(param, c_str_ptr("str"));

    // Create basic blocks to generate code in
    let start_block = LLVMAppendBasicBlockInContext(context.get_context(), print_fn, c_str_ptr("start"));
    let loop_block = LLVMAppendBasicBlockInContext(context.get_context(), print_fn, c_str_ptr("loop"));
    let end_block = LLVMAppendBasicBlockInContext(context.get_context(), print_fn, c_str_ptr("end"));

    // Start
    LLVMPositionBuilderAtEnd(context.get_builder(), start_block);

    let zero = LLVMConstInt(int32_type, 0, 0);
    let op = LLVMIntPredicate::LLVMIntEQ;
    let offset_ptr = LLVMBuildAlloca(context.get_builder(), int32_type, c_str_ptr("offsetptr"));
    LLVMBuildStore(context.get_builder(), zero, offset_ptr);
    let len = LLVMBuildExtractValue(context.get_builder(), param, 0, c_str_ptr("len"));
    let str_ptr = LLVMBuildExtractValue(context.get_builder(), param, 1, c_str_ptr("strptr"));
    let cmp = LLVMBuildICmp(context.get_builder(), op, len, zero, c_str_ptr("cmp"));

    // Branch to end if string len is 0
    LLVMBuildCondBr(context.get_builder(), cmp, end_block, loop_block);

    // Loop
    LLVMPositionBuilderAtEnd(context.get_builder(), loop_block);

    let offset_val = LLVMBuildLoad(context.get_builder(), offset_ptr, c_str_ptr("offsetsval"));
    let ptr_iter = LLVMBuildAdd(context.get_builder(), str_ptr, offset_val, c_str_ptr("iter"));
    let mut putchar_fn = LLVMGetNamedFunction(context.get_module(), c_str_ptr("putchar"));
    if putchar_fn.is_null() {
        let fn_type2 = LLVMFunctionType(int32_type, vec![int32_type].as_ptr() as *mut _, 1, 0);
        putchar_fn = LLVMAddFunction(context.get_module(), c_str_ptr("putchar"), fn_type2);
    }

    // Print char at ptr_iter here
    LLVMBuildCall(context.get_builder(), putchar_fn, vec![ptr_iter].as_ptr() as *mut _, 1, c_str_ptr(""));

    // TODO: Increment offset_val? (move its init out of the loop?)

    // TODO: If equal to len, branch to end else branch to loop

    LLVMBuildBr(context.get_builder(), end_block);

    // End
    LLVMPositionBuilderAtEnd(context.get_builder(), end_block);
    LLVMBuildRetVoid(context.get_builder());
}

unsafe fn generate_types(context: &mut Context) {
    // Define a string structure that looks like:
    // string {
    //     len: i32,
    //     array: i8*
    // }

    let string_type_fields = vec![LLVMInt32TypeInContext(context.get_context()),
                  LLVMPointerType(LLVMInt8TypeInContext(context.get_context()), 0)];

//    let struct_type = LLVMStructType(string_type_fields.as_ptr() as *mut _, 2, 0);
//    let const_str_var = LLVMAddGlobal(context.get_module(), struct_type, c_str_ptr("struct.String"));

    let string_struct_type = LLVMStructCreateNamed(LLVMGetGlobalContext(), c_str_ptr("string"));
    LLVMStructSetBody(string_struct_type, string_type_fields.as_ptr() as *mut _, 2, 0);
//    let string_struct = LLVMStructType(struct_type, 2, 0);
}
<commit_msg>Finished writing general flow of print IR.<commit_after>extern crate llvm_sys;

use std::ptr;
use self::llvm_sys::core::*;
use self::llvm_sys::*;
use llvm::codegen::{Context, c_str_ptr};

// Generate LLVM IR for functions built into the language
pub unsafe fn generate_builtins(context: &mut Context) {
    generate_types(context);
    generate_print(context);

    // Generate a `main` function for the script to be housed
    let int32 = LLVMInt32TypeInContext(context.get_context());
    let fn_type = LLVMFunctionType(int32, ptr::null_mut(), 0, 0);
    let main = LLVMAddFunction(context.get_module(), c_str_ptr("main"), fn_type);
    let bb = LLVMAppendBasicBlockInContext(context.get_context(), main, c_str_ptr("entry"));

    // Goes to the end of the block
    LLVMPositionBuilderAtEnd(context.get_builder(), bb);

    // Adds a return statement
    LLVMBuildRet(context.get_builder(), LLVMConstInt(int32, 1, 1));

    // Reposition to the start of the block
    LLVMPositionBuilder(context.get_builder(), bb, LLVMGetFirstInstruction(bb));
}

unsafe fn generate_print(context: &mut Context) {
    // Types
    let void = LLVMVoidTypeInContext(context.get_context());
    let string_type_fields = vec![LLVMInt32TypeInContext(context.get_context()),
                  LLVMPointerType(LLVMInt8TypeInContext(context.get_context()), 0)];
    let int32_type = LLVMInt32TypeInContext(context.get_context());
    let string_type = LLVMStructTypeInContext(context.get_context(), string_type_fields.as_ptr() as *mut _, 2, 0);

    let args = vec![string_type];

    // 2nd arg is param types (*mut LLVMTypeRef) and 3rd is param count. 4 is variable # of args t/f?
    let fn_type = LLVMFunctionType(void, args.as_ptr() as *mut _, 1, 0);
    let print_fn = LLVMAddFunction(context.get_module(), c_str_ptr("print"), fn_type);

    // Name the param
    let param = LLVMGetFirstParam(print_fn);
    LLVMSetValueName(param, c_str_ptr("str"));

    // Create basic blocks to generate code in
    let start_block = LLVMAppendBasicBlockInContext(context.get_context(), print_fn, c_str_ptr("start"));
    let loop_block = LLVMAppendBasicBlockInContext(context.get_context(), print_fn, c_str_ptr("loop"));
    let end_block = LLVMAppendBasicBlockInContext(context.get_context(), print_fn, c_str_ptr("end"));

    // Start
    LLVMPositionBuilderAtEnd(context.get_builder(), start_block);

    let i32_zero = LLVMConstInt(int32_type, 0, 0);
    let op = LLVMIntPredicate::LLVMIntEQ;
    let offset_ptr = LLVMBuildAlloca(context.get_builder(), int32_type, c_str_ptr("offsetptr"));
    LLVMBuildStore(context.get_builder(), i32_zero, offset_ptr);
    let len = LLVMBuildExtractValue(context.get_builder(), param, 0, c_str_ptr("len"));
    let str_ptr = LLVMBuildExtractValue(context.get_builder(), param, 1, c_str_ptr("strptr"));
    let cmp = LLVMBuildICmp(context.get_builder(), op, len, i32_zero, c_str_ptr("cmp"));

    // Branch to end if string len is 0
    LLVMBuildCondBr(context.get_builder(), cmp, end_block, loop_block);

    // Loop
    LLVMPositionBuilderAtEnd(context.get_builder(), loop_block);

    let offset = LLVMBuildLoad(context.get_builder(), offset_ptr, c_str_ptr("offset"));
    let ptr_iter = LLVMBuildAdd(context.get_builder(), str_ptr, offset, c_str_ptr("iter"));
    let mut putchar_fn = LLVMGetNamedFunction(context.get_module(), c_str_ptr("putchar"));
    if putchar_fn.is_null() {
        let fn_type2 = LLVMFunctionType(int32_type, vec![int32_type].as_ptr() as *mut _, 1, 0);
        putchar_fn = LLVMAddFunction(context.get_module(), c_str_ptr("putchar"), fn_type2);
    }

    // Print char at ptr_iter here
    LLVMBuildCall(context.get_builder(), putchar_fn, vec![ptr_iter].as_ptr() as *mut _, 1, c_str_ptr(""));

    // Increment the offset
    let i32_one = LLVMConstInt(int32_type, 1, 0);
    let inc = LLVMBuildAdd(context.get_builder(), offset, i32_one, c_str_ptr("inc"));
    LLVMBuildStore(context.get_builder(), inc, offset_ptr);

    // Branch if offset equals len, else keep looping
    let op = LLVMIntPredicate::LLVMIntEQ;
    let cmp2 = LLVMBuildICmp(context.get_builder(), op, inc, len, c_str_ptr("cmp"));
    LLVMBuildCondBr(context.get_builder(), cmp2, end_block, loop_block);

    // End
    LLVMPositionBuilderAtEnd(context.get_builder(), end_block);
    LLVMBuildRetVoid(context.get_builder());
}

unsafe fn generate_types(context: &mut Context) {
    // Define a string structure that looks like:
    // string {
    //     len: i32,
    //     array: i8*
    // }

    let string_type_fields = vec![LLVMInt32TypeInContext(context.get_context()),
                  LLVMPointerType(LLVMInt8TypeInContext(context.get_context()), 0)];

//    let struct_type = LLVMStructType(string_type_fields.as_ptr() as *mut _, 2, 0);
//    let const_str_var = LLVMAddGlobal(context.get_module(), struct_type, c_str_ptr("struct.String"));

    let string_struct_type = LLVMStructCreateNamed(LLVMGetGlobalContext(), c_str_ptr("string"));
    LLVMStructSetBody(string_struct_type, string_type_fields.as_ptr() as *mut _, 2, 0);
//    let string_struct = LLVMStructType(struct_type, 2, 0);
}
<|endoftext|>


<commit_before>use analysis::{Analysis, Argument, Enum, Function};
use clang;
use entity::{Entity, EntityType};
use failure::{err_msg, Fallible};
use python_parser::ast::{CompoundStatement, Expression, Statement};
use python_parser::{file_input, make_strspan};
use std::fs::File;
use std::io::Read;

////////////////////////////////////////////////////////////////////////////////

lazy_static! {
    static ref CLANG: Option<clang::Clang> = {
        match clang::Clang::new() {
            Ok(clang) => Some(clang),
            Err(_) => None,
        }
    };
}

////////////////////////////////////////////////////////////////////////////////

pub trait LanguageType: Default {
    fn file_types() -> &'static [&'static str];
    fn extract_entities<T: LanguageType>(analysis: &Analysis<T>) -> Fallible<()>;
}

////////////////////////////////////////////////////////////////////////////////

#[derive(Default, Clone, Debug)]
struct CFamily;

impl CFamily {
    fn format_arguments(arguments: &[clang::Entity]) -> Fallible<Vec<Argument>> {
        let mut args = Vec::new();

        for argument in arguments {
            args.push(Argument::new(
                argument.get_display_name().unwrap_or(String::new()),
                Some(
                    argument
                        .get_type()
                        .ok_or_else(|| err_msg("Argument type can not be parsed from signature."))?
                        .get_display_name(),
                ),
            ));
        }

        Ok(args)
    }

    fn analyse_clang_function_entity(entity: &clang::Entity) -> Fallible<Option<EntityType>> {
        if let Some(entity_name) = entity.get_name() {
            let mut function = Function::new(entity_name);

            // Set return type.
            if let Some(return_type) = entity.get_type() {
                function.set_return_type(return_type.get_display_name().as_str())?;
            }

            // Set arguments vector.
            if let Some(arguments) = entity.get_arguments() {
                function.set_arguments(&Self::format_arguments(&arguments)?);
            }

            // Set description.
            if let Some(description) = entity.get_comment() {
                function.set_description(description.as_str());
            }

            return Ok(Some(EntityType::Function(function)));
        }

        Ok(None)
    }

    fn analyse_clang_enum_entity(entity: &clang::Entity) -> Fallible<Option<EntityType>> {
        if let Some(entity_name) = entity.get_name() {
            let enumeration = Enum::new(entity_name);

            return Ok(Some(EntityType::Enum(enumeration)));
        }

        Ok(None)
    }

    fn analyse_clang_generic_entity(entity: &clang::Entity) -> Fallible<Option<EntityType>> {
        if let Some(entity_name) = entity.get_name() {
            let entity = Entity::new(entity_name);

            return Ok(Some(EntityType::Entity(entity)));
        }

        Ok(None)
    }
}

////////////////////////////////////////////////////////////////////////////////

/// The file extensions which should be checked for C project analysis.
static C_FILE_EXTENSIONS: &[&str] = &["c", "h"];

#[derive(Default, Clone, Debug)]
pub struct C;

impl C {
    fn analyse_clang_entity(entity: &clang::Entity) -> Fallible<Option<EntityType>> {
        let entity_kind = entity.get_kind();

        // Search for functions outside the system headers
        if !entity.is_in_system_header() {
            match &entity_kind {
                clang::EntityKind::FunctionDecl => {
                    return CFamily::analyse_clang_function_entity(entity);
                }
                clang::EntityKind::EnumDecl => {
                    return CFamily::analyse_clang_enum_entity(entity);
                }
                _ => {}
            }

            return Ok(None);
        }

        Ok(None)
    }
}

impl LanguageType for C {
    fn file_types() -> &'static [&'static str] {
        C_FILE_EXTENSIONS
    }

    fn extract_entities<C: LanguageType>(analysis: &Analysis<C>) -> Fallible<()> {
        if let Some(ref clang) = *CLANG {
            let clang_index = clang::Index::new(&clang, false, false);
            for project_file in analysis.project_files().iter() {
                if let EntityType::Entity(mut index) = EntityType::Entity(Entity::new("")) {
                    let parsed_path = &clang_index.parser(&project_file.path).parse()?;
                    let clang_entity = parsed_path.get_entity();

                    // Iterate through the child entities of the current entity
                    for child in clang_entity.get_children() {
                        if let Ok(Some(entity)) = Self::analyse_clang_entity(&child) {
                            index.add_entity::<Entity>(entity);
                        }
                    }

                    println!("{:#?}", index);
                    project_file.add_entity(index);
                }
            }
        }

        Ok(())
    }
}

////////////////////////////////////////////////////////////////////////////////

/// The file extensions which should be checked for C++ project analysis.
static CPP_FILE_EXTENSIONS: &[&str] = &["cpp", "hpp"];

#[derive(Default, Clone, Debug)]
pub struct Cpp;

impl Cpp {
    fn analyse_clang_entity(entity: &clang::Entity) -> Fallible<Option<EntityType>> {
        let entity_kind = entity.get_kind();

        // Search for functions outside the system headers
        if !entity.is_in_system_header() {
            match &entity_kind {
                clang::EntityKind::Constructor
                | clang::EntityKind::Destructor
                | clang::EntityKind::Method
                | clang::EntityKind::FunctionDecl => {
                    return CFamily::analyse_clang_function_entity(entity);
                }
                clang::EntityKind::EnumDecl => {
                    return CFamily::analyse_clang_enum_entity(entity);
                }
                clang::EntityKind::ClassDecl | clang::EntityKind::Namespace => {
                    return CFamily::analyse_clang_generic_entity(entity);
                }
                _ => {}
            }

            return Ok(None);
        }

        Ok(None)
    }

    fn analyse_clang_entity_tree(parent: &mut Entity, clang_entity: &clang::Entity) -> Fallible<()> {
        // Iterate through the child entities of the current entity
        for child in clang_entity.get_children() {
            if let Ok(Some(entity)) = Self::analyse_clang_entity(&child) {
                if let Some(added_entity) = parent.add_entity(entity) {
                    Self::analyse_clang_entity_tree(added_entity, &child)?;
                }
            }
        }

        Ok(())
    }
}

impl LanguageType for Cpp {
    fn file_types() -> &'static [&'static str] {
        CPP_FILE_EXTENSIONS
    }

    fn extract_entities<Cpp: LanguageType>(analysis: &Analysis<Cpp>) -> Fallible<()> {
        if let Some(ref clang) = *CLANG {
            let clang_index = clang::Index::new(&clang, false, false);
            for project_file in analysis.project_files().iter() {
                if let EntityType::Entity(mut index) = EntityType::Entity(Entity::new("")) {
                    let parsed_path = &clang_index.parser(&project_file.path).parse()?;
                    let clang_entity = parsed_path.get_entity();

                    Self::analyse_clang_entity_tree(&mut index, &clang_entity)?;

                    println!("{:#?}", index);
                    project_file.add_entity(index);
                }
            }
        }

        Ok(())
    }
}

////////////////////////////////////////////////////////////////////////////////

/// The file extensions which should be checked for Python project analysis.
static PYTHON_FILE_EXTENSIONS: &[&str] = &["py"];

#[derive(Default, Clone, Debug)]
pub struct Python;

impl Python {
    fn extract_function_doc(function: &mut Function, statement: &Statement) {
        if let Statement::Assignment(ent_v, _) = statement {
            for ent in ent_v.iter() {
                if let Expression::String(expr_v) = ent {
                    for expr in expr_v.iter() {
                        function.set_description(&expr.content.to_string_lossy());
                    }
                }
            }
        }
    }

    fn analyse_statement(entity: &mut Entity, statement: &Statement) -> Fallible<()> {
        if let Statement::Compound(ent_box) = statement {
            match Box::leak((*ent_box).clone()) {
                // Statement is a statement definition
                CompoundStatement::Funcdef(expr) => {
                    let mut function: Function = Function::new(expr.name.as_str());

                    // Split arguments and add them to the function
                    let mut arguments: Vec<Argument> = Vec::new();
                    for arg in &expr.parameters.positional_args {
                        arguments.push(Argument::new(arg.0.as_str(), None));
                    }
                    function.set_arguments(&arguments);

                    if let Some(mut function_inst) =
                        entity.add_entity(EntityType::Function(function))
                    {
                        Self::extract_function_doc(&mut function_inst, &expr.code[0]);
                    }
                }

                // Statement is a class definition
                CompoundStatement::Classdef(expr) => {
                    if let Some(ref mut class_entity) =
                        entity.add_entity(EntityType::Entity(Entity::new(expr.name.as_str())))
                    {
                        for code in &expr.code {
                            Self::analyse_statement(class_entity, &code)?;
                        }
                    }
                }
                _ => {}
            }
        }

        Ok(())
    }
}

impl LanguageType for Python {
    fn file_types() -> &'static [&'static str] {
        PYTHON_FILE_EXTENSIONS
    }

    fn extract_entities<Python: LanguageType>(analysis: &Analysis<Python>) -> Fallible<()> {
        for project_file in analysis.project_files().iter() {
            // Parse file to string
            let mut file = File::open(&project_file.path)?;
            let mut content = String::new();
            file.read_to_string(&mut content)?;

            if let EntityType::Entity(mut index) = EntityType::Entity(Entity::new("")) {
                match file_input(make_strspan(content.as_str())) {
                    Ok(ast) => {
                        for entity in ast.1.iter() {
                            Self::analyse_statement(&mut index, entity)?;
                        }
                    }
                    Err(_) => bail!("Unable to create python AST."),
                }
                println!("{:#?}", index);
                project_file.add_entity(index);
            }
        }

        Ok(())
    }
}

////////////////////////////////////////////////////////////////////////////////

#[cfg(test)]
mod c {
    use super::{Analysis, LanguageType, C, C_FILE_EXTENSIONS};

    #[test]
    fn new() {
        let analysis: Analysis<C> = Analysis::new();

        assert_eq!(analysis.file_types, C_FILE_EXTENSIONS);
        assert_eq!(analysis.project_files().len(), 0);
    }

    #[test]
    fn file_types() {
        assert_eq!(C::file_types(), C_FILE_EXTENSIONS);
    }
}

#[cfg(test)]
mod cpp {
    use super::{Analysis, Cpp, LanguageType, CPP_FILE_EXTENSIONS};

    #[test]
    fn new() {
        let analysis: Analysis<Cpp> = Analysis::new();

        assert_eq!(analysis.file_types, CPP_FILE_EXTENSIONS);
        assert_eq!(analysis.project_files().len(), 0);
    }

    #[test]
    fn file_types() {
        assert_eq!(Cpp::file_types(), CPP_FILE_EXTENSIONS);
    }
}

#[cfg(test)]
mod python {
    use super::{Analysis, LanguageType, Python, PYTHON_FILE_EXTENSIONS};

    #[test]
    fn new() {
        let analysis: Analysis<Python> = Analysis::new();

        assert_eq!(analysis.file_types, PYTHON_FILE_EXTENSIONS);
        assert_eq!(analysis.project_files().len(), 0);
    }

    #[test]
    fn file_types() {
        assert_eq!(Python::file_types(), PYTHON_FILE_EXTENSIONS);
    }
}
<commit_msg>Add comment parsing for clang generic entities<commit_after>use analysis::{Analysis, Argument, Enum, Function};
use clang;
use entity::{Entity, EntityType};
use failure::{err_msg, Fallible};
use python_parser::ast::{CompoundStatement, Expression, Statement};
use python_parser::{file_input, make_strspan};
use std::fs::File;
use std::io::Read;

////////////////////////////////////////////////////////////////////////////////

lazy_static! {
    static ref CLANG: Option<clang::Clang> = {
        match clang::Clang::new() {
            Ok(clang) => Some(clang),
            Err(_) => None,
        }
    };
}

////////////////////////////////////////////////////////////////////////////////

pub trait LanguageType: Default {
    fn file_types() -> &'static [&'static str];
    fn extract_entities<T: LanguageType>(analysis: &Analysis<T>) -> Fallible<()>;
}

////////////////////////////////////////////////////////////////////////////////

#[derive(Default, Clone, Debug)]
struct CFamily;

impl CFamily {
    fn format_arguments(arguments: &[clang::Entity]) -> Fallible<Vec<Argument>> {
        let mut args = Vec::new();

        for argument in arguments {
            args.push(Argument::new(
                argument.get_display_name().unwrap_or(String::new()),
                Some(
                    argument
                        .get_type()
                        .ok_or_else(|| err_msg("Argument type can not be parsed from signature."))?
                        .get_display_name(),
                ),
            ));
        }

        Ok(args)
    }

    fn analyse_clang_function_entity(entity: &clang::Entity) -> Fallible<Option<EntityType>> {
        if let Some(entity_name) = entity.get_name() {
            let mut function = Function::new(entity_name);

            // Set return type.
            if let Some(return_type) = entity.get_type() {
                function.set_return_type(return_type.get_display_name().as_str())?;
            }

            // Set arguments vector.
            if let Some(arguments) = entity.get_arguments() {
                function.set_arguments(&Self::format_arguments(&arguments)?);
            }

            // Set description.
            if let Some(description) = entity.get_comment() {
                function.set_description(description.as_str());
            }

            return Ok(Some(EntityType::Function(function)));
        }

        Ok(None)
    }

    fn analyse_clang_enum_entity(entity: &clang::Entity) -> Fallible<Option<EntityType>> {
        if let Some(entity_name) = entity.get_name() {
            let enumeration = Enum::new(entity_name);

            return Ok(Some(EntityType::Enum(enumeration)));
        }

        Ok(None)
    }

    fn analyse_clang_generic_entity(entity: &clang::Entity) -> Fallible<Option<EntityType>> {
        if let Some(entity_name) = entity.get_name() {
            let mut ent = Entity::new(entity_name);

            // Set description.
            if let Some(description) = entity.get_comment() {
                ent.set_description(description.as_str());
            }

            return Ok(Some(EntityType::Entity(ent)));
        }

        Ok(None)
    }
}

////////////////////////////////////////////////////////////////////////////////

/// The file extensions which should be checked for C project analysis.
static C_FILE_EXTENSIONS: &[&str] = &["c", "h"];

#[derive(Default, Clone, Debug)]
pub struct C;

impl C {
    fn analyse_clang_entity(entity: &clang::Entity) -> Fallible<Option<EntityType>> {
        let entity_kind = entity.get_kind();

        // Search for functions outside the system headers
        if !entity.is_in_system_header() {
            match &entity_kind {
                clang::EntityKind::FunctionDecl => {
                    return CFamily::analyse_clang_function_entity(entity);
                }
                clang::EntityKind::EnumDecl => {
                    return CFamily::analyse_clang_enum_entity(entity);
                }
                _ => {}
            }

            return Ok(None);
        }

        Ok(None)
    }
}

impl LanguageType for C {
    fn file_types() -> &'static [&'static str] {
        C_FILE_EXTENSIONS
    }

    fn extract_entities<C: LanguageType>(analysis: &Analysis<C>) -> Fallible<()> {
        if let Some(ref clang) = *CLANG {
            let clang_index = clang::Index::new(&clang, false, false);
            for project_file in analysis.project_files().iter() {
                if let EntityType::Entity(mut index) = EntityType::Entity(Entity::new("")) {
                    let parsed_path = &clang_index.parser(&project_file.path).parse()?;
                    let clang_entity = parsed_path.get_entity();

                    // Iterate through the child entities of the current entity
                    for child in clang_entity.get_children() {
                        if let Ok(Some(entity)) = Self::analyse_clang_entity(&child) {
                            index.add_entity::<Entity>(entity);
                        }
                    }

                    println!("{:#?}", index);
                    project_file.add_entity(index);
                }
            }
        }

        Ok(())
    }
}

////////////////////////////////////////////////////////////////////////////////

/// The file extensions which should be checked for C++ project analysis.
static CPP_FILE_EXTENSIONS: &[&str] = &["cpp", "hpp"];

#[derive(Default, Clone, Debug)]
pub struct Cpp;

impl Cpp {
    fn analyse_clang_entity(entity: &clang::Entity) -> Fallible<Option<EntityType>> {
        let entity_kind = entity.get_kind();

        // Search for functions outside the system headers
        if !entity.is_in_system_header() {
            match &entity_kind {
                clang::EntityKind::Constructor
                | clang::EntityKind::Destructor
                | clang::EntityKind::Method
                | clang::EntityKind::FunctionDecl => {
                    return CFamily::analyse_clang_function_entity(entity);
                }
                clang::EntityKind::EnumDecl => {
                    return CFamily::analyse_clang_enum_entity(entity);
                }
                clang::EntityKind::ClassDecl | clang::EntityKind::Namespace => {
                    return CFamily::analyse_clang_generic_entity(entity);
                }
                _ => {}
            }

            return Ok(None);
        }

        Ok(None)
    }

    fn analyse_clang_entity_tree(
        parent: &mut Entity,
        clang_entity: &clang::Entity,
    ) -> Fallible<()> {
        // Iterate through the child entities of the current entity
        for child in clang_entity.get_children() {
            if let Ok(Some(entity)) = Self::analyse_clang_entity(&child) {
                if let Some(added_entity) = parent.add_entity(entity) {
                    Self::analyse_clang_entity_tree(added_entity, &child)?;
                }
            }
        }

        Ok(())
    }
}

impl LanguageType for Cpp {
    fn file_types() -> &'static [&'static str] {
        CPP_FILE_EXTENSIONS
    }

    fn extract_entities<Cpp: LanguageType>(analysis: &Analysis<Cpp>) -> Fallible<()> {
        if let Some(ref clang) = *CLANG {
            let clang_index = clang::Index::new(&clang, false, false);
            for project_file in analysis.project_files().iter() {
                if let EntityType::Entity(mut index) = EntityType::Entity(Entity::new("")) {
                    let parsed_path = &clang_index.parser(&project_file.path).parse()?;
                    let clang_entity = parsed_path.get_entity();

                    Self::analyse_clang_entity_tree(&mut index, &clang_entity)?;

                    println!("{:#?}", index);
                    project_file.add_entity(index);
                }
            }
        }

        Ok(())
    }
}

////////////////////////////////////////////////////////////////////////////////

/// The file extensions which should be checked for Python project analysis.
static PYTHON_FILE_EXTENSIONS: &[&str] = &["py"];

#[derive(Default, Clone, Debug)]
pub struct Python;

impl Python {
    fn extract_function_doc(function: &mut Function, statement: &Statement) {
        if let Statement::Assignment(ent_v, _) = statement {
            for ent in ent_v.iter() {
                if let Expression::String(expr_v) = ent {
                    for expr in expr_v.iter() {
                        function.set_description(&expr.content.to_string_lossy());
                    }
                }
            }
        }
    }

    fn analyse_statement(entity: &mut Entity, statement: &Statement) -> Fallible<()> {
        if let Statement::Compound(ent_box) = statement {
            match Box::leak((*ent_box).clone()) {
                // Statement is a statement definition
                CompoundStatement::Funcdef(expr) => {
                    let mut function: Function = Function::new(expr.name.as_str());

                    // Split arguments and add them to the function
                    let mut arguments: Vec<Argument> = Vec::new();
                    for arg in &expr.parameters.positional_args {
                        arguments.push(Argument::new(arg.0.as_str(), None));
                    }
                    function.set_arguments(&arguments);

                    if let Some(mut function_inst) =
                        entity.add_entity(EntityType::Function(function))
                    {
                        Self::extract_function_doc(&mut function_inst, &expr.code[0]);
                    }
                }

                // Statement is a class definition
                CompoundStatement::Classdef(expr) => {
                    if let Some(ref mut class_entity) =
                        entity.add_entity(EntityType::Entity(Entity::new(expr.name.as_str())))
                    {
                        for code in &expr.code {
                            Self::analyse_statement(class_entity, &code)?;
                        }
                    }
                }
                _ => {}
            }
        }

        Ok(())
    }
}

impl LanguageType for Python {
    fn file_types() -> &'static [&'static str] {
        PYTHON_FILE_EXTENSIONS
    }

    fn extract_entities<Python: LanguageType>(analysis: &Analysis<Python>) -> Fallible<()> {
        for project_file in analysis.project_files().iter() {
            // Parse file to string
            let mut file = File::open(&project_file.path)?;
            let mut content = String::new();
            file.read_to_string(&mut content)?;

            if let EntityType::Entity(mut index) = EntityType::Entity(Entity::new("")) {
                match file_input(make_strspan(content.as_str())) {
                    Ok(ast) => {
                        for entity in ast.1.iter() {
                            Self::analyse_statement(&mut index, entity)?;
                        }
                    }
                    Err(_) => bail!("Unable to create python AST."),
                }
                println!("{:#?}", index);
                project_file.add_entity(index);
            }
        }

        Ok(())
    }
}

////////////////////////////////////////////////////////////////////////////////

#[cfg(test)]
mod c {
    use super::{Analysis, LanguageType, C, C_FILE_EXTENSIONS};

    #[test]
    fn new() {
        let analysis: Analysis<C> = Analysis::new();

        assert_eq!(analysis.file_types, C_FILE_EXTENSIONS);
        assert_eq!(analysis.project_files().len(), 0);
    }

    #[test]
    fn file_types() {
        assert_eq!(C::file_types(), C_FILE_EXTENSIONS);
    }
}

#[cfg(test)]
mod cpp {
    use super::{Analysis, Cpp, LanguageType, CPP_FILE_EXTENSIONS};

    #[test]
    fn new() {
        let analysis: Analysis<Cpp> = Analysis::new();

        assert_eq!(analysis.file_types, CPP_FILE_EXTENSIONS);
        assert_eq!(analysis.project_files().len(), 0);
    }

    #[test]
    fn file_types() {
        assert_eq!(Cpp::file_types(), CPP_FILE_EXTENSIONS);
    }
}

#[cfg(test)]
mod python {
    use super::{Analysis, LanguageType, Python, PYTHON_FILE_EXTENSIONS};

    #[test]
    fn new() {
        let analysis: Analysis<Python> = Analysis::new();

        assert_eq!(analysis.file_types, PYTHON_FILE_EXTENSIONS);
        assert_eq!(analysis.project_files().len(), 0);
    }

    #[test]
    fn file_types() {
        assert_eq!(Python::file_types(), PYTHON_FILE_EXTENSIONS);
    }
}
<|endoftext|>


<commit_before>#![deny(warnings)]
#![feature(const_fn)]

extern crate orbclient;
extern crate orbimage;
extern crate orbfont;

use std::env;
use std::fs::File;
use std::path::Path;
use std::process::Command;
use std::thread;

use orbclient::{Color, EventOption, Window};
use orbimage::Image;
use orbfont::Font;

use package::Package;

pub mod package;

const BAR_COLOR: Color = Color::rgb(40, 45, 57);
const BAR_HIGHLIGHT_COLOR: Color = Color::rgb(80, 86, 102);
const TEXT_COLOR: Color = Color::rgb(204, 210, 224);
const TEXT_HIGHLIGHT_COLOR: Color = Color::rgb(235, 241, 255);

//TODO: Implement display size in orbclient
#[cfg(target_os = "redox")]
fn get_display_size() -> (i32, i32) {
    match File::open("display:") {
        Ok(display) => {
            let path = display.path().map(|path| path.into_os_string().into_string().unwrap_or(String::new())).unwrap_or(String::new());
            let res = path.split(":").nth(1).unwrap_or("");
            let width = res.split("/").nth(0).unwrap_or("").parse::<i32>().unwrap_or(0);
            let height = res.split("/").nth(1).unwrap_or("").parse::<i32>().unwrap_or(0);
            (width, height)
        },
        Err(err) => panic!("launcher: failed to get display size: {}", err)
    }
}

#[cfg(not(target_os = "redox"))]
fn get_display_size() -> (i32, i32) {
    panic!("launcher: failed to get display size")
}

fn get_packages() -> Vec<Package> {
    let read_dir = Path::new("/apps/").read_dir().expect("failed to read_dir on /apps/");

    let mut entries = vec![];
    for dir in read_dir {
        let dir = match dir {
            Ok(x) => x,
            Err(_) => continue,
        };
        let file_name = dir.file_name().to_string_lossy().into_owned();
        if dir.file_type().expect("failed to get file_type").is_dir() {
            entries.push(file_name);
        }
    }

    entries.sort();

    let mut packages: Vec<Package> = Vec::new();
    for entry in entries.iter() {
        packages.push(Package::from_path(&format!("/apps/{}", entry)));
    }

    packages
}

fn draw(window: &mut Window, packages: &Vec<Package>, shutdown: &Image, selected: i32){
    let w = window.width();
    let h = window.height();
    window.set(BAR_COLOR);

    let mut x = 0;
    let mut i = 0;
    for package in packages.iter() {
        let y = h as isize - package.icon.height() as isize;

        if i == selected {
            window.rect(x as i32, y as i32,
                              package.icon.width() as u32, package.icon.height() as u32,
                              BAR_HIGHLIGHT_COLOR);
        }

        package.icon.draw(window, x as i32, y as i32);

        x = x + package.icon.width() as i32;
        i += 1;
    }

    x = w as i32 - shutdown.width() as i32;
    let y = h as isize - shutdown.height() as isize;

    if i == selected {
        window.rect(x as i32, y as i32,
                          shutdown.width() as u32, shutdown.height() as u32,
                          BAR_HIGHLIGHT_COLOR);
    }

    shutdown.draw(window, x as i32, y as i32);

    window.sync();
}

fn draw_chooser(window: &mut Window, font: &Font, packages: &Vec<Package>, _mouse_x: i32, mouse_y: i32){
    let w = window.width();

    window.set(BAR_COLOR);

    let mut y = 0;
    for package in packages.iter() {
        let highlight = mouse_y >= y as i32 && mouse_y < y + 32;

        if highlight {
            window.rect(0, y, w, 32, BAR_HIGHLIGHT_COLOR);
        }

        package.icon.draw(window, 0, y);

        let mut c_x = 40;
        for c in package.name.chars() {
            font.render(&c.to_string(), 16.0).draw(window, c_x as i32, y + 8, if highlight { TEXT_HIGHLIGHT_COLOR } else { TEXT_COLOR });
            c_x += 8;
        }

        y += 32;
    }

    window.sync();
}

fn main() {
    let paths = env::args().skip(1);
    if paths.len() > 0 {
        for ref path in paths {
            let mut packages = get_packages();

            packages.retain(|package| -> bool {
                for accept in package.accepts.iter() {
                    if (accept.starts_with('*') && path.ends_with(&accept[1 ..])) ||
                       (accept.ends_with('*') && path.starts_with(&accept[.. accept.len() - 1])) {
                        return true;
                    }
                }
                false
            });

            if packages.len() > 1 {
                let mut window = Window::new(-1, -1, 400, packages.len() as u32 * 32, path).unwrap();
                let font = Font::find(None, None, None).unwrap();

                draw_chooser(&mut window, &font, &packages, -1, -1);
                'choosing: loop {
                    for event in window.events() {
                        match event.to_option() {
                            EventOption::Mouse(mouse_event) => {
                                draw_chooser(&mut window, &font, &packages, mouse_event.x, mouse_event.y);

                                if mouse_event.left_button {
                                    let mut y = 0;
                                    for package in packages.iter() {
                                        if mouse_event.y >= y && mouse_event.y < y + 32 {
                                            if let Err(err) = Command::new(&package.binary).arg(path).spawn() {
                                                println!("{}: Failed to launch: {}", package.binary, err);
                                            }
                                            break 'choosing;
                                        }
                                        y += 32;
                                    }
                                }
                            },
                            EventOption::Quit(_) => break 'choosing,
                            _ => ()
                        }
                    }

                    thread::yield_now();
                }
            } else if let Some(package) = packages.get(0) {
                if let Err(err) = Command::new(&package.binary).arg(&path).spawn() {
                    println!("launcher: failed to launch '{}': {}", package.binary, err);
                }
            } else {
                println!("launcher: no application found for '{}'", path);
            }
        }
    } else {
        let packages = get_packages();

        let shutdown = Image::from_path("/ui/actions/system-shutdown.bmp").unwrap_or(Image::default());

        let (width, height) = get_display_size();
        let mut window = Window::new(0, height - 32, width as u32, 32, "").unwrap();

        let mut selected = -1;

        draw(&mut window, &packages, &shutdown, selected);
        'running: loop {
            for event in window.events() {
                match event.to_option() {
                    EventOption::Mouse(mouse_event) => {
                        let mut now_selected = -1;

                        {
                            let mut x = 0;
                            let mut i = 0;
                            for package in packages.iter() {
                                let y = window.height() as i32 - package.icon.height() as i32;
                                if mouse_event.y >= y && mouse_event.x >= x &&
                                   mouse_event.x < x + package.icon.width() as i32 {
                                    now_selected = i;
                                }
                                x = x + package.icon.width() as i32;
                                i += 1;
                            }

                            x = window.width() as i32 - shutdown.width() as i32;
                            let y = window.height() as i32 - shutdown.height() as i32;
                            if mouse_event.y >= y && mouse_event.x >= x &&
                               mouse_event.x < x + shutdown.width() as i32 {
                                   now_selected = i;
                            }
                        }

                        if now_selected != selected {
                            selected = now_selected;
                            draw(&mut window, &packages, &shutdown, selected);
                        }

                        if mouse_event.left_button {
                            let mut i = 0;
                            for package in packages.iter() {
                                if i == selected {
                                    if let Err(err) = Command::new(&package.binary).spawn() {
                                        println!("{}: Failed to launch: {}", package.binary, err);
                                    }
                                }
                                i += 1;
                            }

                            if i == selected {
                                   File::create("acpi:off").unwrap();
                            }
                        }
                    },
                    EventOption::Quit(_) => break 'running,
                    _ => ()
                }
            }

            thread::yield_now();
        }
    }
}
<commit_msg>Start menu<commit_after>#![deny(warnings)]
#![feature(const_fn)]

extern crate orbclient;
extern crate orbimage;
extern crate orbfont;

use std::env;
use std::fs::File;
use std::path::Path;
use std::process::Command;

use orbclient::{Color, EventOption, Window};
use orbimage::Image;
use orbfont::Font;

use package::Package;

pub mod package;

const BAR_COLOR: Color = Color::rgb(40, 45, 57);
const BAR_HIGHLIGHT_COLOR: Color = Color::rgb(80, 86, 102);
const TEXT_COLOR: Color = Color::rgb(204, 210, 224);
const TEXT_HIGHLIGHT_COLOR: Color = Color::rgb(235, 241, 255);

//TODO: Implement display size in orbclient
#[cfg(target_os = "redox")]
fn get_display_size() -> (i32, i32) {
    match File::open("display:") {
        Ok(display) => {
            let path = display.path().map(|path| path.into_os_string().into_string().unwrap_or(String::new())).unwrap_or(String::new());
            let res = path.split(":").nth(1).unwrap_or("");
            let width = res.split("/").nth(0).unwrap_or("").parse::<i32>().unwrap_or(0);
            let height = res.split("/").nth(1).unwrap_or("").parse::<i32>().unwrap_or(0);
            (width, height)
        },
        Err(err) => panic!("launcher: failed to get display size: {}", err)
    }
}

#[cfg(not(target_os = "redox"))]
fn get_display_size() -> (i32, i32) {
    panic!("launcher: failed to get display size")
}

fn get_packages() -> Vec<Package> {
    let read_dir = Path::new("/apps/").read_dir().expect("failed to read_dir on /apps/");

    let mut entries = vec![];
    for dir in read_dir {
        let dir = match dir {
            Ok(x) => x,
            Err(_) => continue,
        };
        let file_name = dir.file_name().to_string_lossy().into_owned();
        if dir.file_type().expect("failed to get file_type").is_dir() {
            entries.push(file_name);
        }
    }

    entries.sort();

    let mut packages: Vec<Package> = Vec::new();
    for entry in entries.iter() {
        packages.push(Package::from_path(&format!("/apps/{}", entry)));
    }

    packages
}

fn draw(window: &mut Window, packages: &Vec<Package>, start: &Image, shutdown: &Image, selected: i32){
    let w = window.width();
    let h = window.height();
    window.set(BAR_COLOR);

    let mut x = 0;
    let mut i = 0;

    {
        let y = h as isize - start.height() as isize;

        if i == selected {
            window.rect(x as i32, y as i32,
                              start.width() as u32, start.height() as u32,
                              BAR_HIGHLIGHT_COLOR);
        }

        start.draw(window, x as i32, y as i32);

        x += start.width() as i32;
        i += 1;
    }

    for package in packages.iter() {
        let y = h as isize - package.icon.height() as isize;

        if i == selected {
            window.rect(x as i32, y as i32,
                              package.icon.width() as u32, package.icon.height() as u32,
                              BAR_HIGHLIGHT_COLOR);
        }

        package.icon.draw(window, x as i32, y as i32);

        x += package.icon.width() as i32;
        i += 1;
    }

    {
        x = w as i32 - shutdown.width() as i32;
        let y = h as isize - shutdown.height() as isize;

        if i == selected {
            window.rect(x as i32, y as i32,
                              shutdown.width() as u32, shutdown.height() as u32,
                              BAR_HIGHLIGHT_COLOR);
        }

        shutdown.draw(window, x as i32, y as i32);
    }

    window.sync();
}

fn draw_chooser(window: &mut Window, font: &Font, packages: &Vec<Package>, _mouse_x: i32, mouse_y: i32){
    let w = window.width();

    window.set(BAR_COLOR);

    let mut y = 0;
    for package in packages.iter() {
        let highlight = mouse_y >= y as i32 && mouse_y < y + 32;

        if highlight {
            window.rect(0, y, w, 32, BAR_HIGHLIGHT_COLOR);
        }

        package.icon.draw(window, 0, y);

        let mut c_x = 40;
        for c in package.name.chars() {
            font.render(&c.to_string(), 16.0).draw(window, c_x as i32, y + 8, if highlight { TEXT_HIGHLIGHT_COLOR } else { TEXT_COLOR });
            c_x += 8;
        }

        y += 32;
    }

    window.sync();
}

fn main() {
    let paths = env::args().skip(1);
    if paths.len() > 0 {
        for ref path in paths {
            let mut packages = get_packages();

            packages.retain(|package| -> bool {
                for accept in package.accepts.iter() {
                    if (accept.starts_with('*') && path.ends_with(&accept[1 ..])) ||
                       (accept.ends_with('*') && path.starts_with(&accept[.. accept.len() - 1])) {
                        return true;
                    }
                }
                false
            });

            if packages.len() > 1 {
                let mut window = Window::new(-1, -1, 400, packages.len() as u32 * 32, path).unwrap();
                let font = Font::find(None, None, None).unwrap();

                draw_chooser(&mut window, &font, &packages, -1, -1);
                'choosing: loop {
                    for event in window.events() {
                        match event.to_option() {
                            EventOption::Mouse(mouse_event) => {
                                draw_chooser(&mut window, &font, &packages, mouse_event.x, mouse_event.y);

                                if mouse_event.left_button {
                                    let mut y = 0;
                                    for package in packages.iter() {
                                        if mouse_event.y >= y && mouse_event.y < y + 32 {
                                            if let Err(err) = Command::new(&package.binary).arg(path).spawn() {
                                                println!("{}: Failed to launch: {}", package.binary, err);
                                            }
                                            break 'choosing;
                                        }
                                        y += 32;
                                    }
                                }
                            },
                            EventOption::Quit(_) => break 'choosing,
                            _ => ()
                        }
                    }
                }
            } else if let Some(package) = packages.get(0) {
                if let Err(err) = Command::new(&package.binary).arg(&path).spawn() {
                    println!("launcher: failed to launch '{}': {}", package.binary, err);
                }
            } else {
                println!("launcher: no application found for '{}'", path);
            }
        }
    } else {
        let packages = get_packages();

        let start = Image::from_path("/ui/apps/kde.bmp").unwrap_or(Image::default());

        let shutdown = Image::from_path("/ui/actions/system-shutdown.bmp").unwrap_or(Image::default());

        let (width, height) = get_display_size();
        let mut window = Window::new(0, height - 32, width as u32, 32, "").unwrap();

        let mut selected = -1;

        draw(&mut window, &packages, &start, &shutdown, selected);
        'running: loop {
            for event in window.events() {
                match event.to_option() {
                    EventOption::Mouse(mouse_event) => {
                        let mut now_selected = -1;

                        {
                            let mut x = 0;
                            let mut i = 0;

                            {
                                let y = window.height() as i32 - start.height() as i32;
                                if mouse_event.y >= y && mouse_event.x >= x &&
                                   mouse_event.x < x + start.width() as i32 {
                                       now_selected = i;
                                }
                                x += start.width() as i32;
                                i += 1;
                            }

                            for package in packages.iter() {
                                let y = window.height() as i32 - package.icon.height() as i32;
                                if mouse_event.y >= y && mouse_event.x >= x &&
                                   mouse_event.x < x + package.icon.width() as i32 {
                                    now_selected = i;
                                }
                                x += package.icon.width() as i32;
                                i += 1;
                            }

                            {
                                x = window.width() as i32 - shutdown.width() as i32;
                                let y = window.height() as i32 - shutdown.height() as i32;
                                if mouse_event.y >= y && mouse_event.x >= x &&
                                   mouse_event.x < x + shutdown.width() as i32 {
                                       now_selected = i;
                                }
                            }
                        }

                        if now_selected != selected {
                            selected = now_selected;
                            draw(&mut window, &packages, &start, &shutdown, selected);
                        }

                        if mouse_event.left_button {
                            let mut i = 0;

                            if i == selected {
                                let start_h = packages.len() as u32 * 32;
                                let mut start_window = Window::new(0, height - 32 - start_h as i32, 400, start_h, "Start").unwrap();
                                let font = Font::find(None, None, None).unwrap();

                                draw_chooser(&mut start_window, &font, &packages, -1, -1);
                                'start_choosing: loop {
                                    for event in start_window.events() {
                                        match event.to_option() {
                                            EventOption::Mouse(mouse_event) => {
                                                draw_chooser(&mut start_window, &font, &packages, mouse_event.x, mouse_event.y);

                                                if mouse_event.left_button {
                                                    let mut y = 0;
                                                    for package in packages.iter() {
                                                        if mouse_event.y >= y && mouse_event.y < y + 32 {
                                                            if let Err(err) = Command::new(&package.binary).spawn() {
                                                                println!("{}: Failed to launch: {}", package.binary, err);
                                                            }
                                                            break 'start_choosing;
                                                        }
                                                        y += 32;
                                                    }
                                                }
                                            },
                                            EventOption::Quit(_) => break 'start_choosing,
                                            _ => ()
                                        }
                                    }
                                }
                            }
                            i += 1;

                            for package in packages.iter() {
                                if i == selected {
                                    if let Err(err) = Command::new(&package.binary).spawn() {
                                        println!("{}: Failed to launch: {}", package.binary, err);
                                    }
                                }
                                i += 1;
                            }

                            if i == selected {
                                   File::create("acpi:off").unwrap();
                            }
                        }
                    },
                    EventOption::Quit(_) => break 'running,
                    _ => ()
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2014 Michael Yang. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
use std::cmp;
use std::mem;
use libc::c_int;
use num::complex::{
    Complex32,
    Complex64,
};
use error::Error;
use ll::*;
use matrix::{
    Matrix,
};
use scalar::Scalar;
use types::Layout;
use util::transpose_data;

pub trait Gels: Sized {
    fn gels(layout: Layout, a: &mut Matrix<Self>, b: &mut Matrix<Self>) -> Result<(), Error>;
    fn gels_work(layout: Layout, a: &mut Matrix<Self>, b: &mut Matrix<Self>, work: &mut [Self]) -> Result<(), Error>;
    fn gels_work_len(a: &mut Matrix<Self>, b: &mut Matrix<Self>) -> Result<usize, Error>;
}

macro_rules! least_sq_impl(($($t: ident), +) => ($(
    impl Gels for $t {
        fn gels(layout: Layout, a: &mut Matrix<Self>, b: &mut Matrix<Self>) -> Result<(), Error> {
            //TODO: nancheck

            let work_len = try!(Gels::gels_work_len(a, b));
            let mut work: Vec<$t> = Vec::with_capacity(work_len as usize);
            unsafe {
                work.set_len(work_len as usize);
            }

            Gels::gels_work(layout, a, b, &mut work[..])
        }

        fn gels_work(layout: Layout, a: &mut Matrix<Self>, b: &mut Matrix<Self>, work: &mut [Self]) -> Result<(), Error> {
            let mut info: c_int = 0;

            let m = a.rows();
            let n = a.cols();
            let nrhs = b.cols();

            match layout {
                Layout::ColMajor => unsafe {
                    let lda = m;
                    let ldb = b.rows();

                    prefix!($t, gels_)(
                        a.transpose().as_i8().as_mut(),
                        m.as_mut(), n.as_mut(),
                        nrhs.as_mut(),
                        a.as_mut_ptr(), lda.as_mut(),
                        b.as_mut_ptr(), ldb.as_mut(),
                        work.as_mut_ptr(), (work.len() as c_int).as_mut(),
                        &mut info as *mut c_int);
                },
                Layout::RowMajor => {
                    let lda = n;
                    let ldb = nrhs;
                    let mrhs = cmp::max(m, n);

                    let lda_t = cmp::max(1, m);
                    let ldb_t = cmp::max(1, mrhs);

                    let a_t_len = (lda_t * cmp::max(1, n)) as usize;
                    let b_t_len = (ldb_t * cmp::max(1, nrhs)) as usize;
                    let mut a_t = Vec::with_capacity(a_t_len);
                    let mut b_t = Vec::with_capacity(b_t_len);

                    unsafe {
                        a_t.set_len(a_t_len);
                        b_t.set_len(b_t_len);

                        transpose_data(Layout::RowMajor, m as isize, n as isize, a.as_ptr(), lda as isize, a_t.as_mut_ptr(), lda_t as isize);
                        transpose_data(Layout::RowMajor, mrhs as isize, nrhs as isize, b.as_ptr(), ldb as isize, b_t.as_mut_ptr(), ldb_t as isize);

                        prefix!($t, gels_)(
                            a.transpose().as_i8().as_mut(),
                            m.as_mut(), n.as_mut(),
                            nrhs.as_mut(),
                            a_t.as_mut_ptr(), lda_t.as_mut(),
                            b_t.as_mut_ptr(), ldb_t.as_mut(),
                            work.as_mut_ptr(), (work.len() as c_int).as_mut(),
                            &mut info as *mut c_int);

                        transpose_data(Layout::ColMajor, m as isize, n as isize, a_t.as_ptr(), lda_t as isize, a.as_mut_ptr(), lda as isize);
                        transpose_data(Layout::ColMajor, mrhs as isize, nrhs as isize, b_t.as_ptr(), ldb_t as isize, b.as_mut_ptr(), ldb as isize);
                    }
                }
            }

            match info {
                0 => Ok(()),
                x if x < 0 => Err(Error::IllegalParameter(-x as usize)),
                x => Err(Error::DiagonalElementZero(x as usize)),
            }
        }

        fn gels_work_len(a: &mut Matrix<Self>, b: &mut Matrix<Self>) -> Result<usize, Error> {
            let mut info: c_int = 0;
            let mut len_info: $t = unsafe { mem::zeroed() };
            let len_ptr = (&mut len_info) as *mut $t;

            let m = a.rows();
            let n = a.cols();
            let nrhs = b.cols();
            let lda_t = cmp::max(1, m);
            let ldb_t = cmp::max(1, cmp::max(m, n));

            unsafe {
                prefix!($t, gels_)(
                    a.transpose().as_i8().as_mut(),
                    m.as_mut(), n.as_mut(),
                    nrhs.as_mut(),
                    a.as_mut_ptr(), lda_t.as_mut(),
                    b.as_mut_ptr(), ldb_t.as_mut(),
                    len_ptr, (-1 as c_int).as_mut(),
                    &mut info as *mut c_int);
            };

            match info {
                0 => Ok(len_info.as_work()),
                x if x < 0 => Err(Error::IllegalParameter(-x as usize)),
                x => Err(Error::DiagonalElementZero(x as usize)),
            }
        }
    }
)+));

least_sq_impl!(f32, f64, Complex32, Complex64);

#[cfg(test)]
mod gesv_tests {
    use types::Layout::*;
    use least_squares::Gels;

    #[test]
    fn col_major() {
        let mut a = (3i32, 2i32, vec![2.0f32,4.0,7.0,3.0,9.0,4.0]);
        let mut b = (3i32, 2i32, vec![2.0f32,4.0,7.0,6.0,18.0,8.0]);

        Gels::gels(ColMajor, &mut a, &mut b).unwrap();

        let (_, _, x) = b;
        assert_eq!(x, vec![1.0, 0.0, 0.0, 0.0, 2.0, 0.0]);
    }

    #[test]
    fn row_major() {
        let mut a = (3i32, 2i32, vec![2.0f32,3.0,4.0,9.0,7.0,4.0]);
        let mut b = (3i32, 2i32, vec![2.0f32,3.0,4.0,9.0,7.0,4.0]);

        Gels::gels(RowMajor, &mut a, &mut b).unwrap();

        let (_, _, x) = b;
        assert_eq!(x, vec![1.0, 0.0, 0.0, 1.0, 0.0, 0.0]);
    }
}
<commit_msg>Derive gels from work functions<commit_after>// Copyright 2014 Michael Yang. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
use std::cmp;
use std::mem;
use libc::c_int;
use num::complex::{
    Complex32,
    Complex64,
};
use error::Error;
use ll::*;
use matrix::{
    Matrix,
};
use scalar::Scalar;
use types::Layout;
use util::transpose_data;

pub trait Gels: Sized {
    fn gels(layout: Layout, a: &mut Matrix<Self>, b: &mut Matrix<Self>) -> Result<(), Error> {
        //TODO: nancheck

        let work_len = try!(Gels::gels_work_len(a, b));
        let mut work: Vec<_> = Vec::with_capacity(work_len as usize);
        unsafe {
            work.set_len(work_len as usize);
        }

        Gels::gels_work(layout, a, b, &mut work[..])
    }

    fn gels_work(layout: Layout, a: &mut Matrix<Self>, b: &mut Matrix<Self>, work: &mut [Self]) -> Result<(), Error>;
    fn gels_work_len(a: &mut Matrix<Self>, b: &mut Matrix<Self>) -> Result<usize, Error>;
}

macro_rules! least_sq_impl(($($t: ident), +) => ($(
    impl Gels for $t {
        fn gels_work(layout: Layout, a: &mut Matrix<Self>, b: &mut Matrix<Self>, work: &mut [Self]) -> Result<(), Error> {
            let mut info: c_int = 0;

            let m = a.rows();
            let n = a.cols();
            let nrhs = b.cols();

            match layout {
                Layout::ColMajor => unsafe {
                    let lda = m;
                    let ldb = b.rows();

                    prefix!($t, gels_)(
                        a.transpose().as_i8().as_mut(),
                        m.as_mut(), n.as_mut(),
                        nrhs.as_mut(),
                        a.as_mut_ptr(), lda.as_mut(),
                        b.as_mut_ptr(), ldb.as_mut(),
                        work.as_mut_ptr(), (work.len() as c_int).as_mut(),
                        &mut info as *mut c_int);
                },
                Layout::RowMajor => {
                    let lda = n;
                    let ldb = nrhs;
                    let mrhs = cmp::max(m, n);

                    let lda_t = cmp::max(1, m);
                    let ldb_t = cmp::max(1, mrhs);

                    let a_t_len = (lda_t * cmp::max(1, n)) as usize;
                    let b_t_len = (ldb_t * cmp::max(1, nrhs)) as usize;
                    let mut a_t = Vec::with_capacity(a_t_len);
                    let mut b_t = Vec::with_capacity(b_t_len);

                    unsafe {
                        a_t.set_len(a_t_len);
                        b_t.set_len(b_t_len);

                        transpose_data(Layout::RowMajor, m as isize, n as isize, a.as_ptr(), lda as isize, a_t.as_mut_ptr(), lda_t as isize);
                        transpose_data(Layout::RowMajor, mrhs as isize, nrhs as isize, b.as_ptr(), ldb as isize, b_t.as_mut_ptr(), ldb_t as isize);

                        prefix!($t, gels_)(
                            a.transpose().as_i8().as_mut(),
                            m.as_mut(), n.as_mut(),
                            nrhs.as_mut(),
                            a_t.as_mut_ptr(), lda_t.as_mut(),
                            b_t.as_mut_ptr(), ldb_t.as_mut(),
                            work.as_mut_ptr(), (work.len() as c_int).as_mut(),
                            &mut info as *mut c_int);

                        transpose_data(Layout::ColMajor, m as isize, n as isize, a_t.as_ptr(), lda_t as isize, a.as_mut_ptr(), lda as isize);
                        transpose_data(Layout::ColMajor, mrhs as isize, nrhs as isize, b_t.as_ptr(), ldb_t as isize, b.as_mut_ptr(), ldb as isize);
                    }
                }
            }

            match info {
                0 => Ok(()),
                x if x < 0 => Err(Error::IllegalParameter(-x as usize)),
                x => Err(Error::DiagonalElementZero(x as usize)),
            }
        }

        fn gels_work_len(a: &mut Matrix<Self>, b: &mut Matrix<Self>) -> Result<usize, Error> {
            let mut info: c_int = 0;
            let mut len_info: $t = unsafe { mem::zeroed() };
            let len_ptr = (&mut len_info) as *mut $t;

            let m = a.rows();
            let n = a.cols();
            let nrhs = b.cols();
            let lda_t = cmp::max(1, m);
            let ldb_t = cmp::max(1, cmp::max(m, n));

            unsafe {
                prefix!($t, gels_)(
                    a.transpose().as_i8().as_mut(),
                    m.as_mut(), n.as_mut(),
                    nrhs.as_mut(),
                    a.as_mut_ptr(), lda_t.as_mut(),
                    b.as_mut_ptr(), ldb_t.as_mut(),
                    len_ptr, (-1 as c_int).as_mut(),
                    &mut info as *mut c_int);
            };

            match info {
                0 => Ok(len_info.as_work()),
                x if x < 0 => Err(Error::IllegalParameter(-x as usize)),
                x => Err(Error::DiagonalElementZero(x as usize)),
            }
        }
    }
)+));

least_sq_impl!(f32, f64, Complex32, Complex64);

#[cfg(test)]
mod gesv_tests {
    use types::Layout::*;
    use least_squares::Gels;

    #[test]
    fn col_major() {
        let mut a = (3i32, 2i32, vec![2.0f32,4.0,7.0,3.0,9.0,4.0]);
        let mut b = (3i32, 2i32, vec![2.0f32,4.0,7.0,6.0,18.0,8.0]);

        Gels::gels(ColMajor, &mut a, &mut b).unwrap();

        let (_, _, x) = b;
        assert_eq!(x, vec![1.0, 0.0, 0.0, 0.0, 2.0, 0.0]);
    }

    #[test]
    fn row_major() {
        let mut a = (3i32, 2i32, vec![2.0f32,3.0,4.0,9.0,7.0,4.0]);
        let mut b = (3i32, 2i32, vec![2.0f32,3.0,4.0,9.0,7.0,4.0]);

        Gels::gels(RowMajor, &mut a, &mut b).unwrap();

        let (_, _, x) = b;
        assert_eq!(x, vec![1.0, 0.0, 0.0, 1.0, 0.0, 0.0]);
    }
}
<|endoftext|>