@@ -27,6 +27,7 @@ class TokenType(enum.Enum):
2727 SEMICOLON = ";"
2828 COMMA = ","
2929 COLON = ":"
30+ QUESTION = "?"
3031 STAR = "*"
3132 EQUALS = "="
3233
@@ -63,7 +64,6 @@ class TokenType(enum.Enum):
6364 PP_FLAGS = "PP_FLAGS"
6465
6566 # Special
66- LOOKUP = "LOOKUP"
6767 EOF = "EOF"
6868
6969
@@ -112,6 +112,7 @@ def __repr__(self) -> str:
112112 ";" : TokenType .SEMICOLON ,
113113 "," : TokenType .COMMA ,
114114 ":" : TokenType .COLON ,
115+ "?" : TokenType .QUESTION ,
115116 "*" : TokenType .STAR ,
116117 "=" : TokenType .EQUALS ,
117118 "+" : TokenType .PLUS ,
@@ -203,7 +204,7 @@ def _take(self, num: int = 1) -> str:
203204
204205 return result
205206
206- def _expect (self , * chars : str ) -> None :
207+ def _expect (self , * chars : str ) -> str :
207208 """Consume the expected characters or raise an error."""
208209 if self ._current () not in chars :
209210 actual = "end of input" if self .eof else repr (self ._current ())
@@ -287,11 +288,11 @@ def _read_identifier(self) -> str:
287288 return ""
288289
289290 def _read_number (self ) -> str :
290- """Read a numeric literal, supporting decimal, hex (0x), octal (0), binary (0b), and C-style suffixes."""
291+ """Read a numeric literal, supporting decimal, hex (0x), octal (0, 0o ), binary (0b), and C-style suffixes."""
291292 start = self ._pos
292293 is_float = False
293294
294- if self ._current () == "0" and self ._peek () in ("x" , "X" , "b" , "B" ):
295+ if self ._current () == "0" and self ._peek () in ("x" , "X" , "b" , "B" , "o" , "O" ):
295296 self ._expect ("0" ) # Consume leading 0
296297 suffix = self ._take ().lower ()
297298
@@ -301,6 +302,9 @@ def _read_number(self) -> str:
301302 if suffix == "b" and not self ._read_while ("01" ):
302303 raise self ._error ("invalid binary literal" )
303304
305+ if suffix == "o" and not self ._read_while ("01234567" ):
306+ raise self ._error ("invalid octal literal" )
307+
304308 else :
305309 # Consume decimal/octal digits
306310 self ._read_while ("0123456789" )
@@ -318,7 +322,9 @@ def _read_number(self) -> str:
318322 self ._read_while ("uUlL" )
319323
320324 # Convert octal: leading 0 without 0x/0b → insert 'o'
321- if len (raw ) > 1 and raw [0 ] == "0" and raw [1 ].lower () not in ("x" , "b" ):
325+ if len (raw ) > 1 and raw [0 ] == "0" and raw [1 ].lower () not in ("x" , "b" , "o" ):
326+ if raw [1 ] not in "01234567" :
327+ raise self ._error ("invalid octal literal" )
322328 raw = raw [0 ] + "o" + raw [1 :]
323329
324330 return raw
@@ -396,30 +402,6 @@ def _read_preprocessor(self) -> None:
396402
397403 self ._emit (TokenType .STRING , value , line )
398404
399- def _read_lookup (self ) -> None :
400- """Read a lookup definition: ``$name = { dict }``."""
401- line = self ._line
402- col = self ._column
403- start = self ._pos
404-
405- self ._expect ("$" ) # Consume `$`
406-
407- # Read until end of the {...} block
408- brace_depth = 0
409- while not self .eof :
410- ch = self ._current ()
411- if ch == "{" :
412- brace_depth += 1
413- elif ch == "}" :
414- brace_depth -= 1
415- if brace_depth == 0 :
416- self ._expect ("}" ) # Consume final `}`
417- break
418- self ._take ()
419-
420- value = self ._get (start , self ._pos )
421- self ._emit (TokenType .LOOKUP , value .strip (), line , col )
422-
423405 def tokenize (self ) -> list [Token ]:
424406 """Tokenize the input data and return a list of tokens."""
425407 while not self .eof :
@@ -490,10 +472,6 @@ def tokenize(self) -> list[Token]:
490472 elif ch in _SINGLE_CHARS :
491473 self ._emit (_SINGLE_CHARS [ch ], self ._take (), line , col )
492474
493- elif ch == "$" :
494- # Custom lookup definition
495- self ._read_lookup ()
496-
497475 else :
498476 raise self ._error (f"unexpected character { ch !r} " , line = line )
499477
0 commit comments