@@ -27,65 +27,142 @@ def has_overlay_annotations(lines):
2727 return any (ann in line for ann in annotations for line in lines )
2828
2929
30- def insert_toplevel_maybe_local_annotation (filename , lines ):
30+ def is_line_comment (line ):
31+ return line .startswith ("//" ) or (line .startswith ("/*" ) and line .endswith ("*/" ))
32+
33+
34+ def find_file_level_module_declaration (lines ):
3135 '''
32- Find a suitable place to insert an overlay[local?] annotation at the top of the file.
33- Return a pair: (string describing action taken, modified content as list of lines).
36+ Returns the index of the existing file-level module declaration if one
37+ exists. Returns None otherwise.
38+ '''
39+ comment = False
40+ i = - 1
41+ for line in lines :
42+ i = i + 1
43+ trimmed = line .strip ()
44+
45+ if is_line_comment (trimmed ):
46+ continue
47+ elif trimmed .startswith ("/*" ):
48+ comment = True
49+ elif comment and trimmed .endswith ("*/" ):
50+ comment = False
51+ elif not comment and trimmed .endswith ("module;" ):
52+ return i
53+
54+ return None
55+
56+
57+ def is_file_module_qldoc (i , lines ):
58+ '''
59+ Assuming a qldoc ended on line i, determine if it belongs to the implicit
60+ file-level module. If it is followed by another qldoc or imports, then it
61+ does and if it is followed by any other non-empty, non-comment lines, then
62+ we assume that is a declaration of some kind and the qldoc is attached to
63+ that declaration.
64+ '''
65+ comment = False
66+
67+ for line in lines [i + 1 :]:
68+ trimmed = line .strip ()
69+
70+ if trimmed .startswith ("import " ) or trimmed .startswith ("private import " ) or trimmed .startswith ("/**" ):
71+ return True
72+ elif is_line_comment (trimmed ) or not trimmed :
73+ continue
74+ elif trimmed .startswith ("/*" ):
75+ comment = True
76+ elif comment and trimmed .endswith ("*/" ):
77+ comment = False
78+ elif not comment and trimmed :
79+ return False
80+
81+ return True
82+
83+
84+ def find_file_module_qldoc_declaration (lines ):
85+ '''
86+ Returns the index of last line of the implicit file module qldoc if one
87+ exists. Returns None otherwise.
3488 '''
35- out_lines = []
36- status = 0
3789
90+ i = - 1
91+ qldoc = False
92+ comment = False
3893 for line in lines :
39- if status == 0 and line .rstrip ().endswith ("module;" ):
40- out_lines .append ("overlay[local?]\n " )
41- status = 1
42- out_lines .append (line )
94+ i = i + 1
95+ trimmed = line .strip ()
4396
44- if status == 1 :
45- return (f"Annotating \" { filename } \" via existing file-level module statement" , out_lines )
97+ if trimmed .startswith ("//" ):
98+ continue
99+ elif (qldoc or trimmed .startswith ("/**" )) and trimmed .endswith ("*/" ):
100+ # a qldoc just ended; determine if it belongs to the implicit file module
101+ if is_file_module_qldoc (i , lines ):
102+ return i
103+ else :
104+ return None
105+ elif trimmed .startswith ("/**" ):
106+ qldoc = True
107+ elif trimmed .startswith ("/*" ):
108+ comment = True
109+ elif comment and trimmed .endswith ("*/" ):
110+ comment = False
111+ elif (not qldoc and not comment ) and trimmed :
112+ return None
113+
114+ return None
115+
116+
117+ def only_comments (lines ):
118+ '''
119+ Returns true if the lines contain only comments and empty lines.
120+ '''
121+ comment = False
46122
47- out_lines = []
48- empty_line_buffer = []
49- status = 0
50123 for line in lines :
51124 trimmed = line .strip ()
52- if not trimmed :
53- empty_line_buffer . append ( line )
125+
126+ if not trimmed or is_line_comment ( trimmed ):
54127 continue
55- if status <= 1 and trimmed .endswith ("*/" ):
56- status = 2
57- elif status == 0 and trimmed .startswith ("/**" ):
58- status = 1
59- elif status == 0 and not trimmed .startswith ("/*" ):
60- out_lines .append ("overlay[local?]\n " )
61- out_lines .append ("module;\n " )
62- out_lines .append ("\n " )
63- status = 3
64- elif status == 2 and (trimmed .startswith ("import " ) or trimmed .startswith ("private import " )):
65- out_lines .append ("overlay[local?]\n " )
66- out_lines .append ("module;\n " )
67- status = 3
68- elif status == 2 and (trimmed .startswith ("class " ) or trimmed .startswith ("predicate " )
69- or trimmed .startswith ("module " ) or trimmed .startswith ("signature " )):
70- out_lines = ["overlay[local?]\n " , "module;\n " , "\n " ] + out_lines
71- status = 3
72- elif status == 2 and trimmed .startswith ("/*" ):
73- out_lines .append ("overlay[local?]\n " )
74- out_lines .append ("module;\n " )
75- status = 3
76- elif status == 2 :
77- status = 4
78- if empty_line_buffer :
79- out_lines += empty_line_buffer
80- empty_line_buffer = []
81- out_lines .append (line )
82- if status == 3 :
83- out_lines += empty_line_buffer
128+ elif trimmed .startswith ("/*" ):
129+ comment = True
130+ elif comment and trimmed .endswith ("*/" ):
131+ comment = False
132+ elif comment :
133+ continue
134+ elif trimmed :
135+ return False
136+
137+ return True
138+
139+
140+ def insert_toplevel_maybe_local_annotation (filename , lines ):
141+ '''
142+ Find a suitable place to insert an overlay[local?] annotation at the top of the file.
143+ Returns a pair consisting of description and the modified lines or None if no overlay
144+ annotation is necessary (e.g., for files that only contain comments).
145+ '''
146+ if only_comments (lines ):
147+ return None
148+
149+ i = find_file_level_module_declaration (lines )
150+ if not i == None :
151+ out_lines = lines [:i ]
152+ out_lines .append ("overlay[local?]\n " )
153+ out_lines .extend (lines [i :])
154+ return (f"Annotating \" { filename } \" via existing file-level module statement" , out_lines )
84155
85- if status == 3 :
86- return (f"Annotating \" { filename } \" after file-level module qldoc" , out_lines )
156+ i = find_file_module_qldoc_declaration (lines )
157+ if not i == None :
158+ out_lines = lines [:i + 1 ]
159+ out_lines .append ("overlay[local?]\n " )
160+ out_lines .append ("module;\n " )
161+ out_lines .extend (lines [i + 1 :])
162+ return (f"Annotating \" { filename } \" which has a file-level module qldoc" , out_lines )
87163
88- raise Exception (f"Failed to annotate \" { filename } \" as overlay[local?]." )
164+ out_lines = ["overlay[local?]\n " , "module;\n " , "\n " ] + lines
165+ return (f"Annotating \" { filename } \" without file-level module qldoc" , out_lines )
89166
90167
91168def insert_overlay_caller_annotations (lines ):
0 commit comments