aboutsummaryrefslogtreecommitdiff
path: root/rust/src/reader.rs
blob: f0ef2e33fcd71ee2f01bc0c38cf64373a38ea19d (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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
//#![feature(phase)]
//#[phase(plugin)]
//extern crate regex_macros;
//extern crate regex;

extern crate pcre;

use types::{MalVal,MalRet,
            _nil,_true,_false,_int,symbol,string,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(_int(num.unwrap()))
    } else if regex!(r#"^".*"$"#).is_match(token) {
        let new_str = token.slice(1,token.len()-1);
        Ok(string(unescape_str(new_str)))
    } else if token == "nil" {
        Ok(_nil())
    } else if token == "true" {
        Ok(_true())
    } else if token == "false" {
        Ok(_false())
    } else {
        Ok(symbol(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();

    Ok(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 {
        "'" => {
            let _ = rdr.next();
            match read_form(rdr) {
                Ok(f) => Ok(list(vec![symbol("quote"), f])),
                Err(e) => Err(e),
            }
        },
        "`" => {
            let _ = rdr.next();
            match read_form(rdr) {
                Ok(f) => Ok(list(vec![symbol("quasiquote"), f])),
                Err(e) => Err(e),
            }
        },
        "~" => {
            let _ = rdr.next();
            match read_form(rdr) {
                Ok(f) => Ok(list(vec![symbol("unquote"), f])),
                Err(e) => Err(e),
            }
        },
        "~@" => {
            let _ = rdr.next();
            match read_form(rdr) {
                Ok(f) => Ok(list(vec![symbol("splice-unquote"), f])),
                Err(e) => Err(e),
            }
        },

        ")" => 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)
}