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