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