aboutsummaryrefslogtreecommitdiff
path: root/src/parser/value.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/parser/value.rs')
-rw-r--r--src/parser/value.rs155
1 files changed, 155 insertions, 0 deletions
diff --git a/src/parser/value.rs b/src/parser/value.rs
new file mode 100644
index 0000000..14cd951
--- /dev/null
+++ b/src/parser/value.rs
@@ -0,0 +1,155 @@
+use winnow::combinator::alt;
+use winnow::combinator::fail;
+use winnow::combinator::peek;
+use winnow::token::any;
+
+use crate::parser::array::array;
+use crate::parser::datetime::date_time;
+use crate::parser::inline_table::inline_table;
+use crate::parser::numbers::{float, integer};
+use crate::parser::prelude::*;
+use crate::parser::strings::string;
+use crate::repr::{Formatted, Repr};
+use crate::value as v;
+use crate::RawString;
+use crate::Value;
+
+// val = string / boolean / array / inline-table / date-time / float / integer
+pub(crate) fn value<'i>(check: RecursionCheck) -> impl Parser<Input<'i>, v::Value, ContextError> {
+ move |input: &mut Input<'i>| {
+ dispatch!{peek(any);
+ crate::parser::strings::QUOTATION_MARK |
+ crate::parser::strings::APOSTROPHE => string.map(|s| {
+ v::Value::String(Formatted::new(
+ s.into_owned()
+ ))
+ }),
+ crate::parser::array::ARRAY_OPEN => array(check).map(v::Value::Array),
+ crate::parser::inline_table::INLINE_TABLE_OPEN => inline_table(check).map(v::Value::InlineTable),
+ // Date/number starts
+ b'+' | b'-' | b'0'..=b'9' => {
+ // Uncommon enough not to be worth optimizing at this time
+ alt((
+ date_time
+ .map(v::Value::from),
+ float
+ .map(v::Value::from),
+ integer
+ .map(v::Value::from),
+ ))
+ },
+ // Report as if they were numbers because its most likely a typo
+ b'_' => {
+ integer
+ .map(v::Value::from)
+ .context(StrContext::Expected(StrContextValue::Description("leading digit")))
+ },
+ // Report as if they were numbers because its most likely a typo
+ b'.' => {
+ float
+ .map(v::Value::from)
+ .context(StrContext::Expected(StrContextValue::Description("leading digit")))
+ },
+ b't' => {
+ crate::parser::numbers::true_.map(v::Value::from)
+ .context(StrContext::Label("string"))
+ .context(StrContext::Expected(StrContextValue::CharLiteral('"')))
+ .context(StrContext::Expected(StrContextValue::CharLiteral('\'')))
+ },
+ b'f' => {
+ crate::parser::numbers::false_.map(v::Value::from)
+ .context(StrContext::Label("string"))
+ .context(StrContext::Expected(StrContextValue::CharLiteral('"')))
+ .context(StrContext::Expected(StrContextValue::CharLiteral('\'')))
+ },
+ b'i' => {
+ crate::parser::numbers::inf.map(v::Value::from)
+ .context(StrContext::Label("string"))
+ .context(StrContext::Expected(StrContextValue::CharLiteral('"')))
+ .context(StrContext::Expected(StrContextValue::CharLiteral('\'')))
+ },
+ b'n' => {
+ crate::parser::numbers::nan.map(v::Value::from)
+ .context(StrContext::Label("string"))
+ .context(StrContext::Expected(StrContextValue::CharLiteral('"')))
+ .context(StrContext::Expected(StrContextValue::CharLiteral('\'')))
+ },
+ _ => {
+ fail
+ .context(StrContext::Label("string"))
+ .context(StrContext::Expected(StrContextValue::CharLiteral('"')))
+ .context(StrContext::Expected(StrContextValue::CharLiteral('\'')))
+ },
+ }
+ .with_span()
+ .try_map(|(value, span)| apply_raw(value, span))
+ .parse_next(input)
+ }
+}
+
+fn apply_raw(mut val: Value, span: std::ops::Range<usize>) -> Result<Value, std::str::Utf8Error> {
+ match val {
+ Value::String(ref mut f) => {
+ let raw = RawString::with_span(span);
+ f.set_repr_unchecked(Repr::new_unchecked(raw));
+ }
+ Value::Integer(ref mut f) => {
+ let raw = RawString::with_span(span);
+ f.set_repr_unchecked(Repr::new_unchecked(raw));
+ }
+ Value::Float(ref mut f) => {
+ let raw = RawString::with_span(span);
+ f.set_repr_unchecked(Repr::new_unchecked(raw));
+ }
+ Value::Boolean(ref mut f) => {
+ let raw = RawString::with_span(span);
+ f.set_repr_unchecked(Repr::new_unchecked(raw));
+ }
+ Value::Datetime(ref mut f) => {
+ let raw = RawString::with_span(span);
+ f.set_repr_unchecked(Repr::new_unchecked(raw));
+ }
+ Value::Array(ref mut arr) => {
+ arr.span = Some(span);
+ }
+ Value::InlineTable(ref mut table) => {
+ table.span = Some(span);
+ }
+ };
+ val.decorate("", "");
+ Ok(val)
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+
+ #[test]
+ fn values() {
+ let inputs = [
+ "1979-05-27T00:32:00.999999",
+ "-239",
+ "1e200",
+ "9_224_617.445_991_228_313",
+ r#"'''I [dw]on't need \d{2} apples'''"#,
+ r#"'''
+The first newline is
+trimmed in raw strings.
+ All other whitespace
+ is preserved.
+'''"#,
+ r#""Jos\u00E9\n""#,
+ r#""\\\"\b/\f\n\r\t\u00E9\U000A0000""#,
+ r#"{ hello = "world", a = 1}"#,
+ r#"[ { x = 1, a = "2" }, {a = "a",b = "b", c = "c"} ]"#,
+ ];
+ for input in inputs {
+ dbg!(input);
+ let mut parsed = value(Default::default()).parse(new_input(input));
+ if let Ok(parsed) = &mut parsed {
+ parsed.despan(input);
+ }
+ assert_eq!(parsed.map(|a| a.to_string()), Ok(input.to_owned()));
+ }
+ }
+}