Skip to content

Commit 515eac3

Browse files
committed
Extend and simplify overlay annotations script
1 parent 72751c7 commit 515eac3

File tree

1 file changed

+125
-48
lines changed

1 file changed

+125
-48
lines changed

config/add-overlay-annotations.py

Lines changed: 125 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -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

93170
def insert_overlay_caller_annotations(lines):

0 commit comments

Comments
 (0)