1 | use winnow::branch::alt; |
2 | use winnow::bytes::any; |
3 | use winnow::combinator::fail; |
4 | use winnow::combinator::peek; |
5 | |
6 | use crate::parser::array::array; |
7 | use crate::parser::datetime::date_time; |
8 | use crate::parser::inline_table::inline_table; |
9 | use crate::parser::numbers::{float, integer}; |
10 | use crate::parser::prelude::*; |
11 | use crate::parser::strings::string; |
12 | use crate::repr::{Formatted, Repr}; |
13 | use crate::value as v; |
14 | use crate::RawString; |
15 | use crate::Value; |
16 | |
17 | // val = string / boolean / array / inline-table / date-time / float / integer |
18 | pub(crate) fn value( |
19 | check: RecursionCheck, |
20 | ) -> impl FnMut(Input<'_>) -> IResult<Input<'_>, v::Value, ParserError<'_>> { |
21 | move |input| { |
22 | dispatch!{peek(any); |
23 | crate::parser::strings::QUOTATION_MARK | |
24 | crate::parser::strings::APOSTROPHE => string.map(|s| { |
25 | v::Value::String(Formatted::new( |
26 | s.into_owned() |
27 | )) |
28 | }), |
29 | crate::parser::array::ARRAY_OPEN => array(check).map(v::Value::Array), |
30 | crate::parser::inline_table::INLINE_TABLE_OPEN => inline_table(check).map(v::Value::InlineTable), |
31 | // Date/number starts |
32 | b'+' | b'-' | b'0' ..=b'9' => { |
33 | // Uncommon enough not to be worth optimizing at this time |
34 | alt(( |
35 | date_time |
36 | .map(v::Value::from), |
37 | float |
38 | .map(v::Value::from), |
39 | integer |
40 | .map(v::Value::from), |
41 | )) |
42 | }, |
43 | // Report as if they were numbers because its most likely a typo |
44 | b'_' => { |
45 | integer |
46 | .map(v::Value::from) |
47 | .context(Context::Expected(ParserValue::Description("leading digit" ))) |
48 | }, |
49 | // Report as if they were numbers because its most likely a typo |
50 | b'.' => { |
51 | float |
52 | .map(v::Value::from) |
53 | .context(Context::Expected(ParserValue::Description("leading digit" ))) |
54 | }, |
55 | b't' => { |
56 | crate::parser::numbers::true_.map(v::Value::from) |
57 | .context(Context::Expression("string" )) |
58 | .context(Context::Expected(ParserValue::CharLiteral('"' ))) |
59 | .context(Context::Expected(ParserValue::CharLiteral(' \'' ))) |
60 | }, |
61 | b'f' => { |
62 | crate::parser::numbers::false_.map(v::Value::from) |
63 | .context(Context::Expression("string" )) |
64 | .context(Context::Expected(ParserValue::CharLiteral('"' ))) |
65 | .context(Context::Expected(ParserValue::CharLiteral(' \'' ))) |
66 | }, |
67 | b'i' => { |
68 | crate::parser::numbers::inf.map(v::Value::from) |
69 | .context(Context::Expression("string" )) |
70 | .context(Context::Expected(ParserValue::CharLiteral('"' ))) |
71 | .context(Context::Expected(ParserValue::CharLiteral(' \'' ))) |
72 | }, |
73 | b'n' => { |
74 | crate::parser::numbers::nan.map(v::Value::from) |
75 | .context(Context::Expression("string" )) |
76 | .context(Context::Expected(ParserValue::CharLiteral('"' ))) |
77 | .context(Context::Expected(ParserValue::CharLiteral(' \'' ))) |
78 | }, |
79 | _ => { |
80 | fail |
81 | .context(Context::Expression("string" )) |
82 | .context(Context::Expected(ParserValue::CharLiteral('"' ))) |
83 | .context(Context::Expected(ParserValue::CharLiteral(' \'' ))) |
84 | }, |
85 | } |
86 | .with_span() |
87 | .try_map(|(value, span)| apply_raw(value, span)) |
88 | .parse_next(input) |
89 | } |
90 | } |
91 | |
92 | fn apply_raw(mut val: Value, span: std::ops::Range<usize>) -> Result<Value, std::str::Utf8Error> { |
93 | match val { |
94 | Value::String(ref mut f) => { |
95 | let raw = RawString::with_span(span); |
96 | f.set_repr_unchecked(Repr::new_unchecked(raw)); |
97 | } |
98 | Value::Integer(ref mut f) => { |
99 | let raw = RawString::with_span(span); |
100 | f.set_repr_unchecked(Repr::new_unchecked(raw)); |
101 | } |
102 | Value::Float(ref mut f) => { |
103 | let raw = RawString::with_span(span); |
104 | f.set_repr_unchecked(Repr::new_unchecked(raw)); |
105 | } |
106 | Value::Boolean(ref mut f) => { |
107 | let raw = RawString::with_span(span); |
108 | f.set_repr_unchecked(Repr::new_unchecked(raw)); |
109 | } |
110 | Value::Datetime(ref mut f) => { |
111 | let raw = RawString::with_span(span); |
112 | f.set_repr_unchecked(Repr::new_unchecked(raw)); |
113 | } |
114 | Value::Array(ref mut arr) => { |
115 | arr.span = Some(span); |
116 | } |
117 | Value::InlineTable(ref mut table) => { |
118 | table.span = Some(span); |
119 | } |
120 | }; |
121 | val.decorate("" , "" ); |
122 | Ok(val) |
123 | } |
124 | |
125 | #[cfg (test)] |
126 | mod test { |
127 | use super::*; |
128 | |
129 | #[test ] |
130 | fn values() { |
131 | let inputs = [ |
132 | "1979-05-27T00:32:00.999999" , |
133 | "-239" , |
134 | "1e200" , |
135 | "9_224_617.445_991_228_313" , |
136 | r#"'''I [dw]on't need \d{2} apples'''"# , |
137 | r#"''' |
138 | The first newline is |
139 | trimmed in raw strings. |
140 | All other whitespace |
141 | is preserved. |
142 | '''"# , |
143 | r#""Jos\u00E9\n""# , |
144 | r#""\\\"\b/\f\n\r\t\u00E9\U000A0000""# , |
145 | r#"{ hello = "world", a = 1}"# , |
146 | r#"[ { x = 1, a = "2" }, {a = "a",b = "b", c = "c"} ]"# , |
147 | ]; |
148 | for input in inputs { |
149 | dbg!(input); |
150 | let mut parsed = value(Default::default()).parse(new_input(input)); |
151 | if let Ok(parsed) = &mut parsed { |
152 | parsed.despan(input); |
153 | } |
154 | assert_eq!(parsed.map(|a| a.to_string()), Ok(input.to_owned())); |
155 | } |
156 | } |
157 | } |
158 | |