s_exp/
lib.rs

1// Copyright 2020(c) Wael El Oraiby
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in
11// all copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19// THE SOFTWARE.
20#![allow(non_snake_case, non_camel_case_types)]
21
22use alt_std::*;
23use alt_std::{format};
24
25pub struct ParseError {
26    message : String,
27    offset  : usize
28}
29
30pub enum ParseResult<T> {
31    PROk(T),
32    PRErr(ParseError)
33}
34
35use ParseResult::*;
36
37impl<T : core::cmp::PartialEq> PartialEq for ParseResult<T> {
38    fn eq(&self, other: &Self) -> bool {
39        match (self, other) {
40            (PROk(s), PROk(o)) => *s == *o,
41            (PRErr (ParseError{ message: msg1, offset: offset1 }), PRErr (ParseError{ message: msg2, offset: offset2 })) => *msg1 == *msg2 && *offset1 == *offset2,
42            _ => false
43        }
44    }
45}
46
47#[derive(Clone)]
48pub enum Exp {
49    Bool(bool),
50    Char(char),
51    Int(i64),
52    Float(f64),
53    String(String),
54    Symbol(String),
55    List(Vec<Exp>),
56}
57
58impl PartialEq<Exp> for Exp {
59    fn eq(&self, other: &Exp) -> bool {
60        match (self, other) {
61            (Self::Bool(b0),            Self::Bool(bo))     => b0 == bo,
62            (Self::Char(c0),            Self::Char(c1))     => c0 == c1,
63            (Self::Int(i0),             Self::Int(i1))      => i0 == i1,
64            (Self::Float(f0),           Self::Float(f1))    => f0 == f1,
65            (Self::String(s0),          Self::String(s1))   => s0 == s1,
66            (Self::Symbol(s0),          Self::Symbol(s1))   => s0 == s1,
67            (Self::List(s), Self::List(o)) => {
68                if s.len() != o.len() { return false }
69                for i in 0..s.len() {
70                    if !Self::eq(&s[i], &o[i]) { return false }
71                }
72                true
73            },
74            _ => false
75        }
76    }
77}
78impl Exp {
79    fn peek(src: &[u8], offset: usize) -> Option<u8> {
80        if src.len() <= offset {
81            None
82        } else {
83            Some(src[offset])
84        }
85    }
86
87    fn getchar(src: &[u8], offset: &mut usize) -> Option<u8> {
88        match Self::peek(src, *offset) {
89            None => None,
90            Some(c) => { *offset += 1; Some(c) },
91        }
92    }
93
94    fn isDigit(c: u8) -> bool {
95        match c as char {
96            c if c >= '0' && c <= '9' => true,
97            _ => false
98        }
99    }
100
101    fn isAlpha(c: u8) -> bool {
102        match c as char {
103            c if (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') => true,
104            _ => false
105        }
106    }
107
108
109    fn isOp(c: u8) -> bool {
110        match c as char {
111            '+' | '-' | '*' | '/' | '%' | '~' | '!' | '@' | '#' | '$' | '^' | '&' | '|' | '_' | '=' | '<' | '>' | '?' | '.' | ':' | '\\' | '\'' => true,
112            _ => false
113        }
114    }
115
116    fn isWS(c: u8) -> bool {
117        match c as char {
118            ' ' | '\n' | '\t' => true,
119            _ => false
120        }
121    }
122
123    fn isSeparator(c: u8) -> bool {
124        match c as char {
125            '(' | ')' | '{' | '}' | ',' | '\'' | '"' => true,
126            x if Self::isWS(x as u8) => true,
127            _ => false
128        }
129    }
130
131    pub fn parseNumber(src: &[u8], offset: &mut usize) -> ParseResult<Exp> {
132        let mut s = String::new();
133        loop {
134            match Self::peek(src, *offset) {
135                Some(c) if c == b'+' || c == b'-' || c == b'.' || c == b'e' || c == b'E' || Self::isDigit(c) => {
136                    s.add(c);
137                    Self::getchar(src, offset);
138                },
139                Some(c) if Self::isSeparator(c) => break,
140                None => break,
141                _ => return PRErr (ParseError { message: String::from("Unexpected end of stream (sign)"), offset: *offset })
142            }
143        }
144
145        match str::parse::<i64>(s.toStr()) {
146            Ok(i) => return ParseResult::PROk(Exp::Int(i)),
147            _ => ()
148        }
149
150        match str::parse::<f64>(s.toStr()) {
151            Ok(f) => return ParseResult::PROk(Exp::Float(f)),
152            _ => ()
153        }
154
155        PRErr (ParseError { message: String::from("invalid number format"), offset: *offset })
156    }
157
158    fn parseString(src: &[u8], offset: &mut usize) -> ParseResult<String> {
159        let mut s = String::new();
160        match Self::peek(src, *offset) {
161            Some(c) if c as char == '"' => (),
162            _ => return PRErr (ParseError{ message: String::from("Expected \""), offset: *offset })
163        }
164
165        Self::getchar(src, offset);
166        // TODO: handle '\' case
167        loop {
168            match Self::getchar(src, offset) {
169                None => return PRErr (ParseError{ message: String::from("Unexpected end of stream (string)"), offset: *offset }),
170                Some(c) if c as char == '"' => break,
171                Some(c) => s.add(c),
172            }
173        }
174
175        return PROk(s)
176    }
177
178    fn parseSymbol(src: &[u8], offset: &mut usize) -> ParseResult<String> {
179        let mut s = String::new();
180        match Self::peek(src, *offset) {
181            Some(c) if Self::isAlpha(c) || Self::isOp(c) => (),
182            _ => return PRErr (ParseError{ message: String::from("Expected alpha/operator"), offset: *offset })
183        }
184
185        loop {
186            match Self::peek(src, *offset) {
187                Some(c) if Self::isAlpha(c) || Self::isOp(c) || Self::isDigit(c) => s.add(c),
188                _ => break,
189            }
190            Self::getchar(src, offset);
191        }
192
193        return PROk(s)
194    }
195
196    fn skipWS(src: &[u8], offset: &mut usize) {
197        loop {
198            match Self::peek(src, *offset) {
199                Some(c) if Self::isWS(c) => { Self::getchar(src, offset); },
200                _ => break
201            }
202        }
203    }
204
205    fn parseToken(src: &[u8], offset: &mut usize) -> ParseResult<Exp> {
206        match Self::peek(src, *offset) {
207            Some(c) if c as char == '"' => {
208                let stringRes = Self::parseString(src, offset);
209                match stringRes {
210                    PROk(r) => PROk(Exp::String(r)),
211                    PRErr(err) => PRErr(err)
212                }
213            },
214            Some(c) if Self::isDigit(c) || ((c as char == '+' || c as char == '-') && match Self::peek(src, *offset + 1) { Some(c) if Self::isDigit(c) => true, _ => false })  => {
215                let numRes = Self::parseNumber(src, offset);
216                match numRes {
217                    PROk(r) => PROk(r),
218                    PRErr(err) => PRErr(err)
219                }
220            },
221            Some(c) if Self::isAlpha(c) || Self::isOp(c) => {
222                let symbolRes = Self::parseSymbol(src, offset);
223                match symbolRes {
224                    PROk(r) => PROk(Exp::Symbol(r)),
225                    PRErr(err) => PRErr(err)
226                }
227            },
228            Some(c) if c as char == '(' => Self::parseList(src, offset),
229            Some(_) => PRErr(ParseError { message: String::from("unexpected char (token)"), offset: *offset}),
230            None => PRErr(ParseError { message: String::from("unexpected end of stream (token)"), offset: *offset}),
231        }
232    }
233
234    fn parseList(src: &[u8], offset: &mut usize) -> ParseResult<Exp> {
235        match Self::getchar(src, offset) {
236            Some(c) if c as char == '(' => (),
237            Some(_) => return PRErr(ParseError { message: String::from("unexpected character (list)"), offset: *offset}),
238            None => return PRErr(ParseError { message: String::from("unexpected end of stream (list)"), offset: *offset}),
239        }
240
241        let mut cells = Vec::new();
242        loop {
243            Self::skipWS(src, offset);
244            match Self::peek(src, *offset) {
245                Some(c) if c as char == ')' => {
246                    Self::getchar(src, offset);
247                    return PROk(Exp::List(cells))
248                },
249                Some(_) => {
250                    match Self::parseToken(src, offset) {
251                        PROk(c) => cells.pushBack(c),
252                        PRErr(err) => return PRErr(err),
253                    }
254                },
255                None => return PRErr(ParseError { message: String::from("unexpected end of stream (list)"), offset: *offset})
256            }
257        }
258    }
259
260    pub fn fromSExp(src: &[u8]) -> ParseResult<Exp> {
261        let mut offset : usize = 0;
262        Self::skipWS(src, &mut offset);
263        Self::parseToken(src, &mut offset)
264    }
265
266    pub fn toString(&self) -> String {
267        match self {
268            Self::Bool(b) => format!("{}", b),
269            Self::Char(c) => format!("{}", c),
270            Self::Int(i) => format!("{}", i),
271            Self::Float(f) => format!("{}", f),
272            Self::String(s) => {
273                let mut sr = String::new();
274                sr.add('"' as u8);
275                let a = s.asArray();
276                for i in a.iter() {
277                    sr.add(*i);
278                }
279                sr.add('"' as u8);
280                sr
281            },
282            Self::Symbol(s) => s.clone(),
283            Self::List(l) => {
284                let mut s = String::new();
285                s.add('(' as u8);
286                for i in 0..l.len() {
287                    s.append(&(l[i].toString()));
288                    if i != l.len() - 1 {
289                        s.add(' ' as u8);
290                    }
291                }
292                s.add(')' as u8);
293                s
294            }
295        }
296    }
297}
298
299#[cfg(test)]
300mod tests {
301    use super::*;
302
303    #[test]
304    fn testParseInt() {
305        let s = String::from("1234");
306        let mut offset = 0;
307        let res = Exp::parseNumber(s.asArray(), &mut offset);
308        assert!(res == PROk(Exp::Int(1234)));
309
310        let s = String::from("-001234");
311        let mut offset = 0;
312        let res = Exp::parseNumber(s.asArray(), &mut offset);
313        assert!(res == PROk(Exp::Int(-1234)));
314
315        let s = String::from("-1234");
316        let mut offset = 0;
317        let res = Exp::parseNumber(s.asArray(), &mut offset);
318        assert!(res == PROk(Exp::Int(-1234)));
319
320        let s = String::from("-1234 ");
321        let mut offset = 0;
322        let res = Exp::parseNumber(s.asArray(), &mut offset);
323        assert!(res == PROk(Exp::Int(-1234)));
324
325        let s = String::from("-1234+");
326        let mut offset = 0;
327        let res = Exp::parseNumber(s.asArray(), &mut offset);
328        assert!(res != PROk(Exp::Int(-1234)));
329
330        let s = String::from("-1234a");
331        let mut offset = 0;
332        let res = Exp::parseNumber(s.asArray(), &mut offset);
333        assert!(res != PROk(Exp::Int(-1234)));
334    }
335
336    #[test]
337    fn testParseFloat() {
338        let s = String::from("1234.");
339        let mut offset = 0;
340        let res = Exp::parseNumber(s.asArray(), &mut offset);
341        assert!(res == PROk(Exp::Float(1234.)));
342
343        let s = String::from("1234.0");
344        let mut offset = 0;
345        let res = Exp::parseNumber(s.asArray(), &mut offset);
346        assert!(res == PROk(Exp::Float(1234.)));
347
348        let s = String::from("-001234.0");
349        let mut offset = 0;
350        let res = Exp::parseNumber(s.asArray(), &mut offset);
351        assert!(res == PROk(Exp::Float(-1234.)));
352
353        let s = String::from("-1234.0");
354        let mut offset = 0;
355        let res = Exp::parseNumber(s.asArray(), &mut offset);
356        assert!(res == PROk(Exp::Float(-1234.)));
357
358        let s = String::from("-1234.0 ");
359        let mut offset = 0;
360        let res = Exp::parseNumber(s.asArray(), &mut offset);
361        assert!(res == PROk(Exp::Float(-1234.)));
362
363        let s = String::from("-1234.0+");
364        let mut offset = 0;
365        let res = Exp::parseNumber(s.asArray(), &mut offset);
366        assert!(res != PROk(Exp::Float(-1234.)));
367
368        let s = String::from("-1234.0a");
369        let mut offset = 0;
370        let res = Exp::parseNumber(s.asArray(), &mut offset);
371        assert!(res != PROk(Exp::Float(-1234.)));
372
373        let s = String::from("-001234.0E10");
374        let mut offset = 0;
375        let res = Exp::parseNumber(s.asArray(), &mut offset);
376        assert!(res == PROk(Exp::Float(-1234.0E10)));
377
378        let s = String::from("-001234.0E-10");
379        let mut offset = 0;
380        let res = Exp::parseNumber(s.asArray(), &mut offset);
381        assert!(res == PROk(Exp::Float(-1234.0E-10)));
382    }
383
384    #[test]
385    fn testParseString() {
386        let s = String::from("\"1234\"");
387        let mut offset = 0;
388        let res = Exp::parseString(s.asArray(), &mut offset);
389        assert!(res == PROk(String::from("1234")));
390
391        let s = String::from("\"1234");
392        let mut offset = 0;
393        let res = Exp::parseString(s.asArray(), &mut offset);
394        assert!(res != PROk(String::from("1234")));
395    }
396
397    #[test]
398    fn testParseSymbol() {
399        let s = String::from("#t");
400        let mut offset = 0;
401        let res = Exp::parseSymbol(s.asArray(), &mut offset);
402        assert!(res == PROk(String::from("#t")));
403
404        let s = String::from("t123");
405        let mut offset = 0;
406        let res = Exp::parseSymbol(s.asArray(), &mut offset);
407        assert!(res == PROk(String::from("t123")));
408
409        let s = String::from("t123(");
410        let mut offset = 0;
411        let res = Exp::parseSymbol(s.asArray(), &mut offset);
412        assert!(res == PROk(String::from("t123")));
413
414        let s = String::from("t123+=");
415        let mut offset = 0;
416        let res = Exp::parseSymbol(s.asArray(), &mut offset);
417        assert!(res == PROk(String::from("t123+=")));
418
419        let s = String::from("12t123");
420        let mut offset = 0;
421        let res = Exp::parseSymbol(s.asArray(), &mut offset);
422        assert!(res != PROk(String::from("12t123")));
423    }
424
425    #[test]
426    fn testParseList() {
427
428        let cells : [Exp; 3] = [Exp::Symbol(String::from("abcd")), Exp::Int(123), Exp::Symbol(String::from("abc"))];
429
430        let sexp = String::from("(abcd 123 abc)");
431        let res : ParseResult<Exp> = Exp::fromSExp(sexp.asArray());
432        match res {
433            PROk(r) => {
434                let mut v = Vec::new();
435                for c in cells.iter() {
436                    v.pushBack(c.clone());
437                }
438                let e = Exp::List(Vec::from(v));
439                assert!(Exp::eq(&e, &r))
440            },
441            PRErr(err) => panic!("{}", err.message.toStr())
442        }
443    }
444
445    #[test]
446    fn testParseListString() {
447        let sexp = String::from("(abcd 123 abc)");
448        let res = Exp::fromSExp(sexp.asArray());
449        match res {
450            PROk(r) => {
451                let s = r.toString();
452                assert!(s == "(abcd 123 abc)")
453            },
454            PRErr(err) => panic!("{}", err.message.toStr())
455        }
456    }
457}