<commit_before>#[link(name = "rusp",
       vers = "0.1",
       author = "Brendan Zabarauskas",
       url = "https://github.com/bjz/rusp")];

#[comment = "A minimal Lisp dialect for Rust."];
#[crate_type = "lib"];

// Inspired by Peter Norvig's [Lis.py](http://norvig.com/lispy.html)

use std::hashmap::*;

pub mod parse;
pub mod pprint;

///
/// Symbol identifier
///
#[deriving(Eq, Clone, IterBytes)]
pub struct Ident(~str);

///
/// Holds a typed value from the environment
///
#[deriving(Eq, Clone)]
pub enum Value {
    Empty,
    Boolean(bool),
    Integer(int),
    Float(float),
    String(~str),
    ///
    /// Quoted expression
    ///
    /// ~~~
    /// (quote <expr>)
    /// ~~~
    ///
    Quote(@Expr),
    ///
    /// Anonymous function
    ///
    /// ~~~
    /// (|<ident>*| <expr>)
    /// ~~~
    ///
    Lambda(~[Ident], @Expr, @mut Env),
}

///
/// Primitive language expressions
///
#[deriving(Eq, Clone)]
pub enum Expr {
    ///
    /// Symbol reference
    ///
    /// ~~~
    /// <ident>
    /// ~~~
    ///
    Symbol(Ident),
    ///
    /// Constant literal
    ///
    /// ~~~
    /// <val>
    /// ~~~
    ///
    Literal(Value),
    ///
    /// Conditional expression
    ///
    /// ~~~
    /// (if <test> <conseq> <alt>)
    /// ~~~
    ///
    If(@Expr, @Expr, @Expr),
    ///
    /// Symbol definition
    ///
    /// ~~~
    /// (let <ident> <expr>)
    /// ~~~
    ///
    Let(Ident, @Expr),
    ///
    /// Procedure call
    ///
    /// ~~~
    /// (<expr> <expr>+)
    /// ~~~
    ///
    Call(@Expr, ~[@Expr]),
}

#[deriving(Eq)]
pub struct Env {
    values: HashMap<Ident, Value>,
    outer: Option<@mut Env>,
}

impl Env {
    /// Initializes an empty environment
    pub fn empty() -> @mut Env {
        @mut Env { values: HashMap::new(), outer: None }
    }

    /// Initializes a new environment with the specified values
    pub fn new(values: &[(Ident, Value)], outer: Option<@mut Env>) -> @mut Env {
        let env = @mut Env {
            values: HashMap::new(),
            outer: outer,
        };
        for values.each |&(id, val)| {
            env.define(id, val);
        }
        env
    }

    /// Defines a new identifier/value pair
    pub fn define(&mut self, ident: Ident, val: Value) -> bool {
        self.values.insert(ident, val)
    }

    /// Attempts to find a value corresponding to the supplied identifier
    pub fn find(&self, ident: &Ident) -> Option<Value> {
        match self.values.find(ident) {
            Some(val) => Some(val.clone()),
            None => self.outer.chain(|env| env.find(ident)),
        }
    }
}

pub type EvalResult = Result<Value, ~str>;

impl Expr {
    ///
    /// Evaluates an expression tree in the supplied environment
    ///
    pub fn eval(&self, env: @mut Env) -> EvalResult {
        match *self {
            Symbol(ref id) =>{
                match env.find(id) {
                    Some(val) => Ok(val),
                    None => Err(fmt!("The value of `%s` was not defined in this environment", id.to_str())),
                }
            }
            Literal(ref val) => {
                Ok(val.clone())
            }
            If(test, conseq, alt) => {
                do test.eval(env).chain |val| {
                    match val {
                        Boolean(true) => conseq.eval(env),
                        Boolean(false) => alt.eval(env),
                        _ => Err(fmt!("expected Boolean value, found: %?", val)),
                    }
                }
            }
            Let(ref id, expr) => {
                do expr.eval(env).chain |val| {
                    if env.define(id.clone(), val) {
                        Ok(Empty)
                    } else {
                        Err(fmt!("`%s` was already defined in this environment.", id.to_str()))
                    }
                }
            }
            Call(_,_) => fail!(),
        }
    }
}

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

    #[test]
    fn test_env() {
        let outer = Env::new([
            (Ident(~"a"), Integer(0)),
            (Ident(~"b"), Float(1.0)),
            (Ident(~"c"), String(~"hi")),
        ], None);

        let inner = Env::new([
            (Ident(~"a"), Integer(3)),
            (Ident(~"b"), Integer(4)),
        ], Some(outer));

        assert_eq!(outer.find(&Ident(~"a")), Some(Integer(0)));
        assert_eq!(outer.find(&Ident(~"b")), Some(Float(1.0)));
        assert_eq!(outer.find(&Ident(~"c")), Some(String(~"hi")));
        assert_eq!(inner.find(&Ident(~"a")), Some(Integer(3)));
        assert_eq!(inner.find(&Ident(~"b")), Some(Integer(4)));
        assert_eq!(inner.find(&Ident(~"c")), Some(String(~"hi")));
    }

    #[test]
    fn test_eval_symbol() {
        let env = Env::new([
            (Ident(~"a"), Integer(0)),
            (Ident(~"b"), Float(1.0)),
            (Ident(~"c"), String(~"hi")),
        ], None);

        assert_eq!(Symbol(Ident(~"a")).eval(env).get(), Integer(0));
        assert_eq!(Symbol(Ident(~"b")).eval(env).get(), Float(1.0));
        assert_eq!(Symbol(Ident(~"c")).eval(env).get(), String(~"hi"));
        assert!(Symbol(Ident(~"d")).eval(env).is_err());
    }

    #[test]
    fn test_eval_literal() {
        assert_eq!(Literal(Empty).eval(Env::empty()).get(), Empty);
        assert_eq!(Literal(Integer(1)).eval(Env::empty()).get(), Integer(1));
        assert_eq!(Literal(Float(1.0)).eval(Env::empty()).get(), Float(1.0));
        assert_eq!(Literal(String(~"hi")).eval(Env::empty()).get(), String(~"hi"));
        assert_eq!(Literal(Quote(@Literal(Empty))).eval(Env::empty()).get(),
                   Quote(@Literal(Empty)));
    }

    #[test]
    fn test_eval_if() {
        fn not(test: Value) -> EvalResult {
            If(
                @Literal(test),
                @Literal(Boolean(false)),
                @Literal(Boolean(true))
            ).eval(Env::empty())
        }

        assert_eq!(not(Boolean(true)).get(), Boolean(false));
        assert_eq!(not(Boolean(false)).get(), Boolean(true));
        assert!(not(String(~"hi")).is_err());
    }

    #[test]
    fn test_eval_lambda() {

    }

    #[test]
    fn test_eval_let() {
        let env = Env::empty();
        assert!(Let(Ident(~"a"), @Literal(Integer(0))).eval(env).is_ok());
        assert!(Let(Ident(~"b"), @Literal(Float(1.0))).eval(env).is_ok());
        assert!(Let(Ident(~"c"), @Literal(String(~"hi"))).eval(env).is_ok());

        assert_eq!(env.find(&Ident(~"a")), Some(Integer(0)));
        assert_eq!(env.find(&Ident(~"b")), Some(Float(1.0)));
        assert_eq!(env.find(&Ident(~"c")), Some(String(~"hi")));

        assert!(Let(Ident(~"c"), @Literal(Empty)).eval(env).is_err());
    }

    #[test]
    fn test_eval_call() {

    }
}<commit_msg>Update comment<commit_after>#[link(name = "rusp",
       vers = "0.1",
       author = "Brendan Zabarauskas",
       url = "https://github.com/bjz/rusp")];

#[comment = "A minimal Lisp dialect for Rust."];
#[crate_type = "lib"];

// Inspired by Peter Norvig's [Lis.py](http://norvig.com/lispy.html)

use std::hashmap::*;

pub mod parse;
pub mod pprint;

///
/// Symbol identifier
///
#[deriving(Eq, Clone, IterBytes)]
pub struct Ident(~str);

///
/// Holds a typed value from the environment
///
#[deriving(Eq, Clone)]
pub enum Value {
    Empty,
    Boolean(bool),
    Integer(int),
    Float(float),
    String(~str),
    ///
    /// Quoted expression
    ///
    /// ~~~
    /// (quote <expr>)
    /// ~~~
    ///
    Quote(@Expr),
    ///
    /// Anonymous function
    ///
    /// ~~~
    /// (fn (<ident>*) <expr>)
    /// ~~~
    ///
    Lambda(~[Ident], @Expr, @mut Env),
}

///
/// Primitive language expressions
///
#[deriving(Eq, Clone)]
pub enum Expr {
    ///
    /// Symbol reference
    ///
    /// ~~~
    /// <ident>
    /// ~~~
    ///
    Symbol(Ident),
    ///
    /// Constant literal
    ///
    /// ~~~
    /// <val>
    /// ~~~
    ///
    Literal(Value),
    ///
    /// Conditional expression
    ///
    /// ~~~
    /// (if <test> <conseq> <alt>)
    /// ~~~
    ///
    If(@Expr, @Expr, @Expr),
    ///
    /// Symbol definition
    ///
    /// ~~~
    /// (let <ident> <expr>)
    /// ~~~
    ///
    Let(Ident, @Expr),
    ///
    /// Procedure call
    ///
    /// ~~~
    /// (<expr> <expr>+)
    /// ~~~
    ///
    Call(@Expr, ~[@Expr]),
}

#[deriving(Eq)]
pub struct Env {
    values: HashMap<Ident, Value>,
    outer: Option<@mut Env>,
}

impl Env {
    /// Initializes an empty environment
    pub fn empty() -> @mut Env {
        @mut Env { values: HashMap::new(), outer: None }
    }

    /// Initializes a new environment with the specified values
    pub fn new(values: &[(Ident, Value)], outer: Option<@mut Env>) -> @mut Env {
        let env = @mut Env {
            values: HashMap::new(),
            outer: outer,
        };
        for values.each |&(id, val)| {
            env.define(id, val);
        }
        env
    }

    /// Defines a new identifier/value pair
    pub fn define(&mut self, ident: Ident, val: Value) -> bool {
        self.values.insert(ident, val)
    }

    /// Attempts to find a value corresponding to the supplied identifier
    pub fn find(&self, ident: &Ident) -> Option<Value> {
        match self.values.find(ident) {
            Some(val) => Some(val.clone()),
            None => self.outer.chain(|env| env.find(ident)),
        }
    }
}

pub type EvalResult = Result<Value, ~str>;

impl Expr {
    ///
    /// Evaluates an expression tree in the supplied environment
    ///
    pub fn eval(&self, env: @mut Env) -> EvalResult {
        match *self {
            Symbol(ref id) =>{
                match env.find(id) {
                    Some(val) => Ok(val),
                    None => Err(fmt!("The value of `%s` was not defined in this environment", id.to_str())),
                }
            }
            Literal(ref val) => {
                Ok(val.clone())
            }
            If(test, conseq, alt) => {
                do test.eval(env).chain |val| {
                    match val {
                        Boolean(true) => conseq.eval(env),
                        Boolean(false) => alt.eval(env),
                        _ => Err(fmt!("expected Boolean value, found: %?", val)),
                    }
                }
            }
            Let(ref id, expr) => {
                do expr.eval(env).chain |val| {
                    if env.define(id.clone(), val) {
                        Ok(Empty)
                    } else {
                        Err(fmt!("`%s` was already defined in this environment.", id.to_str()))
                    }
                }
            }
            Call(_,_) => fail!(),
        }
    }
}

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

    #[test]
    fn test_env() {
        let outer = Env::new([
            (Ident(~"a"), Integer(0)),
            (Ident(~"b"), Float(1.0)),
            (Ident(~"c"), String(~"hi")),
        ], None);

        let inner = Env::new([
            (Ident(~"a"), Integer(3)),
            (Ident(~"b"), Integer(4)),
        ], Some(outer));

        assert_eq!(outer.find(&Ident(~"a")), Some(Integer(0)));
        assert_eq!(outer.find(&Ident(~"b")), Some(Float(1.0)));
        assert_eq!(outer.find(&Ident(~"c")), Some(String(~"hi")));
        assert_eq!(inner.find(&Ident(~"a")), Some(Integer(3)));
        assert_eq!(inner.find(&Ident(~"b")), Some(Integer(4)));
        assert_eq!(inner.find(&Ident(~"c")), Some(String(~"hi")));
    }

    #[test]
    fn test_eval_symbol() {
        let env = Env::new([
            (Ident(~"a"), Integer(0)),
            (Ident(~"b"), Float(1.0)),
            (Ident(~"c"), String(~"hi")),
        ], None);

        assert_eq!(Symbol(Ident(~"a")).eval(env).get(), Integer(0));
        assert_eq!(Symbol(Ident(~"b")).eval(env).get(), Float(1.0));
        assert_eq!(Symbol(Ident(~"c")).eval(env).get(), String(~"hi"));
        assert!(Symbol(Ident(~"d")).eval(env).is_err());
    }

    #[test]
    fn test_eval_literal() {
        assert_eq!(Literal(Empty).eval(Env::empty()).get(), Empty);
        assert_eq!(Literal(Integer(1)).eval(Env::empty()).get(), Integer(1));
        assert_eq!(Literal(Float(1.0)).eval(Env::empty()).get(), Float(1.0));
        assert_eq!(Literal(String(~"hi")).eval(Env::empty()).get(), String(~"hi"));
        assert_eq!(Literal(Quote(@Literal(Empty))).eval(Env::empty()).get(),
                   Quote(@Literal(Empty)));
    }

    #[test]
    fn test_eval_if() {
        fn not(test: Value) -> EvalResult {
            If(
                @Literal(test),
                @Literal(Boolean(false)),
                @Literal(Boolean(true))
            ).eval(Env::empty())
        }

        assert_eq!(not(Boolean(true)).get(), Boolean(false));
        assert_eq!(not(Boolean(false)).get(), Boolean(true));
        assert!(not(String(~"hi")).is_err());
    }

    #[test]
    fn test_eval_lambda() {

    }

    #[test]
    fn test_eval_let() {
        let env = Env::empty();
        assert!(Let(Ident(~"a"), @Literal(Integer(0))).eval(env).is_ok());
        assert!(Let(Ident(~"b"), @Literal(Float(1.0))).eval(env).is_ok());
        assert!(Let(Ident(~"c"), @Literal(String(~"hi"))).eval(env).is_ok());

        assert_eq!(env.find(&Ident(~"a")), Some(Integer(0)));
        assert_eq!(env.find(&Ident(~"b")), Some(Float(1.0)));
        assert_eq!(env.find(&Ident(~"c")), Some(String(~"hi")));

        assert!(Let(Ident(~"c"), @Literal(Empty)).eval(env).is_err());
    }

    #[test]
    fn test_eval_call() {

    }
}<|endoftext|>


<commit_before>#[doc = "Rust wrappers around the raw JS apis"];

import bg = jsapi::bindgen;
import libc::types::os::arch::c95::size_t;

export rt;
export cx;
export jsobj;
export methods;

// ___________________________________________________________________________
// friendly Rustic API to runtimes

type rt = @rt_rsrc;

class rt_rsrc {
    let ptr : *JSRuntime;
    new(p : {ptr: *JSRuntime}) {
        self.ptr = p.ptr;
    }
    drop {
        JS_Finish(self.ptr);
    }
}

fn rt() -> rt {
    @rt_rsrc({ptr: JS_Init(default_heapsize)})
}

impl methods for rt {
    fn cx() -> cx {
        @cx_rsrc({ptr: JS_NewContext(self.ptr, default_stacksize as size_t),
                  rt: self})
    }
}

// ___________________________________________________________________________
// contexts

type cx = @cx_rsrc;

class cx_rsrc {
    let ptr : *JSContext;
    let rt: rt;
    new(rec : {ptr: *JSContext, rt: rt}) {
        self.ptr = rec.ptr;
        self.rt = rec.rt;
    }
    drop {
        JS_DestroyContext(self.ptr);
    }
}

impl methods for cx {
    fn rooted_obj(obj: *JSObject) -> jsobj {
        let jsobj = @jsobj_rsrc({cx: self, cxptr: self.ptr, ptr: obj});
        JS_AddObjectRoot(self.ptr, ptr::addr_of(jsobj.ptr));
        jsobj
    }

    fn set_default_options_and_version() {
        self.set_options(JSOPTION_VAROBJFIX | JSOPTION_METHODJIT);
        self.set_version(JSVERSION_LATEST);
    }

    fn set_options(v: jsuint) {
        JS_SetOptions(self.ptr, v);
    }

    fn set_version(v: i32) {
        JS_SetVersion(self.ptr, v);
    }

    fn set_logging_error_reporter() {
        JS_SetErrorReporter(self.ptr, reportError);
    }

    fn set_error_reporter(reportfn: *u8) {
        JS_SetErrorReporter(self.ptr, reportfn);
    }

    fn new_compartment(globclsfn: fn(name_pool) -> JSClass) -> result<compartment,()> {
        let np = name_pool();
        let globcls = @globclsfn(np);
        let globobj = JS_NewCompartmentAndGlobalObject(self.ptr, &*globcls as *JSClass, null());
        result(JS_InitStandardClasses(self.ptr, globobj)).chain(|_ok| {
            ok(@{cx: self,
                 name_pool: np,
                 global_class: globcls,
                 mut global_funcs: [],
                 global_obj: self.rooted_obj(globobj)
                })
        })
    }

    fn evaluate_script(glob: jsobj, bytes: [u8], filename: str, line_num: uint) -> result<(),()> {
        vec::as_buf(bytes, |bytes_ptr| {
            str::as_c_str(filename, |filename_cstr| {
                let bytes_ptr = bytes_ptr as *c_char;
                let v: jsval = 0_u64;
                #debug["Evaluating script from %s with bytes %?", filename, bytes];
                if JS_EvaluateScript(self.ptr, glob.ptr,
                                     bytes_ptr, bytes.len() as uintN,
                                     filename_cstr, line_num as uintN,
                                     ptr::addr_of(v)) == ERR {
                    #debug["...err!"];
                    err(())
                } else {
                    // we could return the script result but then we'd have
                    // to root it and so forth and, really, who cares?
                    #debug["...ok!"];
                    ok(())
                }
            })
        })
    }

    unsafe fn get_cx_private() -> *() {
        unsafe::reinterpret_cast(JS_GetContextPrivate(self.ptr))
    }

    unsafe fn set_cx_private(data: *()) {
        JS_SetContextPrivate(self.ptr, unsafe::reinterpret_cast(data));
    }

    unsafe fn get_obj_private(obj: *JSObject) -> *() {
        unsafe::reinterpret_cast(JS_GetPrivate(self.ptr, obj))
    }

    unsafe fn set_obj_private(obj: *JSObject, data: *()) {
        JS_SetPrivate(self.ptr, obj, unsafe::reinterpret_cast(data));
    }
}

extern fn reportError(_cx: *JSContext,
                     msg: *c_char,
                     report: *JSErrorReport) {
    unsafe {
        let fnptr = (*report).filename;
        let fname = if fnptr.is_not_null() {from_c_str(fnptr)} else {"none"};
        let lineno = (*report).lineno;
        let msg = from_c_str(msg);
        #error["Error at %s:%?: %s\n", fname, lineno, msg];
    }
}

// ___________________________________________________________________________
// compartment

type compartment = @{
    cx: cx,
    name_pool: name_pool,
    global_class: @JSClass,
    mut global_funcs: [@~[JSFunctionSpec]],
    global_obj: jsobj
};

impl methods for compartment {
    fn define_functions(specfn: fn(name_pool) -> [JSFunctionSpec]) -> result<(),()> {
        let specvec = @specfn(self.name_pool);
        vec::push(self.global_funcs, specvec);
        vec::as_buf(*specvec, |specs| {
            result(JS_DefineFunctions(self.cx.ptr, self.global_obj.ptr, specs))
        })
    }
}

// ___________________________________________________________________________
// objects

type jsobj = @jsobj_rsrc;

class jsobj_rsrc {
    let cx : cx;
    let cxptr : *JSContext;
    let ptr : *JSObject;
    new(rec : {cx: cx, cxptr: *JSContext, ptr: *JSObject}) {
        self.cx = rec.cx;
        self.cxptr = rec.cxptr;
        self.ptr = rec.ptr;
    }
    drop {
        JS_RemoveObjectRoot(self.cxptr, ptr::addr_of(self.ptr));
    }
}

// ___________________________________________________________________________
// random utilities

impl methods for str {
    fn to_jsstr(cx: cx) -> *JSString {
        str::as_buf(self, |buf| {
            let cbuf = unsafe { unsafe::reinterpret_cast(buf) };
            bg::JS_NewStringCopyN(cx.ptr, cbuf, self.len() as size_t)
        })
    }
}

#[cfg(test)]
mod test {

    #[test]
    fn dummy() {
        let rt = rt();
        let cx = rt.cx();
        cx.set_default_options_and_version();
        cx.set_logging_error_reporter();
        cx.new_compartment(global::global_class).chain |comp| {
            comp.define_functions(global::debug_fns);

            let bytes = str::bytes("debug(22);");
            cx.evaluate_script(comp.global_obj, bytes, "test", 1u)
        };
    }

}
<commit_msg>Update to build with latest rustc syntax changes.<commit_after>#[doc = "Rust wrappers around the raw JS apis"];

import bg = jsapi::bindgen;
import libc::types::os::arch::c95::size_t;

export rt;
export cx;
export jsobj;
export methods;

// ___________________________________________________________________________
// friendly Rustic API to runtimes

type rt = @rt_rsrc;

class rt_rsrc {
    let ptr : *JSRuntime;
    new(p : {ptr: *JSRuntime}) {
        self.ptr = p.ptr;
    }
    drop {
        JS_Finish(self.ptr);
    }
}

fn rt() -> rt {
    @rt_rsrc({ptr: JS_Init(default_heapsize)})
}

impl methods for rt {
    fn cx() -> cx {
        @cx_rsrc({ptr: JS_NewContext(self.ptr, default_stacksize as size_t),
                  rt: self})
    }
}

// ___________________________________________________________________________
// contexts

type cx = @cx_rsrc;

class cx_rsrc {
    let ptr : *JSContext;
    let rt: rt;
    new(rec : {ptr: *JSContext, rt: rt}) {
        self.ptr = rec.ptr;
        self.rt = rec.rt;
    }
    drop {
        JS_DestroyContext(self.ptr);
    }
}

impl methods for cx {
    fn rooted_obj(obj: *JSObject) -> jsobj {
        let jsobj = @jsobj_rsrc({cx: self, cxptr: self.ptr, ptr: obj});
        JS_AddObjectRoot(self.ptr, ptr::addr_of(jsobj.ptr));
        jsobj
    }

    fn set_default_options_and_version() {
        self.set_options(JSOPTION_VAROBJFIX | JSOPTION_METHODJIT);
        self.set_version(JSVERSION_LATEST);
    }

    fn set_options(v: jsuint) {
        JS_SetOptions(self.ptr, v);
    }

    fn set_version(v: i32) {
        JS_SetVersion(self.ptr, v);
    }

    fn set_logging_error_reporter() {
        JS_SetErrorReporter(self.ptr, reportError);
    }

    fn set_error_reporter(reportfn: *u8) {
        JS_SetErrorReporter(self.ptr, reportfn);
    }

    fn new_compartment(globclsfn: fn(name_pool) -> JSClass) -> result<compartment,()> {
        let np = name_pool();
        let globcls = @globclsfn(np);
        let globobj = JS_NewCompartmentAndGlobalObject(self.ptr, &*globcls as *JSClass, null());
        result(JS_InitStandardClasses(self.ptr, globobj)).chain(|_ok| {
            ok(@{cx: self,
                 name_pool: np,
                 global_class: globcls,
                 mut global_funcs: [],
                 global_obj: self.rooted_obj(globobj)
                })
        })
    }

    fn evaluate_script(glob: jsobj, bytes: [u8], filename: str, line_num: uint) -> result<(),()> {
        vec::as_buf(bytes, |bytes_ptr| {
            str::as_c_str(filename, |filename_cstr| {
                let bytes_ptr = bytes_ptr as *c_char;
                let v: jsval = 0_u64;
                #debug["Evaluating script from %s with bytes %?", filename, bytes];
                if JS_EvaluateScript(self.ptr, glob.ptr,
                                     bytes_ptr, bytes.len() as uintN,
                                     filename_cstr, line_num as uintN,
                                     ptr::addr_of(v)) == ERR {
                    #debug["...err!"];
                    err(())
                } else {
                    // we could return the script result but then we'd have
                    // to root it and so forth and, really, who cares?
                    #debug["...ok!"];
                    ok(())
                }
            })
        })
    }

    unsafe fn get_cx_private() -> *() {
        unsafe::reinterpret_cast(JS_GetContextPrivate(self.ptr))
    }

    unsafe fn set_cx_private(data: *()) {
        JS_SetContextPrivate(self.ptr, unsafe::reinterpret_cast(data));
    }

    unsafe fn get_obj_private(obj: *JSObject) -> *() {
        unsafe::reinterpret_cast(JS_GetPrivate(self.ptr, obj))
    }

    unsafe fn set_obj_private(obj: *JSObject, data: *()) {
        JS_SetPrivate(self.ptr, obj, unsafe::reinterpret_cast(data));
    }
}

extern fn reportError(_cx: *JSContext,
                     msg: *c_char,
                     report: *JSErrorReport) {
    unsafe {
        let fnptr = (*report).filename;
        let fname = if fnptr.is_not_null() {from_c_str(fnptr)} else {"none"};
        let lineno = (*report).lineno;
        let msg = from_c_str(msg);
        #error["Error at %s:%?: %s\n", fname, lineno, msg];
    }
}

// ___________________________________________________________________________
// compartment

type compartment = @{
    cx: cx,
    name_pool: name_pool,
    global_class: @JSClass,
    mut global_funcs: [@~[JSFunctionSpec]],
    global_obj: jsobj
};

impl methods for compartment {
    fn define_functions(specfn: fn(name_pool) -> [JSFunctionSpec]) -> result<(),()> {
        let specvec = @specfn(self.name_pool);
        vec::push(self.global_funcs, specvec);
        vec::as_buf(*specvec, |specs| {
            result(JS_DefineFunctions(self.cx.ptr, self.global_obj.ptr, specs))
        })
    }
}

// ___________________________________________________________________________
// objects

type jsobj = @jsobj_rsrc;

class jsobj_rsrc {
    let cx : cx;
    let cxptr : *JSContext;
    let ptr : *JSObject;
    new(rec : {cx: cx, cxptr: *JSContext, ptr: *JSObject}) {
        self.cx = rec.cx;
        self.cxptr = rec.cxptr;
        self.ptr = rec.ptr;
    }
    drop {
        JS_RemoveObjectRoot(self.cxptr, ptr::addr_of(self.ptr));
    }
}

// ___________________________________________________________________________
// random utilities

impl methods for str {
    fn to_jsstr(cx: cx) -> *JSString {
        str::as_buf(self, |buf| {
            let cbuf = unsafe { unsafe::reinterpret_cast(buf) };
            bg::JS_NewStringCopyN(cx.ptr, cbuf, self.len() as size_t)
        })
    }
}

#[cfg(test)]
mod test {

    #[test]
    fn dummy() {
        let rt = rt();
        let cx = rt.cx();
        cx.set_default_options_and_version();
        cx.set_logging_error_reporter();
        cx.new_compartment(global::global_class).chain(|comp| {
            comp.define_functions(global::debug_fns);

            let bytes = str::bytes("debug(22);");
            cx.evaluate_script(comp.global_obj, bytes, "test", 1u)
        });
    }

}
<|endoftext|>


<commit_before>// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// Based off of libcss's examples/example1.c
mod example1 {

    use CssResult;
    use CssProperty;
    use types::*;
    use hint::*;
    use select::*;
    use util::VoidPtrLike;
    use wapcaplet::LwcString;

    struct MyDomNode {
        name: @LwcString
    }

    impl VoidPtrLike for MyDomNode {
        fn from_void_ptr(node: *libc::c_void) -> MyDomNode {
            assert!(node.is_not_null());
            MyDomNode {
                name: unsafe {
                    let box = cast::reinterpret_cast(&node);
                    cast::bump_box_refcount(box);
                    box
                }
            }
        }

        fn to_void_ptr(&self) -> *libc::c_void {
            unsafe { cast::reinterpret_cast(&self.name) }
        }
    }

    #[test]
    fn run() {
        use super::super::stylesheet::{CssUrlResolutionFn, CssStylesheetParams, CssStylesheet,
                                       css_stylesheet_create, CssStylesheetParamsVersion1};
        use super::super::computed::CssComputedStyle;
        use super::super::values::{CssColorColor, CssColorInherit};
        use super::super::ll::types::{CSS_ORIGIN_AUTHOR, CSS_MEDIA_ALL, CSS_MEDIA_SCREEN};
        use super::super::conversions::ToLl;
        use wapcaplet::{LwcString, from_rust_string};

        let data = "h1 { color: red; }\
                    h4 { color: #321; }\
                    h4, h5 { color: #123456; }";

        let resolve: CssUrlResolutionFn = |a,b| resolve_url(a, b);

        let params: CssStylesheetParams = CssStylesheetParams {
            params_version: CssStylesheetParamsVersion1,
            level: CssLevel21,
            charset: ~"UTF-8",
            url: ~"foo",
            title: ~"foo",
            allow_quirks: false,
            inline_style: false,
            resolve: Some(resolve),
            import: None,
            color: None,
            font: None,
        };

        let mut sheet: CssStylesheet = css_stylesheet_create(&params);
        debug!("stylesheet: %?", sheet);
        debug!("stylesheet size: %?", sheet.size());

        sheet.append_data(str::to_bytes(data));
        sheet.data_done();
        debug!("stylesheet size: %?", sheet.size());

        let mut select_ctx: CssSelectCtx = css_select_ctx_create();
        assert!(select_ctx.count_sheets() == 0);
        select_ctx.append_sheet(sheet, CSS_ORIGIN_AUTHOR, CSS_MEDIA_ALL);
        debug!("count sheets: %?", select_ctx.count_sheets());
        assert!(select_ctx.count_sheets() == 1);

        for uint::range(1, 7) |hh| {
            let element = fmt!("h%u", hh);
            let element_name: @LwcString = @from_rust_string(element);
            let node = MyDomNode { name: element_name };
            let select_handler = SelectHandler { bogus: () };
            let style: CssSelectResults = select_ctx.select_style(&node,
                                                                  CSS_MEDIA_SCREEN,
                                                                  None,
                                                                  &select_handler);

            let computed: CssComputedStyle = style.computed_style(CssPseudoElementNone);

            match computed.color() {
                CssColorInherit => {
                    debug!("color of h%u is 'inherit'", hh);
                }
                CssColorColor(color) => {
                    debug!("color of h%u is %x", hh, color.to_ll() as uint);
                }
            }
        }
    }

    fn resolve_url(_base: &str, _rel: &LwcString) -> CssResult<LwcString> {
        fail!(~"resolving url");
    }

    struct SelectHandler {
        bogus: ()
    }

    impl CssSelectHandler<MyDomNode> for SelectHandler {
        fn node_name(&self, node: &MyDomNode) -> CssQName {
            debug!("HL node_name!");
            debug!("SS %?", (*node.name).to_str());

            CssQName {
                ns: None,
                name: (*node.name).clone()
            }
        }

        fn node_id(&self, _node: &MyDomNode) -> Option<LwcString> { None }

        fn named_parent_node(&self, _node: &MyDomNode, _qname: &CssQName) -> Option<MyDomNode> {
            None
        }

        fn parent_node(&self, _node: &MyDomNode) -> Option<MyDomNode> {
            None
        }

        fn node_has_id(&self, _node: &MyDomNode, _name: LwcString) -> bool { false }

        fn named_ancestor_node(&self, _node: &MyDomNode, _qname: &CssQName) -> Option<MyDomNode> {
            None
        }

        fn node_is_root(&self, _node: &MyDomNode) -> bool { false }

        fn node_is_link(&self, _node: &MyDomNode) -> bool { false }

        fn ua_default_for_property(&self, property: CssProperty) -> CssHint {
            match property {
                _ => CssHintDefault
            }
        }
    }
}

#[test]
fn test_arc() {
    use std::arc::ARC;
    use stylesheet::*;
    use types::CssLevel21;
    use wapcaplet::LwcString;
    use super::CssResult;

    let resolve: CssUrlResolutionFn = |a,b| resolve_url(a, b);
    let params: CssStylesheetParams = CssStylesheetParams {
        params_version: CssStylesheetParamsVersion1,
        level: CssLevel21,
        charset: ~"UTF-8",
        url: ~"foo",
        title: ~"foo",
        allow_quirks: false,
        inline_style: false,
        resolve: Some(resolve),
        import: None,
        color: None,
        font: None,
    };

    let sheet: CssStylesheet = css_stylesheet_create(&params);
    let _arc = ARC(sheet);

    fn resolve_url(_base: &str, _rel: &LwcString) -> CssResult<LwcString> {
        fail!(~"resolving url");
    }
}
<commit_msg>update for language changes<commit_after>// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// Based off of libcss's examples/example1.c
mod example1 {

    use CssResult;
    use CssProperty;
    use types::*;
    use hint::*;
    use select::*;
    use util::VoidPtrLike;
    use wapcaplet::LwcString;

    struct MyDomNode {
        name: @LwcString
    }

    impl VoidPtrLike for MyDomNode {
        fn from_void_ptr(node: *libc::c_void) -> MyDomNode {
            assert!(node.is_not_null());
            MyDomNode {
                name: unsafe {
                    let box = cast::transmute(&node);
                    cast::bump_box_refcount(box);
                    box
                }
            }
        }

        fn to_void_ptr(&self) -> *libc::c_void {
            unsafe { cast::transmute(&self.name) }
        }
    }

    #[test]
    fn run() {
        use super::super::stylesheet::{CssUrlResolutionFn, CssStylesheetParams, CssStylesheet,
                                       css_stylesheet_create, CssStylesheetParamsVersion1};
        use super::super::computed::CssComputedStyle;
        use super::super::values::{CssColorColor, CssColorInherit};
        use super::super::ll::types::{CSS_ORIGIN_AUTHOR, CSS_MEDIA_ALL, CSS_MEDIA_SCREEN};
        use super::super::conversions::ToLl;
        use wapcaplet::{LwcString, from_rust_string};

        let data = "h1 { color: red; }\
                    h4 { color: #321; }\
                    h4, h5 { color: #123456; }";

        let resolve: CssUrlResolutionFn = |a,b| resolve_url(a, b);

        let params: CssStylesheetParams = CssStylesheetParams {
            params_version: CssStylesheetParamsVersion1,
            level: CssLevel21,
            charset: ~"UTF-8",
            url: ~"foo",
            title: ~"foo",
            allow_quirks: false,
            inline_style: false,
            resolve: Some(resolve),
            import: None,
            color: None,
            font: None,
        };

        let mut sheet: CssStylesheet = css_stylesheet_create(&params);
        debug!("stylesheet: %?", sheet);
        debug!("stylesheet size: %?", sheet.size());

        sheet.append_data(str::to_bytes(data));
        sheet.data_done();
        debug!("stylesheet size: %?", sheet.size());

        let mut select_ctx: CssSelectCtx = css_select_ctx_create();
        assert!(select_ctx.count_sheets() == 0);
        select_ctx.append_sheet(sheet, CSS_ORIGIN_AUTHOR, CSS_MEDIA_ALL);
        debug!("count sheets: %?", select_ctx.count_sheets());
        assert!(select_ctx.count_sheets() == 1);

        for uint::range(1, 7) |hh| {
            let element = fmt!("h%u", hh);
            let element_name: @LwcString = @from_rust_string(element);
            let node = MyDomNode { name: element_name };
            let select_handler = SelectHandler { bogus: () };
            let style: CssSelectResults = select_ctx.select_style(&node,
                                                                  CSS_MEDIA_SCREEN,
                                                                  None,
                                                                  &select_handler);

            let computed: CssComputedStyle = style.computed_style(CssPseudoElementNone);

            match computed.color() {
                CssColorInherit => {
                    debug!("color of h%u is 'inherit'", hh);
                }
                CssColorColor(color) => {
                    debug!("color of h%u is %x", hh, color.to_ll() as uint);
                }
            }
        }
    }

    fn resolve_url(_base: &str, _rel: &LwcString) -> CssResult<LwcString> {
        fail!(~"resolving url");
    }

    struct SelectHandler {
        bogus: ()
    }

    impl CssSelectHandler<MyDomNode> for SelectHandler {
        fn node_name(&self, node: &MyDomNode) -> CssQName {
            debug!("HL node_name!");
            debug!("SS %?", (*node.name).to_str());

            CssQName {
                ns: None,
                name: (*node.name).clone()
            }
        }

        fn node_id(&self, _node: &MyDomNode) -> Option<LwcString> { None }

        fn named_parent_node(&self, _node: &MyDomNode, _qname: &CssQName) -> Option<MyDomNode> {
            None
        }

        fn parent_node(&self, _node: &MyDomNode) -> Option<MyDomNode> {
            None
        }

        fn node_has_id(&self, _node: &MyDomNode, _name: LwcString) -> bool { false }

        fn named_ancestor_node(&self, _node: &MyDomNode, _qname: &CssQName) -> Option<MyDomNode> {
            None
        }

        fn node_is_root(&self, _node: &MyDomNode) -> bool { false }

        fn node_is_link(&self, _node: &MyDomNode) -> bool { false }

        fn ua_default_for_property(&self, property: CssProperty) -> CssHint {
            match property {
                _ => CssHintDefault
            }
        }
    }
}

#[test]
fn test_arc() {
    use std::arc::ARC;
    use stylesheet::*;
    use types::CssLevel21;
    use wapcaplet::LwcString;
    use super::CssResult;

    let resolve: CssUrlResolutionFn = |a,b| resolve_url(a, b);
    let params: CssStylesheetParams = CssStylesheetParams {
        params_version: CssStylesheetParamsVersion1,
        level: CssLevel21,
        charset: ~"UTF-8",
        url: ~"foo",
        title: ~"foo",
        allow_quirks: false,
        inline_style: false,
        resolve: Some(resolve),
        import: None,
        color: None,
        font: None,
    };

    let sheet: CssStylesheet = css_stylesheet_create(&params);
    let _arc = ARC(sheet);

    fn resolve_url(_base: &str, _rel: &LwcString) -> CssResult<LwcString> {
        fail!(~"resolving url");
    }
}
<|endoftext|>


<commit_before>#![allow(ctypes)]
#![no_std]
#![feature(globs)]
#![feature(asm)]
// Adding pub mod (another fix for this warning) increases footprint, so just
// disble it instead.
#![allow(visible_private_types)]

extern crate core;

use core::prelude::*;
use core::mem::*;

use aspace::AddressSpace;
use con::write;
use dlist::DList;
use process::Process;
use start32::MultiBootInfo;
//use start32::OrigMultiBootInfo;
use start32::PhysAddr;
use start32::MutPhysAddr;
use util::abort;
use x86::idt;
pub use x86::idt::irq_entry;
pub use syscall::syscall;

mod aspace;
#[allow(dead_code)]
mod con;
mod dict;
mod dlist;
mod mboot;
mod mem;
mod process;
mod start32;
mod syscall;
pub mod util;
mod x86;

static log_assoc_procs : bool = false;
static log_page_fault : bool = false;
static log_switch : bool = false;
static log_queue : bool = false;
static log_irq : bool = false;
static log_idle : bool = false;

static mem_test : bool = false;

#[allow(dead_code)]
fn writeMBInfo(infop : *mboot::Info) {
    con::write("Multiboot info at ");
    con::writePtr(infop);
    con::putc('\n');

    let &info = unsafe { &*infop };
    con::write("Flags: ");
    con::writeHex(info.flags as uint);
    con::newline();

    if info.has(mboot::MemorySize) {
        con::writeUInt(info.mem_lower as uint);
        con::write("kB lower memory, ");
        con::writeUInt(info.mem_upper as uint);
        con::write("kB upper memory, ");
        con::writeUInt(((info.mem_lower + info.mem_upper + 1023) / 1024) as uint);
        con::write("MB total.\n");
    }
    if info.has(mboot::CommandLine) {
        let cmdline : *u8 = PhysAddr(info.cmdline as uint);
        con::write("Command line @");
        con::writePtr(cmdline);
        con::write(" (");
        con::writeHex(info.cmdline as uint);
        con::write(") \"");
        con::writeCStr(cmdline);
        con::write("\"\n");
    }
}

#[inline(never)]
pub fn generic_irq_handler(vec : u8) {
    if log_irq {
        write("IRQ! vec=");
        con::writeUInt(vec as uint);
        con::newline();
    }

    let c = cpu();
    let mask = 1 << (vec - 32);
    if c.irq_delayed & mask != 0 {
        write("IRQ: already delayed\n");
        return;
    }
    c.irq_delayed |= mask;
    if !c.irq_process().is_some() {
        abort("IRQ: no irq process");
    }
    let p = c.irq_process().unwrap();

    if log_irq {
        write("IRQ: proc=");
        con::writeMutPtr(p);
        con::newline();
    }

    syscall::try_deliver_irq(p);
}

pub fn page_fault(p : &mut Process, error : uint) -> ! {
    mod pf_errors {
        #![allow(dead_code)]
        // Error code flags.
        pub static PRESENT : uint = 1;
        pub static WRITE : uint = 2;
        pub static USER : uint = 4;
        pub static RSVD : uint = 8;
        pub static INSTR : uint = 16;
    }

    if log_page_fault {
        write("page fault ");
        con::writeHex(error);
        write(" cr2=");
        con::writePHex(x86::cr2());
        write(" rip=");
        con::writePHex(p.regs.rip as uint);
        write(" in process ");
        con::writeMutPtr(p);
        con::newline();
    }

    if (error & pf_errors::USER) == 0 {
        abort("kernel page fault\n");
    }

    let back = p.aspace().find_add_backing(x86::cr2() & !0xfff);
    // FIXME should return e.g. Option<> so we can detect failures better than
    // just aborting in find_add_backing.
    p.aspace().add_pte(back.vaddr(), back.pte());

    unsafe { cpu().switch_to(p); }
}

pub fn handler_NM() {
    abort("NM");
}

pub fn idle() -> ! {
    loop {
        if log_idle {
            write("idle\n");
        }
        cpu().process = None;
        unsafe { asm!("sti; hlt; cli" :::: "volatile"); }
    }
}

pub struct PerCpu {
    selfp : *mut PerCpu,
    stack : *mut u8,
    process : Option<&'static mut Process>,

    // End of assembly-fixed fields.
    memory : mem::PerCpu,
    runqueue : DList<Process>,

    irq_process : Option<&'static mut Process>,
    irq_delayed : uint,
}

impl PerCpu {
    unsafe fn new() -> *mut PerCpu {
        let mut mem = mem::PerCpu::new();
        let p : *mut PerCpu = mem.alloc_frame_panic();
        let stack : *mut u8 = mem.alloc_frame_panic();
        *p = PerCpu {
            selfp : p,
            stack : stack,
            memory : mem,
            runqueue : DList::empty(),
            process : None,
            irq_process : None,
            irq_delayed : 0,
        };
        return p
    }

    unsafe fn start(&mut self) {
        setup_msrs(self.selfp as uint);
    }

    fn queue(&mut self, p: &mut Process) {
        if log_queue {
            write("queue ");
            con::writeMutPtr(p);
            if p.is(process::Queued) {
                write(" already queued");
            }
            con::newline();
        }
        if !p.is_queued() {
            p.set(process::Queued);
            self.runqueue.append(p);
        }
    }

    unsafe fn run(&mut self) -> ! {
        match self.runqueue.pop() {
            Some(p) => {
                let r = &mut *p;
                if !r.is_queued() {
                    abort("popped unqueued item?");
                }
                r.unset(process::Queued);
                self.switch_to(r);
            },
            None => idle()
        }
    }

    unsafe fn switch_to(&mut self, p: &mut Process) -> ! {
        if log_switch {
            write("switch_to ");
            con::writeMutPtr(p);
            write(" rip=");
            con::writeHex(p.regs.rip as uint);
            if p.is(process::FastRet) {
                write(" fastret");
            }
            if p.is(process::Queued) {
                write(" queued");
            }
            con::newline();
        }
        p.set(process::Running);
        self.process = transmute(p as *mut Process);
        // TODO Check fpu_process, see if we need to set/reset TS bit in cr0
        x86::set_cr3(p.cr3);
        extern "C" {
            fn fastret(p : &mut Process) -> !;
            fn slowret(p : &mut Process) -> !;
        }
        if p.is(process::FastRet) {
            p.unset(process::FastRet);
            fastret(p);
        } else {
            slowret(p);
        }
    }

    fn syscall_return(&mut self, p: &mut Process, rax : uint) -> ! {
        p.regs.rax = rax;
        unsafe { self.switch_to(p); }
    }

    fn get_process<'a>(&'a mut self) -> Option<&'a mut Process> {
        match self.process {
            Some(ref p) => Some(unsafe { &mut *(*p as *mut Process) }),
            None => None,
        }
    }

    fn leave_proc(&mut self) {
        match self.process {
            Some(ref mut p) => p.unset(process::Running),
            None => (),
        };
    }

    fn irq_process<'a>(&'a mut self) -> Option<&'a mut Process> {
        match self.irq_process {
            Some(ref p) => Some(unsafe { &mut *(*p as *mut Process) }),
            None => None,
        }
    }

    fn is_irq_process(&self, p: &mut Process) -> bool {
        match self.irq_process {
        None => false,
        Some(ref q) => (p as *mut Process) == (*q as *mut Process),
        }
    }
}

// NB: One of the funky guarantees that Rust gives/requires is that there is
// at most one &mut reference to the same thing at any one time. This function
// can't quite guarantee that...
pub fn cpu() -> &mut PerCpu {
    unsafe {
        let mut ret : *mut PerCpu;
        asm!("movq %gs:($1), $0" : "=r"(ret) : "r"(0));
        return &mut *ret;
    }
}

#[lang="exchange_malloc"]
#[inline(always)]
pub fn xmalloc(size : uint, _align: uint) -> *mut u8 {
    malloc(size)
}

pub fn malloc(size : uint) -> *mut u8 {
    if size > 4096 {
        abort("oversized malloc");
    }
    #[inline(never)]
    fn malloc_() -> *mut u8 {
        cpu().memory.alloc_frame_panic()
    }
    malloc_()
}

#[inline(always)]
pub fn alloc<T>() -> *mut T {
    malloc(size_of::<T>()) as *mut T
}

pub fn free<T>(p : *mut T) {
    #[inline(never)]
    fn free_(p: *mut u8) {
        cpu().memory.free_frame(p);
    }
    if p.is_not_null() {
        free_(p as *mut u8);
    }
}

#[lang="exchange_free"]
#[inline(always)]
pub fn xfree(p : *mut u8, _size: uint, _align: uint) {
    free(p);
}

unsafe fn setup_msrs(gs : uint) {
    use x86::msr::*;
    use x86::rflags;
    use x86::efer;
    use x86::seg;
    #[allow(dead_code)]
    extern {
        fn syscall_entry_stub();
        fn syscall_entry_compat();
    }

    wrmsr(STAR, (seg::user_code32_base << 16) | seg::code);
    wrmsr(LSTAR, syscall_entry_stub as uint);
    wrmsr(CSTAR, syscall_entry_compat as uint);
    // FIXME: We want to clear a lot more flags - Direction for instance.
    // FreeBSD sets PSL_NT|PSL_T|PSL_I|PSL_C|PSL_D
    wrmsr(FMASK, rflags::IF | rflags::VM);
    wrmsr(EFER, rdmsr(EFER) | efer::SCE | efer::NXE);
    wrmsr(GSBASE, gs);
}

#[lang="eh_personality"]
fn dummy() {}

#[inline(never)]
fn new_proc_simple(start : uint, end_unaligned : uint) -> *mut Process {
    let end = (end_unaligned + 0xfff) & !0xfff;
    let start_page = start & !0xfff;
    let aspace : *mut AddressSpace = unsafe { transmute(box AddressSpace::new()) };
    let ret : *mut Process = unsafe { transmute(box Process::new(aspace)) };
    unsafe {
        (*ret).regs.rsp = 0x100000;
        (*ret).regs.rip = 0x100000 + (start & 0xfff);
    }

    unsafe {
        use aspace::mapflag::*;
        // Stack at 1MB - 4kB (not executable)
        (*aspace).mapcard_set(0x0ff000, 0, 0, Anon | R | W);
        (*aspace).mapcard_set(0x100000, 0, start_page - 0x100000, Phys | R | X);
        (*aspace).mapcard_set(0x100000 + (end - start_page), 0, 0, 0);
        /*match (*ret).aspace().mapcard_find(0x100000) {
            Some(c) => {
                write("0x100000: ");
                con::writePHex(c.vaddr());
                write(" -> paddr ");
                con::writePHex(c.paddr(0x100000));
                con::newline();
            },
            None => {
                abort("mapcard we added is not there anymore");
            }
        }*/
    }
    return ret;
}

fn assoc_procs(p : &mut Process, i : uint, q : &mut Process, j : uint) {
    if log_assoc_procs {
        con::writeMutPtr(p);
        con::putc(':');
        con::writeUInt(i);
        write(" <-> ");
        con::writeUInt(j);
        con::putc(':');
        con::writeMutPtr(q);
        con::newline();
    }
    p.assoc_handles(j, q, i);
}

fn init_modules(cpu : &mut PerCpu) {
    let &info = start32::MultiBootInfo();
    if !info.has(mboot::Modules) {
        return;
    }
    let mut head = DList::empty();
    let mut count = 0;
    for m in info.modules(start32::PhysAddr).iter() {
        write("Module ");
        con::writeHex(m.start as uint);
        write("..");
        con::writeHex(m.end as uint);
        write(": ");
        con::writeCStr(start32::PhysAddr(m.string as uint));
        con::newline();

        head.append(new_proc_simple(m.start as uint, m.end as uint));
        count += 1;
    }
    con::writeUInt(count);
    con::newline();
    // Now all processes are in our list. We need to remove them before it
    // gets possible to make them runnable.
    let mut i = 0;
    for p in head.iter() {
        if i == 0 {
            cpu.irq_process = Some(unsafe { &mut *(p as *mut Process) });
        }
        i += 1;
        // start iterating at j = i + 1, and q = p.next...
        let mut j = 0;
        for q in head.iter() {
            j += 1;
            if i < j {
                assoc_procs(p, i, q, j);
            }
        }
    }
    while match head.pop() {
        Some(p) => { cpu.queue(unsafe { &mut *p }); true },
        None => false
    } {}
}

pub fn dump_runqueue(queue: &DList<Process>) {
    let mut count = 0;
    for _ in queue.iter() {
        count += 1;
    }
    con::write("runqueue: ");
    con::writeUInt(count);
    con::newline();
    for p in queue.iter() {
        p.dump();
    }
}

#[no_mangle]
pub unsafe fn start64() -> ! {
    con::init(MutPhysAddr(0xb8000), 80, 25);
    con::clear();
    write("Hello World!\n");
    //writeMBInfo(start32::MultiBootInfo());

    x86::lgdt(start32::Gdtr());
    x86::ltr(x86::seg::tss64);

    idt::init();

    mem::global.init(start32::MultiBootInfo(), start32::MemoryStart());
    write("Memory initialized. ");
    mem::global.stat();

    let pcpu = PerCpu::new();
    let ref mut cpu = *pcpu;
    cpu.start();
    if mem_test {
        cpu.memory.test();
        mem::global.stat();
    }

    init_modules(cpu);
    //dump_runqueue(&cpu.runqueue);

//  let mut i = 0;
//  loop {
//      con.writeUInt(i);
//      con.putc('\n');
//      i += 1;
//  }
    cpu.run();
}
<commit_msg>fastret takes rax as the second argument, somehow I missed this<commit_after>#![allow(ctypes)]
#![no_std]
#![feature(globs)]
#![feature(asm)]
// Adding pub mod (another fix for this warning) increases footprint, so just
// disble it instead.
#![allow(visible_private_types)]

extern crate core;

use core::prelude::*;
use core::mem::*;

use aspace::AddressSpace;
use con::write;
use dlist::DList;
use process::Process;
use start32::MultiBootInfo;
//use start32::OrigMultiBootInfo;
use start32::PhysAddr;
use start32::MutPhysAddr;
use util::abort;
use x86::idt;
pub use x86::idt::irq_entry;
pub use syscall::syscall;

mod aspace;
#[allow(dead_code)]
mod con;
mod dict;
mod dlist;
mod mboot;
mod mem;
mod process;
mod start32;
mod syscall;
pub mod util;
mod x86;

static log_assoc_procs : bool = false;
static log_page_fault : bool = false;
static log_switch : bool = false;
static log_queue : bool = false;
static log_irq : bool = false;
static log_idle : bool = false;

static mem_test : bool = false;

#[allow(dead_code)]
fn writeMBInfo(infop : *mboot::Info) {
    con::write("Multiboot info at ");
    con::writePtr(infop);
    con::putc('\n');

    let &info = unsafe { &*infop };
    con::write("Flags: ");
    con::writeHex(info.flags as uint);
    con::newline();

    if info.has(mboot::MemorySize) {
        con::writeUInt(info.mem_lower as uint);
        con::write("kB lower memory, ");
        con::writeUInt(info.mem_upper as uint);
        con::write("kB upper memory, ");
        con::writeUInt(((info.mem_lower + info.mem_upper + 1023) / 1024) as uint);
        con::write("MB total.\n");
    }
    if info.has(mboot::CommandLine) {
        let cmdline : *u8 = PhysAddr(info.cmdline as uint);
        con::write("Command line @");
        con::writePtr(cmdline);
        con::write(" (");
        con::writeHex(info.cmdline as uint);
        con::write(") \"");
        con::writeCStr(cmdline);
        con::write("\"\n");
    }
}

#[inline(never)]
pub fn generic_irq_handler(vec : u8) {
    if log_irq {
        write("IRQ! vec=");
        con::writeUInt(vec as uint);
        con::newline();
    }

    let c = cpu();
    let mask = 1 << (vec - 32);
    if c.irq_delayed & mask != 0 {
        write("IRQ: already delayed\n");
        return;
    }
    c.irq_delayed |= mask;
    if !c.irq_process().is_some() {
        abort("IRQ: no irq process");
    }
    let p = c.irq_process().unwrap();

    if log_irq {
        write("IRQ: proc=");
        con::writeMutPtr(p);
        con::newline();
    }

    syscall::try_deliver_irq(p);
}

pub fn page_fault(p : &mut Process, error : uint) -> ! {
    mod pf_errors {
        #![allow(dead_code)]
        // Error code flags.
        pub static PRESENT : uint = 1;
        pub static WRITE : uint = 2;
        pub static USER : uint = 4;
        pub static RSVD : uint = 8;
        pub static INSTR : uint = 16;
    }

    if log_page_fault {
        write("page fault ");
        con::writeHex(error);
        write(" cr2=");
        con::writePHex(x86::cr2());
        write(" rip=");
        con::writePHex(p.regs.rip as uint);
        write(" in process ");
        con::writeMutPtr(p);
        con::newline();
    }

    if (error & pf_errors::USER) == 0 {
        abort("kernel page fault\n");
    }

    let back = p.aspace().find_add_backing(x86::cr2() & !0xfff);
    // FIXME should return e.g. Option<> so we can detect failures better than
    // just aborting in find_add_backing.
    p.aspace().add_pte(back.vaddr(), back.pte());

    unsafe { cpu().switch_to(p); }
}

pub fn handler_NM() {
    abort("NM");
}

pub fn idle() -> ! {
    loop {
        if log_idle {
            write("idle\n");
        }
        cpu().process = None;
        unsafe { asm!("sti; hlt; cli" :::: "volatile"); }
    }
}

pub struct PerCpu {
    selfp : *mut PerCpu,
    stack : *mut u8,
    process : Option<&'static mut Process>,

    // End of assembly-fixed fields.
    memory : mem::PerCpu,
    runqueue : DList<Process>,

    irq_process : Option<&'static mut Process>,
    irq_delayed : uint,
}

impl PerCpu {
    unsafe fn new() -> *mut PerCpu {
        let mut mem = mem::PerCpu::new();
        let p : *mut PerCpu = mem.alloc_frame_panic();
        let stack : *mut u8 = mem.alloc_frame_panic();
        *p = PerCpu {
            selfp : p,
            stack : stack,
            memory : mem,
            runqueue : DList::empty(),
            process : None,
            irq_process : None,
            irq_delayed : 0,
        };
        return p
    }

    unsafe fn start(&mut self) {
        setup_msrs(self.selfp as uint);
    }

    fn queue(&mut self, p: &mut Process) {
        if log_queue {
            write("queue ");
            con::writeMutPtr(p);
            if p.is(process::Queued) {
                write(" already queued");
            }
            con::newline();
        }
        if !p.is_queued() {
            p.set(process::Queued);
            self.runqueue.append(p);
        }
    }

    unsafe fn run(&mut self) -> ! {
        match self.runqueue.pop() {
            Some(p) => {
                let r = &mut *p;
                if !r.is_queued() {
                    abort("popped unqueued item?");
                }
                r.unset(process::Queued);
                self.switch_to(r);
            },
            None => idle()
        }
    }

    unsafe fn switch_to(&mut self, p: &mut Process) -> ! {
        if log_switch {
            write("switch_to ");
            con::writeMutPtr(p);
            write(" rip=");
            con::writeHex(p.regs.rip as uint);
            if p.is(process::FastRet) {
                write(" fastret");
            }
            if p.is(process::Queued) {
                write(" queued");
            }
            con::newline();
        }
        p.set(process::Running);
        self.process = transmute(p as *mut Process);
        // TODO Check fpu_process, see if we need to set/reset TS bit in cr0
        x86::set_cr3(p.cr3);
        extern "C" {
            fn fastret(p : &mut Process, rax : uint) -> !;
            fn slowret(p : &mut Process) -> !;
        }
        if p.is(process::FastRet) {
            p.unset(process::FastRet);
            fastret(p, p.regs.rax);
        } else {
            slowret(p);
        }
    }

    fn syscall_return(&mut self, p: &mut Process, rax : uint) -> ! {
        p.regs.rax = rax;
        unsafe { self.switch_to(p); }
    }

    fn get_process<'a>(&'a mut self) -> Option<&'a mut Process> {
        match self.process {
            Some(ref p) => Some(unsafe { &mut *(*p as *mut Process) }),
            None => None,
        }
    }

    fn leave_proc(&mut self) {
        match self.process {
            Some(ref mut p) => p.unset(process::Running),
            None => (),
        };
    }

    fn irq_process<'a>(&'a mut self) -> Option<&'a mut Process> {
        match self.irq_process {
            Some(ref p) => Some(unsafe { &mut *(*p as *mut Process) }),
            None => None,
        }
    }

    fn is_irq_process(&self, p: &mut Process) -> bool {
        match self.irq_process {
        None => false,
        Some(ref q) => (p as *mut Process) == (*q as *mut Process),
        }
    }
}

// NB: One of the funky guarantees that Rust gives/requires is that there is
// at most one &mut reference to the same thing at any one time. This function
// can't quite guarantee that...
pub fn cpu() -> &mut PerCpu {
    unsafe {
        let mut ret : *mut PerCpu;
        asm!("movq %gs:($1), $0" : "=r"(ret) : "r"(0));
        return &mut *ret;
    }
}

#[lang="exchange_malloc"]
#[inline(always)]
pub fn xmalloc(size : uint, _align: uint) -> *mut u8 {
    malloc(size)
}

pub fn malloc(size : uint) -> *mut u8 {
    if size > 4096 {
        abort("oversized malloc");
    }
    #[inline(never)]
    fn malloc_() -> *mut u8 {
        cpu().memory.alloc_frame_panic()
    }
    malloc_()
}

#[inline(always)]
pub fn alloc<T>() -> *mut T {
    malloc(size_of::<T>()) as *mut T
}

pub fn free<T>(p : *mut T) {
    #[inline(never)]
    fn free_(p: *mut u8) {
        cpu().memory.free_frame(p);
    }
    if p.is_not_null() {
        free_(p as *mut u8);
    }
}

#[lang="exchange_free"]
#[inline(always)]
pub fn xfree(p : *mut u8, _size: uint, _align: uint) {
    free(p);
}

unsafe fn setup_msrs(gs : uint) {
    use x86::msr::*;
    use x86::rflags;
    use x86::efer;
    use x86::seg;
    #[allow(dead_code)]
    extern {
        fn syscall_entry_stub();
        fn syscall_entry_compat();
    }

    wrmsr(STAR, (seg::user_code32_base << 16) | seg::code);
    wrmsr(LSTAR, syscall_entry_stub as uint);
    wrmsr(CSTAR, syscall_entry_compat as uint);
    // FIXME: We want to clear a lot more flags - Direction for instance.
    // FreeBSD sets PSL_NT|PSL_T|PSL_I|PSL_C|PSL_D
    wrmsr(FMASK, rflags::IF | rflags::VM);
    wrmsr(EFER, rdmsr(EFER) | efer::SCE | efer::NXE);
    wrmsr(GSBASE, gs);
}

#[lang="eh_personality"]
fn dummy() {}

#[inline(never)]
fn new_proc_simple(start : uint, end_unaligned : uint) -> *mut Process {
    let end = (end_unaligned + 0xfff) & !0xfff;
    let start_page = start & !0xfff;
    let aspace : *mut AddressSpace = unsafe { transmute(box AddressSpace::new()) };
    let ret : *mut Process = unsafe { transmute(box Process::new(aspace)) };
    unsafe {
        (*ret).regs.rsp = 0x100000;
        (*ret).regs.rip = 0x100000 + (start & 0xfff);
    }

    unsafe {
        use aspace::mapflag::*;
        // Stack at 1MB - 4kB (not executable)
        (*aspace).mapcard_set(0x0ff000, 0, 0, Anon | R | W);
        (*aspace).mapcard_set(0x100000, 0, start_page - 0x100000, Phys | R | X);
        (*aspace).mapcard_set(0x100000 + (end - start_page), 0, 0, 0);
        /*match (*ret).aspace().mapcard_find(0x100000) {
            Some(c) => {
                write("0x100000: ");
                con::writePHex(c.vaddr());
                write(" -> paddr ");
                con::writePHex(c.paddr(0x100000));
                con::newline();
            },
            None => {
                abort("mapcard we added is not there anymore");
            }
        }*/
    }
    return ret;
}

fn assoc_procs(p : &mut Process, i : uint, q : &mut Process, j : uint) {
    if log_assoc_procs {
        con::writeMutPtr(p);
        con::putc(':');
        con::writeUInt(i);
        write(" <-> ");
        con::writeUInt(j);
        con::putc(':');
        con::writeMutPtr(q);
        con::newline();
    }
    p.assoc_handles(j, q, i);
}

fn init_modules(cpu : &mut PerCpu) {
    let &info = start32::MultiBootInfo();
    if !info.has(mboot::Modules) {
        return;
    }
    let mut head = DList::empty();
    let mut count = 0;
    for m in info.modules(start32::PhysAddr).iter() {
        write("Module ");
        con::writeHex(m.start as uint);
        write("..");
        con::writeHex(m.end as uint);
        write(": ");
        con::writeCStr(start32::PhysAddr(m.string as uint));
        con::newline();

        head.append(new_proc_simple(m.start as uint, m.end as uint));
        count += 1;
    }
    con::writeUInt(count);
    con::newline();
    // Now all processes are in our list. We need to remove them before it
    // gets possible to make them runnable.
    let mut i = 0;
    for p in head.iter() {
        if i == 0 {
            cpu.irq_process = Some(unsafe { &mut *(p as *mut Process) });
        }
        i += 1;
        // start iterating at j = i + 1, and q = p.next...
        let mut j = 0;
        for q in head.iter() {
            j += 1;
            if i < j {
                assoc_procs(p, i, q, j);
            }
        }
    }
    while match head.pop() {
        Some(p) => { cpu.queue(unsafe { &mut *p }); true },
        None => false
    } {}
}

pub fn dump_runqueue(queue: &DList<Process>) {
    let mut count = 0;
    for _ in queue.iter() {
        count += 1;
    }
    con::write("runqueue: ");
    con::writeUInt(count);
    con::newline();
    for p in queue.iter() {
        p.dump();
    }
}

#[no_mangle]
pub unsafe fn start64() -> ! {
    con::init(MutPhysAddr(0xb8000), 80, 25);
    con::clear();
    write("Hello World!\n");
    //writeMBInfo(start32::MultiBootInfo());

    x86::lgdt(start32::Gdtr());
    x86::ltr(x86::seg::tss64);

    idt::init();

    mem::global.init(start32::MultiBootInfo(), start32::MemoryStart());
    write("Memory initialized. ");
    mem::global.stat();

    let pcpu = PerCpu::new();
    let ref mut cpu = *pcpu;
    cpu.start();
    if mem_test {
        cpu.memory.test();
        mem::global.stat();
    }

    init_modules(cpu);
    //dump_runqueue(&cpu.runqueue);

//  let mut i = 0;
//  loop {
//      con.writeUInt(i);
//      con.putc('\n');
//      i += 1;
//  }
    cpu.run();
}
<|endoftext|>


<commit_before>// See LICENSE file for copyright and license details.

// Marauder is grid-less turn-based strategy game.

extern mod glfw;
extern mod gl;
extern mod cgmath;

use gl::types::{
  GLenum,
  GLuint,
  GLint,
  GLchar,
  GLfloat,
  GLsizeiptr,
  GLboolean
};
use cgmath::matrix::{Matrix, Mat4, Mat3, ToMat4};
use cgmath::projection;
use cgmath::angle;
use cgmath::vector::{Vec3, Vec2};

#[link(name = "glfw")]
#[link(name = "m")]
extern {}

/// usage: let s = mvp_matrix; println(type_of(&s));
fn type_of<T>(_: &T) -> &'static str {
  unsafe {
    (*std::unstable::intrinsics::get_tydesc::<T>()).name
  }
}

fn print_mat4(name: &str, mat: Mat4<f32>) {
  println!("{}:", name);
  for i in range(0u, 4) {
    for j in range(0u, 4) {
      print!("{} ", *mat.cr(i, j));
    }
    println("");
  }
  println("");
}

static mut MOUSE_POS: Vec2<f32> = Vec2{x: 0.0f32, y: 0.0};
static mut CAMERA_POS: Vec3<f32> = Vec3{x: 0.0f32, y: 0.0, z: 0.0};

static WIN_SIZE: Vec2<uint> = Vec2{x: 640, y: 480};
static VERTICES_COUNT: i32 = 3 * 2;

static VERTEX_DATA: [GLfloat, ..VERTICES_COUNT * 3] = [
   0.0,  1.0, 0.0,
   2.0, -1.0, 0.0,
  -2.0, -1.0, 0.0,

  0.0,  1.0,  0.0,
  0.0, -1.0,  2.0,
  0.0, -1.0, -2.0
];

static VERTEX_SHADER_SRC: &'static str = "
  #version 130
  in vec3 position;
  uniform mat4 model_view_proj_matrix;
  void main() {
    vec4 v = vec4(position, 1);
    gl_Position = model_view_proj_matrix * v;
  }
";

static FRAGMENT_SHADER_SRC: &'static str = "
  #version 130
  out vec4 out_color;
  void main() {
    out_color = vec4(1.0, 1.0, 1.0, 1.0);
  }
";

fn compile_shader(src: &str, ty: GLenum) -> GLuint {
  let shader = gl::CreateShader(ty);
  unsafe {
    // Attempt to compile the shader
    gl::ShaderSource(shader, 1, &src.to_c_str().unwrap(), std::ptr::null());
    gl::CompileShader(shader);

    // Get the compile status
    let mut status = gl::FALSE as GLint;
    gl::GetShaderiv(shader, gl::COMPILE_STATUS, &mut status);

    // Fail on error
    if status != (gl::TRUE as GLint) {
      let mut len = 0;
      gl::GetShaderiv(shader, gl::INFO_LOG_LENGTH, &mut len);
      // subtract 1 to skip the trailing null character
      let mut buf = std::vec::from_elem(len as uint - 1, 0u8);
      gl::GetShaderInfoLog(shader, len, std::ptr::mut_null(),
        std::vec::raw::to_mut_ptr(buf) as *mut GLchar
      );
      fail!("compile_shader(): " + std::str::raw::from_utf8(buf));
    }
  }
  shader
}

fn link_program(vertex_shader: GLuint, fragment_shader: GLuint) -> GLuint {
  let program = gl::CreateProgram();
  gl::AttachShader(program, vertex_shader);
  gl::AttachShader(program, fragment_shader);
  gl::LinkProgram(program);
  unsafe {
    // Get the link status
    let mut status = gl::FALSE as GLint;
    gl::GetProgramiv(program, gl::LINK_STATUS, &mut status);

    // Fail on error
    if status != (gl::TRUE as GLint) {
      let mut len: GLint = 0;
      gl::GetProgramiv(program, gl::INFO_LOG_LENGTH, &mut len);
      // subtract 1 to skip the trailing null character
      let mut buf = std::vec::from_elem(len as uint - 1, 0u8);
      gl::GetProgramInfoLog(program, len, std::ptr::mut_null(),
        std::vec::raw::to_mut_ptr(buf) as *mut GLchar
      );
      fail!("link_program(): " + std::str::raw::from_utf8(buf));
    }
  }
  program
}

fn tr(m: Mat4<f32>, v: Vec3<f32>) -> Mat4<f32> {
  let mut t = Mat4::<f32>::identity();
  *t.mut_cr(3, 0) = v.x;
  *t.mut_cr(3, 1) = v.y;
  *t.mut_cr(3, 2) = v.z;
  m.mul_m(&t)
}

fn rot_x(m: Mat4<f32>, angle: f32) -> Mat4<f32> {
  let r = Mat3::from_angle_x(angle::rad(angle)).to_mat4();
  m.mul_m(&r)
}

fn rot_y(m: Mat4<f32>, angle: f32) -> Mat4<f32> {
  let r = Mat3::from_angle_y(angle::rad(angle)).to_mat4();
  m.mul_m(&r)
}

struct Win {
  vertex_shader: GLuint,
  fragment_shader: GLuint,
  program: GLuint,
  vertex_array_obj: GLuint,
  vertex_buffer_obj: GLuint,
  matrixId: GLint, // TODO: _id
  window: glfw::Window
}

impl Win {
  fn new() -> Win {
    // TODO: Move to opengl_init method
    // glfw::window_hint::context_version(3, 2);

    let mut win =  Win{
      vertex_shader: 0,
      fragment_shader: 0,
      program: 0,
      vertex_array_obj: 0,
      vertex_buffer_obj: 0,
      matrixId: 0,
      window: glfw::Window::create(
        WIN_SIZE.x,
        WIN_SIZE.y,
        "OpenGL",
        glfw::Windowed
      ).unwrap()
    };

    win.window.make_context_current();
    win.window.set_cursor_pos_callback(~CursorPosContext);
    win.window.set_key_callback(~KeyContext);

    // Load the OpenGL function pointers
    gl::load_with(glfw::get_proc_address);

    // Create GLSL shaders
    win.vertex_shader = compile_shader(
      VERTEX_SHADER_SRC, gl::VERTEX_SHADER);
    win.fragment_shader = compile_shader(
      FRAGMENT_SHADER_SRC, gl::FRAGMENT_SHADER);
    win.program = link_program(win.vertex_shader, win.fragment_shader);

    unsafe {
      // Create Vertex Array Object
      gl::GenVertexArrays(1, &mut win.vertex_array_obj);
      gl::BindVertexArray(win.vertex_array_obj);

      // Create a Vertex Buffer Object and copy the vertex data to it
      gl::GenBuffers(1, &mut win.vertex_buffer_obj);
      gl::BindBuffer(gl::ARRAY_BUFFER, win.vertex_buffer_obj);
      let float_size = std::mem::size_of::<GLfloat>();
      let vertices_ptr = (VERTEX_DATA.len() * float_size) as GLsizeiptr;
      gl::BufferData(
        gl::ARRAY_BUFFER,
        vertices_ptr,
        std::cast::transmute(&VERTEX_DATA[0]),
        gl::STATIC_DRAW
      );

      gl::UseProgram(win.program);
      gl::BindFragDataLocation(win.program, 0, "out_color".to_c_str().unwrap());

      // Specify the layout of the vertex data
      let pos_attr = gl::GetAttribLocation(
        win.program, "position".to_c_str().unwrap()) as GLuint;
      gl::EnableVertexAttribArray(pos_attr);
      let size = 3;
      let normalized = gl::FALSE as GLboolean;
      let stride = 0;
      gl::VertexAttribPointer(
        pos_attr,
        size,
        gl::FLOAT,
        normalized,
        stride,
        std::ptr::null()
      );

      // Get a handle for our "model_view_proj_matrix" uniform.
      // Only at initialisation time.
      win.matrixId = gl::GetUniformLocation(
        win.program, "model_view_proj_matrix".to_c_str().unwrap()
      );
    }

    win
  }

  fn cleanup(&self) {
    // Cleanup
    gl::DeleteProgram(self.program);
    gl::DeleteShader(self.fragment_shader);
    gl::DeleteShader(self.vertex_shader);
    unsafe {
      gl::DeleteBuffers(1, &self.vertex_buffer_obj);
      gl::DeleteVertexArrays(1, &self.vertex_array_obj);
    }
  }

  fn update_matrices(&self) {
    let fov = angle::deg(45.0f32);
    let ratio = 4.0 / 3.0;
    let display_range_min = 0.1;
    let display_range_max = 100.0;
    let projection_matrix = projection::perspective(
      fov, ratio, display_range_min, display_range_max
    );

    let mut mvp_matrix = projection_matrix;
    unsafe {
      mvp_matrix = tr(mvp_matrix, Vec3{x: 0.0f32, y: 0.0, z: -10.0f32});
      mvp_matrix = rot_x(mvp_matrix, MOUSE_POS.y / 100.0);
      mvp_matrix = rot_y(mvp_matrix, MOUSE_POS.x / 100.0);
      mvp_matrix = tr(mvp_matrix, CAMERA_POS);
    }

    unsafe {
      // Send our transformation to the currently bound shader,
      // in the "model_view_proj_matrix" uniform.
      // For each model you render, since the model_view_proj_matrix
      // will be different (at least the M part).
      gl::UniformMatrix4fv(self.matrixId, 1, gl::FALSE, mvp_matrix.cr(0, 0));
    }
  }

  fn draw(&self) {
    self.update_matrices();

    // Clear the screen to black
    gl::ClearColor(0.3, 0.3, 0.3, 1.0);
    gl::Clear(gl::COLOR_BUFFER_BIT);

    // Draw a triangle from the 3 vertices
    gl::DrawArrays(gl::TRIANGLES, 0, VERTICES_COUNT);

    self.window.swap_buffers();
  }

  fn is_running(&self) -> bool {
    return !self.window.should_close()
  }

  fn process_events(&self) {
    glfw::poll_events();
  }
}

struct CursorPosContext;
impl glfw::CursorPosCallback for CursorPosContext {
  fn call(&self, w: &glfw::Window, xpos: f64, ypos: f64) {
    if w.get_mouse_button(glfw::MouseButtonRight) == glfw::Press {
      // println!("Cursor position: ({}, {})", xpos, ypos);
      unsafe {
        MOUSE_POS.x = xpos as f32;
        MOUSE_POS.y = ypos as f32;
      }
    }
  }
}

struct ErrorContext;
impl glfw::ErrorCallback for ErrorContext {
  fn call(&self, _: glfw::Error, description: ~str) {
    println!("GLFW Error: {:s}", description);
  }
}

struct KeyContext;
impl glfw::KeyCallback for KeyContext {
  fn call(
    &self,
    window: &glfw::Window,
    key:    glfw::Key,
    _:      std::libc::c_int,
    action: glfw::Action,
    _:      glfw::Modifiers
  ) {
    match (action, key) {
      (glfw::Press, glfw::KeyEscape) => {
        window.set_should_close(true)
      }
      (glfw::Press, glfw::KeySpace) => {
        println!("space")
      }
      (glfw::Press, glfw::KeyUp) => unsafe { CAMERA_POS.y -= 1.0f32 },
      (glfw::Press, glfw::KeyDown) => unsafe { CAMERA_POS.y += 1.0f32 },
      (glfw::Press, glfw::KeyRight) => unsafe { CAMERA_POS.x -= 1.0f32 },
      (glfw::Press, glfw::KeyLeft) => unsafe { CAMERA_POS.x += 1.0f32 },
      _ => {}
    }
  }
}

fn main() {
  glfw::set_error_callback(~ErrorContext);
  do glfw::start {
    let win =  Win::new();
    while win.is_running() {
      win.process_events();
      win.draw();
    }
    win.cleanup();
  }
}

#[start]
fn start(argc: int, argv: **u8) -> int {
  std::rt::start_on_main_thread(argc, argv, main)
}

// vim: set tabstop=2 shiftwidth=2 softtabstop=2 expandtab:
<commit_msg>Win: matrixId -> matrix_id<commit_after>// See LICENSE file for copyright and license details.

// Marauder is grid-less turn-based strategy game.

extern mod glfw;
extern mod gl;
extern mod cgmath;

use gl::types::{
  GLenum,
  GLuint,
  GLint,
  GLchar,
  GLfloat,
  GLsizeiptr,
  GLboolean
};
use cgmath::matrix::{Matrix, Mat4, Mat3, ToMat4};
use cgmath::projection;
use cgmath::angle;
use cgmath::vector::{Vec3, Vec2};

#[link(name = "glfw")]
#[link(name = "m")]
extern {}

/// usage: let s = mvp_matrix; println(type_of(&s));
fn type_of<T>(_: &T) -> &'static str {
  unsafe {
    (*std::unstable::intrinsics::get_tydesc::<T>()).name
  }
}

fn print_mat4(name: &str, mat: Mat4<f32>) {
  println!("{}:", name);
  for i in range(0u, 4) {
    for j in range(0u, 4) {
      print!("{} ", *mat.cr(i, j));
    }
    println("");
  }
  println("");
}

static mut MOUSE_POS: Vec2<f32> = Vec2{x: 0.0f32, y: 0.0};
static mut CAMERA_POS: Vec3<f32> = Vec3{x: 0.0f32, y: 0.0, z: 0.0};

static WIN_SIZE: Vec2<uint> = Vec2{x: 640, y: 480};
static VERTICES_COUNT: i32 = 3 * 2;

static VERTEX_DATA: [GLfloat, ..VERTICES_COUNT * 3] = [
   0.0,  1.0, 0.0,
   2.0, -1.0, 0.0,
  -2.0, -1.0, 0.0,

  0.0,  1.0,  0.0,
  0.0, -1.0,  2.0,
  0.0, -1.0, -2.0
];

static VERTEX_SHADER_SRC: &'static str = "
  #version 130
  in vec3 position;
  uniform mat4 model_view_proj_matrix;
  void main() {
    vec4 v = vec4(position, 1);
    gl_Position = model_view_proj_matrix * v;
  }
";

static FRAGMENT_SHADER_SRC: &'static str = "
  #version 130
  out vec4 out_color;
  void main() {
    out_color = vec4(1.0, 1.0, 1.0, 1.0);
  }
";

fn compile_shader(src: &str, ty: GLenum) -> GLuint {
  let shader = gl::CreateShader(ty);
  unsafe {
    // Attempt to compile the shader
    gl::ShaderSource(shader, 1, &src.to_c_str().unwrap(), std::ptr::null());
    gl::CompileShader(shader);

    // Get the compile status
    let mut status = gl::FALSE as GLint;
    gl::GetShaderiv(shader, gl::COMPILE_STATUS, &mut status);

    // Fail on error
    if status != (gl::TRUE as GLint) {
      let mut len = 0;
      gl::GetShaderiv(shader, gl::INFO_LOG_LENGTH, &mut len);
      // subtract 1 to skip the trailing null character
      let mut buf = std::vec::from_elem(len as uint - 1, 0u8);
      gl::GetShaderInfoLog(shader, len, std::ptr::mut_null(),
        std::vec::raw::to_mut_ptr(buf) as *mut GLchar
      );
      fail!("compile_shader(): " + std::str::raw::from_utf8(buf));
    }
  }
  shader
}

fn link_program(vertex_shader: GLuint, fragment_shader: GLuint) -> GLuint {
  let program = gl::CreateProgram();
  gl::AttachShader(program, vertex_shader);
  gl::AttachShader(program, fragment_shader);
  gl::LinkProgram(program);
  unsafe {
    // Get the link status
    let mut status = gl::FALSE as GLint;
    gl::GetProgramiv(program, gl::LINK_STATUS, &mut status);

    // Fail on error
    if status != (gl::TRUE as GLint) {
      let mut len: GLint = 0;
      gl::GetProgramiv(program, gl::INFO_LOG_LENGTH, &mut len);
      // subtract 1 to skip the trailing null character
      let mut buf = std::vec::from_elem(len as uint - 1, 0u8);
      gl::GetProgramInfoLog(program, len, std::ptr::mut_null(),
        std::vec::raw::to_mut_ptr(buf) as *mut GLchar
      );
      fail!("link_program(): " + std::str::raw::from_utf8(buf));
    }
  }
  program
}

fn tr(m: Mat4<f32>, v: Vec3<f32>) -> Mat4<f32> {
  let mut t = Mat4::<f32>::identity();
  *t.mut_cr(3, 0) = v.x;
  *t.mut_cr(3, 1) = v.y;
  *t.mut_cr(3, 2) = v.z;
  m.mul_m(&t)
}

fn rot_x(m: Mat4<f32>, angle: f32) -> Mat4<f32> {
  let r = Mat3::from_angle_x(angle::rad(angle)).to_mat4();
  m.mul_m(&r)
}

fn rot_y(m: Mat4<f32>, angle: f32) -> Mat4<f32> {
  let r = Mat3::from_angle_y(angle::rad(angle)).to_mat4();
  m.mul_m(&r)
}

struct Win {
  vertex_shader: GLuint,
  fragment_shader: GLuint,
  program: GLuint,
  vertex_array_obj: GLuint,
  vertex_buffer_obj: GLuint,
  matrix_id: GLint, // TODO: _id
  window: glfw::Window
}

impl Win {
  fn new() -> Win {
    // TODO: Move to opengl_init method
    // glfw::window_hint::context_version(3, 2);

    let mut win =  Win{
      vertex_shader: 0,
      fragment_shader: 0,
      program: 0,
      vertex_array_obj: 0,
      vertex_buffer_obj: 0,
      matrix_id: 0,
      window: glfw::Window::create(
        WIN_SIZE.x,
        WIN_SIZE.y,
        "OpenGL",
        glfw::Windowed
      ).unwrap()
    };

    win.window.make_context_current();
    win.window.set_cursor_pos_callback(~CursorPosContext);
    win.window.set_key_callback(~KeyContext);

    // Load the OpenGL function pointers
    gl::load_with(glfw::get_proc_address);

    // Create GLSL shaders
    win.vertex_shader = compile_shader(
      VERTEX_SHADER_SRC, gl::VERTEX_SHADER);
    win.fragment_shader = compile_shader(
      FRAGMENT_SHADER_SRC, gl::FRAGMENT_SHADER);
    win.program = link_program(win.vertex_shader, win.fragment_shader);

    unsafe {
      // Create Vertex Array Object
      gl::GenVertexArrays(1, &mut win.vertex_array_obj);
      gl::BindVertexArray(win.vertex_array_obj);

      // Create a Vertex Buffer Object and copy the vertex data to it
      gl::GenBuffers(1, &mut win.vertex_buffer_obj);
      gl::BindBuffer(gl::ARRAY_BUFFER, win.vertex_buffer_obj);
      let float_size = std::mem::size_of::<GLfloat>();
      let vertices_ptr = (VERTEX_DATA.len() * float_size) as GLsizeiptr;
      gl::BufferData(
        gl::ARRAY_BUFFER,
        vertices_ptr,
        std::cast::transmute(&VERTEX_DATA[0]),
        gl::STATIC_DRAW
      );

      gl::UseProgram(win.program);
      gl::BindFragDataLocation(win.program, 0, "out_color".to_c_str().unwrap());

      // Specify the layout of the vertex data
      let pos_attr = gl::GetAttribLocation(
        win.program, "position".to_c_str().unwrap()) as GLuint;
      gl::EnableVertexAttribArray(pos_attr);
      let size = 3;
      let normalized = gl::FALSE as GLboolean;
      let stride = 0;
      gl::VertexAttribPointer(
        pos_attr,
        size,
        gl::FLOAT,
        normalized,
        stride,
        std::ptr::null()
      );

      // Get a handle for our "model_view_proj_matrix" uniform.
      // Only at initialisation time.
      win.matrix_id = gl::GetUniformLocation(
        win.program, "model_view_proj_matrix".to_c_str().unwrap()
      );
    }

    win
  }

  fn cleanup(&self) {
    // Cleanup
    gl::DeleteProgram(self.program);
    gl::DeleteShader(self.fragment_shader);
    gl::DeleteShader(self.vertex_shader);
    unsafe {
      gl::DeleteBuffers(1, &self.vertex_buffer_obj);
      gl::DeleteVertexArrays(1, &self.vertex_array_obj);
    }
  }

  fn update_matrices(&self) {
    let fov = angle::deg(45.0f32);
    let ratio = 4.0 / 3.0;
    let display_range_min = 0.1;
    let display_range_max = 100.0;
    let projection_matrix = projection::perspective(
      fov, ratio, display_range_min, display_range_max
    );

    let mut mvp_matrix = projection_matrix;
    unsafe {
      mvp_matrix = tr(mvp_matrix, Vec3{x: 0.0f32, y: 0.0, z: -10.0f32});
      mvp_matrix = rot_x(mvp_matrix, MOUSE_POS.y / 100.0);
      mvp_matrix = rot_y(mvp_matrix, MOUSE_POS.x / 100.0);
      mvp_matrix = tr(mvp_matrix, CAMERA_POS);
    }

    unsafe {
      // Send our transformation to the currently bound shader,
      // in the "model_view_proj_matrix" uniform.
      // For each model you render, since the model_view_proj_matrix
      // will be different (at least the M part).
      gl::UniformMatrix4fv(self.matrix_id, 1, gl::FALSE, mvp_matrix.cr(0, 0));
    }
  }

  fn draw(&self) {
    self.update_matrices();

    // Clear the screen to black
    gl::ClearColor(0.3, 0.3, 0.3, 1.0);
    gl::Clear(gl::COLOR_BUFFER_BIT);

    // Draw a triangle from the 3 vertices
    gl::DrawArrays(gl::TRIANGLES, 0, VERTICES_COUNT);

    self.window.swap_buffers();
  }

  fn is_running(&self) -> bool {
    return !self.window.should_close()
  }

  fn process_events(&self) {
    glfw::poll_events();
  }
}

struct CursorPosContext;
impl glfw::CursorPosCallback for CursorPosContext {
  fn call(&self, w: &glfw::Window, xpos: f64, ypos: f64) {
    if w.get_mouse_button(glfw::MouseButtonRight) == glfw::Press {
      // println!("Cursor position: ({}, {})", xpos, ypos);
      unsafe {
        MOUSE_POS.x = xpos as f32;
        MOUSE_POS.y = ypos as f32;
      }
    }
  }
}

struct ErrorContext;
impl glfw::ErrorCallback for ErrorContext {
  fn call(&self, _: glfw::Error, description: ~str) {
    println!("GLFW Error: {:s}", description);
  }
}

struct KeyContext;
impl glfw::KeyCallback for KeyContext {
  fn call(
    &self,
    window: &glfw::Window,
    key:    glfw::Key,
    _:      std::libc::c_int,
    action: glfw::Action,
    _:      glfw::Modifiers
  ) {
    match (action, key) {
      (glfw::Press, glfw::KeyEscape) => {
        window.set_should_close(true)
      }
      (glfw::Press, glfw::KeySpace) => {
        println!("space")
      }
      (glfw::Press, glfw::KeyUp) => unsafe { CAMERA_POS.y -= 1.0f32 },
      (glfw::Press, glfw::KeyDown) => unsafe { CAMERA_POS.y += 1.0f32 },
      (glfw::Press, glfw::KeyRight) => unsafe { CAMERA_POS.x -= 1.0f32 },
      (glfw::Press, glfw::KeyLeft) => unsafe { CAMERA_POS.x += 1.0f32 },
      _ => {}
    }
  }
}

fn main() {
  glfw::set_error_callback(~ErrorContext);
  do glfw::start {
    let win =  Win::new();
    while win.is_running() {
      win.process_events();
      win.draw();
    }
    win.cleanup();
  }
}

#[start]
fn start(argc: int, argv: **u8) -> int {
  std::rt::start_on_main_thread(argc, argv, main)
}

// vim: set tabstop=2 shiftwidth=2 softtabstop=2 expandtab:
<|endoftext|>


<commit_before>//! Traits and types for building trie-based indices.
//!
//! The trie structure has each each element of each layer indicate a range of elements
//! in the next layer. Similarly, ranges of elements in the layer itself may correspond
//! to single elements in the layer above.

pub mod ordered;
pub mod ordered_leaf;
pub mod hashed;
// pub mod weighted;
// pub mod unordered;

/// A collection of tuples, and types for building and enumerating them.
///
/// There are some implicit assumptions about the elements in trie-structured data, mostly that
/// the items have some `(key, val)` structure. Perhaps we will nail these down better in the
/// future and get a better name for the trait.
pub trait Trie  : ::std::marker::Sized {
	/// The type of item from which the type is constructed.
	type Item;
	/// The type of cursor used to navigate the type.
	type Cursor: Cursor<Self>;
	/// The type used to merge instances of the type together.
	type MergeBuilder: MergeBuilder<Trie=Self>;
	/// The type used to assemble instances of the type from its `Item`s.
	type TupleBuilder: TupleBuilder<Trie=Self, Item=Self::Item>;

	/// The number of distinct keys, as distinct from the total number of tuples.
	fn keys(&self) -> usize;
	/// The total number of tuples in the collection.
	fn tuples(&self) -> usize;
	/// Returns a cursor capable of navigating the collection.
	fn cursor(&self) -> Self::Cursor { self.cursor_from(0, self.keys()) }
	/// Returns a cursor over a range of data, commonly used by others to restrict navigation to
	/// sub-collections.
	fn cursor_from(&self, lower: usize, upper: usize) -> Self::Cursor;

	/// Merges two collections into a third.
	///
	/// Collections are allowed their own semantics for merging. For example, unordered collections
	/// simply collect values, whereas weighted collections accumulate weights and discard elements
	/// whose weights are zero.
	fn merge(&self, other: &Self) -> Self {
		let mut merger = Self::MergeBuilder::with_capacity(self, other);
		// println!("{:?} and {:?}", self.keys(), other.keys());
		merger.push_merge((self, 0, self.keys()), (other, 0, other.keys()));
		merger.done()
	}
}

/// A type used to assemble collections.
pub trait Builder {
	/// The type of collection produced.
	type Trie: Trie;
	/// Requests a commitment to the offset of the current-most sub-collection.
	///
	/// This is most often used by parent collections to indicate that some set of values are now
	/// logically distinct from the next set of values, and that the builder should acknowledge this
	/// and report the limit (to store as an offset in the parent collection).
	fn boundary(&mut self) -> usize;
	/// Finalizes the building process and returns the collection.
	fn done(self) -> Self::Trie;
}

/// A type used to assemble collections by merging other instances.
pub trait MergeBuilder : Builder {
	/// Allocates an instance of the builder with sufficient capacity to contain the merged data.
	fn with_capacity(other1: &Self::Trie, other2: &Self::Trie) -> Self;
	/// Copies sub-collections of `other` into this collection.
	fn copy_range(&mut self, other: &Self::Trie, lower: usize, upper: usize);
	/// Merges two sub-collections into one sub-collection.
	fn push_merge(&mut self, other1: (&Self::Trie, usize, usize), other2: (&Self::Trie, usize, usize)) -> usize;
}

/// A type used to assemble collections from ordered sequences of tuples.
pub trait TupleBuilder : Builder {
	/// The type of item accepted for construction.
	type Item;
	/// Allocates a new builder.
	fn new() -> Self;
	/// Allocates a new builder with capacity for at least `cap` tuples.
	fn with_capacity(cap: usize) -> Self;	// <-- unclear how to set child capacities...
	/// Inserts a new into the collection.
	fn push_tuple(&mut self, tuple: Self::Item);
}

/// A type supporting navigation.
///
/// The precise meaning of this navigation is not defined by the trait. It is likely that having
/// navigated around, the cursor will be different in some other way, but the `Cursor` trait does
/// not explain how this is so.
pub trait Cursor<Storage> {
	/// The type revealed by the cursor.
	type Key;
	/// Reveals the current key.
	fn key<'a>(&self, storage: &'a Storage) -> &'a Self::Key;
	/// Advances the cursor by one element.
	fn step(&mut self, storage: &Storage);
	/// Advances the cursor until the location where `key` would be expected.
	fn seek(&mut self, storage: &Storage, key: &Self::Key);
	/// Returns `true` if the cursor points at valid data. Returns `false` if the cursor is exhausted.
	fn valid(&self, storage: &Storage) -> bool;
	/// Rewinds the cursor to its initial state.
	fn rewind(&mut self, storage: &Storage);
	/// Repositions the cursor to a different range of values.
	fn reposition(&mut self, storage: &Storage, lower: usize, upper: usize);
}<commit_msg>un-mod hash<commit_after>//! Traits and types for building trie-based indices.
//!
//! The trie structure has each each element of each layer indicate a range of elements
//! in the next layer. Similarly, ranges of elements in the layer itself may correspond
//! to single elements in the layer above.

pub mod ordered;
pub mod ordered_leaf;
// pub mod hashed;
// pub mod weighted;
// pub mod unordered;

/// A collection of tuples, and types for building and enumerating them.
///
/// There are some implicit assumptions about the elements in trie-structured data, mostly that
/// the items have some `(key, val)` structure. Perhaps we will nail these down better in the
/// future and get a better name for the trait.
pub trait Trie  : ::std::marker::Sized {
	/// The type of item from which the type is constructed.
	type Item;
	/// The type of cursor used to navigate the type.
	type Cursor: Cursor<Self>;
	/// The type used to merge instances of the type together.
	type MergeBuilder: MergeBuilder<Trie=Self>;
	/// The type used to assemble instances of the type from its `Item`s.
	type TupleBuilder: TupleBuilder<Trie=Self, Item=Self::Item>;

	/// The number of distinct keys, as distinct from the total number of tuples.
	fn keys(&self) -> usize;
	/// The total number of tuples in the collection.
	fn tuples(&self) -> usize;
	/// Returns a cursor capable of navigating the collection.
	fn cursor(&self) -> Self::Cursor { self.cursor_from(0, self.keys()) }
	/// Returns a cursor over a range of data, commonly used by others to restrict navigation to
	/// sub-collections.
	fn cursor_from(&self, lower: usize, upper: usize) -> Self::Cursor;

	/// Merges two collections into a third.
	///
	/// Collections are allowed their own semantics for merging. For example, unordered collections
	/// simply collect values, whereas weighted collections accumulate weights and discard elements
	/// whose weights are zero.
	fn merge(&self, other: &Self) -> Self {
		let mut merger = Self::MergeBuilder::with_capacity(self, other);
		// println!("{:?} and {:?}", self.keys(), other.keys());
		merger.push_merge((self, 0, self.keys()), (other, 0, other.keys()));
		merger.done()
	}
}

/// A type used to assemble collections.
pub trait Builder {
	/// The type of collection produced.
	type Trie: Trie;
	/// Requests a commitment to the offset of the current-most sub-collection.
	///
	/// This is most often used by parent collections to indicate that some set of values are now
	/// logically distinct from the next set of values, and that the builder should acknowledge this
	/// and report the limit (to store as an offset in the parent collection).
	fn boundary(&mut self) -> usize;
	/// Finalizes the building process and returns the collection.
	fn done(self) -> Self::Trie;
}

/// A type used to assemble collections by merging other instances.
pub trait MergeBuilder : Builder {
	/// Allocates an instance of the builder with sufficient capacity to contain the merged data.
	fn with_capacity(other1: &Self::Trie, other2: &Self::Trie) -> Self;
	/// Copies sub-collections of `other` into this collection.
	fn copy_range(&mut self, other: &Self::Trie, lower: usize, upper: usize);
	/// Merges two sub-collections into one sub-collection.
	fn push_merge(&mut self, other1: (&Self::Trie, usize, usize), other2: (&Self::Trie, usize, usize)) -> usize;
}

/// A type used to assemble collections from ordered sequences of tuples.
pub trait TupleBuilder : Builder {
	/// The type of item accepted for construction.
	type Item;
	/// Allocates a new builder.
	fn new() -> Self;
	/// Allocates a new builder with capacity for at least `cap` tuples.
	fn with_capacity(cap: usize) -> Self;	// <-- unclear how to set child capacities...
	/// Inserts a new into the collection.
	fn push_tuple(&mut self, tuple: Self::Item);
}

/// A type supporting navigation.
///
/// The precise meaning of this navigation is not defined by the trait. It is likely that having
/// navigated around, the cursor will be different in some other way, but the `Cursor` trait does
/// not explain how this is so.
pub trait Cursor<Storage> {
	/// The type revealed by the cursor.
	type Key;
	/// Reveals the current key.
	fn key<'a>(&self, storage: &'a Storage) -> &'a Self::Key;
	/// Advances the cursor by one element.
	fn step(&mut self, storage: &Storage);
	/// Advances the cursor until the location where `key` would be expected.
	fn seek(&mut self, storage: &Storage, key: &Self::Key);
	/// Returns `true` if the cursor points at valid data. Returns `false` if the cursor is exhausted.
	fn valid(&self, storage: &Storage) -> bool;
	/// Rewinds the cursor to its initial state.
	fn rewind(&mut self, storage: &Storage);
	/// Repositions the cursor to a different range of values.
	fn reposition(&mut self, storage: &Storage, lower: usize, upper: usize);
}<|endoftext|>


<commit_before>use quote;
use syn;

use std::error::Error;
use data_structures::ColumnInformation;
use inference::{establish_connection, get_table_data, determine_column_type,
                get_primary_keys, load_table_names, InferConnection};

pub fn derive_infer_table_from_schema(database_url: &str, table_name: &str)
    -> Result<quote::Tokens, Box<Error>>
{

    let connection = try!(establish_connection(database_url));
    let data = try!(get_table_data(&connection, table_name));
    let primary_keys = try!(get_primary_keys(&connection, table_name))
        .into_iter().map(syn::Ident::new);
    let table_name = syn::Ident::new(table_name);

    let mut tokens = Vec::with_capacity(data.len());
    for a in data {
        tokens.push(try!(column_def_tokens(&a, &connection)));
    }

    Ok(quote!(table! {
        #table_name (#(#primary_keys),*) {
            #(#tokens),*,
        }
    }))
}

pub fn infer_schema_for_schema_name<F1, F2>(
    database_url: &str,
    schema_name: Option<&str>,
    error_handler: F1,
    filter: F2
) -> Result<quote::Tokens, Box<Error>>
    where for<'a> F1: Fn(&'a str, Box<Error>),
          for<'a> F2: Fn(&'a str) -> bool
{
    let table_names = try!(load_table_names(&database_url, schema_name));
    let schema_inferences = table_names.into_iter().filter_map(|table_name| {
        if !filter(&table_name){
            return None;
        }
        let mod_ident = syn::Ident::new(format!("infer_{}", table_name));
        let table_name = match schema_name {
            Some(name) => format!("{}.{}", name, table_name),
            None => table_name,
        };
        let table = match derive_infer_table_from_schema(database_url, &table_name){
            Ok(table) => table,
            Err(e) => {
                error_handler(&table_name, e);
                return None;
            },
        };
        Some(quote! {
            mod #mod_ident {
                #table
            }
            pub use self::#mod_ident::*;
        })
    });

    match schema_name {
        Some(name) => {
            let schema_ident = syn::Ident::new(name);
            Ok(quote! { pub mod #schema_ident { #(#schema_inferences)* } })
        }
        None => Ok(quote!(#(#schema_inferences)*)),
    }
}

fn column_def_tokens(
    column: &ColumnInformation,
    connection: &InferConnection,
) -> Result<quote::Tokens, Box<Error>> {
    let column_name = syn::Ident::new(&*column.column_name);
    let column_type = try!(determine_column_type(column, connection));
    let tpe = if column_type.path[0] == "diesel" && column_type.path[1] == "types" {
        let path_segments = column_type.path
            .into_iter()
            .skip(2)
            .map(syn::PathSegment::from)
            .collect();
        syn::Path { global: false, segments: path_segments }
    }else{
        let path_segments = column_type.path
            .into_iter()
            .map(syn::PathSegment::from)
            .collect();
        syn::Path { global: true, segments: path_segments }
    };
    let mut tpe = quote!(#tpe);

    if column_type.is_array {
        tpe = quote!(Array<#tpe>);
    }
    if column_type.is_nullable {
        tpe = quote!(Nullable<#tpe>);
    }
    Ok(quote!(#column_name -> #tpe))
}
<commit_msg>Remove unused HRTB<commit_after>use quote;
use syn;

use std::error::Error;
use data_structures::ColumnInformation;
use inference::{establish_connection, get_table_data, determine_column_type,
                get_primary_keys, load_table_names, InferConnection};

pub fn derive_infer_table_from_schema(database_url: &str, table_name: &str)
    -> Result<quote::Tokens, Box<Error>>
{

    let connection = try!(establish_connection(database_url));
    let data = try!(get_table_data(&connection, table_name));
    let primary_keys = try!(get_primary_keys(&connection, table_name))
        .into_iter().map(syn::Ident::new);
    let table_name = syn::Ident::new(table_name);

    let mut tokens = Vec::with_capacity(data.len());
    for a in data {
        tokens.push(try!(column_def_tokens(&a, &connection)));
    }

    Ok(quote!(table! {
        #table_name (#(#primary_keys),*) {
            #(#tokens),*,
        }
    }))
}

pub fn infer_schema_for_schema_name<F1, F2>(
    database_url: &str,
    schema_name: Option<&str>,
    error_handler: F1,
    filter: F2
) -> Result<quote::Tokens, Box<Error>>
    where F1: Fn(&str, Box<Error>),
          F2: Fn(&str) -> bool
{
    let table_names = try!(load_table_names(&database_url, schema_name));
    let schema_inferences = table_names.into_iter().filter_map(|table_name| {
        if !filter(&table_name){
            return None;
        }
        let mod_ident = syn::Ident::new(format!("infer_{}", table_name));
        let table_name = match schema_name {
            Some(name) => format!("{}.{}", name, table_name),
            None => table_name,
        };
        let table = match derive_infer_table_from_schema(database_url, &table_name){
            Ok(table) => table,
            Err(e) => {
                error_handler(&table_name, e);
                return None;
            },
        };
        Some(quote! {
            mod #mod_ident {
                #table
            }
            pub use self::#mod_ident::*;
        })
    });

    match schema_name {
        Some(name) => {
            let schema_ident = syn::Ident::new(name);
            Ok(quote! { pub mod #schema_ident { #(#schema_inferences)* } })
        }
        None => Ok(quote!(#(#schema_inferences)*)),
    }
}

fn column_def_tokens(
    column: &ColumnInformation,
    connection: &InferConnection,
) -> Result<quote::Tokens, Box<Error>> {
    let column_name = syn::Ident::new(&*column.column_name);
    let column_type = try!(determine_column_type(column, connection));
    let tpe = if column_type.path[0] == "diesel" && column_type.path[1] == "types" {
        let path_segments = column_type.path
            .into_iter()
            .skip(2)
            .map(syn::PathSegment::from)
            .collect();
        syn::Path { global: false, segments: path_segments }
    }else{
        let path_segments = column_type.path
            .into_iter()
            .map(syn::PathSegment::from)
            .collect();
        syn::Path { global: true, segments: path_segments }
    };
    let mut tpe = quote!(#tpe);

    if column_type.is_array {
        tpe = quote!(Array<#tpe>);
    }
    if column_type.is_nullable {
        tpe = quote!(Nullable<#tpe>);
    }
    Ok(quote!(#column_name -> #tpe))
}
<|endoftext|>