The full number grammar adds an optional fraction and an optional exponent to the integer part. Today you scan those, completing the Number token and rejecting the malformed forms.
Scan the optional fraction and exponent of a number, rejecting a dot or exponent with no digits.
The integer part is only the start of a number. After it may come a fraction -
a dot followed by one or more digits - and then an exponent - an e or E, an
optional sign, and one or more digits. 3.14 has a fraction; 1e3 is 1000
through its exponent; -2.5E-2 uses both and comes out to -0.025. Uppercase and
lowercase E are equivalent, and the exponent’s sign may be +, -, or omitted.
The failing edges all come from missing digits. A dot must be followed by at
least one digit, so 1. is malformed. An exponent marker must be followed by at
least one digit, so 1e is malformed. And a fraction cannot be empty even before an
exponent, so 1.e3 is malformed too. Once the full lexeme is matched, hand it to a
floating-point parser to get the Num value; the grammar you enforced guarantees
the text it sees is well-formed. That completes every token kind - the scanner is
done, and parsing begins next.
// after the integer part, optionally:// fraction: '.' then ONE OR MORE digits (a bare '.' is Illegal)// exponent: 'e' or 'E', optional '+' or '-', then ONE OR MORE digits// then parse the whole matched lexeme with a float parser// ("3.14", "1e3", "-2.5E-2" all round-trip through it)