aboutsummaryrefslogtreecommitdiff
path: root/rust/src/reader.rs
blob: 035cfde876c84887e795534039ff72ffc7cb4a52 (plain)
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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
//#![feature(phase)]
//#[phase(plugin)]
//extern crate regex_macros;
//extern crate regex;

extern crate pcre;

use std::rc::Rc;
use types::{MalVal,MalRet,Nil,True,False,Int,Strn,Sym,List};
use self::pcre::Pcre;
use super::printer::unescape_str;

#[deriving(Show, Clone)]
struct Reader {
    tokens   : Vec<String>,
    position : uint,
}

impl Reader {
    fn next(&mut self) -> Option<String> {
        if self.position < self.tokens.len() {
            self.position += 1;
            Some(self.tokens[self.position-1].to_string())
        } else {
            None
        }
    }
    fn peek(&self) -> Option<String> {
        if self.position < self.tokens.len() {
            Some(self.tokens[self.position].to_string())
        } else {
            None
        }
    }
}

fn tokenize(str :String) -> Vec<String> {
    let mut results = vec![];

    let re = match Pcre::compile(r###"[\s,]*(~@|[\[\]{}()'`~^@]|"(?:\\.|[^\\"])*"|;.*|[^\s\[\]{}('"`,;)]*)"###) {
        Err(_) => { fail!("failed to compile regex") },
        Ok(re) => re
    };

    let mut it = re.matches(str.as_slice());
    loop {
        let opt_m = it.next();
        if opt_m.is_none() { break; }
        let m = opt_m.unwrap();
        if m.group(1) == "" { break; }

        results.push((*m.group(1)).to_string());
    }
    results
}

fn read_atom(rdr : &mut Reader) -> MalRet {
    let otoken = rdr.next();
    //println!("read_atom: {}", otoken);
    if otoken.is_none() { return Err("read_atom underflow".to_string()); }
    let stoken = otoken.unwrap();
    let token = stoken.as_slice();
    if regex!(r"^-?[0-9]+$").is_match(token) {
        let num : Option<int> = from_str(token);
        Ok(Rc::new(Int(num.unwrap())))
    } else if regex!(r#"^".*"$"#).is_match(token) {
        let new_str = token.slice(1,token.len()-1);
        Ok(Rc::new(Strn(unescape_str(new_str))))
    } else if token == "nil" {
        Ok(Rc::new(Nil))
    } else if token == "true" {
        Ok(Rc::new(True))
    } else if token == "false" {
        Ok(Rc::new(False))
    } else {
        Ok(Rc::new(Sym(String::from_str(token))))
    }
}

fn read_list(rdr : &mut Reader) -> MalRet {
    let otoken = rdr.next();
    if otoken.is_none() { return Err("read_atom underflow".to_string()); }
    let stoken = otoken.unwrap();
    let token = stoken.as_slice();
    if token != "(" { return Err("expected '('".to_string()); }

    let mut ast_vec : Vec<MalVal> = vec![];
    loop {
        let otoken = rdr.peek();
        if otoken.is_none() { return Err("expected ')', got EOF".to_string()); }
        let stoken = otoken.unwrap();
        let token = stoken.as_slice();
        if token == ")" { break; }

        match read_form(rdr) {
            Ok(mv) => ast_vec.push(mv),
            Err(e) => return Err(e),
        }
    }
    rdr.next();

    //ast_vec.push(Rc::new(Nil));
    Ok(Rc::new(List(ast_vec)))
}

fn read_form(rdr : &mut Reader) -> MalRet {
    let otoken = rdr.peek();
    //println!("read_form: {}", otoken);
    let stoken = otoken.unwrap();
    let token = stoken.as_slice();
    match token {
        ")" => Err("unexected ')'".to_string()),
        "(" => read_list(rdr),
        _   => read_atom(rdr)
    }
}

pub fn read_str(str :String) -> MalRet {
    let tokens = tokenize(str);
    if tokens.len() == 0 {
        return Err("<empty line>".to_string());
    }
    //println!("tokens: {}", tokens);
    let rdr = &mut Reader{tokens: tokens, position: 0};
    read_form(rdr)
}