From 149a4f30f4d727ff26ae1321fc824a1b724f28a5 Mon Sep 17 00:00:00 2001 From: nightcityblade Date: Thu, 13 Aug 2026 00:14:16 +0800 Subject: [PATCH] Fix JavaScript number tokenization --- babel/messages/jslexer.py | 8 ++++---- tests/messages/test_jslexer.py | 9 +++++++++ 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/babel/messages/jslexer.py b/babel/messages/jslexer.py index d751b58f7..9dfdd1b94 100644 --- a/babel/messages/jslexer.py +++ b/babel/messages/jslexer.py @@ -24,8 +24,8 @@ escapes: dict[str, str] = {'b': '\b', 'f': '\f', 'n': '\n', 'r': '\r', 't': '\t'} -name_re = re.compile(r'[\w$_][\w\d$_]*', re.UNICODE) -dotted_name_re = re.compile(r'[\w$_][\w\d$_.]*[\w\d$_.]', re.UNICODE) +name_re = re.compile(r'(?!\d)[\w$_][\w\d$_]*', re.UNICODE) +dotted_name_re = re.compile(r'(?!\d)[\w$_][\w\d$_.]*[\w\d$_.]', re.UNICODE) division_re = re.compile(r'/=?') regex_re = re.compile(r'/(?:[^/\\]*(?:\\.[^/\\]*)*)/[a-zA-Z]*', re.DOTALL) line_re = re.compile(r'(\r\n|\n|\r)') @@ -48,10 +48,10 @@ class Token(NamedTuple): ('dotted_name', dotted_name_re), ('name', name_re), ('number', re.compile(r'''( + (0x[a-fA-F0-9]+) | (?:0|[1-9]\d*) (\.\d+)? - ([eE][-+]?\d+)? | - (0x[a-fA-F0-9]+) + ([eE][-+]?\d+)? )''', re.VERBOSE)), ('jsx_tag', re.compile(r'(?:\s]+|/>)', re.I)), # May be mangled in `get_rules` ('operator', re.compile(r'(%s)' % '|'.join(re.escape(op) for op in operators))), diff --git a/tests/messages/test_jslexer.py b/tests/messages/test_jslexer.py index 00afc78ae..9a361c802 100644 --- a/tests/messages/test_jslexer.py +++ b/tests/messages/test_jslexer.py @@ -12,6 +12,15 @@ def test_dollar_in_identifier(): assert list(jslexer.tokenize('dollar$dollar')) == [('name', 'dollar$dollar', 1)] +def test_number_literals(): + assert list(jslexer.tokenize('42 3.14 1e3 0x2a')) == [ + ('number', '42', 1), + ('number', '3.14', 1), + ('number', '1e3', 1), + ('number', '0x2a', 1), + ] + + def test_dotted_name(): assert list(jslexer.tokenize("foo.bar(quux)", dotted=True)) == [ ('name', 'foo.bar', 1),