liquid_fixpoint/
sexp.rs

1use std::str::Chars;
2
3#[derive(Debug, Clone, PartialEq)]
4pub enum Atom {
5    S(String),
6    Q(String),
7    I(i32),
8    F(f32),
9    B(bool),
10}
11
12#[derive(Debug, Clone, PartialEq)]
13pub enum Sexp {
14    Atom(Atom),
15    List(Vec<Sexp>),
16}
17
18#[derive(Debug)]
19pub enum ParseError {
20    UnexpectedEOF,
21    InvalidToken(String),
22    UnclosedString,
23    InvalidNumber(String),
24}
25
26pub struct Parser<'a> {
27    chars: Chars<'a>,
28    current: Option<char>,
29}
30
31impl<'a> Parser<'a> {
32    pub fn new(input: &'a str) -> Self {
33        let mut chars = input.chars();
34        let current = chars.next();
35        Parser { chars, current }
36    }
37
38    fn bump(&mut self) {
39        self.current = self.chars.next();
40    }
41
42    fn skip_whitespace(&mut self) {
43        while matches!(self.current, Some(c) if c.is_whitespace()) {
44            self.bump();
45        }
46    }
47
48    pub fn parse(&mut self) -> Result<Sexp, ParseError> {
49        self.skip_whitespace();
50        match self.current {
51            Some('(') => self.parse_list(),
52            Some('"') => self.parse_quoted_string(),
53            Some(_) => self.parse_atom(),
54            None => Err(ParseError::UnexpectedEOF),
55        }
56    }
57
58    fn parse_list(&mut self) -> Result<Sexp, ParseError> {
59        self.bump(); // consume '('
60        let mut items = Vec::new();
61
62        loop {
63            self.skip_whitespace();
64            match self.current {
65                Some(')') => {
66                    self.bump(); // consume ')'
67                    break;
68                }
69                Some(_) => {
70                    let expr = self.parse()?;
71                    items.push(expr);
72                }
73                None => return Err(ParseError::UnexpectedEOF),
74            }
75        }
76        Ok(Sexp::List(items))
77    }
78
79    fn parse_quoted_string(&mut self) -> Result<Sexp, ParseError> {
80        self.bump(); // consume '"'
81        let mut result = String::new();
82
83        while let Some(c) = self.current {
84            match c {
85                '"' => {
86                    self.bump(); // consume closing '"'
87                    return Ok(Sexp::Atom(Atom::Q(result)));
88                }
89                _ => {
90                    result.push(c);
91                    self.bump();
92                }
93            }
94        }
95
96        Err(ParseError::UnclosedString)
97    }
98
99    fn parse_atom(&mut self) -> Result<Sexp, ParseError> {
100        let mut s = String::new();
101
102        if self.current == Some('-') {
103            s.push('-');
104            self.bump();
105        }
106
107        while let Some(c) = self.current {
108            if !c.is_whitespace() && c != '(' && c != ')' && c != '"' {
109                s.push(c);
110                self.bump();
111            } else {
112                break;
113            }
114        }
115
116        if s.is_empty() {
117            return Err(ParseError::InvalidToken("Empty atom".to_string()));
118        }
119
120        match s.as_str() {
121            "true" => Ok(Sexp::Atom(Atom::B(true))),
122            "false" => Ok(Sexp::Atom(Atom::B(false))),
123            _ => {
124                if let Ok(i) = s.parse::<i32>() {
125                    Ok(Sexp::Atom(Atom::I(i)))
126                } else if let Ok(f) = s.parse::<f32>() {
127                    Ok(Sexp::Atom(Atom::F(f)))
128                } else {
129                    Ok(Sexp::Atom(Atom::S(s)))
130                }
131            }
132        }
133    }
134
135    pub fn parse_all(&mut self) -> Result<Vec<Sexp>, ParseError> {
136        let mut expressions = Vec::new();
137
138        while self.current.is_some() {
139            self.skip_whitespace();
140            if self.current.is_none() {
141                break;
142            }
143
144            let expr = self.parse()?;
145            expressions.push(expr);
146        }
147
148        Ok(expressions)
149    }
150}