<commit_before>//! Simplistic obj loader.

use std::io::fs::File;
use std::io::Reader;
use std::vec;
use std::str;
use std::str::WordIterator;
use std::from_str::FromStr;
use std::hashmap::HashMap;
use extra::arc::Arc;
use nalgebra::na::{Vec3, Vec2, Indexable};
use mesh::{Mesh, Coord, Normal, UV, SharedImmutable};
use mtl::MtlMaterial;
use mtl;

fn error(line: uint, err: &str) -> ! {
    fail!("At line " + line.to_str() + ": " + err)
}

fn warn(line: uint, err: &str) {
    println("At line " + line.to_str() + ": " + err)
}

/// Parses an obj file.
pub fn parse_file(path: &Path, mtl_base_dir: &Path, basename: &str) -> Option<~[(~str, Mesh, Option<MtlMaterial>)]> {
    if !path.exists() {
        None
    }
    else {
        let s   = File::open(path).expect("Cannot open the file: " + path.as_str().unwrap()).read_to_end();
        let obj = str::from_utf8_owned(s);
        Some(parse(obj, mtl_base_dir, basename))
    }
}

/// Parses a string representing an obj file.
pub fn parse(string: &str, mtl_base_dir: &Path, basename: &str) -> ~[(~str, Mesh, Option<MtlMaterial>)] {
    let mut coords:     ~[Coord]            = ~[];
    let mut normals:    ~[Normal]           = ~[];
    let mut uvs:        ~[UV]               = ~[];
    let mut groups:     HashMap<~str, uint> = HashMap::new();
    let mut groups_ids: ~[~[Vec3<i32>]]     = ~[];
    let mut curr_group: uint                = 0;
    let mut ignore_normals                  = false;
    let mut ignore_uvs                      = false;
    let mut mtllib                          = HashMap::new();
    let mut group2mtl                       = HashMap::new();

    groups_ids.push(~[]);
    groups.insert(basename.to_owned(), 0);

    for (l, line) in string.lines_any().enumerate() {
        let mut words = line.words();
        let tag = words.next();
        match tag {
            None    => { },
            Some(w) => {
                if w.len() != 0 && w[0] != ('#' as u8) {
                    match w {
                        &"v"      => coords.push(parse_v_or_vn(l, words)),
                        &"vn"     => if !ignore_normals { normals.push(parse_v_or_vn(l, words)) },
                        &"f"      => parse_f(l, words, &mut ignore_uvs, &mut ignore_normals, &mut groups_ids, curr_group),
                        &"vt"     => if !ignore_uvs { uvs.push(parse_vt(l, words)) },
                        &"g"      => curr_group = parse_g(l, words, basename, &mut groups, &mut groups_ids),
                        &"mtllib" => parse_mtllib(l, words, mtl_base_dir, &mut mtllib),
                        &"usemtl" => curr_group = parse_usemtl(l, words, curr_group, &mtllib, &mut group2mtl, &mut groups, &mut groups_ids),
                        _         => {
                            println("Warning: unknown line " + l.to_str() + " ignored: `" + line + "'");
                        }
                    }
                }
            }
        }
    }

    if !uvs.is_empty() && ignore_uvs {
        println("Warning: some texture coordinates are missing. Dropping texture coordinates"
                + " infos for every vertex.");
    }

    if !normals.is_empty() && ignore_normals {
        println("Warning: some normals are missing. Dropping normals infos for every vertex.");
    }

    reformat(
        coords,
        if ignore_normals { None } else { Some(normals) },
        if ignore_uvs { None } else { Some(uvs) },
        groups_ids,
        groups,
        group2mtl)
}

fn parse_usemtl<'a>(l:          uint,
                    mut ws:     WordIterator<'a>,
                    curr_group: uint,
                    mtllib:     &HashMap<~str, MtlMaterial>,
                    group2mtl:  &mut HashMap<uint, MtlMaterial>,
                    groups:     &mut HashMap<~str, uint>,
                    groups_ids: &mut ~[~[Vec3<i32>]])
                    -> uint {
    let mname = ws.to_owned_vec().connect(" ");
    let none  = "None";
    if mname.as_slice() != none.as_slice() {
        match mtllib.find(&mname) {
            None    => {
                warn(l, "could not find the material " + mname);

                curr_group
            },
            Some(m) => {
                if !group2mtl.contains_key(&curr_group) {
                    group2mtl.insert(curr_group, m.clone());
                    curr_group
                }
                else {
                    // multiple usemtls for one group
                    // NOTE: this is a violation of the obj specification, but we support it anyway
                    let g         = curr_group.to_str() + mname;
                    let new_group = parse_g(l, g.words(), "auto_generated_group_", groups, groups_ids);

                    group2mtl.insert(new_group, m.clone());

                    new_group
                }
            }
        }
    }
    else {
        curr_group
    }
}

fn parse_mtllib<'a>(l:            uint,
                    mut ws:       WordIterator<'a>,
                    mtl_base_dir: &Path,
                    mtllib:       &mut HashMap<~str, MtlMaterial>) {
    let filename = ws.to_owned_vec().connect(" ");

    let mut path = mtl_base_dir.clone();

    path.push(filename);

    let ms = mtl::parse_file(&path);

    match ms {
        Some(ms) =>
            for m in ms.move_iter() {
                mtllib.insert(m.name.clone(), m);
            },
        None => warn(l, "could not find the mtl file " + path.as_str().unwrap())
    }
}

fn parse_v_or_vn<'a>(l: uint, mut ws: WordIterator<'a>) -> Vec3<f32> {
    let sx = ws.next().unwrap_or_else(|| error(l, "3 components were expected, found 0."));
    let sy = ws.next().unwrap_or_else(|| error(l, "3 components were expected, found 1."));
    let sz = ws.next().unwrap_or_else(|| error(l, "3 components were expected, found 2."));

    let x: Option<f32> = FromStr::from_str(sx);
    let y: Option<f32> = FromStr::from_str(sy);
    let z: Option<f32> = FromStr::from_str(sz);

    let x = x.unwrap_or_else(|| error(l, "failed to parse `" + sx + "' as a f32."));
    let y = y.unwrap_or_else(|| error(l, "failed to parse `" + sy + "' as a f32."));
    let z = z.unwrap_or_else(|| error(l, "failed to parse `" + sz + "' as a f32."));

    Vec3::new(x, y, z)
}

fn parse_f<'a>(l:              uint,
               ws:             WordIterator<'a>,
               ignore_uvs:     &mut bool,
               ignore_normals: &mut bool,
               groups_ids:     &mut ~[~[Vec3<i32>]],
               curr_group:     uint) {
    // Four formats possible: v   v/t   v//n   v/t/n
    for (i, word) in ws.enumerate() {
        let mut curr_ids: Vec3<i32> = Bounded::max_value();

        for (i, w) in word.split('/').enumerate() {
            if i == 0 || w.len() != 0 {
                let idx: Option<i32> = FromStr::from_str(w);
                match idx {
                    Some(id) => curr_ids.set(i, id - 1),
                    None     => error(l, "failed to parse `" + w + "' as a i32.")
                }
            }
        }

        if i > 2 {
            // on the fly triangulation as trangle fan
            let g = &mut groups_ids[curr_group];
            let p1 = g[g.len() - i];
            let p2 = g[g.len() - 1];
            g.push(p1);
            g.push(p2);
        }

        if curr_ids.y == Bounded::max_value() {
            *ignore_uvs = true;
        }
        if curr_ids.z == Bounded::max_value() {
            *ignore_normals = true;
        }

        groups_ids[curr_group].push(curr_ids);
    }
}

fn parse_vt<'a>(l: uint, mut ws: WordIterator<'a>) -> UV {
    let _0 = "0.0";
    let sx  = ws.next().unwrap_or_else(|| error(l, "at least 2 components were expected, found 0."));
    let sy  = ws.next().unwrap_or_else(|| error(l, "at least 2 components were expected, found 1."));
    // let sz  = ws.next().unwrap_or(_0);

    let x: Option<f32> = FromStr::from_str(sx);
    let y: Option<f32> = FromStr::from_str(sy);
    // let z: Option<f32> = FromStr::from_str(sz);

    let x = x.unwrap_or_else(|| error(l, "failed to parse `" + sx + "' as a f32."));
    let y = y.unwrap_or_else(|| error(l, "failed to parse `" + sy + "' as a f32."));
    // let z = z.unwrap_or_else(|| error(l, "failed to parse `" + sz + "' as a f32."));

    Vec2::new(x, y)
}

fn parse_g<'a>(_:          uint,
               mut ws:     WordIterator<'a>,
               prefix:     &str,
               groups:     &mut HashMap<~str, uint>,
               groups_ids: &mut ~[~[Vec3<i32>]])
               -> uint {
    let suffix = ws.to_owned_vec().connect(" ");
    let name   = if suffix.len() == 0 { prefix.to_owned() } else { prefix + "/" + suffix };

    *groups.find_or_insert_with(name, |_| { groups_ids.push(~[]); groups_ids.len() - 1 })
}

fn reformat(coords:     ~[Coord],
            normals:    Option<~[Normal]>,
            uvs:        Option<~[UV]>,
            groups_ids: ~[~[Vec3<i32>]],
            groups:     HashMap<~str, uint>,
            group2mtl:  HashMap<uint, MtlMaterial>) -> ~[(~str, Mesh, Option<MtlMaterial>)] {
    let mut vt2id:  HashMap<Vec3<i32>, u32> = HashMap::new();
    let mut vertex_ids: ~[u32]      = ~[];
    let mut resc: ~[Coord]          = ~[];
    let mut resn: Option<~[Normal]> = normals.as_ref().map(|_| ~[]);
    let mut resu: Option<~[UV]>     = uvs.as_ref().map(|_| ~[]);
    let mut resfs: ~[~[Vec3<u32>]]  = ~[];
    let mut names: ~[~str]          = ~[];
    let mut mtls:  ~[Option<MtlMaterial>] = ~[];

    for (name, i) in groups.move_iter() {
        names.push(name);
        mtls.push(group2mtl.find(&i).map(|m| m.clone()));

        for point in groups_ids[i].iter() {
            let mut abs_point = *point;
            if abs_point.x < 0 { abs_point.x = coords.len() as i32 + abs_point.x + 1 };
            uvs.as_ref().map(|uvs| if abs_point.y < 0
                    { abs_point.y = uvs.len() as i32 + abs_point.y + 1 });
            normals.as_ref().map(|normals| if abs_point.z < 0
                        { abs_point.z = normals.len() as i32 + abs_point.z + 1 });

            let idx = match vt2id.find(&abs_point) {
                Some(i) => { vertex_ids.push(*i); None },
                None    => {
                    let idx = resc.len() as u32;
                    resc.push(coords[abs_point.x]);
                    resu.as_mut().map(|l| l.push(uvs.get_ref()[abs_point.y]));
                    resn.as_mut().map(|l| l.push(normals.get_ref()[abs_point.z]));

                    vertex_ids.push(idx);

                    Some(idx)
                }
            };

            idx.map(|i| vt2id.insert(abs_point, i));
        }

        let mut resf = vec::with_capacity(vertex_ids.len() / 3);

        assert!(vertex_ids.len() % 3 == 0);

        for f in vertex_ids.chunks(3) {
            resf.push(Vec3::new(f[0], f[1], f[2]))
        }

        resfs.push(resf);
        vertex_ids.clear();
    }

    let resc = SharedImmutable(Arc::new(resc));
    let resn = resn.map(|n| SharedImmutable(Arc::new(n)));
    let resu = resu.map(|u| SharedImmutable(Arc::new(u)));

    let mut meshes = ~[];
    for ((fs, name), mtl) in resfs.move_iter().zip(names.move_iter()).zip(mtls.move_iter()) {
        if fs.len() != 0 {
            let fs   = SharedImmutable(Arc::new(fs));
            let mesh = Mesh::new(resc.clone(), fs, resn.clone(), resu.clone(), false);
            meshes.push((name, mesh, mtl))
        }
    }

    meshes
}
<commit_msg>Make usemtl state-setting on the obj loader.<commit_after>//! Simplistic obj loader.

use std::io::fs::File;
use std::io::Reader;
use std::vec;
use std::str;
use std::str::WordIterator;
use std::from_str::FromStr;
use std::hashmap::HashMap;
use extra::arc::Arc;
use nalgebra::na::{Vec3, Vec2, Indexable};
use mesh::{Mesh, Coord, Normal, UV, SharedImmutable};
use mtl::MtlMaterial;
use mtl;

fn error(line: uint, err: &str) -> ! {
    fail!("At line " + line.to_str() + ": " + err)
}

fn warn(line: uint, err: &str) {
    println("At line " + line.to_str() + ": " + err)
}

/// Parses an obj file.
pub fn parse_file(path: &Path, mtl_base_dir: &Path, basename: &str) -> Option<~[(~str, Mesh, Option<MtlMaterial>)]> {
    if !path.exists() {
        None
    }
    else {
        let s   = File::open(path).expect("Cannot open the file: " + path.as_str().unwrap()).read_to_end();
        let obj = str::from_utf8_owned(s);
        Some(parse(obj, mtl_base_dir, basename))
    }
}

/// Parses a string representing an obj file.
pub fn parse(string: &str, mtl_base_dir: &Path, basename: &str) -> ~[(~str, Mesh, Option<MtlMaterial>)] {
    let mut coords:     ~[Coord]            = ~[];
    let mut normals:    ~[Normal]           = ~[];
    let mut uvs:        ~[UV]               = ~[];
    let mut groups:     HashMap<~str, uint> = HashMap::new();
    let mut groups_ids: ~[~[Vec3<i32>]]     = ~[];
    let mut curr_group: uint                = 0;
    let mut ignore_normals                  = false;
    let mut ignore_uvs                      = false;
    let mut mtllib                          = HashMap::new();
    let mut group2mtl                       = HashMap::new();
    let mut curr_mtl                        = None::<MtlMaterial>;

    groups_ids.push(~[]);
    groups.insert(basename.to_owned(), 0);

    for (l, line) in string.lines_any().enumerate() {
        let mut words = line.words();
        let tag = words.next();
        match tag {
            None    => { },
            Some(w) => {
                if w.len() != 0 && w[0] != ('#' as u8) {
                    match w {
                        &"v"      => coords.push(parse_v_or_vn(l, words)),
                        &"vn"     => if !ignore_normals { normals.push(parse_v_or_vn(l, words)) },
                        &"f"      => parse_f(l, words, &mut ignore_uvs, &mut ignore_normals, &mut groups_ids, curr_group),
                        &"vt"     => if !ignore_uvs { uvs.push(parse_vt(l, words)) },
                        &"g"      => {
                            curr_group = parse_g(l, words, basename, &mut groups, &mut groups_ids);
                            curr_mtl.as_ref().map(|mtl| group2mtl.insert(curr_group, mtl.clone()));
                        },
                        &"mtllib" => parse_mtllib(l, words, mtl_base_dir, &mut mtllib),
                        &"usemtl" => curr_group = parse_usemtl(l, words, curr_group, &mtllib, &mut group2mtl, &mut groups, &mut groups_ids, &mut curr_mtl),
                        _         => {
                            println("Warning: unknown line " + l.to_str() + " ignored: `" + line + "'");
                        }
                    }
                }
            }
        }
    }

    if !uvs.is_empty() && ignore_uvs {
        println("Warning: some texture coordinates are missing. Dropping texture coordinates"
                + " infos for every vertex.");
    }

    if !normals.is_empty() && ignore_normals {
        println("Warning: some normals are missing. Dropping normals infos for every vertex.");
    }

    reformat(
        coords,
        if ignore_normals { None } else { Some(normals) },
        if ignore_uvs { None } else { Some(uvs) },
        groups_ids,
        groups,
        group2mtl)
}

fn parse_usemtl<'a>(l:          uint,
                    mut ws:     WordIterator<'a>,
                    curr_group: uint,
                    mtllib:     &HashMap<~str, MtlMaterial>,
                    group2mtl:  &mut HashMap<uint, MtlMaterial>,
                    groups:     &mut HashMap<~str, uint>,
                    groups_ids: &mut ~[~[Vec3<i32>]],
                    curr_mtl:   &mut Option<MtlMaterial>)
                    -> uint {
    let mname = ws.to_owned_vec().connect(" ");
    let none  = "None";
    if mname.as_slice() != none.as_slice() {
        match mtllib.find(&mname) {
            None    => {
                *curr_mtl = None;
                warn(l, "could not find the material " + mname);

                curr_group
            },
            Some(m) => {
                if !group2mtl.contains_key(&curr_group) {
                    group2mtl.insert(curr_group, m.clone());
                    *curr_mtl = Some(m.clone());
                    curr_group
                }
                else {
                    // multiple usemtls for one group
                    // NOTE: this is a violation of the obj specification, but we support it anyway
                    let g         = curr_group.to_str() + mname;
                    let new_group = parse_g(l, g.words(), "auto_generated_group_", groups, groups_ids);

                    group2mtl.insert(new_group, m.clone());
                    *curr_mtl = Some(m.clone());

                    new_group
                }
            }
        }
    }
    else {
        *curr_mtl = None;
        curr_group
    }
}

fn parse_mtllib<'a>(l:            uint,
                    mut ws:       WordIterator<'a>,
                    mtl_base_dir: &Path,
                    mtllib:       &mut HashMap<~str, MtlMaterial>) {
    let filename = ws.to_owned_vec().connect(" ");

    let mut path = mtl_base_dir.clone();

    path.push(filename);

    let ms = mtl::parse_file(&path);

    match ms {
        Some(ms) =>
            for m in ms.move_iter() {
                mtllib.insert(m.name.clone(), m);
            },
        None => warn(l, "could not find the mtl file " + path.as_str().unwrap())
    }
}

fn parse_v_or_vn<'a>(l: uint, mut ws: WordIterator<'a>) -> Vec3<f32> {
    let sx = ws.next().unwrap_or_else(|| error(l, "3 components were expected, found 0."));
    let sy = ws.next().unwrap_or_else(|| error(l, "3 components were expected, found 1."));
    let sz = ws.next().unwrap_or_else(|| error(l, "3 components were expected, found 2."));

    let x: Option<f32> = FromStr::from_str(sx);
    let y: Option<f32> = FromStr::from_str(sy);
    let z: Option<f32> = FromStr::from_str(sz);

    let x = x.unwrap_or_else(|| error(l, "failed to parse `" + sx + "' as a f32."));
    let y = y.unwrap_or_else(|| error(l, "failed to parse `" + sy + "' as a f32."));
    let z = z.unwrap_or_else(|| error(l, "failed to parse `" + sz + "' as a f32."));

    Vec3::new(x, y, z)
}

fn parse_f<'a>(l:              uint,
               ws:             WordIterator<'a>,
               ignore_uvs:     &mut bool,
               ignore_normals: &mut bool,
               groups_ids:     &mut ~[~[Vec3<i32>]],
               curr_group:     uint) {
    // Four formats possible: v   v/t   v//n   v/t/n
    for (i, word) in ws.enumerate() {
        let mut curr_ids: Vec3<i32> = Bounded::max_value();

        for (i, w) in word.split('/').enumerate() {
            if i == 0 || w.len() != 0 {
                let idx: Option<i32> = FromStr::from_str(w);
                match idx {
                    Some(id) => curr_ids.set(i, id - 1),
                    None     => error(l, "failed to parse `" + w + "' as a i32.")
                }
            }
        }

        if i > 2 {
            // on the fly triangulation as trangle fan
            let g = &mut groups_ids[curr_group];
            let p1 = g[g.len() - i];
            let p2 = g[g.len() - 1];
            g.push(p1);
            g.push(p2);
        }

        if curr_ids.y == Bounded::max_value() {
            *ignore_uvs = true;
        }
        if curr_ids.z == Bounded::max_value() {
            *ignore_normals = true;
        }

        groups_ids[curr_group].push(curr_ids);
    }
}

fn parse_vt<'a>(l: uint, mut ws: WordIterator<'a>) -> UV {
    let _0 = "0.0";
    let sx  = ws.next().unwrap_or_else(|| error(l, "at least 2 components were expected, found 0."));
    let sy  = ws.next().unwrap_or_else(|| error(l, "at least 2 components were expected, found 1."));
    // let sz  = ws.next().unwrap_or(_0);

    let x: Option<f32> = FromStr::from_str(sx);
    let y: Option<f32> = FromStr::from_str(sy);
    // let z: Option<f32> = FromStr::from_str(sz);

    let x = x.unwrap_or_else(|| error(l, "failed to parse `" + sx + "' as a f32."));
    let y = y.unwrap_or_else(|| error(l, "failed to parse `" + sy + "' as a f32."));
    // let z = z.unwrap_or_else(|| error(l, "failed to parse `" + sz + "' as a f32."));

    Vec2::new(x, y)
}

fn parse_g<'a>(_:          uint,
               mut ws:     WordIterator<'a>,
               prefix:     &str,
               groups:     &mut HashMap<~str, uint>,
               groups_ids: &mut ~[~[Vec3<i32>]])
               -> uint {
    let suffix = ws.to_owned_vec().connect(" ");
    let name   = if suffix.len() == 0 { prefix.to_owned() } else { prefix + "/" + suffix };

    *groups.find_or_insert_with(name, |_| { groups_ids.push(~[]); groups_ids.len() - 1 })
}

fn reformat(coords:     ~[Coord],
            normals:    Option<~[Normal]>,
            uvs:        Option<~[UV]>,
            groups_ids: ~[~[Vec3<i32>]],
            groups:     HashMap<~str, uint>,
            group2mtl:  HashMap<uint, MtlMaterial>) -> ~[(~str, Mesh, Option<MtlMaterial>)] {
    let mut vt2id:  HashMap<Vec3<i32>, u32> = HashMap::new();
    let mut vertex_ids: ~[u32]      = ~[];
    let mut resc: ~[Coord]          = ~[];
    let mut resn: Option<~[Normal]> = normals.as_ref().map(|_| ~[]);
    let mut resu: Option<~[UV]>     = uvs.as_ref().map(|_| ~[]);
    let mut resfs: ~[~[Vec3<u32>]]  = ~[];
    let mut names: ~[~str]          = ~[];
    let mut mtls:  ~[Option<MtlMaterial>] = ~[];

    for (name, i) in groups.move_iter() {
        names.push(name);
        mtls.push(group2mtl.find(&i).map(|m| m.clone()));

        for point in groups_ids[i].iter() {
            let mut abs_point = *point;
            if abs_point.x < 0 { abs_point.x = coords.len() as i32 + abs_point.x + 1 };
            uvs.as_ref().map(|uvs| if abs_point.y < 0
                    { abs_point.y = uvs.len() as i32 + abs_point.y + 1 });
            normals.as_ref().map(|normals| if abs_point.z < 0
                        { abs_point.z = normals.len() as i32 + abs_point.z + 1 });

            let idx = match vt2id.find(&abs_point) {
                Some(i) => { vertex_ids.push(*i); None },
                None    => {
                    let idx = resc.len() as u32;
                    resc.push(coords[abs_point.x]);
                    resu.as_mut().map(|l| l.push(uvs.get_ref()[abs_point.y]));
                    resn.as_mut().map(|l| l.push(normals.get_ref()[abs_point.z]));

                    vertex_ids.push(idx);

                    Some(idx)
                }
            };

            idx.map(|i| vt2id.insert(abs_point, i));
        }

        let mut resf = vec::with_capacity(vertex_ids.len() / 3);

        assert!(vertex_ids.len() % 3 == 0);

        for f in vertex_ids.chunks(3) {
            resf.push(Vec3::new(f[0], f[1], f[2]))
        }

        resfs.push(resf);
        vertex_ids.clear();
    }

    let resc = SharedImmutable(Arc::new(resc));
    let resn = resn.map(|n| SharedImmutable(Arc::new(n)));
    let resu = resu.map(|u| SharedImmutable(Arc::new(u)));

    let mut meshes = ~[];
    for ((fs, name), mtl) in resfs.move_iter().zip(names.move_iter()).zip(mtls.move_iter()) {
        if fs.len() != 0 {
            let fs   = SharedImmutable(Arc::new(fs));
            let mesh = Mesh::new(resc.clone(), fs, resn.clone(), resu.clone(), false);
            meshes.push((name, mesh, mtl))
        }
    }

    meshes
}
<|endoftext|>


<commit_before>use ast::{Atom, Expr, List, Vector, Env, Function};
use error::*;
use itertools::Itertools;
use std::collections::HashMap;
use std::ops::{Sub, Div};

pub fn env<'a>() -> Env<'a> {
    let mut builtins = HashMap::new();

    let add_symbol = |builtins: &mut HashMap<String, Expr>, name, f| {
        builtins.insert(String::from(name), Function::builtin(name, f).into());
    };

    // TODO: change booleans from functions to special forms
    add_symbol(&mut builtins, "not", Box::new(not));
    add_symbol(&mut builtins, "or", Box::new(or));
    add_symbol(&mut builtins, "and", Box::new(and));
    add_symbol(&mut builtins, "print", Box::new(print));

    // Mathematical tokens
    add_symbol(&mut builtins, "+", Box::new(add));
    add_symbol(&mut builtins, "-", Box::new(sub));
    add_symbol(&mut builtins, "*", Box::new(mul));
    add_symbol(&mut builtins, "/", Box::new(div));

    // Comparison tokens
    add_symbol(&mut builtins, "=", Box::new(equal));
    add_symbol(&mut builtins, "<", Box::new(less));
    add_symbol(&mut builtins, "<=", Box::new(less_eq));
    add_symbol(&mut builtins, ">", Box::new(greater));
    add_symbol(&mut builtins, ">=", Box::new(greater_eq));

    // List operations
    add_symbol(&mut builtins, "first", Box::new(first));
    add_symbol(&mut builtins, "rest", Box::new(rest));
    add_symbol(&mut builtins, "cons", Box::new(cons));

    // REPL operations
    add_symbol(&mut builtins, "exit", Box::new(exit));

    Env::new(builtins, None)
}

fn unwrap_atoms<I>(args: I) -> Result<Vec<Atom>>
where
    I: Iterator<Item = Expr>,
{
    args.map(|e| e.atom().cloned())
        .collect::<Option<Vec<_>>>()
        .ok_or("expected atom".into())
}

fn check_args(f: &str, args: &[Expr], arity: usize) -> Result<()> {
    if args.len() == arity {
        Ok(())
    } else {
        Err(format!("{} expected {} arguments", f, arity).into())
    }
}

pub fn add(args: &[Expr], _env: &Env) -> Result<Expr> {
    // Unwrap to atoms
    let atoms = unwrap_atoms(args.iter().cloned())?;

    // Check all arguments are numeric
    if atoms.iter().all(Atom::is_num) {
        if atoms.iter().any(Atom::is_flt) {
            // If any are float, promote to float and perform float addition
            Ok(Expr::from(
                atoms
                    .into_iter()
                    .map(|a| a.map_int(|x: i64| x as f64))
                    .map(|x| x.flt().unwrap())
                    .sum::<f64>(),
            ))
        } else {
            // Otherwise perform integer addition
            Ok(Expr::from(
                atoms.into_iter().map(|x| x.int().unwrap()).sum::<i64>(),
            ))
        }
    } else {
        Err("#[+] expected numeric".into())
    }
}

pub fn sub(args: &[Expr], _env: &Env) -> Result<Expr> {
    // Unwrap to atoms
    let atoms = unwrap_atoms(args.iter().cloned())?;

    // Check all arguments are numeric
    if !atoms.iter().all(Atom::is_num) {
        return Err("#[-] expected numeric".into());
    }

    // If any are float, promote all to float and perform float subtraction
    if atoms.iter().any(Atom::is_flt) {
        // If one argument, negate and return
        if atoms.len() == 1 {
            let mut atoms = atoms;
            return Ok(Expr::from(atoms.remove(0).map_flt(|x| -x)));
        }
        let mut nums = atoms
            .into_iter()
            .map(|a| a.map_int(|x: i64| x as f64))
            .map(|x| x.flt().unwrap());

        nums.next()
            .map(|first| nums.fold(first, Sub::sub))
            .map(Expr::from)
            .ok_or("#[-] expected 1 (negate) or 2+ (subtract) arguments".into())
    }
    // Otherwise, perform integer addition
    else {
        // If one argument, negate and return
        if atoms.len() == 1 {
            let mut atoms = atoms;
            return Ok(Expr::from(atoms.remove(0).map_int(|x| -x)));
        }
        let mut nums = atoms.iter().map(|x| x.int().unwrap());

        nums.next()
            .map(|first| Expr::from(nums.fold(first, |acc, x| acc - x)))
            .ok_or("Expected 1 (negation) or 2+ (subtraction) arguments".into())
    }
}

pub fn mul(args: &[Expr], _env: &Env) -> Result<Expr> {
    // Unwrap to atoms
    let atoms = unwrap_atoms(args.iter().cloned())?;

    // Check all arguments are numeric
    if atoms.iter().all(Atom::is_num) {
        if atoms.iter().any(Atom::is_flt) {
            // If any are float, promote to float and perform float multiplication
            Ok(Expr::from(
                atoms
                    .into_iter()
                    .map(|a| a.map_int(|x: i64| x as f64))
                    .map(|x| x.flt().unwrap())
                    .product::<f64>(),
            ))
        } else {
            // Otherwise perform integer multiplication
            Ok(Expr::from(
                atoms.into_iter().map(|x| x.int().unwrap()).product::<i64>(),
            ))
        }
    } else {
        Err("#[*] expected numeric".into())
    }
}

pub fn div(args: &[Expr], _env: &Env) -> Result<Expr> {
    // Unwrap to atoms
    let atoms = unwrap_atoms(args.iter().cloned())?;

    // Check all arguments are numeric
    if !atoms.iter().all(Atom::is_num) {
        return Err("#[-] expected numeric".into());
    }

    // If any are float, promote all to float and perform float division
    if atoms.iter().any(Atom::is_flt) {
        let mut nums = atoms
            .into_iter()
            .map(|a| a.map_int(|x: i64| x as f64))
            .map(|x| x.flt().unwrap());

        nums.next()
            .map(|first| nums.fold(first, Div::div))
            .map(Expr::from)
            .ok_or("#[/] expected at least 2 args".into())
    }
    // Otherwise, perform integer division
    else {
        let mut nums = atoms.iter().map(|x| x.int().unwrap());

        nums.next()
            .ok_or("#[/] expected at least 2 args".into())
            .and_then(|first| {
                nums.map(|x| if x == 0 {
                    Err("division by zero".into())
                } else {
                    Ok(x)
                }).fold_results(first, Div::div)
            })
            .map(Expr::from)
    }
}

pub fn equal(args: &[Expr], _env: &Env) -> Result<Expr> {
    check_args("#[=]", args, 2)?;
    Ok(Expr::from(args[0] == args[1]))
}

pub fn less(args: &[Expr], _env: &Env) -> Result<Expr> {
    check_args("#[<]", args, 2)?;
    match (&args[0], &args[1]) {
        (&Expr::Atom(ref a), &Expr::Atom(ref b)) => Ok(Expr::from(a < b)),
        _ => Err(
            format!("comparison undefined for: {}, {}", args[0], args[1]).into(),
        ),
    }
}

pub fn less_eq(args: &[Expr], _env: &Env) -> Result<Expr> {
    check_args("#[<=]", args, 2)?;
    match (&args[0], &args[1]) {
        (&Expr::Atom(ref a), &Expr::Atom(ref b)) => Ok(Expr::from(a <= b)),
        _ => Err(
            format!("comparison undefined for: {}, {}", args[0], args[1]).into(),
        ),
    }
}

pub fn greater(args: &[Expr], _env: &Env) -> Result<Expr> {
    check_args("#[>]", args, 2)?;
    match (&args[0], &args[1]) {
        (&Expr::Atom(ref a), &Expr::Atom(ref b)) => Ok(Expr::from(a > b)),
        _ => Err(
            format!("comparison undefined for: {}, {}", args[0], args[1]).into(),
        ),
    }
}

pub fn greater_eq(args: &[Expr], _env: &Env) -> Result<Expr> {
    check_args("#[>=]", args, 2)?;
    match (&args[0], &args[1]) {
        (&Expr::Atom(ref a), &Expr::Atom(ref b)) => Ok(Expr::from(a >= b)),
        _ => Err(
            format!("comparison undefined for: {}, {}", args[0], args[1]).into(),
        ),
    }
}

pub fn not(args: &[Expr], _env: &Env) -> Result<Expr> {
    check_args("#[not]", args, 1)?;
    match &args[0] {
        &Expr::Atom(Atom::Bool(b)) => Ok(Expr::from(!b)),
        _ => Err(format!("negation undefined for: {}", args[0]).into()),
    }
}

pub fn and(args: &[Expr], _env: &Env) -> Result<Expr> {
    // Unwrap to atoms
    let atoms = unwrap_atoms(args.iter().cloned())?;

    atoms
        .into_iter()
        .map(|a| a.boolean())
        .collect::<Option<Vec<_>>>()
        .ok_or("#[and] expected boolean argument".into())
        .map(|bools| bools.iter().all(|b| *b))
        .map(Expr::from)
}

pub fn or(args: &[Expr], _env: &Env) -> Result<Expr> {
    // Unwrap to atoms
    let atoms = unwrap_atoms(args.iter().cloned())?;

    atoms
        .into_iter()
        .map(|a| a.boolean())
        .collect::<Option<Vec<_>>>()
        .ok_or("#[or] expected boolean argument".into())
        .map(|bools| bools.iter().any(|b| *b))
        .map(Expr::from)
}

pub fn print(args: &[Expr], _env: &Env) -> Result<Expr> {
    check_args("#[print]", args, 1)?;
    println!("{}", args[0]);
    Ok(Expr::Nil)
}

pub fn first(args: &[Expr], _env: &Env) -> Result<Expr> {
    check_args("#[first]", args, 1)?;

    match &args[0] {
        &Expr::List(ref l) => Ok(l.0.first().cloned().unwrap_or(Expr::Nil)),
        &Expr::Vector(ref q) => Ok(q.0.first().cloned().unwrap_or(Expr::Nil)),
        _ => Err("#[first] expected list".into()),
    }
}

pub fn rest(args: &[Expr], _env: &Env) -> Result<Expr> {
    match &args[0] {
        &Expr::List(ref l) => {
            Ok(l.0.split_first()
                .map(|(_, rest)| {
                    Expr::List(List(rest.to_vec()))
                }).unwrap_or(Expr::Nil))
        },
        &Expr::Vector(ref v) => {
            Ok(v.0.split_first()
                .map(|(_, rest)| {
                    Expr::Vector(Vector(rest.to_vec()))
                }).unwrap_or(Expr::Nil))
        },
        _ => Err("#[rest] expected list".into()),
    }
}

// (cons item seq)
pub fn cons(args: &[Expr], _env: &Env) -> Result<Expr> {
    check_args("#[cons]", args, 2)?;

    match &args[2] {
        &Expr::List(ref l) => {
            let mut new = l.clone();
            new.0.insert(0, args[1].clone());
            Ok(Expr::List(new))
        },
        &Expr::Vector(ref v) => {
            let mut new = v.clone();
            new.0.push(args[1].clone());
            Ok(Expr::Vector(new))
        },
        _ => Err("#[cons] expected list".into()),
    }
}

// (exit)
pub fn exit(_args: &[Expr], _env: &Env) -> Result<Expr> {
    Err(ErrorKind::Exit(0).into())
}<commit_msg>Cleanup builtins function<commit_after>use ast::{Atom, Expr, List, Vector, Env, Function, Lambda};
use error::*;
use itertools::Itertools;
use std::collections::HashMap;
use std::ops::{Sub, Div};

pub fn env<'a>() -> Env<'a> {
    let table: Vec<(&str, Box<Lambda>)> = vec![
        ("not",     Box::new(not)),
        ("or",      Box::new(or)),
        ("and",     Box::new(and)),
        ("print",   Box::new(print)),
        ("+",       Box::new(add)),
        ("-",       Box::new(sub)),
        ("*",       Box::new(mul)),
        ("/",       Box::new(div)),
        ("=",       Box::new(equal)),
        ("<",       Box::new(less)),
        ("<=",      Box::new(less_eq)),
        (">",       Box::new(greater)),
        (">=",      Box::new(greater_eq)),
        ("first",   Box::new(first)),
        ("rest",    Box::new(rest)),
        ("cons",    Box::new(cons)),
        ("exit",    Box::new(exit)),
    ];

    let builtins = table.into_iter()
        .map(|(symbol, f)| {
            (String::from(symbol), Expr::from(Function::builtin(symbol, f)))
        }).collect::<HashMap<_, _>>();

    Env::new(builtins, None)
}

fn unwrap_atoms<I>(args: I) -> Result<Vec<Atom>>
where
    I: Iterator<Item = Expr>,
{
    args.map(|e| e.atom().cloned())
        .collect::<Option<Vec<_>>>()
        .ok_or("expected atom".into())
}

fn check_args(f: &str, args: &[Expr], arity: usize) -> Result<()> {
    if args.len() == arity {
        Ok(())
    } else {
        Err(format!("{} expected {} arguments", f, arity).into())
    }
}

pub fn add(args: &[Expr], _env: &Env) -> Result<Expr> {
    // Unwrap to atoms
    let atoms = unwrap_atoms(args.iter().cloned())?;

    // Check all arguments are numeric
    if atoms.iter().all(Atom::is_num) {
        if atoms.iter().any(Atom::is_flt) {
            // If any are float, promote to float and perform float addition
            Ok(Expr::from(
                atoms
                    .into_iter()
                    .map(|a| a.map_int(|x: i64| x as f64))
                    .map(|x| x.flt().unwrap())
                    .sum::<f64>(),
            ))
        } else {
            // Otherwise perform integer addition
            Ok(Expr::from(
                atoms.into_iter().map(|x| x.int().unwrap()).sum::<i64>(),
            ))
        }
    } else {
        Err("#[+] expected numeric".into())
    }
}

pub fn sub(args: &[Expr], _env: &Env) -> Result<Expr> {
    // Unwrap to atoms
    let atoms = unwrap_atoms(args.iter().cloned())?;

    // Check all arguments are numeric
    if !atoms.iter().all(Atom::is_num) {
        return Err("#[-] expected numeric".into());
    }

    // If any are float, promote all to float and perform float subtraction
    if atoms.iter().any(Atom::is_flt) {
        // If one argument, negate and return
        if atoms.len() == 1 {
            let mut atoms = atoms;
            return Ok(Expr::from(atoms.remove(0).map_flt(|x| -x)));
        }
        let mut nums = atoms
            .into_iter()
            .map(|a| a.map_int(|x: i64| x as f64))
            .map(|x| x.flt().unwrap());

        nums.next()
            .map(|first| nums.fold(first, Sub::sub))
            .map(Expr::from)
            .ok_or("#[-] expected 1 (negate) or 2+ (subtract) arguments".into())
    }
    // Otherwise, perform integer addition
    else {
        // If one argument, negate and return
        if atoms.len() == 1 {
            let mut atoms = atoms;
            return Ok(Expr::from(atoms.remove(0).map_int(|x| -x)));
        }
        let mut nums = atoms.iter().map(|x| x.int().unwrap());

        nums.next()
            .map(|first| Expr::from(nums.fold(first, |acc, x| acc - x)))
            .ok_or("Expected 1 (negation) or 2+ (subtraction) arguments".into())
    }
}

pub fn mul(args: &[Expr], _env: &Env) -> Result<Expr> {
    // Unwrap to atoms
    let atoms = unwrap_atoms(args.iter().cloned())?;

    // Check all arguments are numeric
    if atoms.iter().all(Atom::is_num) {
        if atoms.iter().any(Atom::is_flt) {
            // If any are float, promote to float and perform float multiplication
            Ok(Expr::from(
                atoms
                    .into_iter()
                    .map(|a| a.map_int(|x: i64| x as f64))
                    .map(|x| x.flt().unwrap())
                    .product::<f64>(),
            ))
        } else {
            // Otherwise perform integer multiplication
            Ok(Expr::from(
                atoms.into_iter().map(|x| x.int().unwrap()).product::<i64>(),
            ))
        }
    } else {
        Err("#[*] expected numeric".into())
    }
}

pub fn div(args: &[Expr], _env: &Env) -> Result<Expr> {
    // Unwrap to atoms
    let atoms = unwrap_atoms(args.iter().cloned())?;

    // Check all arguments are numeric
    if !atoms.iter().all(Atom::is_num) {
        return Err("#[-] expected numeric".into());
    }

    // If any are float, promote all to float and perform float division
    if atoms.iter().any(Atom::is_flt) {
        let mut nums = atoms
            .into_iter()
            .map(|a| a.map_int(|x: i64| x as f64))
            .map(|x| x.flt().unwrap());

        nums.next()
            .map(|first| nums.fold(first, Div::div))
            .map(Expr::from)
            .ok_or("#[/] expected at least 2 args".into())
    }
    // Otherwise, perform integer division
    else {
        let mut nums = atoms.iter().map(|x| x.int().unwrap());

        nums.next()
            .ok_or("#[/] expected at least 2 args".into())
            .and_then(|first| {
                nums.map(|x| if x == 0 {
                    Err("division by zero".into())
                } else {
                    Ok(x)
                }).fold_results(first, Div::div)
            })
            .map(Expr::from)
    }
}

pub fn equal(args: &[Expr], _env: &Env) -> Result<Expr> {
    check_args("#[=]", args, 2)?;
    Ok(Expr::from(args[0] == args[1]))
}

pub fn less(args: &[Expr], _env: &Env) -> Result<Expr> {
    check_args("#[<]", args, 2)?;
    match (&args[0], &args[1]) {
        (&Expr::Atom(ref a), &Expr::Atom(ref b)) => Ok(Expr::from(a < b)),
        _ => Err(
            format!("comparison undefined for: {}, {}", args[0], args[1]).into(),
        ),
    }
}

pub fn less_eq(args: &[Expr], _env: &Env) -> Result<Expr> {
    check_args("#[<=]", args, 2)?;
    match (&args[0], &args[1]) {
        (&Expr::Atom(ref a), &Expr::Atom(ref b)) => Ok(Expr::from(a <= b)),
        _ => Err(
            format!("comparison undefined for: {}, {}", args[0], args[1]).into(),
        ),
    }
}

pub fn greater(args: &[Expr], _env: &Env) -> Result<Expr> {
    check_args("#[>]", args, 2)?;
    match (&args[0], &args[1]) {
        (&Expr::Atom(ref a), &Expr::Atom(ref b)) => Ok(Expr::from(a > b)),
        _ => Err(
            format!("comparison undefined for: {}, {}", args[0], args[1]).into(),
        ),
    }
}

pub fn greater_eq(args: &[Expr], _env: &Env) -> Result<Expr> {
    check_args("#[>=]", args, 2)?;
    match (&args[0], &args[1]) {
        (&Expr::Atom(ref a), &Expr::Atom(ref b)) => Ok(Expr::from(a >= b)),
        _ => Err(
            format!("comparison undefined for: {}, {}", args[0], args[1]).into(),
        ),
    }
}

pub fn not(args: &[Expr], _env: &Env) -> Result<Expr> {
    check_args("#[not]", args, 1)?;
    match &args[0] {
        &Expr::Atom(Atom::Bool(b)) => Ok(Expr::from(!b)),
        _ => Err(format!("negation undefined for: {}", args[0]).into()),
    }
}

pub fn and(args: &[Expr], _env: &Env) -> Result<Expr> {
    // Unwrap to atoms
    let atoms = unwrap_atoms(args.iter().cloned())?;

    atoms
        .into_iter()
        .map(|a| a.boolean())
        .collect::<Option<Vec<_>>>()
        .ok_or("#[and] expected boolean argument".into())
        .map(|bools| bools.iter().all(|b| *b))
        .map(Expr::from)
}

pub fn or(args: &[Expr], _env: &Env) -> Result<Expr> {
    // Unwrap to atoms
    let atoms = unwrap_atoms(args.iter().cloned())?;

    atoms
        .into_iter()
        .map(|a| a.boolean())
        .collect::<Option<Vec<_>>>()
        .ok_or("#[or] expected boolean argument".into())
        .map(|bools| bools.iter().any(|b| *b))
        .map(Expr::from)
}

pub fn print(args: &[Expr], _env: &Env) -> Result<Expr> {
    check_args("#[print]", args, 1)?;
    println!("{}", args[0]);
    Ok(Expr::Nil)
}

pub fn first(args: &[Expr], _env: &Env) -> Result<Expr> {
    check_args("#[first]", args, 1)?;

    match &args[0] {
        &Expr::List(ref l) => Ok(l.0.first().cloned().unwrap_or(Expr::Nil)),
        &Expr::Vector(ref q) => Ok(q.0.first().cloned().unwrap_or(Expr::Nil)),
        _ => Err("#[first] expected list".into()),
    }
}

pub fn rest(args: &[Expr], _env: &Env) -> Result<Expr> {
    match &args[0] {
        &Expr::List(ref l) => {
            Ok(l.0.split_first()
                .map(|(_, rest)| {
                    Expr::List(List(rest.to_vec()))
                }).unwrap_or(Expr::Nil))
        },
        &Expr::Vector(ref v) => {
            Ok(v.0.split_first()
                .map(|(_, rest)| {
                    Expr::Vector(Vector(rest.to_vec()))
                }).unwrap_or(Expr::Nil))
        },
        _ => Err("#[rest] expected list".into()),
    }
}

// (cons item seq)
pub fn cons(args: &[Expr], _env: &Env) -> Result<Expr> {
    check_args("#[cons]", args, 2)?;

    match &args[2] {
        &Expr::List(ref l) => {
            let mut new = l.clone();
            new.0.insert(0, args[1].clone());
            Ok(Expr::List(new))
        },
        &Expr::Vector(ref v) => {
            let mut new = v.clone();
            new.0.push(args[1].clone());
            Ok(Expr::Vector(new))
        },
        _ => Err("#[cons] expected list".into()),
    }
}

// (exit)
pub fn exit(_args: &[Expr], _env: &Env) -> Result<Expr> {
    Err(ErrorKind::Exit(0).into())
}<|endoftext|>


<commit_before>use core::fmt;
use lazy_static::lazy_static;
use spin::Mutex;
use volatile::Volatile;

const BUFFER_HEIGHT: usize = 25;
const BUFFER_WIDTH: usize = 80;

#[allow(dead_code)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
enum Color {
    Black = 0,
    Blue = 1,
    Green = 2,
    Cyan = 3,
    Red = 4,
    Magenta = 5,
    Brown = 6,
    LightGray = 7,
    DarkGray = 8,
    LightBlue = 9,
    LightGreen = 10,
    LightCyan = 11,
    LightRed = 12,
    Pink = 13,
    Yellow = 14,
    White = 15,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(transparent)]
struct ColorCode(u8);

impl ColorCode {
    fn new(foreground: Color, background: Color) -> ColorCode {
        ColorCode((background as u8) << 4 | (foreground as u8))
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(C)]
struct ScreenChar {
    ascii_character: u8,
    color_code: ColorCode,
}

#[repr(transparent)]
struct Buffer {
    chars: [[Volatile<ScreenChar>; BUFFER_WIDTH]; BUFFER_HEIGHT],
}

pub struct Writer {
    column_position: usize,
    color_code: ColorCode,
    buffer: &'static mut Buffer,
}

impl Writer {
    pub fn write_byte(&mut self, byte: u8) {
        match byte {
            b'\n' => self.new_line(),
            byte => {
                if self.column_position >= BUFFER_WIDTH {
                    self.new_line();
                }

                let row = BUFFER_HEIGHT - 1;
                let col = self.column_position;

                let color_code = self.color_code;
                self.buffer.chars[row][col].write(ScreenChar {
                    ascii_character: byte,
                    color_code,
                });
                self.column_position += 1;
            }
        }
    }

    fn new_line(&mut self) {
        for row in 1..BUFFER_HEIGHT {
            for col in 0..BUFFER_WIDTH {
                let character = self.buffer.chars[row][col].read();
                self.buffer.chars[row - 1][col].write(character);
            }
        }
        self.clear_row(BUFFER_HEIGHT - 1);
        self.column_position = 0;
    }

    fn clear_row(&mut self, row: usize) {
        let blank = ScreenChar {
            ascii_character: b' ',
            color_code: self.color_code,
        };
        for col in 0..BUFFER_WIDTH {
            self.buffer.chars[row][col].write(blank);
        }
    }

    fn write_string(&mut self, s: &str) {
        for byte in s.bytes() {
            match byte {
                // printable ASCII byte or newline
                0x20..=0x7e | b'\n' => self.write_byte(byte),
                // not part of printable ASCII range
                _ => self.write_byte(0xfe),
            }
        }
    }
}

impl fmt::Write for Writer {
    fn write_str(&mut self, s: &str) -> fmt::Result {
        self.write_string(s);
        Ok(())
    }
}

lazy_static! {
    pub static ref WRITER: Mutex<Writer> = Mutex::new(Writer {
        column_position: 0,
        color_code: ColorCode::new(Color::Yellow, Color::Black),
        buffer: unsafe { &mut *(0xb8000 as *mut Buffer) },
    });
}

#[macro_export]
macro_rules! print {
    ($($arg:tt)*) => ($crate::vga::_print(format_args!($($arg)*)));
}

#[macro_export]
macro_rules! println {
    () => ($crate::print!("\n"));
    ($($arg:tt)*) => ($crate::print!("{}\n", format_args!($($arg)*)));
}

#[doc(hidden)]
pub fn _print(args: fmt::Arguments) {
    use core::fmt::Write;
    WRITER.lock().write_fmt(args).unwrap();
}

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

    #[test_case]
    fn test_println_output() {
        serial_print!("test_println_output... ");

        let s = "Some test string that fits on a single line";
        println!("{}", s);
        for (i, c) in s.chars().enumerate() {
            let screen_char = WRITER.lock().buffer.chars[BUFFER_HEIGHT - 2][i].read();
            assert_eq!(char::from(screen_char.ascii_character), c);
        }

        serial_println!("[ok]");
    }
}
<commit_msg>vga: don't glob import crate<commit_after>use core::fmt;
use lazy_static::lazy_static;
use spin::Mutex;
use volatile::Volatile;

const BUFFER_HEIGHT: usize = 25;
const BUFFER_WIDTH: usize = 80;

#[allow(dead_code)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
enum Color {
    Black = 0,
    Blue = 1,
    Green = 2,
    Cyan = 3,
    Red = 4,
    Magenta = 5,
    Brown = 6,
    LightGray = 7,
    DarkGray = 8,
    LightBlue = 9,
    LightGreen = 10,
    LightCyan = 11,
    LightRed = 12,
    Pink = 13,
    Yellow = 14,
    White = 15,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(transparent)]
struct ColorCode(u8);

impl ColorCode {
    fn new(foreground: Color, background: Color) -> ColorCode {
        ColorCode((background as u8) << 4 | (foreground as u8))
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(C)]
struct ScreenChar {
    ascii_character: u8,
    color_code: ColorCode,
}

#[repr(transparent)]
struct Buffer {
    chars: [[Volatile<ScreenChar>; BUFFER_WIDTH]; BUFFER_HEIGHT],
}

pub struct Writer {
    column_position: usize,
    color_code: ColorCode,
    buffer: &'static mut Buffer,
}

impl Writer {
    pub fn write_byte(&mut self, byte: u8) {
        match byte {
            b'\n' => self.new_line(),
            byte => {
                if self.column_position >= BUFFER_WIDTH {
                    self.new_line();
                }

                let row = BUFFER_HEIGHT - 1;
                let col = self.column_position;

                let color_code = self.color_code;
                self.buffer.chars[row][col].write(ScreenChar {
                    ascii_character: byte,
                    color_code,
                });
                self.column_position += 1;
            }
        }
    }

    fn new_line(&mut self) {
        for row in 1..BUFFER_HEIGHT {
            for col in 0..BUFFER_WIDTH {
                let character = self.buffer.chars[row][col].read();
                self.buffer.chars[row - 1][col].write(character);
            }
        }
        self.clear_row(BUFFER_HEIGHT - 1);
        self.column_position = 0;
    }

    fn clear_row(&mut self, row: usize) {
        let blank = ScreenChar {
            ascii_character: b' ',
            color_code: self.color_code,
        };
        for col in 0..BUFFER_WIDTH {
            self.buffer.chars[row][col].write(blank);
        }
    }

    fn write_string(&mut self, s: &str) {
        for byte in s.bytes() {
            match byte {
                // printable ASCII byte or newline
                0x20..=0x7e | b'\n' => self.write_byte(byte),
                // not part of printable ASCII range
                _ => self.write_byte(0xfe),
            }
        }
    }
}

impl fmt::Write for Writer {
    fn write_str(&mut self, s: &str) -> fmt::Result {
        self.write_string(s);
        Ok(())
    }
}

lazy_static! {
    pub static ref WRITER: Mutex<Writer> = Mutex::new(Writer {
        column_position: 0,
        color_code: ColorCode::new(Color::Yellow, Color::Black),
        buffer: unsafe { &mut *(0xb8000 as *mut Buffer) },
    });
}

#[macro_export]
macro_rules! print {
    ($($arg:tt)*) => ($crate::vga::_print(format_args!($($arg)*)));
}

#[macro_export]
macro_rules! println {
    () => ($crate::print!("\n"));
    ($($arg:tt)*) => ($crate::print!("{}\n", format_args!($($arg)*)));
}

#[doc(hidden)]
pub fn _print(args: fmt::Arguments) {
    use core::fmt::Write;
    WRITER.lock().write_fmt(args).unwrap();
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::{serial_print, serial_println};

    #[test_case]
    fn test_println_output() {
        serial_print!("test_println_output... ");

        let s = "Some test string that fits on a single line";
        println!("{}", s);
        for (i, c) in s.chars().enumerate() {
            let screen_char = WRITER.lock().buffer.chars[BUFFER_HEIGHT - 2][i].read();
            assert_eq!(char::from(screen_char.ascii_character), c);
        }

        serial_println!("[ok]");
    }
}
<|endoftext|>


<commit_before>use super::*;

impl State {
    fn poll_call(&mut self) -> Result<Call> {
        if let Some(msg) = self.pending_calls.pop_front() {
            return Ok(msg);
        }

        loop {
            let msg = self.rx.recv()?;
            match msg {
                Message::MethodCall(lang_id, method_call) => {
                    return Ok(Call::MethodCall(lang_id, method_call));
                }
                Message::Notification(lang_id, notification) => {
                    return Ok(Call::Notification(lang_id, notification));
                }
                Message::Output(output) => {
                    let mid = output.id().to_int()?;
                    self.pending_outputs.insert(mid, output);
                }
            }
        }
    }

    fn poll_output(&mut self, id: Id) -> Result<rpc::Output> {
        if let Some(output) = self.pending_outputs.remove(&id) {
            return Ok(output);
        }

        loop {
            let msg = self.rx.recv_timeout(self.wait_output_timeout)?;
            match msg {
                Message::MethodCall(lang_id, method_call) => self.pending_calls
                    .push_back(Call::MethodCall(lang_id, method_call)),
                Message::Notification(lang_id, notification) => self.pending_calls
                    .push_back(Call::Notification(lang_id, notification)),
                Message::Output(output) => {
                    let mid = output.id().to_int()?;
                    if mid == id {
                        return Ok(output);
                    } else {
                        self.pending_outputs.insert(mid, output);
                    }
                }
            }
        }
    }

    pub fn loop_message(&mut self) -> Result<()> {
        loop {
            match self.poll_call()? {
                Call::MethodCall(lang_id, method_call) => {
                    let result = self.handle_method_call(lang_id.as_deref(), &method_call);
                    if let Err(ref err) = result {
                        if err.downcast_ref::<LCError>().is_none() {
                            error!(
                                "Error handling message: {}\n\nMessage: {}\n\nError: {:?}",
                                err,
                                serde_json::to_string(&method_call).unwrap_or_default(),
                                err
                            );
                        }
                    }
                    let _ = self.output(lang_id.as_deref(), method_call.id, result);
                }
                Call::Notification(lang_id, notification) => {
                    let result = self.handle_notification(lang_id.as_deref(), &notification);
                    if let Err(ref err) = result {
                        if err.downcast_ref::<LCError>().is_none() {
                            error!(
                                "Error handling message: {}\n\nMessage: {}\n\nError: {:?}",
                                err,
                                serde_json::to_string(&notification).unwrap_or_default(),
                                err
                            );
                        }
                    }
                }
            }

            if let Err(err) = self.check_fs_notify() {
                warn!("{:?}", err);
            }
        }
    }

    /// Send message to RPC server.
    fn write(&mut self, languageId: Option<&str>, message: &str) -> Result<()> {
        info!("=> {:?} {}", languageId, message);
        if let Some(languageId) = languageId {
            let writer = self.writers
                .get_mut(languageId)
                .ok_or(LCError::NoLanguageServer {
                    languageId: languageId.to_owned(),
                })?;
            write!(
                writer,
                "Content-Length: {}\r\n\r\n{}",
                message.len(),
                message
            )?;
            writer.flush()?;
        } else {
            println!("Content-Length: {}\n\n{}", message.len(), message);
        }

        Ok(())
    }

    /// RPC method call.
    pub fn call<P, V>(&mut self, languageId: Option<&str>, method: &str, params: P) -> Result<V>
    where
        P: Serialize,
        V: DeserializeOwned,
    {
        self.id += 1;
        let id = self.id;

        let method_call = rpc::MethodCall {
            jsonrpc: Some(rpc::Version::V2),
            id: rpc::Id::Num(id),
            method: method.into(),
            params: params.to_params()?,
        };

        let message = serde_json::to_string(&method_call)?;
        self.write(languageId, &message)?;

        match self.poll_output(id)? {
            rpc::Output::Success(success) => Ok(serde_json::from_value(success.result)?),
            rpc::Output::Failure(failure) => Err(format_err!("{}", failure.error.message)),
        }
    }

    /// RPC notification.
    pub fn notify<P>(&mut self, languageId: Option<&str>, method: &str, params: P) -> Result<()>
    where
        P: Serialize,
    {
        let notification = rpc::Notification {
            jsonrpc: Some(rpc::Version::V2),
            method: method.to_owned(),
            params: params.to_params()?,
        };

        let message = serde_json::to_string(&notification)?;
        self.write(languageId, &message)?;

        Ok(())
    }

    /// Write an RPC call output.
    fn output(
        &mut self,
        languageId: Option<&str>,
        id: rpc::Id,
        result: Result<Value>,
    ) -> Result<()> {
        let response = match result {
            Ok(ok) => rpc::Output::Success(rpc::Success {
                jsonrpc: Some(rpc::Version::V2),
                id,
                result: ok,
            }),
            Err(err) => rpc::Output::Failure(rpc::Failure {
                jsonrpc: Some(rpc::Version::V2),
                id,
                error: err.to_rpc_error(),
            }),
        };

        let message = serde_json::to_string(&response)?;
        self.write(languageId, &message)?;
        Ok(())
    }

    /////// Vim wrappers ///////

    #[allow(needless_pass_by_value)]
    pub fn eval<E, T>(&mut self, exp: E) -> Result<T>
    where
        E: VimExp,
        T: DeserializeOwned,
    {
        let result = self.call(None, "eval", exp.to_exp())?;
        Ok(serde_json::from_value(result)?)
    }

    pub fn command<S: AsRef<str>>(&mut self, cmd: S) -> Result<()> {
        self.call::<_, u8>(None, "execute", cmd.as_ref())?;
        Ok(())
    }

    ////// Vim builtin function wrappers ///////

    pub fn echo<S>(&mut self, message: S) -> Result<()>
    where
        S: AsRef<str> + Serialize,
    {
        if self.call::<_, u8>(None, "s:Echo", message)? != 0 {
            bail!("return value is failure");
        }
        Ok(())
    }

    pub fn echo_ellipsis<S: AsRef<str>>(&mut self, message: S) -> Result<()> {
        let message = message.as_ref().lines().collect::<Vec<_>>().join(" ");
        self.call::<_, u8>(None, "s:EchoEllipsis", message)?;
        Ok(())
    }

    pub fn echomsg<S>(&mut self, message: S) -> Result<()>
    where
        S: AsRef<str> + Serialize,
    {
        if self.call::<_, u8>(None, "s:Echomsg", message)? != 0 {
            bail!("return value is failure");
        }
        Ok(())
    }

    pub fn echoerr<S>(&mut self, message: S) -> Result<()>
    where
        S: AsRef<str> + Serialize,
    {
        if self.call::<_, u8>(None, "s:Echoerr", message)? != 0 {
            bail!("return value is failure");
        }
        Ok(())
    }

    pub fn echowarn<S>(&mut self, message: S) -> Result<()>
    where
        S: AsRef<str> + Serialize,
    {
        if self.call::<_, u8>(None, "s:Echowarn", message)? != 0 {
            bail!("return value is failure");
        }
        Ok(())
    }

    pub fn goto_location<P: AsRef<Path>>(
        &mut self,
        goto_cmd: &Option<String>,
        path: P,
        line: u64,
        character: u64,
    ) -> Result<()> {
        let path = path.as_ref().to_string_lossy();
        let mut cmd = "echo | ".to_string();
        let goto;
        if let Some(ref goto_cmd) = *goto_cmd {
            goto = goto_cmd.as_str();
        } else if self.eval::<_, i64>(format!("bufnr('{}')", path))? == -1 {
            goto = "edit";
        } else {
            cmd += "execute 'normal m`' | ";
            goto = "buffer";
        };
        cmd += &format!(
            "execute 'normal! m`' | execute '{} +:call\\ cursor({},{}) ' . fnameescape('{}')",
            goto,
            line + 1,
            character + 1,
            path
        );

        self.command(cmd)
    }

    pub fn setqflist(&mut self, list: &[QuickfixEntry]) -> Result<()> {
        if self.call::<_, u8>(None, "setqflist", json!([list, "r"]))? != 0 {
            bail!("Failed to set quickfix list!");
        }
        Ok(())
    }

    pub fn setloclist(&mut self, list: &[QuickfixEntry]) -> Result<()> {
        if self.call::<_, u8>(None, "setloclist", json!([0, list, "r"]))? != 0 {
            bail!("Failed to set location list!");
        }
        Ok(())
    }

    pub fn get<F, T>(&self, f: F) -> Result<T>
    where
        F: FnOnce(&State) -> Result<T>,
    {
        f(self)
    }

    pub fn update<F, T>(&mut self, f: F) -> Result<T>
    where
        F: FnOnce(&mut State) -> Result<T>,
    {
        use log::Level;

        let before = if log_enabled!(Level::Debug) {
            let s = serde_json::to_string(self)?;
            serde_json::from_str(&s)?
        } else {
            Value::default()
        };
        let result = f(self);
        let after = if log_enabled!(Level::Debug) {
            let s = serde_json::to_string(self)?;
            serde_json::from_str(&s)?
        } else {
            Value::default()
        };
        for (k, (v1, v2)) in diff_value(&before, &after, "state") {
            debug!("{}: {} ==> {}", k, v1, v2);
        }
        result
    }
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(untagged)]
enum RawMessage {
    Notification(rpc::Notification),
    MethodCall(rpc::MethodCall),
    Output(rpc::Output),
}

pub fn loop_reader<T: BufRead>(
    input: T,
    languageId: &Option<String>,
    tx: &Sender<Message>,
) -> Result<()> {
    // Count how many consequent empty lines.
    let mut count_empty_lines = 0;

    let mut input = input;
    let mut content_length = 0;
    loop {
        let mut message = String::new();
        let mut line = String::new();
        if languageId.is_some() {
            input.read_line(&mut line)?;
            let line = line.trim();
            if line.is_empty() {
                count_empty_lines += 1;
                if count_empty_lines > 5 {
                    let mut message = "".to_string();
                    if let Ok(log_server) = get_log_server() {
                        message += "\n\nlanguage server stderr:\n";
                        message += &log_server;
                    }

                    bail!("{}", message);
                }

                let mut buf = vec![0; content_length];
                input.read_exact(buf.as_mut_slice())?;
                message = String::from_utf8(buf)?;
            } else {
                count_empty_lines = 0;
                if !line.starts_with("Content-Length") {
                    continue;
                }

                let tokens: Vec<&str> = line.splitn(2, ':').collect();
                let len = tokens
                    .get(1)
                    .ok_or_else(|| format_err!("Failed to get length! tokens: {:?}", tokens))?
                    .trim();
                content_length = usize::from_str(len)?;
            }
        } else if input.read_line(&mut message)? == 0 {
            break;
        }

        let message = message.trim();
        if message.is_empty() {
            continue;
        }
        info!("<= {:?} {}", languageId, message);
        // let message = message.replace(r#","meta":{}"#, "");
        let message: RawMessage = serde_json::from_str(message)?;
        let message = match message {
            RawMessage::MethodCall(method_call) => {
                Message::MethodCall(languageId.clone(), method_call)
            }
            RawMessage::Notification(notification) => {
                Message::Notification(languageId.clone(), notification)
            }
            RawMessage::Output(output) => Message::Output(output),
        };
        tx.send(message)?;
    }

    Ok(())
}
<commit_msg>Workaround javascript-typescript-langserver outputs extra property `meta`.<commit_after>use super::*;

impl State {
    fn poll_call(&mut self) -> Result<Call> {
        if let Some(msg) = self.pending_calls.pop_front() {
            return Ok(msg);
        }

        loop {
            let msg = self.rx.recv()?;
            match msg {
                Message::MethodCall(lang_id, method_call) => {
                    return Ok(Call::MethodCall(lang_id, method_call));
                }
                Message::Notification(lang_id, notification) => {
                    return Ok(Call::Notification(lang_id, notification));
                }
                Message::Output(output) => {
                    let mid = output.id().to_int()?;
                    self.pending_outputs.insert(mid, output);
                }
            }
        }
    }

    fn poll_output(&mut self, id: Id) -> Result<rpc::Output> {
        if let Some(output) = self.pending_outputs.remove(&id) {
            return Ok(output);
        }

        loop {
            let msg = self.rx.recv_timeout(self.wait_output_timeout)?;
            match msg {
                Message::MethodCall(lang_id, method_call) => self.pending_calls
                    .push_back(Call::MethodCall(lang_id, method_call)),
                Message::Notification(lang_id, notification) => self.pending_calls
                    .push_back(Call::Notification(lang_id, notification)),
                Message::Output(output) => {
                    let mid = output.id().to_int()?;
                    if mid == id {
                        return Ok(output);
                    } else {
                        self.pending_outputs.insert(mid, output);
                    }
                }
            }
        }
    }

    pub fn loop_message(&mut self) -> Result<()> {
        loop {
            match self.poll_call()? {
                Call::MethodCall(lang_id, method_call) => {
                    let result = self.handle_method_call(lang_id.as_deref(), &method_call);
                    if let Err(ref err) = result {
                        if err.downcast_ref::<LCError>().is_none() {
                            error!(
                                "Error handling message: {}\n\nMessage: {}\n\nError: {:?}",
                                err,
                                serde_json::to_string(&method_call).unwrap_or_default(),
                                err
                            );
                        }
                    }
                    let _ = self.output(lang_id.as_deref(), method_call.id, result);
                }
                Call::Notification(lang_id, notification) => {
                    let result = self.handle_notification(lang_id.as_deref(), &notification);
                    if let Err(ref err) = result {
                        if err.downcast_ref::<LCError>().is_none() {
                            error!(
                                "Error handling message: {}\n\nMessage: {}\n\nError: {:?}",
                                err,
                                serde_json::to_string(&notification).unwrap_or_default(),
                                err
                            );
                        }
                    }
                }
            }

            if let Err(err) = self.check_fs_notify() {
                warn!("{:?}", err);
            }
        }
    }

    /// Send message to RPC server.
    fn write(&mut self, languageId: Option<&str>, message: &str) -> Result<()> {
        info!("=> {:?} {}", languageId, message);
        if let Some(languageId) = languageId {
            let writer = self.writers
                .get_mut(languageId)
                .ok_or(LCError::NoLanguageServer {
                    languageId: languageId.to_owned(),
                })?;
            write!(
                writer,
                "Content-Length: {}\r\n\r\n{}",
                message.len(),
                message
            )?;
            writer.flush()?;
        } else {
            println!("Content-Length: {}\n\n{}", message.len(), message);
        }

        Ok(())
    }

    /// RPC method call.
    pub fn call<P, V>(&mut self, languageId: Option<&str>, method: &str, params: P) -> Result<V>
    where
        P: Serialize,
        V: DeserializeOwned,
    {
        self.id += 1;
        let id = self.id;

        let method_call = rpc::MethodCall {
            jsonrpc: Some(rpc::Version::V2),
            id: rpc::Id::Num(id),
            method: method.into(),
            params: params.to_params()?,
        };

        let message = serde_json::to_string(&method_call)?;
        self.write(languageId, &message)?;

        match self.poll_output(id)? {
            rpc::Output::Success(success) => Ok(serde_json::from_value(success.result)?),
            rpc::Output::Failure(failure) => Err(format_err!("{}", failure.error.message)),
        }
    }

    /// RPC notification.
    pub fn notify<P>(&mut self, languageId: Option<&str>, method: &str, params: P) -> Result<()>
    where
        P: Serialize,
    {
        let notification = rpc::Notification {
            jsonrpc: Some(rpc::Version::V2),
            method: method.to_owned(),
            params: params.to_params()?,
        };

        let message = serde_json::to_string(&notification)?;
        self.write(languageId, &message)?;

        Ok(())
    }

    /// Write an RPC call output.
    fn output(
        &mut self,
        languageId: Option<&str>,
        id: rpc::Id,
        result: Result<Value>,
    ) -> Result<()> {
        let response = match result {
            Ok(ok) => rpc::Output::Success(rpc::Success {
                jsonrpc: Some(rpc::Version::V2),
                id,
                result: ok,
            }),
            Err(err) => rpc::Output::Failure(rpc::Failure {
                jsonrpc: Some(rpc::Version::V2),
                id,
                error: err.to_rpc_error(),
            }),
        };

        let message = serde_json::to_string(&response)?;
        self.write(languageId, &message)?;
        Ok(())
    }

    /////// Vim wrappers ///////

    #[allow(needless_pass_by_value)]
    pub fn eval<E, T>(&mut self, exp: E) -> Result<T>
    where
        E: VimExp,
        T: DeserializeOwned,
    {
        let result = self.call(None, "eval", exp.to_exp())?;
        Ok(serde_json::from_value(result)?)
    }

    pub fn command<S: AsRef<str>>(&mut self, cmd: S) -> Result<()> {
        self.call::<_, u8>(None, "execute", cmd.as_ref())?;
        Ok(())
    }

    ////// Vim builtin function wrappers ///////

    pub fn echo<S>(&mut self, message: S) -> Result<()>
    where
        S: AsRef<str> + Serialize,
    {
        if self.call::<_, u8>(None, "s:Echo", message)? != 0 {
            bail!("return value is failure");
        }
        Ok(())
    }

    pub fn echo_ellipsis<S: AsRef<str>>(&mut self, message: S) -> Result<()> {
        let message = message.as_ref().lines().collect::<Vec<_>>().join(" ");
        self.call::<_, u8>(None, "s:EchoEllipsis", message)?;
        Ok(())
    }

    pub fn echomsg<S>(&mut self, message: S) -> Result<()>
    where
        S: AsRef<str> + Serialize,
    {
        if self.call::<_, u8>(None, "s:Echomsg", message)? != 0 {
            bail!("return value is failure");
        }
        Ok(())
    }

    pub fn echoerr<S>(&mut self, message: S) -> Result<()>
    where
        S: AsRef<str> + Serialize,
    {
        if self.call::<_, u8>(None, "s:Echoerr", message)? != 0 {
            bail!("return value is failure");
        }
        Ok(())
    }

    pub fn echowarn<S>(&mut self, message: S) -> Result<()>
    where
        S: AsRef<str> + Serialize,
    {
        if self.call::<_, u8>(None, "s:Echowarn", message)? != 0 {
            bail!("return value is failure");
        }
        Ok(())
    }

    pub fn goto_location<P: AsRef<Path>>(
        &mut self,
        goto_cmd: &Option<String>,
        path: P,
        line: u64,
        character: u64,
    ) -> Result<()> {
        let path = path.as_ref().to_string_lossy();
        let mut cmd = "echo | ".to_string();
        let goto;
        if let Some(ref goto_cmd) = *goto_cmd {
            goto = goto_cmd.as_str();
        } else if self.eval::<_, i64>(format!("bufnr('{}')", path))? == -1 {
            goto = "edit";
        } else {
            cmd += "execute 'normal m`' | ";
            goto = "buffer";
        };
        cmd += &format!(
            "execute 'normal! m`' | execute '{} +:call\\ cursor({},{}) ' . fnameescape('{}')",
            goto,
            line + 1,
            character + 1,
            path
        );

        self.command(cmd)
    }

    pub fn setqflist(&mut self, list: &[QuickfixEntry]) -> Result<()> {
        if self.call::<_, u8>(None, "setqflist", json!([list, "r"]))? != 0 {
            bail!("Failed to set quickfix list!");
        }
        Ok(())
    }

    pub fn setloclist(&mut self, list: &[QuickfixEntry]) -> Result<()> {
        if self.call::<_, u8>(None, "setloclist", json!([0, list, "r"]))? != 0 {
            bail!("Failed to set location list!");
        }
        Ok(())
    }

    pub fn get<F, T>(&self, f: F) -> Result<T>
    where
        F: FnOnce(&State) -> Result<T>,
    {
        f(self)
    }

    pub fn update<F, T>(&mut self, f: F) -> Result<T>
    where
        F: FnOnce(&mut State) -> Result<T>,
    {
        use log::Level;

        let before = if log_enabled!(Level::Debug) {
            let s = serde_json::to_string(self)?;
            serde_json::from_str(&s)?
        } else {
            Value::default()
        };
        let result = f(self);
        let after = if log_enabled!(Level::Debug) {
            let s = serde_json::to_string(self)?;
            serde_json::from_str(&s)?
        } else {
            Value::default()
        };
        for (k, (v1, v2)) in diff_value(&before, &after, "state") {
            debug!("{}: {} ==> {}", k, v1, v2);
        }
        result
    }
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(untagged)]
enum RawMessage {
    Notification(rpc::Notification),
    MethodCall(rpc::MethodCall),
    Output(rpc::Output),
}

pub fn loop_reader<T: BufRead>(
    input: T,
    languageId: &Option<String>,
    tx: &Sender<Message>,
) -> Result<()> {
    // Count how many consequent empty lines.
    let mut count_empty_lines = 0;

    let mut input = input;
    let mut content_length = 0;
    loop {
        let mut message = String::new();
        let mut line = String::new();
        if languageId.is_some() {
            input.read_line(&mut line)?;
            let line = line.trim();
            if line.is_empty() {
                count_empty_lines += 1;
                if count_empty_lines > 5 {
                    let mut message = "".to_string();
                    if let Ok(log_server) = get_log_server() {
                        message += "\n\nlanguage server stderr:\n";
                        message += &log_server;
                    }

                    bail!("{}", message);
                }

                let mut buf = vec![0; content_length];
                input.read_exact(buf.as_mut_slice())?;
                message = String::from_utf8(buf)?;
            } else {
                count_empty_lines = 0;
                if !line.starts_with("Content-Length") {
                    continue;
                }

                let tokens: Vec<&str> = line.splitn(2, ':').collect();
                let len = tokens
                    .get(1)
                    .ok_or_else(|| format_err!("Failed to get length! tokens: {:?}", tokens))?
                    .trim();
                content_length = usize::from_str(len)?;
            }
        } else if input.read_line(&mut message)? == 0 {
            break;
        }

        let message = message.trim();
        if message.is_empty() {
            continue;
        }
        info!("<= {:?} {}", languageId, message);
        // FIXME: Remove extra `meta` property from javascript-typescript-langserver.
        let s = message.replace(r#","meta":{}"#, "");
        let message = serde_json::from_str(&s);
        if let Err(ref err) = message {
            error!(
                "Failed to deserialize output: {}\n\n Message: {}\n\nError: {:?}",
                err, s, err
            );
            continue;
        }
        // TODO: cleanup.
        let message = message.unwrap();
        let message = match message {
            RawMessage::MethodCall(method_call) => {
                Message::MethodCall(languageId.clone(), method_call)
            }
            RawMessage::Notification(notification) => {
                Message::Notification(languageId.clone(), notification)
            }
            RawMessage::Output(output) => Message::Output(output),
        };
        tx.send(message)?;
    }

    Ok(())
}
<|endoftext|>


<commit_before>use std::process::Command;
use std::fs;

fn test_output(result: &str, expected: &str) {
    assert!(Command::new("cmp")
            .arg(result)
            .arg(expected)
            .spawn().unwrap().wait().unwrap().success());
    fs::remove_file(result).unwrap();
}

fn run_exp(dataset: &str, codebook: &str, params: &str) -> bool {
    Command::new("bash")
            .arg("-c")
            .arg(format!(
                "target/release/merfishtools -v exp -t 1 {codebook} --estimate {est} {params} < {raw} > {cdf}",
                codebook=format!("tests/codebook/{}.txt", codebook),
                raw=format!("tests/data/{}.txt", dataset),
                cdf=format!("tests/results/{}.txt", dataset),
                est=format!("tests/results/{}.est.txt", dataset),
                params=params
            ))
            .spawn().unwrap().wait().unwrap().success()
}

#[test]
fn test_exp_mhd4_cell0() {
    assert!(run_exp("140genesData.1.cell0", "140genesData.1", "--p0 0.016 0.015 0.021 0.015 0.051 0.065 0.062 0.026 0.019 0.021 0.023 0.034 0.02 0.029 0.07 0.027 --p1 0.05 0.04 0.065 0.052 0.177 0.135 0.155 0.07 0.053 0.057 0.066 0.09 0.069 0.07 0.145 0.103"));
}

#[test]
fn test_exp_mhd4_thbs_bias() {
    assert!(run_exp("140genesData.1.cell24", "140genesData.1", "--p0 0.016 0.015 0.021 0.015 0.051 0.065 0.062 0.026 0.019 0.021 0.023 0.034 0.02 0.029 0.07 0.027 --p1 0.05 0.04 0.065 0.052 0.177 0.135 0.155 0.07 0.053 0.057 0.066 0.09 0.069 0.07 0.145 0.103"));
}

#[test]
fn test_exp_mhd2() {
    assert!(run_exp("1001genesData.3.cell0", "1001genesData", ""));
}
<commit_msg>Use debug mode for tests.<commit_after>use std::process::Command;
use std::fs;

fn test_output(result: &str, expected: &str) {
    assert!(Command::new("cmp")
            .arg(result)
            .arg(expected)
            .spawn().unwrap().wait().unwrap().success());
    fs::remove_file(result).unwrap();
}

fn run_exp(dataset: &str, codebook: &str, params: &str) -> bool {
    Command::new("bash")
            .arg("-c")
            .arg(format!(
                "target/debug/merfishtools -v exp -t 1 {codebook} --estimate {est} {params} < {raw} > {cdf}",
                codebook=format!("tests/codebook/{}.txt", codebook),
                raw=format!("tests/data/{}.txt", dataset),
                cdf=format!("tests/results/{}.txt", dataset),
                est=format!("tests/results/{}.est.txt", dataset),
                params=params
            ))
            .spawn().unwrap().wait().unwrap().success()
}

#[test]
fn test_exp_mhd4_cell0() {
    assert!(run_exp("140genesData.1.cell0", "140genesData.1", "--p0 0.016 0.015 0.021 0.015 0.051 0.065 0.062 0.026 0.019 0.021 0.023 0.034 0.02 0.029 0.07 0.027 --p1 0.05 0.04 0.065 0.052 0.177 0.135 0.155 0.07 0.053 0.057 0.066 0.09 0.069 0.07 0.145 0.103"));
}

#[test]
fn test_exp_mhd4_thbs_bias() {
    assert!(run_exp("140genesData.1.cell24", "140genesData.1", "--p0 0.016 0.015 0.021 0.015 0.051 0.065 0.062 0.026 0.019 0.021 0.023 0.034 0.02 0.029 0.07 0.027 --p1 0.05 0.04 0.065 0.052 0.177 0.135 0.155 0.07 0.053 0.057 0.066 0.09 0.069 0.07 0.145 0.103"));
}

#[test]
fn test_exp_mhd2() {
    assert!(run_exp("1001genesData.3.cell0", "1001genesData", ""));
}
<|endoftext|>


<commit_before>extern crate firebase;
extern crate url;

use firebase::Firebase;
use firebase::FbOps;
use url::Url;

use std::sync::{Arc, Mutex};

#[test]
fn builds_auth_url() {
    let f = Firebase::authed("http://db.rifebass.com/", "deadbeaf").ok().unwrap();
    assert_eq!(f.get_url(), "http://db.rifebass.com/?auth=deadbeaf");
}

#[test]
fn extends_auth_url() {
    let f = Firebase::authed("http://db.rifebass.com/", "deadbeaf").ok().unwrap();
    let f = f.at("/futurama/SpacePilot3000").ok().unwrap();
    let url_now = "http://db.rifebass.com//futurama/SpacePilot3000.json?auth=deadbeaf";
    assert_eq!(url_now, f.get_url());
}

#[test]
fn double_extends_url() {
    let f = Firebase::authed("http://db.rifebass.com", "deadbeaf").ok().unwrap();
    let f = f.at("/futurama.json").ok().unwrap();
    let f = f.at("SpacePilot3000").ok().unwrap();
    let url_now = "http://db.rifebass.com//futurama/SpacePilot3000.json?auth=deadbeaf";
    assert_eq!(url_now, f.get_url());
}

#[test]
fn handle_slashes() {
    let f = Firebase::authed("http://db.rifebass.com", "deadbeaf").ok().unwrap();
    let f = f.at("futurama.json").ok().unwrap();
    let f = f.at("SpacePilot3000.json").ok().unwrap();
    let url_now = "http://db.rifebass.com//futurama/SpacePilot3000.json?auth=deadbeaf";
    assert_eq!(url_now, f.get_url());

    let f = Firebase::authed("http://db.rifebass.com/", "deadbeaf").ok().unwrap();
    let f = f.at("/futurama/").ok().unwrap();
    let f = f.at("/SpacePilot3000/").ok().unwrap();
    let url_now = "http://db.rifebass.com//futurama/SpacePilot3000.json?auth=deadbeaf";
    assert_eq!(url_now, f.get_url());
}

#[test]
fn handle_json_suffix() {
    let f = Firebase::new("http://db.rifebass.com").ok().unwrap();
    let f = f.at("0.json").ok().unwrap().at("1.json").ok().unwrap()
             .at("1.json").ok().unwrap().at("8.json").ok().unwrap()
             .at("9.json").ok().unwrap().at("9.json").ok().unwrap()
             .at("9.json").ok().unwrap().at("8.json").ok().unwrap()
             .at("8.json").ok().unwrap().at("1.json").ok().unwrap()
             .at("9.json").ok().unwrap().at("9.json").ok().unwrap()
             .at("9.json").ok().unwrap().at("1.json").ok().unwrap()
             .at("1.json").ok().unwrap().at("9.json").ok().unwrap()
             .at("7.json").ok().unwrap().at("2.json").ok().unwrap()
             .at("5.json").ok().unwrap().at("3.json").ok().unwrap();
    let url_now = "http://db.rifebass.com//0/1/1/8/9/9/9/8/8/1/9/9/9/1/1/9/7/2/5/3.json";
    assert_eq!(url_now, f.get_url());
}

#[test]
fn test_ops() {
    let f = Firebase::new("http://db.fe/").ok().expect("url err");
    let f = f.at("lol").ok().expect("extend err");
    let req = f.end_at(13).limit_to_first(4).equal_to(8).shallow(false);
    let correct = Url::parse("http://db.fe//lol.json?limitToFirst=4&endAt=13&equalTo=8&shallow=false").ok().unwrap();
    let generated = Url::parse(&req.get_url()).ok().unwrap();

    assert_queries(&correct, &generated);
}

#[test]
fn test_auth_ops() {
    let f = Firebase::authed("db.fe/", "key").ok().expect("url err").at("lol").ok().unwrap();
    let req = f.order_by("pts").limit_to_last(5).start_at(8);

    let correct = Url::parse("https://db.fe/lol.json?auth=key&orderBy=pts&limitToLast=5&startAt=8").ok().unwrap();
    let generated = Url::parse(&req.get_url()).ok().unwrap();

    assert_queries(&correct, &generated);
}

#[test]
fn test_async_get() {
    let fb = Firebase::new("mybd.firebase.com").ok().unwrap();
    let db_ref = fb.at("Profiles/a9sdc8asd99acc/profile_img").ok().unwrap();

    let finished = Arc::new(Mutex::new(false));

    let marker = finished.clone();
    let thread = db_ref.get_async(move |_| {
        let mut finished = marker.lock().unwrap();
        *finished = true;
    });

    assert!(!*finished.lock().unwrap());
    thread.join().ok();
}

#[test]
fn test_ops_ctor() {
    let fb = Firebase::new("db.fb.com").ok().unwrap();
    let query = fb.ops(&FbOps {
        order_by:       Some("Hello World"),
        limit_to_first: Some(5),
        end_at:         Some(7),
        equal_to:       Some(3),
        shallow:        Some(true),
        format:         Some(true),
        .. FbOps::default()
    });

    let corr = Url::parse("https://db.fb.com/?limitToFirst=5&orderBy=Hello+World&equalTo=3&format=export&shallow=true&endAt=7").ok().unwrap();
    let this = Url::parse(&query.get_url()).ok().unwrap();
    assert_queries(&corr, &this);
}

fn assert_queries(a: &Url, b: &Url) {
    let param_a = a.query_pairs().expect("Url should have query params.");
    let param_b = b.query_pairs().expect("Url should have query params.");

    assert_eq!(param_b.len(), param_a.len());

    for pair in param_a.iter() {
        assert!(param_b.contains(pair));
    }
}
<commit_msg>Added test for parsing into serialize JSON object<commit_after>extern crate firebase;
extern crate url;

use firebase::*;
use url::Url;

use std::sync::{Arc, Mutex};

#[test]
fn builds_auth_url() {
    let f = Firebase::authed("http://db.rifebass.com/", "deadbeaf").ok().unwrap();
    assert_eq!(f.get_url(), "http://db.rifebass.com/?auth=deadbeaf");
}

#[test]
fn extends_auth_url() {
    let f = Firebase::authed("http://db.rifebass.com/", "deadbeaf").ok().unwrap();
    let f = f.at("/futurama/SpacePilot3000").ok().unwrap();
    let url_now = "http://db.rifebass.com//futurama/SpacePilot3000.json?auth=deadbeaf";
    assert_eq!(url_now, f.get_url());
}

#[test]
fn double_extends_url() {
    let f = Firebase::authed("http://db.rifebass.com", "deadbeaf").ok().unwrap();
    let f = f.at("/futurama.json").ok().unwrap();
    let f = f.at("SpacePilot3000").ok().unwrap();
    let url_now = "http://db.rifebass.com//futurama/SpacePilot3000.json?auth=deadbeaf";
    assert_eq!(url_now, f.get_url());
}

#[test]
fn handle_slashes() {
    let f = Firebase::authed("http://db.rifebass.com", "deadbeaf").ok().unwrap();
    let f = f.at("futurama.json").ok().unwrap();
    let f = f.at("SpacePilot3000.json").ok().unwrap();
    let url_now = "http://db.rifebass.com//futurama/SpacePilot3000.json?auth=deadbeaf";
    assert_eq!(url_now, f.get_url());

    let f = Firebase::authed("http://db.rifebass.com/", "deadbeaf").ok().unwrap();
    let f = f.at("/futurama/").ok().unwrap();
    let f = f.at("/SpacePilot3000/").ok().unwrap();
    let url_now = "http://db.rifebass.com//futurama/SpacePilot3000.json?auth=deadbeaf";
    assert_eq!(url_now, f.get_url());
}

#[test]
fn handle_json_suffix() {
    let f = Firebase::new("http://db.rifebass.com").ok().unwrap();
    let f = f.at("0.json").ok().unwrap().at("1.json").ok().unwrap()
             .at("1.json").ok().unwrap().at("8.json").ok().unwrap()
             .at("9.json").ok().unwrap().at("9.json").ok().unwrap()
             .at("9.json").ok().unwrap().at("8.json").ok().unwrap()
             .at("8.json").ok().unwrap().at("1.json").ok().unwrap()
             .at("9.json").ok().unwrap().at("9.json").ok().unwrap()
             .at("9.json").ok().unwrap().at("1.json").ok().unwrap()
             .at("1.json").ok().unwrap().at("9.json").ok().unwrap()
             .at("7.json").ok().unwrap().at("2.json").ok().unwrap()
             .at("5.json").ok().unwrap().at("3.json").ok().unwrap();
    let url_now = "http://db.rifebass.com//0/1/1/8/9/9/9/8/8/1/9/9/9/1/1/9/7/2/5/3.json";
    assert_eq!(url_now, f.get_url());
}

#[test]
fn test_ops() {
    let f = Firebase::new("http://db.fe/").ok().expect("url err");
    let f = f.at("lol").ok().expect("extend err");
    let req = f.end_at(13).limit_to_first(4).equal_to(8).shallow(false);
    let correct = Url::parse("http://db.fe//lol.json?limitToFirst=4&endAt=13&equalTo=8&shallow=false").ok().unwrap();
    let generated = Url::parse(&req.get_url()).ok().unwrap();

    assert_queries(&correct, &generated);
}

#[test]
fn test_auth_ops() {
    let f = Firebase::authed("db.fe/", "key").ok().expect("url err").at("lol").ok().unwrap();
    let req = f.order_by("pts").limit_to_last(5).start_at(8);

    let correct = Url::parse("https://db.fe/lol.json?auth=key&orderBy=pts&limitToLast=5&startAt=8").ok().unwrap();
    let generated = Url::parse(&req.get_url()).ok().unwrap();

    assert_queries(&correct, &generated);
}

#[test]
fn test_async_get() {
    let fb = Firebase::new("mybd.firebase.com").ok().unwrap();
    let db_ref = fb.at("Profiles/a9sdc8asd99acc/profile_img").ok().unwrap();

    let finished = Arc::new(Mutex::new(false));

    let marker = finished.clone();
    let thread = db_ref.get_async(move |_| {
        let mut finished = marker.lock().unwrap();
        *finished = true;
    });

    assert!(!*finished.lock().unwrap());
    thread.join().ok();
}

#[test]
fn test_ops_ctor() {
    let fb = Firebase::new("db.fb.com").ok().unwrap();
    let query = fb.ops(&FbOps {
        order_by:       Some("Hello World"),
        limit_to_first: Some(5),
        end_at:         Some(7),
        equal_to:       Some(3),
        shallow:        Some(true),
        format:         Some(true),
        .. FbOps::default()
    });

    let corr = Url::parse("https://db.fb.com/?limitToFirst=5&orderBy=Hello+World&equalTo=3&format=export&shallow=true&endAt=7").ok().unwrap();
    let this = Url::parse(&query.get_url()).ok().unwrap();
    assert_queries(&corr, &this);
}

#[test]
fn test_resp_json() {
    let response = Response {
        code: 200,
        body: "{
            \"id\":   \"mongo id\",
            \"data\": \"Hello World!\"
        }".to_string(),
    };

    let record = match response.json().ok().expect("Should've parsed json") {
        Json::Object(o) => o,
        _ => panic!("This shouldv'e been a object!"),
    };

    let data = record.get("data").expect("Should've had a data member");

    match data.clone() {
        Json::String(d) => assert_eq!("Hello World!", d),
        _ => panic!("This shouldv'e been a string!"),
    }
}

fn assert_queries(a: &Url, b: &Url) {
    let param_a = a.query_pairs().expect("Url should have query params.");
    let param_b = b.query_pairs().expect("Url should have query params.");

    assert_eq!(param_b.len(), param_a.len());

    for pair in param_a.iter() {
        assert!(param_b.contains(pair));
    }
}
<|endoftext|>