1 use winnow::combinator::cut_err;
2 use winnow::combinator::delimited;
3 use winnow::combinator::separated0;
4 use winnow::token::one_of;
5 use winnow::trace::trace;
6
7 use crate::key::Key;
8 use crate::parser::error::CustomError;
9 use crate::parser::key::key;
10 use crate::parser::prelude::*;
11 use crate::parser::trivia::ws;
12 use crate::parser::value::value;
13 use crate::table::TableKeyValue;
14 use crate::{InlineTable, InternalString, Item, RawString, Value};
15
16 use indexmap::map::Entry;
17
18 // ;; Inline Table
19
20 // inline-table = inline-table-open inline-table-keyvals inline-table-close
inline_table<'i>( check: RecursionCheck, ) -> impl Parser<Input<'i>, InlineTable, ContextError>21 pub(crate) fn inline_table<'i>(
22 check: RecursionCheck,
23 ) -> impl Parser<Input<'i>, InlineTable, ContextError> {
24 trace("inline-table", move |input: &mut Input<'i>| {
25 delimited(
26 INLINE_TABLE_OPEN,
27 cut_err(inline_table_keyvals(check).try_map(|(kv, p)| table_from_pairs(kv, p))),
28 cut_err(INLINE_TABLE_CLOSE)
29 .context(StrContext::Label("inline table"))
30 .context(StrContext::Expected(StrContextValue::CharLiteral('}'))),
31 )
32 .parse_next(input)
33 })
34 }
35
table_from_pairs( v: Vec<(Vec<Key>, TableKeyValue)>, preamble: RawString, ) -> Result<InlineTable, CustomError>36 fn table_from_pairs(
37 v: Vec<(Vec<Key>, TableKeyValue)>,
38 preamble: RawString,
39 ) -> Result<InlineTable, CustomError> {
40 let mut root = InlineTable::new();
41 root.set_preamble(preamble);
42 // Assuming almost all pairs will be directly in `root`
43 root.items.reserve(v.len());
44
45 for (path, kv) in v {
46 let table = descend_path(&mut root, &path)?;
47
48 // "Likewise, using dotted keys to redefine tables already defined in [table] form is not allowed"
49 let mixed_table_types = table.is_dotted() == path.is_empty();
50 if mixed_table_types {
51 return Err(CustomError::DuplicateKey {
52 key: kv.key.get().into(),
53 table: None,
54 });
55 }
56
57 let key: InternalString = kv.key.get_internal().into();
58 match table.items.entry(key) {
59 Entry::Vacant(o) => {
60 o.insert(kv);
61 }
62 Entry::Occupied(o) => {
63 return Err(CustomError::DuplicateKey {
64 key: o.key().as_str().into(),
65 table: None,
66 });
67 }
68 }
69 }
70 Ok(root)
71 }
72
descend_path<'a>( mut table: &'a mut InlineTable, path: &'a [Key], ) -> Result<&'a mut InlineTable, CustomError>73 fn descend_path<'a>(
74 mut table: &'a mut InlineTable,
75 path: &'a [Key],
76 ) -> Result<&'a mut InlineTable, CustomError> {
77 let dotted = !path.is_empty();
78 for (i, key) in path.iter().enumerate() {
79 let entry = table.entry_format(key).or_insert_with(|| {
80 let mut new_table = InlineTable::new();
81 new_table.set_implicit(dotted);
82 new_table.set_dotted(dotted);
83
84 Value::InlineTable(new_table)
85 });
86 match *entry {
87 Value::InlineTable(ref mut sweet_child_of_mine) => {
88 // Since tables cannot be defined more than once, redefining such tables using a
89 // [table] header is not allowed. Likewise, using dotted keys to redefine tables
90 // already defined in [table] form is not allowed.
91 if dotted && !sweet_child_of_mine.is_implicit() {
92 return Err(CustomError::DuplicateKey {
93 key: key.get().into(),
94 table: None,
95 });
96 }
97 table = sweet_child_of_mine;
98 }
99 ref v => {
100 return Err(CustomError::extend_wrong_type(path, i, v.type_name()));
101 }
102 }
103 }
104 Ok(table)
105 }
106
107 // inline-table-open = %x7B ws ; {
108 pub(crate) const INLINE_TABLE_OPEN: u8 = b'{';
109 // inline-table-close = ws %x7D ; }
110 const INLINE_TABLE_CLOSE: u8 = b'}';
111 // inline-table-sep = ws %x2C ws ; , Comma
112 const INLINE_TABLE_SEP: u8 = b',';
113 // keyval-sep = ws %x3D ws ; =
114 pub(crate) const KEYVAL_SEP: u8 = b'=';
115
116 // inline-table-keyvals = [ inline-table-keyvals-non-empty ]
117 // inline-table-keyvals-non-empty =
118 // ( key keyval-sep val inline-table-sep inline-table-keyvals-non-empty ) /
119 // ( key keyval-sep val )
120
inline_table_keyvals<'i>( check: RecursionCheck, ) -> impl Parser<Input<'i>, (Vec<(Vec<Key>, TableKeyValue)>, RawString), ContextError>121 fn inline_table_keyvals<'i>(
122 check: RecursionCheck,
123 ) -> impl Parser<Input<'i>, (Vec<(Vec<Key>, TableKeyValue)>, RawString), ContextError> {
124 move |input: &mut Input<'i>| {
125 let check = check.recursing(input)?;
126 (
127 separated0(keyval(check), INLINE_TABLE_SEP),
128 ws.span().map(RawString::with_span),
129 )
130 .parse_next(input)
131 }
132 }
133
keyval<'i>( check: RecursionCheck, ) -> impl Parser<Input<'i>, (Vec<Key>, TableKeyValue), ContextError>134 fn keyval<'i>(
135 check: RecursionCheck,
136 ) -> impl Parser<Input<'i>, (Vec<Key>, TableKeyValue), ContextError> {
137 move |input: &mut Input<'i>| {
138 (
139 key,
140 cut_err((
141 one_of(KEYVAL_SEP)
142 .context(StrContext::Expected(StrContextValue::CharLiteral('.')))
143 .context(StrContext::Expected(StrContextValue::CharLiteral('='))),
144 (ws.span(), value(check), ws.span()),
145 )),
146 )
147 .map(|(key, (_, v))| {
148 let mut path = key;
149 let key = path.pop().expect("grammar ensures at least 1");
150
151 let (pre, v, suf) = v;
152 let pre = RawString::with_span(pre);
153 let suf = RawString::with_span(suf);
154 let v = v.decorated(pre, suf);
155 (
156 path,
157 TableKeyValue {
158 key,
159 value: Item::Value(v),
160 },
161 )
162 })
163 .parse_next(input)
164 }
165 }
166
167 #[cfg(test)]
168 #[cfg(feature = "parse")]
169 #[cfg(feature = "display")]
170 mod test {
171 use super::*;
172
173 #[test]
inline_tables()174 fn inline_tables() {
175 let inputs = [
176 r#"{}"#,
177 r#"{ }"#,
178 r#"{a = 1e165}"#,
179 r#"{ hello = "world", a = 1}"#,
180 r#"{ hello.world = "a" }"#,
181 ];
182 for input in inputs {
183 dbg!(input);
184 let mut parsed = inline_table(Default::default()).parse(new_input(input));
185 if let Ok(parsed) = &mut parsed {
186 parsed.despan(input);
187 }
188 assert_eq!(parsed.map(|a| a.to_string()), Ok(input.to_owned()));
189 }
190 }
191
192 #[test]
invalid_inline_tables()193 fn invalid_inline_tables() {
194 let invalid_inputs = [r#"{a = 1e165"#, r#"{ hello = "world", a = 2, hello = 1}"#];
195 for input in invalid_inputs {
196 dbg!(input);
197 let mut parsed = inline_table(Default::default()).parse(new_input(input));
198 if let Ok(parsed) = &mut parsed {
199 parsed.despan(input);
200 }
201 assert!(parsed.is_err());
202 }
203 }
204 }
205