@@ -65,32 +65,37 @@ def __init__(self, text: str, filename: str) -> None:
6565 self .column = 1
6666 def tokenize (self ) -> List [Token ]:
6767 tokens : List [Token ] = []
68- while (not self ._eof ):
69- ch :str = self ._peek ()
68+ # Cache frequently used methods/values locally for speed.
69+ _peek = self ._peek
70+ _advance = self ._advance
71+ _is_identifier_start = self ._is_identifier_start
72+ symbols = SYMBOLS
73+ while not self ._eof :
74+ ch : str = _peek ()
7075 if (ch in " \t " ):
71- self . _advance ()
76+ _advance ()
7277 continue
7378 if (ch == "\r " ):
74- self . _advance ()
79+ _advance ()
7580 continue
7681 if (ch == "\n " ):
7782 tokens .append (Token ("NEWLINE" ,"\n " ,self .line ,self .column ))
78- self . _advance ()
83+ _advance ()
7984 continue
8085 if (ch == "#" ):
8186 self ._consume_comment ()
8287 continue
8388 if (ch in SYMBOLS ):
84- tokens .append (Token (SYMBOLS [ch ],ch ,self .line ,self .column ))
85- self . _advance ()
89+ tokens .append (Token (symbols [ch ], ch , self .line , self .column ))
90+ _advance ()
8691 continue
8792 if (ch == "-" ):
8893 tokens .append (self ._consume_signed_number ())
8994 continue
9095 if (ch in "01" ):
9196 tokens .append (self ._consume_unsigned_number ())
9297 continue
93- if ( self . _is_identifier_start (ch ) ):
98+ if _is_identifier_start (ch ):
9499 tokens .append (self ._consume_identifier ())
95100 continue
96101 raise ASMParseError (f"Unexpected character '{ ch } ' at { self .filename } :{ self .line } :{ self .column } " )
@@ -321,8 +326,16 @@ def _parse_expression(self) -> Expression:
321326 token = self ._peek ()
322327 if (token .type == "NUMBER" ):
323328 number :Token = self ._consume ("NUMBER" )
324- value :int = int (number .value ,2 )
325- return (Literal (location = self ._location_from_token (number ),value = value ))
329+ # NUMBER token may be prefixed with '-' for negative literals.
330+ sval = number .value
331+ if sval .startswith ("-" ):
332+ # safe parse of negative binary literal
333+ if len (sval ) == 1 :
334+ raise ASMParseError (f"Invalid numeric literal at line { number .line } " )
335+ value = - int (sval [1 :], 2 )
336+ else :
337+ value = int (sval , 2 )
338+ return Literal (location = self ._location_from_token (number ), value = value )
326339 if (token .type == "IDENT" ):
327340 ident :Token = self ._consume ("IDENT" )
328341 location :SourceLocation = self ._location_from_token (ident )
@@ -503,6 +516,8 @@ def __init__(self) -> None:
503516 self ._register_fixed ("BOR" , 2 , lambda a , b : a | b )
504517 self ._register_fixed ("BXOR" , 2 , lambda a , b : a ^ b )
505518 self ._register_fixed ("BNOT" , 1 , lambda a : ~ a )
519+ self ._register_fixed ("SHL" , 2 , self ._shift_left )
520+ self ._register_fixed ("SHR" , 2 , self ._shift_right )
506521 # Return the inclusive bit-slice [hi:lo] of `a` as an unsigned integer.
507522 # Bits are numbered starting at 0 for the least-significant bit.
508523 # Implementation note: uses masking so negative values yield their
@@ -563,7 +578,12 @@ def invoke(
563578 builtin = self .table .get (name )
564579 if builtin is None :
565580 raise ASMRuntimeError (f"Unknown function '{ name } '" , location = location )
566- builtin .validate (len (args ))
581+ # Inline validation to avoid a method call in hot path.
582+ supplied = len (args )
583+ if supplied < builtin .min_args :
584+ raise ASMRuntimeError (f"{ name } expects at least { builtin .min_args } arguments" , rewrite_rule = name )
585+ if builtin .max_args is not None and supplied > builtin .max_args :
586+ raise ASMRuntimeError (f"{ name } expects at most { builtin .max_args } arguments" , rewrite_rule = name )
567587 return builtin .impl (interpreter , args , arg_nodes , env , location )
568588 def _safe_div (self , a : int , b : int ) -> int :
569589 if b == 0 :
@@ -587,6 +607,16 @@ def _safe_pow(self, a: int, b: int) -> int:
587607 if b < 0 :
588608 raise ASMRuntimeError ("Negative exponent not supported" , rewrite_rule = "POW" )
589609 return pow (a , b )
610+
611+ def _shift_left (self , value : int , amount : int ) -> int :
612+ if amount < 0 :
613+ raise ASMRuntimeError ("SHL amount must be non-negative" , rewrite_rule = "SHL" )
614+ return value << amount
615+
616+ def _shift_right (self , value : int , amount : int ) -> int :
617+ if amount < 0 :
618+ raise ASMRuntimeError ("SHR amount must be non-negative" , rewrite_rule = "SHR" )
619+ return value >> amount
590620 def _lcm (self , a : int , b : int ) -> int :
591621 if a == 0 or b == 0 :
592622 return 0
0 commit comments