1# This file is part of OpenTTD.
2# OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
3# OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
4# See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
5
6#
7# Awk script to automatically generate the code needed
8# to export the script APIs to Squirrel.
9#
10# Note that arrays are 1 based...
11#
12
13
14BEGIN {
15	cls = ""
16	api_selected = ""
17	cls_in_api = ""
18	cls_level = 0
19	skip_function_body = "false"
20	skip_function_par = 0
21	RS = "\r|\n"
22	apis = tolower(api)
23	if (apis == "gs") apis = "game"
24}
25
26{
27	gsub(/Script/, api)
28}
29
30{
31	if (skip_function_body == "true") {
32		input = $0
33		gsub(/[^{]/, "", input)
34		skip_function_par += length(input)
35		if (skip_function_par > 0) {
36			input = $0
37			gsub(/[^}]/, "", input)
38			skip_function_par -= length(input)
39			if (skip_function_par == 0) skip_function_body = "false"
40		}
41	}
42	if (skip_function_body == "true") next
43}
44
45/@file/ {
46	gsub(/script/, apis)
47}
48
49/^([	 ]*)\* @api/ {
50	if (api == "Script") {
51		api_selected = "true"
52		next
53	}
54
55	# By default, classes are not selected
56	if (cls_level == 0) api_selected = "false"
57
58	gsub("^([	 ]*)", "", $0)
59	gsub("* @api ", "", $0)
60
61	if ($0 == "none") {
62		api_selected = "false"
63	} else if ($0 == "-all") {
64		api_selected = "false"
65	} else if (match($0, "-" apis)) {
66		api_selected = "false"
67	} else if (match($0, apis)) {
68		api_selected = "true"
69	}
70
71	next
72}
73
74# Ignore forward declarations of classes
75/^(	*)class(.*);/ { next; }
76# We only want to have public functions exported for now
77/^(	*)class/     {
78	if (cls_level == 0) {
79		if (api_selected == "") {
80			print "Class '"$2"' has no @api. It won't be published to any API." > "/dev/stderr"
81			api_selected = "false"
82		}
83		public = "false"
84		cls_param[0] = ""
85		cls_param[1] = 1
86		cls_param[2] = "x"
87		cls_in_api = api_selected
88		api_selected = ""
89		cls = $2
90
91		if (cls_in_api == "true") {
92			print comment_buffer
93			print
94			print "public:"
95			comment_buffer = ""
96		}
97	} else if (cls_level == 1) {
98		if (api_selected == "") api_selected = cls_in_api
99
100		if (api_selected == "true") {
101			print comment_buffer
102			print
103			print "public:"
104			comment_buffer = ""
105		}
106		api_selected = ""
107	} else {
108		print "Classes nested too deep" > "/dev/stderr"
109		exit 1
110	}
111	cls_level++
112	next
113}
114/^(	*)public/    { if (cls_level == 1) comment_buffer = ""; public = "true";  next; }
115/^(	*)protected/ { if (cls_level == 1) comment_buffer = ""; public = "false"; next; }
116/^(	*)private/   { if (cls_level == 1) comment_buffer = ""; public = "false"; next; }
117
118# Ignore special doxygen blocks
119/^#ifndef DOXYGEN_API/          { doxygen_skip = "true"; next; }
120/^#ifdef DOXYGEN_API/           { doxygen_skip = "next"; next; }
121/^#endif \/\* DOXYGEN_API \*\// { doxygen_skip = "false"; next; }
122/^#else/                         {
123	if (doxygen_skip == "next") {
124		doxygen_skip = "true";
125	} else {
126		doxygen_skip = "false";
127	}
128	next;
129}
130{ if (doxygen_skip == "true") next }
131
132/^#/ {
133	next
134}
135
136# Store comments
137/\/\*\*.*\*\//   { comment_buffer = $0; comment = "false"; next; }
138/\/\*.*\*\//     { comment_buffer = ""; comment = "false"; next; }
139/\/\*\*/         { comment_buffer = $0 "\n"; comment = "true";  next; }
140/\/\*/           { comment_buffer = ""; comment = "false";  next; }
141/\*\//           { comment_buffer = comment_buffer $0; comment = "false"; next; }
142{
143	if (comment == "true" && !match($0, /@api/))
144	{
145		if (match($0, /@game /) && api != "GS") next;
146		if (match($0, /@ai /) && api != "AI") next;
147		gsub("@game ", "", $0);
148		gsub("@ai ", "", $0);
149		comment_buffer = comment_buffer $0 "\n"; next;
150	}
151}
152
153
154# We need to make specialized conversions for structs
155/^(	*)struct/ {
156	comments_so_far = comment_buffer
157	comment_buffer = ""
158	cls_level++
159
160	# Check if we want to publish this struct
161	if (api_selected == "") api_selected = cls_in_api
162	if (api_selected == "false") {
163		api_selected = ""
164		next
165	}
166	api_selected = ""
167
168	if (public == "false") next
169
170	print comments_so_far
171	print $0
172
173	next
174}
175
176# We need to make specialized conversions for enums
177/^(	*)enum/ {
178	comments_so_far = comment_buffer
179	comment_buffer = ""
180	cls_level++
181
182	# Check if we want to publish this enum
183	if (api_selected == "") api_selected = cls_in_api
184	if (api_selected == "false") {
185		api_selected = ""
186		next
187	}
188	api_selected = ""
189
190	if (public == "false") next
191
192	in_enum = "true"
193
194	print comments_so_far
195	print $0
196
197	next
198}
199
200# Maybe the end of the class, if so we can start with the Squirrel export pretty soon
201/};/ {
202	comment_buffer = ""
203	cls_level--
204	if (cls_level != 0) {
205		if (in_enum == "true") print
206		in_enum = "false"
207		next
208	}
209	if (cls == "") {
210		next
211	}
212	if (cls_in_api == "true") print
213	next;
214}
215
216# Empty/white lines
217/^([ 	]*)$/ {
218	print $0
219
220	next
221}
222
223# Skip non-public functions
224{ if (public == "false") next }
225
226# Add enums
227{
228	if (in_enum == "true") {
229		print comment_buffer
230		comment_buffer = ""
231		gsub("=([^/]*),", ",", $0)
232		print $0
233
234		# Check if this a special error enum
235		if (match(enums[enum_size], ".*::ErrorMessages") != 0) {
236			# syntax:
237			# enum ErrorMessages {
238			#	ERR_SOME_ERROR,	// [STR_ITEM1, STR_ITEM2, ...]
239			# }
240
241			##TODO: don't print the STR_*
242		}
243		next
244	}
245}
246
247# Add a const (non-enum) value
248/^[ 	]*static const \w+ \w+ = -?\(?\w*\)?\w+;/ {
249	if (api_selected == "") api_selected = cls_in_api
250	if (api_selected == "false") {
251		api_selected = ""
252		comment_buffer = ""
253		next
254	}
255	print comment_buffer
256	print $0
257	comment_buffer = ""
258	next
259}
260
261# Add a method to the list
262/^.*\(.*\).*$/ {
263	if (cls_level != 1) next
264	if (!match($0, ";")) {
265		gsub(/ :$/, ";")
266		skip_function_body = "true"
267	}
268	if (match($0, "~")) {
269		if (api_selected != "") {
270			print "Destructor for '"cls"' has @api. Tag ignored." > "/dev/stderr"
271			api_selected = ""
272		}
273		next
274	}
275
276	# Check if we want to publish this function
277	if (api_selected == "") api_selected = cls_in_api
278	if (api_selected == "false") {
279		api_selected = ""
280		comment_buffer = ""
281		next
282	}
283	api_selected = ""
284
285	print comment_buffer
286	print $0
287	comment_buffer = ""
288
289	next
290}
291