11#!/usr/bin/env python3
22#
3- # This script checks all the RUL0 files for errors such as sinkhole bugs..
3+ # This script checks all the RUL0 files for errors such as sinkhole bugs.
44# If any are found, they are printed to stdout and the script exits with a non-zero return code.
55#
66# Minimum requirement: Python 3.12+
99import sys
1010import os
1111import itertools
12+ import re
1213
1314SRC_DIRS = [
1415 "Controller/RUL0" ,
@@ -39,14 +40,39 @@ def parse_layout(lines):
3940 return cells
4041
4142
42- def check_cons_layout (cell_lines , cons_lines ):
43+ def parse_checktypes (lines ):
44+ definitions = [line [(line .index ("=" )+ 1 ):].strip () for line in drop_comments (l for _ , l in lines )]
45+ static_cells = {row [0 ]: "optional" not in row and "check" not in row
46+ for row in definitions if row }
47+ return static_cells
48+
49+
50+ def _stringify_layout (lines ):
51+ return "" .join (f" { line_no } : { line } " for line_no , line in lines )
52+
53+
54+ def check_cons_layout (cell_lines , checktype_lines , cons_lines ):
4355 cell_layout = parse_layout (cell_lines )
4456 cons_layout = parse_layout (cons_lines )
45- bad_cells = [xy for xy in cons_layout .keys () if xy not in cell_layout ]
57+ static_cells = parse_checktypes (checktype_lines )
58+ undefined_cells = set (c for c in cell_layout .values () if c != '+' and c not in static_cells )
59+ if undefined_cells :
60+ raise Exception (f"Missing CheckType definition for cell { ', ' .join (undefined_cells )} :\n { _stringify_layout (cell_lines )} " )
61+ bad_cells = [xy for xy in cons_layout .keys () if xy not in cell_layout ] # constraint != '.' but cell == '.'
62+ if not bad_cells :
63+ bad_cells = [xy for xy , c in cell_layout .items ()
64+ if static_cells .get (c ) and
65+ (xy not in cons_layout or cons_layout [xy ] == '.' )] # cell != '.' but constraint == '.'
4666 if bad_cells :
47- cell_layout_str = "" .join (f" { line_no } : { line } " for line_no , line in cell_lines )
48- cons_layout_str = "" .join (f" { line_no } : { line } " for line_no , line in cons_lines )
49- raise Exception (f"Potential sinkhole bug in ConsLayout at cells { ' ' .join (map (str , bad_cells ))} :\n { cell_layout_str } ---\n { cons_layout_str } " )
67+ raise Exception (f"Potential sinkhole bug in ConsLayout at cells { ' ' .join (map (str , bad_cells ))} :\n { _stringify_layout (cell_lines )} ---\n { _stringify_layout (cons_lines )} " )
68+
69+
70+ _relevant_line_starts = (
71+ re .compile (r"^CellLayout" , re .IGNORECASE ),
72+ re .compile (r"^CheckType" , re .IGNORECASE ),
73+ re .compile (r"^ConsLayout" , re .IGNORECASE ),
74+ re .compile (r"^\[HighwayIntersectionInfo" ),
75+ )
5076
5177
5278def scan_rul0_file (lines ):
@@ -55,18 +81,25 @@ def relevant_lines():
5581 line = line .lstrip ()
5682 if line .startswith (";###RHD###" ): # TODO for simplicity, we ignore LHD for now
5783 line = line [10 :]
58- if line .startswith ("CellLayout" ) or line .startswith ("ConsLayout" ):
59- yield line_no , line
60-
61- grouped = list ((b , list (it )) for b , it in itertools .groupby (relevant_lines (), key = lambda tup : tup [1 ].startswith ("ConsLayout" )))
62- if grouped and grouped [0 ][0 ]:
63- yield "Found no matching CellLayout for first ConsLayout in file"
64- elif grouped and not grouped [- 1 ][0 ]:
65- yield "Found no matching ConsLayout for last CellLayout in file"
66- else :
67- for ((_ , cell_lines ), (_ , cons_lines )) in itertools .batched (grouped , 2 ):
84+ for re_idx , pattern in enumerate (_relevant_line_starts ):
85+ if pattern .match (line ):
86+ yield line_no , line , re_idx
87+ break
88+
89+ grouped = [list (it ) for heading , it in
90+ itertools .groupby (relevant_lines (), key = lambda tup : tup [2 ] == 3 )
91+ if not heading ]
92+ for grouped_lines in grouped :
93+ cell_lines = [(line_no , line ) for line_no , line , re_idx in grouped_lines if re_idx == 0 ]
94+ checktype_lines = [(line_no , line ) for line_no , line , re_idx in grouped_lines if re_idx == 1 ]
95+ cons_lines = [(line_no , line ) for line_no , line , re_idx in grouped_lines if re_idx == 2 ]
96+ if not cell_lines and not cons_lines : # checktype_lines might be non-empty in `CopyFrom` case
97+ continue
98+ elif not cell_lines or not checktype_lines or not cons_lines :
99+ yield f"Found no matching CellLayout, ConsLayout or CheckType definitions starting at line { grouped_lines [0 ][0 ]} "
100+ else :
68101 try :
69- check_cons_layout (cell_lines , cons_lines )
102+ check_cons_layout (cell_lines , checktype_lines , cons_lines )
70103 except Exception as err :
71104 yield str (err )
72105
0 commit comments