1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
mod parser;
pub mod errors;

use ast;
use pest::Parser;
use pest::prelude::StringInput;
use self::errors::*;
use std::fs::File;
use std::io::BufReader;
use std::io::Read;
use std::path::Path;

static NL: u8 = '\n' as u8;

pub fn find_line(path: &Path, pos: (usize, usize)) -> Result<(String, usize, (usize, usize))> {
    let file = File::open(path)?;
    let reader = BufReader::new(&file);

    let start = pos.0;
    let end = pos.1;

    let mut line_start = 0usize;
    let mut line_buffer: Vec<u8> = Vec::new();
    let mut lines: usize = 0;
    let mut it = reader.bytes().enumerate();

    while let Some((i, b)) = it.next() {
        let b = b?;

        if b == NL {
            if i >= start {
                let line = String::from_utf8(line_buffer)?;
                let range = (start - line_start, end - line_start);
                return Ok((line, lines, range));
            }

            line_start = i;
            lines = lines + 1;
            line_buffer.clear();
            continue;
        }

        line_buffer.push(b);
    }

    Err("bad file position".into())
}

pub fn parse_file(path: &Path) -> Result<ast::File> {
    let mut f = File::open(path)?;
    let mut content = String::new();

    f.read_to_string(&mut content)?;

    let mut parser = parser::Rdp::new(StringInput::new(&content));

    if !parser.file() {
        let (_, pos) = parser.tracked_len_pos();
        let (line, lines, _) = find_line(path, (pos, pos))?;
        return Err(ErrorKind::Syntax("unexpected input".to_owned(), line, lines).into());
    }

    if !parser.end() {
        return Err("not parsed until end".into());
    }

    Ok(parser._file())
}