1#![allow(clippy::enum_variant_names)]
17
18use super::string_helpers;
19use crate::prelude::*;
20use crate::value_type::{BitSequence, Composite, Primitive, Value, Variant};
21use core::num::ParseIntError;
22use yap::{types::StrTokens, IntoTokens, TokenLocation, Tokens};
23
24pub struct FromStrBuilder {
28 custom_parsers: Vec<CustomParser>,
29}
30
31type CustomParser = Box<dyn Fn(&mut &str) -> Option<Result<Value<()>, ParseError>> + 'static>;
32
33impl FromStrBuilder {
34 pub(crate) fn new() -> Self {
35 FromStrBuilder { custom_parsers: Vec::new() }
36 }
37
38 pub fn add_custom_parser<F>(mut self, f: F) -> Self
51 where
52 F: Fn(&mut &str) -> Option<Result<Value<()>, ParseError>> + 'static,
53 {
54 self.custom_parsers.push(Box::new(f));
55 self
56 }
57
58 pub fn parse<'a>(&self, s: &'a str) -> (Result<Value<()>, ParseError>, &'a str) {
61 let mut tokens = s.into_tokens();
62 let res = parse_value(&mut tokens, &self.custom_parsers);
63 let remaining = tokens.remaining();
64 (res, remaining)
65 }
66}
67
68#[derive(Debug, PartialEq, Eq)]
70pub struct ParseError {
71 pub start_loc: usize,
73 pub end_loc: Option<usize>,
76 pub err: ParseErrorKind,
78}
79
80impl core::error::Error for ParseError {}
81
82impl ParseError {
83 pub fn new_at<E: Into<ParseErrorKind>>(err: E, loc: usize) -> Self {
85 Self { start_loc: loc, end_loc: None, err: err.into() }
86 }
87 pub fn new_between<E: Into<ParseErrorKind>>(err: E, start: usize, end: usize) -> Self {
89 Self { start_loc: start, end_loc: Some(end), err: err.into() }
90 }
91}
92
93impl core::fmt::Display for ParseError {
94 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
95 if let Some(end_loc) = self.end_loc {
96 write!(f, "Error from char {} to {}: {}", self.start_loc, end_loc, self.err)
97 } else {
98 write!(f, "Error at char {}: {}", self.start_loc, self.err)
99 }
100 }
101}
102
103macro_rules! at_between {
105 ($ty:ident) => {
106 impl $ty {
107 pub fn at(self, loc: usize) -> ParseError {
109 ParseError::new_at(self, loc)
110 }
111 pub fn at_one(self, loc: usize) -> ParseError {
113 ParseError::new_between(self, loc, loc + 1)
114 }
115 pub fn between(self, start: usize, end: usize) -> ParseError {
117 ParseError::new_between(self, start, end)
118 }
119 }
120 };
121}
122
123#[derive(Debug, PartialEq, Eq, thiserror::Error)]
125#[allow(missing_docs)]
126pub enum ParseErrorKind {
127 #[error("Expected a value")]
128 ExpectedValue,
129 #[error(transparent)]
130 Complex(#[from] ParseComplexError),
131 #[error(transparent)]
132 Char(#[from] ParseCharError),
133 #[error(transparent)]
134 String(#[from] ParseStringError),
135 #[error(transparent)]
136 Number(#[from] ParseNumberError),
137 #[error(transparent)]
138 BitSequence(#[from] ParseBitSequenceError),
139 #[error("Custom error: {0}")]
140 Custom(String),
141}
142at_between!(ParseErrorKind);
143
144impl ParseErrorKind {
145 pub fn custom<T: core::fmt::Display>(value: T) -> Self {
147 ParseErrorKind::Custom(format!("{value}"))
148 }
149
150 pub fn custom_debug<T: core::fmt::Debug>(value: T) -> Self {
153 ParseErrorKind::Custom(format!("{value:?}"))
154 }
155}
156
157impl From<String> for ParseErrorKind {
158 fn from(s: String) -> Self {
159 ParseErrorKind::Custom(s)
160 }
161}
162
163#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
164#[allow(missing_docs)]
165pub enum ParseComplexError {
166 #[error("The first character in a field name should be alphabetic")]
167 InvalidStartingCharacterInIdent,
168 #[error(
169 "Field name is not valid (it should begin with an alphabetical char and then consist only of alphanumeric chars)"
170 )]
171 InvalidFieldName,
172 #[error("Missing field separator; expected {0}")]
173 MissingFieldSeparator(char),
174 #[error("Missing closing '{0}'")]
175 ExpectedCloserToMatch(char, usize),
176}
177at_between!(ParseComplexError);
178
179#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
180#[allow(missing_docs)]
181pub enum ParseCharError {
182 #[error("Expected a single character")]
183 ExpectedValidCharacter,
184 #[error("Expected an escape code to follow the '\\'")]
185 ExpectedValidEscapeCode,
186 #[error("Expected a closing quote to match the opening quote at position {0}")]
187 ExpectedClosingQuoteToMatch(usize),
188}
189at_between!(ParseCharError);
190
191#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
192#[allow(missing_docs)]
193pub enum ParseStringError {
194 #[error("Expected a closing quote to match the opening quote at position {0}")]
195 ExpectedClosingQuoteToMatch(usize),
196 #[error("Expected an escape code to follow the '\\'")]
197 ExpectedValidEscapeCode,
198}
199at_between!(ParseStringError);
200
201#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
202#[allow(missing_docs)]
203pub enum ParseNumberError {
204 #[error("Expected one or more digits")]
205 ExpectedDigit,
206 #[error("Failed to parse digits into an integer: {0}")]
207 ParsingFailed(#[from] ParseIntError),
208}
209at_between!(ParseNumberError);
210
211#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
212#[allow(missing_docs)]
213pub enum ParseBitSequenceError {
214 #[error("Expected a closing bracket ('>') to match the opening one at position {_0}")]
215 ExpectedClosingBracketToMatch(usize),
216 #[error("Invalid character; expecting a 0 or 1")]
217 InvalidCharacter,
218}
219at_between!(ParseBitSequenceError);
220
221fn parse_value(
223 t: &mut StrTokens,
224 custom_parsers: &[CustomParser],
225) -> Result<Value<()>, ParseError> {
226 if !custom_parsers.is_empty() {
228 let s = t.remaining();
229 let start_offset = t.offset();
230 let cursor = &mut &*s;
231
232 for parser in custom_parsers {
233 if let Some(res) = parser(cursor) {
234 match res {
235 Ok(value) => {
236 for _ in cursor.len()..s.len() {
240 t.next();
241 }
242 return Ok(value);
243 }
244 Err(e) => {
245 return Err(ParseError {
248 start_loc: start_offset + e.start_loc,
249 end_loc: e.end_loc.map(|l| start_offset + l),
250 err: e.err,
251 });
252 }
253 }
254 }
255 }
256 }
257
258 let val = yap::one_of!(t;
262 transpose_err(parse_bool(t).map(Value::bool).ok_or(None)),
263 transpose_err(parse_char(t).map(Value::char)),
264 transpose_err(parse_string(t).map(Value::string)),
265 transpose_err(parse_number(t).map(Value::primitive)),
266 transpose_err(parse_named_composite(t, custom_parsers).map(|v| v.into())),
267 transpose_err(parse_unnamed_composite(t, custom_parsers).map(|v| v.into())),
268 transpose_err(parse_bit_sequence(t).map(Value::bit_sequence)),
269 transpose_err(parse_variant(t, custom_parsers).map(|v| v.into())),
270 );
271
272 match val {
273 Some(Ok(val)) => Ok(val),
274 Some(Err(e)) => Err(e),
275 None => Err(ParseError::new_at(ParseErrorKind::ExpectedValue, t.offset())),
276 }
277}
278
279fn parse_named_composite(
286 t: &mut StrTokens,
287 custom_parsers: &[CustomParser],
288) -> Result<Composite<()>, Option<ParseError>> {
289 let start = t.offset();
290 if !t.token('{') {
291 return Err(None);
292 }
293 skip_whitespace(t);
294
295 if t.token('}') {
297 return Ok(Composite::Named(vec![]));
298 }
299
300 let vals = t
301 .sep_by_err(
302 |t| parse_field_name_and_value(t, custom_parsers),
303 |t| skip_spaced_separator(t, ','),
304 )
305 .collect::<Result<_, _>>()?;
306
307 skip_whitespace(t);
308 if !t.token('}') {
309 return Err(Some(ParseComplexError::ExpectedCloserToMatch('}', start).at_one(t.offset())));
310 }
311 Ok(Composite::Named(vals))
312}
313
314fn parse_unnamed_composite(
316 t: &mut StrTokens,
317 custom_parsers: &[CustomParser],
318) -> Result<Composite<()>, Option<ParseError>> {
319 let start = t.offset();
320 if !t.token('(') {
321 return Err(None);
322 }
323 skip_whitespace(t);
324
325 if t.token(')') {
327 return Ok(Composite::Unnamed(vec![]));
328 }
329
330 let vals = t
331 .sep_by_err(|t| parse_value(t, custom_parsers), |t| skip_spaced_separator(t, ','))
332 .collect::<Result<_, _>>()?;
333
334 skip_whitespace(t);
335 if !t.token(')') {
336 return Err(Some(ParseComplexError::ExpectedCloserToMatch(')', start).at_one(t.offset())));
337 }
338 Ok(Composite::Unnamed(vals))
339}
340
341fn parse_variant(
343 t: &mut StrTokens,
344 custom_parsers: &[CustomParser],
345) -> Result<Variant<()>, Option<ParseError>> {
346 let ident = match parse_optional_variant_ident(t) {
347 Some(ident) => ident,
348 None => return Err(None),
349 };
350
351 skip_whitespace(t);
352
353 let composite = yap::one_of!(t;
354 transpose_err(parse_named_composite(t, custom_parsers)),
355 transpose_err(parse_unnamed_composite(t, custom_parsers))
356 );
357
358 match composite {
359 Some(Ok(values)) => Ok(Variant { name: ident, values }),
360 Some(Err(e)) => Err(Some(e)),
361 None => Err(None),
362 }
363}
364
365fn parse_bit_sequence(t: &mut StrTokens) -> Result<BitSequence, Option<ParseError>> {
367 let start = t.offset();
368 if !t.token('<') {
369 return Err(None);
370 }
371 let bits = t.take_while(|&c| c == '0' || c == '1').into_iter().map(|c| c == '1');
372 let mut seq = BitSequence::new();
373 for bit in bits {
374 seq.push(bit);
375 }
376 if !t.token('>') {
377 return Err(Some(
378 ParseBitSequenceError::ExpectedClosingBracketToMatch(start)
379 .between(t.offset(), t.offset() + 1),
380 ));
381 }
382 Ok(seq)
383}
384
385fn parse_bool(t: &mut StrTokens) -> Option<bool> {
387 if t.tokens("true".chars()) {
388 Some(true)
389 } else if t.tokens("false".chars()) {
390 Some(false)
391 } else {
392 None
393 }
394}
395
396fn parse_char(t: &mut StrTokens) -> Result<char, Option<ParseError>> {
398 let start = t.offset();
399 if !t.token('\'') {
400 return Err(None);
401 }
402 let char = match t.next() {
403 None => return Err(Some(ParseCharError::ExpectedValidCharacter.at_one(t.offset()))),
404 Some(c) => c,
405 };
406
407 let char = if char == '\\' {
410 let escape_code = match t.next() {
411 None => return Err(Some(ParseCharError::ExpectedValidEscapeCode.at_one(t.offset()))),
412 Some(c) => c,
413 };
414 match string_helpers::from_escape_code(escape_code) {
415 None => return Err(Some(ParseCharError::ExpectedValidEscapeCode.at_one(t.offset()))),
416 Some(c) => c,
417 }
418 } else {
419 char
420 };
421
422 if !t.token('\'') {
423 return Err(Some(ParseCharError::ExpectedClosingQuoteToMatch(start).at_one(t.offset())));
424 }
425 Ok(char)
426}
427
428fn parse_number(t: &mut StrTokens) -> Result<Primitive, Option<ParseError>> {
430 let start_loc = t.offset();
431 let is_positive = t.token('+') || !t.token('-');
432
433 let sign = if is_positive { "".chars() } else { "-".chars() };
435
436 let mut seen_n = false;
438 let digits = t
439 .take_while(|c| {
440 if c.is_ascii_digit() {
441 seen_n = true;
442 true
443 } else {
444 seen_n && *c == '_'
445 }
446 })
447 .into_iter()
448 .filter(|c| c.is_ascii_digit());
449
450 let n_str: String = sign.chain(digits).collect();
452 let end_loc = t.offset();
453
454 if end_loc == start_loc {
456 return Err(None);
457 }
458
459 if !seen_n {
461 return Err(Some(ParseNumberError::ExpectedDigit.between(end_loc, end_loc + 1)));
462 }
463
464 if is_positive {
466 n_str
467 .parse::<u128>()
468 .map(Primitive::u128)
469 .map_err(|e| Some(ParseNumberError::ParsingFailed(e).between(start_loc, end_loc)))
470 } else {
471 n_str
472 .parse::<i128>()
473 .map(Primitive::i128)
474 .map_err(|e| Some(ParseNumberError::ParsingFailed(e).between(start_loc, end_loc)))
475 }
476}
477
478fn parse_string(t: &mut StrTokens) -> Result<String, Option<ParseError>> {
480 let start = t.offset();
481 if !t.token('"') {
482 return Err(None);
483 }
484
485 let mut out: String = String::new();
486 let mut next_is_escaped = false;
487 loop {
488 let pos = t.offset();
489 let char = match t.next() {
490 Some(c) => c,
491 None => {
492 return Err(Some(
493 ParseStringError::ExpectedClosingQuoteToMatch(start).at_one(t.offset()),
494 ))
495 }
496 };
497
498 match char {
499 '\\' if !next_is_escaped => {
501 next_is_escaped = true;
502 }
503 c if next_is_escaped => match string_helpers::from_escape_code(c) {
505 Some(c) => {
506 out.push(c);
507 next_is_escaped = false;
508 }
509 None => {
510 return Err(Some(
511 ParseStringError::ExpectedValidEscapeCode.between(pos, pos + 1),
512 ))
513 }
514 },
515 '"' => {
517 break; }
519 c => {
521 out.push(c);
522 }
523 }
524 }
525
526 Ok(out)
527}
528
529fn parse_field_name_and_value(
531 t: &mut StrTokens,
532 custom_parsers: &[CustomParser],
533) -> Result<(String, Value<()>), ParseError> {
534 let name = parse_field_name(t)?;
535 if !skip_spaced_separator(t, ':') {
536 return Err(ParseComplexError::MissingFieldSeparator(':').at_one(t.offset()));
537 }
538 let value = parse_value(t, custom_parsers)?;
539 Ok((name, value))
540}
541
542fn parse_field_name(t: &mut StrTokens) -> Result<String, ParseError> {
544 let field_name = yap::one_of!(t;
545 transpose_err(parse_string(t)),
546 Some(parse_ident(t)),
547 );
548
549 match field_name {
550 Some(Ok(name)) => Ok(name),
551 Some(Err(e)) => Err(e),
552 None => Err(ParseComplexError::InvalidFieldName.at(t.offset())),
553 }
554}
555
556fn parse_optional_variant_ident(t: &mut StrTokens) -> Option<String> {
561 fn parse_i_string(t: &mut StrTokens) -> Option<String> {
562 if t.next()? != 'v' {
563 return None;
564 }
565 parse_string(t).ok()
566 }
567
568 yap::one_of!(t;
569 parse_i_string(t),
570 parse_ident(t).ok()
571 )
572}
573
574fn parse_ident(t: &mut StrTokens) -> Result<String, ParseError> {
576 let start = t.location();
577 if t.skip_while(|c| c.is_alphabetic()) == 0 {
578 return Err(ParseComplexError::InvalidStartingCharacterInIdent.at_one(start.offset()));
579 }
580 t.skip_while(|c| c.is_alphanumeric() || *c == '_');
581 let end = t.location();
582
583 let ident_str = t.slice(start, end).as_iter().collect();
584 Ok(ident_str)
585}
586
587fn skip_whitespace(t: &mut StrTokens) {
589 t.skip_while(|c| c.is_whitespace());
590}
591
592fn skip_spaced_separator(t: &mut StrTokens, s: char) -> bool {
594 skip_whitespace(t);
595 let is_sep = t.token(s);
596 skip_whitespace(t);
597 is_sep
598}
599
600fn transpose_err<T, E>(r: Result<T, Option<E>>) -> Option<Result<T, E>> {
602 match r {
603 Ok(val) => Some(Ok(val)),
604 Err(Some(e)) => Some(Err(e)),
605 Err(None) => None,
606 }
607}
608
609#[cfg(test)]
610mod test {
611 use crate::value;
612
613 use super::*;
614
615 fn from(s: &str) -> Result<Value<()>, ParseError> {
616 let (res, remaining) = FromStrBuilder::new().parse(s);
617 match res {
618 Ok(value) => {
619 assert_eq!(remaining.len(), 0, "was not expecting any unparsed output");
620 Ok(value)
621 }
622 Err(e) => Err(e),
623 }
624 }
625
626 #[test]
627 fn parse_bools() {
628 assert_eq!(from("true"), Ok(Value::bool(true)));
629 assert_eq!(from("false"), Ok(Value::bool(false)));
630 }
631
632 #[test]
633 fn parse_numbers() {
634 assert_eq!(from("123"), Ok(Value::u128(123)));
635 assert_eq!(from("1_234_56"), Ok(Value::u128(123_456)));
636 assert_eq!(from("+1_234_56"), Ok(Value::u128(123_456)));
637 assert_eq!(from("-123_4"), Ok(Value::i128(-1234)));
638 assert_eq!(from("-abc"), Err(ParseNumberError::ExpectedDigit.between(1, 2)));
639 }
640
641 #[test]
642 fn parse_chars() {
643 assert_eq!(from("'a'"), Ok(Value::char('a')));
644 assert_eq!(from("'😀'"), Ok(Value::char('😀')));
645 assert_eq!(from("'\\n'"), Ok(Value::char('\n')));
646 assert_eq!(from("'\\t'"), Ok(Value::char('\t')));
647 assert_eq!(from("'\\\"'"), Ok(Value::char('"')));
648 assert_eq!(from("'\\\''"), Ok(Value::char('\'')));
649 assert_eq!(from("'\\r'"), Ok(Value::char('\r')));
650 assert_eq!(from("'\\\\'"), Ok(Value::char('\\')));
651 assert_eq!(from("'\\0'"), Ok(Value::char('\0')));
652 assert_eq!(from("'a"), Err(ParseCharError::ExpectedClosingQuoteToMatch(0).at_one(2)));
653 }
654
655 #[test]
656 fn parse_strings() {
657 assert_eq!(from("\"\\n \\r \\t \\0 \\\"\""), Ok(Value::string("\n \r \t \0 \"")));
658 assert_eq!(from("\"Hello there 😀\""), Ok(Value::string("Hello there 😀")));
659 assert_eq!(from("\"Hello\\n\\t there\""), Ok(Value::string("Hello\n\t there")));
660 assert_eq!(from("\"Hello\\\\ there\""), Ok(Value::string("Hello\\ there")));
661 assert_eq!(
662 from("\"Hello\\p there\""),
663 Err(ParseStringError::ExpectedValidEscapeCode.between(7, 8))
664 );
665 assert_eq!(from("\"Hi"), Err(ParseStringError::ExpectedClosingQuoteToMatch(0).at_one(3)));
666 }
667
668 #[test]
669 fn parse_unnamed_composites() {
670 assert_eq!(
671 from("( true, 1234 ,\t\n\t \"Hello!\" )"),
672 Ok(value!((true, 1234u32, "Hello!")))
673 );
674 assert_eq!(from("()"), Ok(value!(())));
675 assert_eq!(from("(\n\n\t\t\n)"), Ok(value!(())));
676 }
677
678 #[test]
679 fn parse_named_composites() {
680 assert_eq!(
681 from(
682 "{
683 hello: true,
684 foo: 1234,
685 \"Hello there 😀\": \"Hello!\"
686 }"
687 ),
688 Ok(Value::named_composite([
689 ("hello", Value::bool(true)),
690 ("foo", Value::u128(1234)),
691 ("Hello there 😀", Value::string("Hello!"))
692 ]))
693 );
694 }
695
696 #[test]
697 fn parse_variants() {
698 assert_eq!(
699 from(
700 "MyVariant {
701 hello: true,
702 foo: 1234,
703 \"Hello there 😀\": \"Hello!\"
704 }"
705 ),
706 Ok(Value::named_variant(
707 "MyVariant",
708 [
709 ("hello", Value::bool(true)),
710 ("foo", Value::u128(1234)),
711 ("Hello there 😀", Value::string("Hello!"))
712 ]
713 ))
714 );
715
716 assert_eq!(
717 from("Foo ( true, 1234 ,\t\n\t \"Hello!\" )"),
718 Ok(value!(Foo(true, 1234u32, "Hello!")))
719 );
720
721 assert_eq!(from("Foo()"), Ok(value!(Foo())));
722 assert_eq!(from("Foo{}"), Ok(value!(Foo {})));
723 assert_eq!(from("Foo( \t)"), Ok(value!(Foo())));
724 assert_eq!(from("Foo{ }"), Ok(value!(Foo {})));
725
726 assert_eq!(
728 from("v\"variant name\" { }"),
729 Ok(Value::named_variant::<_, String, _>("variant name", []))
730 );
731 }
732
733 #[test]
734 fn parse_bit_sequences() {
735 use scale_bits::bits;
736 assert_eq!(
737 from("<011010110101101>"),
738 Ok(Value::bit_sequence(bits![0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1]))
739 );
740 assert_eq!(from("<01101>"), Ok(Value::bit_sequence(bits![0, 1, 1, 0, 1])));
741 assert_eq!(from("<0>"), Ok(Value::bit_sequence(bits![0])));
742 assert_eq!(from("<>"), Ok(Value::bit_sequence(bits![])));
743 }
744
745 #[test]
746 fn custom_parsers() {
747 let custom_parser = FromStrBuilder::new()
748 .add_custom_parser(|s| {
750 let mut toks = s.into_tokens();
751
752 if !toks.tokens("0x".chars()) {
754 return None;
755 }
756
757 let from = toks.location();
758 let num_hex_chars = toks.skip_while(|c| {
759 c.is_numeric()
760 || ['a', 'b', 'c', 'd', 'e', 'f'].contains(&c.to_ascii_lowercase())
761 });
762
763 if num_hex_chars % 2 != 0 {
765 let e = ParseErrorKind::custom("Wrong number hex")
766 .between(from.offset(), toks.offset());
767 return Some(Err(e));
768 }
769
770 let hex: String = toks.slice(from, toks.location()).as_iter().collect();
772 *s = toks.remaining();
774
775 Some(Ok(Value::string(hex)))
776 });
777
778 let expected = [
779 ("(1, 0x1234, true)", (Ok(value!((1u8, "1234", true))), "")),
781 (
783 "0x12345zzz",
784 (Err(ParseErrorKind::custom("Wrong number hex").between(2, 7)), "0x12345zzz"),
785 ),
786 (
788 "(true, 0x12345)",
789 (Err(ParseErrorKind::custom("Wrong number hex").between(9, 14)), ", 0x12345)"),
790 ),
791 ];
792
793 for (s, v) in expected {
794 let (expected_res, expected_leftover) = (v.0, v.1);
795 let (res, leftover) = custom_parser.parse(s);
796 assert_eq!(res, expected_res, "result isn't what we expected for: {s}");
797 assert_eq!(leftover, expected_leftover, "wrong number of leftover bytes for: {s}");
798 }
799 }
800}