11#!/usr/bin/env python3
22# -*- coding: utf-8 -*-
3-
43import re
54import sys
65from collections import defaultdict
7- from typing import Any , Dict , List , NamedTuple , Optional , Tuple , Union
6+ from types import MappingProxyType
7+ from typing import Any , Final , NamedTuple
8+
9+ from src .api import errmsg , global_
810
9- import src . api . global_
10- from src . arch . z80 . peephole import evaluator , pattern
11+ from . import pattern
12+ from . evaluator import BINARY , FN , OPERS , UNARY , Evaluator
1113
12- TreeType = List [ Union [ str , List [ Any ] ]]
14+ TreeType = list [ str | list [ "TreeType" ]]
1315
14- COMMENT = ";;"
16+ COMMENT : Final [ str ] = ";;"
1517RE_REGION = re .compile (r"([_a-zA-Z][a-zA-Z0-9]*)[ \t]*{{" )
1618RE_DEF = re .compile (r"([_a-zA-Z][a-zA-Z0-9]*)[ \t]*:[ \t]*(.*)" )
1719RE_IFPARSE = re .compile (r'"(""|[^"])*"|[(),]|\b[_a-zA-Z]+\b|[^," \t()]+' )
2729O_FLAG = "OFLAG"
2830
2931# Operators : priority (lower number -> highest priority)
30- IF_OPERATORS = {
31- evaluator .FN .OP_NMUL : 3 ,
32- evaluator .FN .OP_NDIV : 3 ,
33- evaluator .FN .OP_PLUS : 5 ,
34- evaluator .FN .OP_NPLUS : 5 ,
35- evaluator .FN .OP_NSUB : 5 ,
36- evaluator .FN .OP_NE : 10 ,
37- evaluator .FN .OP_EQ : 10 ,
38- evaluator .FN .OP_AND : 15 ,
39- evaluator .FN .OP_OR : 20 ,
40- evaluator .FN .OP_IN : 25 ,
41- evaluator .FN .OP_COMMA : 30 ,
42- }
32+ IF_OPERATORS : Final [MappingProxyType [FN , int ]] = MappingProxyType (
33+ {
34+ FN .OP_NMUL : 3 ,
35+ FN .OP_NDIV : 3 ,
36+ FN .OP_PLUS : 5 ,
37+ FN .OP_NPLUS : 5 ,
38+ FN .OP_NSUB : 5 ,
39+ FN .OP_NE : 10 ,
40+ FN .OP_EQ : 10 ,
41+ FN .OP_AND : 15 ,
42+ FN .OP_OR : 20 ,
43+ FN .OP_IN : 25 ,
44+ FN .OP_COMMA : 30 ,
45+ }
46+ )
4347
4448
4549# Which one of the above are REGIONS (that is, {{ ... }} blocks)
5357REQUIRED = (REG_REPLACE , REG_WITH , O_LEVEL , O_FLAG )
5458
5559
56- def simplify_expr (expr : List [Any ]) -> List [Any ]:
60+ def simplify_expr (expr : list [Any ]) -> list [Any ]:
5761 """Simplifies ("unnest") a list, removing redundant brackets.
5862 i.e. [[x, [[y]]] becomes [x, [y]]
5963 """
@@ -75,14 +79,14 @@ class SourceLine(NamedTuple):
7579# Defines a define expr with its linenum
7680class DefineLine (NamedTuple ):
7781 lineno : int
78- expr : evaluator . Evaluator
82+ expr : Evaluator
7983
8084
81- def parse_ifline (if_line : str , lineno : int ) -> Optional [ TreeType ] :
85+ def parse_ifline (if_line : str , lineno : int ) -> TreeType | None :
8286 """Given a line from within a IF region (i.e. $1 == "af'")
8387 returns it as a list of tokens ['$1', '==', "af'"]
8488 """
85- stack : List [TreeType ] = []
89+ stack : list [TreeType ] = []
8690 expr : TreeType = []
8791 paren = 0
8892 error_ = False
@@ -98,7 +102,7 @@ def parse_ifline(if_line: str, lineno: int) -> Optional[TreeType]:
98102
99103 tok = qq .group ()
100104 if not RE_ID .match (tok ):
101- for oper in evaluator . OPERS :
105+ for oper in OPERS :
102106 if tok .startswith (oper ):
103107 tok = tok [: len (oper )]
104108 break
@@ -111,19 +115,19 @@ def parse_ifline(if_line: str, lineno: int) -> Optional[TreeType]:
111115 expr = []
112116 continue
113117
114- if tok in evaluator . UNARY :
118+ if tok in UNARY :
115119 stack .append (expr )
116120 expr = [tok ]
117121 continue
118122
119123 if tok == ")" :
120124 paren -= 1
121125 if paren < 0 :
122- src . api . errmsg .warning (lineno , "Too much closed parenthesis" )
126+ errmsg .warning (lineno , "Too much closed parenthesis" )
123127 return None
124128
125- if expr and expr [- 1 ] == evaluator . FN .OP_COMMA :
126- src . api . errmsg .warning (lineno , "missing element in list" )
129+ if expr and expr [- 1 ] == FN .OP_COMMA :
130+ errmsg .warning (lineno , "missing element in list" )
127131 return None
128132
129133 stack [- 1 ].append (expr )
@@ -133,31 +137,31 @@ def parse_ifline(if_line: str, lineno: int) -> Optional[TreeType]:
133137 tok = tok [1 :- 1 ]
134138 expr .append (tok )
135139
136- if tok == evaluator . FN .OP_COMMA :
140+ if tok == FN .OP_COMMA :
137141 if len (expr ) < 2 or expr [- 2 ] == tok :
138- src . api . errmsg .warning (lineno , "Unexpected {} in list" .format (tok ))
142+ errmsg .warning (lineno , "Unexpected {} in list" .format (tok ))
139143 return None
140144
141145 while len (expr ) == 2 and isinstance (expr [- 2 ], str ):
142- op : Union [ str , TreeType ] = expr [- 2 ]
143- if op in evaluator . UNARY :
146+ op : str | TreeType = expr [- 2 ]
147+ if op in UNARY :
144148 stack [- 1 ].append (expr )
145149 expr = stack .pop ()
146150 else :
147151 break
148152
149- if len (expr ) == 3 and expr [1 ] != evaluator . FN .OP_COMMA :
150- left_ , op , right_ = expr # type: ignore
153+ if len (expr ) == 3 and expr [1 ] != FN .OP_COMMA :
154+ left_ , op , right_ = expr
151155 if not isinstance (op , str ) or op not in IF_OPERATORS :
152- src . api . errmsg .warning (lineno , "Unexpected binary operator '{0}'" .format (op ))
156+ errmsg .warning (lineno , "Unexpected binary operator '{0}'" .format (op ))
153157 return None
154158 if isinstance (left_ , list ) and len (left_ ) == 3 and IF_OPERATORS [left_ [- 2 ]] > IF_OPERATORS [op ]:
155159 expr = [[left_ [:- 2 ], left_ [- 2 ], [left_ [- 1 ], op , right_ ]]] # Rebalance tree
156160 else :
157161 expr = [expr ]
158162
159163 if not error_ and paren :
160- src . api . errmsg .warning (lineno , "unclosed parenthesis in IF section" )
164+ errmsg .warning (lineno , "unclosed parenthesis in IF section" )
161165 return None
162166
163167 while stack and not error_ :
@@ -166,32 +170,32 @@ def parse_ifline(if_line: str, lineno: int) -> Optional[TreeType]:
166170
167171 if len (expr ) == 2 :
168172 op = expr [0 ]
169- if not isinstance (op , str ) or op not in evaluator . UNARY :
170- src . api . errmsg .warning (lineno , "unexpected unary operator '{0}'" .format (op ))
173+ if not isinstance (op , str ) or op not in UNARY :
174+ errmsg .warning (lineno , "unexpected unary operator '{0}'" .format (op ))
171175 return None
172176 elif len (expr ) == 3 :
173177 op = expr [1 ]
174- if not isinstance (op , str ) or op not in evaluator . BINARY :
175- src . api . errmsg .warning (lineno , "unexpected binary operator '{0}'" .format (op ))
178+ if not isinstance (op , str ) or op not in BINARY :
179+ errmsg .warning (lineno , "unexpected binary operator '{0}'" .format (op ))
176180 return None
177181
178182 if error_ :
179- src . api . errmsg .warning (lineno , "syntax error in IF section" )
183+ errmsg .warning (lineno , "syntax error in IF section" )
180184 return None
181185
182186 return simplify_expr (expr )
183187
184188
185- def parse_define_line (sourceline : SourceLine ) -> Tuple [ Optional [ str ], Optional [ TreeType ] ]:
189+ def parse_define_line (sourceline : SourceLine ) -> tuple [ str | None , TreeType | None ]:
186190 """Given a line $nnn = <expression>, returns a tuple the parsed
187191 ("$var", [expression]) or None, None if error."""
188192 if "=" not in sourceline .line :
189- src . api . errmsg .warning (sourceline .lineno , "assignation '=' not found" )
193+ errmsg .warning (sourceline .lineno , "assignation '=' not found" )
190194 return None , None
191195
192- result : List [str ] = [x .strip () for x in sourceline .line .split ("=" , 1 )]
196+ result : list [str ] = [x .strip () for x in sourceline .line .split ("=" , 1 )]
193197 if not pattern .RE_SVAR .match (result [0 ]): # Define vars
194- src . api . errmsg .warning (sourceline .lineno , "'{0}' not a variable name" .format (result [0 ]))
198+ errmsg .warning (sourceline .lineno , "'{0}' not a variable name" .format (result [0 ]))
195199 return None , None
196200
197201 right_part = parse_ifline (result [1 ], sourceline .lineno )
@@ -201,7 +205,7 @@ def parse_define_line(sourceline: SourceLine) -> Tuple[Optional[str], Optional[T
201205 return result [0 ], right_part
202206
203207
204- def parse_str (spec : str ) -> Optional [ Dict [ str , Union [ str , int , TreeType ]]] :
208+ def parse_str (spec : str ) -> dict [ str , str | int | TreeType ] | None :
205209 """Given a string with an optimizer template definition,
206210 parses it and return a python object as a result.
207211 If any error is detected, fname will be used as filename.
@@ -210,26 +214,26 @@ def parse_str(spec: str) -> Optional[Dict[str, Union[str, int, TreeType]]]:
210214 ST_INITIAL = 0
211215 ST_REGION = 1
212216
213- result : Dict [str , Any ] = defaultdict (list )
217+ result : dict [str , Any ] = defaultdict (list )
214218 state = ST_INITIAL
215219 line_num = 0
216220 region_name = None
217221 is_ok = True
218222
219- def add_entry (key : str , val : Union [ str , int , TreeType ] ) -> bool :
223+ def add_entry (key : str , val : str | int | TreeType ) -> bool :
220224 key = key .upper ()
221225 if key in result :
222- src . api . errmsg .warning (line_num , "duplicated definition {0}" .format (key ))
226+ errmsg .warning (line_num , "duplicated definition {0}" .format (key ))
223227 return False
224228
225229 if key not in REGIONS and key not in SCALARS :
226- src . api . errmsg .warning (line_num , "unknown definition parameter '{0}'" .format (key ))
230+ errmsg .warning (line_num , "unknown definition parameter '{0}'" .format (key ))
227231 return False
228232
229233 if key in NUMERIC :
230234 assert isinstance (val , str )
231235 if not RE_INT .match (val ):
232- src . api . errmsg .warning (line_num , "field '{0} must be integer" .format (key ))
236+ errmsg .warning (line_num , "field '{0} must be integer" .format (key ))
233237 return False
234238 val = int (val )
235239
@@ -238,7 +242,7 @@ def add_entry(key: str, val: Union[str, int, TreeType]) -> bool:
238242
239243 def check_entry (key : str ) -> bool :
240244 if key not in result :
241- src . api . errmsg .warning (line_num , "undefined section {0}" .format (key ))
245+ errmsg .warning (line_num , "undefined section {0}" .format (key ))
242246 return False
243247
244248 return True
@@ -279,7 +283,7 @@ def check_entry(key: str) -> bool:
279283 region_name = None
280284 continue
281285
282- src . api . errmsg .warning (line_num , "syntax error. Cannot parse file" )
286+ errmsg .warning (line_num , "syntax error. Cannot parse file" )
283287 is_ok = False
284288 break
285289
@@ -291,10 +295,10 @@ def check_entry(key: str) -> bool:
291295 is_ok = False
292296 break
293297 if var_ in defined_vars :
294- src . api . errmsg .warning (source_line .lineno , "duplicated variable '{0}'" .format (var_ ))
298+ errmsg .warning (source_line .lineno , "duplicated variable '{0}'" .format (var_ ))
295299 is_ok = False
296300 break
297- defines .append ([var_ , DefineLine (expr = evaluator . Evaluator (expr ), lineno = source_line .lineno )])
301+ defines .append ([var_ , DefineLine (expr = Evaluator (expr ), lineno = source_line .lineno )])
298302 defined_vars .add (var_ )
299303 result [REG_DEFINE ] = defines
300304
@@ -313,25 +317,25 @@ def check_entry(key: str) -> bool:
313317
314318 if is_ok :
315319 if not result [REG_REPLACE ]: # Empty REPLACE region??
316- src . api . errmsg .warning (line_num , "empty region {0}" .format (REG_REPLACE ))
320+ errmsg .warning (line_num , "empty region {0}" .format (REG_REPLACE ))
317321 is_ok = False
318322
319323 if not is_ok :
320- src . api . errmsg .warning (line_num , "this optimizer template will be ignored due to errors" )
324+ errmsg .warning (line_num , "this optimizer template will be ignored due to errors" )
321325 return None
322326
323327 return result
324328
325329
326330def parse_file (fname : str ):
327331 """Opens and parse a file given by filename"""
328- tmp = src . api . global_ .FILENAME
329- src . api . global_ .FILENAME = fname # set filename so it shows up in error/warning msgs
332+ tmp = global_ .FILENAME
333+ global_ .FILENAME = fname # set filename so it shows up in error/warning msgs
330334
331335 with open (fname , "rt" ) as f :
332336 result = parse_str (f .read ())
333337
334- src . api . global_ .FILENAME = tmp # restores original filename
338+ global_ .FILENAME = tmp # restores original filename
335339 return result
336340
337341
0 commit comments