<commit_before>mod crc;
#[macro_use]
mod macros;

pub use self::crc::{crc8, crc16};

// Convert one to four byte slices in an unsigned 32-bit number.
//
// NOTE: This assumes big-endian since most numbers in the FLAC binary are
// that endianness.
#[inline]
pub fn to_u32(bytes: &[u8]) -> u32 {
  let length = bytes.len();

  assert!(length <= 4);

  (0..length).fold(0, |result, i|
    result + ((bytes[i] as u32) << ((length - 1 - i) * 8))
  )
}

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

  #[test]
  #[should_panic]
  fn test_panic_to_u32() {
    to_u32(&[0x00, 0x00, 0x00, 0x00, 0x00]);
  }

  #[test]
  fn test_to_u32() {
    let bytes = [0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef];

    assert_eq!(to_u32(&bytes[0..1]), 0x00000001);
    assert_eq!(to_u32(&bytes[3..5]), 0x00006789);
    assert_eq!(to_u32(&bytes[1..4]), 0x00234567);
    assert_eq!(to_u32(&bytes[4..]), 0x89abcdef);
  }
}
<commit_msg>Fix comment about `to_u32`<commit_after>mod crc;
#[macro_use]
mod macros;

pub use self::crc::{crc8, crc16};

// Convert one to four byte slices into an unsigned 32-bit number.
//
// NOTE: This assumes big-endian since most numbers in the FLAC binary are
// that endianness.
#[inline]
pub fn to_u32(bytes: &[u8]) -> u32 {
  let length = bytes.len();

  assert!(length <= 4);

  (0..length).fold(0, |result, i|
    result + ((bytes[i] as u32) << ((length - 1 - i) * 8))
  )
}

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

  #[test]
  #[should_panic]
  fn test_panic_to_u32() {
    to_u32(&[0x00, 0x00, 0x00, 0x00, 0x00]);
  }

  #[test]
  fn test_to_u32() {
    let bytes = [0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef];

    assert_eq!(to_u32(&bytes[0..1]), 0x00000001);
    assert_eq!(to_u32(&bytes[3..5]), 0x00006789);
    assert_eq!(to_u32(&bytes[1..4]), 0x00234567);
    assert_eq!(to_u32(&bytes[4..]), 0x89abcdef);
  }
}
<|endoftext|>


<commit_before>/*
Copyright (c) 2016-2017, Robert Ou <rqou@robertou.com> and contributors
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice,
   this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
   this list of conditions and the following disclaimer in the documentation
   and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

// Xilinx JED file I/O

use std::num::Wrapping;
use std::str;

use *;
use bitstream::{read_32_bitstream_logical};

#[derive(Eq, PartialEq, Copy, Clone)]
enum Ternary {
    Zero,
    One,
    Undef,
}

const STX: u8 = 0x02;
const ETX: u8 = 0x03;

// Reads .jed file and outputs the fuses as an array of booleans and optional device name
pub fn read_jed(in_bytes: &[u8]) -> Result<(Vec<bool>, Option<String>), &'static str> {
    let mut fuse_csum = Wrapping(0u16);
    let mut fuse_expected_csum = None;
    let mut file_csum = Wrapping(0u16);
    let mut num_fuses: u32 = 0;
    let mut device = None;
    let mut jed_stx: usize = 0;
    let mut jed_etx: usize;
    let mut fuses_ternary = vec![];
    let mut default_fuse = Ternary::Undef;

    // Find STX
    while in_bytes[jed_stx] != STX {
        jed_stx += 1;
        if jed_stx >= in_bytes.len() {
            return Err("STX not found");
        }
    }

    // Checksum and find ETX
    jed_etx = jed_stx;
    while in_bytes[jed_etx] != ETX {
        file_csum += Wrapping(in_bytes[jed_etx] as u16);
        jed_etx += 1;
        if jed_etx >= in_bytes.len() {
            return Err("ETX not found");
        }
    }
    // Add the ETX to the checksum too
    file_csum += Wrapping(ETX as u16);

    // Check the checksum
    if jed_etx + 4 >= in_bytes.len() {
        return Err("unexpected end of file - checksum");
    }
    let csum_expected = &in_bytes[jed_etx + 1..jed_etx + 5];
    let csum_expected = str::from_utf8(csum_expected);
    if csum_expected.is_err() {
        return Err("invalid character encountered - file checksum");
    }
    let csum_expected = u16::from_str_radix(csum_expected.unwrap(), 16);
    if csum_expected.is_err() {
        return Err("invalid character encountered - file checksum");
    }
    let csum_expected = csum_expected.unwrap();
    if csum_expected != 0 && csum_expected != file_csum.0 {
        return Err("invalid file checksum");
    }

    // Make a str object out of the body
    let jed_body = str::from_utf8(&in_bytes[jed_stx + 1..jed_etx]);
    if jed_body.is_err() {
        return Err("invalid character encountered - non-ASCII");
    }
    let jed_body = jed_body.unwrap();

    // Ready to parse each line
    for l in jed_body.split('*') {
        let l = l.trim_matches(|c| c == ' ' || c == '\r' || c == '\n');
        if l.len() == 0 {
            // FIXME: Should we do something else here?
            // ignore empty fields
            continue;
        }

        // Now we can look at the first byte to figure out what we have
        match l.chars().next().unwrap() {
            'X' | 'J' => {}, // Do nothing, don't care about these
            'F' => {
                // Default state
                let (_, default_state_str) = l.split_at(1);
                default_fuse = match default_state_str {
                    "0" => Ternary::Zero,
                    "1" => Ternary::One,
                    _ => return Err("invalid character encountered - F field")
                }
            },
            'N' => {
                // Notes; we want to extract N DEVICE but otherwise ignore it
                let note_pieces = l.split(|c| c == ' ' || c == '\r' || c == '\n').collect::<Vec<_>>();
                if note_pieces.len() == 3 && note_pieces[1] == "DEVICE" {
                    device = Some(note_pieces[2].to_owned());
                }
            },
            'Q' => {
                // Look for QF
                if l.starts_with("QF") {
                    let (_, num_fuses_str) = l.split_at(2);
                    let num_fuses_maybe = u32::from_str_radix(num_fuses_str, 10);
                    if num_fuses_maybe.is_err() {
                        return Err("invalid character encountered - QF field");
                    }
                    num_fuses = num_fuses_maybe.unwrap();
                    fuses_ternary.reserve(num_fuses as usize);
                    for _ in 0..num_fuses {
                        fuses_ternary.push(Ternary::Undef);
                    }
                }
            },
            'L' => {
                // A set of fuses
                if num_fuses == 0 {
                    return Err("missing QF field");
                }

                let mut fuse_field_splitter = l.split(|c| c == ' ' || c == '\r' || c == '\n');
                let fuse_idx_str = fuse_field_splitter.next();
                let (_, fuse_idx_str) = fuse_idx_str.unwrap().split_at(1);
                let fuse_idx_maybe = u32::from_str_radix(fuse_idx_str, 10);
                if fuse_idx_maybe.is_err() {
                    return Err("invalid character encountered - fuse number");
                }
                let mut fuse_idx = fuse_idx_maybe.unwrap();

                let fuse_bits_part = fuse_field_splitter.next();
                if fuse_bits_part.is_none() {
                    return Err("malformed L field");
                }
                let fuse_bits_part = fuse_bits_part.unwrap();
                for fuse in fuse_bits_part.chars() {
                    match fuse {
                        '0' => {
                            if fuse_idx >= num_fuses {
                                return Err("invalid fuse index out of range");
                            }
                            fuses_ternary[fuse_idx as usize] = Ternary::Zero;
                            fuse_idx += 1;
                        },
                        '1' => {
                            if fuse_idx >= num_fuses {
                                return Err("invalid fuse index out of range");
                            }
                            fuses_ternary[fuse_idx as usize] = Ternary::One;
                            fuse_idx += 1;
                        },
                        ' ' | '\r' | '\n' => {}, // Do nothing
                        _ => return Err("invalid character encountered - fuse value"),
                    }
                }
            },
            'C' => {
                // Checksum
                let (_, csum_str) = l.split_at(1);
                if csum_str.len() != 4 {
                    return Err("malformed fuse checksum");
                }
                let csum_maybe = u16::from_str_radix(csum_str, 16);
                if csum_maybe.is_err() {
                    return Err("invalid character encountered - C");
                }
                fuse_expected_csum = Some(csum_maybe.unwrap());
            }
            _ => return Err("unrecognized field"),
        }
    }

    if default_fuse == Ternary::Undef {
        return Err("missing F field")
    }

    // Fill in the default values
    for x in &mut fuses_ternary {
        if *x == Ternary::Undef {
            *x = default_fuse;
        }
    }

    // Un-ternary it
    let fuses = fuses_ternary.iter().map(|&x| match x {
        Ternary::Zero => false,
        Ternary::One => true,
        _ => unreachable!(),
    }).collect::<Vec<_>>();

    // Fuse checksum
    if let Some(fuse_expected_csum) = fuse_expected_csum {
        for i in 0..num_fuses {
            if fuses[i as usize] {
                // Fuse is a 1 and contributes to the sum
                fuse_csum += Wrapping(1u16 << (i % 8));
            }
        }

        if fuse_expected_csum != fuse_csum.0 {
            return Err("invalid fuse checksum");
        }
    }

    Ok((fuses, device))
}

// Processes a fuse array into a bitstream object
pub fn process_jed(fuses: &[bool], device: &str) -> Result<XC2Bitstream, &'static str> {
    let device_split = device.split('-').collect::<Vec<_>>();

    if device_split.len() != 3 {
        return Err("malformed device name");
    }

    // TODO: Validate these
    let device_speed = device_split[1];
    let device_package = device_split[2];

    // Part name
    match device_split[0] {
        "XC2C32A" => {
            if fuses.len() != 12278 {
                return Err("wrong number of fuses");
            }
            let bits = read_32_bitstream_logical(fuses);
            if let Err(err) = bits {
                return Err(err);
            }
            Ok(XC2Bitstream {
                speed_grade: device_speed.to_owned(),
                package: device_package.to_owned(),
                bits: bits.unwrap(),
            })
        },
        _ => Err("unsupported part"),
    }
}

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

    #[test]
    fn read_no_stx() {
        let ret = read_jed(b"asdf");

        assert_eq!(ret, Err("STX not found"));
    }

    #[test]
    fn read_no_etx() {
        let ret = read_jed(b"asdf\x02fdsa");

        assert_eq!(ret, Err("ETX not found"));
    }

    #[test]
    fn read_no_csum() {
        let ret = read_jed(b"asdf\x02fdsa\x03");
        assert_eq!(ret, Err("unexpected end of file - checksum"));

        let ret = read_jed(b"asdf\x02fdsa\x03AAA");
        assert_eq!(ret, Err("unexpected end of file - checksum"));
    }

    #[test]
    fn read_bad_csum() {
        let ret = read_jed(b"asdf\x02fdsa\x03AAAA");

        assert_eq!(ret, Err("invalid file checksum"));
    }

    #[test]
    fn read_malformed_csum() {
        let ret = read_jed(b"asdf\x02fdsa\x03AAAZ");

        assert_eq!(ret, Err("invalid character encountered - file checksum"));
    }

    #[test]
    fn read_no_qf_no_f() {
        let ret = read_jed(b"\x02\x030000");

        assert_eq!(ret, Err("missing F field"));
    }

    #[test]
    fn read_empty_no_fuses() {
        let ret = read_jed(b"\x02F0*\x030000");

        assert_eq!(ret, Ok((vec![], None)));
    }

    #[test]
    fn read_bogus_f_command() {
        let ret = read_jed(b"\x02F2*\x030000");

        assert_eq!(ret, Err("invalid character encountered - F field"));
    }

    #[test]
    fn read_empty_with_device() {
        let ret = read_jed(b"\x02F0*N DEVICE asdf*\x030000");

        assert_eq!(ret, Ok((vec![], Some(String::from("asdf")))));
    }

    #[test]
    fn read_l_without_qf() {
        let ret = read_jed(b"\x02F0*L0 0*\x030000");

        assert_eq!(ret, Err("missing QF field"));
    }

    #[test]
    fn read_one_fuse() {
        let ret = read_jed(b"\x02F0*QF1*L0 1*\x030000");

        assert_eq!(ret, Ok((vec![true], None)));
    }

    #[test]
    fn read_one_fuse_csum_good() {
        let ret = read_jed(b"\x02F0*QF1*L0 1*C0001*\x030000");

        assert_eq!(ret, Ok((vec![true], None)));
    }

    #[test]
    fn read_one_fuse_csum_bad() {
        let ret = read_jed(b"\x02F0*QF1*L0 1*C0002*\x030000");

        assert_eq!(ret, Err("invalid fuse checksum"));
    }
}
<commit_msg>xc2bit: Fix .jed parsing with no F field<commit_after>/*
Copyright (c) 2016-2017, Robert Ou <rqou@robertou.com> and contributors
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice,
   this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
   this list of conditions and the following disclaimer in the documentation
   and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

// Xilinx JED file I/O

use std::num::Wrapping;
use std::str;

use *;
use bitstream::{read_32_bitstream_logical};

#[derive(Eq, PartialEq, Copy, Clone)]
enum Ternary {
    Zero,
    One,
    Undef,
}

const STX: u8 = 0x02;
const ETX: u8 = 0x03;

// Reads .jed file and outputs the fuses as an array of booleans and optional device name
pub fn read_jed(in_bytes: &[u8]) -> Result<(Vec<bool>, Option<String>), &'static str> {
    let mut fuse_csum = Wrapping(0u16);
    let mut fuse_expected_csum = None;
    let mut file_csum = Wrapping(0u16);
    let mut num_fuses: u32 = 0;
    let mut device = None;
    let mut jed_stx: usize = 0;
    let mut jed_etx: usize;
    let mut fuses_ternary = vec![];
    let mut default_fuse = Ternary::Undef;

    // Find STX
    while in_bytes[jed_stx] != STX {
        jed_stx += 1;
        if jed_stx >= in_bytes.len() {
            return Err("STX not found");
        }
    }

    // Checksum and find ETX
    jed_etx = jed_stx;
    while in_bytes[jed_etx] != ETX {
        file_csum += Wrapping(in_bytes[jed_etx] as u16);
        jed_etx += 1;
        if jed_etx >= in_bytes.len() {
            return Err("ETX not found");
        }
    }
    // Add the ETX to the checksum too
    file_csum += Wrapping(ETX as u16);

    // Check the checksum
    if jed_etx + 4 >= in_bytes.len() {
        return Err("unexpected end of file - checksum");
    }
    let csum_expected = &in_bytes[jed_etx + 1..jed_etx + 5];
    let csum_expected = str::from_utf8(csum_expected);
    if csum_expected.is_err() {
        return Err("invalid character encountered - file checksum");
    }
    let csum_expected = u16::from_str_radix(csum_expected.unwrap(), 16);
    if csum_expected.is_err() {
        return Err("invalid character encountered - file checksum");
    }
    let csum_expected = csum_expected.unwrap();
    if csum_expected != 0 && csum_expected != file_csum.0 {
        return Err("invalid file checksum");
    }

    // Make a str object out of the body
    let jed_body = str::from_utf8(&in_bytes[jed_stx + 1..jed_etx]);
    if jed_body.is_err() {
        return Err("invalid character encountered - non-ASCII");
    }
    let jed_body = jed_body.unwrap();

    // Ready to parse each line
    for l in jed_body.split('*') {
        let l = l.trim_matches(|c| c == ' ' || c == '\r' || c == '\n');
        if l.len() == 0 {
            // FIXME: Should we do something else here?
            // ignore empty fields
            continue;
        }

        // Now we can look at the first byte to figure out what we have
        match l.chars().next().unwrap() {
            'X' | 'J' => {}, // Do nothing, don't care about these
            'F' => {
                // Default state
                let (_, default_state_str) = l.split_at(1);
                default_fuse = match default_state_str {
                    "0" => Ternary::Zero,
                    "1" => Ternary::One,
                    _ => return Err("invalid character encountered - F field")
                }
            },
            'N' => {
                // Notes; we want to extract N DEVICE but otherwise ignore it
                let note_pieces = l.split(|c| c == ' ' || c == '\r' || c == '\n').collect::<Vec<_>>();
                if note_pieces.len() == 3 && note_pieces[1] == "DEVICE" {
                    device = Some(note_pieces[2].to_owned());
                }
            },
            'Q' => {
                // Look for QF
                if l.starts_with("QF") {
                    let (_, num_fuses_str) = l.split_at(2);
                    let num_fuses_maybe = u32::from_str_radix(num_fuses_str, 10);
                    if num_fuses_maybe.is_err() {
                        return Err("invalid character encountered - QF field");
                    }
                    num_fuses = num_fuses_maybe.unwrap();
                    fuses_ternary.reserve(num_fuses as usize);
                    for _ in 0..num_fuses {
                        fuses_ternary.push(Ternary::Undef);
                    }
                }
            },
            'L' => {
                // A set of fuses
                if num_fuses == 0 {
                    return Err("missing QF field");
                }

                let mut fuse_field_splitter = l.split(|c| c == ' ' || c == '\r' || c == '\n');
                let fuse_idx_str = fuse_field_splitter.next();
                let (_, fuse_idx_str) = fuse_idx_str.unwrap().split_at(1);
                let fuse_idx_maybe = u32::from_str_radix(fuse_idx_str, 10);
                if fuse_idx_maybe.is_err() {
                    return Err("invalid character encountered - fuse number");
                }
                let mut fuse_idx = fuse_idx_maybe.unwrap();

                let fuse_bits_part = fuse_field_splitter.next();
                if fuse_bits_part.is_none() {
                    return Err("malformed L field");
                }
                let fuse_bits_part = fuse_bits_part.unwrap();
                for fuse in fuse_bits_part.chars() {
                    match fuse {
                        '0' => {
                            if fuse_idx >= num_fuses {
                                return Err("invalid fuse index out of range");
                            }
                            fuses_ternary[fuse_idx as usize] = Ternary::Zero;
                            fuse_idx += 1;
                        },
                        '1' => {
                            if fuse_idx >= num_fuses {
                                return Err("invalid fuse index out of range");
                            }
                            fuses_ternary[fuse_idx as usize] = Ternary::One;
                            fuse_idx += 1;
                        },
                        ' ' | '\r' | '\n' => {}, // Do nothing
                        _ => return Err("invalid character encountered - fuse value"),
                    }
                }
            },
            'C' => {
                // Checksum
                let (_, csum_str) = l.split_at(1);
                if csum_str.len() != 4 {
                    return Err("malformed fuse checksum");
                }
                let csum_maybe = u16::from_str_radix(csum_str, 16);
                if csum_maybe.is_err() {
                    return Err("invalid character encountered - C");
                }
                fuse_expected_csum = Some(csum_maybe.unwrap());
            }
            _ => return Err("unrecognized field"),
        }
    }

    // Fill in the default values
    for x in &mut fuses_ternary {
        if *x == Ternary::Undef {
            // There cannot be undefined fuses if there isn't an F field
            if default_fuse == Ternary::Undef {
                return Err("missing F field")
            }

            *x = default_fuse;
        }
    }

    // Un-ternary it
    let fuses = fuses_ternary.iter().map(|&x| match x {
        Ternary::Zero => false,
        Ternary::One => true,
        _ => unreachable!(),
    }).collect::<Vec<_>>();

    // Fuse checksum
    if let Some(fuse_expected_csum) = fuse_expected_csum {
        for i in 0..num_fuses {
            if fuses[i as usize] {
                // Fuse is a 1 and contributes to the sum
                fuse_csum += Wrapping(1u16 << (i % 8));
            }
        }

        if fuse_expected_csum != fuse_csum.0 {
            return Err("invalid fuse checksum");
        }
    }

    Ok((fuses, device))
}

// Processes a fuse array into a bitstream object
pub fn process_jed(fuses: &[bool], device: &str) -> Result<XC2Bitstream, &'static str> {
    let device_split = device.split('-').collect::<Vec<_>>();

    if device_split.len() != 3 {
        return Err("malformed device name");
    }

    // TODO: Validate these
    let device_speed = device_split[1];
    let device_package = device_split[2];

    // Part name
    match device_split[0] {
        "XC2C32A" => {
            if fuses.len() != 12278 {
                return Err("wrong number of fuses");
            }
            let bits = read_32_bitstream_logical(fuses);
            if let Err(err) = bits {
                return Err(err);
            }
            Ok(XC2Bitstream {
                speed_grade: device_speed.to_owned(),
                package: device_package.to_owned(),
                bits: bits.unwrap(),
            })
        },
        _ => Err("unsupported part"),
    }
}

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

    #[test]
    fn read_no_stx() {
        let ret = read_jed(b"asdf");

        assert_eq!(ret, Err("STX not found"));
    }

    #[test]
    fn read_no_etx() {
        let ret = read_jed(b"asdf\x02fdsa");

        assert_eq!(ret, Err("ETX not found"));
    }

    #[test]
    fn read_no_csum() {
        let ret = read_jed(b"asdf\x02fdsa\x03");
        assert_eq!(ret, Err("unexpected end of file - checksum"));

        let ret = read_jed(b"asdf\x02fdsa\x03AAA");
        assert_eq!(ret, Err("unexpected end of file - checksum"));
    }

    #[test]
    fn read_bad_csum() {
        let ret = read_jed(b"asdf\x02fdsa\x03AAAA");

        assert_eq!(ret, Err("invalid file checksum"));
    }

    #[test]
    fn read_malformed_csum() {
        let ret = read_jed(b"asdf\x02fdsa\x03AAAZ");

        assert_eq!(ret, Err("invalid character encountered - file checksum"));
    }

    #[test]
    fn read_no_f() {
        let ret = read_jed(b"\x02QF1*\x030000");

        assert_eq!(ret, Err("missing F field"));
    }

    #[test]
    fn read_empty_no_fuses() {
        let ret = read_jed(b"\x02F0*\x030000");

        assert_eq!(ret, Ok((vec![], None)));
    }

    #[test]
    fn read_bogus_f_command() {
        let ret = read_jed(b"\x02F2*\x030000");

        assert_eq!(ret, Err("invalid character encountered - F field"));
    }

    #[test]
    fn read_empty_with_device() {
        let ret = read_jed(b"\x02F0*N DEVICE asdf*\x030000");

        assert_eq!(ret, Ok((vec![], Some(String::from("asdf")))));
    }

    #[test]
    fn read_l_without_qf() {
        let ret = read_jed(b"\x02F0*L0 0*\x030000");

        assert_eq!(ret, Err("missing QF field"));
    }

    #[test]
    fn read_one_fuse() {
        let ret = read_jed(b"\x02F0*QF1*L0 1*\x030000");

        assert_eq!(ret, Ok((vec![true], None)));
    }

    #[test]
    fn read_one_fuse_csum_good() {
        let ret = read_jed(b"\x02F0*QF1*L0 1*C0001*\x030000");

        assert_eq!(ret, Ok((vec![true], None)));
    }

    #[test]
    fn read_one_fuse_csum_bad() {
        let ret = read_jed(b"\x02F0*QF1*L0 1*C0002*\x030000");

        assert_eq!(ret, Err("invalid fuse checksum"));
    }
}
<|endoftext|>


<commit_before>use super::*;

#[test]
fn test_v2_addition_simple() {
    let v1 = Vector2 { x: 1, y: 1 };
    let v2 = Vector2 { x: 2, y: 2};
    assert_eq!(v1 + v2, Vector2 { x: 3, y: 3})
}

#[test]
fn test_v2_subtraction_simple() {
    let v1 = Vector2 { x: 2, y: 2 };
    let v2 = Vector2 { x: 1, y: 1};
    assert_eq!(v1 - v2, Vector2 { x: 1, y: 1})
}

#[test]
fn test_v2_dot_simple() {
    let v1 = Vector2 { x: 2, y: 2 };
    let v2 = Vector2 { x: 2, y: 3 };
    assert_eq!(v1 * v2, 10)
}
<commit_msg>Start adding QuickCheck tests<commit_after>use super::*;
use quickcheck::quickcheck;

#[test]
fn test_v2_addition_simple() {
    let v1 = Vector2 { x: 1, y: 1 };
    let v2 = Vector2 { x: 2, y: 2 };
    assert_eq!(v1 + v2, Vector2 { x: 3, y: 3})
}

#[test]
fn test_v2_subtraction_simple() {
    let v1 = Vector2 { x: 2, y: 2 };
    let v2 = Vector2 { x: 1, y: 1 };
    assert_eq!(v1 - v2, Vector2 { x: 1, y: 1})
}

#[test]
fn test_v2_dot_simple() {
    let v1 = Vector2 { x: 2, y: 2 };
    let v2 = Vector2 { x: 2, y: 3 };
    assert_eq!(v1 * v2, 10)
}

macro_rules! e { ($e:expr) => { $e } }
macro_rules! v2_arith_props {
    ($($fun:ident, $op:tt)*) => {$(
        #[quickcheck]
        fn $fun( x1: isize, x2: isize
                           , y1: isize, y2: isize) -> bool {
            let v1 = Vector2 { x: x1, y: y1 };
            let v2 = Vector2 { x: x2, y: y2 };
            Vector2 { x: e!(x1 $op x2), y: e!(y1 $op y2)} == e!(v1 $op v2)
        }
    )*}
}

v2_arith_props!( prop_v2_addition, +
                 prop_v2_subtraction, -
                //  prop_v2_division, /
                //  prop_v2_mod, %
                );
<|endoftext|>


<commit_before>use std::io::{Error, LineWriter, Write};

type Offset = usize;
type Value = f64;
type Line = usize;

pub enum OpCode {
    Constant(Offset),
    Return,
}

pub struct Chunk {
    instructions: Vec<OpCode>,
    values: Vec<Value>,
    lines: Vec<Line>,
}

impl Chunk {
    pub fn new() -> Chunk {
        Chunk {
            instructions: Vec::new(),
            values: Vec::new(),
            lines: Vec::new(),
        }
    }

    pub fn add_instruction(&mut self, instruction: OpCode, line: Line) -> () {
        self.instructions.push(instruction);
        self.lines.push(line);
    }

    pub fn add_constant(&mut self, constant: Value) -> Offset {
        self.values.push(constant);
        self.values.len() - 1
    }

    pub fn disassemble<T>(&self, name: &str, out: &mut LineWriter<T>) -> Result<(), Error>
    where
        T: Write,
    {
        try!(writeln!(out, "== {} ==", name));
        let mut i = 0;
        let mut line = 0;
        for instruction in self.instructions.iter() {
            // Note that this is not printing offsets as the book does.
            // Using the OpCode enum all the opcodes have the same size.
            // It is not space-efficient, but for now it's fine
            try!(write!(out, "{:04}", i));
            if line == self.lines[i] {
                try!(write!(out, "   |"));
            } else {
                line = self.lines[i];
                try!(write!(out, "{:4}", line));
            }
            try!(write!(out, " "));
            i = i + 1;
            try!(match instruction {
                &OpCode::Return => writeln!(out, "OP_RETURN"),
                &OpCode::Constant(offset) => {
                    writeln!(out, "OP_CONSTANT {:4} '{:}'", offset, self.values[offset])
                }
            });
        }
        Ok(())
    }
}
<commit_msg>Added a doctest<commit_after>use std::io::{Error, LineWriter, Write};

type Offset = usize;
type Value = f64;
type Line = usize;

pub enum OpCode {
    Constant(Offset),
    Return,
}

pub struct Chunk {
    instructions: Vec<OpCode>,
    values: Vec<Value>,
    lines: Vec<Line>,
}

impl Chunk {
    pub fn new() -> Chunk {
        Chunk {
            instructions: Vec::new(),
            values: Vec::new(),
            lines: Vec::new(),
        }
    }

    /// Adds a new instruction to the chunk
    /// # Example
    /// ```
    /// use rulox::vm::bytecode::*;
    /// let mut chunk = Chunk::new();
    /// chunk.add_instruction(OpCode::Return, 1);
    /// ```
    pub fn add_instruction(&mut self, instruction: OpCode, line: Line) -> () {
        self.instructions.push(instruction);
        self.lines.push(line);
    }

    pub fn add_constant(&mut self, constant: Value) -> Offset {
        self.values.push(constant);
        self.values.len() - 1
    }

    pub fn disassemble<T>(&self, name: &str, out: &mut LineWriter<T>) -> Result<(), Error>
    where
        T: Write,
    {
        try!(writeln!(out, "== {} ==", name));
        let mut i = 0;
        let mut line = 0;
        for instruction in self.instructions.iter() {
            // Note that this is not printing offsets as the book does.
            // Using the OpCode enum all the opcodes have the same size.
            // It is not space-efficient, but for now it's fine
            try!(write!(out, "{:04}", i));
            if line == self.lines[i] {
                try!(write!(out, "   |"));
            } else {
                line = self.lines[i];
                try!(write!(out, "{:4}", line));
            }
            try!(write!(out, " "));
            i = i + 1;
            try!(match instruction {
                &OpCode::Return => writeln!(out, "OP_RETURN"),
                &OpCode::Constant(offset) => {
                    writeln!(out, "OP_CONSTANT {:4} '{:}'", offset, self.values[offset])
                }
            });
        }
        Ok(())
    }
}
<|endoftext|>


<commit_before>use frontend::scanner::{scan_into_iterator, ScannerError, Token, TokenWithContext};
use std::iter::Peekable;
use vm::bytecode::{BinaryOp, Chunk, OpCode};

enum CompilationError {
    ScannerError(ScannerError),
}

#[derive(PartialEq, PartialOrd)]
enum Precedence {
    None,
    Assignment,
    Or,
    And,
    Equality,
    Comparison,
    Term,
    Factor,
    Unary,
    Call,
    Primary,
}

impl Precedence {
    fn next(&self) -> Precedence {
        match *self {
            Precedence::None => Precedence::Assignment,
            Precedence::Assignment => Precedence::Or,
            Precedence::Or => Precedence::And,
            Precedence::And => Precedence::Equality,
            Precedence::Equality => Precedence::Comparison,
            Precedence::Comparison => Precedence::Term,
            Precedence::Term => Precedence::Factor,
            Precedence::Factor => Precedence::Unary,
            Precedence::Unary => Precedence::Call,
            Precedence::Call => Precedence::Primary,
            Precedence::Primary => panic!("What is it supposed to happen"),
        }
    }
}

//TODO: replace with "function pointers", if possible
enum RuleFunction {
    Grouping,
    Unary,
    Binary,
    Number,
}

struct Rule {
    precedence: Precedence,
    prefix: Option<RuleFunction>,
    infix: Option<RuleFunction>,
}

impl Rule {
    /// Finds the rule associated with a Token type.
    /// This is derived straight from the Pratt Parser table.
    fn find(token: &Token) -> Rule {
        match token {
            Token::LeftParen => Rule {
                precedence: Precedence::Call,
                prefix: Some(RuleFunction::Grouping),
                infix: None,
            },
            Token::RightParen => Rule {
                precedence: Precedence::None,
                prefix: None,
                infix: None,
            },
            Token::LeftBrace => Rule {
                precedence: Precedence::None,
                prefix: Some(RuleFunction::Grouping),
                infix: None,
            },
            Token::RightBrace => Rule {
                precedence: Precedence::None,
                prefix: None,
                infix: None,
            },
            Token::Comma => Rule {
                precedence: Precedence::None,
                prefix: None,
                infix: None,
            },
            Token::Dot => Rule {
                precedence: Precedence::Call,
                prefix: None,
                infix: None,
            },
            Token::Minus => Rule {
                precedence: Precedence::Term,
                prefix: Some(RuleFunction::Unary),
                infix: Some(RuleFunction::Binary),
            },
            Token::Plus => Rule {
                precedence: Precedence::Term,
                prefix: None,
                infix: Some(RuleFunction::Binary),
            },
            Token::Slash => Rule {
                precedence: Precedence::Factor,
                prefix: None,
                infix: Some(RuleFunction::Binary),
            },
            Token::Star => Rule {
                precedence: Precedence::Factor,
                prefix: None,
                infix: Some(RuleFunction::Binary),
            },
            Token::Semicolon => Rule {
                precedence: Precedence::None,
                prefix: None,
                infix: None,
            },
            Token::NumberLiteral(_) => Rule {
                precedence: Precedence::None,
                prefix: Some(RuleFunction::Number),
                infix: None,
            },
            _ => unimplemented!(),
            /*
  { NULL,     NULL,    PREC_NONE },       // TOKEN_BANG
  { NULL,     NULL,    PREC_EQUALITY },   // TOKEN_BANG_EQUAL
  { NULL,     NULL,    PREC_NONE },       // TOKEN_EQUAL
  { NULL,     NULL,    PREC_EQUALITY },   // TOKEN_EQUAL_EQUAL
  { NULL,     NULL,    PREC_COMPARISON }, // TOKEN_GREATER
  { NULL,     NULL,    PREC_COMPARISON }, // TOKEN_GREATER_EQUAL
  { NULL,     NULL,    PREC_COMPARISON }, // TOKEN_LESS
  { NULL,     NULL,    PREC_COMPARISON }, // TOKEN_LESS_EQUAL
  { NULL,     NULL,    PREC_NONE },       // TOKEN_IDENTIFIER
  { NULL,     NULL,    PREC_NONE },       // TOKEN_STRING
  { NULL,     NULL,    PREC_AND },        // TOKEN_AND
  { NULL,     NULL,    PREC_NONE },       // TOKEN_CLASS
  { NULL,     NULL,    PREC_NONE },       // TOKEN_ELSE
  { NULL,     NULL,    PREC_NONE },       // TOKEN_FALSE
  { NULL,     NULL,    PREC_NONE },       // TOKEN_FUN
  { NULL,     NULL,    PREC_NONE },       // TOKEN_FOR
  { NULL,     NULL,    PREC_NONE },       // TOKEN_IF
  { NULL,     NULL,    PREC_NONE },       // TOKEN_NIL
  { NULL,     NULL,    PREC_OR },         // TOKEN_OR
  { NULL,     NULL,    PREC_NONE },       // TOKEN_PRINT
  { NULL,     NULL,    PREC_NONE },       // TOKEN_RETURN
  { NULL,     NULL,    PREC_NONE },       // TOKEN_SUPER
  { NULL,     NULL,    PREC_NONE },       // TOKEN_THIS
  { NULL,     NULL,    PREC_NONE },       // TOKEN_TRUE
  { NULL,     NULL,    PREC_NONE },       // TOKEN_VAR
  { NULL,     NULL,    PREC_NONE },       // TOKEN_WHILE
  { NULL,     NULL,    PREC_NONE },       // TOKEN_ERROR
  { NULL,     NULL,    PREC_NONE },       // TOKEN_EOF
*/
        }
    }
}

struct Parser<'a, I>
where
    I: Iterator<Item = Result<TokenWithContext, ScannerError>>,
{
    chunk: &'a mut Chunk,
    tokens: Peekable<I>,
    errors: Vec<CompilationError>,
}

impl<'a, I> Parser<'a, I>
where
    I: Iterator<Item = Result<TokenWithContext, ScannerError>>,
{
    fn new(chunk: &'a mut Chunk, tokens: I) -> Parser<I> {
        Parser {
            chunk: chunk,
            tokens: tokens.peekable(),
            errors: vec![],
        }
    }

    fn skip_to_valid(&mut self) -> () {
        loop {
            match self.tokens.peek() {
                Some(Ok(TokenWithContext {
                    token: Token::Whitespace,
                    lexeme: _,
                    position: _,
                })) => {
                    // No op, just skip it
                }
                Some(Ok(TokenWithContext {
                    token: Token::Comment,
                    lexeme: _,
                    position: _,
                })) => {
                    // No op, just skip it
                }
                Some(Err(ref e)) => {
                    self.errors.push(CompilationError::ScannerError(e.clone()));
                }
                _ => return (),
            }
            let _ = self.tokens.next();
        }
    }

    /// Advances to the next valid token (skipping and keeping track of errors).
    /// This consumes an item from the token stream and returns it.
    fn advance(&mut self) -> Option<TokenWithContext> {
        // Peek to skip errors and keep track of them.
        // This makes unwrapping safe.
        self.skip_to_valid();
        self.tokens.next().map(|r| r.unwrap())
    }

    /// Adds an opcode to the current chunk
    fn emit(&mut self, opcode: OpCode, line: usize) -> () {
        self.chunk.add_instruction(opcode, line)
    }

    fn peek(&mut self) -> Option<&TokenWithContext> {
        self.skip_to_valid();
        self.tokens.peek().map(|result| match result {
            Ok(ref token_with_context) => token_with_context,
            Err(_) => unreachable!("We already skipped errors"),
        })
    }

    fn dispatch(&mut self, rule_function: RuleFunction) -> Result<(), ()> {
        match rule_function {
            RuleFunction::Grouping => self.grouping(),
            RuleFunction::Unary => self.unary(),
            RuleFunction::Binary => self.binary(),
            RuleFunction::Number => self.number(),
        }
    }

    fn parse_precedence(&mut self, precedence: Precedence) -> Result<(), ()> {
        let prefix_function = {
            let peeked = self.peek().ok_or_else(|| ())?; //TODO: fill with details. Is this even okay?
            Rule::find(&peeked.token).prefix.ok_or_else(|| ())? //TODO: fill with details
        };
        self.dispatch(prefix_function)?;
        loop {
            let infix_function = {
                match self.peek() {
                    Some(peeked) => {
                        let infix_rule = Rule::find(&peeked.token);
                        if precedence <= infix_rule.precedence {
                            infix_rule.infix.ok_or_else(|| ())? // TODO: make it meaningful. Was: error("Expect expression.");
                        } else {
                            return Ok(());
                        }
                    }
                    None => return Ok(()),
                }
            };
            self.dispatch(infix_function)?;
        }
    }

    fn expression(&mut self) -> Result<(), ()> {
        self.parse_precedence(Precedence::Assignment)
    }

    fn consume(&mut self, token: Token) -> Result<(), ()> {
        let current = self.advance();
        if let Some(ref token_with_context) = current {
            if token_with_context.token == token {
                Ok(())
            } else {
                Err(())
            }
        } else {
            Err(()) //TODO: make it meaningful. Was "Expect ')' after expression.");
        }
    }

    fn number(&mut self) -> Result<(), ()> {
        let current = self.advance();
        let (value, line) = if let Some(ref t) = current {
            if let Token::NumberLiteral(ref n) = t.token {
                (*n, t.position.line)
            } else {
                // TODO: better message. To get here we had to peek a NumberLiteral
                panic!()
            }
        } else {
            panic!()
        };
        let constant = self.chunk.add_constant(value);
        self.chunk.add_instruction(OpCode::Constant(constant), line);
        Ok(())
    }

    fn grouping(&mut self) -> Result<(), ()> {
        let _ = self.consume(Token::LeftParen)?;
        self.expression()?;
        self.consume(Token::RightParen)
    }

    fn unary(&mut self) -> Result<(), ()> {
        let (opcode, line) = match self.advance() {
            Some(TokenWithContext {
                token: Token::Minus,
                position,
                lexeme: _,
            }) => (OpCode::Negate, position.line),
            _ => unreachable!("This code is executed only when we know we have a unary expression"),
        };
        let _ = self.parse_precedence(Precedence::Unary)?;
        self.emit(opcode, line);
        Ok(())
    }

    fn binary(&mut self) -> Result<(), ()> {
        let (opcode, line) = match self.advance() {
            Some(TokenWithContext {
                token: Token::Plus,
                position,
                lexeme: _,
            }) => (OpCode::Binary(BinaryOp::Add), position.line),
            Some(TokenWithContext {
                token: Token::Minus,
                position,
                lexeme: _,
            }) => (OpCode::Binary(BinaryOp::Subtract), position.line),
            Some(TokenWithContext {
                token: Token::Star,
                position,
                lexeme: _,
            }) => (OpCode::Binary(BinaryOp::Multiply), position.line),
            Some(TokenWithContext {
                token: Token::Slash,
                position,
                lexeme: _,
            }) => (OpCode::Binary(BinaryOp::Divide), position.line),
            _ => {
                unreachable!("This code is executed only when we know we have a binary expression")
            }
        };
        let precedence = {
            let peeked = self.peek().ok_or_else(|| ())?; //TODO: fill in details
            Rule::find(&peeked.token).precedence.next()
        };
        self.parse_precedence(precedence)?;
        self.emit(opcode, line);
        Ok(())
    }
}

pub fn compile(text: &str) -> Result<Chunk, ()> {
    let mut chunk = Chunk::new();
    let tokens = scan_into_iterator(text);
    {
        let mut parser = Parser::new(&mut chunk, tokens);
        parser.expression()?;
        // TODO: assert that we consumed everything
        parser.emit(OpCode::Return, 0); // Line is meaningless
    }
    Ok(chunk)
}
<commit_msg>Slightly improved error reporting<commit_after>use frontend::scanner::{scan_into_iterator, ScannerError, Token, TokenWithContext};
use std::iter::Peekable;
use vm::bytecode::{BinaryOp, Chunk, OpCode};

enum ParsingError {
    UnexpectedEndOfFile,
    ExpectedAtLine(usize),
}

enum CompilationError {
    ScannerError(ScannerError),
    ParsingError(ParsingError),
}

#[derive(PartialEq, PartialOrd)]
enum Precedence {
    None,
    Assignment,
    Or,
    And,
    Equality,
    Comparison,
    Term,
    Factor,
    Unary,
    Call,
    Primary,
}

impl Precedence {
    fn next(&self) -> Precedence {
        match *self {
            Precedence::None => Precedence::Assignment,
            Precedence::Assignment => Precedence::Or,
            Precedence::Or => Precedence::And,
            Precedence::And => Precedence::Equality,
            Precedence::Equality => Precedence::Comparison,
            Precedence::Comparison => Precedence::Term,
            Precedence::Term => Precedence::Factor,
            Precedence::Factor => Precedence::Unary,
            Precedence::Unary => Precedence::Call,
            Precedence::Call => Precedence::Primary,
            Precedence::Primary => panic!("What is it supposed to happen"),
        }
    }
}

//TODO: replace with "function pointers", if possible
enum RuleFunction {
    Grouping,
    Unary,
    Binary,
    Number,
}

struct Rule {
    precedence: Precedence,
    prefix: Option<RuleFunction>,
    infix: Option<RuleFunction>,
}

impl Rule {
    /// Finds the rule associated with a Token type.
    /// This is derived straight from the Pratt Parser table.
    fn find(token: &Token) -> Rule {
        match token {
            Token::LeftParen => Rule {
                precedence: Precedence::Call,
                prefix: Some(RuleFunction::Grouping),
                infix: None,
            },
            Token::RightParen => Rule {
                precedence: Precedence::None,
                prefix: None,
                infix: None,
            },
            Token::LeftBrace => Rule {
                precedence: Precedence::None,
                prefix: Some(RuleFunction::Grouping),
                infix: None,
            },
            Token::RightBrace => Rule {
                precedence: Precedence::None,
                prefix: None,
                infix: None,
            },
            Token::Comma => Rule {
                precedence: Precedence::None,
                prefix: None,
                infix: None,
            },
            Token::Dot => Rule {
                precedence: Precedence::Call,
                prefix: None,
                infix: None,
            },
            Token::Minus => Rule {
                precedence: Precedence::Term,
                prefix: Some(RuleFunction::Unary),
                infix: Some(RuleFunction::Binary),
            },
            Token::Plus => Rule {
                precedence: Precedence::Term,
                prefix: None,
                infix: Some(RuleFunction::Binary),
            },
            Token::Slash => Rule {
                precedence: Precedence::Factor,
                prefix: None,
                infix: Some(RuleFunction::Binary),
            },
            Token::Star => Rule {
                precedence: Precedence::Factor,
                prefix: None,
                infix: Some(RuleFunction::Binary),
            },
            Token::Semicolon => Rule {
                precedence: Precedence::None,
                prefix: None,
                infix: None,
            },
            Token::NumberLiteral(_) => Rule {
                precedence: Precedence::None,
                prefix: Some(RuleFunction::Number),
                infix: None,
            },
            _ => unimplemented!(),
            /*
  { NULL,     NULL,    PREC_NONE },       // TOKEN_BANG
  { NULL,     NULL,    PREC_EQUALITY },   // TOKEN_BANG_EQUAL
  { NULL,     NULL,    PREC_NONE },       // TOKEN_EQUAL
  { NULL,     NULL,    PREC_EQUALITY },   // TOKEN_EQUAL_EQUAL
  { NULL,     NULL,    PREC_COMPARISON }, // TOKEN_GREATER
  { NULL,     NULL,    PREC_COMPARISON }, // TOKEN_GREATER_EQUAL
  { NULL,     NULL,    PREC_COMPARISON }, // TOKEN_LESS
  { NULL,     NULL,    PREC_COMPARISON }, // TOKEN_LESS_EQUAL
  { NULL,     NULL,    PREC_NONE },       // TOKEN_IDENTIFIER
  { NULL,     NULL,    PREC_NONE },       // TOKEN_STRING
  { NULL,     NULL,    PREC_AND },        // TOKEN_AND
  { NULL,     NULL,    PREC_NONE },       // TOKEN_CLASS
  { NULL,     NULL,    PREC_NONE },       // TOKEN_ELSE
  { NULL,     NULL,    PREC_NONE },       // TOKEN_FALSE
  { NULL,     NULL,    PREC_NONE },       // TOKEN_FUN
  { NULL,     NULL,    PREC_NONE },       // TOKEN_FOR
  { NULL,     NULL,    PREC_NONE },       // TOKEN_IF
  { NULL,     NULL,    PREC_NONE },       // TOKEN_NIL
  { NULL,     NULL,    PREC_OR },         // TOKEN_OR
  { NULL,     NULL,    PREC_NONE },       // TOKEN_PRINT
  { NULL,     NULL,    PREC_NONE },       // TOKEN_RETURN
  { NULL,     NULL,    PREC_NONE },       // TOKEN_SUPER
  { NULL,     NULL,    PREC_NONE },       // TOKEN_THIS
  { NULL,     NULL,    PREC_NONE },       // TOKEN_TRUE
  { NULL,     NULL,    PREC_NONE },       // TOKEN_VAR
  { NULL,     NULL,    PREC_NONE },       // TOKEN_WHILE
  { NULL,     NULL,    PREC_NONE },       // TOKEN_ERROR
  { NULL,     NULL,    PREC_NONE },       // TOKEN_EOF
*/
        }
    }
}

struct Parser<'a, I>
where
    I: Iterator<Item = Result<TokenWithContext, ScannerError>>,
{
    chunk: &'a mut Chunk,
    tokens: Peekable<I>,
    errors: Vec<CompilationError>,
}

impl<'a, I> Parser<'a, I>
where
    I: Iterator<Item = Result<TokenWithContext, ScannerError>>,
{
    fn new(chunk: &'a mut Chunk, tokens: I) -> Parser<I> {
        Parser {
            chunk: chunk,
            tokens: tokens.peekable(),
            errors: vec![],
        }
    }

    fn skip_to_valid(&mut self) -> () {
        loop {
            match self.tokens.peek() {
                Some(Ok(TokenWithContext {
                    token: Token::Whitespace,
                    lexeme: _,
                    position: _,
                })) => {
                    // No op, just skip it
                }
                Some(Ok(TokenWithContext {
                    token: Token::Comment,
                    lexeme: _,
                    position: _,
                })) => {
                    // No op, just skip it
                }
                Some(Err(ref e)) => {
                    self.errors.push(CompilationError::ScannerError(e.clone()));
                }
                _ => return (),
            }
            let _ = self.tokens.next();
        }
    }

    /// Advances to the next valid token (skipping and keeping track of errors).
    /// This consumes an item from the token stream and returns it.
    fn advance(&mut self) -> Option<TokenWithContext> {
        // Peek to skip errors and keep track of them.
        // This makes unwrapping safe.
        self.skip_to_valid();
        self.tokens.next().map(|r| r.unwrap())
    }

    /// Adds an opcode to the current chunk
    fn emit(&mut self, opcode: OpCode, line: usize) -> () {
        self.chunk.add_instruction(opcode, line)
    }

    fn peek(&mut self) -> Option<&TokenWithContext> {
        self.skip_to_valid();
        self.tokens.peek().map(|result| match result {
            Ok(ref token_with_context) => token_with_context,
            Err(_) => unreachable!("We already skipped errors"),
        })
    }

    fn dispatch(&mut self, rule_function: RuleFunction) -> Result<(), ParsingError> {
        match rule_function {
            RuleFunction::Grouping => self.grouping(),
            RuleFunction::Unary => self.unary(),
            RuleFunction::Binary => self.binary(),
            RuleFunction::Number => self.number(),
        }
    }

    fn parse_precedence(&mut self, precedence: Precedence) -> Result<(), ParsingError> {
        let prefix_function = {
            let peeked = self.peek().ok_or(ParsingError::UnexpectedEndOfFile)?;
            Rule::find(&peeked.token)
                .prefix
                .ok_or_else(|| ParsingError::ExpectedAtLine(peeked.position.line))?
        };
        self.dispatch(prefix_function)?;
        loop {
            let infix_function = {
                match self.peek() {
                    Some(peeked) => {
                        let infix_rule = Rule::find(&peeked.token);
                        if precedence <= infix_rule.precedence {
                            infix_rule
                                .infix
                                .ok_or_else(|| ParsingError::ExpectedAtLine(peeked.position.line))?
                        } else {
                            return Ok(());
                        }
                    }
                    None => return Ok(()),
                }
            };
            self.dispatch(infix_function)?;
        }
    }

    fn expression(&mut self) -> Result<(), ParsingError> {
        self.parse_precedence(Precedence::Assignment)
    }

    fn consume(&mut self, token: Token) -> Result<(), ParsingError> {
        let current = self.advance().ok_or(ParsingError::UnexpectedEndOfFile)?;
        if current.token == token {
            Ok(())
        } else {
            Err(ParsingError::ExpectedAtLine(current.position.line))
        }
    }

    fn number(&mut self) -> Result<(), ParsingError> {
        let current = self.advance();
        let (value, line) = if let Some(ref t) = current {
            if let Token::NumberLiteral(ref n) = t.token {
                (*n, t.position.line)
            } else {
                unreachable!()
            }
        } else {
            panic!()
        };
        let constant = self.chunk.add_constant(value);
        self.chunk.add_instruction(OpCode::Constant(constant), line);
        Ok(())
    }

    fn grouping(&mut self) -> Result<(), ParsingError> {
        let _ = self.consume(Token::LeftParen)?;
        self.expression()?;
        self.consume(Token::RightParen)
    }

    fn unary(&mut self) -> Result<(), ParsingError> {
        let (opcode, line) = match self.advance() {
            Some(TokenWithContext {
                token: Token::Minus,
                position,
                lexeme: _,
            }) => (OpCode::Negate, position.line),
            _ => unreachable!("This code is executed only when we know we have a unary expression"),
        };
        let _ = self.parse_precedence(Precedence::Unary)?;
        self.emit(opcode, line);
        Ok(())
    }

    fn binary(&mut self) -> Result<(), ParsingError> {
        let (opcode, line) = match self.advance() {
            Some(TokenWithContext {
                token: Token::Plus,
                position,
                lexeme: _,
            }) => (OpCode::Binary(BinaryOp::Add), position.line),
            Some(TokenWithContext {
                token: Token::Minus,
                position,
                lexeme: _,
            }) => (OpCode::Binary(BinaryOp::Subtract), position.line),
            Some(TokenWithContext {
                token: Token::Star,
                position,
                lexeme: _,
            }) => (OpCode::Binary(BinaryOp::Multiply), position.line),
            Some(TokenWithContext {
                token: Token::Slash,
                position,
                lexeme: _,
            }) => (OpCode::Binary(BinaryOp::Divide), position.line),
            _ => {
                unreachable!("This code is executed only when we know we have a binary expression")
            }
        };
        let precedence = {
            let peeked = self
                .peek()
                .ok_or_else(|| ParsingError::UnexpectedEndOfFile)?;
            Rule::find(&peeked.token).precedence.next()
        };
        self.parse_precedence(precedence)?;
        self.emit(opcode, line);
        Ok(())
    }

    fn parse(&mut self) -> Result<(), ()> {
        loop {
            match self.expression() {
                Ok(()) => return Ok(()),
                Err(error) => {
                    self.errors.push(CompilationError::ParsingError(error))
                    //TODO: add recovery mode
                }
            }
        }
    }
}

pub fn compile(text: &str) -> Result<Chunk, ()> {
    let mut chunk = Chunk::new();
    let tokens = scan_into_iterator(text);
    {
        let mut parser = Parser::new(&mut chunk, tokens);
        parser.parse()?;
        // TODO: assert that we consumed everything
        parser.emit(OpCode::Return, 0); // Line is meaningless
    }
    Ok(chunk)
}
<|endoftext|>


<commit_before>//! Protocol-related utilities
//!
//! This module contains several handlers to manage the Wayland protocol
//! and the clients.
//!
//! Most utilities provided in this module work in the same way:
//!
//! - An `init` function or method will take the event loop as argument and
//!   insert one or more globals into it.
//! - If you want to remove a previously inserted global, just call the
//!   `destroy()` method on the associated `Global`. If you don't plan to
//!   destroy the global at all, you don't need to bother keeping the
//!   `Global` around.

use std::sync::atomic::{AtomicUsize, Ordering};

pub mod compositor;
pub mod data_device;
#[cfg(feature = "backend_drm")]
pub mod dmabuf;
pub mod explicit_synchronization;
pub mod output;
pub mod seat;
pub mod shell;
pub mod shm;

/// A global [`SerialCounter`] for use in your compositor.
///
/// Is is also used internally by some parts of Smithay.
pub static SERIAL_COUNTER: SerialCounter = SerialCounter {
    serial: AtomicUsize::new(0),
};

/// A serial type, whose comparison takes into account the wrapping-around behavior of the
/// underlying counter.
#[derive(Debug, Copy, Clone)]
pub struct Serial(u32);

impl PartialEq for Serial {
    fn eq(&self, other: &Self) -> bool {
        self.0 == other.0
    }
}

impl Eq for Serial {}

impl PartialOrd for Serial {
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        let distance = if self.0 > other.0 {
            self.0 - other.0
        } else {
            other.0 - self.0
        };
        if distance < u32::MAX / 2 {
            self.0.partial_cmp(&other.0)
        } else {
            // wrap-around occured, invert comparison
            other.0.partial_cmp(&self.0)
        }
    }
}

impl From<u32> for Serial {
    fn from(n: u32) -> Self {
        Serial(n)
    }
}

impl From<Serial> for u32 {
    fn from(serial: Serial) -> u32 {
        serial.0
    }
}

/// A counter for generating serials, for use in the client protocol
///
/// A global instance of this counter is available as the `SERIAL_COUNTER`
/// static. It is recommended to only use this global counter to ensure the
/// uniqueness of serials.
///
/// The counter will wrap around on overflow, ensuring it can run for as long
/// as needed.
pub struct SerialCounter {
    // TODO: replace with an AtomicU32 when stabilized
    serial: AtomicUsize,
}

impl SerialCounter {
    /// Retrieve the next serial from the counter
    pub fn next_serial(&self) -> Serial {
        Serial(self.serial.fetch_add(1, Ordering::AcqRel) as u32)
    }
}
<commit_msg>Add unit tests for SerialCounter<commit_after>//! Protocol-related utilities
//!
//! This module contains several handlers to manage the Wayland protocol
//! and the clients.
//!
//! Most utilities provided in this module work in the same way:
//!
//! - An `init` function or method will take the event loop as argument and
//!   insert one or more globals into it.
//! - If you want to remove a previously inserted global, just call the
//!   `destroy()` method on the associated `Global`. If you don't plan to
//!   destroy the global at all, you don't need to bother keeping the
//!   `Global` around.

use std::sync::atomic::{AtomicUsize, Ordering};

pub mod compositor;
pub mod data_device;
#[cfg(feature = "backend_drm")]
pub mod dmabuf;
pub mod explicit_synchronization;
pub mod output;
pub mod seat;
pub mod shell;
pub mod shm;

/// A global [`SerialCounter`] for use in your compositor.
///
/// Is is also used internally by some parts of Smithay.
pub static SERIAL_COUNTER: SerialCounter = SerialCounter {
    serial: AtomicUsize::new(0),
};

/// A serial type, whose comparison takes into account the wrapping-around behavior of the
/// underlying counter.
#[derive(Debug, Copy, Clone)]
pub struct Serial(u32);

impl PartialEq for Serial {
    fn eq(&self, other: &Self) -> bool {
        self.0 == other.0
    }
}

impl Eq for Serial {}

impl PartialOrd for Serial {
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        let distance = if self.0 > other.0 {
            self.0 - other.0
        } else {
            other.0 - self.0
        };
        if distance < u32::MAX / 2 {
            self.0.partial_cmp(&other.0)
        } else {
            // wrap-around occured, invert comparison
            other.0.partial_cmp(&self.0)
        }
    }
}

impl From<u32> for Serial {
    fn from(n: u32) -> Self {
        Serial(n)
    }
}

impl From<Serial> for u32 {
    fn from(serial: Serial) -> u32 {
        serial.0
    }
}

/// A counter for generating serials, for use in the client protocol
///
/// A global instance of this counter is available as the `SERIAL_COUNTER`
/// static. It is recommended to only use this global counter to ensure the
/// uniqueness of serials.
///
/// The counter will wrap around on overflow, ensuring it can run for as long
/// as needed.
pub struct SerialCounter {
    // TODO: replace with an AtomicU32 when stabilized
    serial: AtomicUsize,
}

impl SerialCounter {
    /// Retrieve the next serial from the counter
    pub fn next_serial(&self) -> Serial {
        Serial(self.serial.fetch_add(1, Ordering::AcqRel) as u32)
    }
}

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

    fn create_serial_counter(initial_value: u32) -> SerialCounter {
        SerialCounter {
            serial: AtomicUsize::new(initial_value as usize),
        }
    }

    #[test]
    fn serial_equals_self() {
        let counter = create_serial_counter(0);
        let serial = counter.next_serial();
        assert!(serial == serial);
    }

    #[test]
    fn consecutive_serials() {
        let counter = create_serial_counter(0);
        let serial1 = counter.next_serial();
        let serial2 = counter.next_serial();
        assert!(serial1 < serial2);
    }

    #[test]
    fn non_consecutive_serials() {
        let skip_serials = 147;

        let counter = create_serial_counter(0);
        let serial1 = counter.next_serial();
        for _ in 0..skip_serials {
            let _ = counter.next_serial();
        }
        let serial2 = counter.next_serial();
        assert!(serial1 < serial2);
    }

    #[test]
    fn serial_wrap_around() {
        let counter = create_serial_counter(u32::MAX);
        let serial1 = counter.next_serial();
        let serial2 = counter.next_serial();

        assert!(serial1 == u32::MAX.into());
        assert!(serial2 == 0.into());

        assert!(serial1 < serial2);
    }
}
<|endoftext|>


<commit_before>use color::Colorf32;
use fastup::{self, Node, parse_for_first_node, parse_color};
use stats_once::{CPU_USAGE_TOTAL, CPU_USAGE_PER_CORE};
use super::error_node;

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

impl super::Widget for Widget {
    fn expand(&self, args: Vec<String>) -> Node {
        match args.len() {
            2 => node_from_args(args),
            _ => error_node("(ccdd44: cpu) takes 2 arguments: color-cold\\|color-hot"),
        }
    }
}

fn node_from_args(args: Vec<String>) -> Node {
    try_node_from_args(args).unwrap_or_else(|e| error_node(&e))
}

fn try_node_from_args(args: Vec<String>) -> Result<Node, String> {
    assert_eq!(args.len(), 2);

    let cold = parse_color(&args[0]).map_err(escape_fastup)?;
    let hot = parse_color(&args[1]).map_err(escape_fastup)?;

    Ok(cpu_node(cold.into(), hot.into()))
}

fn cpu_node(cold: Colorf32, hot: Colorf32) -> Node {
    const BG_LIGHTNESS: f32 = 0.15;
    const FG_LIGHTNESS: f32 = 0.7;
    const IDLE_LIGHTNESS: f32 = 0.3;
    const IDLE_THRESHOLD: f32 = 0.05;
    const PROGRESS_INDICATORS: &'static str = "▁▂▃▄▅▆▇";

    let cpu_total = *CPU_USAGE_TOTAL;
    let cpu_usages = &CPU_USAGE_PER_CORE;

    let bg_cold = cold.set_lightness(BG_LIGHTNESS);
    let bg_hot = hot.set_lightness(BG_LIGHTNESS);
    let bg = bg_cold.mix(bg_hot, cpu_total).clamp_to_888();

    let idle_cold = cold.set_lightness(IDLE_LIGHTNESS);
    let idle_hot = hot.set_lightness(IDLE_LIGHTNESS);
    let idle = idle_cold.mix(idle_hot, cpu_total).clamp_to_888();

    let fg_cold = cold.set_lightness(FG_LIGHTNESS);
    let fg_hot = hot.set_lightness(FG_LIGHTNESS);
    let fg_for = |usage: f32| {
        if usage <= IDLE_THRESHOLD {
            idle
        } else {
            let usage = (usage - IDLE_THRESHOLD) / (1.0 - IDLE_THRESHOLD);
            fg_cold.mix(fg_hot, usage).clamp_to_888()
        }
    };

    let indicator_for = |usage: f32| {
        let len = PROGRESS_INDICATORS.chars().count() as f32;
        let i = (len * usage).min(len - 1.0).max(0.0) as usize;
        PROGRESS_INDICATORS.chars().nth(i).unwrap()
    };

    let cpu_usages = cpu_usages.iter()
        .map(|&usage| format!("({}:{})", fg_for(usage), indicator_for(usage)))
        .collect::<String>();
    let total_usage = {
        let color = fg_for(cpu_total);
        let percent = (100.0 * cpu_total) as i32;
        let percent = format!("{:>3}%", percent);
        let percent = escape_fastup(percent);
        format!("({}:{})", color, percent)
    };

    let node = format!("[{}: \\ {}{} ]", bg, cpu_usages, total_usage);
    parse_for_first_node(&node).unwrap()
}

fn escape_fastup<T>(input: T) -> String
    where T: ToString
{
    fastup::escape_for_text(&input.to_string())
}

<commit_msg>widget: + debug options for <cpu><commit_after>use color::Colorf32;
use fastup::{self, Node, parse_for_first_node, parse_color};
use stats_once::{CPU_USAGE_TOTAL, CPU_USAGE_PER_CORE};
use super::error_node;

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

impl super::Widget for Widget {
    fn expand(&self, args: Vec<String>) -> Node {
        match args.len() {
            2 => node_from_args(args),
            x if x > 2 => node_from_args(args),     // debug only
            _ => error_node("(ccdd44: cpu) takes 2 arguments: color-cold\\|color-hot"),
        }
    }
}

fn node_from_args(args: Vec<String>) -> Node {
    try_node_from_args(args).unwrap_or_else(|e| error_node(&e))
}

fn try_node_from_args(args: Vec<String>) -> Result<Node, String> {
    assert!(args.len() >= 2);

    let cold = parse_color(&args[0]).map_err(escape_fastup)?;
    let hot = parse_color(&args[1]).map_err(escape_fastup)?;
    if args.len() == 2 {
        Ok(cpu_node(cold.into(),
                    hot.into(),
                    *CPU_USAGE_TOTAL,
                    &CPU_USAGE_PER_CORE))
    } else {
        let usages = args.into_iter()
            .skip(2)
            .map(|arg| arg.parse::<f32>().map_err(escape_fastup))
            .fold(Ok::<_, String>(Vec::new()),
                  |usages, percent| match usages {
                      Ok(mut usages) => {
                          let percent = percent?;
                          usages.push(percent / 100.0);
                          Ok(usages)
                      }
                      Err(_) => usages,
                  })?;

        let total: f32 = usages.iter().sum();
        let len = usages.len() as f32;
        let total = total / len;

        Ok(cpu_node(cold.into(), hot.into(), total, &usages))
    }
}

fn cpu_node(cold: Colorf32, hot: Colorf32, cpu_total: f32, cpu_usages: &[f32]) -> Node {
    const BG_LIGHTNESS: f32 = 0.15;
    const FG_LIGHTNESS: f32 = 0.7;
    const IDLE_LIGHTNESS: f32 = 0.3;
    const IDLE_THRESHOLD: f32 = 0.05;
    const PROGRESS_INDICATORS: &'static str = "▁▂▃▄▅▆▇";

    let bg_cold = cold.set_lightness(BG_LIGHTNESS);
    let bg_hot = hot.set_lightness(BG_LIGHTNESS);
    let bg = bg_cold.mix(bg_hot, cpu_total).clamp_to_888();

    let idle_cold = cold.set_lightness(IDLE_LIGHTNESS);
    let idle_hot = hot.set_lightness(IDLE_LIGHTNESS);
    let idle = idle_cold.mix(idle_hot, cpu_total).clamp_to_888();

    let fg_cold = cold.set_lightness(FG_LIGHTNESS);
    let fg_hot = hot.set_lightness(FG_LIGHTNESS);
    let fg_for = |usage: f32| if usage <= IDLE_THRESHOLD {
        idle
    } else {
        let usage = (usage - IDLE_THRESHOLD) / (1.0 - IDLE_THRESHOLD);
        fg_cold.mix(fg_hot, usage).clamp_to_888()
    };

    let indicator_for = |usage: f32| {
        let len = PROGRESS_INDICATORS.chars().count() as f32;
        let i = (len * usage).min(len - 1.0).max(0.0) as usize;
        PROGRESS_INDICATORS.chars().nth(i).unwrap()
    };

    let cpu_usages = cpu_usages.iter()
        .map(|&usage| format!("({}:{})", fg_for(usage), indicator_for(usage)))
        .collect::<String>();
    let total_usage = {
        let color = fg_for(cpu_total);
        let percent = (100.0 * cpu_total) as i32;
        let percent = format!("{:>3}%", percent);
        let percent = escape_fastup(percent);
        format!("({}:{})", color, percent)
    };

    let node = format!("[{}: \\ {}{} ]", bg, cpu_usages, total_usage);
    parse_for_first_node(&node).unwrap()
}

fn escape_fastup<T>(input: T) -> String
    where T: ToString
{
    fastup::escape_for_text(&input.to_string())
}

<|endoftext|>


<commit_before>
use xkb::ffi::{xkb_context, xkb_keymap, xkb_state, xkb_keymap_compile_flags};

use xcb::ffi::xcb_connection_t;
use std::os::raw::{c_int};

pub const XKB_X11_MIN_MAJOR_XKB_VERSION: u16 = 1;
pub const XKB_X11_MIN_MINOR_XKB_VERSION: u16 = 0;

#[repr(C)]
pub enum xkb_x11_setup_xkb_extension_flags {
    /** Do not apply any flags. */
    NO_FLAGS = 0
}

#[link(name = "xkbcommon-x11")]
extern {

    pub fn xkb_x11_setup_xkb_extension(connection: *mut xcb_connection_t,
                                       major_xkb_version: u16,
                                       minor_xkb_version: u16,
                                       flags: xkb_x11_setup_xkb_extension_flags,
                                       major_xkb_version_out: *mut u16,
                                       minor_xkb_version_out: *mut u16,
                                       base_event_out: *mut u8,
                                       base_error_out: *mut u8) -> c_int;

    pub fn xkb_x11_get_core_keyboard_device_id(connection: *mut xcb_connection_t)
            -> i32;

    pub fn xkb_x11_keymap_new_from_device(context: *mut xkb_context,
                                          connection: *mut xcb_connection_t,
                                          device_id: i32,
                                          flags: xkb_keymap_compile_flags)
            -> *mut xkb_keymap;

    pub fn xkb_x11_state_new_from_device(keymap: *mut xkb_keymap,
                                         connection: *mut xcb_connection_t,
                                         device_id: i32)
            -> *mut xkb_state;

}
<commit_msg>remove minor comments<commit_after>
use xkb::ffi::{xkb_context, xkb_keymap, xkb_state, xkb_keymap_compile_flags};

use xcb::ffi::xcb_connection_t;
use std::os::raw::{c_int};

pub const XKB_X11_MIN_MAJOR_XKB_VERSION: u16 = 1;
pub const XKB_X11_MIN_MINOR_XKB_VERSION: u16 = 0;

#[repr(C)]
#[allow(non_camel_case_types)]
pub enum xkb_x11_setup_xkb_extension_flags {
    /** Do not apply any flags. */
    NO_FLAGS = 0
}

#[link(name = "xkbcommon-x11")]
extern {

    pub fn xkb_x11_setup_xkb_extension(connection: *mut xcb_connection_t,
                                       major_xkb_version: u16,
                                       minor_xkb_version: u16,
                                       flags: xkb_x11_setup_xkb_extension_flags,
                                       major_xkb_version_out: *mut u16,
                                       minor_xkb_version_out: *mut u16,
                                       base_event_out: *mut u8,
                                       base_error_out: *mut u8) -> c_int;

    pub fn xkb_x11_get_core_keyboard_device_id(connection: *mut xcb_connection_t)
            -> i32;

    pub fn xkb_x11_keymap_new_from_device(context: *mut xkb_context,
                                          connection: *mut xcb_connection_t,
                                          device_id: i32,
                                          flags: xkb_keymap_compile_flags)
            -> *mut xkb_keymap;

    pub fn xkb_x11_state_new_from_device(keymap: *mut xkb_keymap,
                                         connection: *mut xcb_connection_t,
                                         device_id: i32)
            -> *mut xkb_state;

}
<|endoftext|>


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

use std::num::Float;
use cgmath::{perspective, deg, Matrix4, Vector, Vector3, Deg, ToRad};
use common::types::{ZInt, Size2, ZFloat};
use common::misc::{clamp};
use zgl::{Zgl};
use types::{WorldPos};

pub struct Camera {
    x_angle: Deg<ZFloat>,
    z_angle: Deg<ZFloat>,
    pos: WorldPos,
    max_pos: WorldPos,
    zoom: ZFloat,
    projection_mat: Matrix4<ZFloat>,
}

fn get_projection_mat(win_size: &Size2<ZInt>) -> Matrix4<ZFloat> {
    let fov = deg(45.0f32);
    let ratio = win_size.w as ZFloat / win_size.h as ZFloat;
    let display_range_min = 0.1;
    let display_range_max = 100.0;
    perspective(
        fov, ratio, display_range_min, display_range_max)
}

impl Camera {
    pub fn new(win_size: &Size2<ZInt>) -> Camera {
        Camera {
            x_angle: deg(45.0),
            z_angle: deg(0.0),
            pos: WorldPos{v: Vector::from_value(0.0)},
            max_pos: WorldPos{v: Vector::from_value(0.0)},
            zoom: 10.0,
            projection_mat: get_projection_mat(win_size),
        }
    }

    pub fn mat(&self, zgl: &Zgl) -> Matrix4<ZFloat> {
        let mut m = self.projection_mat;
        m = zgl.tr(m, &Vector3{x: 0.0, y: 0.0, z: -self.zoom});
        m = zgl.rot_x(m, &-self.x_angle);
        m = zgl.rot_z(m, &-self.z_angle);
        m = zgl.tr(m, &self.pos.v);
        m
    }

    pub fn add_horizontal_angle(&mut self, angle: Deg<ZFloat>) {
        self.z_angle = self.z_angle + angle; // TODO: cgmath: Deg: '+='
        while self.z_angle < deg(0.0) {
            self.z_angle = self.z_angle + deg(360.0);
        }
        while self.z_angle > deg(360.0) {
            self.z_angle = self.z_angle - deg(360.0);
        }
    }

    pub fn add_vertical_angle(&mut self, angle: Deg<ZFloat>) {
        self.x_angle = self.x_angle + angle;
        self.x_angle = clamp(self.x_angle, deg(10.0), deg(50.0));
    }

    fn clamp_pos(&mut self) {
        self.pos.v.x = clamp(self.pos.v.x, self.max_pos.v.x, 0.0);
        self.pos.v.y = clamp(self.pos.v.y, self.max_pos.v.y, 0.0);
    }

    pub fn set_pos(&mut self, pos: WorldPos) {
        self.pos = pos;
        self.clamp_pos();
    }

    pub fn set_max_pos(&mut self, max_pos: WorldPos) {
        self.max_pos = max_pos;
    }

    pub fn change_zoom(&mut self, ratio: ZFloat) {
        self.zoom *= ratio;
        self.zoom = clamp(self.zoom, 5.0, 40.0);
    }

    pub fn get_z_angle(&self) -> &Deg<ZFloat> {
        &self.z_angle
    }

    pub fn move_camera(&mut self, angle: Deg<ZFloat>, speed: ZFloat) {
        let speed_in_radians = (self.z_angle - angle).to_rad().s;
        let dx = speed_in_radians.sin();
        let dy = speed_in_radians.cos();
        // TODO: handle zoom
        // self.pos.v.x -= dy * speed * self.zoom;
        // self.pos.v.y -= dx * speed * self.zoom;
        self.pos.v.x -= dy * speed;
        self.pos.v.y -= dx * speed;
        self.clamp_pos();
    }

    pub fn regenerate_projection_mat(&mut self, win_size: &Size2<ZInt>) {
        self.projection_mat = get_projection_mat(win_size);
    }
}

// vim: set tabstop=4 shiftwidth=4 softtabstop=4 expandtab:
<commit_msg>camera: Increased default zoom: 10 -> 16<commit_after>// See LICENSE file for copyright and license details.

use std::num::Float;
use cgmath::{perspective, deg, Matrix4, Vector, Vector3, Deg, ToRad};
use common::types::{ZInt, Size2, ZFloat};
use common::misc::{clamp};
use zgl::{Zgl};
use types::{WorldPos};

pub struct Camera {
    x_angle: Deg<ZFloat>,
    z_angle: Deg<ZFloat>,
    pos: WorldPos,
    max_pos: WorldPos,
    zoom: ZFloat,
    projection_mat: Matrix4<ZFloat>,
}

fn get_projection_mat(win_size: &Size2<ZInt>) -> Matrix4<ZFloat> {
    let fov = deg(45.0f32);
    let ratio = win_size.w as ZFloat / win_size.h as ZFloat;
    let display_range_min = 0.1;
    let display_range_max = 100.0;
    perspective(
        fov, ratio, display_range_min, display_range_max)
}

impl Camera {
    pub fn new(win_size: &Size2<ZInt>) -> Camera {
        Camera {
            x_angle: deg(45.0),
            z_angle: deg(0.0),
            pos: WorldPos{v: Vector::from_value(0.0)},
            max_pos: WorldPos{v: Vector::from_value(0.0)},
            zoom: 16.0,
            projection_mat: get_projection_mat(win_size),
        }
    }

    pub fn mat(&self, zgl: &Zgl) -> Matrix4<ZFloat> {
        let mut m = self.projection_mat;
        m = zgl.tr(m, &Vector3{x: 0.0, y: 0.0, z: -self.zoom});
        m = zgl.rot_x(m, &-self.x_angle);
        m = zgl.rot_z(m, &-self.z_angle);
        m = zgl.tr(m, &self.pos.v);
        m
    }

    pub fn add_horizontal_angle(&mut self, angle: Deg<ZFloat>) {
        self.z_angle = self.z_angle + angle; // TODO: cgmath: Deg: '+='
        while self.z_angle < deg(0.0) {
            self.z_angle = self.z_angle + deg(360.0);
        }
        while self.z_angle > deg(360.0) {
            self.z_angle = self.z_angle - deg(360.0);
        }
    }

    pub fn add_vertical_angle(&mut self, angle: Deg<ZFloat>) {
        self.x_angle = self.x_angle + angle;
        self.x_angle = clamp(self.x_angle, deg(10.0), deg(50.0));
    }

    fn clamp_pos(&mut self) {
        self.pos.v.x = clamp(self.pos.v.x, self.max_pos.v.x, 0.0);
        self.pos.v.y = clamp(self.pos.v.y, self.max_pos.v.y, 0.0);
    }

    pub fn set_pos(&mut self, pos: WorldPos) {
        self.pos = pos;
        self.clamp_pos();
    }

    pub fn set_max_pos(&mut self, max_pos: WorldPos) {
        self.max_pos = max_pos;
    }

    pub fn change_zoom(&mut self, ratio: ZFloat) {
        self.zoom *= ratio;
        self.zoom = clamp(self.zoom, 5.0, 40.0);
    }

    pub fn get_z_angle(&self) -> &Deg<ZFloat> {
        &self.z_angle
    }

    pub fn move_camera(&mut self, angle: Deg<ZFloat>, speed: ZFloat) {
        let speed_in_radians = (self.z_angle - angle).to_rad().s;
        let dx = speed_in_radians.sin();
        let dy = speed_in_radians.cos();
        // TODO: handle zoom
        // self.pos.v.x -= dy * speed * self.zoom;
        // self.pos.v.y -= dx * speed * self.zoom;
        self.pos.v.x -= dy * speed;
        self.pos.v.y -= dx * speed;
        self.clamp_pos();
    }

    pub fn regenerate_projection_mat(&mut self, win_size: &Size2<ZInt>) {
        self.projection_mat = get_projection_mat(win_size);
    }
}

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


<commit_before>#![crate_name = "rustful_macros"]

#![crate_type = "dylib"]

#![doc(html_root_url = "http://ogeon.github.io/rustful/doc/")]

#![feature(plugin_registrar, quote, rustc_private, collections, path)]

//!This crate provides some helpful macros for rustful, including `insert_routes!` and `content_type!`.
//!
//!#`insert_routes!`
//!The `insert_routes!` macro generates routes from the provided handlers and routing tree and
//!adds them to the provided router. The router is then returned.
//!
//!This can be useful to lower the risk of typing errors, among other things.
//!
//!##Example 1
//!
//!```rust ignore
//!#![feature(plugin)]
//!#[plugin]
//!#[no_link]
//!extern crate rustful_macros;
//!
//!extern crate rustful;
//!
//!...
//!
//!let router = insert_routes!{
//!    TreeRouter::new(): {
//!        "/about" => Get: about_us,
//!        "/user/:user" => Get: show_user,
//!        "/product/:name" => Get: show_product,
//!        "/*" => Get: show_error,
//!        "/" => Get: show_welcome
//!    }
//!};
//!```
//!
//!##Example 2
//!
//!```rust ignore
//!#![feature(plugin)]
//!#[plugin]
//!#[no_link]
//!extern crate rustful_macros;
//!
//!extern crate rustful;
//!
//!...
//!
//!let mut router = TreeRouter::new();
//!insert_routes!{
//!    &mut router: {
//!        "/" => Get: show_home,
//!        "home" => Get: show_home,
//!        "user/:username" => {Get: show_user, Post: save_user},
//!        "product" => {
//!            Get: show_all_products,
//!
//!            "json" => Get: send_all_product_data
//!            ":id" => {
//!                Get: show_product,
//!                Post | Delete: edit_product,
//!
//!                "json" => Get: send_product_data
//!            }
//!        }
//!    }
//!};
//!```

extern crate syntax;
extern crate rustc;

use std::path::BytesContainer;

use syntax::{ast, codemap};
use syntax::ext::base::{
    ExtCtxt, MacResult, MacExpr,
    NormalTT, TTMacroExpander
};
use syntax::ext::build::AstBuilder;
use syntax::ext::quote::rt::ExtParseUtils;
use syntax::parse;
use syntax::parse::token;
use syntax::parse::parser;
use syntax::parse::parser::Parser;
use syntax::ptr::P;
//use syntax::print::pprust;

use rustc::plugin::Registry;

#[plugin_registrar]
#[doc(hidden)]
pub fn macro_registrar(reg: &mut Registry) {
    let expander = Box::new(expand_routes) as Box<TTMacroExpander>;
    reg.register_syntax_extension(token::intern("insert_routes"), NormalTT(expander, None));
}

fn expand_routes<'cx>(cx: &'cx mut ExtCtxt, sp: codemap::Span, tts: &[ast::TokenTree]) -> Box<MacResult + 'cx> {
    let insert_method = cx.ident_of("insert");
    let router_ident = cx.ident_of("router");
    let router_var = cx.expr_ident(sp, router_ident);
    let router_trait_path = cx.path_global(sp, vec![cx.ident_of("rustful"), cx.ident_of("Router")]);
    let router_trait_use = cx.item_use_simple(sp, ast::Inherited, router_trait_path);

    let mut parser = parse::new_parser_from_tts(
        cx.parse_sess(), cx.cfg(), tts.to_vec()
    );

    let mut calls = vec![cx.stmt_item(sp, router_trait_use), cx.stmt_let(sp, true, router_ident, parser.parse_expr())];
    parser.expect(&token::Colon);

    for (path, method, handler) in parse_routes(cx, &mut parser).into_iter() {
        let path_expr = cx.parse_expr(format!("\"{}\"", path));
        let method_expr = cx.expr_path(method);
        calls.push(cx.stmt_expr(cx.expr_method_call(sp, router_var.clone(), insert_method, vec![method_expr, path_expr, handler])));
    }

    let block = cx.expr_block(cx.block_all(sp, calls, Some(router_var)));

    MacExpr::new(block)
}

fn parse_routes(cx: &mut ExtCtxt, parser: &mut Parser) -> Vec<(String, ast::Path, P<ast::Expr>)> {
    parse_subroutes("", cx, parser)
}

fn parse_subroutes(base: &str, cx: &mut ExtCtxt, parser: &mut Parser) -> Vec<(String, ast::Path, P<ast::Expr>)> {
    let mut routes = Vec::new();

    parser.eat(&token::OpenDelim(token::Brace));

    while !parser.eat(&token::Eof) {
        match parser.parse_optional_str() {
            Some((ref s, _, _)) => {
                if !parser.eat(&token::FatArrow) {
                    parser.expect(&token::FatArrow);
                    break;
                }

                let mut new_base = base.to_string();
                match s.container_as_str() {
                    Some(s) => {
                        new_base.push_str(s.trim_matches('/'));
                        new_base.push_str("/");
                    },
                    None => cx.span_err(parser.span, "invalid path")
                }

                if parser.eat(&token::Eof) {
                    cx.span_err(parser.span, "unexpected end of routing tree");
                }

                if parser.eat(&token::OpenDelim(token::Brace)) {
                    let subroutes = parse_subroutes(&*new_base, cx, parser);
                    routes.push_all(&*subroutes);

                    if parser.eat(&token::CloseDelim(token::Brace)) {
                        if !parser.eat(&token::Comma) {
                            break;
                        }
                    } else {
                        parser.expect_one_of(&[token::Comma, token::CloseDelim(token::Brace)], &[]);
                    }
                } else {
                    for (method, handler) in parse_handler(parser).into_iter() {
                        routes.push((new_base.clone(), method, handler))
                    }

                    if !parser.eat(&token::Comma) {
                        break;
                    }
                }
            },
            None => {
                for (method, handler) in parse_handler(parser).into_iter() {
                    routes.push((base.to_string(), method, handler))
                }

                if !parser.eat(&token::Comma) {
                    break;
                }
            }
        }
    }

    routes
}

fn parse_handler(parser: &mut Parser) -> Vec<(ast::Path, P<ast::Expr>)> {
    let mut methods = Vec::new();

    loop {
        methods.push(parser.parse_path(parser::NoTypesAllowed));

        if parser.eat(&token::Colon) {
            break;
        }

        if !parser.eat(&token::BinOp(token::Or)) {
            parser.expect_one_of(&[token::Colon, token::BinOp(token::Or)], &[]);
        }
    }

    let handler = parser.parse_expr();

    methods.into_iter().map(|m| (m, handler.clone())).collect()
}

/**
A macro for assigning content types.

It takes a main type, a sub type and a parameter list. Instead of this:

```
response.headers.content_type = Some(MediaType {
    type_: String::from_str("text"),
    subtype: String::from_str("html"),
    parameters: vec!((String::from_str("charset"), String::from_str("UTF-8")))
});
```

it can be written like this:

```
response.headers.content_type = content_type!("text", "html", "charset": "UTF-8");
```

The `"charset": "UTF-8"` part defines the parameter list for the content type.
It may contain more than one parameter, or be omitted:

```
response.headers.content_type = content_type!("application", "octet-stream", "type": "image/gif", "padding": "4");
```

```
response.headers.content_type = content_type!("image", "png");
```
**/
#[macro_export]
macro_rules! content_type {
    ($main_type:expr, $sub_type:expr) => ({
        ::rustful::mime::Mime (
            std::str::FromStr::from_str($main_type).unwrap(),
            std::str::FromStr::from_str($sub_type).unwrap(),
            Vec::new()
        )
    });

    ($main_type:expr, $sub_type:expr, $(($param:expr, $value:expr)),+) => ({
        ::rustful::mime::Mime (
            std::str::FromStr::from_str($main_type).unwrap(),
            std::str::FromStr::from_str($sub_type).unwrap(),
            vec!( $( (std::str::FromStr::from_str($param).unwrap(), std::str::FromStr::from_str($value).unwrap()) ),+ )
        )
    });
}

/**
A macro for callig `send` in response and aborting the handle function if it fails.

This macro will print an error to `stdout`.
**/
#[macro_export]
macro_rules! try_send {
    ($writer:expr, $content:expr) => (
        match $writer.send($content) {
            Ok(v) => v,
            Err(::rustful::ResponseError::IoError(e)) => {
                println!("IO error: {}", e);
                return;
            },
            Err(::rustful::ResponseError::PluginError(e)) => {
                println!("plugin error: {}", e);
                return;
            }
        }
    );

    ($writer:expr, $content:expr, $what:expr) => (
        match $writer.send($content) {
            Ok(v) => v,
            Err(::rustful::ResponseError::IoError(e)) => {
                println!("IO error while {}: {}", $what, e);
                return;
            },
            Err(::rustful::ResponseError::PluginError(e)) => {
                println!("plugin error while {}: {}", $what, e);
                return;
            }
        }
    )
}<commit_msg>Use the core feature in rustful_macros<commit_after>#![crate_name = "rustful_macros"]

#![crate_type = "dylib"]

#![doc(html_root_url = "http://ogeon.github.io/rustful/doc/")]

#![feature(plugin_registrar, quote, rustc_private, collections, path, core)]

//!This crate provides some helpful macros for rustful, including `insert_routes!` and `content_type!`.
//!
//!#`insert_routes!`
//!The `insert_routes!` macro generates routes from the provided handlers and routing tree and
//!adds them to the provided router. The router is then returned.
//!
//!This can be useful to lower the risk of typing errors, among other things.
//!
//!##Example 1
//!
//!```rust ignore
//!#![feature(plugin)]
//!#[plugin]
//!#[no_link]
//!extern crate rustful_macros;
//!
//!extern crate rustful;
//!
//!...
//!
//!let router = insert_routes!{
//!    TreeRouter::new(): {
//!        "/about" => Get: about_us,
//!        "/user/:user" => Get: show_user,
//!        "/product/:name" => Get: show_product,
//!        "/*" => Get: show_error,
//!        "/" => Get: show_welcome
//!    }
//!};
//!```
//!
//!##Example 2
//!
//!```rust ignore
//!#![feature(plugin)]
//!#[plugin]
//!#[no_link]
//!extern crate rustful_macros;
//!
//!extern crate rustful;
//!
//!...
//!
//!let mut router = TreeRouter::new();
//!insert_routes!{
//!    &mut router: {
//!        "/" => Get: show_home,
//!        "home" => Get: show_home,
//!        "user/:username" => {Get: show_user, Post: save_user},
//!        "product" => {
//!            Get: show_all_products,
//!
//!            "json" => Get: send_all_product_data
//!            ":id" => {
//!                Get: show_product,
//!                Post | Delete: edit_product,
//!
//!                "json" => Get: send_product_data
//!            }
//!        }
//!    }
//!};
//!```

extern crate syntax;
extern crate rustc;

use std::path::BytesContainer;

use syntax::{ast, codemap};
use syntax::ext::base::{
    ExtCtxt, MacResult, MacExpr,
    NormalTT, TTMacroExpander
};
use syntax::ext::build::AstBuilder;
use syntax::ext::quote::rt::ExtParseUtils;
use syntax::parse;
use syntax::parse::token;
use syntax::parse::parser;
use syntax::parse::parser::Parser;
use syntax::ptr::P;
//use syntax::print::pprust;

use rustc::plugin::Registry;

#[plugin_registrar]
#[doc(hidden)]
pub fn macro_registrar(reg: &mut Registry) {
    let expander = Box::new(expand_routes) as Box<TTMacroExpander>;
    reg.register_syntax_extension(token::intern("insert_routes"), NormalTT(expander, None));
}

fn expand_routes<'cx>(cx: &'cx mut ExtCtxt, sp: codemap::Span, tts: &[ast::TokenTree]) -> Box<MacResult + 'cx> {
    let insert_method = cx.ident_of("insert");
    let router_ident = cx.ident_of("router");
    let router_var = cx.expr_ident(sp, router_ident);
    let router_trait_path = cx.path_global(sp, vec![cx.ident_of("rustful"), cx.ident_of("Router")]);
    let router_trait_use = cx.item_use_simple(sp, ast::Inherited, router_trait_path);

    let mut parser = parse::new_parser_from_tts(
        cx.parse_sess(), cx.cfg(), tts.to_vec()
    );

    let mut calls = vec![cx.stmt_item(sp, router_trait_use), cx.stmt_let(sp, true, router_ident, parser.parse_expr())];
    parser.expect(&token::Colon);

    for (path, method, handler) in parse_routes(cx, &mut parser).into_iter() {
        let path_expr = cx.parse_expr(format!("\"{}\"", path));
        let method_expr = cx.expr_path(method);
        calls.push(cx.stmt_expr(cx.expr_method_call(sp, router_var.clone(), insert_method, vec![method_expr, path_expr, handler])));
    }

    let block = cx.expr_block(cx.block_all(sp, calls, Some(router_var)));

    MacExpr::new(block)
}

fn parse_routes(cx: &mut ExtCtxt, parser: &mut Parser) -> Vec<(String, ast::Path, P<ast::Expr>)> {
    parse_subroutes("", cx, parser)
}

fn parse_subroutes(base: &str, cx: &mut ExtCtxt, parser: &mut Parser) -> Vec<(String, ast::Path, P<ast::Expr>)> {
    let mut routes = Vec::new();

    parser.eat(&token::OpenDelim(token::Brace));

    while !parser.eat(&token::Eof) {
        match parser.parse_optional_str() {
            Some((ref s, _, _)) => {
                if !parser.eat(&token::FatArrow) {
                    parser.expect(&token::FatArrow);
                    break;
                }

                let mut new_base = base.to_string();
                match s.container_as_str() {
                    Some(s) => {
                        new_base.push_str(s.trim_matches('/'));
                        new_base.push_str("/");
                    },
                    None => cx.span_err(parser.span, "invalid path")
                }

                if parser.eat(&token::Eof) {
                    cx.span_err(parser.span, "unexpected end of routing tree");
                }

                if parser.eat(&token::OpenDelim(token::Brace)) {
                    let subroutes = parse_subroutes(&*new_base, cx, parser);
                    routes.push_all(&*subroutes);

                    if parser.eat(&token::CloseDelim(token::Brace)) {
                        if !parser.eat(&token::Comma) {
                            break;
                        }
                    } else {
                        parser.expect_one_of(&[token::Comma, token::CloseDelim(token::Brace)], &[]);
                    }
                } else {
                    for (method, handler) in parse_handler(parser).into_iter() {
                        routes.push((new_base.clone(), method, handler))
                    }

                    if !parser.eat(&token::Comma) {
                        break;
                    }
                }
            },
            None => {
                for (method, handler) in parse_handler(parser).into_iter() {
                    routes.push((base.to_string(), method, handler))
                }

                if !parser.eat(&token::Comma) {
                    break;
                }
            }
        }
    }

    routes
}

fn parse_handler(parser: &mut Parser) -> Vec<(ast::Path, P<ast::Expr>)> {
    let mut methods = Vec::new();

    loop {
        methods.push(parser.parse_path(parser::NoTypesAllowed));

        if parser.eat(&token::Colon) {
            break;
        }

        if !parser.eat(&token::BinOp(token::Or)) {
            parser.expect_one_of(&[token::Colon, token::BinOp(token::Or)], &[]);
        }
    }

    let handler = parser.parse_expr();

    methods.into_iter().map(|m| (m, handler.clone())).collect()
}

/**
A macro for assigning content types.

It takes a main type, a sub type and a parameter list. Instead of this:

```
response.headers.content_type = Some(MediaType {
    type_: String::from_str("text"),
    subtype: String::from_str("html"),
    parameters: vec!((String::from_str("charset"), String::from_str("UTF-8")))
});
```

it can be written like this:

```
response.headers.content_type = content_type!("text", "html", "charset": "UTF-8");
```

The `"charset": "UTF-8"` part defines the parameter list for the content type.
It may contain more than one parameter, or be omitted:

```
response.headers.content_type = content_type!("application", "octet-stream", "type": "image/gif", "padding": "4");
```

```
response.headers.content_type = content_type!("image", "png");
```
**/
#[macro_export]
macro_rules! content_type {
    ($main_type:expr, $sub_type:expr) => ({
        ::rustful::mime::Mime (
            std::str::FromStr::from_str($main_type).unwrap(),
            std::str::FromStr::from_str($sub_type).unwrap(),
            Vec::new()
        )
    });

    ($main_type:expr, $sub_type:expr, $(($param:expr, $value:expr)),+) => ({
        ::rustful::mime::Mime (
            std::str::FromStr::from_str($main_type).unwrap(),
            std::str::FromStr::from_str($sub_type).unwrap(),
            vec!( $( (std::str::FromStr::from_str($param).unwrap(), std::str::FromStr::from_str($value).unwrap()) ),+ )
        )
    });
}

/**
A macro for callig `send` in response and aborting the handle function if it fails.

This macro will print an error to `stdout`.
**/
#[macro_export]
macro_rules! try_send {
    ($writer:expr, $content:expr) => (
        match $writer.send($content) {
            Ok(v) => v,
            Err(::rustful::ResponseError::IoError(e)) => {
                println!("IO error: {}", e);
                return;
            },
            Err(::rustful::ResponseError::PluginError(e)) => {
                println!("plugin error: {}", e);
                return;
            }
        }
    );

    ($writer:expr, $content:expr, $what:expr) => (
        match $writer.send($content) {
            Ok(v) => v,
            Err(::rustful::ResponseError::IoError(e)) => {
                println!("IO error while {}: {}", $what, e);
                return;
            },
            Err(::rustful::ResponseError::PluginError(e)) => {
                println!("plugin error while {}: {}", $what, e);
                return;
            }
        }
    )
}<|endoftext|>