Skip to content

Commit 0f23582

Browse files
committed
Extend and simplify overlay annotations script
1 parent 72751c7 commit 0f23582

File tree

1 file changed

+122
-49
lines changed

1 file changed

+122
-49
lines changed

config/add-overlay-annotations.py

Lines changed: 122 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -29,65 +29,138 @@ 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.
3640
'''
37-
out_lines = []
38-
status = 0
41+
comment = False
42+
for i, line in enumerate(lines):
43+
trimmed = line.strip()
3944

40-
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)
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
4553

46-
if status == 1:
47-
return (f"Annotating \"{filename}\" via existing file-level module statement", out_lines)
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.
88+
'''
89+
90+
qldoc = False
91+
comment = False
92+
for i, line in enumerate(lines):
93+
trimmed = line.strip()
94+
95+
if trimmed.startswith("//"):
96+
continue
97+
elif (qldoc or trimmed.startswith("/**")) and trimmed.endswith("*/"):
98+
# a qldoc just ended; determine if it belongs to the implicit file module
99+
if is_file_module_qldoc(i, lines):
100+
return i
101+
else:
102+
return None
103+
elif trimmed.startswith("/**"):
104+
qldoc = True
105+
elif trimmed.startswith("/*"):
106+
comment = True
107+
elif comment and trimmed.endswith("*/"):
108+
comment = False
109+
elif (not qldoc and not comment) and trimmed:
110+
return None
111+
112+
return None
113+
114+
115+
def only_comments(lines):
116+
'''
117+
Returns true if the lines contain only comments and empty lines.
118+
'''
119+
comment = False
48120

49-
out_lines = []
50-
empty_line_buffer = []
51-
status = 0
52121
for line in lines:
53122
trimmed = line.strip()
54-
if not trimmed:
55-
empty_line_buffer.append(line)
123+
124+
if not trimmed or is_line_comment(trimmed):
56125
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
126+
elif trimmed.startswith("/*"):
127+
comment = True
128+
elif comment and trimmed.endswith("*/"):
129+
comment = False
130+
elif comment:
131+
continue
132+
elif trimmed:
133+
return False
134+
135+
return True
136+
137+
138+
def insert_toplevel_maybe_local_annotation(filename, lines):
139+
'''
140+
Find a suitable place to insert an overlay[local?] annotation at the top of the file.
141+
Returns a pair consisting of description and the modified lines or None if no overlay
142+
annotation is necessary (e.g., for files that only contain comments).
143+
'''
144+
if only_comments(lines):
145+
return None
146+
147+
i = find_file_level_module_declaration(lines)
148+
if not i == None:
149+
out_lines = lines[:i]
150+
out_lines.append("overlay[local?]\n")
151+
out_lines.extend(lines[i:])
152+
return (f"Annotating \"{filename}\" via existing file-level module statement", out_lines)
86153

87-
if status == 3:
88-
return (f"Annotating \"{filename}\" after file-level module qldoc", out_lines)
154+
i = find_file_module_qldoc_declaration(lines)
155+
if not i == None:
156+
out_lines = lines[:i+1]
157+
out_lines.append("overlay[local?]\n")
158+
out_lines.append("module;\n")
159+
out_lines.extend(lines[i+1:])
160+
return (f"Annotating \"{filename}\" which has a file-level module qldoc", out_lines)
89161

90-
raise Exception(f"Failed to annotate \"{filename}\" as overlay[local?].")
162+
out_lines = ["overlay[local?]\n", "module;\n", "\n"] + lines
163+
return (f"Annotating \"{filename}\" without file-level module qldoc", out_lines)
91164

92165

93166
def insert_overlay_caller_annotations(lines):

0 commit comments

Comments
 (0)