<commit_before>//! Tests that make sure accessing thread-locals while exiting the thread doesn't cause panics.

#[macro_use]
extern crate crossbeam_channel;
extern crate crossbeam_utils;

use std::thread;
use std::time::Duration;

use crossbeam_channel::unbounded;
use crossbeam_utils::thread::scope;

fn ms(ms: u64) -> Duration {
    Duration::from_millis(ms)
}

#[test]
fn use_while_exiting() {
    struct Foo;

    impl Drop for Foo {
        fn drop(&mut self) {
            // A blocking operation after the thread-locals have been dropped. This will attempt to
            // use the thread-locals and must not panic.
            let (_s, r) = unbounded::<()>();
            select! {
                recv(r) -> _ => {}
                default(ms(100)) => {}
            }
        }
    }

    thread_local! {
        static FOO: Foo = Foo;
    }

    let (s, r) = unbounded::<()>();

    scope(|scope| {
        scope.spawn(|_| {
            // First initialize `FOO`, then the thread-locals related to crossbeam-channel.
            FOO.with(|_| ());
            r.recv().unwrap();
            // At thread exit, thread-locals related to crossbeam-channel get dropped first and
            // `FOO` is dropped last.
        });

        scope.spawn(|_| {
            thread::sleep(ms(100));
            s.send(()).unwrap();
        });
    }).unwrap();
}
<commit_msg>Skip use_while_exiting test on macOS<commit_after>//! Tests that make sure accessing thread-locals while exiting the thread doesn't cause panics.

#[macro_use]
extern crate crossbeam_channel;
extern crate crossbeam_utils;

use std::thread;
use std::time::Duration;

use crossbeam_channel::unbounded;
use crossbeam_utils::thread::scope;

fn ms(ms: u64) -> Duration {
    Duration::from_millis(ms)
}

#[test]
#[cfg_attr(target_os = "macos", ignore = "TLS is destroyed too early on macOS")]
fn use_while_exiting() {
    struct Foo;

    impl Drop for Foo {
        fn drop(&mut self) {
            // A blocking operation after the thread-locals have been dropped. This will attempt to
            // use the thread-locals and must not panic.
            let (_s, r) = unbounded::<()>();
            select! {
                recv(r) -> _ => {}
                default(ms(100)) => {}
            }
        }
    }

    thread_local! {
        static FOO: Foo = Foo;
    }

    let (s, r) = unbounded::<()>();

    scope(|scope| {
        scope.spawn(|_| {
            // First initialize `FOO`, then the thread-locals related to crossbeam-channel.
            FOO.with(|_| ());
            r.recv().unwrap();
            // At thread exit, thread-locals related to crossbeam-channel get dropped first and
            // `FOO` is dropped last.
        });

        scope.spawn(|_| {
            thread::sleep(ms(100));
            s.send(()).unwrap();
        });
    }).unwrap();
}
<|endoftext|>


<commit_before>
/**
 *  wc (word count) implementation in Rust
 *
 *  The purpose is to learn Rust a bit
 *
 */

use std::io::{BufferedReader, Reader, File, Lines, Buffer, IoError};
use std::os::args;

#[deriving(Show)]
pub struct WordCount {
    pub lines: uint,
    pub words: uint,
    pub chars: uint,
    pub bytes: uint
}

#[deriving(Show)]
struct Cfg {
    pub byte_count: bool,
    pub char_count: bool,
    pub line_count: bool,
    pub word_count: bool,
    pub path: String
}

fn wc<'r, T: Buffer>(lines : &'r mut Lines<'r, T>) -> Result<WordCount, IoError> {
    let mut ret : WordCount = WordCount { lines: 0u, words: 0u, chars: 0u, bytes: 0u };

    for res in lines {
        let line = try!(res);
        let slice: &str = line.as_slice();

        ret.lines += 1;
        ret.words += slice.split(' ').count();
        ret.chars += slice.chars().count();
        ret.bytes += slice.bytes().count();

    }

    return Ok(ret);
}

fn parse_args(args: Vec<String>) -> Cfg {

    let path = String::new();
    let mut cfg = Cfg{byte_count: false, char_count: false, line_count: false, word_count: false, path: path};
    let mut idx = 1u;

    while idx < args.len() {
        match args[idx].as_slice() {
            "--lines" => cfg.line_count = true,
            "-l"      => cfg.line_count = true,
            "--bytes" => cfg.byte_count = true,
            "-c"      => cfg.byte_count = true,
            "--chars" => cfg.char_count = true,
            "-m"      => cfg.char_count = true,
            "--words" => cfg.word_count = true,
            "-w"      => cfg.word_count = true,
            _ => cfg.path = args[idx].clone()
        }
        idx += 1;
    }

    if !cfg.char_count && ! cfg.line_count && !cfg.word_count {
        cfg.char_count = true;
        cfg.line_count = true;
        cfg.word_count = true;
    }

    cfg
}

//newline, word, character, byte, maximum line length
fn print_results(wc : &WordCount, cfg: &Cfg) {

    let mut buf = String::new();

    if cfg.line_count {
        buf = format!("{:u}", wc.lines);
    }

    //TODO: add creates new object, investigate things like extend
    if cfg.word_count {
        buf = buf.add(&format!(" {:u}", wc.words));
    }
    if cfg.char_count {
        buf = buf.add(&format!(" {:u}", wc.chars));
    }
    if cfg.byte_count {
        buf = buf.add(&format!(" {:u}", wc.bytes));
    }

    let mut sbuf = buf.as_slice();

    if sbuf.char_at(0) == ' ' {
        sbuf = sbuf.slice_from(1);
    }

    println!("{:s} {:s}", sbuf, cfg.path);
}

fn main() {

    let cfg = parse_args(args());
    println!("cfg: {}", cfg);

    if cfg.path.len() == 0 {
        fail!("Reading from stdin is not yet implemented");
    }

    let mut ret : WordCount = WordCount { lines: 0u, words: 0u, chars: 0u, bytes: 0u };

    // TODO: clone is just a workaround
    let p = Path::new(cfg.path.clone());
    // errors can be checked on opening file ...
    let f = match File::open(&p) {
        Err(why) => fail!("Can't open {}: {}", p.display(), why.desc),
        Ok(f) => f,
    };
    let mut br = BufferedReader::new(f);

    let res = match wc(&mut br.lines()) {
        Ok(res) => res,
        Err(e) => fail!(e)
    };
    //println!("{} {}", res, p.display());

    print_results(&res, &cfg);

}
<commit_msg>Problem: does support only one file<commit_after>/**
 *  wc (word count) implementation in Rust
 *
 *  The purpose is to learn Rust a bit
 *
 *  author: michal.vyskocil@gmail.com
 *
 * TODO:
 *   * output is not properly formatted
 *   * word splitting is done on space only, where manual page claims whitespace
 */

use std::io::{BufferedReader, Reader, File, Lines, Buffer, IoError};
use std::os::args;

#[deriving(Show)]
pub struct WordCount {
    pub lines: uint,
    pub words: uint,
    pub chars: uint,
    pub bytes: uint
}

impl WordCount {
    fn sum(&mut self, other: &WordCount) {
        self.lines += other.lines;
        self.words += other.words;
        self.chars += other.chars;
        self.bytes += other.bytes;
    }
}

#[deriving(Show)]
struct Cfg {
    pub byte_count: bool,
    pub char_count: bool,
    pub line_count: bool,
    pub word_count: bool,
}

fn wc<'r, T: Buffer>(lines : &'r mut Lines<'r, T>) -> Result<WordCount, IoError> {
    let mut ret : WordCount = WordCount { lines: 0u, words: 0u, chars: 0u, bytes: 0u };

    for res in lines {
        let line = try!(res);
        let slice: &str = line.as_slice();

        ret.lines += 1;
        ret.words += slice.split(' ').count();
        ret.chars += slice.chars().count();
        ret.bytes += slice.bytes().count();

    }

    return Ok(ret);
}

fn parse_args(args: Vec<String>) -> (Cfg, Vec<String>) {

    let mut cfg = Cfg{byte_count: false, char_count: false, line_count: false, word_count: false};
    let mut idx = 1u;
    let mut paths = Vec::new();

    while idx < args.len() {
        match args[idx].as_slice() {
            "--lines" => cfg.line_count = true,
            "-l"      => cfg.line_count = true,
            "--bytes" => cfg.byte_count = true,
            "-c"      => cfg.byte_count = true,
            "--chars" => cfg.char_count = true,
            "-m"      => cfg.char_count = true,
            "--words" => cfg.word_count = true,
            "-w"      => cfg.word_count = true,
            _ => paths.push(args[idx].clone())  //FIXME: unecessary clone
        }
        idx += 1;
    }

    if !cfg.byte_count && (!cfg.char_count && ! cfg.line_count && !cfg.word_count) {
        cfg.char_count = true;
        cfg.line_count = true;
        cfg.word_count = true;
    }

    (cfg, paths)
}

//newline, word, character, byte, maximum line length
fn print_results(wc : &WordCount, cfg: &Cfg, path: &str) {

    let mut buf = String::new();

    if cfg.line_count {
        buf = format!("{:u}", wc.lines);
    }

    //TODO: add creates new object, investigate things like extend
    if cfg.word_count {
        buf = buf.add(&format!(" {:u}", wc.words));
    }
    if cfg.char_count {
        buf = buf.add(&format!(" {:u}", wc.chars));
    }
    if cfg.byte_count {
        buf = buf.add(&format!(" {:u}", wc.bytes));
    }

    let mut sbuf = buf.as_slice();

    if sbuf.char_at(0) == ' ' {
        sbuf = sbuf.slice_from(1);
    }

    println!("{:s} {:s}", sbuf, path);
}

fn main() {

    let (cfg, paths) = parse_args(args());
    println!("cfg: {}, paths: {}", cfg, paths);

    if paths.len() == 0 {
        fail!("Reading from stdin is not yet implemented");
    }

    let mut total : WordCount = WordCount { lines: 0u, words: 0u, chars: 0u, bytes: 0u };

    for path_i in paths.iter() {

        let path = path_i.as_slice();

        // TODO: clone is just a workaround
        let p = Path::new(path);
        // errors can be checked on opening file ...
        let f = match File::open(&p) {
            Err(why) => fail!("Can't open {}: {}", p.display(), why.desc),
            Ok(f) => f,
        };
        let mut br = BufferedReader::new(f);

        let res = match wc(&mut br.lines()) {
            Ok(res) => res,
            Err(e) => fail!(e)
        };

        total.sum(&res);
        print_results(&res, &cfg, path);
    }

    if paths.len() > 1 {
        print_results(&total, &cfg, "total");
    }

}
<|endoftext|>


<commit_before>use prolog_parser::ast::*;

use prolog::instructions::*;
use prolog::debray_allocator::*;
use prolog::codegen::*;
use prolog::machine::*;
use prolog::toplevel::*;

use std::collections::{HashMap, HashSet, VecDeque};
use std::mem;

#[allow(dead_code)]
fn print_code(code: &Code) {
    for clause in code {
        match clause {
            &Line::Arithmetic(ref arith) =>
                println!("{}", arith),
            &Line::Fact(ref fact) =>
                for fact_instr in fact {
                    println!("{}", fact_instr);
                },
            &Line::Cut(ref cut) =>
                println!("{}", cut),
            &Line::Choice(ref choice) =>
                println!("{}", choice),
            &Line::Control(ref control) =>
                println!("{}", control),
            &Line::IndexedChoice(ref choice) =>
                println!("{}", choice),
            &Line::Indexing(ref indexing) =>
                println!("{}", indexing),
            &Line::Query(ref query) =>
                for query_instr in query {
                    println!("{}", query_instr);
                }
        }
    }
}

pub fn parse_code(wam: &mut Machine, buffer: &str) -> Result<TopLevelPacket, ParserError>
{
    let atom_tbl = wam.atom_tbl();
    let flags = wam.machine_flags();

    let index = MachineCodeIndices {
        code_dir: &mut wam.code_dir,
        op_dir: &mut wam.op_dir,
    };

    let mut worker = TopLevelWorker::new(buffer.as_bytes(), atom_tbl, flags, index);
    worker.parse_code()
}

// throw errors if declaration or query found.
fn compile_relation(tl: &TopLevel, non_counted_bt: bool, flags: MachineFlags) -> Result<Code, ParserError>
{
    let mut cg = CodeGenerator::<DebrayAllocator>::new(non_counted_bt, flags);

    match tl {
        &TopLevel::Declaration(_) | &TopLevel::Query(_) =>
            Err(ParserError::ExpectedRel),
        &TopLevel::Predicate(ref clauses) =>
            cg.compile_predicate(&clauses.0),
        &TopLevel::Fact(ref fact) =>
            Ok(cg.compile_fact(fact)),
        &TopLevel::Rule(ref rule) =>
            cg.compile_rule(rule)
    }
}

// set first jmp_by_call or jmp_by_index instruction to code.len() -
// idx, where idx is the place it occurs. It only does this to the
// *first* uninitialized jmp index it encounters, then returns.
fn set_first_index(code: &mut Code)
{
    let code_len = code.len();

    for (idx, line) in code.iter_mut().enumerate() {
        match line {
            &mut Line::Control(ControlInstruction::JmpBy(_, ref mut offset, ..)) if *offset == 0 => {
                *offset = code_len - idx;
                break;
            },
            _ => {}
        };
    }
}

fn compile_appendix(code: &mut Code, queue: Vec<TopLevel>, non_counted_bt: bool, flags: MachineFlags)
                    -> Result<(), ParserError>
{
    for tl in queue.iter() {
        set_first_index(code);
        code.append(&mut compile_relation(tl, non_counted_bt, flags)?);
    }

    Ok(())
}

fn compile_query(terms: Vec<QueryTerm>, queue: Vec<TopLevel>, flags: MachineFlags)
                 -> Result<(Code, AllocVarDict), ParserError>
{
    // count backtracking inferences.
    let mut cg = CodeGenerator::<DebrayAllocator>::new(false, flags);
    let mut code = try!(cg.compile_query(&terms));

    compile_appendix(&mut code, queue, false, flags)?;

    Ok((code, cg.take_vars()))
}

fn compile_decl(wam: &mut Machine, tl: TopLevel, queue: Vec<TopLevel>) -> EvalSession
{
    match tl {
        TopLevel::Declaration(Declaration::Op(op_decl)) => {
            try_eval_session!(op_decl.submit(clause_name!("user"), &mut wam.op_dir));
            EvalSession::EntrySuccess
        },
        TopLevel::Declaration(Declaration::UseModule(name)) =>
            wam.use_module_in_toplevel(name),
        TopLevel::Declaration(Declaration::UseQualifiedModule(name, exports)) =>
            wam.use_qualified_module_in_toplevel(name, exports),
        TopLevel::Declaration(_) =>
            EvalSession::from(ParserError::InvalidModuleDecl),
        _ => {
            let name = try_eval_session!(if let Some(name) = tl.name() {
                Ok(name)
            } else {
                Err(SessionError::NamelessEntry)
            });

            let mut code = try_eval_session!(compile_relation(&tl, false, wam.machine_flags()));
            try_eval_session!(compile_appendix(&mut code, queue, false, wam.machine_flags()));

            if !code.is_empty() {
                wam.add_user_code(name, tl.arity(), code, tl.as_predicate().ok().unwrap())
            } else {
                EvalSession::from(SessionError::ImpermissibleEntry(String::from("no code generated.")))
            }
        }
    }
}

pub fn compile_packet(wam: &mut Machine, tl: TopLevelPacket) -> EvalSession
{
    match tl {
        TopLevelPacket::Query(terms, queue) =>
            match compile_query(terms, queue, wam.machine_flags()) {
                Ok((mut code, vars)) => wam.submit_query(code, vars),
                Err(e) => EvalSession::from(e)
            },
        TopLevelPacket::Decl(tl, queue) =>
            compile_decl(wam, tl, queue)
    }
}

pub struct ListingCompiler<'a> {
    wam: &'a mut Machine,
    non_counted_bt_preds: HashSet<PredicateKey>,
    module: Option<Module>
}

impl<'a> ListingCompiler<'a> {
    pub fn new(wam: &'a mut Machine) -> Self {
        ListingCompiler { wam,
                          module: None,
                          non_counted_bt_preds: HashSet::new() }
    }

    fn get_module_name(&self) -> ClauseName {
        self.module.as_ref()
            .map(|module| module.module_decl.name.clone())
            .unwrap_or(ClauseName::BuiltIn("user"))
    }

    fn gen_code(&mut self, decls: Vec<(Predicate, VecDeque<TopLevel>)>, code_dir: &mut CodeDir)
                -> Result<Code, SessionError>
    {
        let mut code = vec![];

        for (decl, queue) in decls {
            let (name, arity) = decl.0.first().and_then(|cl| {
                let arity = cl.arity();
                cl.name().map(|name| (name, arity))
            }).ok_or(SessionError::NamelessEntry)?;

            let non_counted_bt = self.non_counted_bt_preds.contains(&(name.clone(), arity));

            let p = code.len() + self.wam.code_size();
            let mut decl_code = compile_relation(&TopLevel::Predicate(decl), non_counted_bt,
                                                 self.wam.machine_flags())?;

            compile_appendix(&mut decl_code, Vec::from(queue), non_counted_bt,
                             self.wam.machine_flags())?;

            let idx = code_dir.entry((name, arity)).or_insert(CodeIndex::default());
            set_code_index!(idx, IndexPtr::Index(p), self.get_module_name());

            code.extend(decl_code.into_iter());
        }

        Ok(code)
    }

    fn add_code(self, code: Code, indices: MachineCodeIndices) {
        let code_dir = mem::replace(indices.code_dir, HashMap::new());
        let op_dir   = mem::replace(indices.op_dir, HashMap::new());

        if let Some(mut module) = self.module {
            module.code_dir.extend(as_module_code_dir(code_dir));
            module.op_dir.extend(op_dir.into_iter());

            self.wam.add_module(module, code);
        } else {
            self.wam.add_batched_code(code, code_dir);
            self.wam.add_batched_ops(op_dir);
        }
    }

    fn add_non_counted_bt_flag(&mut self, name: ClauseName, arity: usize) {
        self.non_counted_bt_preds.insert((name, arity));
    }
}

fn use_module(module: &mut Option<Module>, submodule: &Module, indices: &mut MachineCodeIndices)
{
    indices.use_module(submodule);

    if let &mut Some(ref mut module) = module {
        module.use_module(submodule);
    }
}

fn use_qualified_module(module: &mut Option<Module>, submodule: &Module, exports: &Vec<PredicateKey>,
                        indices: &mut MachineCodeIndices)
{
    indices.use_qualified_module(submodule, exports);

    if let &mut Some(ref mut module) = module {
        module.use_qualified_module(submodule, exports);
    }
}

pub
fn compile_listing(wam: &mut Machine, src_str: &str, mut indices: MachineCodeIndices) -> EvalSession
{
    let mut worker = TopLevelBatchWorker::new(src_str.as_bytes(), wam.atom_tbl(), wam.machine_flags());
    let mut compiler = ListingCompiler::new(wam);

    while let Some(decl) = try_eval_session!(worker.consume(&mut indices)) {
        match decl {
            Declaration::NonCountedBacktracking(name, arity) =>
                compiler.add_non_counted_bt_flag(name, arity),
            Declaration::Op(op_decl) =>
                try_eval_session!(op_decl.submit(compiler.get_module_name(), &mut indices.op_dir)),
            Declaration::UseModule(name) =>
                if let Some(ref submodule) = compiler.wam.get_module(name.clone()) {
                    use_module(&mut compiler.module, submodule, &mut indices);
                } else {
                    return EvalSession::from(SessionError::ModuleNotFound);
                },
            Declaration::UseQualifiedModule(name, exports) =>
                if let Some(ref submodule) = compiler.wam.get_module(name.clone()) {
                    use_qualified_module(&mut compiler.module, submodule, &exports, &mut indices);
                } else {
                    return EvalSession::from(SessionError::ModuleNotFound);
                },
            Declaration::Module(module_decl) =>
                if compiler.module.is_none() {
                    worker.source_mod = module_decl.name.clone();
                    compiler.module = Some(Module::new(module_decl));
                } else {
                    return EvalSession::from(ParserError::InvalidModuleDecl);
                }
        }
    }

    let code = try_eval_session!(compiler.gen_code(worker.results, &mut indices.code_dir));
    compiler.add_code(code, indices);

    EvalSession::EntrySuccess
}

pub fn compile_user_module(wam: &mut Machine, src_str: &str) -> EvalSession {
    let mut indices = machine_code_indices!(&mut CodeDir::new(), &mut default_op_dir());

    if let Some(ref builtins) = wam.modules.get(&clause_name!("builtins")) {
        indices.use_module(builtins);
    } else {
        return EvalSession::from(SessionError::ModuleNotFound);
    }

    compile_listing(wam, src_str, indices)
}
<commit_msg>add permission error for attempts at redefining builtins, re: #50<commit_after>use prolog_parser::ast::*;

use prolog::instructions::*;
use prolog::debray_allocator::*;
use prolog::codegen::*;
use prolog::machine::*;
use prolog::toplevel::*;

use std::collections::{HashMap, HashSet, VecDeque};
use std::mem;

#[allow(dead_code)]
fn print_code(code: &Code) {
    for clause in code {
        match clause {
            &Line::Arithmetic(ref arith) =>
                println!("{}", arith),
            &Line::Fact(ref fact) =>
                for fact_instr in fact {
                    println!("{}", fact_instr);
                },
            &Line::Cut(ref cut) =>
                println!("{}", cut),
            &Line::Choice(ref choice) =>
                println!("{}", choice),
            &Line::Control(ref control) =>
                println!("{}", control),
            &Line::IndexedChoice(ref choice) =>
                println!("{}", choice),
            &Line::Indexing(ref indexing) =>
                println!("{}", indexing),
            &Line::Query(ref query) =>
                for query_instr in query {
                    println!("{}", query_instr);
                }
        }
    }
}

pub fn parse_code(wam: &mut Machine, buffer: &str) -> Result<TopLevelPacket, ParserError>
{
    let atom_tbl = wam.atom_tbl();
    let flags = wam.machine_flags();

    let index = MachineCodeIndices {
        code_dir: &mut wam.code_dir,
        op_dir: &mut wam.op_dir,
    };

    let mut worker = TopLevelWorker::new(buffer.as_bytes(), atom_tbl, flags, index);
    worker.parse_code()
}

// throw errors if declaration or query found.
fn compile_relation(tl: &TopLevel, non_counted_bt: bool, flags: MachineFlags) -> Result<Code, ParserError>
{
    let mut cg = CodeGenerator::<DebrayAllocator>::new(non_counted_bt, flags);

    match tl {
        &TopLevel::Declaration(_) | &TopLevel::Query(_) =>
            Err(ParserError::ExpectedRel),
        &TopLevel::Predicate(ref clauses) =>
            cg.compile_predicate(&clauses.0),
        &TopLevel::Fact(ref fact) =>
            Ok(cg.compile_fact(fact)),
        &TopLevel::Rule(ref rule) =>
            cg.compile_rule(rule)
    }
}

// set first jmp_by_call or jmp_by_index instruction to code.len() -
// idx, where idx is the place it occurs. It only does this to the
// *first* uninitialized jmp index it encounters, then returns.
fn set_first_index(code: &mut Code)
{
    let code_len = code.len();

    for (idx, line) in code.iter_mut().enumerate() {
        match line {
            &mut Line::Control(ControlInstruction::JmpBy(_, ref mut offset, ..)) if *offset == 0 => {
                *offset = code_len - idx;
                break;
            },
            _ => {}
        };
    }
}

fn compile_appendix(code: &mut Code, queue: Vec<TopLevel>, non_counted_bt: bool, flags: MachineFlags)
                    -> Result<(), ParserError>
{
    for tl in queue.iter() {
        set_first_index(code);
        code.append(&mut compile_relation(tl, non_counted_bt, flags)?);
    }

    Ok(())
}

fn compile_query(terms: Vec<QueryTerm>, queue: Vec<TopLevel>, flags: MachineFlags)
                 -> Result<(Code, AllocVarDict), ParserError>
{
    // count backtracking inferences.
    let mut cg = CodeGenerator::<DebrayAllocator>::new(false, flags);
    let mut code = try!(cg.compile_query(&terms));

    compile_appendix(&mut code, queue, false, flags)?;

    Ok((code, cg.take_vars()))
}

fn compile_decl(wam: &mut Machine, tl: TopLevel, queue: Vec<TopLevel>) -> EvalSession
{
    match tl {
        TopLevel::Declaration(Declaration::Op(op_decl)) => {
            try_eval_session!(op_decl.submit(clause_name!("user"), &mut wam.op_dir));
            EvalSession::EntrySuccess
        },
        TopLevel::Declaration(Declaration::UseModule(name)) =>
            wam.use_module_in_toplevel(name),
        TopLevel::Declaration(Declaration::UseQualifiedModule(name, exports)) =>
            wam.use_qualified_module_in_toplevel(name, exports),
        TopLevel::Declaration(_) =>
            EvalSession::from(ParserError::InvalidModuleDecl),
        _ => {
            let name = try_eval_session!(if let Some(name) = tl.name() {
                match ClauseType::from(name.clone(), tl.arity(), None) {
                    ClauseType::Named(..) | ClauseType::Op(..) =>
                        Ok(name),
                    _ => {
                        let err_str = format!("{}/{}", name.as_str(), tl.arity());
                        Err(SessionError::ImpermissibleEntry(err_str))
                    }
                }
            } else {
                Err(SessionError::NamelessEntry)
            });

            let mut code = try_eval_session!(compile_relation(&tl, false, wam.machine_flags()));
            try_eval_session!(compile_appendix(&mut code, queue, false, wam.machine_flags()));

            if !code.is_empty() {
                wam.add_user_code(name, tl.arity(), code, tl.as_predicate().ok().unwrap())
            } else {
                EvalSession::from(SessionError::ImpermissibleEntry(String::from("no code generated.")))
            }
        }
    }
}

pub fn compile_packet(wam: &mut Machine, tl: TopLevelPacket) -> EvalSession
{
    match tl {
        TopLevelPacket::Query(terms, queue) =>
            match compile_query(terms, queue, wam.machine_flags()) {
                Ok((mut code, vars)) => wam.submit_query(code, vars),
                Err(e) => EvalSession::from(e)
            },
        TopLevelPacket::Decl(tl, queue) =>
            compile_decl(wam, tl, queue)
    }
}

pub struct ListingCompiler<'a> {
    wam: &'a mut Machine,
    non_counted_bt_preds: HashSet<PredicateKey>,
    module: Option<Module>
}

impl<'a> ListingCompiler<'a> {
    pub fn new(wam: &'a mut Machine) -> Self {
        ListingCompiler { wam,
                          module: None,
                          non_counted_bt_preds: HashSet::new() }
    }

    fn get_module_name(&self) -> ClauseName {
        self.module.as_ref()
            .map(|module| module.module_decl.name.clone())
            .unwrap_or(ClauseName::BuiltIn("user"))
    }

    fn gen_code(&mut self, decls: Vec<(Predicate, VecDeque<TopLevel>)>, code_dir: &mut CodeDir)
                -> Result<Code, SessionError>
    {
        let mut code = vec![];

        for (decl, queue) in decls {
            let (name, arity) = decl.0.first().and_then(|cl| {
                let arity = cl.arity();
                cl.name().map(|name| (name, arity))
            }).ok_or(SessionError::NamelessEntry)?;

            let non_counted_bt = self.non_counted_bt_preds.contains(&(name.clone(), arity));

            let p = code.len() + self.wam.code_size();
            let mut decl_code = compile_relation(&TopLevel::Predicate(decl), non_counted_bt,
                                                 self.wam.machine_flags())?;

            compile_appendix(&mut decl_code, Vec::from(queue), non_counted_bt,
                             self.wam.machine_flags())?;

            let idx = code_dir.entry((name, arity)).or_insert(CodeIndex::default());
            set_code_index!(idx, IndexPtr::Index(p), self.get_module_name());

            code.extend(decl_code.into_iter());
        }

        Ok(code)
    }

    fn add_code(self, code: Code, indices: MachineCodeIndices) {
        let code_dir = mem::replace(indices.code_dir, HashMap::new());
        let op_dir   = mem::replace(indices.op_dir, HashMap::new());

        if let Some(mut module) = self.module {
            module.code_dir.extend(as_module_code_dir(code_dir));
            module.op_dir.extend(op_dir.into_iter());

            self.wam.add_module(module, code);
        } else {
            self.wam.add_batched_code(code, code_dir);
            self.wam.add_batched_ops(op_dir);
        }
    }

    fn add_non_counted_bt_flag(&mut self, name: ClauseName, arity: usize) {
        self.non_counted_bt_preds.insert((name, arity));
    }
}

fn use_module(module: &mut Option<Module>, submodule: &Module, indices: &mut MachineCodeIndices)
{
    indices.use_module(submodule);

    if let &mut Some(ref mut module) = module {
        module.use_module(submodule);
    }
}

fn use_qualified_module(module: &mut Option<Module>, submodule: &Module, exports: &Vec<PredicateKey>,
                        indices: &mut MachineCodeIndices)
{
    indices.use_qualified_module(submodule, exports);

    if let &mut Some(ref mut module) = module {
        module.use_qualified_module(submodule, exports);
    }
}

pub
fn compile_listing(wam: &mut Machine, src_str: &str, mut indices: MachineCodeIndices) -> EvalSession
{
    let mut worker = TopLevelBatchWorker::new(src_str.as_bytes(), wam.atom_tbl(), wam.machine_flags());
    let mut compiler = ListingCompiler::new(wam);

    while let Some(decl) = try_eval_session!(worker.consume(&mut indices)) {
        match decl {
            Declaration::NonCountedBacktracking(name, arity) =>
                compiler.add_non_counted_bt_flag(name, arity),
            Declaration::Op(op_decl) =>
                try_eval_session!(op_decl.submit(compiler.get_module_name(), &mut indices.op_dir)),
            Declaration::UseModule(name) =>
                if let Some(ref submodule) = compiler.wam.get_module(name.clone()) {
                    use_module(&mut compiler.module, submodule, &mut indices);
                } else {
                    return EvalSession::from(SessionError::ModuleNotFound);
                },
            Declaration::UseQualifiedModule(name, exports) =>
                if let Some(ref submodule) = compiler.wam.get_module(name.clone()) {
                    use_qualified_module(&mut compiler.module, submodule, &exports, &mut indices);
                } else {
                    return EvalSession::from(SessionError::ModuleNotFound);
                },
            Declaration::Module(module_decl) =>
                if compiler.module.is_none() {
                    worker.source_mod = module_decl.name.clone();
                    compiler.module = Some(Module::new(module_decl));
                } else {
                    return EvalSession::from(ParserError::InvalidModuleDecl);
                }
        }
    }

    let code = try_eval_session!(compiler.gen_code(worker.results, &mut indices.code_dir));
    compiler.add_code(code, indices);

    EvalSession::EntrySuccess
}

pub fn compile_user_module(wam: &mut Machine, src_str: &str) -> EvalSession {
    let mut indices = machine_code_indices!(&mut CodeDir::new(), &mut default_op_dir());

    if let Some(ref builtins) = wam.modules.get(&clause_name!("builtins")) {
        indices.use_module(builtins);
    } else {
        return EvalSession::from(SessionError::ModuleNotFound);
    }

    compile_listing(wam, src_str, indices)
}
<|endoftext|>


<commit_before>use clap::{App, AppSettings, Arg, SubCommand};
use error::Error;
use std::path::Path;
use std::process::exit;
use std::{env, fs, u64};
use tree_sitter_cli::{
    config, error, generate, highlight, loader, logger, parse, test, wasm, web_ui,
};

const BUILD_VERSION: &'static str = env!("CARGO_PKG_VERSION");
const BUILD_SHA: Option<&'static str> = option_env!("BUILD_SHA");

fn main() {
    if let Err(e) = run() {
        println!("");
        eprintln!("{}", e.message());
        exit(1);
    }
}

fn run() -> error::Result<()> {
    let version = if let Some(build_sha) = BUILD_SHA {
        format!("{} ({})", BUILD_VERSION, build_sha)
    } else {
        BUILD_VERSION.to_string()
    };

    let matches = App::new("tree-sitter")
        .version(version.as_str())
        .setting(AppSettings::SubcommandRequiredElseHelp)
        .author("Max Brunsfeld <maxbrunsfeld@gmail.com>")
        .about("Generates and tests parsers")
        .subcommand(SubCommand::with_name("init-config").about("Generate a default config file"))
        .subcommand(
            SubCommand::with_name("generate")
                .about("Generate a parser")
                .arg(Arg::with_name("grammar-path").index(1))
                .arg(Arg::with_name("log").long("log"))
                .arg(Arg::with_name("properties-only").long("properties"))
                .arg(Arg::with_name("no-minimize").long("no-minimize")),
        )
        .subcommand(
            SubCommand::with_name("parse")
                .about("Parse a file")
                .arg(
                    Arg::with_name("path")
                        .index(1)
                        .multiple(true)
                        .required(true),
                )
                .arg(Arg::with_name("scope").long("scope").takes_value(true))
                .arg(Arg::with_name("debug").long("debug").short("d"))
                .arg(Arg::with_name("debug-graph").long("debug-graph").short("D"))
                .arg(Arg::with_name("quiet").long("quiet").short("q"))
                .arg(Arg::with_name("time").long("time").short("t"))
                .arg(Arg::with_name("allow-cancellation").long("cancel"))
                .arg(Arg::with_name("timeout").long("timeout").takes_value(true))
                .arg(
                    Arg::with_name("edits")
                        .long("edit")
                        .short("edit")
                        .takes_value(true)
                        .multiple(true)
                        .number_of_values(1),
                ),
        )
        .subcommand(
            SubCommand::with_name("test")
                .about("Run a parser's tests")
                .arg(
                    Arg::with_name("filter")
                        .long("filter")
                        .short("f")
                        .takes_value(true),
                )
                .arg(Arg::with_name("debug").long("debug").short("d"))
                .arg(Arg::with_name("debug-graph").long("debug-graph").short("D")),
        )
        .subcommand(
            SubCommand::with_name("highlight")
                .about("Highlight a file")
                .arg(
                    Arg::with_name("path")
                        .index(1)
                        .multiple(true)
                        .required(true),
                )
                .arg(Arg::with_name("scope").long("scope").takes_value(true))
                .arg(Arg::with_name("html").long("html").short("h"))
                .arg(Arg::with_name("time").long("time").short("t")),
        )
        .subcommand(
            SubCommand::with_name("build-wasm")
                .about("Compile a parser to WASM")
                .arg(
                    Arg::with_name("docker")
                        .long("docker")
                        .help("Run emscripten via docker even if it is installed locally"),
                )
                .arg(Arg::with_name("path").index(1).multiple(true)),
        )
        .subcommand(
            SubCommand::with_name("web-ui").about("Test a parser interactively in the browser"),
        )
        .subcommand(
            SubCommand::with_name("dump-languages")
                .about("Print info about all known language parsers"),
        )
        .get_matches();

    let home_dir = dirs::home_dir().expect("Failed to read home directory");
    let current_dir = env::current_dir().unwrap();
    let config = config::Config::load(&home_dir);
    let mut loader = loader::Loader::new(config.binary_directory.clone());

    if matches.subcommand_matches("init-config").is_some() {
        let config = config::Config::new(&home_dir);
        config.save(&home_dir)?;
    } else if let Some(matches) = matches.subcommand_matches("generate") {
        let grammar_path = matches.value_of("grammar-path");
        let properties_only = matches.is_present("properties-only");
        if matches.is_present("log") {
            logger::init();
        }
        generate::generate_parser_in_directory(&current_dir, grammar_path, properties_only)?;
    } else if let Some(matches) = matches.subcommand_matches("test") {
        let debug = matches.is_present("debug");
        let debug_graph = matches.is_present("debug-graph");
        let filter = matches.value_of("filter");
        let corpus_path = current_dir.join("corpus");
        if let Some(language) = loader.languages_at_path(&current_dir)?.first() {
            test::run_tests_at_path(*language, &corpus_path, debug, debug_graph, filter)?;
        } else {
            eprintln!("No language found");
        }
    } else if let Some(matches) = matches.subcommand_matches("parse") {
        let debug = matches.is_present("debug");
        let debug_graph = matches.is_present("debug-graph");
        let quiet = matches.is_present("quiet");
        let time = matches.is_present("time");
        let edits = matches
            .values_of("edits")
            .map_or(Vec::new(), |e| e.collect());
        let allow_cancellation = matches.is_present("allow-cancellation");
        let timeout = matches
            .value_of("timeout")
            .map_or(0, |t| u64::from_str_radix(t, 10).unwrap());
        loader.find_all_languages(&config.parser_directories)?;
        let paths = matches
            .values_of("path")
            .unwrap()
            .into_iter()
            .collect::<Vec<_>>();
        let max_path_length = paths.iter().map(|p| p.chars().count()).max().unwrap();
        let mut has_error = false;
        for path in paths {
            let path = Path::new(path);
            let language = if let Some(scope) = matches.value_of("scope") {
                if let Some(config) =
                    loader
                        .language_configuration_for_scope(scope)
                        .map_err(Error::wrap(|| {
                            format!("Failed to load language for scope '{}'", scope)
                        }))?
                {
                    config.0
                } else {
                    return Error::err(format!("Unknown scope '{}'", scope));
                }
            } else if let Some((lang, _)) = loader
                .language_configuration_for_file_name(path)
                .map_err(Error::wrap(|| {
                    format!(
                        "Failed to load language for file name {:?}",
                        path.file_name().unwrap()
                    )
                }))?
            {
                lang
            } else if let Some(lang) = loader
                .languages_at_path(&current_dir)
                .map_err(Error::wrap(|| {
                    "Failed to load language in current directory"
                }))?
                .first()
                .cloned()
            {
                lang
            } else {
                eprintln!("No language found");
                return Ok(());
            };
            has_error |= parse::parse_file_at_path(
                language,
                path,
                &edits,
                max_path_length,
                quiet,
                time,
                timeout,
                debug,
                debug_graph,
                allow_cancellation,
            )?;
        }

        if has_error {
            return Error::err(String::new());
        }
    } else if let Some(matches) = matches.subcommand_matches("highlight") {
        let paths = matches.values_of("path").unwrap().into_iter();
        let html_mode = matches.is_present("html");
        let time = matches.is_present("time");
        loader.find_all_languages(&config.parser_directories)?;

        if html_mode {
            println!("{}", highlight::HTML_HEADER);
        }

        let language_config;
        if let Some(scope) = matches.value_of("scope") {
            language_config = loader.language_configuration_for_scope(scope)?;
            if language_config.is_none() {
                return Error::err(format!("Unknown scope '{}'", scope));
            }
        } else {
            language_config = None;
        }

        for path in paths {
            let path = Path::new(path);
            let (language, language_config) = match language_config {
                Some(v) => v,
                None => match loader.language_configuration_for_file_name(path)? {
                    Some(v) => v,
                    None => {
                        eprintln!("No language found for path {:?}", path);
                        continue;
                    }
                },
            };

            if let Some(sheet) = language_config.highlight_property_sheet(language)? {
                let source = fs::read(path)?;
                if html_mode {
                    highlight::html(&loader, &config.theme, &source, language, sheet)?;
                } else {
                    highlight::ansi(&loader, &config.theme, &source, language, sheet, time)?;
                }
            } else {
                return Error::err(format!("No syntax highlighting property sheet specified"));
            }
        }
    } else if let Some(matches) = matches.subcommand_matches("build-wasm") {
        let grammar_path = current_dir.join(matches.value_of("path").unwrap_or(""));
        wasm::compile_language_to_wasm(&grammar_path, matches.is_present("docker"))?;
    } else if matches.subcommand_matches("web-ui").is_some() {
        web_ui::serve(&current_dir);
    } else if matches.subcommand_matches("dump-languages").is_some() {
        loader.find_all_languages(&config.parser_directories)?;
        for (configuration, language_path) in loader.get_all_language_configurations() {
            println!(
                "scope: {}\nparser: {:?}\nproperties: {:?}\nfile_types: {:?}\ncontent_regex: {:?}\ninjection_regex: {:?}\n",
                configuration.scope.as_ref().unwrap_or(&String::new()),
                language_path,
                configuration.highlight_property_sheet_path,
                configuration.file_types,
                configuration.content_regex,
                configuration.injection_regex,
            );
        }
    }

    Ok(())
}
<commit_msg>Remove unnecessary blank lines from parse command output on error<commit_after>use clap::{App, AppSettings, Arg, SubCommand};
use error::Error;
use std::path::Path;
use std::process::exit;
use std::{env, fs, u64};
use tree_sitter_cli::{
    config, error, generate, highlight, loader, logger, parse, test, wasm, web_ui,
};

const BUILD_VERSION: &'static str = env!("CARGO_PKG_VERSION");
const BUILD_SHA: Option<&'static str> = option_env!("BUILD_SHA");

fn main() {
    if let Err(e) = run() {
        if !e.message().is_empty() {
            println!("");
            eprintln!("{}", e.message());
        }
        exit(1);
    }
}

fn run() -> error::Result<()> {
    let version = if let Some(build_sha) = BUILD_SHA {
        format!("{} ({})", BUILD_VERSION, build_sha)
    } else {
        BUILD_VERSION.to_string()
    };

    let matches = App::new("tree-sitter")
        .version(version.as_str())
        .setting(AppSettings::SubcommandRequiredElseHelp)
        .author("Max Brunsfeld <maxbrunsfeld@gmail.com>")
        .about("Generates and tests parsers")
        .subcommand(SubCommand::with_name("init-config").about("Generate a default config file"))
        .subcommand(
            SubCommand::with_name("generate")
                .about("Generate a parser")
                .arg(Arg::with_name("grammar-path").index(1))
                .arg(Arg::with_name("log").long("log"))
                .arg(Arg::with_name("properties-only").long("properties"))
                .arg(Arg::with_name("no-minimize").long("no-minimize")),
        )
        .subcommand(
            SubCommand::with_name("parse")
                .about("Parse a file")
                .arg(
                    Arg::with_name("path")
                        .index(1)
                        .multiple(true)
                        .required(true),
                )
                .arg(Arg::with_name("scope").long("scope").takes_value(true))
                .arg(Arg::with_name("debug").long("debug").short("d"))
                .arg(Arg::with_name("debug-graph").long("debug-graph").short("D"))
                .arg(Arg::with_name("quiet").long("quiet").short("q"))
                .arg(Arg::with_name("time").long("time").short("t"))
                .arg(Arg::with_name("allow-cancellation").long("cancel"))
                .arg(Arg::with_name("timeout").long("timeout").takes_value(true))
                .arg(
                    Arg::with_name("edits")
                        .long("edit")
                        .short("edit")
                        .takes_value(true)
                        .multiple(true)
                        .number_of_values(1),
                ),
        )
        .subcommand(
            SubCommand::with_name("test")
                .about("Run a parser's tests")
                .arg(
                    Arg::with_name("filter")
                        .long("filter")
                        .short("f")
                        .takes_value(true),
                )
                .arg(Arg::with_name("debug").long("debug").short("d"))
                .arg(Arg::with_name("debug-graph").long("debug-graph").short("D")),
        )
        .subcommand(
            SubCommand::with_name("highlight")
                .about("Highlight a file")
                .arg(
                    Arg::with_name("path")
                        .index(1)
                        .multiple(true)
                        .required(true),
                )
                .arg(Arg::with_name("scope").long("scope").takes_value(true))
                .arg(Arg::with_name("html").long("html").short("h"))
                .arg(Arg::with_name("time").long("time").short("t")),
        )
        .subcommand(
            SubCommand::with_name("build-wasm")
                .about("Compile a parser to WASM")
                .arg(
                    Arg::with_name("docker")
                        .long("docker")
                        .help("Run emscripten via docker even if it is installed locally"),
                )
                .arg(Arg::with_name("path").index(1).multiple(true)),
        )
        .subcommand(
            SubCommand::with_name("web-ui").about("Test a parser interactively in the browser"),
        )
        .subcommand(
            SubCommand::with_name("dump-languages")
                .about("Print info about all known language parsers"),
        )
        .get_matches();

    let home_dir = dirs::home_dir().expect("Failed to read home directory");
    let current_dir = env::current_dir().unwrap();
    let config = config::Config::load(&home_dir);
    let mut loader = loader::Loader::new(config.binary_directory.clone());

    if matches.subcommand_matches("init-config").is_some() {
        let config = config::Config::new(&home_dir);
        config.save(&home_dir)?;
    } else if let Some(matches) = matches.subcommand_matches("generate") {
        let grammar_path = matches.value_of("grammar-path");
        let properties_only = matches.is_present("properties-only");
        if matches.is_present("log") {
            logger::init();
        }
        generate::generate_parser_in_directory(&current_dir, grammar_path, properties_only)?;
    } else if let Some(matches) = matches.subcommand_matches("test") {
        let debug = matches.is_present("debug");
        let debug_graph = matches.is_present("debug-graph");
        let filter = matches.value_of("filter");
        let corpus_path = current_dir.join("corpus");
        if let Some(language) = loader.languages_at_path(&current_dir)?.first() {
            test::run_tests_at_path(*language, &corpus_path, debug, debug_graph, filter)?;
        } else {
            eprintln!("No language found");
        }
    } else if let Some(matches) = matches.subcommand_matches("parse") {
        let debug = matches.is_present("debug");
        let debug_graph = matches.is_present("debug-graph");
        let quiet = matches.is_present("quiet");
        let time = matches.is_present("time");
        let edits = matches
            .values_of("edits")
            .map_or(Vec::new(), |e| e.collect());
        let allow_cancellation = matches.is_present("allow-cancellation");
        let timeout = matches
            .value_of("timeout")
            .map_or(0, |t| u64::from_str_radix(t, 10).unwrap());
        loader.find_all_languages(&config.parser_directories)?;
        let paths = matches
            .values_of("path")
            .unwrap()
            .into_iter()
            .collect::<Vec<_>>();
        let max_path_length = paths.iter().map(|p| p.chars().count()).max().unwrap();
        let mut has_error = false;
        for path in paths {
            let path = Path::new(path);
            let language = if let Some(scope) = matches.value_of("scope") {
                if let Some(config) =
                    loader
                        .language_configuration_for_scope(scope)
                        .map_err(Error::wrap(|| {
                            format!("Failed to load language for scope '{}'", scope)
                        }))?
                {
                    config.0
                } else {
                    return Error::err(format!("Unknown scope '{}'", scope));
                }
            } else if let Some((lang, _)) = loader
                .language_configuration_for_file_name(path)
                .map_err(Error::wrap(|| {
                    format!(
                        "Failed to load language for file name {:?}",
                        path.file_name().unwrap()
                    )
                }))?
            {
                lang
            } else if let Some(lang) = loader
                .languages_at_path(&current_dir)
                .map_err(Error::wrap(|| {
                    "Failed to load language in current directory"
                }))?
                .first()
                .cloned()
            {
                lang
            } else {
                eprintln!("No language found");
                return Ok(());
            };
            has_error |= parse::parse_file_at_path(
                language,
                path,
                &edits,
                max_path_length,
                quiet,
                time,
                timeout,
                debug,
                debug_graph,
                allow_cancellation,
            )?;
        }

        if has_error {
            return Error::err(String::new());
        }
    } else if let Some(matches) = matches.subcommand_matches("highlight") {
        let paths = matches.values_of("path").unwrap().into_iter();
        let html_mode = matches.is_present("html");
        let time = matches.is_present("time");
        loader.find_all_languages(&config.parser_directories)?;

        if html_mode {
            println!("{}", highlight::HTML_HEADER);
        }

        let language_config;
        if let Some(scope) = matches.value_of("scope") {
            language_config = loader.language_configuration_for_scope(scope)?;
            if language_config.is_none() {
                return Error::err(format!("Unknown scope '{}'", scope));
            }
        } else {
            language_config = None;
        }

        for path in paths {
            let path = Path::new(path);
            let (language, language_config) = match language_config {
                Some(v) => v,
                None => match loader.language_configuration_for_file_name(path)? {
                    Some(v) => v,
                    None => {
                        eprintln!("No language found for path {:?}", path);
                        continue;
                    }
                },
            };

            if let Some(sheet) = language_config.highlight_property_sheet(language)? {
                let source = fs::read(path)?;
                if html_mode {
                    highlight::html(&loader, &config.theme, &source, language, sheet)?;
                } else {
                    highlight::ansi(&loader, &config.theme, &source, language, sheet, time)?;
                }
            } else {
                return Error::err(format!("No syntax highlighting property sheet specified"));
            }
        }
    } else if let Some(matches) = matches.subcommand_matches("build-wasm") {
        let grammar_path = current_dir.join(matches.value_of("path").unwrap_or(""));
        wasm::compile_language_to_wasm(&grammar_path, matches.is_present("docker"))?;
    } else if matches.subcommand_matches("web-ui").is_some() {
        web_ui::serve(&current_dir);
    } else if matches.subcommand_matches("dump-languages").is_some() {
        loader.find_all_languages(&config.parser_directories)?;
        for (configuration, language_path) in loader.get_all_language_configurations() {
            println!(
                "scope: {}\nparser: {:?}\nproperties: {:?}\nfile_types: {:?}\ncontent_regex: {:?}\ninjection_regex: {:?}\n",
                configuration.scope.as_ref().unwrap_or(&String::new()),
                language_path,
                configuration.highlight_property_sheet_path,
                configuration.file_types,
                configuration.content_regex,
                configuration.injection_regex,
            );
        }
    }

    Ok(())
}
<|endoftext|>


<commit_before>//! [PLY][1] encoding.
//!
//! This module provides support for the PLY format via the `FromPly` and
//! `ToPly` traits. These traits can be used with a decoder and encoder to read
//! and write mesh data structures to and from the PLY format.
//!
//! PLY support is implemented using the [`ply-rs`][2] crate.
//!
//! # Examples
//!
//! Reading a PLY file into a `MeshGraph`:
//!
//! ```rust
//! # extern crate nalgebra;
//! # extern crate plexus;
//! #
//! use nalgebra::Point3;
//! use plexus::encoding::ply::{FromPly, PositionEncoding};
//! use plexus::graph::MeshGraph;
//! use std::io::Read;
//!
//! type E3 = Point3<f64>;
//!
//! // Read from a file, network, etc.
//! fn read() -> impl Read {
//!     // ...
//!     # let ply: &[u8] = include_bytes!("../../../data/cube.ply");
//!     # ply
//! }
//!
//! let encoding = PositionEncoding::<E3>::default();
//! let (graph, _) = MeshGraph::<E3>::from_ply(encoding, read()).unwrap();
//! ```
//!
//! [1]: https://en.wikipedia.org/wiki/PLY_(file_format)
//! [2]: https://crates.io/crates/ply-rs

#![cfg(feature = "encoding-ply")]

use num::cast;
use num::NumCast;
use ply_rs::parser::Parser;
use ply_rs::ply::KeyMap;
use smallvec::SmallVec;
use std::io::{self, Read, Write};
use std::iter::FromIterator;
use std::marker::PhantomData;
use theon::space::{EuclideanSpace, FiniteDimensional};
use typenum::U3;

use crate::buffer::BufferError;
use crate::encoding::{FaceDecoder, FromEncoding, VertexDecoder};
use crate::graph::GraphError;

pub use ply_rs::ply::{
    ElementDef as ElementDefinition, Property, PropertyDef as PropertyDefinition, PropertyType,
};

// TODO: These traits only allow a single element to be read for each topology
//       (vertices, faces, etc.). It may be useful to allow code to aggregate
//       various elements to produce an output for a single topology.
// TODO: Consider using the new-type pattern to hide underlying types and expose
//       a smaller and more tailored API surface.

pub type Header = KeyMap<ElementDefinition>;
pub type Payload = KeyMap<Vec<Element>>;
pub type Element = KeyMap<Property>;

#[derive(Debug)]
pub enum PlyError {
    ElementNotFound,
    PropertyNotFound,
    Encoding,
    Io(io::Error),
}

impl From<BufferError> for PlyError {
    fn from(_: BufferError) -> Self {
        PlyError::Encoding
    }
}

impl From<io::Error> for PlyError {
    fn from(error: io::Error) -> Self {
        PlyError::Io(error)
    }
}

impl From<GraphError> for PlyError {
    fn from(_: GraphError) -> Self {
        PlyError::Encoding
    }
}

pub trait ElementExt {
    fn scalar<T>(&self, key: &str) -> Result<T, PlyError>
    where
        T: NumCast;

    fn list<T, I>(&self, key: &str) -> Result<I, PlyError>
    where
        T: NumCast,
        I: FromIterator<T>;
}

impl ElementExt for Element {
    fn scalar<T>(&self, key: &str) -> Result<T, PlyError>
    where
        T: NumCast,
    {
        self.get(key)
            .ok_or_else(|| PlyError::PropertyNotFound)?
            .clone()
            .into_scalar()
    }

    fn list<T, I>(&self, key: &str) -> Result<I, PlyError>
    where
        T: NumCast,
        I: FromIterator<T>,
    {
        self.get(key)
            .ok_or_else(|| PlyError::PropertyNotFound)?
            .clone()
            .into_list()
    }
}

pub trait PropertyExt {
    fn into_scalar<T>(self) -> Result<T, PlyError>
    where
        T: NumCast;

    fn into_list<T, I>(self) -> Result<I, PlyError>
    where
        T: NumCast,
        I: FromIterator<T>;
}

impl PropertyExt for Property {
    fn into_scalar<T>(self) -> Result<T, PlyError>
    where
        T: NumCast,
    {
        match self {
            Property::Char(value) => num_cast_scalar(value),
            Property::UChar(value) => num_cast_scalar(value),
            Property::Short(value) => num_cast_scalar(value),
            Property::UShort(value) => num_cast_scalar(value),
            Property::Int(value) => num_cast_scalar(value),
            Property::UInt(value) => num_cast_scalar(value),
            Property::Float(value) => num_cast_scalar(value),
            Property::Double(value) => num_cast_scalar(value),
            _ => Err(PlyError::Encoding),
        }
    }

    fn into_list<T, I>(self) -> Result<I, PlyError>
    where
        T: NumCast,
        I: FromIterator<T>,
    {
        match self {
            Property::ListChar(values) => num_cast_list(values),
            Property::ListUChar(values) => num_cast_list(values),
            Property::ListShort(values) => num_cast_list(values),
            Property::ListUShort(values) => num_cast_list(values),
            Property::ListInt(values) => num_cast_list(values),
            Property::ListUInt(values) => num_cast_list(values),
            Property::ListFloat(values) => num_cast_list(values),
            Property::ListDouble(values) => num_cast_list(values),
            _ => Err(PlyError::Encoding),
        }
    }
}

pub trait VertexElementDecoder {
    fn decode_vertex_elements<'a>(
        &self,
        definitions: &'a Header,
        elements: &'a Payload,
    ) -> Result<(&'a ElementDefinition, &'a Vec<Element>), PlyError> {
        decode_elements(definitions, elements, "vertex")
    }
}

pub trait VertexPropertyDecoder: VertexDecoder {
    fn decode_vertex_properties<'a, I>(
        &self,
        definition: &'a ElementDefinition,
        elements: I,
    ) -> Result<Self::Output, PlyError>
    where
        I: IntoIterator<Item = &'a Element>;
}

pub trait FaceElementDecoder {
    fn decode_face_elements<'a>(
        &self,
        definitions: &'a Header,
        elements: &'a Payload,
    ) -> Result<(&'a ElementDefinition, &'a Vec<Element>), PlyError> {
        decode_elements(definitions, elements, "face")
    }
}

pub trait FacePropertyDecoder: FaceDecoder {
    fn decode_face_properties<'a, I>(
        &self,
        definition: &'a ElementDefinition,
        elements: I,
    ) -> Result<Self::Output, PlyError>
    where
        I: IntoIterator<Item = &'a Element>;
}

pub trait FromPly<E>: Sized {
    fn from_ply<R>(decoder: E, read: R) -> Result<(Self, (Header, Payload)), PlyError>
    where
        R: Read;
}

impl<T, E> FromPly<E> for T
where
    T: FromEncoding<E>,
    PlyError: From<<T as FromEncoding<E>>::Error>,
    E: FaceElementDecoder + FacePropertyDecoder + VertexPropertyDecoder + VertexElementDecoder,
{
    fn from_ply<R>(decoder: E, mut read: R) -> Result<(Self, (Header, Payload)), PlyError>
    where
        R: Read,
    {
        let ply = Parser::<Element>::new().read_ply(&mut read)?;
        let vertices = decoder
            .decode_vertex_elements(&ply.header.elements, &ply.payload)
            .and_then(|(definition, elements)| {
                decoder.decode_vertex_properties(definition, elements)
            })?;
        let faces = decoder
            .decode_face_elements(&ply.header.elements, &ply.payload)
            .and_then(|(definition, elements)| {
                decoder.decode_face_properties(definition, elements)
            })?;
        let mesh = T::from_encoding(vertices, faces)?;
        Ok((mesh, (ply.header.elements, ply.payload)))
    }
}

pub trait ToPly<E> {
    fn to_ply<W>(&self, definitions: Header, encoder: E, write: W) -> Result<usize, PlyError>
    where
        W: Write;
}

pub struct PositionEncoding<T> {
    phantom: PhantomData<T>,
}

impl<T> Default for PositionEncoding<T> {
    fn default() -> Self {
        PositionEncoding {
            phantom: PhantomData,
        }
    }
}

impl<T> FaceDecoder for PositionEncoding<T> {
    type Output = Vec<(Self::Index, Self::Face)>;
    type Index = SmallVec<[usize; 4]>;
    type Face = ();
}

impl<T> FaceElementDecoder for PositionEncoding<T> {}

impl<T> FacePropertyDecoder for PositionEncoding<T> {
    fn decode_face_properties<'a, I>(
        &self,
        _: &'a ElementDefinition,
        elements: I,
    ) -> Result<<Self as FaceDecoder>::Output, PlyError>
    where
        I: IntoIterator<Item = &'a Element>,
    {
        elements
            .into_iter()
            .map(|element| {
                let indices = element.list("vertex_index")?;
                Ok((indices, ()))
            })
            .collect::<Result<_, _>>()
    }
}

impl<T> VertexDecoder for PositionEncoding<T> {
    type Output = Vec<Self::Vertex>;
    type Vertex = T;
}

impl<T> VertexElementDecoder for PositionEncoding<T> {}

// TODO: Support two-dimensional spaces.
impl<T> VertexPropertyDecoder for PositionEncoding<T>
where
    T: EuclideanSpace + FiniteDimensional<N = U3>,
{
    fn decode_vertex_properties<'a, I>(
        &self,
        _: &'a ElementDefinition,
        elements: I,
    ) -> Result<<Self as VertexDecoder>::Output, PlyError>
    where
        I: IntoIterator<Item = &'a Element>,
    {
        elements
            .into_iter()
            .map(|element| {
                let vertex = EuclideanSpace::from_xyz(
                    element.scalar("x")?,
                    element.scalar("y")?,
                    element.scalar("z")?,
                );
                Ok(vertex)
            })
            .collect::<Result<_, _>>()
    }
}

pub fn decode_elements<'a, K>(
    definitions: &'a Header,
    elements: &'a Payload,
    name: K,
) -> Result<(&'a ElementDefinition, &'a Vec<Element>), PlyError>
where
    K: AsRef<str>,
{
    definitions
        .get(name.as_ref())
        .ok_or_else(|| PlyError::ElementNotFound)
        .map(|definition| (definition, elements.get(&definition.name).unwrap()))
}

fn num_cast_scalar<T, U>(value: T) -> Result<U, PlyError>
where
    T: NumCast,
    U: NumCast,
{
    cast::cast(value).ok_or_else(|| PlyError::Encoding)
}

fn num_cast_list<T, U, I>(values: Vec<T>) -> Result<I, PlyError>
where
    T: NumCast,
    U: NumCast,
    I: FromIterator<U>,
{
    values
        .into_iter()
        .map(num_cast_scalar)
        .collect::<Result<_, _>>()
}

#[cfg(test)]
mod tests {
    use nalgebra::Point3;

    use crate::buffer::MeshBuffer;
    use crate::encoding::ply::{FromPly, PositionEncoding};
    use crate::graph::MeshGraph;
    use crate::primitive::Tetragon;

    type E3 = Point3<f64>;

    #[test]
    fn decode_into_buffer() {
        let buffer = {
            let ply: &[u8] = include_bytes!("../../../data/cube.ply");
            MeshBuffer::<Tetragon<usize>, E3>::from_ply(PositionEncoding::<E3>::default(), ply)
                .unwrap()
                .0
        };
        assert_eq!(8, buffer.as_vertex_slice().len());
        assert_eq!(6, buffer.as_index_slice().len());
    }

    #[test]
    fn decode_into_graph() {
        let graph = {
            let ply: &[u8] = include_bytes!("../../../data/cube.ply");
            MeshGraph::<E3>::from_ply(PositionEncoding::<E3>::default(), ply)
                .unwrap()
                .0
        };
        assert_eq!(8, graph.vertex_count());
        assert_eq!(12, graph.edge_count());
        assert_eq!(6, graph.face_count());
    }
}
<commit_msg>Remove unnecessary explicit type.<commit_after>//! [PLY][1] encoding.
//!
//! This module provides support for the PLY format via the `FromPly` and
//! `ToPly` traits. These traits can be used with a decoder and encoder to read
//! and write mesh data structures to and from the PLY format.
//!
//! PLY support is implemented using the [`ply-rs`][2] crate.
//!
//! # Examples
//!
//! Reading a PLY file into a `MeshGraph`:
//!
//! ```rust
//! # extern crate nalgebra;
//! # extern crate plexus;
//! #
//! use nalgebra::Point3;
//! use plexus::encoding::ply::{FromPly, PositionEncoding};
//! use plexus::graph::MeshGraph;
//! use std::io::Read;
//!
//! type E3 = Point3<f64>;
//!
//! // Read from a file, network, etc.
//! fn read() -> impl Read {
//!     // ...
//!     # let ply: &[u8] = include_bytes!("../../../data/cube.ply");
//!     # ply
//! }
//!
//! let encoding = PositionEncoding::<E3>::default();
//! let (graph, _) = MeshGraph::<E3>::from_ply(encoding, read()).unwrap();
//! ```
//!
//! [1]: https://en.wikipedia.org/wiki/PLY_(file_format)
//! [2]: https://crates.io/crates/ply-rs

#![cfg(feature = "encoding-ply")]

use num::cast;
use num::NumCast;
use ply_rs::parser::Parser;
use ply_rs::ply::KeyMap;
use smallvec::SmallVec;
use std::io::{self, Read, Write};
use std::iter::FromIterator;
use std::marker::PhantomData;
use theon::space::{EuclideanSpace, FiniteDimensional};
use typenum::U3;

use crate::buffer::BufferError;
use crate::encoding::{FaceDecoder, FromEncoding, VertexDecoder};
use crate::graph::GraphError;

pub use ply_rs::ply::{
    ElementDef as ElementDefinition, Property, PropertyDef as PropertyDefinition, PropertyType,
};

// TODO: These traits only allow a single element to be read for each topology
//       (vertices, faces, etc.). It may be useful to allow code to aggregate
//       various elements to produce an output for a single topology.
// TODO: Consider using the new-type pattern to hide underlying types and expose
//       a smaller and more tailored API surface.

pub type Header = KeyMap<ElementDefinition>;
pub type Payload = KeyMap<Vec<Element>>;
pub type Element = KeyMap<Property>;

#[derive(Debug)]
pub enum PlyError {
    ElementNotFound,
    PropertyNotFound,
    Encoding,
    Io(io::Error),
}

impl From<BufferError> for PlyError {
    fn from(_: BufferError) -> Self {
        PlyError::Encoding
    }
}

impl From<io::Error> for PlyError {
    fn from(error: io::Error) -> Self {
        PlyError::Io(error)
    }
}

impl From<GraphError> for PlyError {
    fn from(_: GraphError) -> Self {
        PlyError::Encoding
    }
}

pub trait ElementExt {
    fn scalar<T>(&self, key: &str) -> Result<T, PlyError>
    where
        T: NumCast;

    fn list<T, I>(&self, key: &str) -> Result<I, PlyError>
    where
        T: NumCast,
        I: FromIterator<T>;
}

impl ElementExt for Element {
    fn scalar<T>(&self, key: &str) -> Result<T, PlyError>
    where
        T: NumCast,
    {
        self.get(key)
            .ok_or_else(|| PlyError::PropertyNotFound)?
            .clone()
            .into_scalar()
    }

    fn list<T, I>(&self, key: &str) -> Result<I, PlyError>
    where
        T: NumCast,
        I: FromIterator<T>,
    {
        self.get(key)
            .ok_or_else(|| PlyError::PropertyNotFound)?
            .clone()
            .into_list()
    }
}

pub trait PropertyExt {
    fn into_scalar<T>(self) -> Result<T, PlyError>
    where
        T: NumCast;

    fn into_list<T, I>(self) -> Result<I, PlyError>
    where
        T: NumCast,
        I: FromIterator<T>;
}

impl PropertyExt for Property {
    fn into_scalar<T>(self) -> Result<T, PlyError>
    where
        T: NumCast,
    {
        match self {
            Property::Char(value) => num_cast_scalar(value),
            Property::UChar(value) => num_cast_scalar(value),
            Property::Short(value) => num_cast_scalar(value),
            Property::UShort(value) => num_cast_scalar(value),
            Property::Int(value) => num_cast_scalar(value),
            Property::UInt(value) => num_cast_scalar(value),
            Property::Float(value) => num_cast_scalar(value),
            Property::Double(value) => num_cast_scalar(value),
            _ => Err(PlyError::Encoding),
        }
    }

    fn into_list<T, I>(self) -> Result<I, PlyError>
    where
        T: NumCast,
        I: FromIterator<T>,
    {
        match self {
            Property::ListChar(values) => num_cast_list(values),
            Property::ListUChar(values) => num_cast_list(values),
            Property::ListShort(values) => num_cast_list(values),
            Property::ListUShort(values) => num_cast_list(values),
            Property::ListInt(values) => num_cast_list(values),
            Property::ListUInt(values) => num_cast_list(values),
            Property::ListFloat(values) => num_cast_list(values),
            Property::ListDouble(values) => num_cast_list(values),
            _ => Err(PlyError::Encoding),
        }
    }
}

pub trait VertexElementDecoder {
    fn decode_vertex_elements<'a>(
        &self,
        definitions: &'a Header,
        elements: &'a Payload,
    ) -> Result<(&'a ElementDefinition, &'a Vec<Element>), PlyError> {
        decode_elements(definitions, elements, "vertex")
    }
}

pub trait VertexPropertyDecoder: VertexDecoder {
    fn decode_vertex_properties<'a, I>(
        &self,
        definition: &'a ElementDefinition,
        elements: I,
    ) -> Result<Self::Output, PlyError>
    where
        I: IntoIterator<Item = &'a Element>;
}

pub trait FaceElementDecoder {
    fn decode_face_elements<'a>(
        &self,
        definitions: &'a Header,
        elements: &'a Payload,
    ) -> Result<(&'a ElementDefinition, &'a Vec<Element>), PlyError> {
        decode_elements(definitions, elements, "face")
    }
}

pub trait FacePropertyDecoder: FaceDecoder {
    fn decode_face_properties<'a, I>(
        &self,
        definition: &'a ElementDefinition,
        elements: I,
    ) -> Result<Self::Output, PlyError>
    where
        I: IntoIterator<Item = &'a Element>;
}

pub trait FromPly<E>: Sized {
    fn from_ply<R>(decoder: E, read: R) -> Result<(Self, (Header, Payload)), PlyError>
    where
        R: Read;
}

impl<T, E> FromPly<E> for T
where
    T: FromEncoding<E>,
    PlyError: From<<T as FromEncoding<E>>::Error>,
    E: FaceElementDecoder + FacePropertyDecoder + VertexPropertyDecoder + VertexElementDecoder,
{
    fn from_ply<R>(decoder: E, mut read: R) -> Result<(Self, (Header, Payload)), PlyError>
    where
        R: Read,
    {
        let ply = Parser::<Element>::new().read_ply(&mut read)?;
        let vertices = decoder
            .decode_vertex_elements(&ply.header.elements, &ply.payload)
            .and_then(|(definition, elements)| {
                decoder.decode_vertex_properties(definition, elements)
            })?;
        let faces = decoder
            .decode_face_elements(&ply.header.elements, &ply.payload)
            .and_then(|(definition, elements)| {
                decoder.decode_face_properties(definition, elements)
            })?;
        let mesh = T::from_encoding(vertices, faces)?;
        Ok((mesh, (ply.header.elements, ply.payload)))
    }
}

pub trait ToPly<E> {
    fn to_ply<W>(&self, definitions: Header, encoder: E, write: W) -> Result<usize, PlyError>
    where
        W: Write;
}

pub struct PositionEncoding<T> {
    phantom: PhantomData<T>,
}

impl<T> Default for PositionEncoding<T> {
    fn default() -> Self {
        PositionEncoding {
            phantom: PhantomData,
        }
    }
}

impl<T> FaceDecoder for PositionEncoding<T> {
    type Output = Vec<(Self::Index, Self::Face)>;
    type Index = SmallVec<[usize; 4]>;
    type Face = ();
}

impl<T> FaceElementDecoder for PositionEncoding<T> {}

impl<T> FacePropertyDecoder for PositionEncoding<T> {
    fn decode_face_properties<'a, I>(
        &self,
        _: &'a ElementDefinition,
        elements: I,
    ) -> Result<<Self as FaceDecoder>::Output, PlyError>
    where
        I: IntoIterator<Item = &'a Element>,
    {
        elements
            .into_iter()
            .map(|element| {
                let indices = element.list("vertex_index")?;
                Ok((indices, ()))
            })
            .collect()
    }
}

impl<T> VertexDecoder for PositionEncoding<T> {
    type Output = Vec<Self::Vertex>;
    type Vertex = T;
}

impl<T> VertexElementDecoder for PositionEncoding<T> {}

// TODO: Support two-dimensional spaces.
impl<T> VertexPropertyDecoder for PositionEncoding<T>
where
    T: EuclideanSpace + FiniteDimensional<N = U3>,
{
    fn decode_vertex_properties<'a, I>(
        &self,
        _: &'a ElementDefinition,
        elements: I,
    ) -> Result<<Self as VertexDecoder>::Output, PlyError>
    where
        I: IntoIterator<Item = &'a Element>,
    {
        elements
            .into_iter()
            .map(|element| {
                let vertex = EuclideanSpace::from_xyz(
                    element.scalar("x")?,
                    element.scalar("y")?,
                    element.scalar("z")?,
                );
                Ok(vertex)
            })
            .collect()
    }
}

pub fn decode_elements<'a, K>(
    definitions: &'a Header,
    elements: &'a Payload,
    name: K,
) -> Result<(&'a ElementDefinition, &'a Vec<Element>), PlyError>
where
    K: AsRef<str>,
{
    definitions
        .get(name.as_ref())
        .ok_or_else(|| PlyError::ElementNotFound)
        .map(|definition| (definition, elements.get(&definition.name).unwrap()))
}

fn num_cast_scalar<T, U>(value: T) -> Result<U, PlyError>
where
    T: NumCast,
    U: NumCast,
{
    cast::cast(value).ok_or_else(|| PlyError::Encoding)
}

fn num_cast_list<T, U, I>(values: Vec<T>) -> Result<I, PlyError>
where
    T: NumCast,
    U: NumCast,
    I: FromIterator<U>,
{
    values
        .into_iter()
        .map(num_cast_scalar)
        .collect::<Result<_, _>>()
}

#[cfg(test)]
mod tests {
    use nalgebra::Point3;

    use crate::buffer::MeshBuffer;
    use crate::encoding::ply::{FromPly, PositionEncoding};
    use crate::graph::MeshGraph;
    use crate::primitive::Tetragon;

    type E3 = Point3<f64>;

    #[test]
    fn decode_into_buffer() {
        let buffer = {
            let ply: &[u8] = include_bytes!("../../../data/cube.ply");
            MeshBuffer::<Tetragon<usize>, E3>::from_ply(PositionEncoding::<E3>::default(), ply)
                .unwrap()
                .0
        };
        assert_eq!(8, buffer.as_vertex_slice().len());
        assert_eq!(6, buffer.as_index_slice().len());
    }

    #[test]
    fn decode_into_graph() {
        let graph = {
            let ply: &[u8] = include_bytes!("../../../data/cube.ply");
            MeshGraph::<E3>::from_ply(PositionEncoding::<E3>::default(), ply)
                .unwrap()
                .0
        };
        assert_eq!(8, graph.vertex_count());
        assert_eq!(12, graph.edge_count());
        assert_eq!(6, graph.face_count());
    }
}
<|endoftext|>


<commit_before>use ast::*;
use ast::Stmt::*;
use ast::visit::*;
use ctxt::{Context, Fct, FctId, FctParent, FctSrc, GlobalId, NodeMap};
use error::msg::Msg;
use semck;
use sym::Sym;
use ty::BuiltinType;

pub fn check<'a, 'ast>(ctxt: &Context<'ast>, map_global_defs: &NodeMap<GlobalId>) {
    println!("start fctdefck");
    for fct in &ctxt.fcts {
        let mut fct = fct.borrow_mut();
        let ast = fct.ast;

        if !(fct.is_src() || fct.kind.is_definition()) {
            continue;
        }

        ctxt.sym.borrow_mut().push_level();

        match fct.parent {
            FctParent::Class(owner_class) => {
                let cls = ctxt.classes[owner_class].borrow();

                if fct.has_self() {
                    fct.param_types.push(cls.ty);
                }

                let mut type_param_id = 0;

                for &name in &cls.type_params {
                    let sym = Sym::SymTypeParam(type_param_id.into());
                    ctxt.sym.borrow_mut().insert(name, sym);
                    type_param_id += 1;
                }
            }

            FctParent::Impl(impl_id) => {
                let ximpl = ctxt.impls[impl_id].borrow();
                let cls = ctxt.classes[ximpl.cls_id()].borrow();
                fct.param_types.push(cls.ty);
            }

            FctParent::Trait(_) => {
                fct.param_types.push(BuiltinType::This);
            }

            _ => {}
        }

        for p in &ast.params {
            let ty = semck::read_type(ctxt, &p.data_type).unwrap_or(BuiltinType::Unit);

            if ty == BuiltinType::This && !fct.in_trait() {
                ctxt.diag.borrow_mut().report(p.data_type.pos(), Msg::SelfTypeUnavailable);
            }

            fct.param_types.push(ty);

            if fct.is_src() {
                let src = fct.src();
                let mut src = src.lock().unwrap();

                let var = *src.map_vars.get(p.id).unwrap();
                src.vars[var].ty = ty;
            }
        }

        if let Some(ret) = ast.return_type.as_ref() {
            let ty = semck::read_type(ctxt, ret).unwrap_or(BuiltinType::Unit);

            if ty == BuiltinType::This && !fct.in_trait() {
                ctxt.diag.borrow_mut().report(ret.pos(), Msg::SelfTypeUnavailable);
            }

            fct.return_type = ty;
        }

        fct.initialized = true;

        match fct.parent {
            FctParent::Class(clsid) => {
                let cls = ctxt.classes[clsid].borrow();
                check_against_methods(ctxt, cls.ty, &*fct, &cls.methods);
            }

            FctParent::Trait(traitid) => {
                let xtrait = ctxt.traits[traitid].borrow();
                let ty = BuiltinType::Trait(traitid);
                check_against_methods(ctxt, ty, &*fct, &xtrait.methods);
            }

            FctParent::Impl(implid) => {
                let ximpl = ctxt.impls[implid].borrow();
                let ty = BuiltinType::Trait(ximpl.trait_id());
                check_against_methods(ctxt, ty, &*fct, &ximpl.methods);
            }

            _ => {}
        }

        if !fct.is_src() {
            continue;
        }

        let src = fct.src();
        let mut src = src.lock().unwrap();

        let mut defck = FctDefCheck {
            ctxt: ctxt,
            fct: &mut *fct,
            src: &mut src,
            ast: ast,
            current_type: BuiltinType::Unit,
            map_global_defs: map_global_defs,
        };

        defck.check();

        ctxt.sym.borrow_mut().pop_level();
    }
}

fn check_against_methods(ctxt: &Context, ty: BuiltinType, fct: &Fct, methods: &[FctId]) {
    for &method in methods {
        if method == fct.id {
            continue;
        }

        let method = ctxt.fcts[method].borrow();

        if method.initialized && method.name == fct.name &&
           method.params_with_self() == fct.params_with_self() {
            let cls_name = ty.name(ctxt);
            let param_names = method.params_without_self()
                .iter()
                .map(|a| a.name(ctxt))
                .collect::<Vec<String>>();
            let method_name = ctxt.interner.str(method.name).to_string();

            let msg = Msg::MethodExists(cls_name, method_name, param_names, method.pos);
            ctxt.diag.borrow_mut().report(fct.ast.pos, msg);
            return;
        }
    }
}

struct FctDefCheck<'a, 'ast: 'a> {
    ctxt: &'a Context<'ast>,
    fct: &'a mut Fct<'ast>,
    src: &'a mut FctSrc<'ast>,
    ast: &'ast Function,
    current_type: BuiltinType,
    map_global_defs: &'a NodeMap<GlobalId>,
}

impl<'a, 'ast> FctDefCheck<'a, 'ast> {
    fn check(&mut self) {
        self.visit_fct(self.ast);
    }
}

impl<'a, 'ast> Visitor<'ast> for FctDefCheck<'a, 'ast> {
    fn visit_fct(&mut self, f: &'ast Function) {
        self.visit_stmt(f.block());
    }

    fn visit_global(&mut self, g: &'ast Global) {
        let global_id = *self.map_global_defs.get(g.id).unwrap();
        let mut global = self.ctxt.globals[global_id].borrow_mut();

        self.visit_type(&g.data_type);
        global.ty = self.current_type;
    }

    fn visit_stmt(&mut self, s: &'ast Stmt) {
        match *s {
            StmtVar(ref var) => {
                if let Some(ref data_type) = var.data_type {
                    self.visit_type(data_type);

                    let varid = *self.src
                                     .map_vars
                                     .get(var.id)
                                     .unwrap();
                    self.src.vars[varid].ty = self.current_type;
                }

                if let Some(ref expr) = var.expr {
                    visit::walk_expr(self, expr);
                }
            }

            StmtDo(ref try) => {
                for catch in &try.catch_blocks {
                    self.visit_type(&catch.data_type);
                    self.src.set_ty(catch.id, self.current_type);

                    let var = *self.src
                                   .map_vars
                                   .get(catch.id)
                                   .unwrap();
                    self.src.vars[var].ty = self.current_type;

                    if !self.current_type.reference_type() {
                        let ty = self.current_type.name(self.ctxt);
                        self.ctxt
                            .diag
                            .borrow_mut()
                            .report(catch.data_type.pos(), Msg::ReferenceTypeExpected(ty));
                    }
                }

                if try.catch_blocks.is_empty() && try.finally_block.is_none() {
                    self.ctxt
                        .diag
                        .borrow_mut()
                        .report(try.pos, Msg::CatchOrFinallyExpected);
                }

                visit::walk_stmt(self, s);
            }

            _ => visit::walk_stmt(self, s),
        }
    }

    fn visit_type(&mut self, t: &'ast Type) {
        self.current_type = semck::read_type(self.ctxt, t).unwrap_or(BuiltinType::Unit);
    }
}

#[cfg(test)]
mod tests {
    use error::msg::Msg;
    use semck::tests::*;

    #[test]
    fn self_param() {
        err("fun foo(x: Self) {}", pos(1, 12), Msg::SelfTypeUnavailable);
    }

    #[test]
    fn self_return_type() {
        err("fun foo() -> Self {}", pos(1, 14), Msg::SelfTypeUnavailable);
    }
}
<commit_msg>fix tests, remove println<commit_after>use ast::*;
use ast::Stmt::*;
use ast::visit::*;
use ctxt::{Context, Fct, FctId, FctParent, FctSrc, GlobalId, NodeMap};
use error::msg::Msg;
use semck;
use sym::Sym;
use ty::BuiltinType;

pub fn check<'a, 'ast>(ctxt: &Context<'ast>, map_global_defs: &NodeMap<GlobalId>) {
    for fct in &ctxt.fcts {
        let mut fct = fct.borrow_mut();
        let ast = fct.ast;

        if !(fct.is_src() || fct.kind.is_definition()) {
            continue;
        }

        ctxt.sym.borrow_mut().push_level();

        match fct.parent {
            FctParent::Class(owner_class) => {
                let cls = ctxt.classes[owner_class].borrow();

                if fct.has_self() {
                    fct.param_types.push(cls.ty);
                }

                let mut type_param_id = 0;

                for &name in &cls.type_params {
                    let sym = Sym::SymTypeParam(type_param_id.into());
                    ctxt.sym.borrow_mut().insert(name, sym);
                    type_param_id += 1;
                }
            }

            FctParent::Impl(impl_id) => {
                let ximpl = ctxt.impls[impl_id].borrow();
                let cls = ctxt.classes[ximpl.cls_id()].borrow();
                fct.param_types.push(cls.ty);
            }

            FctParent::Trait(_) => {
                fct.param_types.push(BuiltinType::This);
            }

            _ => {}
        }

        for p in &ast.params {
            let ty = semck::read_type(ctxt, &p.data_type).unwrap_or(BuiltinType::Unit);

            if ty == BuiltinType::This && !fct.in_trait() {
                ctxt.diag.borrow_mut().report(p.data_type.pos(), Msg::SelfTypeUnavailable);
            }

            fct.param_types.push(ty);

            if fct.is_src() {
                let src = fct.src();
                let mut src = src.lock().unwrap();

                let var = *src.map_vars.get(p.id).unwrap();
                src.vars[var].ty = ty;
            }
        }

        if let Some(ret) = ast.return_type.as_ref() {
            let ty = semck::read_type(ctxt, ret).unwrap_or(BuiltinType::Unit);

            if ty == BuiltinType::This && !fct.in_trait() {
                ctxt.diag.borrow_mut().report(ret.pos(), Msg::SelfTypeUnavailable);
            }

            fct.return_type = ty;
        }

        fct.initialized = true;

        match fct.parent {
            FctParent::Class(clsid) => {
                let cls = ctxt.classes[clsid].borrow();
                check_against_methods(ctxt, cls.ty, &*fct, &cls.methods);
            }

            FctParent::Trait(traitid) => {
                let xtrait = ctxt.traits[traitid].borrow();
                let ty = BuiltinType::Trait(traitid);
                check_against_methods(ctxt, ty, &*fct, &xtrait.methods);
            }

            FctParent::Impl(implid) => {
                let ximpl = ctxt.impls[implid].borrow();
                let ty = BuiltinType::Trait(ximpl.trait_id());
                check_against_methods(ctxt, ty, &*fct, &ximpl.methods);
            }

            _ => {}
        }

        if !fct.is_src() {
            continue;
        }

        let src = fct.src();
        let mut src = src.lock().unwrap();

        let mut defck = FctDefCheck {
            ctxt: ctxt,
            fct: &mut *fct,
            src: &mut src,
            ast: ast,
            current_type: BuiltinType::Unit,
            map_global_defs: map_global_defs,
        };

        defck.check();

        ctxt.sym.borrow_mut().pop_level();
    }
}

fn check_against_methods(ctxt: &Context, ty: BuiltinType, fct: &Fct, methods: &[FctId]) {
    for &method in methods {
        if method == fct.id {
            continue;
        }

        let method = ctxt.fcts[method].borrow();

        if method.initialized && method.name == fct.name &&
           method.params_with_self() == fct.params_with_self() {
            let cls_name = ty.name(ctxt);
            let param_names = method.params_without_self()
                .iter()
                .map(|a| a.name(ctxt))
                .collect::<Vec<String>>();
            let method_name = ctxt.interner.str(method.name).to_string();

            let msg = Msg::MethodExists(cls_name, method_name, param_names, method.pos);
            ctxt.diag.borrow_mut().report(fct.ast.pos, msg);
            return;
        }
    }
}

struct FctDefCheck<'a, 'ast: 'a> {
    ctxt: &'a Context<'ast>,
    fct: &'a mut Fct<'ast>,
    src: &'a mut FctSrc<'ast>,
    ast: &'ast Function,
    current_type: BuiltinType,
    map_global_defs: &'a NodeMap<GlobalId>,
}

impl<'a, 'ast> FctDefCheck<'a, 'ast> {
    fn check(&mut self) {
        self.visit_fct(self.ast);
    }
}

impl<'a, 'ast> Visitor<'ast> for FctDefCheck<'a, 'ast> {
    fn visit_fct(&mut self, f: &'ast Function) {
        self.visit_stmt(f.block());
    }

    fn visit_global(&mut self, g: &'ast Global) {
        let global_id = *self.map_global_defs.get(g.id).unwrap();
        let mut global = self.ctxt.globals[global_id].borrow_mut();

        self.visit_type(&g.data_type);
        global.ty = self.current_type;
    }

    fn visit_stmt(&mut self, s: &'ast Stmt) {
        match *s {
            StmtVar(ref var) => {
                if let Some(ref data_type) = var.data_type {
                    self.visit_type(data_type);

                    let varid = *self.src
                                     .map_vars
                                     .get(var.id)
                                     .unwrap();
                    self.src.vars[varid].ty = self.current_type;
                }

                if let Some(ref expr) = var.expr {
                    visit::walk_expr(self, expr);
                }
            }

            StmtDo(ref try) => {
                for catch in &try.catch_blocks {
                    self.visit_type(&catch.data_type);
                    self.src.set_ty(catch.id, self.current_type);

                    let var = *self.src
                                   .map_vars
                                   .get(catch.id)
                                   .unwrap();
                    self.src.vars[var].ty = self.current_type;

                    if !self.current_type.reference_type() {
                        let ty = self.current_type.name(self.ctxt);
                        self.ctxt
                            .diag
                            .borrow_mut()
                            .report(catch.data_type.pos(), Msg::ReferenceTypeExpected(ty));
                    }
                }

                if try.catch_blocks.is_empty() && try.finally_block.is_none() {
                    self.ctxt
                        .diag
                        .borrow_mut()
                        .report(try.pos, Msg::CatchOrFinallyExpected);
                }

                visit::walk_stmt(self, s);
            }

            _ => visit::walk_stmt(self, s),
        }
    }

    fn visit_type(&mut self, t: &'ast Type) {
        self.current_type = semck::read_type(self.ctxt, t).unwrap_or(BuiltinType::Unit);
    }
}

#[cfg(test)]
mod tests {
    use error::msg::Msg;
    use semck::tests::*;

    #[test]
    fn self_param() {
        err("fun foo(x: Self) {}", pos(1, 12), Msg::SelfTypeUnavailable);
    }

    #[test]
    fn self_return_type() {
        err("fun foo() -> Self {}", pos(1, 14), Msg::SelfTypeUnavailable);
    }
}
<|endoftext|>


<commit_before>// binary.rs

pub use BASE16       = base16::BASE16;
pub use BASE32       = base32::BASE32_STD;
pub use BASE64       = base64::BASE64_STD;
pub use BASE32_HEX   = base32::BASE32_HEX;
pub use BASE64_URL   = base64::BASE64_URL;
pub use Base16Writer = base16::Base16Writer;
pub use Base32Writer = base32::Base32Writer;
pub use Base64Writer = base64::Base64Writer;
pub use Base32Reader = base32::Base32Reader;
pub use Base64Reader = base64::Base64Reader;

pub trait Encode {
    fn encode(&self, buf: &[const u8]) -> ~[u8];
}

pub trait Decode {
    fn decode(&self, buf: &[const u8]) -> ~[u8];
}

pub trait Convert {
    static fn convert(buf: &[const u8], to: self, from: self) -> ~[u8];
}

pub trait Codec : Encode, Decode, Convert {
}

// `end` indicates whether the Decode method have encountered paddings or not.
struct DecodeResult {
    end: bool,
    ndecoded: uint,
}

trait BinaryEncoder {
    fn encode(&self, dst: &[mut u8], src: &[const u8]);
    fn encoded_len(&self, src_length: uint) -> uint;
    fn encode_bytes(&self, src: &[const u8]) -> ~[u8];
}

trait BinaryDecoder {
    fn decode(&self, dst: &[mut u8], src: &[const u8]) -> DecodeResult;
    fn decoded_len(&self, src_length: uint) -> uint;
    fn decode_bytes(&self, src: &[const u8]) -> ~[u8];
}

pub enum Binary {
    Base16,
    Base32,
    Base64,
    Base32Hex,
    Base64Url,
}

pub impl Binary : Encode {
    fn encode(&self, buf: &[const u8]) -> ~[u8] {
        match *self {
            Base16    => base16::encode(buf),
            Base32    => base32::encode(buf),
            Base64    => base64::encode(buf),
            Base32Hex => base32::hex_encode(buf),
            Base64Url => base64::urlsafe_encode(buf)
        }
    }
}

pub impl Binary : Decode {
    fn decode(&self, buf: &[const u8]) -> ~[u8] {
        match *self {
            Base16    => base16::decode(buf),
            Base32    => base32::decode(buf),
            Base64    => base64::decode(buf),
            Base32Hex => base32::hex_decode(buf),
            Base64Url => base64::urlsafe_decode(buf)
        }
    }
}

pub impl Binary : Convert {
    static fn convert(buf: &[const u8], to: Binary, from: Binary) -> ~[u8] {
        let buf = from.decode(buf);
        let buf = to.encode(buf);
        move buf
    }
}

pub impl Binary : Codec;
<commit_msg>Update for language changes<commit_after>// binary.rs

pub use BASE16       = base16::BASE16;
pub use BASE32       = base32::BASE32_STD;
pub use BASE64       = base64::BASE64_STD;
pub use BASE32_HEX   = base32::BASE32_HEX;
pub use BASE64_URL   = base64::BASE64_URL;
pub use Base16Writer = base16::Base16Writer;
pub use Base32Writer = base32::Base32Writer;
pub use Base64Writer = base64::Base64Writer;
pub use Base32Reader = base32::Base32Reader;
pub use Base64Reader = base64::Base64Reader;

pub trait Encode {
    fn encode(&self, buf: &[const u8]) -> ~[u8];
}

pub trait Decode {
    fn decode(&self, buf: &[const u8]) -> ~[u8];
}

pub trait Convert {
    static fn convert(buf: &[const u8], to: self, from: self) -> ~[u8];
}

pub trait Codec : Encode Decode Convert {
}

// `end` indicates whether the Decode method have encountered paddings or not.
struct DecodeResult {
    end: bool,
    ndecoded: uint,
}

trait BinaryEncoder {
    fn encode(&self, dst: &[mut u8], src: &[const u8]);
    fn encoded_len(&self, src_length: uint) -> uint;
    fn encode_bytes(&self, src: &[const u8]) -> ~[u8];
}

trait BinaryDecoder {
    fn decode(&self, dst: &[mut u8], src: &[const u8]) -> DecodeResult;
    fn decoded_len(&self, src_length: uint) -> uint;
    fn decode_bytes(&self, src: &[const u8]) -> ~[u8];
}

pub enum Binary {
    Base16,
    Base32,
    Base64,
    Base32Hex,
    Base64Url,
}

pub impl Binary : Encode {
    fn encode(&self, buf: &[const u8]) -> ~[u8] {
        match *self {
            Base16    => base16::encode(buf),
            Base32    => base32::encode(buf),
            Base64    => base64::encode(buf),
            Base32Hex => base32::hex_encode(buf),
            Base64Url => base64::urlsafe_encode(buf)
        }
    }
}

pub impl Binary : Decode {
    fn decode(&self, buf: &[const u8]) -> ~[u8] {
        match *self {
            Base16    => base16::decode(buf),
            Base32    => base32::decode(buf),
            Base64    => base64::decode(buf),
            Base32Hex => base32::hex_decode(buf),
            Base64Url => base64::urlsafe_decode(buf)
        }
    }
}

pub impl Binary : Convert {
    static fn convert(buf: &[const u8], to: Binary, from: Binary) -> ~[u8] {
        let buf = from.decode(buf);
        let buf = to.encode(buf);
        move buf
    }
}

pub impl Binary : Codec;
<|endoftext|>


<commit_before>use structopt::StructOpt;

mod cmdline;
mod commands;
mod core;
mod entrypoint;
mod logging;
mod pathenv;
mod terminal;
mod tmux;
mod utils;
mod version;
mod zsh;

fn main() {
    let opt = cmdline::Opt::from_args();
    logging::setup(opt.verbosity);
    log::debug!("options: {:#?}", opt);
    match opt.cmd {
        Some(ref sub_command) => {
            use cmdline::SubCommand;
            match *sub_command {
                SubCommand::Info => commands::info::display_info(),
                SubCommand::Completions(ref opt) => commands::completion::completion(opt),
            }
        }
        None => entrypoint::main(opt),
    }
}
<commit_msg>Remove unnecessary ref<commit_after>use structopt::StructOpt;

mod cmdline;
mod commands;
mod core;
mod entrypoint;
mod logging;
mod pathenv;
mod terminal;
mod tmux;
mod utils;
mod version;
mod zsh;

fn main() {
    let opt = cmdline::Opt::from_args();
    logging::setup(opt.verbosity);
    log::debug!("options: {:#?}", opt);
    match opt.cmd {
        Some(sub_command) => {
            use cmdline::SubCommand;
            match sub_command {
                SubCommand::Info => commands::info::display_info(),
                SubCommand::Completions(ref x) => commands::completion::completion(x),
            }
        }
        None => entrypoint::main(opt),
    }
}
<|endoftext|>