<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::BlobBinding::BlobMethods;
use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull;
use dom::bindings::codegen::Bindings::FileReaderBinding::{self, FileReaderConstants, FileReaderMethods};
use dom::bindings::error::{Error, ErrorResult, Fallible};
use dom::bindings::global::GlobalRef;
use dom::bindings::inheritance::Castable;
use dom::bindings::js::{JS, MutNullableHeap, Root};
use dom::bindings::refcounted::Trusted;
use dom::bindings::reflector::{Reflectable, reflect_dom_object};
use dom::bindings::str::DOMString;
use dom::blob::Blob;
use dom::domexception::{DOMErrorName, DOMException};
use dom::event::{Event, EventBubbles, EventCancelable};
use dom::eventtarget::EventTarget;
use dom::progressevent::ProgressEvent;
use encoding::all::UTF_8;
use encoding::label::encoding_from_whatwg_label;
use encoding::types::{DecoderTrap, EncodingRef};
use hyper::mime::{Attr, Mime};
use rustc_serialize::base64::{CharacterSet, Config, Newline, ToBase64};
use script_runtime::ScriptThreadEventCategory::FileRead;
use script_runtime::{ScriptChan, CommonScriptMsg};
use script_thread::Runnable;
use std::cell::Cell;
use std::sync::Arc;
use string_cache::Atom;
use util::thread::spawn_named;

#[derive(PartialEq, Clone, Copy, JSTraceable, HeapSizeOf)]
pub enum FileReaderFunction {
    ReadAsText,
    ReadAsDataUrl,
}

pub type TrustedFileReader = Trusted<FileReader>;

#[derive(Clone, HeapSizeOf)]
pub struct ReadMetaData {
    pub blobtype: String,
    pub label: Option<String>,
    pub function: FileReaderFunction
}

impl ReadMetaData {
    pub fn new(blobtype: String,
               label: Option<String>, function: FileReaderFunction) -> ReadMetaData {
        ReadMetaData {
            blobtype: blobtype,
            label: label,
            function: function,
        }
    }
}

#[derive(PartialEq, Clone, Copy, JSTraceable, HeapSizeOf)]
pub struct GenerationId(u32);

#[repr(u16)]
#[derive(Copy, Clone, Debug, PartialEq, JSTraceable, HeapSizeOf)]
pub enum FileReaderReadyState {
    Empty = FileReaderConstants::EMPTY,
    Loading = FileReaderConstants::LOADING,
    Done = FileReaderConstants::DONE,
}

#[dom_struct]
pub struct FileReader {
    eventtarget: EventTarget,
    ready_state: Cell<FileReaderReadyState>,
    error: MutNullableHeap<JS<DOMException>>,
    result: DOMRefCell<Option<DOMString>>,
    generation_id: Cell<GenerationId>,
}

impl FileReader {
    pub fn new_inherited() -> FileReader {
        FileReader {
            eventtarget: EventTarget::new_inherited(),
            ready_state: Cell::new(FileReaderReadyState::Empty),
            error: MutNullableHeap::new(None),
            result: DOMRefCell::new(None),
            generation_id: Cell::new(GenerationId(0)),
        }
    }

    pub fn new(global: GlobalRef) -> Root<FileReader> {
        reflect_dom_object(box FileReader::new_inherited(),
                           global, FileReaderBinding::Wrap)
    }

    pub fn Constructor(global: GlobalRef) -> Fallible<Root<FileReader>> {
        Ok(FileReader::new(global))
    }

    //https://w3c.github.io/FileAPI/#dfn-error-steps
    pub fn process_read_error(filereader: TrustedFileReader, gen_id: GenerationId, error: DOMErrorName) {
        let fr = filereader.root();

        macro_rules! return_on_abort(
            () => (
                if gen_id != fr.generation_id.get() {
                    return
                }
            );
        );

        return_on_abort!();
        // Step 1
        fr.change_ready_state(FileReaderReadyState::Done);
        *fr.result.borrow_mut() = None;

        let global = fr.r().global();
        let exception = DOMException::new(global.r(), error);
        fr.error.set(Some(&exception));

        fr.dispatch_progress_event(atom!("error"), 0, None);
        return_on_abort!();
        // Step 3
        fr.dispatch_progress_event(atom!("loadend"), 0, None);
        return_on_abort!();
        // Step 4
        fr.terminate_ongoing_reading();
    }

    // https://w3c.github.io/FileAPI/#dfn-readAsText
    pub fn process_read_data(filereader: TrustedFileReader, gen_id: GenerationId) {
        let fr = filereader.root();

        macro_rules! return_on_abort(
            () => (
                if gen_id != fr.generation_id.get() {
                    return
                }
            );
        );
        return_on_abort!();
        //FIXME Step 7 send current progress
        fr.dispatch_progress_event(atom!("progress"), 0, None);
    }

    // https://w3c.github.io/FileAPI/#dfn-readAsText
    pub fn process_read(filereader: TrustedFileReader, gen_id: GenerationId) {
        let fr = filereader.root();

        macro_rules! return_on_abort(
            () => (
                if gen_id != fr.generation_id.get() {
                    return
                }
            );
        );
        return_on_abort!();
        // Step 6
        fr.dispatch_progress_event(atom!("loadstart"), 0, None);
    }

    // https://w3c.github.io/FileAPI/#dfn-readAsText
    pub fn process_read_eof(filereader: TrustedFileReader, gen_id: GenerationId,
                            data: ReadMetaData, blob_contents: Arc<Vec<u8>>) {
        let fr = filereader.root();

        macro_rules! return_on_abort(
            () => (
                if gen_id != fr.generation_id.get() {
                    return
                }
            );
        );

        return_on_abort!();
        // Step 8.1
        fr.change_ready_state(FileReaderReadyState::Done);
        // Step 8.2

        let output = match data.function {
            FileReaderFunction::ReadAsDataUrl =>
                FileReader::perform_readasdataurl(data, &blob_contents),
            FileReaderFunction::ReadAsText =>
                FileReader::perform_readastext(data, &blob_contents),
        };

        *fr.result.borrow_mut() = Some(output);

        // Step 8.3
        fr.dispatch_progress_event(atom!("load"), 0, None);
        return_on_abort!();
        // Step 8.4
        if fr.ready_state.get() != FileReaderReadyState::Loading {
            fr.dispatch_progress_event(atom!("loadend"), 0, None);
        }
        return_on_abort!();
        // Step 9
        fr.terminate_ongoing_reading();
    }

    // https://w3c.github.io/FileAPI/#dfn-readAsText
    fn perform_readastext(data: ReadMetaData, blob_bytes: &[u8])
        -> DOMString {
        let blob_label = &data.label;
        let blob_type = &data.blobtype;

        //https://w3c.github.io/FileAPI/#encoding-determination
        // Steps 1 & 2 & 3
        let mut encoding = blob_label.as_ref()
            .map(|string| &**string)
            .and_then(encoding_from_whatwg_label);

        // Step 4 & 5
        encoding = encoding.or_else(|| {
            let resultmime = blob_type.parse::<Mime>().ok();
            resultmime.and_then(|Mime(_, _, ref parameters)| {
                parameters.iter()
                    .find(|&&(ref k, _)| &Attr::Charset == k)
                    .and_then(|&(_, ref v)| encoding_from_whatwg_label(&v.to_string()))
            })
        });

        // Step 6
        let enc = encoding.unwrap_or(UTF_8 as EncodingRef);

        let convert = blob_bytes;
        // Step 7
        let output = enc.decode(convert, DecoderTrap::Replace).unwrap();
        DOMString::from(output)
    }

    //https://w3c.github.io/FileAPI/#dfn-readAsDataURL
    fn perform_readasdataurl(data: ReadMetaData, bytes: &[u8])
        -> DOMString {
        let config = Config {
            char_set: CharacterSet::UrlSafe,
            newline: Newline::LF,
            pad: true,
            line_length: None
        };
        let base64 = bytes.to_base64(config);

        let output = if data.blobtype.is_empty() {
            format!("data:base64,{}", base64)
        } else {
            format!("data:{};base64,{}", data.blobtype, base64)
        };

        DOMString::from(output)
    }
}

impl FileReaderMethods for FileReader {
    // https://w3c.github.io/FileAPI/#dfn-onloadstart
    event_handler!(loadstart, GetOnloadstart, SetOnloadstart);

    // https://w3c.github.io/FileAPI/#dfn-onprogress
    event_handler!(progress, GetOnprogress, SetOnprogress);

    // https://w3c.github.io/FileAPI/#dfn-onload
    event_handler!(load, GetOnload, SetOnload);

    // https://w3c.github.io/FileAPI/#dfn-onabort
    event_handler!(abort, GetOnabort, SetOnabort);

    // https://w3c.github.io/FileAPI/#dfn-onerror
    event_handler!(error, GetOnerror, SetOnerror);

    // https://w3c.github.io/FileAPI/#dfn-onloadend
    event_handler!(loadend, GetOnloadend, SetOnloadend);

    //TODO https://w3c.github.io/FileAPI/#dfn-readAsArrayBuffer
    // https://w3c.github.io/FileAPI/#dfn-readAsDataURL
    fn ReadAsDataURL(&self, blob: &Blob) -> ErrorResult {
        self.read(FileReaderFunction::ReadAsDataUrl, blob, None)
    }

    // https://w3c.github.io/FileAPI/#dfn-readAsText
    fn ReadAsText(&self, blob: &Blob, label: Option<DOMString>) -> ErrorResult {
        self.read(FileReaderFunction::ReadAsText, blob, label)
    }

    // https://w3c.github.io/FileAPI/#dfn-abort
    fn Abort(&self) {
        // Step 2
        if self.ready_state.get() == FileReaderReadyState::Loading {
            self.change_ready_state(FileReaderReadyState::Done);
        }
        // Steps 1 & 3
        *self.result.borrow_mut() = None;

        let global = self.global();
        let exception = DOMException::new(global.r(), DOMErrorName::AbortError);
        self.error.set(Some(&exception));

        self.terminate_ongoing_reading();
        // Steps 5 & 6
        self.dispatch_progress_event(atom!("abort"), 0, None);
        self.dispatch_progress_event(atom!("loadend"), 0, None);
    }

    // https://w3c.github.io/FileAPI/#dfn-error
    fn GetError(&self) -> Option<Root<DOMException>> {
        self.error.get()
    }

    // https://w3c.github.io/FileAPI/#dfn-result
    fn GetResult(&self) -> Option<DOMString> {
        self.result.borrow().clone()
    }

    // https://w3c.github.io/FileAPI/#dfn-readyState
    fn ReadyState(&self) -> u16 {
        self.ready_state.get() as u16
    }
}

impl FileReader {
    fn dispatch_progress_event(&self, type_: Atom, loaded: u64, total: Option<u64>) {
        let global = self.global();
        let progressevent = ProgressEvent::new(global.r(),
            type_, EventBubbles::DoesNotBubble, EventCancelable::NotCancelable,
            total.is_some(), loaded, total.unwrap_or(0));
        progressevent.upcast::<Event>().fire(self.upcast());
    }

    fn terminate_ongoing_reading(&self) {
        let GenerationId(prev_id) = self.generation_id.get();
        self.generation_id.set(GenerationId(prev_id + 1));
    }

    fn read(&self, function: FileReaderFunction, blob: &Blob, label: Option<DOMString>) -> ErrorResult {
        // Step 1
        if self.ready_state.get() == FileReaderReadyState::Loading {
            return Err(Error::InvalidState);
        }
        // Step 2
        if blob.IsClosed() {
            let global = self.global();
            let exception = DOMException::new(global.r(), DOMErrorName::InvalidStateError);
            self.error.set(Some(&exception));

            self.dispatch_progress_event(atom!("error"), 0, None);
            return Ok(());
        }

        // Step 3
        self.change_ready_state(FileReaderReadyState::Loading);

        // Step 4
        let blob_contents = Arc::new(blob.get_bytes().unwrap_or(vec![]));

        let type_ = blob.Type();

        let load_data = ReadMetaData::new(String::from(type_), label.map(String::from), function);

        let fr = Trusted::new(self);
        let gen_id = self.generation_id.get();

        let script_chan = self.global().r().file_reading_task_source();

        spawn_named("file reader async operation".to_owned(), move || {
            perform_annotated_read_operation(gen_id, load_data, blob_contents, fr, script_chan)
        });
        Ok(())
    }

    fn change_ready_state(&self, state: FileReaderReadyState) {
        self.ready_state.set(state);
    }
}

#[derive(Clone)]
pub enum FileReaderEvent {
    ProcessRead(TrustedFileReader, GenerationId),
    ProcessReadData(TrustedFileReader, GenerationId),
    ProcessReadError(TrustedFileReader, GenerationId, DOMErrorName),
    ProcessReadEOF(TrustedFileReader, GenerationId, ReadMetaData, Arc<Vec<u8>>)
}

impl Runnable for FileReaderEvent {
    fn handler(self: Box<FileReaderEvent>) {
        let file_reader_event = *self;
        match file_reader_event {
            FileReaderEvent::ProcessRead(filereader, gen_id) => {
                FileReader::process_read(filereader, gen_id);
            },
            FileReaderEvent::ProcessReadData(filereader, gen_id) => {
                FileReader::process_read_data(filereader, gen_id);
            },
            FileReaderEvent::ProcessReadError(filereader, gen_id, error) => {
                FileReader::process_read_error(filereader, gen_id, error);
            },
            FileReaderEvent::ProcessReadEOF(filereader, gen_id, data, blob_contents) => {
                FileReader::process_read_eof(filereader, gen_id, data, blob_contents);
            }
        }
    }
}

// https://w3c.github.io/FileAPI/#thread-read-operation
fn perform_annotated_read_operation(gen_id: GenerationId, data: ReadMetaData, blob_contents: Arc<Vec<u8>>,
    filereader: TrustedFileReader, script_chan: Box<ScriptChan + Send>) {
    let chan = &script_chan;
    // Step 4
    let thread = box FileReaderEvent::ProcessRead(filereader.clone(), gen_id);
    chan.send(CommonScriptMsg::RunnableMsg(FileRead, thread)).unwrap();

    let thread = box FileReaderEvent::ProcessReadData(filereader.clone(), gen_id);
    chan.send(CommonScriptMsg::RunnableMsg(FileRead, thread)).unwrap();

    let thread = box FileReaderEvent::ProcessReadEOF(filereader, gen_id, data, blob_contents);
    chan.send(CommonScriptMsg::RunnableMsg(FileRead, thread)).unwrap();
}
<commit_msg>Auto merge of #12373 - cynicaldevil:impl-runnable, r=KiChjang<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::BlobBinding::BlobMethods;
use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull;
use dom::bindings::codegen::Bindings::FileReaderBinding::{self, FileReaderConstants, FileReaderMethods};
use dom::bindings::error::{Error, ErrorResult, Fallible};
use dom::bindings::global::GlobalRef;
use dom::bindings::inheritance::Castable;
use dom::bindings::js::{JS, MutNullableHeap, Root};
use dom::bindings::refcounted::Trusted;
use dom::bindings::reflector::{Reflectable, reflect_dom_object};
use dom::bindings::str::DOMString;
use dom::blob::Blob;
use dom::domexception::{DOMErrorName, DOMException};
use dom::event::{Event, EventBubbles, EventCancelable};
use dom::eventtarget::EventTarget;
use dom::progressevent::ProgressEvent;
use encoding::all::UTF_8;
use encoding::label::encoding_from_whatwg_label;
use encoding::types::{DecoderTrap, EncodingRef};
use hyper::mime::{Attr, Mime};
use rustc_serialize::base64::{CharacterSet, Config, Newline, ToBase64};
use script_runtime::ScriptThreadEventCategory::FileRead;
use script_runtime::{ScriptChan, CommonScriptMsg};
use script_thread::Runnable;
use std::cell::Cell;
use std::sync::Arc;
use string_cache::Atom;
use util::thread::spawn_named;

#[derive(PartialEq, Clone, Copy, JSTraceable, HeapSizeOf)]
pub enum FileReaderFunction {
    ReadAsText,
    ReadAsDataUrl,
}

pub type TrustedFileReader = Trusted<FileReader>;

#[derive(Clone, HeapSizeOf)]
pub struct ReadMetaData {
    pub blobtype: String,
    pub label: Option<String>,
    pub function: FileReaderFunction
}

impl ReadMetaData {
    pub fn new(blobtype: String,
               label: Option<String>, function: FileReaderFunction) -> ReadMetaData {
        ReadMetaData {
            blobtype: blobtype,
            label: label,
            function: function,
        }
    }
}

#[derive(PartialEq, Clone, Copy, JSTraceable, HeapSizeOf)]
pub struct GenerationId(u32);

#[repr(u16)]
#[derive(Copy, Clone, Debug, PartialEq, JSTraceable, HeapSizeOf)]
pub enum FileReaderReadyState {
    Empty = FileReaderConstants::EMPTY,
    Loading = FileReaderConstants::LOADING,
    Done = FileReaderConstants::DONE,
}

#[dom_struct]
pub struct FileReader {
    eventtarget: EventTarget,
    ready_state: Cell<FileReaderReadyState>,
    error: MutNullableHeap<JS<DOMException>>,
    result: DOMRefCell<Option<DOMString>>,
    generation_id: Cell<GenerationId>,
}

impl FileReader {
    pub fn new_inherited() -> FileReader {
        FileReader {
            eventtarget: EventTarget::new_inherited(),
            ready_state: Cell::new(FileReaderReadyState::Empty),
            error: MutNullableHeap::new(None),
            result: DOMRefCell::new(None),
            generation_id: Cell::new(GenerationId(0)),
        }
    }

    pub fn new(global: GlobalRef) -> Root<FileReader> {
        reflect_dom_object(box FileReader::new_inherited(),
                           global, FileReaderBinding::Wrap)
    }

    pub fn Constructor(global: GlobalRef) -> Fallible<Root<FileReader>> {
        Ok(FileReader::new(global))
    }

    //https://w3c.github.io/FileAPI/#dfn-error-steps
    pub fn process_read_error(filereader: TrustedFileReader, gen_id: GenerationId, error: DOMErrorName) {
        let fr = filereader.root();

        macro_rules! return_on_abort(
            () => (
                if gen_id != fr.generation_id.get() {
                    return
                }
            );
        );

        return_on_abort!();
        // Step 1
        fr.change_ready_state(FileReaderReadyState::Done);
        *fr.result.borrow_mut() = None;

        let global = fr.r().global();
        let exception = DOMException::new(global.r(), error);
        fr.error.set(Some(&exception));

        fr.dispatch_progress_event(atom!("error"), 0, None);
        return_on_abort!();
        // Step 3
        fr.dispatch_progress_event(atom!("loadend"), 0, None);
        return_on_abort!();
        // Step 4
        fr.terminate_ongoing_reading();
    }

    // https://w3c.github.io/FileAPI/#dfn-readAsText
    pub fn process_read_data(filereader: TrustedFileReader, gen_id: GenerationId) {
        let fr = filereader.root();

        macro_rules! return_on_abort(
            () => (
                if gen_id != fr.generation_id.get() {
                    return
                }
            );
        );
        return_on_abort!();
        //FIXME Step 7 send current progress
        fr.dispatch_progress_event(atom!("progress"), 0, None);
    }

    // https://w3c.github.io/FileAPI/#dfn-readAsText
    pub fn process_read(filereader: TrustedFileReader, gen_id: GenerationId) {
        let fr = filereader.root();

        macro_rules! return_on_abort(
            () => (
                if gen_id != fr.generation_id.get() {
                    return
                }
            );
        );
        return_on_abort!();
        // Step 6
        fr.dispatch_progress_event(atom!("loadstart"), 0, None);
    }

    // https://w3c.github.io/FileAPI/#dfn-readAsText
    pub fn process_read_eof(filereader: TrustedFileReader, gen_id: GenerationId,
                            data: ReadMetaData, blob_contents: Arc<Vec<u8>>) {
        let fr = filereader.root();

        macro_rules! return_on_abort(
            () => (
                if gen_id != fr.generation_id.get() {
                    return
                }
            );
        );

        return_on_abort!();
        // Step 8.1
        fr.change_ready_state(FileReaderReadyState::Done);
        // Step 8.2

        let output = match data.function {
            FileReaderFunction::ReadAsDataUrl =>
                FileReader::perform_readasdataurl(data, &blob_contents),
            FileReaderFunction::ReadAsText =>
                FileReader::perform_readastext(data, &blob_contents),
        };

        *fr.result.borrow_mut() = Some(output);

        // Step 8.3
        fr.dispatch_progress_event(atom!("load"), 0, None);
        return_on_abort!();
        // Step 8.4
        if fr.ready_state.get() != FileReaderReadyState::Loading {
            fr.dispatch_progress_event(atom!("loadend"), 0, None);
        }
        return_on_abort!();
        // Step 9
        fr.terminate_ongoing_reading();
    }

    // https://w3c.github.io/FileAPI/#dfn-readAsText
    fn perform_readastext(data: ReadMetaData, blob_bytes: &[u8])
        -> DOMString {
        let blob_label = &data.label;
        let blob_type = &data.blobtype;

        //https://w3c.github.io/FileAPI/#encoding-determination
        // Steps 1 & 2 & 3
        let mut encoding = blob_label.as_ref()
            .map(|string| &**string)
            .and_then(encoding_from_whatwg_label);

        // Step 4 & 5
        encoding = encoding.or_else(|| {
            let resultmime = blob_type.parse::<Mime>().ok();
            resultmime.and_then(|Mime(_, _, ref parameters)| {
                parameters.iter()
                    .find(|&&(ref k, _)| &Attr::Charset == k)
                    .and_then(|&(_, ref v)| encoding_from_whatwg_label(&v.to_string()))
            })
        });

        // Step 6
        let enc = encoding.unwrap_or(UTF_8 as EncodingRef);

        let convert = blob_bytes;
        // Step 7
        let output = enc.decode(convert, DecoderTrap::Replace).unwrap();
        DOMString::from(output)
    }

    //https://w3c.github.io/FileAPI/#dfn-readAsDataURL
    fn perform_readasdataurl(data: ReadMetaData, bytes: &[u8])
        -> DOMString {
        let config = Config {
            char_set: CharacterSet::UrlSafe,
            newline: Newline::LF,
            pad: true,
            line_length: None
        };
        let base64 = bytes.to_base64(config);

        let output = if data.blobtype.is_empty() {
            format!("data:base64,{}", base64)
        } else {
            format!("data:{};base64,{}", data.blobtype, base64)
        };

        DOMString::from(output)
    }
}

impl FileReaderMethods for FileReader {
    // https://w3c.github.io/FileAPI/#dfn-onloadstart
    event_handler!(loadstart, GetOnloadstart, SetOnloadstart);

    // https://w3c.github.io/FileAPI/#dfn-onprogress
    event_handler!(progress, GetOnprogress, SetOnprogress);

    // https://w3c.github.io/FileAPI/#dfn-onload
    event_handler!(load, GetOnload, SetOnload);

    // https://w3c.github.io/FileAPI/#dfn-onabort
    event_handler!(abort, GetOnabort, SetOnabort);

    // https://w3c.github.io/FileAPI/#dfn-onerror
    event_handler!(error, GetOnerror, SetOnerror);

    // https://w3c.github.io/FileAPI/#dfn-onloadend
    event_handler!(loadend, GetOnloadend, SetOnloadend);

    //TODO https://w3c.github.io/FileAPI/#dfn-readAsArrayBuffer
    // https://w3c.github.io/FileAPI/#dfn-readAsDataURL
    fn ReadAsDataURL(&self, blob: &Blob) -> ErrorResult {
        self.read(FileReaderFunction::ReadAsDataUrl, blob, None)
    }

    // https://w3c.github.io/FileAPI/#dfn-readAsText
    fn ReadAsText(&self, blob: &Blob, label: Option<DOMString>) -> ErrorResult {
        self.read(FileReaderFunction::ReadAsText, blob, label)
    }

    // https://w3c.github.io/FileAPI/#dfn-abort
    fn Abort(&self) {
        // Step 2
        if self.ready_state.get() == FileReaderReadyState::Loading {
            self.change_ready_state(FileReaderReadyState::Done);
        }
        // Steps 1 & 3
        *self.result.borrow_mut() = None;

        let global = self.global();
        let exception = DOMException::new(global.r(), DOMErrorName::AbortError);
        self.error.set(Some(&exception));

        self.terminate_ongoing_reading();
        // Steps 5 & 6
        self.dispatch_progress_event(atom!("abort"), 0, None);
        self.dispatch_progress_event(atom!("loadend"), 0, None);
    }

    // https://w3c.github.io/FileAPI/#dfn-error
    fn GetError(&self) -> Option<Root<DOMException>> {
        self.error.get()
    }

    // https://w3c.github.io/FileAPI/#dfn-result
    fn GetResult(&self) -> Option<DOMString> {
        self.result.borrow().clone()
    }

    // https://w3c.github.io/FileAPI/#dfn-readyState
    fn ReadyState(&self) -> u16 {
        self.ready_state.get() as u16
    }
}

impl FileReader {
    fn dispatch_progress_event(&self, type_: Atom, loaded: u64, total: Option<u64>) {
        let global = self.global();
        let progressevent = ProgressEvent::new(global.r(),
            type_, EventBubbles::DoesNotBubble, EventCancelable::NotCancelable,
            total.is_some(), loaded, total.unwrap_or(0));
        progressevent.upcast::<Event>().fire(self.upcast());
    }

    fn terminate_ongoing_reading(&self) {
        let GenerationId(prev_id) = self.generation_id.get();
        self.generation_id.set(GenerationId(prev_id + 1));
    }

    fn read(&self, function: FileReaderFunction, blob: &Blob, label: Option<DOMString>) -> ErrorResult {
        // Step 1
        if self.ready_state.get() == FileReaderReadyState::Loading {
            return Err(Error::InvalidState);
        }
        // Step 2
        if blob.IsClosed() {
            let global = self.global();
            let exception = DOMException::new(global.r(), DOMErrorName::InvalidStateError);
            self.error.set(Some(&exception));

            self.dispatch_progress_event(atom!("error"), 0, None);
            return Ok(());
        }

        // Step 3
        self.change_ready_state(FileReaderReadyState::Loading);

        // Step 4
        let blob_contents = Arc::new(blob.get_bytes().unwrap_or(vec![]));

        let type_ = blob.Type();

        let load_data = ReadMetaData::new(String::from(type_), label.map(String::from), function);

        let fr = Trusted::new(self);
        let gen_id = self.generation_id.get();

        let script_chan = self.global().r().file_reading_task_source();

        spawn_named("file reader async operation".to_owned(), move || {
            perform_annotated_read_operation(gen_id, load_data, blob_contents, fr, script_chan)
        });
        Ok(())
    }

    fn change_ready_state(&self, state: FileReaderReadyState) {
        self.ready_state.set(state);
    }
}

#[derive(Clone)]
pub enum FileReaderEvent {
    ProcessRead(TrustedFileReader, GenerationId),
    ProcessReadData(TrustedFileReader, GenerationId),
    ProcessReadError(TrustedFileReader, GenerationId, DOMErrorName),
    ProcessReadEOF(TrustedFileReader, GenerationId, ReadMetaData, Arc<Vec<u8>>)
}

impl Runnable for FileReaderEvent {
    fn name(&self) -> &'static str { "FileReaderEvent" }

    fn handler(self: Box<FileReaderEvent>) {
        let file_reader_event = *self;
        match file_reader_event {
            FileReaderEvent::ProcessRead(filereader, gen_id) => {
                FileReader::process_read(filereader, gen_id);
            },
            FileReaderEvent::ProcessReadData(filereader, gen_id) => {
                FileReader::process_read_data(filereader, gen_id);
            },
            FileReaderEvent::ProcessReadError(filereader, gen_id, error) => {
                FileReader::process_read_error(filereader, gen_id, error);
            },
            FileReaderEvent::ProcessReadEOF(filereader, gen_id, data, blob_contents) => {
                FileReader::process_read_eof(filereader, gen_id, data, blob_contents);
            }
        }
    }
}

// https://w3c.github.io/FileAPI/#thread-read-operation
fn perform_annotated_read_operation(gen_id: GenerationId, data: ReadMetaData, blob_contents: Arc<Vec<u8>>,
    filereader: TrustedFileReader, script_chan: Box<ScriptChan + Send>) {
    let chan = &script_chan;
    // Step 4
    let thread = box FileReaderEvent::ProcessRead(filereader.clone(), gen_id);
    chan.send(CommonScriptMsg::RunnableMsg(FileRead, thread)).unwrap();

    let thread = box FileReaderEvent::ProcessReadData(filereader.clone(), gen_id);
    chan.send(CommonScriptMsg::RunnableMsg(FileRead, thread)).unwrap();

    let thread = box FileReaderEvent::ProcessReadEOF(filereader, gen_id, data, blob_contents);
    chan.send(CommonScriptMsg::RunnableMsg(FileRead, thread)).unwrap();
}
<|endoftext|>


<commit_before>macro_rules! report_error {
    ($func:path, $err:expr) => {{
        let err_str = &format!("{:?}", $err);

        match $crate::notification::create_error(err_str) {
            Ok(_)    => (),
            Err(err) => println!("error creating error notification: {:?}", err),
        }

        $func!("error: {}", err_str);
    }}
}

#[macro_export]
macro_rules! check_err {
    ($x:expr) => {{
        match $x {
            Ok(_) => (),
            Err(err) => report_error!(println, err),
        }
    }};

    ($x:expr, $default:expr) => {{
        match $x {
            Ok(v) => v,
            Err(err) => {
                report_error!(println, err);
                $default
            },
        }
    }};
}

#[macro_export]
macro_rules! check_err_p {
    ($x:expr) => {{
        match $x {
            Ok(v) => v,
            Err(err) => report_error!(panic, err),
        }
    }};
}

#[macro_export]
macro_rules! check_err_c {
    ($x:expr, $additional_code:block) => {{
        match $x {
            Ok(v) => v,
            Err(err) => {
                report_error!(println, err);
                $additional_code
                continue
            }
        }
    }};

    ($x:expr) => (check_err_c!($x, {}));
}<commit_msg>Improve error reporting<commit_after>macro_rules! report_error {
    ($func:path, $err:expr) => {{
        match $crate::notification::create_error($err.description()) {
            Ok(_)    => (),
            Err(err) => println!("error creating error notification: {:?}", err),
        }

        $func!("error: {:?}", $err);
    }}
}

#[macro_export]
macro_rules! check_err {
    ($x:expr) => {{
        match $x {
            Ok(_) => (),
            Err(err) => report_error!(println, err),
        }
    }};

    ($x:expr, $default:expr) => {{
        match $x {
            Ok(v) => v,
            Err(err) => {
                report_error!(println, err);
                $default
            },
        }
    }};
}

#[macro_export]
macro_rules! check_err_p {
    ($x:expr) => {{
        match $x {
            Ok(v) => v,
            Err(err) => report_error!(panic, err),
        }
    }};
}

#[macro_export]
macro_rules! check_err_c {
    ($x:expr, $additional_code:block) => {{
        match $x {
            Ok(v) => v,
            Err(err) => {
                report_error!(println, err);
                $additional_code
                continue
            }
        }
    }};

    ($x:expr) => (check_err_c!($x, {}));
}<|endoftext|>


<commit_before>#[test]
fn it_works() {
}
<commit_msg>Manually implement some unexported functions<commit_after>//!Bindings and rewrites of include/linux/time.h and include/linux/time64.h

extern crate linux_api;

use linux_api::*;

///Checks to see if two timespecs are equal
pub fn timespec_equal(a: &timespec, b: &timespec) -> c_int {
    return ((a.tv_sec == b.tv_sec) && (a.tv_nsec == b.tv_nsec)) as c_int;
}

///Compares two timespecs
///
///If lhs < rhs, return < 0
///If lhs == rhs, return 0
///If lhs > rhs, return > 0
pub fn timespec_compare(lhs: &timespec, rhs: &timespec) -> c_int {
    if lhs.tv_sec < rhs.tv_sec { return -1; }
    if lhs.tv_sec > rhs.tv_sec { return 1; }
    return (lhs.tv_nsec - rhs.tv_nsec) as c_int;
}

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

    #[test]
    fn test_timespec_equal_when_equal() {
        let one: timespec = timespec { tv_sec: 77, tv_nsec: 77 };
        let two: timespec = timespec { tv_sec: 77, tv_nsec: 77 };

        assert_eq!(1, timespec_equal(&one, &two));
    }

    #[test]
    fn test_timespec_equal_when_inequal() {
        let one: timespec = timespec { tv_sec: 0, tv_nsec: 266 };
        let two: timespec = timespec { tv_sec: 4, tv_nsec: 0 };

        assert_eq!(0, timespec_equal(&one, &two));
    }

    #[test]
    fn test_timespec_compare_same() {
        let one = timespec { tv_sec: 10, tv_nsec: 0 };
        let two = timespec { tv_sec: 10, tv_nsec: 0 };

        assert_eq!(0, timespec_compare(&one, &two));
    }

    #[test]
    fn test_timespec_compare_larger() {
        let one = timespec { tv_sec: 11, tv_nsec: 0 };
        let two = timespec { tv_sec: 10, tv_nsec: 0 };

        assert_eq!(1, timespec_compare(&one, &two));
    }

    #[test]
    fn test_timespec_compare_smaller() {
        let one = timespec { tv_sec: 9, tv_nsec: 0 };
        let two = timespec { tv_sec: 10, tv_nsec: 0 };

        assert_eq!(-1, timespec_compare(&one, &two));
    }

}<|endoftext|>


<commit_before>#![crate_type="dylib"]
#![feature(plugin_registrar)]
#![feature(rustc_private, core)]
#![deny(warnings)]
#![allow(unused_features)]

extern crate syntax;
extern crate rustc;
extern crate glassful;

use syntax::ast;
use syntax::parse::token;
use syntax::codemap::Span;
use syntax::ext::base::{ExtCtxt, MacResult, MacExpr, DummyResult};
use syntax::ext::build::AstBuilder;
use rustc::plugin::Registry;

#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
    reg.register_macro("glassful", expand);
}

fn expand(cx: &mut ExtCtxt, outer_span: Span, toks: &[ast::TokenTree])
    -> Box<MacResult + 'static>
{
    let inner_span = match toks {
        [] => {
            cx.span_err(outer_span, "empty invocation");
            return DummyResult::expr(outer_span);
        }
        [ref first, ..] => {
            let first = first.get_span();
            let last = toks.iter().rev().next().unwrap().get_span();
            if first.expn_id != last.expn_id {
                cx.span_err(first, "invocation is split between expansion contexts??");
                cx.span_note(last, "last token is here");
                return DummyResult::expr(outer_span);
            }

            Span {
                lo: first.lo,
                hi: last.hi,
                expn_id: first.expn_id,
            }
        }
    };

    let src = match cx.codemap().span_to_snippet(inner_span) {
        Err(e) => {
            cx.span_err(inner_span, &format!("can't extract source snippet: {:?}", e)[..]);
            return DummyResult::expr(inner_span);
        }
        Ok(src) => src,
    };

    match glassful::try_translate(src) {
        None => {
            cx.span_err(outer_span, "translation failed");
            DummyResult::expr(outer_span)
        }
        Some(res) => {
            let interned = token::intern_and_get_ident(&res[..]);
            MacExpr::new(cx.expr_str(inner_span, interned))
        }
    }
}
<commit_msg>Use MacEager instead of MacExpr<commit_after>#![crate_type="dylib"]
#![feature(plugin_registrar)]
#![feature(rustc_private, core)]
#![deny(warnings)]
#![allow(unused_features)]

extern crate syntax;
extern crate rustc;
extern crate glassful;

use syntax::ast;
use syntax::parse::token;
use syntax::codemap::Span;
use syntax::ext::base::{ExtCtxt, MacEager, MacResult, DummyResult};
use syntax::ext::build::AstBuilder;
use rustc::plugin::Registry;

#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
    reg.register_macro("glassful", expand);
}

fn expand(cx: &mut ExtCtxt, outer_span: Span, toks: &[ast::TokenTree])
    -> Box<MacResult + 'static>
{
    let inner_span = match toks {
        [] => {
            cx.span_err(outer_span, "empty invocation");
            return DummyResult::expr(outer_span);
        }
        [ref first, ..] => {
            let first = first.get_span();
            let last = toks.iter().rev().next().unwrap().get_span();
            if first.expn_id != last.expn_id {
                cx.span_err(first, "invocation is split between expansion contexts??");
                cx.span_note(last, "last token is here");
                return DummyResult::expr(outer_span);
            }

            Span {
                lo: first.lo,
                hi: last.hi,
                expn_id: first.expn_id,
            }
        }
    };

    let src = match cx.codemap().span_to_snippet(inner_span) {
        Err(e) => {
            cx.span_err(inner_span, &format!("can't extract source snippet: {:?}", e)[..]);
            return DummyResult::expr(inner_span);
        }
        Ok(src) => src,
    };

    match glassful::try_translate(src) {
        None => {
            cx.span_err(outer_span, "translation failed");
            DummyResult::expr(outer_span)
        }
        Some(res) => {
            let interned = token::intern_and_get_ident(&res[..]);
            MacEager::expr(cx.expr_str(inner_span, interned))
        }
    }
}
<|endoftext|>


<commit_before>use core::cmp::{Eq, Ord};
use core::f64::consts::pi;

use funs::triganomic::{cos, sin};
use mat::{Mat3, Mat4};
use num::kinds::{Float, Number};
use num::conv::cast;
use quat::Quat;
use vec::Vec3;

/**
 * The base trait for anglular units
 */
pub trait Angle<T>: Add<self,self>,
                    Sub<self,self>,
                    Mul<T,self>,
                    Div<T,self>,
                    // Div<self,T>,     // TODO: not sure how to implement this, or if it is even possible...
                    Modulo<T,self>,
                    // Modulo<self,T>,  // TODO: not sure how to implement this, or if it is even possible...
                    Neg<self>,
                    Eq, Ord {
    static pure fn full_turn() -> self;
    static pure fn half_turn() -> self;
    static pure fn quadrant()  -> self;
    static pure fn sextant()   -> self;
    static pure fn octant()    -> self;
    static pure fn zero()      -> self;

    pure fn to_radians(&self) -> Radians<T>;
    pure fn to_degrees(&self) -> Degrees<T>;
    pure fn wrap(&self) -> self;
    pure fn opposite(&self) -> self;
}

pub enum Radians<T> = T;

pub impl<T:Copy Float> Radians<T>: Angle<T> {
    #[inline(always)] static pure fn full_turn()    -> Radians<T> { Radians(Float::two_pi())    }
    #[inline(always)] static pure fn half_turn()    -> Radians<T> { Radians(Float::pi())        }
    #[inline(always)] static pure fn quadrant()     -> Radians<T> { Radians(Float::frac_pi_2()) }
    #[inline(always)] static pure fn sextant()      -> Radians<T> { Radians(Float::frac_pi_3()) }
    #[inline(always)] static pure fn octant()       -> Radians<T> { Radians(Float::frac_pi_4()) }
    #[inline(always)] static pure fn zero()         -> Radians<T> { Radians(Number::zero())     }

    #[inline(always)] pure fn to_radians(&self) -> Radians<T> { *self }
    #[inline(always)] pure fn to_degrees(&self) -> Degrees<T> { Degrees(**self * cast(180.0 / pi)) }

    #[inline(always)]
    pure fn wrap(&self) -> Radians<T> {
        let theta = (*self) % cast(2.0 * pi);

        // keep in the domain of 0 to 1 rad
        if theta >= Angle::zero() {
            theta
        } else {
            theta + Angle::full_turn()
        }
    }

    #[inline(always)]
    pure fn opposite(&self) -> Radians<T> {
        (self + Angle::half_turn()).wrap()     // TODO: test!
    }
}

pub impl<T:Copy Float> Radians<T>: Add<Radians<T>, Radians<T>> {
    #[inline(always)]
    pure fn add(rhs: &Radians<T>) -> Radians<T> {
        Radians(*self + **rhs)
    }
}

pub impl<T:Copy Float> Radians<T>: Sub<Radians<T>, Radians<T>> {
    #[inline(always)]
    pure fn sub(&self, rhs: &Radians<T>) -> Radians<T> {
        Radians(**self - **rhs)
    }
}

pub impl<T:Copy Float> Radians<T>: Mul<T, Radians<T>> {
    #[inline(always)]
    pure fn mul(&self, rhs: &T) -> Radians<T> {
        Radians(**self * *rhs)
    }
}

pub impl<T:Copy Float> Radians<T>: Div<T, Radians<T>> {
    #[inline(always)]
    pure fn div(&self, rhs: &T) -> Radians<T> {
        Radians(**self / *rhs)
    }
}

pub impl<T:Copy Float> Radians<T>: Modulo<T, Radians<T>> {
    #[inline(always)]
    pure fn modulo(&self, rhs: &T) -> Radians<T> {
        Radians(**self % *rhs)
    }
}

pub impl<T:Copy Float> Radians<T>: Neg<Radians<T>> {
    #[inline(always)]
    pure fn neg(&self) -> Radians<T> {
        Radians(-**self)
    }
}

pub impl<T:Copy Float> Radians<T>: Eq {
    #[inline(always)] pure fn eq(&self, other: &Radians<T>) -> bool { **self == **other }
    #[inline(always)] pure fn ne(&self, other: &Radians<T>) -> bool { **self != **other }
}

pub impl<T:Copy Float> Radians<T>: Ord {
    #[inline(always)] pure fn lt(&self, other: &Radians<T>) -> bool { **self <  **other }
    #[inline(always)] pure fn le(&self, other: &Radians<T>) -> bool { **self <= **other }
    #[inline(always)] pure fn ge(&self, other: &Radians<T>) -> bool { **self >= **other }
    #[inline(always)] pure fn gt(&self, other: &Radians<T>) -> bool { **self >  **other }
}

pub impl<T> Radians<T>: ToStr {
    pure fn to_str() -> ~str { fmt!("%? rad", *self) }
}

pub enum Degrees<T> = T;

// FIXME: not sure why I need the Eq and Ord trait bounds, but Rust complains if I don't include them
pub impl<T:Copy Float> Degrees<T>: Angle<T> {
    #[inline(always)] static pure fn full_turn()    -> Degrees<T> { Degrees(cast(360.0)) }
    #[inline(always)] static pure fn half_turn()    -> Degrees<T> { Degrees(cast(180.0)) }
    #[inline(always)] static pure fn quadrant()     -> Degrees<T> { Degrees(cast(90.0))  }
    #[inline(always)] static pure fn sextant()      -> Degrees<T> { Degrees(cast(60.0))  }
    #[inline(always)] static pure fn octant()       -> Degrees<T> { Degrees(cast(45.0))  }
    #[inline(always)] static pure fn zero()         -> Degrees<T> { Degrees(cast(0.0))   }

    #[inline(always)] pure fn to_radians(&self) -> Radians<T> { Radians(**self * cast(pi / 180.0)) }
    #[inline(always)] pure fn to_degrees(&self) -> Degrees<T> { *self }

    #[inline(always)]
    pure fn wrap(&self) -> Degrees<T> {
        let theta = (*self) % cast(360);

        // keep in the domain of 0 to 360 degrees
        if theta >= Angle::zero() {
            theta
        } else {
            theta + Angle::full_turn()
        }
    }

    #[inline(always)]
    pure fn opposite(&self) -> Degrees<T> {
        (self + Angle::half_turn()).wrap()      // TODO: test!
    }
}

pub impl<T:Copy Float> Degrees<T>: Add<Degrees<T>, Degrees<T>> {
    #[inline(always)]
    pure fn add(rhs: &Degrees<T>) -> Degrees<T> {
        Degrees(*self + **rhs)
    }
}

pub impl<T:Copy Float> Degrees<T>: Sub<Degrees<T>, Degrees<T>> {
    #[inline(always)]
    pure fn sub(&self, rhs: &Degrees<T>) -> Degrees<T> {
        Degrees(**self - **rhs)
    }
}

pub impl<T:Copy Float> Degrees<T>: Mul<T, Degrees<T>> {
    #[inline(always)]
    pure fn mul(&self, rhs: &T) -> Degrees<T> {
        Degrees(**self * *rhs)
    }
}

pub impl<T:Copy Float> Degrees<T>: Div<T, Degrees<T>> {
    #[inline(always)]
    pure fn div(&self, rhs: &T) -> Degrees<T> {
        Degrees(**self / *rhs)
    }
}

pub impl<T:Copy Float> Degrees<T>: Modulo<T, Degrees<T>> {
    #[inline(always)]
    pure fn modulo(&self, rhs: &T) -> Degrees<T> {
        Degrees(**self % *rhs)
    }
}

pub impl<T:Copy Float> Degrees<T>: Neg<Degrees<T>> {
    #[inline(always)]
    pure fn neg(&self) -> Degrees<T> {
        Degrees(-**self)
    }
}

pub impl<T:Copy Float> Degrees<T>: Eq {
    #[inline(always)] pure fn eq(&self, other: &Degrees<T>) -> bool { **self == **other }
    #[inline(always)] pure fn ne(&self, other: &Degrees<T>) -> bool { **self != **other }
}

pub impl<T:Copy Float> Degrees<T>: Ord {
    #[inline(always)] pure fn lt(&self, other: &Degrees<T>) -> bool { **self <  **other }
    #[inline(always)] pure fn le(&self, other: &Degrees<T>) -> bool { **self <= **other }
    #[inline(always)] pure fn ge(&self, other: &Degrees<T>) -> bool { **self >= **other }
    #[inline(always)] pure fn gt(&self, other: &Degrees<T>) -> bool { **self >  **other }
}

pub impl<T> Degrees<T>: ToStr {
    pure fn to_str() -> ~str { fmt!("%?\xB0", *self) }
}

/**
 * An angular rotation around an arbitary axis
 */
pub struct Rotation<T> {
    theta: Radians<T>,
    axis: Vec3<T>,
}

pub impl<T:Copy Float> Rotation<T> {
    #[inline(always)]
    static pure fn new(theta: Radians<T>, axis: Vec3<T>) -> Rotation<T> {
        Rotation { theta: move theta, axis: move axis }
    }

    #[inline(always)]
    pure fn to_mat3() -> Mat3<T> {
        let c:  T = cos(&self.theta);
        let s:  T = sin(&self.theta);
        let _0: T = cast(0);
        let _1: T = cast(1);
        // let _0: T = Number::from(0);    // FIXME: causes ICE
        // let _1: T = Number::from(0);    // FIXME: causes ICE
        let t:  T = _1 - c;

        let x = self.axis.x;
        let y = self.axis.y;
        let z = self.axis.z;

        Mat3::new(t * x * x + c,       t * x * y + s * z,   t * x * z - s * y,
                  t * x * y - s * z,   t * y * y + c,       t * y * z + s * x,
                  t * x * z - s - y,   t * y * z - s * x,   t * z * z + c)
    }

    #[inline(always)]
    pure fn to_mat4() -> Mat4<T> {
        self.to_mat3().to_mat4()
    }

    #[inline(always)]
    pure fn to_quat() -> Quat<T> {
        let half = self.theta / Number::from(2);
        Quat::from_sv(cos(&half), self.axis.mul_t(sin(&half)))
    }
}

pub struct Euler<T> {
    x: Radians<T>,   // pitch
    y: Radians<T>,   // yaw
    z: Radians<T>,   // roll
}<commit_msg>Rix rotation to mat3 conversion<commit_after>use core::cmp::{Eq, Ord};
use core::f64::consts::pi;

use funs::triganomic::{cos, sin};
use mat::{Mat3, Mat4};
use num::kinds::{Float, Number};
use num::conv::cast;
use quat::Quat;
use vec::Vec3;

/**
 * The base trait for anglular units
 */
pub trait Angle<T>: Add<self,self>,
                    Sub<self,self>,
                    Mul<T,self>,
                    Div<T,self>,
                    // Div<self,T>,     // TODO: not sure how to implement this, or if it is even possible...
                    Modulo<T,self>,
                    // Modulo<self,T>,  // TODO: not sure how to implement this, or if it is even possible...
                    Neg<self>,
                    Eq, Ord {
    static pure fn full_turn() -> self;
    static pure fn half_turn() -> self;
    static pure fn quadrant()  -> self;
    static pure fn sextant()   -> self;
    static pure fn octant()    -> self;
    static pure fn zero()      -> self;

    pure fn to_radians(&self) -> Radians<T>;
    pure fn to_degrees(&self) -> Degrees<T>;
    pure fn wrap(&self) -> self;
    pure fn opposite(&self) -> self;
}

pub enum Radians<T> = T;

pub impl<T:Copy Float> Radians<T>: Angle<T> {
    #[inline(always)] static pure fn full_turn()    -> Radians<T> { Radians(Float::two_pi())    }
    #[inline(always)] static pure fn half_turn()    -> Radians<T> { Radians(Float::pi())        }
    #[inline(always)] static pure fn quadrant()     -> Radians<T> { Radians(Float::frac_pi_2()) }
    #[inline(always)] static pure fn sextant()      -> Radians<T> { Radians(Float::frac_pi_3()) }
    #[inline(always)] static pure fn octant()       -> Radians<T> { Radians(Float::frac_pi_4()) }
    #[inline(always)] static pure fn zero()         -> Radians<T> { Radians(Number::zero())     }

    #[inline(always)] pure fn to_radians(&self) -> Radians<T> { *self }
    #[inline(always)] pure fn to_degrees(&self) -> Degrees<T> { Degrees(**self * cast(180.0 / pi)) }

    #[inline(always)]
    pure fn wrap(&self) -> Radians<T> {
        let theta = (*self) % cast(2.0 * pi);

        // keep in the domain of 0 to 1 rad
        if theta >= Angle::zero() {
            theta
        } else {
            theta + Angle::full_turn()
        }
    }

    #[inline(always)]
    pure fn opposite(&self) -> Radians<T> {
        (self + Angle::half_turn()).wrap()     // TODO: test!
    }
}

pub impl<T:Copy Float> Radians<T>: Add<Radians<T>, Radians<T>> {
    #[inline(always)]
    pure fn add(rhs: &Radians<T>) -> Radians<T> {
        Radians(*self + **rhs)
    }
}

pub impl<T:Copy Float> Radians<T>: Sub<Radians<T>, Radians<T>> {
    #[inline(always)]
    pure fn sub(&self, rhs: &Radians<T>) -> Radians<T> {
        Radians(**self - **rhs)
    }
}

pub impl<T:Copy Float> Radians<T>: Mul<T, Radians<T>> {
    #[inline(always)]
    pure fn mul(&self, rhs: &T) -> Radians<T> {
        Radians(**self * *rhs)
    }
}

pub impl<T:Copy Float> Radians<T>: Div<T, Radians<T>> {
    #[inline(always)]
    pure fn div(&self, rhs: &T) -> Radians<T> {
        Radians(**self / *rhs)
    }
}

pub impl<T:Copy Float> Radians<T>: Modulo<T, Radians<T>> {
    #[inline(always)]
    pure fn modulo(&self, rhs: &T) -> Radians<T> {
        Radians(**self % *rhs)
    }
}

pub impl<T:Copy Float> Radians<T>: Neg<Radians<T>> {
    #[inline(always)]
    pure fn neg(&self) -> Radians<T> {
        Radians(-**self)
    }
}

pub impl<T:Copy Float> Radians<T>: Eq {
    #[inline(always)] pure fn eq(&self, other: &Radians<T>) -> bool { **self == **other }
    #[inline(always)] pure fn ne(&self, other: &Radians<T>) -> bool { **self != **other }
}

pub impl<T:Copy Float> Radians<T>: Ord {
    #[inline(always)] pure fn lt(&self, other: &Radians<T>) -> bool { **self <  **other }
    #[inline(always)] pure fn le(&self, other: &Radians<T>) -> bool { **self <= **other }
    #[inline(always)] pure fn ge(&self, other: &Radians<T>) -> bool { **self >= **other }
    #[inline(always)] pure fn gt(&self, other: &Radians<T>) -> bool { **self >  **other }
}

pub impl<T> Radians<T>: ToStr {
    pure fn to_str() -> ~str { fmt!("%? rad", *self) }
}

pub enum Degrees<T> = T;

// FIXME: not sure why I need the Eq and Ord trait bounds, but Rust complains if I don't include them
pub impl<T:Copy Float> Degrees<T>: Angle<T> {
    #[inline(always)] static pure fn full_turn()    -> Degrees<T> { Degrees(cast(360.0)) }
    #[inline(always)] static pure fn half_turn()    -> Degrees<T> { Degrees(cast(180.0)) }
    #[inline(always)] static pure fn quadrant()     -> Degrees<T> { Degrees(cast(90.0))  }
    #[inline(always)] static pure fn sextant()      -> Degrees<T> { Degrees(cast(60.0))  }
    #[inline(always)] static pure fn octant()       -> Degrees<T> { Degrees(cast(45.0))  }
    #[inline(always)] static pure fn zero()         -> Degrees<T> { Degrees(cast(0.0))   }

    #[inline(always)] pure fn to_radians(&self) -> Radians<T> { Radians(**self * cast(pi / 180.0)) }
    #[inline(always)] pure fn to_degrees(&self) -> Degrees<T> { *self }

    #[inline(always)]
    pure fn wrap(&self) -> Degrees<T> {
        let theta = (*self) % cast(360);

        // keep in the domain of 0 to 360 degrees
        if theta >= Angle::zero() {
            theta
        } else {
            theta + Angle::full_turn()
        }
    }

    #[inline(always)]
    pure fn opposite(&self) -> Degrees<T> {
        (self + Angle::half_turn()).wrap()      // TODO: test!
    }
}

pub impl<T:Copy Float> Degrees<T>: Add<Degrees<T>, Degrees<T>> {
    #[inline(always)]
    pure fn add(rhs: &Degrees<T>) -> Degrees<T> {
        Degrees(*self + **rhs)
    }
}

pub impl<T:Copy Float> Degrees<T>: Sub<Degrees<T>, Degrees<T>> {
    #[inline(always)]
    pure fn sub(&self, rhs: &Degrees<T>) -> Degrees<T> {
        Degrees(**self - **rhs)
    }
}

pub impl<T:Copy Float> Degrees<T>: Mul<T, Degrees<T>> {
    #[inline(always)]
    pure fn mul(&self, rhs: &T) -> Degrees<T> {
        Degrees(**self * *rhs)
    }
}

pub impl<T:Copy Float> Degrees<T>: Div<T, Degrees<T>> {
    #[inline(always)]
    pure fn div(&self, rhs: &T) -> Degrees<T> {
        Degrees(**self / *rhs)
    }
}

pub impl<T:Copy Float> Degrees<T>: Modulo<T, Degrees<T>> {
    #[inline(always)]
    pure fn modulo(&self, rhs: &T) -> Degrees<T> {
        Degrees(**self % *rhs)
    }
}

pub impl<T:Copy Float> Degrees<T>: Neg<Degrees<T>> {
    #[inline(always)]
    pure fn neg(&self) -> Degrees<T> {
        Degrees(-**self)
    }
}

pub impl<T:Copy Float> Degrees<T>: Eq {
    #[inline(always)] pure fn eq(&self, other: &Degrees<T>) -> bool { **self == **other }
    #[inline(always)] pure fn ne(&self, other: &Degrees<T>) -> bool { **self != **other }
}

pub impl<T:Copy Float> Degrees<T>: Ord {
    #[inline(always)] pure fn lt(&self, other: &Degrees<T>) -> bool { **self <  **other }
    #[inline(always)] pure fn le(&self, other: &Degrees<T>) -> bool { **self <= **other }
    #[inline(always)] pure fn ge(&self, other: &Degrees<T>) -> bool { **self >= **other }
    #[inline(always)] pure fn gt(&self, other: &Degrees<T>) -> bool { **self >  **other }
}

pub impl<T> Degrees<T>: ToStr {
    pure fn to_str() -> ~str { fmt!("%?\xB0", *self) }
}

/**
 * An angular rotation around an arbitary axis
 */
pub struct Rotation<T> {
    theta: Radians<T>,
    axis: Vec3<T>,
}

pub impl<T:Copy Float> Rotation<T> {
    #[inline(always)]
    static pure fn new(theta: Radians<T>, axis: Vec3<T>) -> Rotation<T> {
        Rotation { theta: move theta, axis: move axis }
    }

    #[inline(always)]
    pure fn to_mat3() -> Mat3<T> {
        let c:  T = cos(&self.theta);
        let s:  T = sin(&self.theta);
        let _0: T = cast(0);
        let _1: T = cast(1);
        // let _0: T = Number::from(0);    // FIXME: causes ICE
        // let _1: T = Number::from(1);    // FIXME: causes ICE
        let _1_c:  T = _1 - c;

        let x = self.axis.x;
        let y = self.axis.y;
        let z = self.axis.z;

        Mat3::new(_1_c * x * x + c,       _1_c * x * y + s * z,   _1_c * x * z - s * y,
                  _1_c * x * y - s * z,   _1_c * y * y + c,       _1_c * y * z + s * x,
                  _1_c * x * z + s * y,   _1_c * y * z - s * x,   _1_c * z * z + c)
    }

    #[inline(always)]
    pure fn to_mat4() -> Mat4<T> {
        self.to_mat3().to_mat4()
    }

    #[inline(always)]
    pure fn to_quat() -> Quat<T> {
        let half = self.theta / Number::from(2);
        Quat::from_sv(cos(&half), self.axis.mul_t(sin(&half)))
    }
}

pub struct Euler<T> {
    x: Radians<T>,   // pitch
    y: Radians<T>,   // yaw
    z: Radians<T>,   // roll
}<|endoftext|>


<commit_before>use core::cell::Cell;
use common::volatile_cell::VolatileCell;
use common::take_cell::TakeCell;
use core::mem::transmute;
use hil::gpio::{Client, GPIOPin, InputMode, InterruptMode};

pub struct PortRegisters {
    pub data_in: VolatileCell<u32>,
    pub data_out: VolatileCell<u32>,
    _reserved: [u32; 2],
    pub output_enable: VolatileCell<u32>,
    pub output_disable: VolatileCell<u32>,
    _reserved2: [u32; 2],
    pub interrupt_enable: VolatileCell<u32>,
    pub interrupt_disable: VolatileCell<u32>,
    pub interrupt_type_set: VolatileCell<u32>,
    pub interrupt_type_clear: VolatileCell<u32>,
    pub interrupt_pol_set: VolatileCell<u32>,
    pub interrupt_pol_clear: VolatileCell<u32>,
    pub interrupt_status: VolatileCell<u32>
}

pub const GPIO0_BASE: *mut PortRegisters = 0x40200000 as *mut PortRegisters;

pub struct Port {
    pub pins: [Pin; 16],
}

pub static mut PORT0: Port = Port {
    pins: [Pin::new(GPIO0_BASE, PinNum::P0),
           Pin::new(GPIO0_BASE, PinNum::P1),
           Pin::new(GPIO0_BASE, PinNum::P2),
           Pin::new(GPIO0_BASE, PinNum::P3),
           Pin::new(GPIO0_BASE, PinNum::P4),
           Pin::new(GPIO0_BASE, PinNum::P5),
           Pin::new(GPIO0_BASE, PinNum::P6),
           Pin::new(GPIO0_BASE, PinNum::P7),
           Pin::new(GPIO0_BASE, PinNum::P8),
           Pin::new(GPIO0_BASE, PinNum::P9),
           Pin::new(GPIO0_BASE, PinNum::P10),
           Pin::new(GPIO0_BASE, PinNum::P11),
           Pin::new(GPIO0_BASE, PinNum::P12),
           Pin::new(GPIO0_BASE, PinNum::P13),
           Pin::new(GPIO0_BASE, PinNum::P14),
           Pin::new(GPIO0_BASE, PinNum::P15)],
};

#[derive(Clone,Copy,Debug)]
pub enum PinNum {
    P0 = 0,
    P1,
    P2,
    P3,
    P4,
    P5,
    P6,
    P7,
    P8,
    P9,
    P10,
    P11,
    P12,
    P13,
    P14,
    P15,
}

pub struct Pin {
    port: *mut PortRegisters,
    pin: PinNum,
    client_data: Cell<usize>,
    change: Cell<bool>,
    client: TakeCell<&'static Client>,
}

impl Pin {
    const fn new(port: *mut PortRegisters, pin: PinNum) -> Pin {
        Pin {
            port: port,
            pin: pin,
            change: Cell::new(false),
            client_data: Cell::new(0),
            client: TakeCell::empty(),
        }
    }

    pub fn handle_interrupt(&self) {
        let mask = 1 << (self.pin as u32);

        let port: &mut PortRegisters = unsafe { transmute(self.port) };
        port.interrupt_status.set(mask);

        if self.change.get() {
            if port.interrupt_pol_set.get() & mask != 0 {
                port.interrupt_pol_clear.set(mask);
            } else {
                port.interrupt_pol_set.set(mask);
            }
        }

        self.client.map(|client| {
            client.fired(self.client_data.get());
        });
    }

    pub fn set_client(&self, client: &'static Client) {
        self.client.put(Some(client));
    }
}

impl GPIOPin for Pin {
    fn enable_output(&self) {
        let port: &mut PortRegisters = unsafe { transmute(self.port) };
        port.output_enable.set(1 << (self.pin as u32));
    }

    fn enable_input(&self, _mode: InputMode) {
        // Noop, input is always enabled on this chip

        // InputMode equivilant is actually set in the Pinmux. It actually kind of
        // makes sense to have this be something that's setup by the platform
        // initialization, rather than chosen by the client of a particular GPIO
        // pin.
    }

    fn disable(&self) {
        let port: &mut PortRegisters = unsafe { transmute(self.port) };
        port.output_disable.set(1 << (self.pin as u32));
    }

    fn set(&self) {
        let port: &mut PortRegisters = unsafe { transmute(self.port) };
        let data_out = port.data_out.get();
        port.data_out.set(data_out | 1 << (self.pin as u32));
    }

    fn clear(&self) {
        let port: &mut PortRegisters = unsafe { transmute(self.port) };
        let data_out = port.data_out.get();
        port.data_out.set(data_out & !(1 << (self.pin as u32)));
    }

    fn toggle(&self) {
        let port: &mut PortRegisters = unsafe { transmute(self.port) };
        let data_out = port.data_out.get();
        port.data_out.set(data_out ^ 1 << (self.pin as u32));
    }

    fn read(&self) -> bool {
        let port: &mut PortRegisters = unsafe { transmute(self.port) };
        port.data_in.get() & (1 << (self.pin as u32)) != 0
    }

    fn enable_interrupt(&self, identifier: usize, mode: InterruptMode) {
        self.client_data.set(identifier);

        let port: &mut PortRegisters = unsafe { transmute(self.port) };
        let mask = 1 << (self.pin as u32);
        match mode {
            InterruptMode::RisingEdge => {
                port.interrupt_pol_set.set(mask);
                self.change.set(false);
            },
            InterruptMode::FallingEdge => {
                port.interrupt_pol_clear.set(mask);
                self.change.set(false);
            },
            InterruptMode::Change => {
                self.change.set(true);
                if self.read() {
                    port.interrupt_pol_clear.set(mask);
                } else {
                    port.interrupt_pol_set.set(mask);
                }
            }
        }
        port.interrupt_type_set.set(mask);
        port.interrupt_enable.set(mask);
    }

    fn disable_interrupt(&self) {
        let port: &mut PortRegisters = unsafe { transmute(self.port) };
        let mask = 1 << (self.pin as u32);
        port.interrupt_disable.set(mask);
    }
}

interrupt_handler!(gpio0_combined_handler, 81);

interrupt_handler!(gpio0_0_handler, 65);
interrupt_handler!(gpio0_1_handler, 66);
<commit_msg>Add some comments to GPIO<commit_after>use core::cell::Cell;
use common::volatile_cell::VolatileCell;
use common::take_cell::TakeCell;
use core::mem::transmute;
use hil::gpio::{Client, GPIOPin, InputMode, InterruptMode};

pub struct PortRegisters {
    pub data_in: VolatileCell<u32>,
    pub data_out: VolatileCell<u32>,
    _reserved: [u32; 2],
    pub output_enable: VolatileCell<u32>,
    pub output_disable: VolatileCell<u32>,
    _reserved2: [u32; 2],
    pub interrupt_enable: VolatileCell<u32>,
    pub interrupt_disable: VolatileCell<u32>,
    pub interrupt_type_set: VolatileCell<u32>,
    pub interrupt_type_clear: VolatileCell<u32>,
    pub interrupt_pol_set: VolatileCell<u32>,
    pub interrupt_pol_clear: VolatileCell<u32>,
    pub interrupt_status: VolatileCell<u32>
}

pub const GPIO0_BASE: *mut PortRegisters = 0x40200000 as *mut PortRegisters;

pub struct Port {
    pub pins: [Pin; 16],
}

pub static mut PORT0: Port = Port {
    pins: [Pin::new(GPIO0_BASE, PinNum::P0),
           Pin::new(GPIO0_BASE, PinNum::P1),
           Pin::new(GPIO0_BASE, PinNum::P2),
           Pin::new(GPIO0_BASE, PinNum::P3),
           Pin::new(GPIO0_BASE, PinNum::P4),
           Pin::new(GPIO0_BASE, PinNum::P5),
           Pin::new(GPIO0_BASE, PinNum::P6),
           Pin::new(GPIO0_BASE, PinNum::P7),
           Pin::new(GPIO0_BASE, PinNum::P8),
           Pin::new(GPIO0_BASE, PinNum::P9),
           Pin::new(GPIO0_BASE, PinNum::P10),
           Pin::new(GPIO0_BASE, PinNum::P11),
           Pin::new(GPIO0_BASE, PinNum::P12),
           Pin::new(GPIO0_BASE, PinNum::P13),
           Pin::new(GPIO0_BASE, PinNum::P14),
           Pin::new(GPIO0_BASE, PinNum::P15)],
};

#[derive(Clone,Copy,Debug)]
pub enum PinNum {
    P0 = 0,
    P1,
    P2,
    P3,
    P4,
    P5,
    P6,
    P7,
    P8,
    P9,
    P10,
    P11,
    P12,
    P13,
    P14,
    P15,
}

pub struct Pin {
    port: *mut PortRegisters,
    pin: PinNum,
    client_data: Cell<usize>,
    change: Cell<bool>,
    client: TakeCell<&'static Client>,
}

impl Pin {
    const fn new(port: *mut PortRegisters, pin: PinNum) -> Pin {
        Pin {
            port: port,
            pin: pin,
            change: Cell::new(false),
            client_data: Cell::new(0),
            client: TakeCell::empty(),
        }
    }

    pub fn handle_interrupt(&self) {
        let mask = 1 << (self.pin as u32);

        let port: &mut PortRegisters = unsafe { transmute(self.port) };
        port.interrupt_status.set(mask);

        // If our InterruptMode was `Change`, we need to flip the direction of
        // the interrupt polarity.
        if self.change.get() {
            if port.interrupt_pol_set.get() & mask != 0 {
                port.interrupt_pol_clear.set(mask);
            } else {
                port.interrupt_pol_set.set(mask);
            }
        }

        self.client.map(|client| {
            client.fired(self.client_data.get());
        });
    }

    pub fn set_client(&self, client: &'static Client) {
        self.client.put(Some(client));
    }
}

impl GPIOPin for Pin {
    fn enable_output(&self) {
        let port: &mut PortRegisters = unsafe { transmute(self.port) };
        port.output_enable.set(1 << (self.pin as u32));
    }

    fn enable_input(&self, _mode: InputMode) {
        // Noop, input is always enabled on this chip

        // InputMode equivilant is actually set in the Pinmux. It actually kind of
        // makes sense to have this be something that's setup by the platform
        // initialization, rather than chosen by the client of a particular GPIO
        // pin.
    }

    fn disable(&self) {
        let port: &mut PortRegisters = unsafe { transmute(self.port) };
        port.output_disable.set(1 << (self.pin as u32));
    }

    fn set(&self) {
        let port: &mut PortRegisters = unsafe { transmute(self.port) };
        let data_out = port.data_out.get();
        port.data_out.set(data_out | 1 << (self.pin as u32));
    }

    fn clear(&self) {
        let port: &mut PortRegisters = unsafe { transmute(self.port) };
        let data_out = port.data_out.get();
        port.data_out.set(data_out & !(1 << (self.pin as u32)));
    }

    fn toggle(&self) {
        let port: &mut PortRegisters = unsafe { transmute(self.port) };
        let data_out = port.data_out.get();
        port.data_out.set(data_out ^ 1 << (self.pin as u32));
    }

    fn read(&self) -> bool {
        let port: &mut PortRegisters = unsafe { transmute(self.port) };
        port.data_in.get() & (1 << (self.pin as u32)) != 0
    }

    // `InterruptMode::Change` is not implemented in hardware, so we simulate it
    // in software. This could lead to missing events if a toggle happens before
    // we install the new events.
    fn enable_interrupt(&self, identifier: usize, mode: InterruptMode) {
        self.client_data.set(identifier);

        let port: &mut PortRegisters = unsafe { transmute(self.port) };
        let mask = 1 << (self.pin as u32);
        match mode {
            InterruptMode::RisingEdge => {
                port.interrupt_pol_set.set(mask);
                self.change.set(false);
            },
            InterruptMode::FallingEdge => {
                port.interrupt_pol_clear.set(mask);
                self.change.set(false);
            },
            InterruptMode::Change => {
                self.change.set(true);
                // Set the interrupt polarity based on whatever the current
                // state of the pin is.
                if self.read() {
                    port.interrupt_pol_clear.set(mask);
                } else {
                    port.interrupt_pol_set.set(mask);
                }
            }
        }
        port.interrupt_type_set.set(mask);
        port.interrupt_enable.set(mask);
    }

    fn disable_interrupt(&self) {
        let port: &mut PortRegisters = unsafe { transmute(self.port) };
        let mask = 1 << (self.pin as u32);
        port.interrupt_disable.set(mask);
    }
}

interrupt_handler!(gpio0_combined_handler, 81);

interrupt_handler!(gpio0_0_handler, 65);
interrupt_handler!(gpio0_1_handler, 66);
<|endoftext|>


<commit_before>use bullet::{BulletDrawable, bullets::Bullets};
use cgmath;
use cgmath::{Deg, Point2};
use character::{CharacterDrawable, controls::CharacterInputState};
use critter::CritterData;
use data;
use game::constants::{ASPECT_RATIO, NORMAL_DEATH_SPRITE_OFFSET, SPRITE_OFFSET, ZOMBIE_SHEET_TOTAL_WIDTH, ZOMBIE_STILL_SPRITE_OFFSET};
use game::get_random_bool;
use gfx;
use gfx_app::{ColorFormat, DepthFormat};
use graphics::{camera::CameraInputState, can_move_to_tile, Dimensions, direction, direction_movement, get_orientation, load_texture, orientation::{Orientation, Stance}, overlaps};
use shaders::{CharacterSheet, critter_pipeline, Position, Projection, VertexData};
use specs;
use specs::prelude::{ReadStorage, Read, WriteStorage};
use zombie::zombies::Zombies;

pub mod zombies;

const SHADER_VERT: &[u8] = include_bytes!("../shaders/character.v.glsl");
const SHADER_FRAG: &[u8] = include_bytes!("../shaders/character.f.glsl");

#[derive(Debug, Clone)]
pub struct ZombieDrawable {
  projection: Projection,
  pub position: Position,
  previous_position: Position,
  orientation: Orientation,
  pub stance: Stance,
  direction: Orientation,
  pub movement_direction: Point2<f32>,
  zombie_idx: usize,
  zombie_death_idx: usize,
  is_colliding: bool,
}

impl ZombieDrawable {
  pub fn new(position: Position) -> ZombieDrawable {
    let view = Dimensions::get_view_matrix();
    ZombieDrawable {
      projection: Projection {
        model: view.into(),
        view: view.into(),
        proj: cgmath::perspective(Deg(60.0f32), ASPECT_RATIO, 0.1, 4000.0).into(),
      },
      position,
      previous_position: Position::new([0.0, 0.0]),
      orientation: Orientation::Left,
      stance: Stance::Still,
      direction: Orientation::Left,
      movement_direction: Point2::new(0.0, 0.0),
      zombie_idx: 0,
      zombie_death_idx: 0,
      is_colliding: false
    }
  }

  pub fn update(&mut self, world_to_clip: &Projection, ci: &CharacterInputState, character: &CharacterDrawable) {
    self.projection = *world_to_clip;

    let offset_delta =
      Position::new([
        ci.x_movement - self.previous_position.position[0],
        ci.y_movement - self.previous_position.position[1]
      ]);

    self.previous_position = Position::new([
      ci.x_movement,
      ci.y_movement
    ]);
    let movement_speed = 1.4;

    let is_dead = self.stance != Stance::NormalDeath && self.stance != Stance::CriticalDeath;
    if is_dead {
      let dir = direction(
        Point2::new(self.position.position[0], self.position.position[1]),
        Point2::new(character.position.position[0], character.position.position[1])
      );

      self.direction = get_orientation(dir);

      self.movement_direction = direction_movement(dir);
      self.stance = Stance::Walking;
    } else {
      self.movement_direction = Point2::new(0.0, 0.0);
    }

    let new_pos = Position::new([
      self.position.position[0] + offset_delta.position[0] + (self.movement_direction.x * movement_speed),
      self.position.position[1] + offset_delta.position[1] + (self.movement_direction.y * movement_speed)
    ]);

    let new_pos_without_movement = Position::new([
      self.position.position[0] + offset_delta.position[0],
      self.position.position[1] + offset_delta.position[1]
    ]);

    let tile_pos = Position::new([ci.x_movement - new_pos.position[0], ci.y_movement - new_pos.position[1]]);

    if can_move_to_tile(tile_pos) {
      self.position = new_pos;
    } else {
      self.position = new_pos_without_movement;
    }
  }

  pub fn check_bullet_hits(&mut self, bullets: &[BulletDrawable]) {
    bullets.iter().for_each(|bullet| {
      if overlaps(self.position, bullet.position, 20.0, 40.0) && self.stance != Stance::NormalDeath && self.stance != Stance::CriticalDeath {
        self.stance =
          if get_random_bool() {
            Stance::NormalDeath
          } else {
            Stance::CriticalDeath
          };
      }
    });
  }

  pub fn update_alive_idx(&mut self, max_idx: usize) {
    if self.zombie_idx < max_idx {
      self.zombie_idx += 1;
    } else {
      self.zombie_idx = 0;
    }
  }

  pub fn update_death_idx(&mut self, max_idx: usize) {
    if self.zombie_death_idx < max_idx {
      self.zombie_death_idx += 1;
    }
  }
}

pub struct ZombieDrawSystem<R: gfx::Resources> {
  bundle: gfx::pso::bundle::Bundle<R, critter_pipeline::Data<R>>,
  data: Vec<CritterData>,
}

impl<R: gfx::Resources> ZombieDrawSystem<R> {
  pub fn new<F>(factory: &mut F,
                rtv: gfx::handle::RenderTargetView<R, ColorFormat>,
                dsv: gfx::handle::DepthStencilView<R, DepthFormat>) -> ZombieDrawSystem<R>
                where F: gfx::Factory<R> {
    use gfx::traits::FactoryExt;

    let zombie_bytes = include_bytes!("../../assets/zombie.png");

    let vertex_data: [VertexData; 4] = [
      VertexData::new([-25.0, -35.0], [0.0, 1.0]),
      VertexData::new([25.0, -35.0], [1.0, 1.0]),
      VertexData::new([25.0, 35.0], [1.0, 0.0]),
      VertexData::new([-25.0, 35.0], [0.0, 0.0]),
    ];

    let index_data: [u16; 6] = [0, 1, 2, 2, 3, 0];

    let (vertex_buf, slice) = factory.create_vertex_buffer_with_slice(&vertex_data[..], &index_data[..]);

    let char_texture = load_texture(factory, zombie_bytes);
    let pso = factory
      .create_pipeline_simple(SHADER_VERT,
                              SHADER_FRAG,
                              critter_pipeline::new())
      .unwrap();

    let pipeline_data = critter_pipeline::Data {
      vbuf: vertex_buf,
      projection_cb: factory.create_constant_buffer(1),
      position_cb: factory.create_constant_buffer(1),
      character_sprite_cb: factory.create_constant_buffer(1),
      charactersheet: (char_texture, factory.create_sampler_linear()),
      out_color: rtv,
      out_depth: dsv,
    };

    let data = data::load_zombie();

    ZombieDrawSystem {
      bundle: gfx::Bundle::new(slice, pso, pipeline_data),
      data,
    }
  }

  fn get_next_sprite(&self, drawable: &mut ZombieDrawable) -> CharacterSheet {
    let zombie_sprite = match drawable.stance {
      Stance::Still => {
        let sprite_idx = (drawable.direction as usize * 4 + drawable.zombie_idx) as usize;
        (&self.data[sprite_idx], sprite_idx)
      },
      Stance::Walking if drawable.orientation != Orientation::Still => {
        let sprite_idx = (drawable.direction as usize * 8 + drawable.zombie_idx + ZOMBIE_STILL_SPRITE_OFFSET) as usize;
        (&self.data[sprite_idx], sprite_idx)
      },
      Stance::NormalDeath if drawable.orientation != Orientation::Still => {
        let sprite_idx = (drawable.direction as usize * 6 + drawable.zombie_death_idx + NORMAL_DEATH_SPRITE_OFFSET) as usize;
        (&self.data[sprite_idx], sprite_idx)
      },
      Stance::CriticalDeath if drawable.orientation != Orientation::Still => {
        let sprite_idx = (drawable.direction as usize * 8 + drawable.zombie_death_idx) as usize;
        (&self.data[sprite_idx], sprite_idx)
      },
      _ => {
        drawable.direction = drawable.orientation;
        let sprite_idx = (drawable.orientation as usize * 8 + drawable.zombie_idx + ZOMBIE_STILL_SPRITE_OFFSET) as usize;
        (&self.data[sprite_idx], sprite_idx)
      },
    };

    let (y_div, row_idx) =
      if drawable.stance == Stance::NormalDeath || drawable.stance == Stance::CriticalDeath {
        (0.0, 2)
      } else {
        (1.0, 2)
      };

    let elements_x = ZOMBIE_SHEET_TOTAL_WIDTH / (zombie_sprite.0.data[2] + SPRITE_OFFSET);
    CharacterSheet {
      x_div: elements_x,
      y_div,
      row_idx,
      index: zombie_sprite.1 as f32,
    }
  }

  pub fn draw<C>(&mut self,
                 mut drawable: &mut ZombieDrawable,
                 encoder: &mut gfx::Encoder<R, C>)
                 where C: gfx::CommandBuffer<R> {
    encoder.update_constant_buffer(&self.bundle.data.projection_cb, &drawable.projection);
    encoder.update_constant_buffer(&self.bundle.data.position_cb, &drawable.position);
    encoder.update_constant_buffer(&self.bundle.data.character_sprite_cb,
                                   &self.get_next_sprite(&mut drawable));
    self.bundle.encode(encoder);
  }
}

#[derive(Debug)]
pub struct PreDrawSystem;

impl PreDrawSystem {
  pub fn new() -> PreDrawSystem {
    PreDrawSystem {}
  }
}

impl<'a> specs::prelude::System<'a> for PreDrawSystem {
  #[cfg_attr(feature = "cargo-clippy", allow(type_complexity))]
  type SystemData = (WriteStorage<'a, Zombies>,
                     ReadStorage<'a, CameraInputState>,
                     ReadStorage<'a, CharacterInputState>,
                     ReadStorage<'a, CharacterDrawable>,
                     ReadStorage<'a, Bullets>,
                     Read<'a, Dimensions>);

  fn run(&mut self, (mut zombies, camera_input, character_input, character, bullets, dim): Self::SystemData) {
    use specs::join::Join;

    for (zs, camera, ci, c, bs) in (&mut zombies, &camera_input, &character_input, &character, &bullets).join() {
      let world_to_clip = dim.world_to_projection(camera);

      for z in &mut zs.zombies {
        z.update(&world_to_clip, ci, c);
        z.check_bullet_hits(&bs.bullets);
      }
    }
  }
}
<commit_msg>Iniatialize empty_tiles for proper pathfinding<commit_after>use bullet::{BulletDrawable, bullets::Bullets};
use cgmath;
use cgmath::{Deg, Point2};
use character::{CharacterDrawable, controls::CharacterInputState};
use critter::CritterData;
use data;
use game::constants::{ASPECT_RATIO, NORMAL_DEATH_SPRITE_OFFSET, SPRITE_OFFSET, TERRAIN_OBJECTS, ZOMBIE_SHEET_TOTAL_WIDTH, ZOMBIE_STILL_SPRITE_OFFSET};
use game::get_random_bool;
use gfx;
use gfx_app::{ColorFormat, DepthFormat};
use graphics::{camera::CameraInputState, can_move_to_tile, Dimensions, direction, direction_movement, get_orientation, load_texture, orientation::{Orientation, Stance}, overlaps};
use shaders::{CharacterSheet, critter_pipeline, Position, Projection, VertexData};
use specs;
use specs::prelude::{Read, ReadStorage, WriteStorage};
use zombie::zombies::Zombies;

pub mod zombies;

const SHADER_VERT: &[u8] = include_bytes!("../shaders/character.v.glsl");
const SHADER_FRAG: &[u8] = include_bytes!("../shaders/character.f.glsl");

#[derive(Debug, Clone)]
pub struct ZombieDrawable {
  projection: Projection,
  pub position: Position,
  previous_position: Position,
  orientation: Orientation,
  pub stance: Stance,
  direction: Orientation,
  pub movement_direction: Point2<f32>,
  zombie_idx: usize,
  zombie_death_idx: usize,
  is_colliding: bool,
  empty_tiles: Vec<Point2<usize>>,
}

impl ZombieDrawable {
  pub fn new(position: Position) -> ZombieDrawable {
    let view = Dimensions::get_view_matrix();
    let mut empty_tiles = [Point2::new(0,0); 64*64].to_vec();

    for x in 0..64 {
      for y in 0..64 {
        empty_tiles[y + x * 64] = Point2::new(x, y);
      }
    }

    empty_tiles = empty_tiles.iter()
                             .filter(|ref e| !TERRAIN_OBJECTS.contains(&[e.x, e.y]))
                             .cloned()
                             .collect();

    ZombieDrawable {
      projection: Projection {
        model: view.into(),
        view: view.into(),
        proj: cgmath::perspective(Deg(60.0f32), ASPECT_RATIO, 0.1, 4000.0).into(),
      },
      position,
      previous_position: Position::new([0.0, 0.0]),
      orientation: Orientation::Left,
      stance: Stance::Still,
      direction: Orientation::Left,
      movement_direction: Point2::new(0.0, 0.0),
      zombie_idx: 0,
      zombie_death_idx: 0,
      is_colliding: false,
      empty_tiles,
    }
  }

  pub fn update(&mut self, world_to_clip: &Projection, ci: &CharacterInputState, character: &CharacterDrawable) {
    self.projection = *world_to_clip;

    let offset_delta =
      Position::new([
        ci.x_movement - self.previous_position.position[0],
        ci.y_movement - self.previous_position.position[1]
      ]);

    self.previous_position = Position::new([
      ci.x_movement,
      ci.y_movement
    ]);
    let movement_speed = 1.4;

    let is_dead = self.stance != Stance::NormalDeath && self.stance != Stance::CriticalDeath;
    if is_dead {
      let dir = direction(
        Point2::new(self.position.position[0], self.position.position[1]),
        Point2::new(character.position.position[0], character.position.position[1])
      );

      self.direction = get_orientation(dir);

      self.movement_direction = direction_movement(dir);
      self.stance = Stance::Walking;
    } else {
      self.movement_direction = Point2::new(0.0, 0.0);
    }

    let new_pos = Position::new([
      self.position.position[0] + offset_delta.position[0] + (self.movement_direction.x * movement_speed),
      self.position.position[1] + offset_delta.position[1] + (self.movement_direction.y * movement_speed)
    ]);

    let new_pos_without_movement = Position::new([
      self.position.position[0] + offset_delta.position[0],
      self.position.position[1] + offset_delta.position[1]
    ]);

    let tile_pos = Position::new([ci.x_movement - new_pos.position[0], ci.y_movement - new_pos.position[1]]);

    if can_move_to_tile(tile_pos) {
      self.position = new_pos;
    } else {
      self.position = new_pos_without_movement;
    }
  }

  pub fn check_bullet_hits(&mut self, bullets: &[BulletDrawable]) {
    bullets.iter().for_each(|bullet| {
      if overlaps(self.position, bullet.position, 20.0, 40.0) && self.stance != Stance::NormalDeath && self.stance != Stance::CriticalDeath {
        self.stance =
          if get_random_bool() {
            Stance::NormalDeath
          } else {
            Stance::CriticalDeath
          };
      }
    });
  }

  pub fn update_alive_idx(&mut self, max_idx: usize) {
    if self.zombie_idx < max_idx {
      self.zombie_idx += 1;
    } else {
      self.zombie_idx = 0;
    }
  }

  pub fn update_death_idx(&mut self, max_idx: usize) {
    if self.zombie_death_idx < max_idx {
      self.zombie_death_idx += 1;
    }
  }
}

pub struct ZombieDrawSystem<R: gfx::Resources> {
  bundle: gfx::pso::bundle::Bundle<R, critter_pipeline::Data<R>>,
  data: Vec<CritterData>,
}

impl<R: gfx::Resources> ZombieDrawSystem<R> {
  pub fn new<F>(factory: &mut F,
                rtv: gfx::handle::RenderTargetView<R, ColorFormat>,
                dsv: gfx::handle::DepthStencilView<R, DepthFormat>) -> ZombieDrawSystem<R>
                where F: gfx::Factory<R> {
    use gfx::traits::FactoryExt;

    let zombie_bytes = include_bytes!("../../assets/zombie.png");

    let vertex_data: [VertexData; 4] = [
      VertexData::new([-25.0, -35.0], [0.0, 1.0]),
      VertexData::new([25.0, -35.0], [1.0, 1.0]),
      VertexData::new([25.0, 35.0], [1.0, 0.0]),
      VertexData::new([-25.0, 35.0], [0.0, 0.0]),
    ];

    let index_data: [u16; 6] = [0, 1, 2, 2, 3, 0];

    let (vertex_buf, slice) = factory.create_vertex_buffer_with_slice(&vertex_data[..], &index_data[..]);

    let char_texture = load_texture(factory, zombie_bytes);
    let pso = factory
      .create_pipeline_simple(SHADER_VERT,
                              SHADER_FRAG,
                              critter_pipeline::new())
      .unwrap();

    let pipeline_data = critter_pipeline::Data {
      vbuf: vertex_buf,
      projection_cb: factory.create_constant_buffer(1),
      position_cb: factory.create_constant_buffer(1),
      character_sprite_cb: factory.create_constant_buffer(1),
      charactersheet: (char_texture, factory.create_sampler_linear()),
      out_color: rtv,
      out_depth: dsv,
    };

    let data = data::load_zombie();

    ZombieDrawSystem {
      bundle: gfx::Bundle::new(slice, pso, pipeline_data),
      data,
    }
  }

  fn get_next_sprite(&self, drawable: &mut ZombieDrawable) -> CharacterSheet {
    let zombie_sprite = match drawable.stance {
      Stance::Still => {
        let sprite_idx = (drawable.direction as usize * 4 + drawable.zombie_idx) as usize;
        (&self.data[sprite_idx], sprite_idx)
      },
      Stance::Walking if drawable.orientation != Orientation::Still => {
        let sprite_idx = (drawable.direction as usize * 8 + drawable.zombie_idx + ZOMBIE_STILL_SPRITE_OFFSET) as usize;
        (&self.data[sprite_idx], sprite_idx)
      },
      Stance::NormalDeath if drawable.orientation != Orientation::Still => {
        let sprite_idx = (drawable.direction as usize * 6 + drawable.zombie_death_idx + NORMAL_DEATH_SPRITE_OFFSET) as usize;
        (&self.data[sprite_idx], sprite_idx)
      },
      Stance::CriticalDeath if drawable.orientation != Orientation::Still => {
        let sprite_idx = (drawable.direction as usize * 8 + drawable.zombie_death_idx) as usize;
        (&self.data[sprite_idx], sprite_idx)
      },
      _ => {
        drawable.direction = drawable.orientation;
        let sprite_idx = (drawable.orientation as usize * 8 + drawable.zombie_idx + ZOMBIE_STILL_SPRITE_OFFSET) as usize;
        (&self.data[sprite_idx], sprite_idx)
      },
    };

    let (y_div, row_idx) =
      if drawable.stance == Stance::NormalDeath || drawable.stance == Stance::CriticalDeath {
        (0.0, 2)
      } else {
        (1.0, 2)
      };

    let elements_x = ZOMBIE_SHEET_TOTAL_WIDTH / (zombie_sprite.0.data[2] + SPRITE_OFFSET);
    CharacterSheet {
      x_div: elements_x,
      y_div,
      row_idx,
      index: zombie_sprite.1 as f32,
    }
  }

  pub fn draw<C>(&mut self,
                 mut drawable: &mut ZombieDrawable,
                 encoder: &mut gfx::Encoder<R, C>)
                 where C: gfx::CommandBuffer<R> {
    encoder.update_constant_buffer(&self.bundle.data.projection_cb, &drawable.projection);
    encoder.update_constant_buffer(&self.bundle.data.position_cb, &drawable.position);
    encoder.update_constant_buffer(&self.bundle.data.character_sprite_cb,
                                   &self.get_next_sprite(&mut drawable));
    self.bundle.encode(encoder);
  }
}

#[derive(Debug)]
pub struct PreDrawSystem;

impl PreDrawSystem {
  pub fn new() -> PreDrawSystem {
    PreDrawSystem {}
  }
}

impl<'a> specs::prelude::System<'a> for PreDrawSystem {
  #[cfg_attr(feature = "cargo-clippy", allow(type_complexity))]
  type SystemData = (WriteStorage<'a, Zombies>,
                     ReadStorage<'a, CameraInputState>,
                     ReadStorage<'a, CharacterInputState>,
                     ReadStorage<'a, CharacterDrawable>,
                     ReadStorage<'a, Bullets>,
                     Read<'a, Dimensions>);

  fn run(&mut self, (mut zombies, camera_input, character_input, character, bullets, dim): Self::SystemData) {
    use specs::join::Join;

    for (zs, camera, ci, c, bs) in (&mut zombies, &camera_input, &character_input, &character, &bullets).join() {
      let world_to_clip = dim.world_to_projection(camera);

      for z in &mut zs.zombies {
        z.update(&world_to_clip, ci, c);
        z.check_bullet_hits(&bs.bullets);
      }
    }
  }
}
<|endoftext|>


<commit_before>#![doc(html_root_url="https://drakulix.github.io/zwreec/",
    html_logo_url="https://dl.dropboxusercontent.com/u/70410095/zwreec/logo.png")]
//! Twee-to-Zcode compile library
//!
//! Zwreec is a compiler for [interactive fiction](http://en.wikipedia.org/wiki/Interactive_fiction)
//! in the Twee format (created by the [Twine software](http://en.wikipedia.org/wiki/Twine_(software)))
//! to [Z-Machine](http://en.wikipedia.org/wiki/Z-machine) instructions (Zcode)
//! which can be run with interpreters like [frotz](http://frotz.sourceforge.net).
//!
//! This library was developed as part of the course "Softwareprojekt
//! Übersetzerbau" in 2015.
//!
//! # Requirements and Usage
//!
//! The library uses a [fork of rustlex](https://github.com/Farthen/rustlex) to
//! to do lexical analysis. Since rustlex requires the Rust Nightly Builds, so
//! does zwreec. More precisely, this library was *only* developed and tested
//! against Rust 1.2.0-nightly, build 2015-06-01. You can install this specific
//! version using:
//!
//! ```sh
//! $ curl -s https://static.rust-lang.org/rustup.sh | sh -s -- --channel=nightly --date=2015-06-02
//! ```
//!
//! To use zwreec in your project you can add it as a dependency to your `Cargo.toml`.
//!
//! ```toml
//! [dependencies.zwreec]
//! git = "https:://github.com/Drakulix/zwreec"
//! ```
//!
//! Then you can use it in your crate root.
//!
//! ```rust
//! extern crate zwreec;
//! ```
//!
//! # Features
//!
//! Only rudimentary Twee features are supported right now. This is about to change in the upcoming weeks.
//! Check the github issues for more information on the currently supported features.
//!
//! # Example
//!
//! The following example is itself a full working command line compiler. It is a simpler
//! version of the [Reference Binary Implementation](#reference-binary-implementation).
//!
//! ```no_run
//! extern crate zwreec;
//!
//! use std::env;
//! use std::error::Error;
//! use std::fs::File;
//! use std::path::Path;
//!
//! fn main() {
//!     let mut args: Vec<String> = env::args().collect();
//!
//!     if args.len() != 2 { panic!("Need exactly one input file!"); }
//!
//!     let cfg = zwreec::config::Config::default_config();
//!     let mut input = match File::open(Path::new(&args[1])) {
//!         Ok(file) => file,
//!         Err(why) => { panic!("Couldn't open input: {}", Error::description(&why)); }
//!     };
//!     let mut output = match File::create(Path::new("a.z8")) {
//!         Ok(file) => file,
//!         Err(why) => { panic!("Couldn't open output: {}", Error::description(&why)); }
//!     };
//!
//!     zwreec::compile(cfg, &mut input, &mut output);
//! }
//! ```
//!
//! # Reference Binary Implementation
//!
//! Zwreecs [Github-Repository](https://github.com/Drakulix/zwreec) contains a
//! reference binary implementation that uses this library and provides a simple
//! command line interface to compile Twee files.
//!
//! To build the binary, you need the rust version as outlined in [Requirements and
//! Usage](#requirements-and-usage). Then you can build the binary using
//!
//! ```sh
//! $ cargo build
//! ```
//!
//! The resulting binary can be found at `target/debug/zwreec`.
//!
//! # Logging
//!
//! Zwreec uses the logging facade provided by
//! [log](../log/index.html). The reference binary implementation of zwreec also includes an
//! implementation of the `Log` trait.

#![feature(plugin)]
#![plugin(rustlex)]
#[allow(plugin_as_library)]
extern crate rustlex;
#[macro_use] extern crate log;
extern crate time;
extern crate term;
extern crate getopts;

pub mod config;
pub mod frontend;
pub mod backend;
pub use backend::zcode::zfile;
pub mod utils;

use frontend::codegen;

use config::{Config,TestCase};
use std::io::{Read,Write};

#[allow(unused_variables)]
pub fn compile<R: Read, W: Write>(cfg: Config, input: &mut R, output: &mut W) {
    // tokenize
    let tokens = frontend::lexer::lex(&cfg, input);

    //create parser
    let parser = frontend::parser::Parser::new(&cfg);

    //build up ast from tokens
    let ast = frontend::ast::AST::build(parser.parse(tokens.inspect(|ref token| {
        debug!("{:?}", token);
    })));
    ast.print(false);

    // create code
    codegen::generate_zcode(&cfg, ast, output);
}

#[allow(unused_variables)]
pub fn test_library<R: Read, W: Write>(cfg: Config, input: &mut Option<R>, output: &mut Option<W>) {
    for case in cfg.test_cases {
        match case {
            TestCase::ZcodeBackend => {
                match output.as_mut() {
                     Some(o) => backend::zcode::temp_create_zcode_example(o),
                     None => error!("TestCase::ZcodeBackend requires output!"),
                }
            }
        }
    }
}
<commit_msg>Add function comments to main lib<commit_after>#![doc(html_root_url="https://drakulix.github.io/zwreec/",
    html_logo_url="https://dl.dropboxusercontent.com/u/70410095/zwreec/logo.png")]
//! Twee-to-Zcode compile library
//!
//! Zwreec is a compiler for [interactive fiction](http://en.wikipedia.org/wiki/Interactive_fiction)
//! in the Twee format (created by the [Twine software](http://en.wikipedia.org/wiki/Twine_(software)))
//! to [Z-Machine](http://en.wikipedia.org/wiki/Z-machine) instructions (Zcode)
//! which can be run with interpreters like [frotz](http://frotz.sourceforge.net).
//!
//! This library was developed as part of the course "Softwareprojekt
//! Übersetzerbau" in 2015.
//!
//! # Requirements and Usage
//!
//! The library uses a [fork of rustlex](https://github.com/Farthen/rustlex) to
//! to do lexical analysis. Since rustlex requires the Rust Nightly Builds, so
//! does zwreec. More precisely, this library was *only* developed and tested
//! against Rust 1.2.0-nightly, build 2015-06-01. You can install this specific
//! version using:
//!
//! ```sh
//! $ curl -s https://static.rust-lang.org/rustup.sh | sh -s -- --channel=nightly --date=2015-06-02
//! ```
//!
//! To use zwreec in your project you can add it as a dependency to your `Cargo.toml`.
//!
//! ```toml
//! [dependencies.zwreec]
//! git = "https:://github.com/Drakulix/zwreec"
//! ```
//!
//! Then you can use it in your crate root.
//!
//! ```rust
//! extern crate zwreec;
//! ```
//!
//! # Features
//!
//! Only rudimentary Twee features are supported right now. This is about to change in the upcoming weeks.
//! Check the github issues for more information on the currently supported features.
//!
//! # Example
//!
//! The following example is itself a full working command line compiler. It is a simpler
//! version of the [Reference Binary Implementation](#reference-binary-implementation).
//!
//! ```no_run
//! extern crate zwreec;
//!
//! use std::env;
//! use std::error::Error;
//! use std::fs::File;
//! use std::path::Path;
//!
//! fn main() {
//!     let mut args: Vec<String> = env::args().collect();
//!
//!     if args.len() != 2 { panic!("Need exactly one input file!"); }
//!
//!     let cfg = zwreec::config::Config::default_config();
//!     let mut input = match File::open(Path::new(&args[1])) {
//!         Ok(file) => file,
//!         Err(why) => { panic!("Couldn't open input: {}", Error::description(&why)); }
//!     };
//!     let mut output = match File::create(Path::new("a.z8")) {
//!         Ok(file) => file,
//!         Err(why) => { panic!("Couldn't open output: {}", Error::description(&why)); }
//!     };
//!
//!     zwreec::compile(cfg, &mut input, &mut output);
//! }
//! ```
//!
//! # Reference Binary Implementation
//!
//! Zwreecs [Github-Repository](https://github.com/Drakulix/zwreec) contains a
//! reference binary implementation that uses this library and provides a simple
//! command line interface to compile Twee files.
//!
//! To build the binary, you need the rust version as outlined in [Requirements and
//! Usage](#requirements-and-usage). Then you can build the binary using
//!
//! ```sh
//! $ cargo build
//! ```
//!
//! The resulting binary can be found at `target/debug/zwreec`.
//!
//! # Logging
//!
//! Zwreec uses the logging facade provided by
//! [log](../log/index.html). The reference binary implementation of zwreec also includes an
//! implementation of the `Log` trait.

#![feature(plugin)]
#![plugin(rustlex)]
#[allow(plugin_as_library)]
extern crate rustlex;
#[macro_use] extern crate log;
extern crate time;
extern crate term;
extern crate getopts;

pub mod config;
pub mod frontend;
pub mod backend;
pub mod utils;

use config::{Config,TestCase};
use std::io::{Read,Write};

/// Compiles a Twee Input to Zcode
///
/// This is the main call into the Zwreec compiler. It will process `input: Read`
/// by calling the different parts of the compiler-chain, notably `frontend::lexer`
/// to generate a tokenstream, `frontend::parser` and `frontend::ast` to generate
/// the Abstract Syntax Tree and lastly `frontend::codegen` to generate the Zcode.
///
/// # Example
///
/// ```no_run
/// # use std::env;
/// # use std::fs::File;
/// # use std::path::Path;
/// # let mut args: Vec<String> = env::args().collect();
/// #
/// # if args.len() != 2 { panic!("Need exactly one input file!"); }
/// #
/// let cfg = zwreec::config::Config::default_config();
/// let mut input = File::open(Path::new(&args[1])).unwrap();
/// let mut output = File::create(Path::new("a.z8")).unwrap();
///
/// zwreec::compile(cfg, &mut input, &mut output);
/// ```
#[allow(unused_variables)]
pub fn compile<R: Read, W: Write>(cfg: Config, input: &mut R, output: &mut W) {
    // tokenize
    let tokens = frontend::lexer::lex(&cfg, input);

    //create parser
    let parser = frontend::parser::Parser::new(&cfg);

    //build up ast from tokens
    let ast = frontend::ast::AST::build(parser.parse(tokens.inspect(|ref token| {
        debug!("{:?}", token);
    })));
    ast.print(false);

    // create code
    frontend::codegen::generate_zcode(&cfg, ast, output);
}

/// Run internal library tests
///
/// This function is used to circumvent certain parts of the compiler toolchain.
/// It currently only processes `TestCase::ZcodeBackend` which creates a Zcode
/// file using all available OP-Codes.
///
/// **Warning:** This function should be considered unstable and might be removed
/// in later versions.
///
/// # Example
///
/// ```
/// # use std::env;
/// # use std::fs::File;
/// # use std::path::Path;
/// let mut cfg = zwreec::config::Config::default_config();
/// cfg.test_cases.push(zwreec::config::TestCase::ZcodeBackend);
///
/// let mut input: Option<File> = None;
/// let mut output = Some(File::create(Path::new("a.z8")).unwrap());
///
/// zwreec::test_library(cfg, &mut input, &mut output);
/// ```
#[allow(unused_variables)]
pub fn test_library<R: Read, W: Write>(cfg: Config, input: &mut Option<R>, output: &mut Option<W>) {
    for case in cfg.test_cases {
        match case {
            TestCase::ZcodeBackend => {
                match output.as_mut() {
                     Some(o) => backend::zcode::temp_create_zcode_example(o),
                     None => error!("TestCase::ZcodeBackend requires output!"),
                }
            }
        }
    }
}
<|endoftext|>