1use crate::pest::Parser;
4use pest::iterators::Pair;
5use std::collections::HashMap;
6
7use super::value;
8
9#[allow(clippy::upper_case_acronyms)]
10#[derive(Parser)]
11#[grammar = "parser.pest"]
12struct YojsonParser;
13
14pub fn parse(text: &str) -> Result<value::Value, pest::error::Error<Rule>> {
15 let json = YojsonParser::parse(Rule::json, text)?.next().unwrap();
16 let value = parse_value(json);
17 Ok(value)
18}
19
20fn parse_value(pair: Pair<Rule>) -> value::Value {
21 match pair.as_rule() {
22 Rule::null => value::Value::Null,
23 Rule::bool => value::Value::Bool(pair.as_str().parse().unwrap()),
24 Rule::integer => value::Value::Integer(pair.as_str().parse().unwrap()),
25 Rule::float => {
26 let str = pair.as_str();
27 let f = if str == "NaN" {
28 f64::NAN
29 } else if str == "Infinity" {
30 f64::INFINITY
31 } else if str == "-Infinity" {
32 f64::NEG_INFINITY
33 } else {
34 str.parse().unwrap()
35 };
36 value::Value::Float(f)
37 }
38 Rule::string => {
39 let str = parse_string(pair.into_inner().next().unwrap());
40 value::Value::String(str)
41 }
42 Rule::assoc => {
43 let mut assoc = HashMap::new();
44 pair.into_inner().for_each(|pair| {
45 let mut inner_rules = pair.into_inner();
46 let name = parse_name(inner_rules.next().unwrap());
47 let value = parse_value(inner_rules.next().unwrap());
48 assoc.insert(name, value);
49 });
50 value::Value::Assoc(assoc)
51 }
52 Rule::array => value::Value::Array(pair.into_inner().map(parse_value).collect()),
53 Rule::tuple => value::Value::Tuple(pair.into_inner().map(parse_value).collect()),
54 Rule::variant => {
55 let mut inner_rules = pair.into_inner();
56 let name = parse_name(inner_rules.next().unwrap());
57 let value = match inner_rules.next() {
58 None => None,
59 Some(rule) => Some(Box::new(parse_value(rule))),
60 };
61 let variant = (name, value);
62 value::Value::Variant(variant)
63 }
64 Rule::json
65 | Rule::EOI
66 | Rule::pair
67 | Rule::value
68 | Rule::inner
69 | Rule::name
70 | Rule::ascii_inner
71 | Rule::quoted_string
72 | Rule::c
73 | Rule::escape_char
74 | Rule::unicode_char
75 | Rule::unquoted_string
76 | Rule::ascii_char
77 | Rule::WHITESPACE
78 | Rule::COMMENT => unreachable!(),
79 }
80}
81
82fn parse_name(pair: Pair<Rule>) -> String {
83 let mut inner_rules = pair.into_inner().peekable();
84 let str = match inner_rules.peek().unwrap().as_rule() {
85 Rule::ascii_inner => inner_rules.next().unwrap().as_str().to_string(),
86 Rule::inner => {
87 let mut s = String::new();
88 for pair in inner_rules.next().unwrap().into_inner() {
89 match pair.as_rule() {
90 Rule::c => s.push_str(pair.as_str()),
91 Rule::escape_char => {
92 let c = match pair.as_str().chars().nth(1).unwrap() {
93 '"' => '\"',
94 '\\' => '\\',
95 '/' => '/',
96 'b' => '\u{0008}', 'f' => '\u{000c}', 'n' => '\n',
99 'r' => '\r',
100 't' => '\t',
101 _ => unreachable!(),
102 };
103 s.push(c)
104 }
105 Rule::unicode_char => {
106 let hex = pair.as_str().chars().skip(2).collect::<String>();
107 let hex_i64 = i64::from_str_radix(&hex, 16).unwrap();
108 let str = String::from_utf8(vec![hex_i64 as u8]).unwrap();
109 s.push_str(&str)
110 }
111 _ => break,
112 }
113 }
114 s
115 }
116 _ => unreachable!(),
117 };
118 str
119}
120
121fn parse_string(pair: Pair<Rule>) -> String {
122 let mut s = String::new();
123 let inner_rules = pair.into_inner();
124 for pair in inner_rules {
125 match pair.as_rule() {
126 Rule::c => s.push_str(pair.as_str()),
127 Rule::escape_char => {
128 let c = match pair.as_str().chars().nth(1).unwrap() {
129 '"' => '\"',
130 '\\' => '\\',
131 '/' => '/',
132 'b' => '\u{0008}', 'f' => '\u{000c}', 'n' => '\n',
135 'r' => '\r',
136 't' => '\t',
137 _ => unreachable!(),
138 };
139 s.push(c)
140 }
141 Rule::unicode_char => {
142 let hex = pair.as_str().chars().skip(2).collect::<String>();
143 let hex_i64 = i64::from_str_radix(&hex, 16).unwrap();
144 let str = String::from_utf8(vec![hex_i64 as u8]).unwrap();
145 s.push_str(&str)
146 }
147 _ => break,
148 }
149 }
150 s
151}