book_json_parser/
book_json_parser.rs1use std::collections::HashMap;
2
3#[derive(Debug)]
5pub enum JSON {
6 Object(HashMap<String, JSON>),
7 Array(Vec<JSON>),
8 String(String),
9 Number(f64),
10 Boolean(bool),
11 Null,
12}
13
14pub fn parse_value_ws(parser: &mut trivet::Parser) -> trivet::errors::ParseResult<JSON> {
17 match parser.peek() {
18 '{' => parse_object_ws(parser),
20
21 '[' => parse_array_ws(parser),
23
24 '"' => Ok(JSON::String(parser.parse_string_match_delimiter_ws()?)),
26
27 ch if ch == '-' || ch.is_ascii_digit() => Ok(JSON::Number(parser.parse_f64_decimal_ws()?)),
29
30 't' if parser.peek_and_consume_str_ws("true") => Ok(JSON::Boolean(true)),
32
33 'f' if parser.peek_and_consume_str_ws("false") => Ok(JSON::Boolean(false)),
35
36 'n' if parser.peek_and_consume_str_ws("null") => Ok(JSON::Null),
38
39 ch => Err(trivet::errors::unexpected_text_error(
41 parser.loc(),
42 "the start of a JSON value",
43 &ch.to_string(),
44 )),
45 }
46}
47
48fn parse_object_ws(parser: &mut trivet::Parser) -> trivet::errors::ParseResult<JSON> {
51 parser.consume(); parser.consume_ws();
53 let mut first = true;
54 let mut map = HashMap::new();
55 while !parser.peek_and_consume_ws('}') {
56 if first {
59 first = false;
60 } else if !parser.peek_and_consume_ws(',') {
61 return Err(trivet::errors::syntax_error(
62 parser.loc(),
63 "There must be a comma (,) between members of an object.",
64 ));
65 }
66
67 if parser.peek() != '"' {
69 return Err(trivet::errors::syntax_error(
70 parser.loc(),
71 "Names in a JSON object must be quoted strings.",
72 ));
73 }
74 let name = parser.parse_string_match_delimiter_ws()?;
75
76 if !parser.peek_and_consume_ws(':') {
78 return Err(trivet::errors::syntax_error(
79 parser.loc(),
80 "Names in a JSON object must be followed by a colon (:).",
81 ));
82 }
83
84 let value = parse_value_ws(parser)?;
86
87 map.insert(name, value);
89 }
90 Ok(JSON::Object(map))
91}
92
93fn parse_array_ws(parser: &mut trivet::Parser) -> trivet::errors::ParseResult<JSON> {
96 parser.consume(); parser.consume_ws_only();
98 let mut first = true;
99 let mut array = vec![];
100 while !parser.peek_and_consume_ws(']') {
101 if first {
104 first = false;
105 } else if !parser.peek_and_consume_ws(',') {
106 return Err(trivet::errors::syntax_error(
107 parser.loc(),
108 "There must be a comma (,) between members of an array.",
109 ));
110 }
111
112 let value = parse_value_ws(parser)?;
114
115 array.push(value);
117 }
118 Ok(JSON::Array(array))
119}
120
121pub fn main() {
123 let mut parser = trivet::parse_from_stdin();
124 parser.parse_comments = false;
125 let numpar = parser.borrow_number_parser();
126 numpar.settings.permit_binary = false;
127 numpar.settings.permit_hexadecimal = false;
128 numpar.settings.permit_octal = false;
129 numpar.settings.permit_underscores = false;
130 numpar.settings.decimal_only_floats = true;
131 numpar.settings.permit_plus = false;
132 numpar.settings.permit_leading_zero = false;
133 numpar.settings.permit_empty_whole = false;
134 numpar.settings.permit_empty_fraction = false;
135 let strpar = parser.borrow_string_parser();
136 strpar.set(trivet::strings::StringStandard::JSON);
137 let _ = parser
138 .borrow_core()
139 .replace_whitespace_test(Box::new(|ch| [' ', '\n', '\r', '\t'].contains(&ch)));
140 parser.consume_ws();
141 let result = parse_value_ws(&mut parser);
142 match result {
143 Err(error) => {
144 println!("ERROR: {}", error);
145 std::process::exit(1);
146 }
147 Ok(json) => {
148 if parser.is_at_eof() {
151 println!("{:?}", json);
153 std::process::exit(0)
154 } else {
155 println!("Found unexpected trailing characters after JSON value.");
156 std::process::exit(1);
157 }
158 }
159 }
160}