1let Categories := {}
2let OutputDirectory := Args[1]
3
4for FileName in dir(OutputDirectory) do
5	if FileName[-4, 0] = ".rst" then
6		print('Unlinking {FileName}\n')
7		file::unlink(OutputDirectory + "/" + FileName)
8	end
9end
10
11fun category_name(Type) do
12	if Type:find("State") then
13		ret "internal"
14	elseif let Match := Type % r"^ML(.*)T$" then
15		ret Match[2]:lower
16	else
17		ret Type
18	end
19end
20
21fun write_category(Category) do
22	print('Creating Category: {Category}\n')
23	let File := file('{OutputDirectory}/{Category:lower}.rst', "w")
24	File:write(Category, "\n")
25	for I in Category do File:write("=") end
26	File:write("\n\n")
27	File:write(".. include:: <isonum.txt>\n\n")
28	ret File
29end
30
31fun type_name(Type) do
32	Type or ret "any"
33	if let Match := Type % r"^ML(.*)T$" then
34		ret Match[2]:lower
35	else
36		ret Type
37	end
38end
39
40fun is_symbol(Method) (Method = "\"<op>\"") or (Method:lower = Method:upper)
41
42fun write_method(Category, Kind, Method, Params, Return, Description, Location) do
43	:<print('[{Category}] {Kind}: {Method}\n')
44	print("Params:\n")
45	for Param in Params do print('\t{Param}\n') end
46	print('Returns: {Return}\n')
47	print('Description: {Description}\n\n')>:
48
49	let File := Categories[Category, write_category]
50	let Title := stringbuffer()
51
52	var Terminator := ""
53	var Skip := 1
54	Title:write(':mini:`meth ')
55	if Method = "\"[]\"" then
56		Title:write("(", Params[1][1], ": ", type_name(Params[1][2]), ")[")
57		Terminator := "]"
58	:<elseif Method[1] = "\"" then
59		if Params:length = 1 and is_symbol(Method) then
60			Title:write('{Method[2, -1]}(', Params[1][1], ": ", type_name(Params[1][2]), ")")
61		else
62			Title:write("(", Params[1][1], ": ", type_name(Params[1][2]), ")")
63			if is_symbol(Method) then
64				Title:write(' {Method[2, -1]} ')
65			else
66				Title:write(':{Method[2, -1]}')
67				if Params:length > 1 then
68					Title:write("(")
69					Terminator := ")"
70				end
71			end
72		end>:
73	else
74		if let Match := Method % r"ML(\w+)T" then
75			Title:write('{Match[2]:lower}(')
76		elseif Method[1] = "\"" then
77			if is_symbol(Method) then
78				Title:write('{Method[2, -1]}(')
79			else
80				Title:write(':{Method[2, -1]}(')
81			end
82		else
83			Title:write('{Method}(')
84		end
85		Skip := 0
86		Terminator := ")"
87	end
88	var Seperator := ""
89	for Param in Params skip Skip do
90		Title:write(Seperator, Param[1], ": ", type_name(Param[2]))
91		Seperator := ", "
92	end
93	Title:write(Terminator, "`")
94	if Return:length > 0 then
95		Title:write(" |rarr| :mini:`", type_name(Return[1]), "`")
96		for Type in Return skip 1 do
97			Title:write(" or :mini:`", type_name(Type), "`")
98		end
99	end
100	let TitleString := Title:get
101	File:write(TitleString, "\n")
102	for Line in Description do
103		File:write("   ", Line, "\n")
104	end
105	for Param in Params do
106		if Param[3]:length > 0 then
107			File:write("   :", type_name(Param[2]), " ", Param[1], ": ", Param[3], "\n")
108		end
109	end
110	:>File:write('   *Defined at line {Location[2]} in {Location[1]}*\n')
111	File:write("\n")
112end
113
114fun write_function(Category, Kind, Name, Params, Return, Description, Location) do
115	:<print('[{Category}] {Kind}: {Name}\n')
116	print("Params:\n")
117	for Param in Params do print('\t{Param}\n') end
118	print('Returns: {Return}\n')
119	print('Description: {Description}\n')>:
120
121	let File := Categories[Category, write_category]
122	let Title := stringbuffer()
123
124	var Terminator := ""
125	Title:write(':mini:`fun ', Name, "(")
126	var Seperator := ""
127	for Param in Params do
128		Title:write(Seperator, Param[1], ": ", type_name(Param[2]))
129		Seperator := ", "
130	end
131	Title:write(")`")
132	if Return:length > 0 then
133		Title:write(" |rarr| :mini:`", type_name(Return[1]), "`")
134		for Type in Return skip 1 do
135			Title:write(" or :mini:`", type_name(Type), "`")
136		end
137	end
138	let TitleString := Title:get
139	File:write(TitleString, "\n")
140	for Line in Description do
141		File:write("   ", Line, "\n")
142	end
143	for Param in Params do
144		if Param in method then
145		elseif Param[3]:length > 0 then
146			File:write("   :", type_name(Param[2]), " ", Param[1], ": ", Param[3], "\n")
147		end
148	end
149	:>File:write('   *Defined at line {Location[2]} in {Location[1]}*\n')
150	File:write("\n")
151end
152
153fun write_constructor(Category, Kind, Name, Params, Return, Description, Location) do
154	:<print('[{Category}] {Kind}: {Name}\n')
155	print("Params:\n")
156	for Param in Params do print('\t{Param}\n') end
157	print('Returns: {Return}\n')
158	print('Description: {Description}\n')>:
159
160	let File := Categories[Category, write_category]
161	let Title := stringbuffer()
162
163	var Terminator := ""
164	Title:write(':mini:`constructor ', Name, "(")
165	var Seperator := ""
166	for Param in Params do
167		Title:write(Seperator, Param[1], ": ", type_name(Param[2]))
168		Seperator := ", "
169	end
170	Title:write(")`")
171	if Return:length > 0 then
172		Title:write(" |rarr| :mini:`", type_name(Return[1]), "`")
173		for Type in Return skip 1 do
174			Title:write(" or :mini:`", type_name(Type), "`")
175		end
176	end
177	let TitleString := Title:get
178	File:write(TitleString, "\n")
179	for Line in Description do
180		File:write("   ", Line, "\n")
181	end
182	for Param in Params do
183		if Param in method then
184		elseif Param[3]:length > 0 then
185			File:write("   :", type_name(Param[2]), " ", Param[1], ": ", Param[3], "\n")
186		end
187	end
188	:>File:write('   *Defined at line {Location[2]} in {Location[1]}*\n')
189	File:write("\n")
190end
191
192fun write_type(Category, Name, Parents, Description, Location) do
193	:<print('[{Category}] ML_TYPE: {Name} [{Parents}]\n')
194	print('Description: {Description}\n')>:
195
196	let File := Categories[Category, write_category]
197	let Title := stringbuffer()
198
199	Title:write(':mini:`type {type_name(Name)}')
200	if Parents:length > 0 then
201		Title:write(" < ")
202		var Seperator := ""
203		for Parent in Parents do
204			Title:write(Seperator, type_name(Parent))
205			Seperator := ", "
206		end
207	end
208	Title:write("`\n")
209	File:write(Title:get)
210	for Line in Description do
211		File:write("   ", Line, "\n")
212	end
213	:>File:write('   *Defined at line {Location[2]} in {Location[1]}*\n')
214	File:write("\n")
215end
216
217def Subscripts := {
218	"/0" is "₀", "/1" is "₁", "/2" is "₂", "/3" is "₃", "/4" is "₄",
219	"/5" is "₅", "/6" is "₆", "/7" is "₇", "/8" is "₈", "/9" is "₉",
220	"/i" is "ᵢ", "/j" is "ⱼ", "/m" is "ₘ", "/n" is "ₙ"
221}
222
223fun process(FileName) do
224	let File := file(FileName, 'r')
225	var DefaultCategory := "general"
226	if let Match := FileName % r"ml_([a-z]+).c" then
227		DefaultCategory := Match[2]
228	end
229	var LineNo := 0
230	loop
231		let Line := while File:read
232		:>print('{LineNo}: {Line}')
233		LineNo := old + 1
234		do
235			if Line % r"//!" then
236				DefaultCategory := Line[4, 0]:trim
237			elseif Line % r"^ML_METHOD_DECL" then
238			elseif Line % r"^ML_METHOD_ANON" then
239			elseif Line % r"^ML_METHOD" then
240				let Location := (FileName, LineNo)
241				let I := Line:find("("), J := Line:find(")")
242				let Kind := Line[1, I]
243				let Types := Line[I + 1, J] / ", "
244				var Method := Types:pop
245				var Category := DefaultCategory
246				let Params := []
247				var Return := []
248				let Description := []
249				loop
250					let Line := while File:read
251					LineNo := old + 1
252					if Line % r"//@" then
253						Method := Line[4, 0]:trim
254					elseif Line % r"^//<" then
255						if let J := Line:find(":", 4) then
256							let K := Line:find(" ", J + 2) or 0
257							Params:put((
258								Line[4, J]:trim:replace(Subscripts),
259								Line[J + 1, K]:trim,
260								Line[K, 0]:trim
261							))
262						else
263							let J := Line:find(" ", 4) or 0
264							Params:put((
265								Line[4, J]:trim:replace(Subscripts),
266								Types:pop,
267								Line[J, 0]:trim
268							))
269						end
270					elseif Line % r"^//>" then
271						Return := list(Line[4, 0] / "|", :trim, :replace(_, Subscripts))
272					elseif Line % r"^//!" then
273						Category := Line[4, 0]:trim
274					elseif Line % r"^// " then
275						Description:put(Line[4, 0]:replace(Subscripts))
276					else
277						exit
278					end
279				end
280				for Type in Types do
281					Params:put(('Arg/{Params:length + 1}':replace(Subscripts), Type, ""))
282				end
283				write_method(Category, Kind, Method, Params, Return, Description, Location)
284			elseif Line % r"^MATH_REAL" then
285				let Location := (FileName, LineNo)
286				let I := Line:find("("), J := Line:find(")")
287				let Kind := Line[1, I]
288				let Types := ["number"]
289				let Method := (Line[I + 1, J] / ", ")[2]
290				var Category := "math"
291				let Params := []
292				var Return := []
293				let Description := []
294				Params:put((
295					"Arg/1":trim:replace(Subscripts),
296					"number",
297					""
298				))
299				if Kind = "MATH_REAL_REAL" then
300					Params:put((
301						"Arg/2":trim:replace(Subscripts),
302						"number",
303						""
304					))
305				end
306				Return := ["number"]
307				write_method(Category, Kind, Method, Params, Return, Description, Location)
308			elseif Line % r"^ML_FUNCTION" then
309				let Location := (FileName, LineNo)
310				let I := Line:find("("), J := Line:find(")")
311				let Kind := Line[1, I]
312				var Name := Line[I + 1, J]:lower
313				var Category := DefaultCategory
314				let Params := []
315				var Return := []
316				let Description := []
317				loop
318					let Line := while File:read
319					LineNo := old + 1
320					if Line % r"//@" then
321						Name := Line[4, 0]:trim
322					elseif Line % r"^//<" then
323						if let J := Line:find(":", 4) then
324							let K := Line:find(" ", J + 2) or 0
325							Params:put([
326								Line[4, J]:trim:replace(Subscripts),
327								Line[J + 1, K]:trim,
328								Line[K, 0]:trim
329							])
330						else
331							let J := Line:find(" ", 4) or 0
332							Params:put([
333								Line[4, J]:trim:replace(Subscripts),
334								"MLAnyT",
335								Line[J, 0]:trim
336							])
337						end
338					elseif Line % r"^//>" then
339						Return := list(Line[4, 0] / "|", :trim, :replace(_, Subscripts))
340					elseif Line % r"^//!" then
341						Category := Line[4, 0]:trim
342					elseif Line % r"^// " then
343						Description:put(Line[4, 0]:replace(Subscripts))
344					elseif let Match := Line % r"ML_CHECK(X?)_ARG_COUNT\(([0-9]+)\)" then
345						let Count := integer(Match[3])
346						for I in (Params:length + 1) .. Count do
347							Params:put(['Arg/{I}':replace(Subscripts), "any", ""])
348						end
349					elseif let Match := Line % r"ML_CHECK(X?)_ARG_TYPE\(([0-9]+), (\w+)\)" then
350						let Index := integer(Match[3]) + 1
351						Params[Index][2] := type_name(Match[4])
352					else
353						exit
354					end
355				end
356				write_function(Category, Kind, Name, Params, Return, Description, Location)
357			elseif Line % r"^ML_CONSTRUCTOR" then
358				let Location := (FileName, LineNo)
359				let I := Line:find("("), J := Line:find(")")
360				let Kind := Line[1, I]
361				var Name := Line[I + 1, J]:lower
362				var Category := DefaultCategory
363				let Params := []
364				let Description := []
365				loop
366					let Line := while File:read
367					LineNo := old + 1
368					if Line % r"//@" then
369						Name := Line[4, 0]:trim
370					elseif Line % r"^//<" then
371						if let J := Line:find(":", 4) then
372							let K := Line:find(" ", J + 2) or 0
373							Params:put([
374								Line[4, J]:trim:replace(Subscripts),
375								Line[J + 1, K]:trim,
376								Line[K, 0]:trim
377							])
378						else
379							let J := Line:find(" ", 4) or 0
380							Params:put([
381								Line[4, J]:trim:replace(Subscripts),
382								"MLAnyT",
383								Line[J, 0]:trim
384							])
385						end
386					elseif Line % r"^//!" then
387						Category := Line[4, 0]:trim
388					elseif Line % r"^// " then
389						Description:put(Line[4, 0]:replace(Subscripts))
390					else
391						exit
392					end
393				end
394				write_constructor(Category, Kind, Name, Params, [Name], Description, Location)
395			elseif Line % r"^(ML_TYPE|ML_INTERFACE)" then
396				let Location := (FileName, LineNo)
397				let I := Line:find("("), J := Line:find("(", I + 1)
398				let K := Line:find(")", J + 1)
399				var Name := Line[I + 1, J]:trim(", ")
400				var Category := DefaultCategory
401				let Parents := Line[J + 1, K] / ", "
402				let Title := Line[K + 1, 0]:trim(", ){")
403				let Description := []
404				loop
405					let Line := while File:read
406					LineNo := old + 1
407					if Line % r"//@" then
408						Name := Line[4, 0]:trim
409					elseif Line % r"^//!" then
410						Category := Line[4, 0]:trim
411					elseif Line % r"^// " then
412						Description:put(Line[4, 0]:replace(Subscripts))
413					else
414						exit
415					end
416				end
417				write_type(Category, Name, Parents, Description, Location)
418			elseif Line % r"^#define " then
419				loop
420					let Line := while File:read
421					LineNo := old + 1
422					until Line = "\n"
423				end
424			end
425		on Error do
426			print(Line, "\n")
427			print('Error: {Error:message} in {FileName}:{LineNo}\n')
428			for Source in Error:trace do
429				print('\t{Source[1]}:{Source[2]}\n')
430			end
431		end
432	end
433	File:close
434end
435
436let FileNames := list(Args skip 1)
437FileNames:sort(; A, B) do
438	A = "ml_types.c" and ret A
439	B = "ml_types.c" and ret nil
440end
441
442for FileName in FileNames do process(FileName) end
443
444for Category, File in Categories do File:close end
445