Skip to content

Commit eaf24a2

Browse files
committed
Extend and simplify overlay annotations script
1 parent 52f0e0c commit eaf24a2

1 file changed

Lines changed: 125 additions & 48 deletions

File tree

config/add-overlay-annotations.py

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

91168
def insert_overlay_caller_annotations(lines):

0 commit comments

Comments
 (0)