From e44d54999bc2180c52e73fdaa3280f5898e242a5 Mon Sep 17 00:00:00 2001 From: Linus Kirkwood Date: Mon, 29 Apr 2024 22:15:56 +1000 Subject: [PATCH 01/27] Add bare_key_chars macro to toml --- src/toml.rs | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/toml.rs b/src/toml.rs index 29bab86..db44f1c 100644 --- a/src/toml.rs +++ b/src/toml.rs @@ -10,6 +10,35 @@ use hashbrown::HashMap; #[cfg(not(feature = "no_std"))] use std::collections::HashMap; +/// Pattern matching any valid bare key character as u32. +/// ABNF line: https://github.com/toml-lang/toml/blob/2431aa308a7bc97eeb50673748606e23a6e0f201/toml.abnf#L55 +macro_rules! bare_key_chars { + () => { + 0x41..=0x5A + | 0x61..=0x7A + | 0x30..=0x39 + | 0x2D + | 0x5F + | 0xB2 + | 0xB3 + | 0xB9 + | 0xBC..=0xBE + | 0xC0..=0xD6 + | 0xD8..=0xF6 + | 0xF8..=0x37D + | 0x37F..=0x1FFF + | 0x200C..=0x200D + | 0x203F..=0x2040 + | 0x2070..=0x218F + | 0x2460..=0x24FF + | 0x2C00..=0x2FEF + | 0x3001..=0xD7FF + | 0xF900..=0xFDCF + | 0xFDF0..=0xFFFD + | 0x10000..=0xEFFFF + } +} + /// A parser for TOML string values. /// /// ```rust From 5d37f89552eb94449845705d392b110596f30b81 Mon Sep 17 00:00:00 2001 From: Linus Kirkwood Date: Mon, 29 Apr 2024 22:18:09 +1000 Subject: [PATCH 02/27] Added toml test for unquoted keys --- tests/toml.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tests/toml.rs b/tests/toml.rs index c89d919..68bae3c 100644 --- a/tests/toml.rs +++ b/tests/toml.rs @@ -1,3 +1,5 @@ +use std::collections::HashMap; + use nanoserde::Toml; use nanoserde::TomlParser; @@ -84,3 +86,20 @@ fn assert_specific_toml_types() { ] ); } + +#[test] +fn key_start_num() { + let toml_str = r#" + [table] + 1key = value + key = 1value + -0key = +nanvalue +"#; + assert_eq!( + *TomlParser::parse(toml_str).unwrap()["table"].arr(), + vec![HashMap::from([( + "1key".to_string(), + Toml::Str("value".to_string()) + ),])] + ); +} From 56e4ad6e356e1fab3edf9cf0481e4a1b33b0b292 Mon Sep 17 00:00:00 2001 From: Linus Kirkwood Date: Mon, 29 Apr 2024 22:20:37 +1000 Subject: [PATCH 03/27] Match toml chars as u32 + remove alnum match logic Now casting the current toml character to u32 for easier matching against the values provided in the abnf for toml. Removed the existing logic for parsing most characters to start again. --- src/toml.rs | 187 +++++++++++++++------------------------------------- 1 file changed, 52 insertions(+), 135 deletions(-) diff --git a/src/toml.rs b/src/toml.rs index db44f1c..ffc3e73 100644 --- a/src/toml.rs +++ b/src/toml.rs @@ -379,150 +379,29 @@ impl TomlParser { if self.cur == '\0' { return Ok(TomlTok::Eof); } - match self.cur { - ',' => { + match self.cur as u32 { + 0x2C => { + // , self.next(i); return Ok(TomlTok::Comma); } - '[' => { + 0x5B => { + // [ self.next(i); return Ok(TomlTok::BlockOpen); } - ']' => { + 0x5D => { + // ] self.next(i); return Ok(TomlTok::BlockClose); } - '=' => { + 0x3D => { + // = self.next(i); return Ok(TomlTok::Equals); } - '+' | '-' | '0'..='9' => { - let mut num = String::new(); - let is_neg = if self.cur == '-' { - num.push(self.cur); - self.next(i); - true - } else { - if self.cur == '+' { - self.next(i); - } - false - }; - if self.cur == 'n' { - self.next(i); - if self.cur == 'a' { - self.next(i); - if self.cur == 'n' { - self.next(i); - return Ok(TomlTok::Nan(is_neg)); - } else { - return Err(self.err_parse("nan")); - } - } else { - return Err(self.err_parse("nan")); - } - } - if self.cur == 'i' { - self.next(i); - if self.cur == 'n' { - self.next(i); - if self.cur == 'f' { - self.next(i); - return Ok(TomlTok::Inf(is_neg)); - } else { - return Err(self.err_parse("inf")); - } - } else { - return Err(self.err_parse("nan")); - } - } - while self.cur >= '0' && self.cur <= '9' || self.cur == '_' { - if self.cur != '_' { - num.push(self.cur); - } - self.next(i); - } - if self.cur == '.' { - num.push(self.cur); - self.next(i); - while self.cur >= '0' && self.cur <= '9' || self.cur == '_' { - if self.cur != '_' { - num.push(self.cur); - } - self.next(i); - } - if let Ok(num) = num.parse() { - return Ok(TomlTok::F64(num)); - } else { - return Err(self.err_parse("number")); - } - } else if self.cur == '-' { - // lets assume its a date. whatever. i don't feel like more parsing today - num.push(self.cur); - self.next(i); - while self.cur >= '0' && self.cur <= '9' - || self.cur == ':' - || self.cur == '-' - || self.cur == 'T' - { - num.push(self.cur); - self.next(i); - } - return Ok(TomlTok::Date(num)); - } else { - if is_neg { - if let Ok(num) = num.parse() { - return Ok(TomlTok::I64(num)); - } else { - return Err(self.err_parse("number")); - } - } - if let Ok(num) = num.parse() { - return Ok(TomlTok::U64(num)); - } else { - return Err(self.err_parse("number")); - } - } - } - 'a'..='z' | 'A'..='Z' | '_' => { - let mut ident = String::new(); - while self.cur >= 'a' && self.cur <= 'z' - || self.cur >= 'A' && self.cur <= 'Z' - || self.cur == '_' - || self.cur == '-' - { - ident.push(self.cur); - self.next(i); - } - if self.cur == '.' { - while self.cur == '.' { - self.next(i); - while self.cur >= 'a' && self.cur <= 'z' - || self.cur >= 'A' && self.cur <= 'Z' - || self.cur == '_' - || self.cur == '-' - { - ident.push(self.cur); - self.next(i); - } - } - return Ok(TomlTok::Ident(ident)); - } - if ident == "true" { - return Ok(TomlTok::Bool(true)); - } - if ident == "false" { - return Ok(TomlTok::Bool(false)); - } - if ident == "inf" { - return Ok(TomlTok::Inf(false)); - } - if ident == "nan" { - return Ok(TomlTok::Nan(false)); - } - return Ok(TomlTok::Ident(ident)); - } - '#' => { + 0x23 => { + // # while self.cur != '\n' && self.cur != '\0' { self.next(i); } @@ -535,7 +414,8 @@ impl TomlParser { self.next(i); } } - '"' => { + 0x22 => { + // " let mut val = String::new(); self.next(i); let mut braces = 1; @@ -573,10 +453,47 @@ impl TomlParser { self.next(i); return Ok(TomlTok::Str(val)); } - _ => { - return Err(self.err_parse("tokenizer")); + bare_key_chars!() => todo!("parse unquoted key"), + _ => return Err(self.err_parse("tokenizer")), + } + } + } + + fn next_ident(&mut self, i: &mut Chars, mut start: String) -> Result { + while self.cur >= 'a' && self.cur <= 'z' + || self.cur >= 'A' && self.cur <= 'Z' + || self.cur == '_' + || self.cur == '-' + { + start.push(self.cur); + self.next(i); + } + if self.cur == '.' { + while self.cur == '.' { + self.next(i); + while self.cur >= 'a' && self.cur <= 'z' + || self.cur >= 'A' && self.cur <= 'Z' + || self.cur == '_' + || self.cur == '-' + { + start.push(self.cur); + self.next(i); } } + return Ok(TomlTok::Ident(start)); + } + if start == "true" { + return Ok(TomlTok::Bool(true)); + } + if start == "false" { + return Ok(TomlTok::Bool(false)); + } + if start == "inf" { + return Ok(TomlTok::Inf(false)); + } + if start == "nan" { + return Ok(TomlTok::Nan(false)); } + return Ok(TomlTok::Ident(start)); } } From a03929820be8940e86e7d77435ece16b60553abc Mon Sep 17 00:00:00 2001 From: Linus Kirkwood Date: Mon, 29 Apr 2024 22:47:37 +1000 Subject: [PATCH 04/27] Restore original toml number parsing logic --- src/toml.rs | 89 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/src/toml.rs b/src/toml.rs index ffc3e73..bb156cf 100644 --- a/src/toml.rs +++ b/src/toml.rs @@ -414,6 +414,95 @@ impl TomlParser { self.next(i); } } + 0x2B | 0x2D | 0x30..=0x39 => { + // + - 0-9 + let mut num = String::new(); + let is_neg = if self.cur == '-' { + num.push(self.cur); + self.next(i); + true + } else { + if self.cur == '+' { + self.next(i); + } + false + }; + if self.cur == 'n' { + self.next(i); + if self.cur == 'a' { + self.next(i); + if self.cur == 'n' { + self.next(i); + return Ok(TomlTok::Nan(is_neg)); + } else { + return Err(self.err_parse("nan")); + } + } else { + return Err(self.err_parse("nan")); + } + } + if self.cur == 'i' { + self.next(i); + if self.cur == 'n' { + self.next(i); + if self.cur == 'f' { + self.next(i); + return Ok(TomlTok::Inf(is_neg)); + } else { + return Err(self.err_parse("inf")); + } + } else { + return Err(self.err_parse("nan")); + } + } + while self.cur >= '0' && self.cur <= '9' || self.cur == '_' { + if self.cur != '_' { + num.push(self.cur); + } + self.next(i); + } + if self.cur == '.' { + num.push(self.cur); + self.next(i); + while self.cur >= '0' && self.cur <= '9' || self.cur == '_' { + if self.cur != '_' { + num.push(self.cur); + } + self.next(i); + } + if let Ok(num) = num.parse() { + return Ok(TomlTok::F64(num)); + } else { + return Err(self.err_parse("number")); + } + } else if self.cur == '-' { + // lets assume its a date. whatever. i don't feel like more parsing today + num.push(self.cur); + self.next(i); + while self.cur >= '0' && self.cur <= '9' + || self.cur == ':' + || self.cur == '-' + || self.cur == 'T' + { + num.push(self.cur); + self.next(i); + } + return Ok(TomlTok::Date(num)); + } else { + if is_neg { + if let Ok(num) = num.parse() { + return Ok(TomlTok::I64(num)); + } else { + return Err(self.err_parse("number")); + } + } + if let Ok(num) = num.parse() { + return Ok(TomlTok::U64(num)); + } else { + return Err(self.err_parse("number")); + } + } + } 0x22 => { // " let mut val = String::new(); From 84a7a055b7332c491fb781f6bce976bfac057674 Mon Sep 17 00:00:00 2001 From: Linus Kirkwood Date: Mon, 29 Apr 2024 22:53:27 +1000 Subject: [PATCH 05/27] Add allow for overlapping toml char pattern lint --- src/toml.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/toml.rs b/src/toml.rs index bb156cf..c16d17b 100644 --- a/src/toml.rs +++ b/src/toml.rs @@ -379,6 +379,8 @@ impl TomlParser { if self.cur == '\0' { return Ok(TomlTok::Eof); } + + #[allow(unreachable_patterns)] match self.cur as u32 { 0x2C => { // , From c3fabf89c2b5fcf3bfddae935cbdcdf54fd4db92 Mon Sep 17 00:00:00 2001 From: Linus Kirkwood Date: Mon, 29 Apr 2024 22:54:13 +1000 Subject: [PATCH 06/27] Add toml parse_bare_key method --- src/toml.rs | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/src/toml.rs b/src/toml.rs index c16d17b..a6a47f4 100644 --- a/src/toml.rs +++ b/src/toml.rs @@ -437,10 +437,10 @@ impl TomlParser { self.next(i); return Ok(TomlTok::Nan(is_neg)); } else { - return Err(self.err_parse("nan")); + return self.parse_bare_key(i, num); } } else { - return Err(self.err_parse("nan")); + return self.parse_bare_key(i, num); } } if self.cur == 'i' { @@ -451,10 +451,10 @@ impl TomlParser { self.next(i); return Ok(TomlTok::Inf(is_neg)); } else { - return Err(self.err_parse("inf")); + return self.parse_bare_key(i, num); } } else { - return Err(self.err_parse("nan")); + return self.parse_bare_key(i, num); } } while self.cur >= '0' && self.cur <= '9' || self.cur == '_' { @@ -495,13 +495,13 @@ impl TomlParser { if let Ok(num) = num.parse() { return Ok(TomlTok::I64(num)); } else { - return Err(self.err_parse("number")); + return self.parse_bare_key(i, num); } } if let Ok(num) = num.parse() { return Ok(TomlTok::U64(num)); } else { - return Err(self.err_parse("number")); + return self.parse_bare_key(i, num); } } } @@ -544,12 +544,23 @@ impl TomlParser { self.next(i); return Ok(TomlTok::Str(val)); } - bare_key_chars!() => todo!("parse unquoted key"), + bare_key_chars!() => return self.parse_bare_key(i, String::new()), _ => return Err(self.err_parse("tokenizer")), } } } + /// Parse a bare key from the current character. + fn parse_bare_key(&mut self, i: &mut Chars, mut start: String) -> Result { + let mut val = String::new(); + while matches!(self.cur as u32, bare_key_chars!()) { + val.push(self.cur); + self.next(i); + } + + todo!("Return paths for parsing bare key") + } + fn next_ident(&mut self, i: &mut Chars, mut start: String) -> Result { while self.cur >= 'a' && self.cur <= 'z' || self.cur >= 'A' && self.cur <= 'Z' From 612ab3c68f8cc66a53508ea48b85b19d85cbc274 Mon Sep 17 00:00:00 2001 From: Linus Kirkwood Date: Tue, 30 Apr 2024 00:06:44 +1000 Subject: [PATCH 07/27] Change language from bare_key to ident in toml --- src/toml.rs | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/src/toml.rs b/src/toml.rs index a6a47f4..c061256 100644 --- a/src/toml.rs +++ b/src/toml.rs @@ -12,7 +12,7 @@ use std::collections::HashMap; /// Pattern matching any valid bare key character as u32. /// ABNF line: https://github.com/toml-lang/toml/blob/2431aa308a7bc97eeb50673748606e23a6e0f201/toml.abnf#L55 -macro_rules! bare_key_chars { +macro_rules! bare_key_ident { () => { 0x41..=0x5A | 0x61..=0x7A @@ -437,10 +437,10 @@ impl TomlParser { self.next(i); return Ok(TomlTok::Nan(is_neg)); } else { - return self.parse_bare_key(i, num); + return self.parse_ident(i, num); } } else { - return self.parse_bare_key(i, num); + return self.parse_ident(i, num); } } if self.cur == 'i' { @@ -451,10 +451,10 @@ impl TomlParser { self.next(i); return Ok(TomlTok::Inf(is_neg)); } else { - return self.parse_bare_key(i, num); + return self.parse_ident(i, num); } } else { - return self.parse_bare_key(i, num); + return self.parse_ident(i, num); } } while self.cur >= '0' && self.cur <= '9' || self.cur == '_' { @@ -495,13 +495,13 @@ impl TomlParser { if let Ok(num) = num.parse() { return Ok(TomlTok::I64(num)); } else { - return self.parse_bare_key(i, num); + return self.parse_ident(i, num); } } if let Ok(num) = num.parse() { return Ok(TomlTok::U64(num)); } else { - return self.parse_bare_key(i, num); + return self.parse_ident(i, num); } } } @@ -544,17 +544,16 @@ impl TomlParser { self.next(i); return Ok(TomlTok::Str(val)); } - bare_key_chars!() => return self.parse_bare_key(i, String::new()), + bare_key_ident!() => return self.parse_ident(i, String::new()), _ => return Err(self.err_parse("tokenizer")), } } } - /// Parse a bare key from the current character. - fn parse_bare_key(&mut self, i: &mut Chars, mut start: String) -> Result { - let mut val = String::new(); - while matches!(self.cur as u32, bare_key_chars!()) { - val.push(self.cur); + /// Parse an ident or similar, starting with the current character. + fn parse_ident(&mut self, i: &mut Chars, mut start: String) -> Result { + while matches!(self.cur as u32, bare_key_ident!()) { + start.push(self.cur); self.next(i); } From e1614288f2a43f3df0e9b6604ca6466a23cc113e Mon Sep 17 00:00:00 2001 From: Linus Kirkwood Date: Tue, 30 Apr 2024 00:59:56 +1000 Subject: [PATCH 08/27] Updated toml key test case --- tests/toml.rs | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/tests/toml.rs b/tests/toml.rs index 68bae3c..e39a9b9 100644 --- a/tests/toml.rs +++ b/tests/toml.rs @@ -90,16 +90,21 @@ fn assert_specific_toml_types() { #[test] fn key_start_num() { let toml_str = r#" - [table] - 1key = value - key = 1value - -0key = +nanvalue -"#; + [foo.bar.baz] + 1key = "myval" + -inf = 0 + 2024-04-30 = 100 + ½ = 0.5 + "#; + assert_eq!( - *TomlParser::parse(toml_str).unwrap()["table"].arr(), - vec![HashMap::from([( - "1key".to_string(), - Toml::Str("value".to_string()) - ),])] + // TODO parse dotted keys correctly (nested table) + *TomlParser::parse(toml_str).unwrap()["foo.bar.baz"].arr(), + vec![HashMap::from([ + ("1key".to_string(), Toml::Str("myval".to_string())), + ("-inf".to_string(), Toml::Num(0.0)), + ("2024-04-30".to_string(), Toml::Num(100.0)), + ("½".to_string(), Toml::Num(0.5)) + ])] ); } From 41fa5f240f1153599925f0e451780be0ab8c23fb Mon Sep 17 00:00:00 2001 From: Linus Kirkwood Date: Tue, 30 Apr 2024 01:00:35 +1000 Subject: [PATCH 09/27] Implemented toml parse_ident --- src/toml.rs | 46 ++++++++++------------------------------------ 1 file changed, 10 insertions(+), 36 deletions(-) diff --git a/src/toml.rs b/src/toml.rs index c061256..54f5bd7 100644 --- a/src/toml.rs +++ b/src/toml.rs @@ -557,44 +557,18 @@ impl TomlParser { self.next(i); } - todo!("Return paths for parsing bare key") - } - - fn next_ident(&mut self, i: &mut Chars, mut start: String) -> Result { - while self.cur >= 'a' && self.cur <= 'z' - || self.cur >= 'A' && self.cur <= 'Z' - || self.cur == '_' - || self.cur == '-' - { + if self.cur == '.' { start.push(self.cur); self.next(i); + return self.parse_ident(i, start); // recursion here could be a problem } - if self.cur == '.' { - while self.cur == '.' { - self.next(i); - while self.cur >= 'a' && self.cur <= 'z' - || self.cur >= 'A' && self.cur <= 'Z' - || self.cur == '_' - || self.cur == '-' - { - start.push(self.cur); - self.next(i); - } - } - return Ok(TomlTok::Ident(start)); - } - if start == "true" { - return Ok(TomlTok::Bool(true)); - } - if start == "false" { - return Ok(TomlTok::Bool(false)); - } - if start == "inf" { - return Ok(TomlTok::Inf(false)); - } - if start == "nan" { - return Ok(TomlTok::Nan(false)); - } - return Ok(TomlTok::Ident(start)); + + Ok(match start.as_ref() { + "true" => TomlTok::Bool(true), + "false" => TomlTok::Bool(false), + "inf" => TomlTok::Inf(false), + "nan" => TomlTok::Nan(false), + _ => TomlTok::Ident(start), + }) } } From fe27c84d0fdae91940b397757443034a723ae89a Mon Sep 17 00:00:00 2001 From: Linus Kirkwood Date: Tue, 30 Apr 2024 01:14:59 +1000 Subject: [PATCH 10/27] Add toml nan/inf todo and removed unused token --- src/toml.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/toml.rs b/src/toml.rs index 54f5bd7..28e6611 100644 --- a/src/toml.rs +++ b/src/toml.rs @@ -63,6 +63,7 @@ pub enum TomlTok { I64(i64), F64(f64), Bool(bool), + // TODO add option to enforce + sign for conversion to ident Nan(bool), Inf(bool), Date(String), @@ -70,7 +71,6 @@ pub enum TomlTok { BlockOpen, BlockClose, Comma, - Bof, Eof, } From 5ba789b5aadc01fe6821593ebbed7a38692e6bc3 Mon Sep 17 00:00:00 2001 From: Linus Kirkwood Date: Tue, 30 Apr 2024 01:15:38 +1000 Subject: [PATCH 11/27] Impl into string for toml token --- src/toml.rs | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/toml.rs b/src/toml.rs index 28e6611..191086b 100644 --- a/src/toml.rs +++ b/src/toml.rs @@ -74,6 +74,39 @@ pub enum TomlTok { Eof, } +impl Into for TomlTok { + fn into(self) -> String { + match self { + Self::Ident(string) => string, + Self::Str(string) => string, + Self::U64(number) => number.to_string(), + Self::I64(number) => number.to_string(), + Self::F64(number) => number.to_string(), + Self::Bool(boolean) => boolean.to_string(), + Self::Nan(negative) => { + if negative { + "-nan".to_string() + } else { + "nan".to_string() + } + } + Self::Inf(negative) => { + if negative { + "-inf".to_string() + } else { + "inf".to_string() + } + } + Self::Date(string) => string, + Self::Equals => '='.to_string(), + Self::BlockOpen => '['.to_string(), + Self::BlockClose => ']'.to_string(), + Self::Comma => ','.to_string(), + Self::Eof => '\0'.to_string(), + } + } +} + /// A TOML value. #[derive(Debug, PartialEq)] pub enum Toml { From cd4881c7769fbca3597dc0ae16e41e1291fcdaed Mon Sep 17 00:00:00 2001 From: Linus Kirkwood Date: Tue, 30 Apr 2024 01:25:55 +1000 Subject: [PATCH 12/27] Reordered toml number parsing conditionals --- src/toml.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/toml.rs b/src/toml.rs index 191086b..9517797 100644 --- a/src/toml.rs +++ b/src/toml.rs @@ -527,15 +527,11 @@ impl TomlParser { if is_neg { if let Ok(num) = num.parse() { return Ok(TomlTok::I64(num)); - } else { - return self.parse_ident(i, num); } - } - if let Ok(num) = num.parse() { + } else if let Ok(num) = num.parse() { return Ok(TomlTok::U64(num)); - } else { - return self.parse_ident(i, num); } + return self.parse_ident(i, num); } } 0x22 => { From b18d925f1fbbce1d2459e99c788e81b0d617768d Mon Sep 17 00:00:00 2001 From: Linus Kirkwood Date: Tue, 30 Apr 2024 01:37:59 +1000 Subject: [PATCH 13/27] Cleaned up some toml match statements --- src/toml.rs | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/src/toml.rs b/src/toml.rs index 9517797..6262fd0 100644 --- a/src/toml.rs +++ b/src/toml.rs @@ -298,6 +298,13 @@ impl TomlParser { let tok = self.next_tok(i)?; let key = match tok { TomlTok::Ident(key) => key, + TomlTok::U64(_) + | TomlTok::I64(_) + | TomlTok::F64(_) + | TomlTok::Bool(_) + | TomlTok::Nan(_) + | TomlTok::Inf(_) + | TomlTok::Date(_) => tok.into(), _ => return Err(self.err_token(tok)), }; let tok = self.next_tok(i)?; @@ -313,9 +320,15 @@ impl TomlParser { _ => return Err(self.err_token(tok)), } } - TomlTok::Str(key) | TomlTok::Ident(key) => { - self.parse_key_value(local_scope, key, i, out.out())? - } + TomlTok::Str(_) + | TomlTok::Ident(_) + | TomlTok::U64(_) + | TomlTok::I64(_) + | TomlTok::F64(_) + | TomlTok::Bool(_) + | TomlTok::Nan(_) + | TomlTok::Inf(_) + | TomlTok::Date(_) => self.parse_key_value(local_scope, tok.into(), i, out.out())?, _ => return Err(self.err_token(tok)), } Ok(true) @@ -490,7 +503,7 @@ impl TomlParser { return self.parse_ident(i, num); } } - while self.cur >= '0' && self.cur <= '9' || self.cur == '_' { + while matches!(self.cur, '0'..='9' | '_') { if self.cur != '_' { num.push(self.cur); } @@ -499,7 +512,7 @@ impl TomlParser { if self.cur == '.' { num.push(self.cur); self.next(i); - while self.cur >= '0' && self.cur <= '9' || self.cur == '_' { + while matches!(self.cur, '0'..='9' | '_') { if self.cur != '_' { num.push(self.cur); } @@ -514,11 +527,7 @@ impl TomlParser { // lets assume its a date. whatever. i don't feel like more parsing today num.push(self.cur); self.next(i); - while self.cur >= '0' && self.cur <= '9' - || self.cur == ':' - || self.cur == '-' - || self.cur == 'T' - { + while matches!(self.cur, '0'..='9' | ':' | '-' | 'T') { num.push(self.cur); self.next(i); } From a9962c1fe21fdf98bed0457ef3cfe96916c0ed2c Mon Sep 17 00:00:00 2001 From: Linus Kirkwood Date: Tue, 30 Apr 2024 02:11:34 +1000 Subject: [PATCH 14/27] Formatted toml token comments and rename macro --- src/toml.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/toml.rs b/src/toml.rs index 6262fd0..2208e2d 100644 --- a/src/toml.rs +++ b/src/toml.rs @@ -10,9 +10,9 @@ use hashbrown::HashMap; #[cfg(not(feature = "no_std"))] use std::collections::HashMap; -/// Pattern matching any valid bare key character as u32. +/// Pattern matching any valid unquoted key character as u32. /// ABNF line: https://github.com/toml-lang/toml/blob/2431aa308a7bc97eeb50673748606e23a6e0f201/toml.abnf#L55 -macro_rules! bare_key_ident { +macro_rules! ident_chars { () => { 0x41..=0x5A | 0x61..=0x7A @@ -428,28 +428,28 @@ impl TomlParser { #[allow(unreachable_patterns)] match self.cur as u32 { + // , 0x2C => { - // , self.next(i); return Ok(TomlTok::Comma); } + // [ 0x5B => { - // [ self.next(i); return Ok(TomlTok::BlockOpen); } + // ] 0x5D => { - // ] self.next(i); return Ok(TomlTok::BlockClose); } + // = 0x3D => { - // = self.next(i); return Ok(TomlTok::Equals); } + // # 0x23 => { - // # while self.cur != '\n' && self.cur != '\0' { self.next(i); } @@ -543,8 +543,8 @@ impl TomlParser { return self.parse_ident(i, num); } } + // " 0x22 => { - // " let mut val = String::new(); self.next(i); let mut braces = 1; @@ -582,7 +582,7 @@ impl TomlParser { self.next(i); return Ok(TomlTok::Str(val)); } - bare_key_ident!() => return self.parse_ident(i, String::new()), + ident_chars!() => return self.parse_ident(i, String::new()), _ => return Err(self.err_parse("tokenizer")), } } @@ -590,7 +590,7 @@ impl TomlParser { /// Parse an ident or similar, starting with the current character. fn parse_ident(&mut self, i: &mut Chars, mut start: String) -> Result { - while matches!(self.cur as u32, bare_key_ident!()) { + while matches!(self.cur as u32, ident_chars!()) { start.push(self.cur); self.next(i); } From 63e069bc12bda201e9c7936f3c60ad50597f0597 Mon Sep 17 00:00:00 2001 From: Linus Kirkwood Date: Tue, 30 Apr 2024 02:12:00 +1000 Subject: [PATCH 15/27] Moved toml number parsing into separate function --- src/toml.rs | 172 +++++++++++++++++++++++++++------------------------- 1 file changed, 91 insertions(+), 81 deletions(-) diff --git a/src/toml.rs b/src/toml.rs index 2208e2d..8e727a0 100644 --- a/src/toml.rs +++ b/src/toml.rs @@ -462,87 +462,8 @@ impl TomlParser { self.next(i); } } - 0x2B | 0x2D | 0x30..=0x39 => { - // + - 0-9 - let mut num = String::new(); - let is_neg = if self.cur == '-' { - num.push(self.cur); - self.next(i); - true - } else { - if self.cur == '+' { - self.next(i); - } - false - }; - if self.cur == 'n' { - self.next(i); - if self.cur == 'a' { - self.next(i); - if self.cur == 'n' { - self.next(i); - return Ok(TomlTok::Nan(is_neg)); - } else { - return self.parse_ident(i, num); - } - } else { - return self.parse_ident(i, num); - } - } - if self.cur == 'i' { - self.next(i); - if self.cur == 'n' { - self.next(i); - if self.cur == 'f' { - self.next(i); - return Ok(TomlTok::Inf(is_neg)); - } else { - return self.parse_ident(i, num); - } - } else { - return self.parse_ident(i, num); - } - } - while matches!(self.cur, '0'..='9' | '_') { - if self.cur != '_' { - num.push(self.cur); - } - self.next(i); - } - if self.cur == '.' { - num.push(self.cur); - self.next(i); - while matches!(self.cur, '0'..='9' | '_') { - if self.cur != '_' { - num.push(self.cur); - } - self.next(i); - } - if let Ok(num) = num.parse() { - return Ok(TomlTok::F64(num)); - } else { - return Err(self.err_parse("number")); - } - } else if self.cur == '-' { - // lets assume its a date. whatever. i don't feel like more parsing today - num.push(self.cur); - self.next(i); - while matches!(self.cur, '0'..='9' | ':' | '-' | 'T') { - num.push(self.cur); - self.next(i); - } - return Ok(TomlTok::Date(num)); - } else { - if is_neg { - if let Ok(num) = num.parse() { - return Ok(TomlTok::I64(num)); - } - } else if let Ok(num) = num.parse() { - return Ok(TomlTok::U64(num)); - } - return self.parse_ident(i, num); - } - } + // + - 0-9 + 0x2B | 0x2D | 0x30..=0x39 => return self.parse_num(i), // " 0x22 => { let mut val = String::new(); @@ -609,4 +530,93 @@ impl TomlParser { _ => TomlTok::Ident(start), }) } + + /// Parses a number (or an ident that starts with numbers), starting with the current character. + fn parse_num(&mut self, i: &mut Chars) -> Result { + let mut num = String::new(); + + let mut negative = false; + if self.cur == '+' { + self.next(i) + } else if self.cur == '-' { + negative = true; + self.next(i); + } + + if self.cur == 'n' { + self.next(i); + if self.cur == 'a' { + self.next(i); + if self.cur == 'n' { + self.next(i); + if matches!(self.cur as u32, ident_chars!()) { + return Ok(TomlTok::Nan(negative)); + } else { + return self.parse_ident(i, num); + } + } + } + } else if self.cur == 'i' { + self.next(i); + if self.cur == 'n' { + self.next(i); + if self.cur == 'f' { + self.next(i); + if matches!(self.cur as u32, ident_chars!()) { + return Ok(TomlTok::Inf(negative)); + } else { + return self.parse_ident(i, num); + } + } + } + } + + while matches!(self.cur, '0'..='9' | '_') { + if self.cur != '_' { + num.push(self.cur); + } + self.next(i); + } + + if self.cur == '.' { + num.push(self.cur); + self.next(i); + while matches!(self.cur, '0'..='9' | '_') { + if self.cur != '_' { + num.push(self.cur); + } + self.next(i); + } + if let Ok(num) = num.parse() { + return Ok(TomlTok::F64(num)); + } else { + return Err(self.err_parse("number")); + } + } else if self.cur == '-' { + // lets assume its a date. whatever. i don't feel like more parsing today + num.push(self.cur); + self.next(i); + while matches!(self.cur, '0'..='9' | ':' | '-' | 'T') { + num.push(self.cur); + self.next(i); + } + return Ok(TomlTok::Date(num)); + // TODO rework this + } + + match negative { + true => { + if let Ok(num) = num.parse() { + return Ok(TomlTok::I64(num)); + } + } + false => { + if let Ok(num) = num.parse() { + return Ok(TomlTok::U64(num)); + } + } + } + + return self.parse_ident(i, num); + } } From ba568f92c9fe8af7c4ef893126cfb14ec5addea5 Mon Sep 17 00:00:00 2001 From: Linus Kirkwood Date: Tue, 30 Apr 2024 02:36:25 +1000 Subject: [PATCH 16/27] Added guard for ident starting with num toml --- src/toml.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/toml.rs b/src/toml.rs index 8e727a0..71ab2b5 100644 --- a/src/toml.rs +++ b/src/toml.rs @@ -604,6 +604,10 @@ impl TomlParser { // TODO rework this } + if matches!(self.cur as u32, ident_chars!()) { + return self.parse_ident(i, num); + } + match negative { true => { if let Ok(num) = num.parse() { From 25cd04086b85b7260772a3174858df183b617201 Mon Sep 17 00:00:00 2001 From: Linus Kirkwood Date: Tue, 30 Apr 2024 02:36:50 +1000 Subject: [PATCH 17/27] Return err if num contains illegal chars toml --- src/toml.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/toml.rs b/src/toml.rs index 71ab2b5..8ea66f5 100644 --- a/src/toml.rs +++ b/src/toml.rs @@ -621,6 +621,6 @@ impl TomlParser { } } - return self.parse_ident(i, num); + return Err(self.err_parse("tokenizer")); } } From f958591025ad9bbb6e01af1fb6b19db2a7575b48 Mon Sep 17 00:00:00 2001 From: Linus Kirkwood Date: Tue, 30 Apr 2024 02:46:24 +1000 Subject: [PATCH 18/27] Added toml ident_term_chars macro --- src/toml.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/toml.rs b/src/toml.rs index 8ea66f5..2a07ec9 100644 --- a/src/toml.rs +++ b/src/toml.rs @@ -39,6 +39,13 @@ macro_rules! ident_chars { } } +/// Pattern matching a character that can terminate a valid ident. +macro_rules! ident_term_chars { + () => { + ' ' | '\t' | '\n' | '\0' | '=' + }; +} + /// A parser for TOML string values. /// /// ```rust From 181f74a2b4a34a7e8aa5eccbdc78f4412f065374 Mon Sep 17 00:00:00 2001 From: Linus Kirkwood Date: Tue, 30 Apr 2024 02:46:43 +1000 Subject: [PATCH 19/27] Fix not pushing chars during inf/nan toml parse --- src/toml.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/toml.rs b/src/toml.rs index 2a07ec9..395d675 100644 --- a/src/toml.rs +++ b/src/toml.rs @@ -546,15 +546,19 @@ impl TomlParser { if self.cur == '+' { self.next(i) } else if self.cur == '-' { + num.push(self.cur); negative = true; self.next(i); } if self.cur == 'n' { + num.push(self.cur); self.next(i); if self.cur == 'a' { + num.push(self.cur); self.next(i); if self.cur == 'n' { + num.push(self.cur); self.next(i); if matches!(self.cur as u32, ident_chars!()) { return Ok(TomlTok::Nan(negative)); @@ -564,10 +568,13 @@ impl TomlParser { } } } else if self.cur == 'i' { + num.push(self.cur); self.next(i); if self.cur == 'n' { + num.push(self.cur); self.next(i); if self.cur == 'f' { + num.push(self.cur); self.next(i); if matches!(self.cur as u32, ident_chars!()) { return Ok(TomlTok::Inf(negative)); From 16e37a24de2d76f707ef05ae6a58703a4c26a5cb Mon Sep 17 00:00:00 2001 From: Linus Kirkwood Date: Tue, 30 Apr 2024 02:47:01 +1000 Subject: [PATCH 20/27] Start using ident_term_chars to detect inf/nan --- src/toml.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/toml.rs b/src/toml.rs index 395d675..db1149a 100644 --- a/src/toml.rs +++ b/src/toml.rs @@ -560,10 +560,8 @@ impl TomlParser { if self.cur == 'n' { num.push(self.cur); self.next(i); - if matches!(self.cur as u32, ident_chars!()) { + if matches!(self.cur, ident_term_chars!()) { return Ok(TomlTok::Nan(negative)); - } else { - return self.parse_ident(i, num); } } } @@ -576,10 +574,8 @@ impl TomlParser { if self.cur == 'f' { num.push(self.cur); self.next(i); - if matches!(self.cur as u32, ident_chars!()) { + if matches!(self.cur, ident_term_chars!()) { return Ok(TomlTok::Inf(negative)); - } else { - return self.parse_ident(i, num); } } } From 16334707b9319db63e20e2f2565e3431dd8b9e4f Mon Sep 17 00:00:00 2001 From: Linus Kirkwood Date: Tue, 30 Apr 2024 02:50:31 +1000 Subject: [PATCH 21/27] Start using ident_term_chars to detect toml idents --- src/toml.rs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/toml.rs b/src/toml.rs index db1149a..edce665 100644 --- a/src/toml.rs +++ b/src/toml.rs @@ -529,13 +529,17 @@ impl TomlParser { return self.parse_ident(i, start); // recursion here could be a problem } - Ok(match start.as_ref() { - "true" => TomlTok::Bool(true), - "false" => TomlTok::Bool(false), - "inf" => TomlTok::Inf(false), - "nan" => TomlTok::Nan(false), - _ => TomlTok::Ident(start), - }) + if matches!(self.cur, ident_term_chars!()) { + return Ok(match start.as_ref() { + "true" => TomlTok::Bool(true), + "false" => TomlTok::Bool(false), + "inf" => TomlTok::Inf(false), + "nan" => TomlTok::Nan(false), + _ => TomlTok::Ident(start), + }); + } + + Err(self.err_parse("tokenizer")) } /// Parses a number (or an ident that starts with numbers), starting with the current character. From 4967feabb78400f1cd5e30aab56877565e505425 Mon Sep 17 00:00:00 2001 From: Linus Kirkwood Date: Tue, 30 Apr 2024 02:50:49 +1000 Subject: [PATCH 22/27] Add close block char to toml ident_term_chars --- src/toml.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/toml.rs b/src/toml.rs index edce665..81e4332 100644 --- a/src/toml.rs +++ b/src/toml.rs @@ -42,7 +42,7 @@ macro_rules! ident_chars { /// Pattern matching a character that can terminate a valid ident. macro_rules! ident_term_chars { () => { - ' ' | '\t' | '\n' | '\0' | '=' + ' ' | '\t' | '\n' | '\0' | '=' | ']' }; } From 94174faee6025825f3c3498c9ea831b6980e1a3d Mon Sep 17 00:00:00 2001 From: Linus Kirkwood Date: Tue, 30 Apr 2024 02:55:20 +1000 Subject: [PATCH 23/27] Updated toml key test --- tests/toml.rs | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/tests/toml.rs b/tests/toml.rs index e39a9b9..8a9bf26 100644 --- a/tests/toml.rs +++ b/tests/toml.rs @@ -88,23 +88,25 @@ fn assert_specific_toml_types() { } #[test] -fn key_start_num() { +fn toml_key_chars() { let toml_str = r#" [foo.bar.baz] - 1key = "myval" + 123abc456def = "myval" -inf = 0 2024-04-30 = 100 ½ = 0.5 "#; assert_eq!( - // TODO parse dotted keys correctly (nested table) - *TomlParser::parse(toml_str).unwrap()["foo.bar.baz"].arr(), - vec![HashMap::from([ - ("1key".to_string(), Toml::Str("myval".to_string())), - ("-inf".to_string(), Toml::Num(0.0)), - ("2024-04-30".to_string(), Toml::Num(100.0)), - ("½".to_string(), Toml::Num(0.5)) - ])] + TomlParser::parse(toml_str).unwrap(), + HashMap::from([ + ( + "foo.bar.baz.123abc456def".to_string(), + Toml::Str("myval".to_string()) + ), + ("foo.bar.baz.-inf".to_string(), Toml::Num(0.0)), + ("foo.bar.baz.2024-04-30".to_string(), Toml::Num(100.0)), + ("foo.bar.baz.½".to_string(), Toml::Num(0.5)) + ]) ); } From 4eb4037071d4eacdb619525efca0db0d2cf00bf1 Mon Sep 17 00:00:00 2001 From: Linus Kirkwood Date: Tue, 30 Apr 2024 02:58:37 +1000 Subject: [PATCH 24/27] Replaced tomltok into string with from impl --- src/toml.rs | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/toml.rs b/src/toml.rs index 81e4332..1c0b3ad 100644 --- a/src/toml.rs +++ b/src/toml.rs @@ -81,35 +81,35 @@ pub enum TomlTok { Eof, } -impl Into for TomlTok { - fn into(self) -> String { - match self { - Self::Ident(string) => string, - Self::Str(string) => string, - Self::U64(number) => number.to_string(), - Self::I64(number) => number.to_string(), - Self::F64(number) => number.to_string(), - Self::Bool(boolean) => boolean.to_string(), - Self::Nan(negative) => { +impl From for String { + fn from(value: TomlTok) -> Self { + match value { + TomlTok::Ident(string) => string, + TomlTok::Str(string) => string, + TomlTok::U64(number) => number.to_string(), + TomlTok::I64(number) => number.to_string(), + TomlTok::F64(number) => number.to_string(), + TomlTok::Bool(boolean) => boolean.to_string(), + TomlTok::Nan(negative) => { if negative { "-nan".to_string() } else { "nan".to_string() } } - Self::Inf(negative) => { + TomlTok::Inf(negative) => { if negative { "-inf".to_string() } else { "inf".to_string() } } - Self::Date(string) => string, - Self::Equals => '='.to_string(), - Self::BlockOpen => '['.to_string(), - Self::BlockClose => ']'.to_string(), - Self::Comma => ','.to_string(), - Self::Eof => '\0'.to_string(), + TomlTok::Date(string) => string, + TomlTok::Equals => '='.to_string(), + TomlTok::BlockOpen => '['.to_string(), + TomlTok::BlockClose => ']'.to_string(), + TomlTok::Comma => ','.to_string(), + TomlTok::Eof => '\0'.to_string(), } } } From 73c9917d0a11524a0c77d04ae8d3865dac2759f8 Mon Sep 17 00:00:00 2001 From: Linus Kirkwood Date: Tue, 30 Apr 2024 02:58:59 +1000 Subject: [PATCH 25/27] Removed unnecessary return --- src/toml.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/toml.rs b/src/toml.rs index 1c0b3ad..30b6c10 100644 --- a/src/toml.rs +++ b/src/toml.rs @@ -635,6 +635,6 @@ impl TomlParser { } } - return Err(self.err_parse("tokenizer")); + Err(self.err_parse("tokenizer")) } } From 994dbad87b0decdbe2433a1dbb42ae93c8bff2ea Mon Sep 17 00:00:00 2001 From: Linus Kirkwood Date: Tue, 30 Apr 2024 12:13:56 +1000 Subject: [PATCH 26/27] Fixed toml no_std test --- tests/toml.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/toml.rs b/tests/toml.rs index 8a9bf26..4b1c821 100644 --- a/tests/toml.rs +++ b/tests/toml.rs @@ -1,3 +1,6 @@ +#[cfg(feature = "no_std")] +use hashbrown::HashMap; +#[cfg(not(feature = "no_std"))] use std::collections::HashMap; use nanoserde::Toml; From 3a57d03e129881bf5ca89ea9430c7be7b36526b8 Mon Sep 17 00:00:00 2001 From: Linus Kirkwood Date: Tue, 30 Apr 2024 12:31:32 +1000 Subject: [PATCH 27/27] Always match toml chars as char not u32 --- src/toml.rs | 71 ++++++++++++++++++++++++----------------------------- 1 file changed, 32 insertions(+), 39 deletions(-) diff --git a/src/toml.rs b/src/toml.rs index 30b6c10..bfe4843 100644 --- a/src/toml.rs +++ b/src/toml.rs @@ -14,28 +14,28 @@ use std::collections::HashMap; /// ABNF line: https://github.com/toml-lang/toml/blob/2431aa308a7bc97eeb50673748606e23a6e0f201/toml.abnf#L55 macro_rules! ident_chars { () => { - 0x41..=0x5A - | 0x61..=0x7A - | 0x30..=0x39 - | 0x2D - | 0x5F - | 0xB2 - | 0xB3 - | 0xB9 - | 0xBC..=0xBE - | 0xC0..=0xD6 - | 0xD8..=0xF6 - | 0xF8..=0x37D - | 0x37F..=0x1FFF - | 0x200C..=0x200D - | 0x203F..=0x2040 - | 0x2070..=0x218F - | 0x2460..=0x24FF - | 0x2C00..=0x2FEF - | 0x3001..=0xD7FF - | 0xF900..=0xFDCF - | 0xFDF0..=0xFFFD - | 0x10000..=0xEFFFF + '\u{41}'..='\u{5A}' + | '\u{61}'..='\u{7A}' + | '\u{30}'..='\u{39}' + | '\u{2D}' + | '\u{5F}' + | '\u{B2}' + | '\u{B3}' + | '\u{B9}' + | '\u{BC}'..='\u{BE}' + | '\u{C0}'..='\u{D6}' + | '\u{D8}'..='\u{F6}' + | '\u{F8}'..='\u{37D}' + | '\u{37F}'..='\u{1FFF}' + | '\u{200C}'..='\u{200D}' + | '\u{203F}'..='\u{2040}' + | '\u{2070}'..='\u{218F}' + | '\u{2460}'..='\u{24FF}' + | '\u{2C00}'..='\u{2FEF}' + | '\u{3001}'..='\u{D7FF}' + | '\u{F900}'..='\u{FDCF}' + | '\u{FDF0}'..='\u{FFFD}' + | '\u{10000}'..='\u{EFFFF}' } } @@ -434,29 +434,24 @@ impl TomlParser { } #[allow(unreachable_patterns)] - match self.cur as u32 { - // , - 0x2C => { + match self.cur { + ',' => { self.next(i); return Ok(TomlTok::Comma); } - // [ - 0x5B => { + '[' => { self.next(i); return Ok(TomlTok::BlockOpen); } - // ] - 0x5D => { + ']' => { self.next(i); return Ok(TomlTok::BlockClose); } - // = - 0x3D => { + '=' => { self.next(i); return Ok(TomlTok::Equals); } - // # - 0x23 => { + '#' => { while self.cur != '\n' && self.cur != '\0' { self.next(i); } @@ -469,10 +464,8 @@ impl TomlParser { self.next(i); } } - // + - 0-9 - 0x2B | 0x2D | 0x30..=0x39 => return self.parse_num(i), - // " - 0x22 => { + '+' | '-' | '0'..='9' => return self.parse_num(i), + '"' => { let mut val = String::new(); self.next(i); let mut braces = 1; @@ -518,7 +511,7 @@ impl TomlParser { /// Parse an ident or similar, starting with the current character. fn parse_ident(&mut self, i: &mut Chars, mut start: String) -> Result { - while matches!(self.cur as u32, ident_chars!()) { + while matches!(self.cur, ident_chars!()) { start.push(self.cur); self.next(i); } @@ -618,7 +611,7 @@ impl TomlParser { // TODO rework this } - if matches!(self.cur as u32, ident_chars!()) { + if matches!(self.cur, ident_chars!()) { return self.parse_ident(i, num); }