<commit_before>use super::bit;
use super::error::{Error, ErrorKind};
use super::query::Query;
use super::result::Result;
use super::utf8::{COMMA, CR, HT, LF, RIGHT_BRACE, SPACE};
use fnv::{FnvHashMap, FnvHashSet};

#[inline]
pub fn basic_parse<'a>(
    rec: &'a [u8],
    index: &[Vec<u64>],
    queries: &mut FnvHashMap<&[u8], Query>,
    start: usize,
    end: usize,
    level: usize,
    stats: &mut Vec<FnvHashSet<usize>>,
    set_stats: bool,
    results: &mut Vec<Option<&'a [u8]>>,
    b_quote: &[u64],
    colon_positions: &mut Vec<Vec<usize>>,
) -> Result<()> {
    generate_colon_positions(index, start, end, level, colon_positions);

    let query_num = queries.len();
    let mut found_num = 0;
    let mut vei = end;
    let cp_len = colon_positions[level].len();
    for i in (0..cp_len).rev() {
        let (fsi, fei) = search_pre_field_indices(
            b_quote,
            if i > 0 {
                colon_positions[level][i - 1]
            } else {
                start
            },
            colon_positions[level][i],
        )?;
        let field = &rec[fsi + 1..fei];
        if let Some(query) = queries.get_mut(field) {
            let (vsi, vei) = search_post_value_indices(
                rec,
                colon_positions[level][i] + 1,
                vei,
                if i == cp_len - 1 { RIGHT_BRACE } else { COMMA },
            )?;
            found_num += 1;
            if set_stats && !stats[query.i].contains(&i) {
                stats[query.i].insert(i);
            }
            if let Some(ref mut children) = query.children {
                basic_parse(
                    rec,
                    index,
                    children,
                    vsi,
                    vei,
                    level + 1,
                    stats,
                    set_stats,
                    results,
                    b_quote,
                    colon_positions,
                )?;
            }
            if query.target {
                results[query.ri] = Some(&rec[vsi..vei + 1]);
            }
            if found_num == query_num {
                return Ok(());
            }
        }
        vei = fsi - 1;
    }
    Ok(())
}

#[inline]
pub fn speculative_parse<'a>(rec: &'a [u8], index: &[Vec<u64>], queries: &FnvHashMap<&[u8], Query>, start: usize, end: usize, level: usize, stats: &[FnvHashSet<usize>], results: &mut Vec<Option<&'a [u8]>>, b_quote: &[u64], colon_positions: &mut Vec<Vec<usize>>) -> Result<bool> {
    generate_colon_positions(index, start, end, level, colon_positions);

    for (&s, q) in queries {
        let mut found = false;
        for &i in &stats[q.i] {
            let cp_len = colon_positions[level].len();
            if i >= cp_len {
                continue;
            }
            let (fsi, fei) = search_pre_field_indices(
                b_quote,
                if i > 0 {
                    colon_positions[level][i - 1]
                } else {
                    start
                },
                colon_positions[level][i],
            )?;
            let field = &rec[fsi + 1..fei];
            if s == field {
                let vei = if i < cp_len - 1 {
                    let (nfsi, _) = search_pre_field_indices(
                        b_quote,
                        colon_positions[level][i],
                        colon_positions[level][i + 1],
                    )?;
                    nfsi - 1
                } else {
                    end
                };
                let (vsi, vei) = search_post_value_indices(
                    rec,
                    colon_positions[level][i] + 1,
                    vei,
                    if i == cp_len - 1 { RIGHT_BRACE } else { COMMA },
                )?;
                if let Some(ref children) = q.children {
                    found = speculative_parse(
                        rec,
                        index,
                        children,
                        vsi,
                        vei,
                        level + 1,
                        stats,
                        results,
                        b_quote,
                        colon_positions,
                    )?;
                } else {
                    found = true;
                }
                if q.target {
                    results[q.ri] = Some(&rec[vsi..vei + 1]);
                }
                break;
            }
        }
        if !found {
            return Ok(false);
        }
    }
    Ok(true)
}

#[inline]
fn generate_colon_positions(index: &[Vec<u64>], start: usize, end: usize, level: usize, colon_positions: &mut Vec<Vec<usize>>) {
    let cp = &mut colon_positions[level];
    cp.clear();
    for i in start / 64..(end + 63) / 64 {
        let mut m_colon = index[level][i];
        while m_colon != 0 {
            let m_bit = bit::e(m_colon);
            let offset = i * 64 + (m_bit.trailing_zeros() as usize);
            if start <= offset && offset <= end {
                cp.push(offset);
            }
            m_colon = bit::r(m_colon);
        }
    }
}

#[inline]
fn search_pre_field_indices(b_quote: &[u64], start: usize, end: usize) -> Result<(usize, usize)> {
    let mut si = 0;
    let mut ei = 0;
    let mut ei_set = false;
    let mut n_quote = 0;
    for i in (start / 64..(end + 63) / 64).rev() {
        let mut m_quote = b_quote[i];
        while m_quote != 0 {
            let m_bit = bit::e(m_quote);
            let offset = i * 64 + (m_bit.trailing_zeros() as usize);
            if end <= offset {
                break;
            }
            if start < offset {
                if ei_set {
                    si = offset;
                } else {
                    si = ei;
                    ei = offset;
                }
                n_quote += 1;
            }
            m_quote = bit::r(m_quote);
        }
        if n_quote >= 2 {
            break;
        }
        if n_quote == 1 && !ei_set {
            ei_set = true;
        }
    }
    if n_quote >= 2 {
        Ok((si, ei))
    } else {
        Err(Error::from(ErrorKind::InvalidRecord))
    }
}

#[inline]
fn search_post_value_indices(rec: &[u8], si: usize, ei: usize, ignore_once_char: u8) -> Result<(usize, usize)> {
    let mut si = si;
    let mut ei = ei;
    let mut ignore_once_char_ignored = false;
    let n = rec.len();
    while si < n {
        match rec[si] {
            SPACE | HT | LF | CR => {
                si += 1;
            }
            _ => {
                break;
            }
        }
    }
    if si == n {
        return Err(Error::from(ErrorKind::InvalidRecord));
    }
    while si <= ei {
        match rec[ei] {
            SPACE | HT | LF | CR => {
                ei -= 1;
            }
            char if char == ignore_once_char => {
                if ignore_once_char_ignored {
                    break;
                }
                ignore_once_char_ignored = true;
                ei -= 1;
            }
            _ => {
                break;
            }
        }
    }
    if ei < si {
        return Err(Error::from(ErrorKind::InvalidRecord));
    }
    Ok((si, ei))
}

#[cfg(test)]
mod tests {
    use super::*;
    use super::super::avx;
    use super::super::index_builder;
    use super::super::utf8::{BACKSLASH, COLON, LEFT_BRACE, QUOTE, RIGHT_BRACE};

    #[test]
    fn test_basic_parse() {
        let json_rec_str = r#"{ "aaa" : "AAA", "bbb" : 111, "ccc": ["C1", "C2"], "ddd" : { "d1" : "D1", "d2" : "D2", "d3": 333 }, "eee": { "e1": "EEE" } } "#;
        let json_rec = json_rec_str.as_bytes();
        let mut b_backslash = Vec::with_capacity((json_rec.len() + 63) / 64);
        let mut b_quote = Vec::with_capacity((json_rec.len() + 63) / 64);
        let mut b_colon = Vec::with_capacity((json_rec.len() + 63) / 64);
        let mut b_left = Vec::with_capacity((json_rec.len() + 63) / 64);
        let mut b_right = Vec::with_capacity((json_rec.len() + 63) / 64);
        index_builder::build_structural_character_bitmap(
            &json_rec,
            &mut b_backslash,
            &mut b_quote,
            &mut b_colon,
            &mut b_left,
            &mut b_right,
            &avx::mm256i(BACKSLASH as i8),
            &avx::mm256i(QUOTE as i8),
            &avx::mm256i(COLON as i8),
            &avx::mm256i(LEFT_BRACE as i8),
            &avx::mm256i(RIGHT_BRACE as i8),
        );
        index_builder::build_structural_quote_bitmap(&b_backslash, &mut b_quote);
        let mut b_string_mask = Vec::with_capacity(b_quote.len());
        index_builder::build_string_mask_bitmap(&b_quote, &mut b_string_mask);
        for i in 0..b_string_mask.len() {
            let b = b_string_mask[i];
            b_colon[i] &= b;
            b_left[i] &= b;
            b_right[i] &= b;
        }
        let l = 10;
        let mut index = vec![Vec::new(); l];
        let mut s_left = Vec::new();
        let r = index_builder::build_leveled_colon_bitmap(&b_colon, &b_left, &b_right, l, &mut s_left, &mut index);
        assert_eq!(Ok(()), r);
        let mut children = FnvHashMap::default();
        children.insert(
            "d1".as_bytes(),
            Query {
                i: 0,
                ri: 0,
                target: true,
                children: None,
            },
        );
        children.insert(
            "d3".as_bytes(),
            Query {
                i: 1,
                ri: 1,
                target: true,
                children: None,
            },
        );
        let mut queries = FnvHashMap::default();
        queries.insert(
            "aaa".as_bytes(),
            Query {
                i: 2,
                ri: 2,
                target: true,
                children: None,
            },
        );
        queries.insert(
            "bbb".as_bytes(),
            Query {
                i: 3,
                ri: 3,
                target: true,
                children: None,
            },
        );
        queries.insert(
            "ccc".as_bytes(),
            Query {
                i: 4,
                ri: 4,
                target: true,
                children: None,
            },
        );
        queries.insert(
            "ddd".as_bytes(),
            Query {
                i: 5,
                ri: 0,
                target: false,
                children: Some(children),
            },
        );
        queries.insert(
            "eee".as_bytes(),
            Query {
                i: 6,
                ri: 5,
                target: true,
                children: None,
            },
        );

        let mut stats = vec![
            FnvHashSet::default(),
            FnvHashSet::default(),
            FnvHashSet::default(),
            FnvHashSet::default(),
            FnvHashSet::default(),
            FnvHashSet::default(),
            FnvHashSet::default(),
        ];

        let mut results = vec![None, None, None, None, None, None];
        let mut colon_positions = vec![Vec::new(); l];
        let result = basic_parse(
            json_rec,
            &index,
            &mut queries,
            0,
            json_rec.len() - 1,
            0,
            &mut stats,
            true,
            &mut results,
            &b_quote,
            &mut colon_positions,
        );

        assert_eq!(Ok(()), result);
        assert_eq!(Some(r#""D1""#.as_bytes()), results[0]);
        assert_eq!(Some(r#"333"#.as_bytes()), results[1]);
        assert_eq!(Some(r#""AAA""#.as_bytes()), results[2]);
        assert_eq!(Some(r#"111"#.as_bytes()), results[3]);
        assert_eq!(Some(r#"["C1", "C2"]"#.as_bytes()), results[4]);
        assert_eq!(Some(r#"{ "e1": "EEE" }"#.as_bytes()), results[5]);
    }

    #[test]
    fn test_generate_colon_positions() {
        struct TestCase {
            index: Vec<Vec<u64>>,
            start: usize,
            end: usize,
            level: usize,
            want: Vec<usize>,
        }
        let test_cases = vec![
            TestCase {
                index: vec![vec![]],
                start: 0,
                end: 0,
                level: 0,
                want: vec![],
            },
            TestCase {
                index: vec![
                    vec![
                        0b00000001_11000000_00000000_00000000_00001000_00000000_00000011_10000000,
                    ],
                ],
                start: 8,
                end: 55,
                level: 0,
                want: vec![8, 9, 27, 54, 55],
            },
            TestCase {
                index: vec![
                    vec![
                        0b10000000_00000000_00000000_00000000_00001000_00000000_00000011_10000000,
                        0b00000001_11000001_00000000_00000011_00000000_00000000_00000000_00000001,
                    ],
                ],
                start: 8,
                end: 119,
                level: 0,
                want: vec![8, 9, 27, 63, 64, 96, 97, 112, 118, 119],
            },
        ];
        for t in test_cases {
            let mut cp = vec![Vec::new(); t.level + 1];
            generate_colon_positions(&t.index, t.start, t.end, t.level, &mut cp);
            assert_eq!(t.want, cp[0]);
        }
    }
}
<commit_msg>refactor(parser): remove unnecessary `mut` flag from arguments<commit_after>use super::bit;
use super::error::{Error, ErrorKind};
use super::query::Query;
use super::result::Result;
use super::utf8::{COMMA, CR, HT, LF, RIGHT_BRACE, SPACE};
use fnv::{FnvHashMap, FnvHashSet};

#[inline]
pub fn basic_parse<'a>(
    rec: &'a [u8],
    index: &[Vec<u64>],
    queries: &FnvHashMap<&[u8], Query>,
    start: usize,
    end: usize,
    level: usize,
    stats: &mut Vec<FnvHashSet<usize>>,
    set_stats: bool,
    results: &mut Vec<Option<&'a [u8]>>,
    b_quote: &[u64],
    colon_positions: &mut Vec<Vec<usize>>,
) -> Result<()> {
    generate_colon_positions(index, start, end, level, colon_positions);

    let mut found_num = 0;
    let mut vei = end;
    let cp_len = colon_positions[level].len();
    for i in (0..cp_len).rev() {
        let (fsi, fei) = search_pre_field_indices(
            b_quote,
            if i > 0 {
                colon_positions[level][i - 1]
            } else {
                start
            },
            colon_positions[level][i],
        )?;
        let field = &rec[fsi + 1..fei];
        if let Some(query) = queries.get(field) {
            let (vsi, vei) = search_post_value_indices(
                rec,
                colon_positions[level][i] + 1,
                vei,
                if i == cp_len - 1 { RIGHT_BRACE } else { COMMA },
            )?;
            found_num += 1;
            if set_stats && !stats[query.i].contains(&i) {
                stats[query.i].insert(i);
            }
            if let Some(ref children) = query.children {
                basic_parse(
                    rec,
                    index,
                    children,
                    vsi,
                    vei,
                    level + 1,
                    stats,
                    set_stats,
                    results,
                    b_quote,
                    colon_positions,
                )?;
            }
            if query.target {
                results[query.ri] = Some(&rec[vsi..vei + 1]);
            }
            if found_num == queries.len() {
                return Ok(());
            }
        }
        vei = fsi - 1;
    }
    Ok(())
}

#[inline]
pub fn speculative_parse<'a>(rec: &'a [u8], index: &[Vec<u64>], queries: &FnvHashMap<&[u8], Query>, start: usize, end: usize, level: usize, stats: &[FnvHashSet<usize>], results: &mut Vec<Option<&'a [u8]>>, b_quote: &[u64], colon_positions: &mut Vec<Vec<usize>>) -> Result<bool> {
    generate_colon_positions(index, start, end, level, colon_positions);

    for (&s, q) in queries {
        let mut found = false;
        for &i in &stats[q.i] {
            let cp_len = colon_positions[level].len();
            if i >= cp_len {
                continue;
            }
            let (fsi, fei) = search_pre_field_indices(
                b_quote,
                if i > 0 {
                    colon_positions[level][i - 1]
                } else {
                    start
                },
                colon_positions[level][i],
            )?;
            let field = &rec[fsi + 1..fei];
            if s == field {
                let vei = if i < cp_len - 1 {
                    let (nfsi, _) = search_pre_field_indices(
                        b_quote,
                        colon_positions[level][i],
                        colon_positions[level][i + 1],
                    )?;
                    nfsi - 1
                } else {
                    end
                };
                let (vsi, vei) = search_post_value_indices(
                    rec,
                    colon_positions[level][i] + 1,
                    vei,
                    if i == cp_len - 1 { RIGHT_BRACE } else { COMMA },
                )?;
                if let Some(ref children) = q.children {
                    found = speculative_parse(
                        rec,
                        index,
                        children,
                        vsi,
                        vei,
                        level + 1,
                        stats,
                        results,
                        b_quote,
                        colon_positions,
                    )?;
                } else {
                    found = true;
                }
                if q.target {
                    results[q.ri] = Some(&rec[vsi..vei + 1]);
                }
                break;
            }
        }
        if !found {
            return Ok(false);
        }
    }
    Ok(true)
}

#[inline]
fn generate_colon_positions(index: &[Vec<u64>], start: usize, end: usize, level: usize, colon_positions: &mut Vec<Vec<usize>>) {
    let cp = &mut colon_positions[level];
    cp.clear();
    for i in start / 64..(end + 63) / 64 {
        let mut m_colon = index[level][i];
        while m_colon != 0 {
            let m_bit = bit::e(m_colon);
            let offset = i * 64 + (m_bit.trailing_zeros() as usize);
            if start <= offset && offset <= end {
                cp.push(offset);
            }
            m_colon = bit::r(m_colon);
        }
    }
}

#[inline]
fn search_pre_field_indices(b_quote: &[u64], start: usize, end: usize) -> Result<(usize, usize)> {
    let mut si = 0;
    let mut ei = 0;
    let mut ei_set = false;
    let mut n_quote = 0;
    for i in (start / 64..(end + 63) / 64).rev() {
        let mut m_quote = b_quote[i];
        while m_quote != 0 {
            let m_bit = bit::e(m_quote);
            let offset = i * 64 + (m_bit.trailing_zeros() as usize);
            if end <= offset {
                break;
            }
            if start < offset {
                if ei_set {
                    si = offset;
                } else {
                    si = ei;
                    ei = offset;
                }
                n_quote += 1;
            }
            m_quote = bit::r(m_quote);
        }
        if n_quote >= 2 {
            break;
        }
        if n_quote == 1 && !ei_set {
            ei_set = true;
        }
    }
    if n_quote >= 2 {
        Ok((si, ei))
    } else {
        Err(Error::from(ErrorKind::InvalidRecord))
    }
}

#[inline]
fn search_post_value_indices(rec: &[u8], si: usize, ei: usize, ignore_once_char: u8) -> Result<(usize, usize)> {
    let mut si = si;
    let mut ei = ei;
    let mut ignore_once_char_ignored = false;
    let n = rec.len();
    while si < n {
        match rec[si] {
            SPACE | HT | LF | CR => {
                si += 1;
            }
            _ => {
                break;
            }
        }
    }
    if si == n {
        return Err(Error::from(ErrorKind::InvalidRecord));
    }
    while si <= ei {
        match rec[ei] {
            SPACE | HT | LF | CR => {
                ei -= 1;
            }
            char if char == ignore_once_char => {
                if ignore_once_char_ignored {
                    break;
                }
                ignore_once_char_ignored = true;
                ei -= 1;
            }
            _ => {
                break;
            }
        }
    }
    if ei < si {
        return Err(Error::from(ErrorKind::InvalidRecord));
    }
    Ok((si, ei))
}

#[cfg(test)]
mod tests {
    use super::*;
    use super::super::avx;
    use super::super::index_builder;
    use super::super::utf8::{BACKSLASH, COLON, LEFT_BRACE, QUOTE, RIGHT_BRACE};

    #[test]
    fn test_basic_parse() {
        let json_rec_str = r#"{ "aaa" : "AAA", "bbb" : 111, "ccc": ["C1", "C2"], "ddd" : { "d1" : "D1", "d2" : "D2", "d3": 333 }, "eee": { "e1": "EEE" } } "#;
        let json_rec = json_rec_str.as_bytes();
        let mut b_backslash = Vec::with_capacity((json_rec.len() + 63) / 64);
        let mut b_quote = Vec::with_capacity((json_rec.len() + 63) / 64);
        let mut b_colon = Vec::with_capacity((json_rec.len() + 63) / 64);
        let mut b_left = Vec::with_capacity((json_rec.len() + 63) / 64);
        let mut b_right = Vec::with_capacity((json_rec.len() + 63) / 64);
        index_builder::build_structural_character_bitmap(
            &json_rec,
            &mut b_backslash,
            &mut b_quote,
            &mut b_colon,
            &mut b_left,
            &mut b_right,
            &avx::mm256i(BACKSLASH as i8),
            &avx::mm256i(QUOTE as i8),
            &avx::mm256i(COLON as i8),
            &avx::mm256i(LEFT_BRACE as i8),
            &avx::mm256i(RIGHT_BRACE as i8),
        );
        index_builder::build_structural_quote_bitmap(&b_backslash, &mut b_quote);
        let mut b_string_mask = Vec::with_capacity(b_quote.len());
        index_builder::build_string_mask_bitmap(&b_quote, &mut b_string_mask);
        for i in 0..b_string_mask.len() {
            let b = b_string_mask[i];
            b_colon[i] &= b;
            b_left[i] &= b;
            b_right[i] &= b;
        }
        let l = 10;
        let mut index = vec![Vec::new(); l];
        let mut s_left = Vec::new();
        let r = index_builder::build_leveled_colon_bitmap(&b_colon, &b_left, &b_right, l, &mut s_left, &mut index);
        assert_eq!(Ok(()), r);
        let mut children = FnvHashMap::default();
        children.insert(
            "d1".as_bytes(),
            Query {
                i: 0,
                ri: 0,
                target: true,
                children: None,
            },
        );
        children.insert(
            "d3".as_bytes(),
            Query {
                i: 1,
                ri: 1,
                target: true,
                children: None,
            },
        );
        let mut queries = FnvHashMap::default();
        queries.insert(
            "aaa".as_bytes(),
            Query {
                i: 2,
                ri: 2,
                target: true,
                children: None,
            },
        );
        queries.insert(
            "bbb".as_bytes(),
            Query {
                i: 3,
                ri: 3,
                target: true,
                children: None,
            },
        );
        queries.insert(
            "ccc".as_bytes(),
            Query {
                i: 4,
                ri: 4,
                target: true,
                children: None,
            },
        );
        queries.insert(
            "ddd".as_bytes(),
            Query {
                i: 5,
                ri: 0,
                target: false,
                children: Some(children),
            },
        );
        queries.insert(
            "eee".as_bytes(),
            Query {
                i: 6,
                ri: 5,
                target: true,
                children: None,
            },
        );

        let mut stats = vec![
            FnvHashSet::default(),
            FnvHashSet::default(),
            FnvHashSet::default(),
            FnvHashSet::default(),
            FnvHashSet::default(),
            FnvHashSet::default(),
            FnvHashSet::default(),
        ];

        let mut results = vec![None, None, None, None, None, None];
        let mut colon_positions = vec![Vec::new(); l];
        let result = basic_parse(
            json_rec,
            &index,
            &mut queries,
            0,
            json_rec.len() - 1,
            0,
            &mut stats,
            true,
            &mut results,
            &b_quote,
            &mut colon_positions,
        );

        assert_eq!(Ok(()), result);
        assert_eq!(Some(r#""D1""#.as_bytes()), results[0]);
        assert_eq!(Some(r#"333"#.as_bytes()), results[1]);
        assert_eq!(Some(r#""AAA""#.as_bytes()), results[2]);
        assert_eq!(Some(r#"111"#.as_bytes()), results[3]);
        assert_eq!(Some(r#"["C1", "C2"]"#.as_bytes()), results[4]);
        assert_eq!(Some(r#"{ "e1": "EEE" }"#.as_bytes()), results[5]);
    }

    #[test]
    fn test_generate_colon_positions() {
        struct TestCase {
            index: Vec<Vec<u64>>,
            start: usize,
            end: usize,
            level: usize,
            want: Vec<usize>,
        }
        let test_cases = vec![
            TestCase {
                index: vec![vec![]],
                start: 0,
                end: 0,
                level: 0,
                want: vec![],
            },
            TestCase {
                index: vec![
                    vec![
                        0b00000001_11000000_00000000_00000000_00001000_00000000_00000011_10000000,
                    ],
                ],
                start: 8,
                end: 55,
                level: 0,
                want: vec![8, 9, 27, 54, 55],
            },
            TestCase {
                index: vec![
                    vec![
                        0b10000000_00000000_00000000_00000000_00001000_00000000_00000011_10000000,
                        0b00000001_11000001_00000000_00000011_00000000_00000000_00000000_00000001,
                    ],
                ],
                start: 8,
                end: 119,
                level: 0,
                want: vec![8, 9, 27, 63, 64, 96, 97, 112, 118, 119],
            },
        ];
        for t in test_cases {
            let mut cp = vec![Vec::new(); t.level + 1];
            generate_colon_positions(&t.index, t.start, t.end, t.level, &mut cp);
            assert_eq!(t.want, cp[0]);
        }
    }
}
<|endoftext|>


<commit_before>//! A parser for lambda expressions with
//! [De Bruijn indices](https://en.wikipedia.org/wiki/De_Bruijn_index)

use term::*;
use term::Term::*;
use term::Notation::*;
use self::Token::*;
use self::CToken::*;
use self::Error::*;
use self::Expression::*;

/// A type to represent a parsing error.
#[derive(Debug, PartialEq)]
pub enum Error {
    InvalidCharacter((usize, char)),
    InvalidExpression,
    EmptyExpression
}

#[derive(Debug, PartialEq)]
enum Token {
    Lambda,
    Lparen,
    Rparen,
    Number(usize)
}

#[derive(Debug, PartialEq)]
enum CToken {
    CLambda(String),
    CLparen,
    CRparen,
    CName(String)
}

fn tokenize(input: &str) -> Result<Vec<Token>, Error> {
    let mut chars = input.chars();
    let mut tokens = Vec::new();
    let mut position = 0;

    while let Some(c) = chars.next() {
        match c {
     '\\' | 'λ' => { tokens.push(Lambda) },
            '(' => { tokens.push(Lparen) },
            ')' => { tokens.push(Rparen) },
             x  => {
                if let Some(n) = x.to_digit(16) {
                    tokens.push(Number(n as usize))
                } else if x.is_whitespace() {
                    ()
                } else {
                    return Err(InvalidCharacter((position, x)))
                }
            }
        }
        position += if c == 'λ' { 2 } else { 1 };
    }

    Ok(tokens)
}

fn tokenize_classic(input: &str) -> Result<Vec<CToken>, Error> {
    let mut chars = input.chars().peekable();
    let mut tokens = Vec::new();
    let mut position = 0;

    while let Some(c) = chars.next() {
        match c {
            '\\' | 'λ' => {
                let mut name = String::new();
                while let Some(c) = chars.next() {
                    if c == '.' {
                        break
                    } else if "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".contains(c) {
                        name.push(c)
                    } else {
                        return Err(InvalidCharacter((position, c)))
                    }
                    position += 1;
                }
                tokens.push(CLambda(name))
            },
            '(' => { tokens.push(CLparen) },
            ')' => { tokens.push(CRparen) },
             x  => {
                if x.is_whitespace() {
                    ()
                } else if "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".contains(x) {
                    let mut name = x.to_string();
                    while let Some(&c) = chars.peek() {
                        if c.is_whitespace() || c == '(' || c == ')' {
                            break
                        } else {
                            name.push(c);
                            chars.next();
                        }
                        position += 1;
                    }
                    tokens.push(CName(name))
                } else {
                    return Err(InvalidCharacter((position, x)))
                }
            }
        }
        position += if c == 'λ' { 2 } else { 1 };
    }

    Ok(tokens)
}

fn convert_classic_tokens(tokens: &[CToken]) -> Vec<Token> {
    let mut stack = Vec::new();
    let mut pos = 0;

    _convert_classic_tokens(tokens, &mut stack, &mut pos)
}

fn _convert_classic_tokens(tokens: &[CToken], stack: &mut Vec<String>, pos: &mut usize) -> Vec<Token>
{
    let mut output = Vec::new();
    let mut inner_stack_count = 0;

    while *pos < tokens.len() {
        match tokens[*pos] {
            CLambda(ref name) => {
                output.push(Lambda);
                stack.push(name.clone());
                inner_stack_count += 1;
            },
            CLparen => {
                output.push(Lparen);
                *pos += 1;
                output.append(&mut _convert_classic_tokens(tokens, stack, pos));
            },
            CRparen => {
                output.push(Rparen);
                for _ in 0..inner_stack_count { stack.pop(); }
                return output
            },
            CName(ref name) => {
                let number = if stack.contains(name) {
                    stack.iter().rev().position(|t| t == name).unwrap() + 1
                } else {
                    stack.len() + 1
                };
                output.push(Number(number));
            }
        }
        *pos += 1;
    }

    output
}

#[derive(Debug, PartialEq)]
enum Expression {
    Abstraction,
    Sequence(Vec<Expression>),
    Variable(usize)
}

fn _get_ast(tokens: &[Token], pos: &mut usize) -> Result<Expression, Error> {
    let mut expr = Vec::new();

    if tokens.is_empty() { return Err(EmptyExpression) }

    while *pos < tokens.len() {
        match tokens[*pos] {
            Lambda => {
                expr.push(Abstraction)
            },
            Number(i) => {
                expr.push(Variable(i))
            },
            Lparen => {
                *pos += 1;
                let subtree = try!(_get_ast(&tokens, pos));
                expr.push(subtree);
            },
            Rparen => {
                return Ok(Sequence(expr))
            }
        }
        *pos += 1;
    }

    Ok(Sequence(expr))
}

fn get_ast(tokens: &[Token]) -> Result<Expression, Error> {
    let mut pos = 0;

    _get_ast(tokens, &mut pos)
}

/// Parses the input `&str` as a lambda `Term`. The lambdas can be represented either with the
/// greek letter (λ) or a backslash (\\ - less aesthetic, but only one byte in size) and whitespaces
///  are ignored. The indices in the `DeBruijn` notation mode start with 1 and are hexadecimal
/// digits.
///
/// # Example
/// ```
/// use lambda_calculus::parser::parse;
/// use lambda_calculus::term::Notation::*;
/// use lambda_calculus::arithmetic::{succ, pred};
///
/// assert_eq!(parse(&"λ λ λ 2 (3 2 1)", DeBruijn), Ok(succ()));
/// assert_eq!(parse(&r#"\ \ \ 2 (3 2 1)"#, DeBruijn), Ok(succ()));
/// assert_eq!(parse(&"λn.λf.λx.n (λg.λh.h (g f)) (λu.x) (λu.u)", Classic), Ok(pred()));
/// ```
pub fn parse(input: &str, notation: Notation) -> Result<Term, Error> {
    let tokens = if notation == DeBruijn {
        try!(tokenize(input))
    } else {
        convert_classic_tokens(&try!(tokenize_classic(input)))
    };
    let ast = try!(get_ast(&tokens));

    let exprs = try!(if let Sequence(exprs) = ast { Ok(exprs) } else { Err(InvalidExpression) });

    let mut stack = Vec::new();
    let mut output = Vec::new();
    let term = fold_exprs(&exprs, &mut stack, &mut output);

    term
}

fn fold_exprs(exprs: &[Expression], stack: &mut Vec<Expression>, output: &mut Vec<Term>)
    -> Result<Term, Error>
{
    let mut iter = exprs.into_iter();

    while let Some(ref expr) = iter.next() {
        match **expr {
            Variable(i) => output.push(Var(i)),
            Abstraction => stack.push(Abstraction),
            Sequence(ref exprs) => {
                let mut stack2 = Vec::new();
                let mut output2 = Vec::new();
                let subexpr = try!(fold_exprs(&exprs, &mut stack2, &mut output2));
                output.push(subexpr)
            }
        }
    }

    let mut ret = try!(fold_terms(output.drain(..).collect()));

    while let Some(Abstraction) = stack.pop() {
        ret = abs(ret);
    }

    Ok(ret)
}

fn fold_terms(mut terms: Vec<Term>) -> Result<Term, Error> {
    if terms.len() > 1 {
        let fst = terms.remove(0);
        Ok( terms.into_iter().fold(fst, |acc, t| app(acc, t)) )
    } else if terms.len() == 1 {
        Ok( terms.pop().unwrap() )
    } else {
        Err(EmptyExpression)
    }
}

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

    #[test]
    fn tokenization_error() {
        assert_eq!(tokenize(&"λλx2"), Err(InvalidCharacter((4, 'x'))))
    }

    #[test]
    fn tokenization_success() {
        let quine = "λ 1 ( (λ 1 1) (λ λ λ λ λ 1 4 (3 (5 5) 2) ) ) 1";
        let tokens = tokenize(&quine);

        assert!(tokens.is_ok());
        assert_eq!(tokens.unwrap(), vec![Lambda, Number(1), Lparen, Lparen, Lambda, Number(1),
            Number(1), Rparen, Lparen, Lambda, Lambda, Lambda, Lambda, Lambda, Number(1),
            Number(4), Lparen, Number(3), Lparen, Number(5), Number(5), Rparen, Number(2),
            Rparen, Rparen, Rparen, Number(1)]);
    }

    #[test]
    fn tokenization_success_classic() {
        let blc_dbr = "(λ11)(λλλ1(λλλλ3(λ5(3(λ2(3(λλ3(λ123)))(4(λ4(λ31(21))))))(1(2(λ12))\
            (λ4(λ4(λ2(14)))5))))(33)2)(λ1((λ11)(λ11)))";
        let blc_cla = format!("{}", parse(&blc_dbr, DeBruijn).unwrap());

        let tokens_cla = tokenize_classic(&blc_cla);
        let tokens_dbr = tokenize(&blc_dbr);

        assert!(tokens_cla.is_ok());
        assert!(tokens_dbr.is_ok());

        assert_eq!(convert_classic_tokens(&tokens_cla.unwrap()), tokens_dbr.unwrap());
    }

    #[test]
    fn alternative_lambda_parsing() {
        assert_eq!(parse(&"\\\\\\2(321)", DeBruijn), parse(&"λλλ2(321)", DeBruijn))
    }

    #[test]
    fn succ_ast() {
        let tokens = tokenize(&"λλλ2(321)").unwrap();
        let ast = get_ast(&tokens);

        assert_eq!(ast,
            Ok(Sequence(vec![
                Abstraction,
                Abstraction,
                Abstraction,
                Variable(2),
                Sequence(vec![
                    Variable(3),
                    Variable(2),
                    Variable(1)
                ])
            ])
        ));
    }

    #[test]
    fn parse_y() {
        let y = "λ(λ2(11))(λ2(11))";
        assert_eq!(parse(&y, DeBruijn).expect("parsing Y failed!"),
            abs(
                app(
                    abs(
                        app(
                            Var(2),
                            app(Var(1), Var(1))
                        )
                    ),
                    abs(
                        app(
                            Var(2),
                            app(Var(1), Var(1))
                        )
                    )
                )
            )
        );
    }

    #[test]
    fn parse_quine() {
        let quine = "λ1((λ11)(λλλλλ14(3(55)2)))1";
        assert_eq!(parse(&quine, DeBruijn).expect("parsing QUINE failed!"),
            abs(app(app(Var(1), app(abs(app(Var(1), Var(1))), abs(abs(abs(abs(abs(app(app(Var(1),
            Var(4)), app(app(Var(3), app(Var(5), Var(5))), Var(2)))))))))), Var(1)))
        );
    }

    #[test]
    fn parse_blc() {
        let blc = "(λ11)(λλλ1(λλλλ3(λ5(3(λ2(3(λλ3(λ123)))(4(λ4(λ31(21))))))(1(2(λ12))\
                   (λ4(λ4(λ2(14)))5))))(33)2)(λ1((λ11)(λ11)))";
        assert_eq!(parse(&blc, DeBruijn).expect("parsing BLC failed!"),
            app(app(abs(app(Var(1), Var(1))), abs(abs(abs(app(app(app(Var(1),
            abs(abs(abs(abs(app(Var(3), abs(app(app(Var(5), app(Var(3), abs(app(app(Var(2),
            app(Var(3), abs(abs(app(Var(3), abs(app(app(Var(1), Var(2)), Var(3)))))))), app(Var(4),
            abs(app(Var(4), abs(app(app(Var(3), Var(1)), app(Var(2), Var(1))))))))))),
            app(app(Var(1), app(Var(2), abs(app(Var(1), Var(2))))), abs(app(app(Var(4),
            abs(app(Var(4), abs(app(Var(2), app(Var(1), Var(4))))))), Var(5)))))))))))),
            app(Var(3), Var(3))), Var(2)))))), abs(app(Var(1), app(abs(app(Var(1), Var(1))),
            abs(app(Var(1), Var(1)))))))
        );
    }
}
<commit_msg>improve parser docs<commit_after>//! A parser for lambda expressions

use term::*;
use term::Term::*;
use term::Notation::*;
use self::Token::*;
use self::CToken::*;
use self::Error::*;
use self::Expression::*;

/// A type to represent a parsing error.
#[derive(Debug, PartialEq)]
pub enum Error {
    InvalidCharacter((usize, char)),
    InvalidExpression,
    EmptyExpression
}

#[derive(Debug, PartialEq)]
enum Token {
    Lambda,
    Lparen,
    Rparen,
    Number(usize)
}

#[derive(Debug, PartialEq)]
enum CToken {
    CLambda(String),
    CLparen,
    CRparen,
    CName(String)
}

fn tokenize(input: &str) -> Result<Vec<Token>, Error> {
    let mut chars = input.chars();
    let mut tokens = Vec::new();
    let mut position = 0;

    while let Some(c) = chars.next() {
        match c {
     '\\' | 'λ' => { tokens.push(Lambda) },
            '(' => { tokens.push(Lparen) },
            ')' => { tokens.push(Rparen) },
             x  => {
                if let Some(n) = x.to_digit(16) {
                    tokens.push(Number(n as usize))
                } else if x.is_whitespace() {
                    ()
                } else {
                    return Err(InvalidCharacter((position, x)))
                }
            }
        }
        position += if c == 'λ' { 2 } else { 1 };
    }

    Ok(tokens)
}

fn tokenize_classic(input: &str) -> Result<Vec<CToken>, Error> {
    let mut chars = input.chars().peekable();
    let mut tokens = Vec::new();
    let mut position = 0;

    while let Some(c) = chars.next() {
        match c {
            '\\' | 'λ' => {
                let mut name = String::new();
                while let Some(c) = chars.next() {
                    if c == '.' {
                        break
                    } else if "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".contains(c) {
                        name.push(c)
                    } else {
                        return Err(InvalidCharacter((position, c)))
                    }
                    position += 1;
                }
                tokens.push(CLambda(name))
            },
            '(' => { tokens.push(CLparen) },
            ')' => { tokens.push(CRparen) },
             x  => {
                if x.is_whitespace() {
                    ()
                } else if "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".contains(x) {
                    let mut name = x.to_string();
                    while let Some(&c) = chars.peek() {
                        if c.is_whitespace() || c == '(' || c == ')' {
                            break
                        } else {
                            name.push(c);
                            chars.next();
                        }
                        position += 1;
                    }
                    tokens.push(CName(name))
                } else {
                    return Err(InvalidCharacter((position, x)))
                }
            }
        }
        position += if c == 'λ' { 2 } else { 1 };
    }

    Ok(tokens)
}

fn convert_classic_tokens(tokens: &[CToken]) -> Vec<Token> {
    let mut stack = Vec::new();
    let mut pos = 0;

    _convert_classic_tokens(tokens, &mut stack, &mut pos)
}

fn _convert_classic_tokens(tokens: &[CToken], stack: &mut Vec<String>, pos: &mut usize) -> Vec<Token>
{
    let mut output = Vec::new();
    let mut inner_stack_count = 0;

    while *pos < tokens.len() {
        match tokens[*pos] {
            CLambda(ref name) => {
                output.push(Lambda);
                stack.push(name.clone());
                inner_stack_count += 1;
            },
            CLparen => {
                output.push(Lparen);
                *pos += 1;
                output.append(&mut _convert_classic_tokens(tokens, stack, pos));
            },
            CRparen => {
                output.push(Rparen);
                for _ in 0..inner_stack_count { stack.pop(); }
                return output
            },
            CName(ref name) => {
                let number = if stack.contains(name) {
                    stack.iter().rev().position(|t| t == name).unwrap() + 1
                } else {
                    stack.len() + 1
                };
                output.push(Number(number));
            }
        }
        *pos += 1;
    }

    output
}

#[derive(Debug, PartialEq)]
enum Expression {
    Abstraction,
    Sequence(Vec<Expression>),
    Variable(usize)
}

fn _get_ast(tokens: &[Token], pos: &mut usize) -> Result<Expression, Error> {
    let mut expr = Vec::new();

    if tokens.is_empty() { return Err(EmptyExpression) }

    while *pos < tokens.len() {
        match tokens[*pos] {
            Lambda => {
                expr.push(Abstraction)
            },
            Number(i) => {
                expr.push(Variable(i))
            },
            Lparen => {
                *pos += 1;
                let subtree = try!(_get_ast(&tokens, pos));
                expr.push(subtree);
            },
            Rparen => {
                return Ok(Sequence(expr))
            }
        }
        *pos += 1;
    }

    Ok(Sequence(expr))
}

fn get_ast(tokens: &[Token]) -> Result<Expression, Error> {
    let mut pos = 0;

    _get_ast(tokens, &mut pos)
}

/// Parses the input `&str` as a lambda `Term`.
///
/// - lambdas can be represented either with the greek letter (λ) or a backslash (\\ -
/// less aesthetic, but only one byte in size)
/// - `Classic` mode ignores whitespaces where unambiguous
/// - `DeBruijn` mode ignores all whitespaces (since indices > 15 are very unlikely)
/// - the indices in the `DeBruijn` notation mode start with 1 and are hexadecimal digits
///
/// # Example
/// ```
/// use lambda_calculus::parser::parse;
/// use lambda_calculus::term::Notation::*;
/// use lambda_calculus::arithmetic::{succ, pred};
///
/// assert_eq!(parse(&"λn. λf. λx. n (λg. λh. h (g f)) (λu. x) (λu. u)", Classic), Ok(pred()));
/// assert_eq!(parse(&"λn.λf.λx.n(λg.λh.h(g f))(λu.x)(λu.u)", Classic), Ok(pred()));
///
/// assert_eq!(parse(  &"λλλ2(321)",  DeBruijn), Ok(succ()));
/// assert_eq!(parse(&r#"\\\2(321)"#, DeBruijn), Ok(succ()));
/// ```
pub fn parse(input: &str, notation: Notation) -> Result<Term, Error> {
    let tokens = if notation == DeBruijn {
        try!(tokenize(input))
    } else {
        convert_classic_tokens(&try!(tokenize_classic(input)))
    };
    let ast = try!(get_ast(&tokens));

    let exprs = try!(if let Sequence(exprs) = ast { Ok(exprs) } else { Err(InvalidExpression) });

    let mut stack = Vec::new();
    let mut output = Vec::new();
    let term = fold_exprs(&exprs, &mut stack, &mut output);

    term
}

fn fold_exprs(exprs: &[Expression], stack: &mut Vec<Expression>, output: &mut Vec<Term>)
    -> Result<Term, Error>
{
    let mut iter = exprs.into_iter();

    while let Some(ref expr) = iter.next() {
        match **expr {
            Variable(i) => output.push(Var(i)),
            Abstraction => stack.push(Abstraction),
            Sequence(ref exprs) => {
                let mut stack2 = Vec::new();
                let mut output2 = Vec::new();
                let subexpr = try!(fold_exprs(&exprs, &mut stack2, &mut output2));
                output.push(subexpr)
            }
        }
    }

    let mut ret = try!(fold_terms(output.drain(..).collect()));

    while let Some(Abstraction) = stack.pop() {
        ret = abs(ret);
    }

    Ok(ret)
}

fn fold_terms(mut terms: Vec<Term>) -> Result<Term, Error> {
    if terms.len() > 1 {
        let fst = terms.remove(0);
        Ok( terms.into_iter().fold(fst, |acc, t| app(acc, t)) )
    } else if terms.len() == 1 {
        Ok( terms.pop().unwrap() )
    } else {
        Err(EmptyExpression)
    }
}

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

    #[test]
    fn tokenization_error() {
        assert_eq!(tokenize(&"λλx2"), Err(InvalidCharacter((4, 'x'))))
    }

    #[test]
    fn tokenization_success() {
        let quine = "λ 1 ( (λ 1 1) (λ λ λ λ λ 1 4 (3 (5 5) 2) ) ) 1";
        let tokens = tokenize(&quine);

        assert!(tokens.is_ok());
        assert_eq!(tokens.unwrap(), vec![Lambda, Number(1), Lparen, Lparen, Lambda, Number(1),
            Number(1), Rparen, Lparen, Lambda, Lambda, Lambda, Lambda, Lambda, Number(1),
            Number(4), Lparen, Number(3), Lparen, Number(5), Number(5), Rparen, Number(2),
            Rparen, Rparen, Rparen, Number(1)]);
    }

    #[test]
    fn tokenization_success_classic() {
        let blc_dbr = "(λ11)(λλλ1(λλλλ3(λ5(3(λ2(3(λλ3(λ123)))(4(λ4(λ31(21))))))(1(2(λ12))\
            (λ4(λ4(λ2(14)))5))))(33)2)(λ1((λ11)(λ11)))";
        let blc_cla = format!("{}", parse(&blc_dbr, DeBruijn).unwrap());

        let tokens_cla = tokenize_classic(&blc_cla);
        let tokens_dbr = tokenize(&blc_dbr);

        assert!(tokens_cla.is_ok());
        assert!(tokens_dbr.is_ok());

        assert_eq!(convert_classic_tokens(&tokens_cla.unwrap()), tokens_dbr.unwrap());
    }

    #[test]
    fn alternative_lambda_parsing() {
        assert_eq!(parse(&"\\\\\\2(321)", DeBruijn), parse(&"λλλ2(321)", DeBruijn))
    }

    #[test]
    fn succ_ast() {
        let tokens = tokenize(&"λλλ2(321)").unwrap();
        let ast = get_ast(&tokens);

        assert_eq!(ast,
            Ok(Sequence(vec![
                Abstraction,
                Abstraction,
                Abstraction,
                Variable(2),
                Sequence(vec![
                    Variable(3),
                    Variable(2),
                    Variable(1)
                ])
            ])
        ));
    }

    #[test]
    fn parse_y() {
        let y = "λ(λ2(11))(λ2(11))";
        assert_eq!(parse(&y, DeBruijn).expect("parsing Y failed!"),
            abs(
                app(
                    abs(
                        app(
                            Var(2),
                            app(Var(1), Var(1))
                        )
                    ),
                    abs(
                        app(
                            Var(2),
                            app(Var(1), Var(1))
                        )
                    )
                )
            )
        );
    }

    #[test]
    fn parse_quine() {
        let quine = "λ1((λ11)(λλλλλ14(3(55)2)))1";
        assert_eq!(parse(&quine, DeBruijn).expect("parsing QUINE failed!"),
            abs(app(app(Var(1), app(abs(app(Var(1), Var(1))), abs(abs(abs(abs(abs(app(app(Var(1),
            Var(4)), app(app(Var(3), app(Var(5), Var(5))), Var(2)))))))))), Var(1)))
        );
    }

    #[test]
    fn parse_blc() {
        let blc = "(λ11)(λλλ1(λλλλ3(λ5(3(λ2(3(λλ3(λ123)))(4(λ4(λ31(21))))))(1(2(λ12))\
                   (λ4(λ4(λ2(14)))5))))(33)2)(λ1((λ11)(λ11)))";
        assert_eq!(parse(&blc, DeBruijn).expect("parsing BLC failed!"),
            app(app(abs(app(Var(1), Var(1))), abs(abs(abs(app(app(app(Var(1),
            abs(abs(abs(abs(app(Var(3), abs(app(app(Var(5), app(Var(3), abs(app(app(Var(2),
            app(Var(3), abs(abs(app(Var(3), abs(app(app(Var(1), Var(2)), Var(3)))))))), app(Var(4),
            abs(app(Var(4), abs(app(app(Var(3), Var(1)), app(Var(2), Var(1))))))))))),
            app(app(Var(1), app(Var(2), abs(app(Var(1), Var(2))))), abs(app(app(Var(4),
            abs(app(Var(4), abs(app(Var(2), app(Var(1), Var(4))))))), Var(5)))))))))))),
            app(Var(3), Var(3))), Var(2)))))), abs(app(Var(1), app(abs(app(Var(1), Var(1))),
            abs(app(Var(1), Var(1)))))))
        );
    }
}
<|endoftext|>


<commit_before>#[derive(Debug,PartialEq,Eq)]
pub struct PosixHeader<'a> {
    pub name:     & 'a str,
    pub mode:     & 'a str,
    pub uid:      & 'a str,
    pub gid:      & 'a str,
    pub size:     & 'a str,
    pub mtime:    & 'a str,
    pub chksum:   & 'a str,
    pub typeflag: char,
    pub linkname: & 'a str,
    pub ustar:    Option<UStarHeader<'a>>
}

#[derive(Debug,PartialEq,Eq)]
pub struct UStarHeader<'a> {
    pub magic:    & 'a str,
    pub version:  & 'a str,
    pub uname:    & 'a str,
    pub gname:    & 'a str,
    pub devmajor: & 'a str,
    pub devminor: & 'a str,
    pub prefix:   & 'a str,
}

#[derive(Debug,PartialEq,Eq)]
pub struct TarEntry<'a> {
    pub header:   PosixHeader<'a>,
    pub contents: & 'a str
}
<commit_msg>start parsing headers<commit_after>use std::str::from_utf8;
use nom::IResult;

#[derive(Debug,PartialEq,Eq)]
pub struct PosixHeader<'a> {
    pub name:     & 'a str,
    pub mode:     & 'a str,
    pub uid:      & 'a str,
    pub gid:      & 'a str,
    pub size:     & 'a str,
    pub mtime:    & 'a str,
    pub chksum:   & 'a str,
    pub typeflag: char,
    pub linkname: & 'a str,
    pub ustar:    Option<UStarHeader<'a>>
}

#[derive(Debug,PartialEq,Eq)]
pub struct UStarHeader<'a> {
    pub magic:    & 'a str,
    pub version:  & 'a str,
    pub uname:    & 'a str,
    pub gname:    & 'a str,
    pub devmajor: & 'a str,
    pub devminor: & 'a str,
    pub prefix:   & 'a str,
}

#[derive(Debug,PartialEq,Eq)]
pub struct TarEntry<'a> {
    pub header:   PosixHeader<'a>,
    pub contents: & 'a str
}

pub fn parse_header(i: &[u8]) -> IResult<&[u8], PosixHeader> {
    chain!(i,
        name:     map_res!(take!(100), from_utf8) ~
        mode:     map_res!(take!(8),   from_utf8) ~
        uid:      map_res!(take!(8),   from_utf8) ~
        gid:      map_res!(take!(8),   from_utf8) ~
        size:     map_res!(take!(12),  from_utf8) ~
        mtime:    map_res!(take!(12),  from_utf8) ~
        chksum:   map_res!(take!(8),   from_utf8) ~
        /* TODO: typeflag */
        linkname: map_res!(take!(100), from_utf8),
        /* TODO: ustar */
        ||{
            PosixHeader {
                name:     name,
                mode:     mode,
                uid:      uid,
                gid:      gid,
                size:     size,
                mtime:    mtime,
                chksum:   chksum,
                typeflag: '1',
                linkname: linkname,
                ustar:    None
            }
        }
    )
}

pub fn parse_entry(i: &[u8]) -> IResult<&[u8], TarEntry> {
    chain!(i,
        header: parse_header,
        /* TODO: contents */
        ||{
            TarEntry {
                header: header,
                contents: ""
            }
        }
    )
}

pub fn parse_tar(i: &[u8]) -> IResult<&[u8], Vec<TarEntry>> {
    many0!(i, parse_entry)
}
<|endoftext|>


<commit_before>//! The parser functionality

use crate::bindings::*;
use crate::c_helpers::*;
use crate::tree::*;

use std::convert::AsRef;
use std::error::Error;
use std::ffi::c_void;
use std::ffi::{CStr, CString};
use std::fmt;
use std::fs;
use std::io;
use std::os::raw::{c_char, c_int};
use std::ptr;
use std::slice;
use std::str;

enum XmlParserOption {
  Recover = 1,
  Nodefdtd = 4,
  Noerror = 32,
  Nowarning = 64,
  Pedantic = 128,
  Noblanks = 256,
  Nonet = 2048,
  Noimplied = 8192,
  Compact = 65_536,
  Ignoreenc = 2_097_152,
}

enum HtmlParserOption {
  Recover = 1,
  Nodefdtd = 4,
  Noerror = 32,
  Nowarning = 64,
  Pedantic = 128,
  Noblanks = 256,
  Nonet = 2048,
  Noimplied = 8192,
  Compact = 65_536,
  Ignoreenc = 2_097_152,
}

/// Parser Options
pub struct ParserOptions<'a> {
  /// Relaxed parsing
  pub recover: bool,
  /// do not default a doctype if not found
  pub no_def_dtd: bool,
  /// do not default a doctype if not found
  pub no_error: bool,
  /// suppress warning reports
  pub no_warning: bool,
  /// pedantic error reporting
  pub pedantic: bool,
  /// remove blank nodes
  pub no_blanks: bool,
  /// Forbid network access
  pub no_net: bool,
  /// Do not add implied html/body... elements
  pub no_implied: bool,
  /// compact small text nodes
  pub compact: bool,
  /// ignore internal document encoding hint
  pub ignore_enc: bool,
  /// manually-specified encoding
  pub encoding: Option<&'a str>,
}

impl<'a> ParserOptions<'a> {
  pub(crate) fn to_flags(&self, format: &ParseFormat) -> i32 {

    macro_rules! to_option_flag {
      (
        $condition:expr => $variant:ident
      ) => {
          {
            if $condition {
              match format {
                ParseFormat::HTML => HtmlParserOption::$variant as i32,
                ParseFormat::XML => XmlParserOption::$variant as i32,
              }
            } else {
              0
            }
          }
      };
    }

    let flags = 0;
    let flags = flags + to_option_flag!(self.recover => Recover);
    let flags = flags + to_option_flag!(self.no_def_dtd => Nodefdtd);
    let flags = flags + to_option_flag!(self.no_error => Noerror);
    let flags = flags + to_option_flag!(self.no_warning => Nowarning);
    let flags = flags + to_option_flag!(self.no_warning => Nowarning);
    let flags = flags + to_option_flag!(self.pedantic => Pedantic);
    let flags = flags + to_option_flag!(self.no_blanks => Noblanks);
    let flags = flags + to_option_flag!(self.no_net => Nonet);
    let flags = flags + to_option_flag!(self.no_implied => Noimplied);
    let flags = flags + to_option_flag!(self.compact => Compact);
    let flags = flags + to_option_flag!(self.ignore_enc => Ignoreenc);
    flags
  }
}

impl<'a> Default for ParserOptions<'a> {
    fn default() -> Self {
        ParserOptions {
          recover: true,
          no_def_dtd: false,
          no_error: true,
          no_warning: true,
          pedantic: false,
          no_blanks: false,
          no_net: false,
          no_implied: false,
          compact: false,
          ignore_enc: false,
          encoding: None,
        }
    }
}

///Parser Errors
pub enum XmlParseError {
  ///Parsing returned a null pointer as document pointer
  GotNullPointer,
  ///Could not open file error.
  FileOpenError,
  ///Document too large for libxml2.
  DocumentTooLarge,
}

impl Error for XmlParseError {}

impl fmt::Debug for XmlParseError
{
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    write!(f, "{}", self)
  }
}

impl fmt::Display for XmlParseError
{
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    write!(f, "{}", match self {
      XmlParseError::GotNullPointer   => "Got a Null pointer",
      XmlParseError::FileOpenError    => "Unable to open path to file.",
      XmlParseError::DocumentTooLarge => "Document too large for i32.",
    })
  }
}

/// Default encoding when not provided.
const DEFAULT_ENCODING: *const c_char = ptr::null();

/// Default URL when not provided.
const DEFAULT_URL: *const c_char = ptr::null();

/// Open file function.
fn xml_open(filename: &str) -> io::Result<*mut c_void> {
  let ptr = Box::into_raw(Box::new(fs::File::open(filename)?));
  Ok(ptr as *mut c_void)
}

/// Read callback for an FS file.
unsafe extern "C" fn xml_read(context: *mut c_void, buffer: *mut c_char, len: c_int) -> c_int {
  // Len is always positive, typically 40-4000 bytes.
  let file = context as *mut fs::File;
  let buf = slice::from_raw_parts_mut(buffer as *mut u8, len as usize);
  match io::Read::read(&mut *file, buf) {
    Ok(v) => v as c_int,
    Err(_) => -1,
  }
}

type XmlReadCallback = unsafe extern "C" fn(*mut c_void, *mut c_char, c_int) -> c_int;

/// Close callback for an FS file.
unsafe extern "C" fn xml_close(context: *mut c_void) -> c_int {
  // Take rust ownership of the context and then drop it.
  let file = context as *mut fs::File;
  let _ = Box::from_raw(file);
  0
}

type XmlCloseCallback = unsafe extern "C" fn(*mut c_void) -> c_int;

///Convert usize to i32 safely.
fn try_usize_to_i32(value: usize) -> Result<i32, XmlParseError> {
  if cfg!(target_pointer_width = "16") || (value < i32::max_value() as usize) {
    // Cannot safely use our value comparison, but the conversion if always safe.
    // Or, if the value can be safely represented as a 32-bit signed integer.
    Ok(value as i32)
  } else {
    // Document too large, cannot parse using libxml2.
    Err(XmlParseError::DocumentTooLarge)
  }
}

#[derive(PartialEq)]
/// Enum for the parse formats supported by libxml2
pub enum ParseFormat {
  /// Strict parsing for XML
  XML,
  /// Relaxed parsing for HTML
  HTML,
}
/// Parsing API wrapper for libxml2
pub struct Parser {
  /// The `ParseFormat` for this parser
  pub format: ParseFormat,
}
impl Default for Parser {
  /// Create a parser for XML documents
  fn default() -> Self {
    Parser {
      format: ParseFormat::XML,
    }
  }
}
impl Parser {
  /// Create a parser for HTML documents
  pub fn default_html() -> Self {
    Parser {
      format: ParseFormat::HTML,
    }
  }

  /// Parses the XML/HTML file `filename` to generate a new `Document`
  pub fn parse_file(&self, filename: &str) -> Result<Document, XmlParseError> {
    self.parse_file_with_options(filename, ParserOptions::default())
  }

  /// Parses the XML/HTML file `filename` with a manually-specified parser-options
  /// to generate a new `Document`
  pub fn parse_file_with_options(
    &self,
    filename: &str,
    parser_options: ParserOptions,
  ) -> Result<Document, XmlParseError> {
    // Create extern C callbacks for to read and close a Rust file through
    // a void pointer.
    let ioread: Option<XmlReadCallback> = Some(xml_read);
    let ioclose: Option<XmlCloseCallback> = Some(xml_close);
    let ioctx = match xml_open(filename) {
      Ok(v) => v,
      Err(_) => return Err(XmlParseError::FileOpenError),
    };

    // Process encoding.
    let encoding_cstring: Option<CString> = parser_options.encoding.map(|v| CString::new(v).unwrap());
    let encoding_ptr = match encoding_cstring {
      Some(v) => v.as_ptr(),
      None => DEFAULT_ENCODING,
    };

    // Process url.
    let url_ptr = DEFAULT_URL;

    unsafe {
      xmlKeepBlanksDefault(1);
    }

    let options = parser_options.to_flags(&self.format);

    match self.format {
      ParseFormat::XML => {
        unsafe {
          let doc_ptr = xmlReadIO(ioread, ioclose, ioctx, url_ptr, encoding_ptr, options);
          if doc_ptr.is_null() {
            Err(XmlParseError::GotNullPointer)
          } else {
            Ok(Document::new_ptr(doc_ptr))
          }
        }
      }
      ParseFormat::HTML => {
        unsafe {
          let doc_ptr = htmlReadIO(ioread, ioclose, ioctx, url_ptr, encoding_ptr, options);
          if doc_ptr.is_null() {
            Err(XmlParseError::GotNullPointer)
          } else {
            Ok(Document::new_ptr(doc_ptr))
          }
        }
      }
    }
  }

  ///Parses the XML/HTML bytes `input` to generate a new `Document`
  pub fn parse_string<Bytes: AsRef<[u8]>>(&self, input: Bytes) -> Result<Document, XmlParseError> {
    self.parse_string_with_options(input, ParserOptions::default())
  }

  ///Parses the XML/HTML bytes `input` with a manually-specified
  ///parser-options to generate a new `Document`
  pub fn parse_string_with_options<Bytes: AsRef<[u8]>>(
    &self,
    input: Bytes,
    parser_options: ParserOptions,
  ) -> Result<Document, XmlParseError> {
    // Process input bytes.
    let input_bytes = input.as_ref();
    let input_ptr = input_bytes.as_ptr() as *const c_char;
    let input_len = try_usize_to_i32(input_bytes.len())?;

    // Process encoding.
    let encoding_cstring: Option<CString> = parser_options.encoding.map(|v| CString::new(v).unwrap());
    let encoding_ptr = match encoding_cstring {
      Some(v) => v.as_ptr(),
      None => DEFAULT_ENCODING,
    };

    // Process url.
    let url_ptr = DEFAULT_URL;

    let options = parser_options.to_flags(&self.format);

    match self.format {
      ParseFormat::XML => unsafe {
        let docptr = xmlReadMemory(input_ptr, input_len, url_ptr, encoding_ptr, options);
        if docptr.is_null() {
          Err(XmlParseError::GotNullPointer)
        } else {
          Ok(Document::new_ptr(docptr))
        }
      },
      ParseFormat::HTML => unsafe {
        let docptr = htmlReadMemory(input_ptr, input_len, url_ptr, encoding_ptr, options);
        if docptr.is_null() {
          Err(XmlParseError::GotNullPointer)
        } else {
          Ok(Document::new_ptr(docptr))
        }
      },
    }
  }

  /// Checks a string for well-formedness.
  pub fn is_well_formed_html<Bytes: AsRef<[u8]>>(&self, input: Bytes) -> bool {
    self.is_well_formed_html_with_encoding(input, None)
  }

  /// Checks a string for well-formedness with manually-specified encoding.
  /// IMPORTANT: This function is currently implemented in a HACKY way, to ignore invalid errors for HTML5 elements (such as <math>)
  ///            this means you should NEVER USE IT WHILE THREADING, it is CERTAIN TO BREAK
  ///
  /// Help is welcome in implementing it correctly.
  pub fn is_well_formed_html_with_encoding<Bytes: AsRef<[u8]>>(
    &self,
    input: Bytes,
    encoding: Option<&str>,
  ) -> bool {
    // Process input string.
    let input_bytes = input.as_ref();
    if input_bytes.is_empty() {
      return false;
    }
    let input_ptr = input_bytes.as_ptr() as *const c_char;
    let input_len = match try_usize_to_i32(input_bytes.len()) {
      Ok(v) => v,
      Err(_) => return false,
    };

    // Process encoding.
    let encoding_cstring: Option<CString> = encoding.map(|v| CString::new(v).unwrap());
    let encoding_ptr = match encoding_cstring {
      Some(v) => v.as_ptr(),
      None => DEFAULT_ENCODING,
    };

    // Process url.
    let url_ptr = DEFAULT_URL;
    // disable generic error lines from libxml2
    match self.format {
      ParseFormat::XML => false, // TODO: Add support for XML at some point
      ParseFormat::HTML => unsafe {
        let ctxt = htmlNewParserCtxt();
        setWellFormednessHandler(ctxt);
        let docptr = htmlCtxtReadMemory(ctxt, input_ptr, input_len, url_ptr, encoding_ptr, 10_596); // htmlParserOption = 4+32+64+256+2048+8192
        let well_formed_final = if htmlWellFormed(ctxt) {
          // Basic well-formedness passes, let's check if we have an <html> element as root too
          if !docptr.is_null() {
            let node_ptr = xmlDocGetRootElement(docptr);
            let name_ptr = xmlNodeGetName(node_ptr);
            if name_ptr.is_null() {
              false
            }
            //empty string
            else {
              let c_root_name = CStr::from_ptr(name_ptr);
              let root_name = str::from_utf8(c_root_name.to_bytes()).unwrap().to_owned();
              root_name == "html"
            }
          } else {
            false
          }
        } else {
          false
        };

        if !ctxt.is_null() {
          htmlFreeParserCtxt(ctxt);
        }
        if !docptr.is_null() {
          xmlFreeDoc(docptr);
        }
        well_formed_final
      },
    }
  }
}
<commit_msg>remove unnecessary curly-braces<commit_after>//! The parser functionality

use crate::bindings::*;
use crate::c_helpers::*;
use crate::tree::*;

use std::convert::AsRef;
use std::error::Error;
use std::ffi::c_void;
use std::ffi::{CStr, CString};
use std::fmt;
use std::fs;
use std::io;
use std::os::raw::{c_char, c_int};
use std::ptr;
use std::slice;
use std::str;

enum XmlParserOption {
  Recover = 1,
  Nodefdtd = 4,
  Noerror = 32,
  Nowarning = 64,
  Pedantic = 128,
  Noblanks = 256,
  Nonet = 2048,
  Noimplied = 8192,
  Compact = 65_536,
  Ignoreenc = 2_097_152,
}

enum HtmlParserOption {
  Recover = 1,
  Nodefdtd = 4,
  Noerror = 32,
  Nowarning = 64,
  Pedantic = 128,
  Noblanks = 256,
  Nonet = 2048,
  Noimplied = 8192,
  Compact = 65_536,
  Ignoreenc = 2_097_152,
}

/// Parser Options
pub struct ParserOptions<'a> {
  /// Relaxed parsing
  pub recover: bool,
  /// do not default a doctype if not found
  pub no_def_dtd: bool,
  /// do not default a doctype if not found
  pub no_error: bool,
  /// suppress warning reports
  pub no_warning: bool,
  /// pedantic error reporting
  pub pedantic: bool,
  /// remove blank nodes
  pub no_blanks: bool,
  /// Forbid network access
  pub no_net: bool,
  /// Do not add implied html/body... elements
  pub no_implied: bool,
  /// compact small text nodes
  pub compact: bool,
  /// ignore internal document encoding hint
  pub ignore_enc: bool,
  /// manually-specified encoding
  pub encoding: Option<&'a str>,
}

impl<'a> ParserOptions<'a> {
  pub(crate) fn to_flags(&self, format: &ParseFormat) -> i32 {

    macro_rules! to_option_flag {
      (
        $condition:expr => $variant:ident
      ) => {
        if $condition {
          match format {
            ParseFormat::HTML => HtmlParserOption::$variant as i32,
            ParseFormat::XML => XmlParserOption::$variant as i32,
          }
        } else {
          0
        }
      };
    }

    let flags = 0;
    let flags = flags + to_option_flag!(self.recover => Recover);
    let flags = flags + to_option_flag!(self.no_def_dtd => Nodefdtd);
    let flags = flags + to_option_flag!(self.no_error => Noerror);
    let flags = flags + to_option_flag!(self.no_warning => Nowarning);
    let flags = flags + to_option_flag!(self.no_warning => Nowarning);
    let flags = flags + to_option_flag!(self.pedantic => Pedantic);
    let flags = flags + to_option_flag!(self.no_blanks => Noblanks);
    let flags = flags + to_option_flag!(self.no_net => Nonet);
    let flags = flags + to_option_flag!(self.no_implied => Noimplied);
    let flags = flags + to_option_flag!(self.compact => Compact);
    let flags = flags + to_option_flag!(self.ignore_enc => Ignoreenc);
    flags
  }
}

impl<'a> Default for ParserOptions<'a> {
    fn default() -> Self {
        ParserOptions {
          recover: true,
          no_def_dtd: false,
          no_error: true,
          no_warning: true,
          pedantic: false,
          no_blanks: false,
          no_net: false,
          no_implied: false,
          compact: false,
          ignore_enc: false,
          encoding: None,
        }
    }
}

///Parser Errors
pub enum XmlParseError {
  ///Parsing returned a null pointer as document pointer
  GotNullPointer,
  ///Could not open file error.
  FileOpenError,
  ///Document too large for libxml2.
  DocumentTooLarge,
}

impl Error for XmlParseError {}

impl fmt::Debug for XmlParseError
{
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    write!(f, "{}", self)
  }
}

impl fmt::Display for XmlParseError
{
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    write!(f, "{}", match self {
      XmlParseError::GotNullPointer   => "Got a Null pointer",
      XmlParseError::FileOpenError    => "Unable to open path to file.",
      XmlParseError::DocumentTooLarge => "Document too large for i32.",
    })
  }
}

/// Default encoding when not provided.
const DEFAULT_ENCODING: *const c_char = ptr::null();

/// Default URL when not provided.
const DEFAULT_URL: *const c_char = ptr::null();

/// Open file function.
fn xml_open(filename: &str) -> io::Result<*mut c_void> {
  let ptr = Box::into_raw(Box::new(fs::File::open(filename)?));
  Ok(ptr as *mut c_void)
}

/// Read callback for an FS file.
unsafe extern "C" fn xml_read(context: *mut c_void, buffer: *mut c_char, len: c_int) -> c_int {
  // Len is always positive, typically 40-4000 bytes.
  let file = context as *mut fs::File;
  let buf = slice::from_raw_parts_mut(buffer as *mut u8, len as usize);
  match io::Read::read(&mut *file, buf) {
    Ok(v) => v as c_int,
    Err(_) => -1,
  }
}

type XmlReadCallback = unsafe extern "C" fn(*mut c_void, *mut c_char, c_int) -> c_int;

/// Close callback for an FS file.
unsafe extern "C" fn xml_close(context: *mut c_void) -> c_int {
  // Take rust ownership of the context and then drop it.
  let file = context as *mut fs::File;
  let _ = Box::from_raw(file);
  0
}

type XmlCloseCallback = unsafe extern "C" fn(*mut c_void) -> c_int;

///Convert usize to i32 safely.
fn try_usize_to_i32(value: usize) -> Result<i32, XmlParseError> {
  if cfg!(target_pointer_width = "16") || (value < i32::max_value() as usize) {
    // Cannot safely use our value comparison, but the conversion if always safe.
    // Or, if the value can be safely represented as a 32-bit signed integer.
    Ok(value as i32)
  } else {
    // Document too large, cannot parse using libxml2.
    Err(XmlParseError::DocumentTooLarge)
  }
}

#[derive(PartialEq)]
/// Enum for the parse formats supported by libxml2
pub enum ParseFormat {
  /// Strict parsing for XML
  XML,
  /// Relaxed parsing for HTML
  HTML,
}
/// Parsing API wrapper for libxml2
pub struct Parser {
  /// The `ParseFormat` for this parser
  pub format: ParseFormat,
}
impl Default for Parser {
  /// Create a parser for XML documents
  fn default() -> Self {
    Parser {
      format: ParseFormat::XML,
    }
  }
}
impl Parser {
  /// Create a parser for HTML documents
  pub fn default_html() -> Self {
    Parser {
      format: ParseFormat::HTML,
    }
  }

  /// Parses the XML/HTML file `filename` to generate a new `Document`
  pub fn parse_file(&self, filename: &str) -> Result<Document, XmlParseError> {
    self.parse_file_with_options(filename, ParserOptions::default())
  }

  /// Parses the XML/HTML file `filename` with a manually-specified parser-options
  /// to generate a new `Document`
  pub fn parse_file_with_options(
    &self,
    filename: &str,
    parser_options: ParserOptions,
  ) -> Result<Document, XmlParseError> {
    // Create extern C callbacks for to read and close a Rust file through
    // a void pointer.
    let ioread: Option<XmlReadCallback> = Some(xml_read);
    let ioclose: Option<XmlCloseCallback> = Some(xml_close);
    let ioctx = match xml_open(filename) {
      Ok(v) => v,
      Err(_) => return Err(XmlParseError::FileOpenError),
    };

    // Process encoding.
    let encoding_cstring: Option<CString> = parser_options.encoding.map(|v| CString::new(v).unwrap());
    let encoding_ptr = match encoding_cstring {
      Some(v) => v.as_ptr(),
      None => DEFAULT_ENCODING,
    };

    // Process url.
    let url_ptr = DEFAULT_URL;

    unsafe {
      xmlKeepBlanksDefault(1);
    }

    let options = parser_options.to_flags(&self.format);

    match self.format {
      ParseFormat::XML => {
        unsafe {
          let doc_ptr = xmlReadIO(ioread, ioclose, ioctx, url_ptr, encoding_ptr, options);
          if doc_ptr.is_null() {
            Err(XmlParseError::GotNullPointer)
          } else {
            Ok(Document::new_ptr(doc_ptr))
          }
        }
      }
      ParseFormat::HTML => {
        unsafe {
          let doc_ptr = htmlReadIO(ioread, ioclose, ioctx, url_ptr, encoding_ptr, options);
          if doc_ptr.is_null() {
            Err(XmlParseError::GotNullPointer)
          } else {
            Ok(Document::new_ptr(doc_ptr))
          }
        }
      }
    }
  }

  ///Parses the XML/HTML bytes `input` to generate a new `Document`
  pub fn parse_string<Bytes: AsRef<[u8]>>(&self, input: Bytes) -> Result<Document, XmlParseError> {
    self.parse_string_with_options(input, ParserOptions::default())
  }

  ///Parses the XML/HTML bytes `input` with a manually-specified
  ///parser-options to generate a new `Document`
  pub fn parse_string_with_options<Bytes: AsRef<[u8]>>(
    &self,
    input: Bytes,
    parser_options: ParserOptions,
  ) -> Result<Document, XmlParseError> {
    // Process input bytes.
    let input_bytes = input.as_ref();
    let input_ptr = input_bytes.as_ptr() as *const c_char;
    let input_len = try_usize_to_i32(input_bytes.len())?;

    // Process encoding.
    let encoding_cstring: Option<CString> = parser_options.encoding.map(|v| CString::new(v).unwrap());
    let encoding_ptr = match encoding_cstring {
      Some(v) => v.as_ptr(),
      None => DEFAULT_ENCODING,
    };

    // Process url.
    let url_ptr = DEFAULT_URL;

    let options = parser_options.to_flags(&self.format);

    match self.format {
      ParseFormat::XML => unsafe {
        let docptr = xmlReadMemory(input_ptr, input_len, url_ptr, encoding_ptr, options);
        if docptr.is_null() {
          Err(XmlParseError::GotNullPointer)
        } else {
          Ok(Document::new_ptr(docptr))
        }
      },
      ParseFormat::HTML => unsafe {
        let docptr = htmlReadMemory(input_ptr, input_len, url_ptr, encoding_ptr, options);
        if docptr.is_null() {
          Err(XmlParseError::GotNullPointer)
        } else {
          Ok(Document::new_ptr(docptr))
        }
      },
    }
  }

  /// Checks a string for well-formedness.
  pub fn is_well_formed_html<Bytes: AsRef<[u8]>>(&self, input: Bytes) -> bool {
    self.is_well_formed_html_with_encoding(input, None)
  }

  /// Checks a string for well-formedness with manually-specified encoding.
  /// IMPORTANT: This function is currently implemented in a HACKY way, to ignore invalid errors for HTML5 elements (such as <math>)
  ///            this means you should NEVER USE IT WHILE THREADING, it is CERTAIN TO BREAK
  ///
  /// Help is welcome in implementing it correctly.
  pub fn is_well_formed_html_with_encoding<Bytes: AsRef<[u8]>>(
    &self,
    input: Bytes,
    encoding: Option<&str>,
  ) -> bool {
    // Process input string.
    let input_bytes = input.as_ref();
    if input_bytes.is_empty() {
      return false;
    }
    let input_ptr = input_bytes.as_ptr() as *const c_char;
    let input_len = match try_usize_to_i32(input_bytes.len()) {
      Ok(v) => v,
      Err(_) => return false,
    };

    // Process encoding.
    let encoding_cstring: Option<CString> = encoding.map(|v| CString::new(v).unwrap());
    let encoding_ptr = match encoding_cstring {
      Some(v) => v.as_ptr(),
      None => DEFAULT_ENCODING,
    };

    // Process url.
    let url_ptr = DEFAULT_URL;
    // disable generic error lines from libxml2
    match self.format {
      ParseFormat::XML => false, // TODO: Add support for XML at some point
      ParseFormat::HTML => unsafe {
        let ctxt = htmlNewParserCtxt();
        setWellFormednessHandler(ctxt);
        let docptr = htmlCtxtReadMemory(ctxt, input_ptr, input_len, url_ptr, encoding_ptr, 10_596); // htmlParserOption = 4+32+64+256+2048+8192
        let well_formed_final = if htmlWellFormed(ctxt) {
          // Basic well-formedness passes, let's check if we have an <html> element as root too
          if !docptr.is_null() {
            let node_ptr = xmlDocGetRootElement(docptr);
            let name_ptr = xmlNodeGetName(node_ptr);
            if name_ptr.is_null() {
              false
            }
            //empty string
            else {
              let c_root_name = CStr::from_ptr(name_ptr);
              let root_name = str::from_utf8(c_root_name.to_bytes()).unwrap().to_owned();
              root_name == "html"
            }
          } else {
            false
          }
        } else {
          false
        };

        if !ctxt.is_null() {
          htmlFreeParserCtxt(ctxt);
        }
        if !docptr.is_null() {
          xmlFreeDoc(docptr);
        }
        well_formed_final
      },
    }
  }
}
<|endoftext|>


<commit_before>use std::cell::RefCell;

use game::{LevelTable, Turn, Shadowcast, AnsiRenderer};
use frontends::ansi;
use util::LeakyReserver;
use ecs::{self, EntityId, EcsAction};
use math::Coord;
use debug;

pub struct GameCtx<'a> {
    levels: LevelTable,
    renderer: AnsiRenderer<'a>,
    input_source: ansi::InputSource,
    entity_ids: RefCell<LeakyReserver<EntityId>>,
    turn_id: u64,
    level_id: isize,
    pc_id: Option<EntityId>,
    pc_observer: Shadowcast,
}

impl<'a> GameCtx<'a> {
    pub fn new(window: ansi::Window<'a>, input_source: ansi::InputSource) -> Self {
        GameCtx {
            levels: LevelTable::new(),
            renderer: AnsiRenderer::new(window),
            input_source: input_source,
            entity_ids: RefCell::new(LeakyReserver::new()),
            turn_id: 0,
            level_id: 0,
            pc_id: None,
            pc_observer: Shadowcast::new(),
        }
    }

    pub fn run(&mut self) {
        self.init_demo();

        self.pc_render_ansi();

        self.input_source.get_event();
    }

    fn new_id(&self) -> EntityId {
        self.entity_ids.borrow_mut().reserve()
    }

    fn commit(&mut self, action: &mut EcsAction) {
        let level = self.levels.level_mut(self.level_id);
        level.spatial_hash.update(Turn::new(&level.ecs, self.turn_id), action);
        level.ecs.commit(action);
    }

    fn pc_render_ansi(&mut self) {
        self.pc_observe_ansi();
        self.pc_draw_ansi(Coord::new(0, 0), 37, 26);
    }

    fn pc_observe_ansi(&self) {
        let level = self.levels.level(self.level_id);
        let entity = level.ecs.entity(self.pc_id.unwrap());
        let mut knowledge = entity.ansi_drawable_knowledge_borrow_mut().unwrap();
        let level_knowledge = knowledge.level_mut(self.level_id);
        let position = entity.position().unwrap();
        let vision_distance = entity.vision_distance().unwrap();
        let turn = Turn::new(&level.ecs, self.turn_id);
        self.pc_observer.observe(position, &level.spatial_hash, vision_distance, level_knowledge, turn);
    }

    fn pc_draw_ansi(&mut self, top_left: Coord, width: usize, height: usize) {
        let level = self.levels.level(self.level_id);
        let entity = level.ecs.entity(self.pc_id.unwrap());
        let knowledge = entity.ansi_drawable_knowledge_borrow().unwrap();
        let level_knowledge = knowledge.level(self.level_id);
        self.renderer.render(level_knowledge, self.turn_id, top_left, width, height);
    }

    fn init_demo(&mut self) {
        let strings = demo_level_str();

        let mut g = EcsAction::new();

        let mut y = 0;
        for line in &strings {
            let mut x = 0;
            for ch in line.chars() {
                let coord = Coord::new(x, y);
                match ch {
                    '#' => {
                        ecs::prototypes::wall(g.entity_mut(self.new_id()), coord);
                        ecs::prototypes::floor(g.entity_mut(self.new_id()), coord);
                    }
                    '&' => {
                        ecs::prototypes::tree(g.entity_mut(self.new_id()), coord);
                        ecs::prototypes::outside_floor(g.entity_mut(self.new_id()), coord);
                    }
                    '.' => {
                        ecs::prototypes::floor(g.entity_mut(self.new_id()), coord);
                    }
                    ',' => {
                        ecs::prototypes::outside_floor(g.entity_mut(self.new_id()), coord);
                    }
                    '@' => {
                        let id = self.new_id();
                        self.pc_id = Some(id);
                        ecs::prototypes::pc(g.entity_mut(id), coord);
                        ecs::prototypes::outside_floor(g.entity_mut(self.new_id()), coord);
                    }
                    _ => panic!(),
                }
                x += 1;
            }
            y += 1;
        }

        self.commit(&mut g);
    }
}

fn demo_level_str() -> Vec<&'static str> {
    vec!["&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&",
         "&,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,&",
         "&,,############################,,,,,,&",
         "&,,#.........#................#,,&,,,&",
         "&,,#.........#................#,,,&,,&",
         "&,,#..........................#,,&,,,&",
         "&&,#.........#................#,,,,,,&",
         "&,&#.........##########.#######,,,,,,&",
         "&,,#.........#,,,,,,,,,,,,,,,,,,,,,,,&",
         "&&,#.........#,,,,,,,,,&,,,,,,,&,&,&,&",
         "&,,#.........#,,,,,&,,,,,,,,&,,,,,,,,&",
         "&,,#..........,,,,,,&,,,,,,,,,,,,,,,,&",
         "&&,#.........#,,,,,&,,,,,,,,,&,,,,,,,&",
         "&,,#.........#,,,,,,,,,,&,,&,,,&,&,,,&",
         "&,&#.........#,,,,@,,,,&,,,,,,,,,,,,,&",
         "&,,###########,,,,,,,&,,,,,,,&,&,,,,,&",
         "&,,&,,,,,,,,,,,,,,,,,&,,,,&,,,,,,,,,,&",
         "&,&,,,,,,,,,,,,&,,,,,,,,,,,,,,,,,,,,,&",
         "&,,,&,,,,,,,,,,,,,,,,&,,,,,#########,&",
         "&,&,,,&,,,,,&,,&,,,,&,,,,,,#.......#,&",
         "&,,,,,&,,,,,,,,,&,,,,&,,,,,#.......#,&",
         "&,,,,,,,,,&,,,,,,,,,,,,,&,,........#,&",
         "&,&,&,,,,&&,,,&,&,,,,,,,&,,#.......#,&",
         "&,,,,,,,,,,,,,,,,,,,&,,,,,,#.......#,&",
         "&,,,&,,,,,,,&,,,,,,,,,,,,,,#########,&",
          "&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&"]
}

<commit_msg>Removed unused import<commit_after>use std::cell::RefCell;

use game::{LevelTable, Turn, Shadowcast, AnsiRenderer};
use frontends::ansi;
use util::LeakyReserver;
use ecs::{self, EntityId, EcsAction};
use math::Coord;

pub struct GameCtx<'a> {
    levels: LevelTable,
    renderer: AnsiRenderer<'a>,
    input_source: ansi::InputSource,
    entity_ids: RefCell<LeakyReserver<EntityId>>,
    turn_id: u64,
    level_id: isize,
    pc_id: Option<EntityId>,
    pc_observer: Shadowcast,
}

impl<'a> GameCtx<'a> {
    pub fn new(window: ansi::Window<'a>, input_source: ansi::InputSource) -> Self {
        GameCtx {
            levels: LevelTable::new(),
            renderer: AnsiRenderer::new(window),
            input_source: input_source,
            entity_ids: RefCell::new(LeakyReserver::new()),
            turn_id: 0,
            level_id: 0,
            pc_id: None,
            pc_observer: Shadowcast::new(),
        }
    }

    pub fn run(&mut self) {
        self.init_demo();

        self.pc_render_ansi();

        self.input_source.get_event();
    }

    fn new_id(&self) -> EntityId {
        self.entity_ids.borrow_mut().reserve()
    }

    fn commit(&mut self, action: &mut EcsAction) {
        let level = self.levels.level_mut(self.level_id);
        level.spatial_hash.update(Turn::new(&level.ecs, self.turn_id), action);
        level.ecs.commit(action);
    }

    fn pc_render_ansi(&mut self) {
        self.pc_observe_ansi();
        self.pc_draw_ansi(Coord::new(0, 0), 37, 26);
    }

    fn pc_observe_ansi(&self) {
        let level = self.levels.level(self.level_id);
        let entity = level.ecs.entity(self.pc_id.unwrap());
        let mut knowledge = entity.ansi_drawable_knowledge_borrow_mut().unwrap();
        let level_knowledge = knowledge.level_mut(self.level_id);
        let position = entity.position().unwrap();
        let vision_distance = entity.vision_distance().unwrap();
        let turn = Turn::new(&level.ecs, self.turn_id);
        self.pc_observer.observe(position, &level.spatial_hash, vision_distance, level_knowledge, turn);
    }

    fn pc_draw_ansi(&mut self, top_left: Coord, width: usize, height: usize) {
        let level = self.levels.level(self.level_id);
        let entity = level.ecs.entity(self.pc_id.unwrap());
        let knowledge = entity.ansi_drawable_knowledge_borrow().unwrap();
        let level_knowledge = knowledge.level(self.level_id);
        self.renderer.render(level_knowledge, self.turn_id, top_left, width, height);
    }

    fn init_demo(&mut self) {
        let strings = demo_level_str();

        let mut g = EcsAction::new();

        let mut y = 0;
        for line in &strings {
            let mut x = 0;
            for ch in line.chars() {
                let coord = Coord::new(x, y);
                match ch {
                    '#' => {
                        ecs::prototypes::wall(g.entity_mut(self.new_id()), coord);
                        ecs::prototypes::floor(g.entity_mut(self.new_id()), coord);
                    }
                    '&' => {
                        ecs::prototypes::tree(g.entity_mut(self.new_id()), coord);
                        ecs::prototypes::outside_floor(g.entity_mut(self.new_id()), coord);
                    }
                    '.' => {
                        ecs::prototypes::floor(g.entity_mut(self.new_id()), coord);
                    }
                    ',' => {
                        ecs::prototypes::outside_floor(g.entity_mut(self.new_id()), coord);
                    }
                    '@' => {
                        let id = self.new_id();
                        self.pc_id = Some(id);
                        ecs::prototypes::pc(g.entity_mut(id), coord);
                        ecs::prototypes::outside_floor(g.entity_mut(self.new_id()), coord);
                    }
                    _ => panic!(),
                }
                x += 1;
            }
            y += 1;
        }

        self.commit(&mut g);
    }
}

fn demo_level_str() -> Vec<&'static str> {
    vec!["&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&",
         "&,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,&",
         "&,,############################,,,,,,&",
         "&,,#.........#................#,,&,,,&",
         "&,,#.........#................#,,,&,,&",
         "&,,#..........................#,,&,,,&",
         "&&,#.........#................#,,,,,,&",
         "&,&#.........##########.#######,,,,,,&",
         "&,,#.........#,,,,,,,,,,,,,,,,,,,,,,,&",
         "&&,#.........#,,,,,,,,,&,,,,,,,&,&,&,&",
         "&,,#.........#,,,,,&,,,,,,,,&,,,,,,,,&",
         "&,,#..........,,,,,,&,,,,,,,,,,,,,,,,&",
         "&&,#.........#,,,,,&,,,,,,,,,&,,,,,,,&",
         "&,,#.........#,,,,,,,,,,&,,&,,,&,&,,,&",
         "&,&#.........#,,,,@,,,,&,,,,,,,,,,,,,&",
         "&,,###########,,,,,,,&,,,,,,,&,&,,,,,&",
         "&,,&,,,,,,,,,,,,,,,,,&,,,,&,,,,,,,,,,&",
         "&,&,,,,,,,,,,,,&,,,,,,,,,,,,,,,,,,,,,&",
         "&,,,&,,,,,,,,,,,,,,,,&,,,,,#########,&",
         "&,&,,,&,,,,,&,,&,,,,&,,,,,,#.......#,&",
         "&,,,,,&,,,,,,,,,&,,,,&,,,,,#.......#,&",
         "&,,,,,,,,,&,,,,,,,,,,,,,&,,........#,&",
         "&,&,&,,,,&&,,,&,&,,,,,,,&,,#.......#,&",
         "&,,,,,,,,,,,,,,,,,,,&,,,,,,#.......#,&",
         "&,,,&,,,,,,,&,,,,,,,,,,,,,,#########,&",
          "&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&"]
}

<|endoftext|>