-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgen_codes_ml.py
160 lines (129 loc) · 4.75 KB
/
gen_codes_ml.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
from re import findall, DOTALL
from datetime import datetime
from typing import List, Dict
from yaml import safe_load
ENUMS_SOURCE_FILE = "somc/lib/report/error.ml"
EXPLANATIONS_FILE = "error_explanations.yaml"
OUT_FILE = "somc/lib/report/codes.ml"
HEADER = f"(* File generated by gen_codes_ml.py on {datetime.now().ctime()}. *)\n"
IMPORTS = "\nopen Error\n"
ERR_TO_CODE_DECL_FMT = "\nlet code_from_{name}_error : {type} -> int = function\n"
ERR_TO_CODE_BODY_FMT = " | {name}{underscore} -> {index}\n"
ANY_TO_CODE_DECL_FMT = "\nlet code_from_error = function\n"
ANY_TO_CODE_BODY_FMT = " | {uppername}_error e -> code_from_{name}_error e\n"
FROM_CODE_DECL_FMT = "\nlet error_name_from_code = function\n"
FROM_CODE_BODY_FMT = " | {index} -> Some (\"{kind}\", \"{name}\")\n"
FROM_CODE_TAIL_FMT = " | _ -> None\n"
GET_CODE_OPTFN = """
let get_code_opt = function
| Other_error _ -> None
| e -> Some (code_from_error e)
"""
EXPL_FROM_CODE_DECL_FMT = "\nlet explanation_from_code : int -> string option = function\n"
EXPL_FROM_CODE_BODY_FMT = " | {index} -> Some \"{explanation}\"\n"
EXPL_FROM_CODE_TAIL_FMT = " | _ -> None\n"
ENUM_REGEX = r"type (\w+)_error =\n(.*?)\nlet"
VARIANT_REGEX = r"\s*(\w+)(?: (.*?)\n)?"
PROPERTIES_REGEX = r"^(of [^\(]*)?(?:\(\*(?:\+(.*?)|-(.*?))\*\))?.*$"
class Variant:
index: int
name: str
has_value: bool
text: str
class Enum:
name: str
variants: List[Variant]
def read_enums(source: str):
enums = []
for m in findall(ENUM_REGEX, source, DOTALL):
eenum = Enum()
eenum.name = m[0]
eenum.variants = []
base_index = (len(enums) + 1) * 100
for v in findall(VARIANT_REGEX, m[1]):
variant = Variant()
variant.index = base_index + len(eenum.variants) + 1
variant.name = v[0]
props = findall(PROPERTIES_REGEX, v[1])[0]
variant.has_value = len(props[0]) > 0
if props[2]:
variant.text = props[2]
elif props[1]:
variant.text = variant.name \
.replace("_", " ").lower() \
+ " " + props[1]
else:
variant.text = variant.name \
.replace("_", " ").lower()
eenum.variants.append(variant)
enums.append(eenum)
return enums
def print_enums(enums: List[Enum]):
print(f"found {len(enums)} error enums:\n")
for e in enums:
print(f" {e.name} error: ({len(e.variants)} variants)")
for v in e.variants:
has_value = " _" if v.has_value else ""
print(f" {v.index}: {v.name}{has_value} \"{v.text}\"")
print()
def generate_file(enums: List[Enum], expls: Dict[str, Dict[str, str]]):
txt = HEADER
txt += IMPORTS
# let code_from_{name}_error = function ...
for e in enums:
txt += ERR_TO_CODE_DECL_FMT.format(
name = e.name,
type = e.name + "_error"
)
for v in e.variants:
txt += ERR_TO_CODE_BODY_FMT.format(
name = v.name,
underscore = ' _' if v.has_value else '',
index = v.index
)
# let code_from_error = function ...
txt += ANY_TO_CODE_DECL_FMT
for e in enums:
txt += ANY_TO_CODE_BODY_FMT.format(
uppername = e.name.title(),
name = e.name)
# let error_name_from_code = function ...
txt += FROM_CODE_DECL_FMT
for e in enums:
for v in e.variants:
txt += FROM_CODE_BODY_FMT.format(
index = v.index,
kind = e.name,
name = v.text
)
txt += FROM_CODE_TAIL_FMT
txt += GET_CODE_OPTFN
# let explanation_from_code
txt += EXPL_FROM_CODE_DECL_FMT
for e in enums:
if not e.name in expls.keys(): continue
for v in e.variants:
if not v.name in expls[e.name].keys(): continue
expl = expls[e.name][v.name].strip()
txt += EXPL_FROM_CODE_BODY_FMT.format(
index = v.index,
explanation = expl.replace("\\", "\\\\").replace("\"", "\\\"")
)
txt += EXPL_FROM_CODE_TAIL_FMT
# print("output:\n")
# print(" " + " ".join(txt.splitlines(True)) + "\n")
return txt
def main():
with open(ENUMS_SOURCE_FILE, "r") as f:
enums_source = f.read()
enums = read_enums(enums_source)
print_enums(enums)
with open(EXPLANATIONS_FILE, "r") as f:
expls = safe_load(f)
print("found explanations:")
for k, v in expls.items():
print(f" {k}: {len(v.keys())} explanations")
with open(OUT_FILE, 'w') as f:
f.write(generate_file(enums, expls))
if __name__ == "__main__":
main()