From f960e39f8b6f67c23f1d67c8ccd09c2d7bd6568d Mon Sep 17 00:00:00 2001 From: Brummel Date: Sun, 10 May 2026 15:04:50 +0200 Subject: [PATCH] floats iter 2.1 fixup: drop task-tags + refresh stale prose + uppercase-E + leading-dot tests --- crates/ailang-surface/src/lex.rs | 40 ++++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/crates/ailang-surface/src/lex.rs b/crates/ailang-surface/src/lex.rs index a67df2e..1bfd013 100644 --- a/crates/ailang-surface/src/lex.rs +++ b/crates/ailang-surface/src/lex.rs @@ -10,7 +10,9 @@ //! 3. After tokenisation, classify by first character: //! - `"` ⇒ string literal (consume until closing `"`; `\"` and //! `\n` escapes supported). -//! - digit, or `-` followed by digit ⇒ integer literal. +//! - digit, or `-` followed by digit ⇒ integer or float literal +//! (float if the token contains `.`, `e`, or `E`; integer +//! otherwise). //! - otherwise ⇒ ident. //! //! Operators (`+`, `==`, `<=`, `**`), qualified names like @@ -40,7 +42,7 @@ pub enum Tok { /// Integer literal. Original textual form preserved for error messages; /// parsed `i64` value carried for direct use. Int(i64), - /// IEEE-754 binary64 literal (Floats milestone iter 2). The + /// IEEE-754 binary64 literal. The /// payload is the bit pattern produced by `f64::to_bits`. The /// surface forms recognised by the lexer are /// `.(e[+-]?)?` and @@ -49,7 +51,7 @@ pub enum Tok { Float(u64), /// String literal contents (after escape processing). Str(String), - /// Identifier. Anything that isn't a paren, integer, or string. + /// Identifier. Anything that isn't a paren, integer, float, or string. Ident(String), } @@ -347,7 +349,7 @@ mod tests { assert!(matches!(&toks[0].tok, Tok::Str(s) if s == "em — dash and naïve")); } - /// Iter 22-floats.2 RED: a `.` token lexes as + /// A `.` token lexes as /// `Tok::Float` carrying the IEEE-754 binary64 bit pattern of /// `f64::from_str(raw).unwrap().to_bits()`. Pinned literal: /// `1.5_f64` → bit pattern `0x3FF8000000000000`. @@ -475,4 +477,34 @@ mod tests { "expected InvalidFloat for `1..5`, got {err:?}" ); } + + /// Spec A2 grammar accepts both `e` and `E` for the exponent + /// marker. The `looks_like_float` validator handles both bytes; + /// pin the uppercase form explicitly. + #[test] + fn float_uppercase_exponent() { + let toks = tokenize("1.5E3 2E-5").unwrap(); + assert_eq!(toks.len(), 2); + match toks[0].tok { + Tok::Float(b) => assert_eq!(b, 1.5e3_f64.to_bits()), + ref other => panic!("expected Tok::Float, got {other:?}"), + } + match toks[1].tok { + Tok::Float(b) => assert_eq!(b, 2e-5_f64.to_bits()), + ref other => panic!("expected Tok::Float, got {other:?}"), + } + } + + /// Spec A2 rejects bare leading dot at the *float* level — `.5` is + /// not a float literal. The lexer's digit-lead rule does not fire + /// (first byte `.` is not a digit and not `-`-followed-by-digit), + /// so `.5` falls through to the maximal-non-paren-non-whitespace + /// run and lexes as `Tok::Ident(".5")`. Downstream parser / + /// typecheck stages produce the appropriate diagnostic. + #[test] + fn float_leading_dot_lexes_as_ident() { + let toks = tokenize(".5").unwrap(); + assert_eq!(toks.len(), 1); + assert!(matches!(&toks[0].tok, Tok::Ident(s) if s == ".5")); + } }