diff --git a/bauble/src/parse/parser.rs b/bauble/src/parse/parser.rs index 7e30e65..60a4023 100644 --- a/bauble/src/parse/parser.rs +++ b/bauble/src/parse/parser.rs @@ -216,13 +216,40 @@ pub fn parser<'a>() -> impl Parser<'a, ParserSource<'a>, ParseValues, Extra<'a>> .to_slice() .map_with(|ident: &str, e| ident.to_owned().spanned(e.span())); + let digits_ident = any() + .try_map(move |c: char, span| { + if c.is_ascii_digit() { + Ok(c) + } else { + Err( + chumsky::label::LabelError::, _>::expected_found( + [TextExpected::IdentifierPart], + Some(chumsky::util::MaybeRef::Val(c)), + span, + ), + ) + } + }) + .repeated() + .at_least(1) + .to_slice() + .map_with(|ident: &str, e| ident.to_owned().spanned(e.span())); + + // Identifier that can follow rust identifier rules or be a decimal number (rust identifiers + // can't start with decimal numbers). + let rust_or_digits_ident = ident.or(digits_ident); + let mut path = chumsky::recursive::Recursive::declare(); let path_start; let path_end; path.define({ - let ident_with_generics = ident.then(path.clone().delimited_by(just('<'), just('>'))); + let ident_with_generics = + rust_or_digits_ident.then(path.clone().delimited_by(just('<'), just('>'))); - path_start = ident.then_ignore(just("::")).repeated().collect::>(); + path_start = rust_or_digits_ident + .then_ignore(just("::")) + .repeated() + .collect::>(); path_end = ident_with_generics .clone() .map_with(|(ident, path), e| { @@ -233,8 +260,10 @@ pub fn parser<'a>() -> impl Parser<'a, ParserSource<'a>, ParseValues, Extra<'a>> .map_with(|(ident, path), e| { PathEnd::WithIdentGeneric(ident, Spanned::new(e.span(), Box::new(path))) })) - .or(ident.map(PathEnd::Ident)) - .or(just("*::").ignore_then(ident).map(PathEnd::WithIdent)); + .or(rust_or_digits_ident.map(PathEnd::Ident)) + .or(just("*::") + .ignore_then(rust_or_digits_ident) + .map(PathEnd::WithIdent)); path_start .map_with(|v, e| v.spanned(e.span())) .then(path_end.clone().map_with(|v, e| v.spanned(e.span()))) @@ -640,7 +669,7 @@ pub fn parser<'a>() -> impl Parser<'a, ParserSource<'a>, ParseValues, Extra<'a>> } uses.then( - binding(ident, object, path.clone(), comments) + binding(rust_or_digits_ident, object, path.clone(), comments) .repeated() .collect::>(), ) diff --git a/bauble/tests/integration.rs b/bauble/tests/integration.rs index 8df7794..31588f7 100644 --- a/bauble/tests/integration.rs +++ b/bauble/tests/integration.rs @@ -505,3 +505,26 @@ pub fn ref_explicit_type_incorrect() { ] ); } + +#[test] +fn decimal_digits_identifiers() { + let a = &test_file!( + "a", + "2 = integration::Test { x: -5, y: 5 }\n\ + 123 = integration::Test { x: -5, y: 5 }\n\ + test_ref2 = $2 + test_ref23 = $123 + ", + Test { x: -5, y: 5 }, + Test { x: -5, y: 5 }, + TestRef("a::2".into()), + TestRef("a::123".into()), + ); + + test_load( + &|ctx| { + ctx.register_type::(); + }, + &[a], + ); +}