json_rs/
parser.rs

1use std::collections::HashMap;
2
3use crate::lexer::{Token, TokenPos};
4use crate::json::{*, self};
5
6pub struct Parser {
7    /// Array of lexed tokens
8    tokens: Vec<TokenPos>,
9    /// Current token
10    pos: usize,
11}
12
13impl From<Vec<TokenPos>> for Parser {
14    fn from(tokens: Vec<TokenPos>) -> Self {
15        Self {
16            tokens,
17            pos: 0,
18        }
19    }
20}
21
22impl Parser {
23    #[inline]
24    fn curr(&self) -> Token {
25        self.tokens[self.pos].0.clone()
26    }
27    #[inline]
28    fn advance(&mut self, len: usize) {
29        self.pos += len;
30    }
31
32    fn expect(&mut self, expected: Token) -> json::Result<()> {
33        if self.curr() == expected {
34            self.pos += 1;
35            Ok(())
36        } else {
37            Err(JSONError::ParseError(format!("expected {:?}, found {:?}", expected, self.curr())))
38        }
39    }
40
41    /// Parse tokens in current
42    pub fn parse(&mut self) -> json::Result<JSONValue> {
43        let (line, column) = (self.tokens[self.pos].1, self.tokens[self.pos].2);
44        match self.curr().clone() {
45            Token::OpenBrace => {
46                // begin object
47                let mut ret: HashMap<String, JSONValue> = HashMap::new();
48
49                self.advance(1);
50
51                // catches the case of an empty object
52                if self.curr() == Token::CloseBrace {
53                    return Ok(JSONValue::Object(ret))
54                }
55
56                // while last character is a comma
57                loop {
58                    // expect a string literal as a key
59                    let key = match self.curr().clone() {
60                        // chops off the quotations
61                        Token::StringLiteral(val) => val[1..val.len() - 1].to_owned(),
62                        _ => return Err(JSONError::ParseError(format!("expected string literal at line {line}, column {column}"))),
63                    };
64                    self.advance(1);
65
66                    // expect a colon
67                    self.expect(Token::Colon)?;
68                    // expect a JSONValue
69                    let val = self.parse()?;
70                    self.advance(1);
71
72                    ret.insert(key, val);
73
74                    if self.curr() == Token::CloseBrace {
75                        break;
76                    }
77                    self.expect(Token::Comma)?;
78                }
79                
80                Ok(JSONValue::Object(ret))
81            },
82            Token::CloseBrace => {
83                Err(JSONError::ParseError(format!("unexpected token `CloseBrace` at line {line}, column {column}")))
84            },
85            Token::OpenBracket => {
86                // begin array
87                let mut ret: Vec<JSONValue> = vec![];
88
89                // parse next token continuously, until the end of the array is reached
90                self.pos += 1;
91
92                // catch the case of an empty array
93                if self.curr() == Token::CloseBracket {
94                    return Ok(JSONValue::Array(ret));
95                }
96
97                loop {
98                    ret.push(self.parse()?);
99                    // moves us off of value
100                    self.pos += 1;
101
102                    // if we're at the end of the array...
103                    if self.curr() == Token::CloseBracket {
104                        break;
105                    }
106
107                    self.expect(Token::Comma)?;
108                }
109
110                Ok(JSONValue::Array(ret))
111            },
112            Token::CloseBracket => {
113                Err(JSONError::ParseError(format!("unexpected token `CloseBracket` at line {line}, column {column}")))
114            },
115            Token::Colon => {
116                Err(JSONError::ParseError(format!("unexpected token `Colon` at line {line}, column {column}")))
117            },
118            Token::Comma => {
119                Err(JSONError::ParseError(format!("unexpected token `Comma` at line {line}, column {column}")))
120            },
121            Token::StringLiteral(val) => {
122                // begin string
123
124                // StringLiteral includes the '"' characters; filter those off
125                let trimmed = val[1..val.len() - 1].to_owned();
126                let t_iter: Vec<char> = trimmed.chars().collect();
127                let mut formatted: Vec<char> = vec![];
128                let mut i = 0;
129                while i < t_iter.len() {
130                    if t_iter[i] == '\\' {
131                        formatted.push(match t_iter[i+1] {
132                            '"' => '"',
133                            '\\' => '\\',
134                            '/' => '/',
135                            'b' => '\u{0008}',
136                            'f' => '\u{000c}',
137                            'n' => '\u{000a}',
138                            'r' => '\u{000d}',
139                            't' => '\u{0009}',
140                            'u' => {
141                                let chars: String = t_iter[i+2..i+6].iter().collect();
142
143                                let num = u16::from_str_radix(&chars, 16)
144                                    .or(Err(JSONError::ValueError(format!("invalid hexadecimal code: {}", chars))))? as u32;
145                                
146                                i += 4;
147                                match char::from_u32(num) {
148                                    Some(v) => v,
149                                    None => return Err(JSONError::ValueError(format!("invalid utf16 hexadecimal code: {}", &chars)))
150                                }
151                            }
152                            _ => return Err(JSONError::ValueError(format!("invalid escape char: {}", t_iter[i+1])))
153                        });
154                        i += 1;
155                    } else {
156                        formatted.push(t_iter[i]);
157                    }
158                    i += 1;
159                }
160
161                Ok(JSONValue::String(String::from_utf8(formatted.iter().map(|c| *c as u8).collect()).unwrap()))
162            },
163            Token::NumericLiteral(val) => {
164                // begin number
165                Ok(JSONValue::Number(val.parse().unwrap()))
166            },
167            Token::True => {
168                Ok(JSONValue::Bool(true))
169            },
170            Token::False => {
171                Ok(JSONValue::Bool(false))
172            },
173            Token::Null => {
174                Ok(JSONValue::Null)
175            }
176            Token::Unknown(text) => {
177                Err(JSONError::ParseError(format!("unexpected token `{text}` at line {line}, column {column}")))
178            }
179        }
180    }
181}