diff --git a/core/src/sql/escape.rs b/core/src/sql/escape.rs index 1badad4b..bd46e5aa 100644 --- a/core/src/sql/escape.rs +++ b/core/src/sql/escape.rs @@ -79,15 +79,12 @@ pub fn escape_ident(s: &str) -> Cow<'_, str> { #[inline] pub fn escape_normal<'a>(s: &'a str, l: char, r: char, e: &str) -> Cow<'a, str> { - // Loop over each character - for x in s.bytes() { - // Check if character is allowed - if !(x.is_ascii_alphanumeric() || x == b'_') { - return Cow::Owned(format!("{l}{}{r}", s.replace(r, e))); - } + // Is there no need to escape the value? + if s.bytes().all(|x| x.is_ascii_alphanumeric() || x == b'_') { + return Cow::Borrowed(s); } // Output the value - Cow::Borrowed(s) + Cow::Owned(format!("{l}{}{r}", s.replace(r, e))) } pub fn escape_reserved_keyword(s: &str) -> Option { diff --git a/core/src/syn/parser/object.rs b/core/src/syn/parser/object.rs index 4c1d9702..4adbb375 100644 --- a/core/src/syn/parser/object.rs +++ b/core/src/syn/parser/object.rs @@ -3,9 +3,10 @@ use std::collections::BTreeMap; use reblessive::Stk; use crate::{ - sql::{Block, Geometry, Number, Object, Strand, Value}, + sql::{Block, Geometry, Object, Strand, Value}, syn::{ error::bail, + lexer::compound, parser::{enter_object_recursion, mac::expected, ParseResult, Parser}, token::{t, Glued, Span, TokenKind}, }, @@ -602,10 +603,8 @@ impl Parser<'_> { match token.kind { x if Self::kind_is_keyword_like(x) => { self.pop_peek(); - let str = self.lexer.reader.span(token.span); - // Lexer should ensure that the token is valid utf-8 - let str = std::str::from_utf8(str).unwrap().to_owned(); - Ok(str) + let str = self.lexer.span_str(token.span); + Ok(str.to_string()) } TokenKind::Identifier => { self.pop_peek(); @@ -616,9 +615,16 @@ impl Parser<'_> { let str = self.next_token_value::()?.0; Ok(str) } - TokenKind::Digits | TokenKind::Glued(Glued::Number) => { - let number = self.next_token_value::()?.to_string(); - Ok(number) + TokenKind::Digits => { + self.pop_peek(); + let span = self.lexer.lex_compound(token, compound::number)?.span; + let str = self.lexer.span_str(span); + Ok(str.to_string()) + } + TokenKind::Glued(Glued::Number) => { + self.pop_peek(); + let str = self.lexer.span_str(token.span); + Ok(str.to_string()) } _ => unexpected!(self, token, "an object key"), } diff --git a/core/src/syn/parser/test/value.rs b/core/src/syn/parser/test/value.rs index 16693923..fda0b418 100644 --- a/core/src/syn/parser/test/value.rs +++ b/core/src/syn/parser/test/value.rs @@ -38,6 +38,16 @@ fn parse_coordinate() { assert_eq!(x.y(), -18.0); } +#[test] +fn parse_numeric_object_key() { + let v = test_parse!(parse_value_table, "{ 00: 0 }").unwrap(); + let Value::Object(object) = v else { + panic!("not an object"); + }; + assert!(object.len() == 1); + assert_eq!(object.get("00").cloned(), Some(Value::Number(Number::Int(0)))); +} + #[test] fn parse_like_operator() { test_parse!(parse_value_field, "a ~ b").unwrap();