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