1 # $Id: squirrel_export.awk 24288 2012-05-26 14:16:27Z frosch $
3 # This file is part of OpenTTD.
4 # 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.
5 # 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.
6 # 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/>.
9 # Awk script to automatically generate the code needed
10 # to export the script APIs to Squirrel.
12 # Note that arrays are 1 based...
15 # Simple insertion sort.
16 function array_sort
(ARRAY
, ELEMENTS
, temp
, i
, j
)
18 for (i =
2; i
<= ELEMENTS
; i
++)
19 for (j = i
; ARRAY
[j
- 1] > ARRAY
[j
]; --j
) {
21 ARRAY
[j
] = ARRAY
[j
- 1]
27 function dump_class_templates
(name
)
30 gsub("^Script", "", realname
)
32 print " template <> inline " name
" *GetParam(ForceType<" name
" *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (" name
" *)instance; }"
33 print " template <> inline " name
" &GetParam(ForceType<" name
" &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(" name
" *)instance; }"
34 print " template <> inline const " name
" *GetParam(ForceType<const " name
" *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (" name
" *)instance; }"
35 print " template <> inline const " name
" &GetParam(ForceType<const " name
" &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(" name
" *)instance; }"
36 if (name ==
"ScriptEvent") {
37 print " template <> inline int Return<" name
" *>(HSQUIRRELVM vm, " name
" *res) { if (res == NULL) { sq_pushnull(vm); return 1; } Squirrel::CreateClassInstanceVM(vm, \"" realname
"\", res, NULL, DefSQDestructorCallback<" name
">, true); return 1; }"
38 } else if (name ==
"ScriptText") {
40 print " template <> inline Text *GetParam(ForceType<Text *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) {"
41 print " if (sq_gettype(vm, index) == OT_INSTANCE) {"
42 print " return GetParam(ForceType<ScriptText *>(), vm, index, ptr);"
44 print " if (sq_gettype(vm, index) == OT_STRING) {"
45 print " return new RawText(GetParam(ForceType<const char *>(), vm, index, ptr));"
50 print " template <> inline int Return<" name
" *>(HSQUIRRELVM vm, " name
" *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, \"" realname
"\", res, NULL, DefSQDestructorCallback<" name
">, true); return 1; }"
54 function dump_fileheader
()
56 # Break the Id tag, so SVN doesn't replace it
60 print " * This file is part of OpenTTD."
61 print " * 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."
62 print " * 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."
63 print " * 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/>."
66 print "/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */"
68 print "#include \"../" filename "\""
69 if (api
!= "Template") {
70 gsub("script_", "template_", filename)
71 print "#include \"../template/" filename ".sq\""
75 function reset_reader
()
79 enum_string_to_error_size =
0
80 enum_error_to_string_size =
0
83 static_method_size =
0
85 start_squirrel_define_on_next_line =
"false"
93 enum_string_to_error_size =
0
94 enum_error_to_string_size =
0
98 static_method_size =
0
103 start_squirrel_define_on_next_line =
"false"
104 has_fileheader =
"false"
108 if (apis ==
"gs") apis =
"game"
113 gsub("^" apis
"_", "script_", filename)
116 # Ignore special doxygen blocks
117 /^
#ifndef DOXYGEN_API/ { doxygen_skip = "next"; next; }
118 /^
#ifdef DOXYGEN_API/ { doxygen_skip = "true"; next; }
119 /^
#endif \/\* DOXYGEN_API \*\// { doxygen_skip = "false"; next; }
121 if (doxygen_skip ==
"next") {
122 doxygen_skip =
"true";
124 doxygen_skip =
"false";
128 { if (doxygen_skip ==
"true") next }
131 # By default, classes are not selected
132 if (cls_level ==
0) api_selected =
"false"
134 gsub("^([ ]*)", "", $
0)
135 gsub("* @api ", "", $
0)
137 if (api ==
"Template") {
138 api_selected =
"true"
139 if ($
0 ==
"none" || $
0 ==
"-all") api_selected =
"false"
144 api_selected =
"false"
145 } else if ($
0 ==
"-all") {
146 api_selected =
"false"
147 } else if (match($
0, "-" apis
)) {
148 api_selected =
"false"
149 } else if (match($
0, apis
)) {
150 api_selected =
"true"
156 # Remove the old squirrel stuff
157 /#ifdef DEFINE_SQUIRREL_CLASS/ { squirrel_stuff = "true"; next; }
158 /^
#endif \/\* DEFINE_SQUIRREL_CLASS \*\// { if (squirrel_stuff == "true") { squirrel_stuff = "false"; next; } }
159 { if (squirrel_stuff ==
"true") next; }
161 # Ignore forward declarations of classes
162 /^
( *)class
(.
*);/ { next; }
163 # We only want to have public functions exported for now
165 if (cls_level ==
0) {
166 if (api_selected ==
"") {
167 print "Class '"$
2"' has no @api. It won't be published to any API." > "/dev/stderr"
168 api_selected =
"false"
174 cls_in_api = api_selected
177 if (match($
4, "public") || match($
4, "protected") || match($
4, "private")) {
182 } else if (cls_level ==
1) {
183 if (api_selected ==
"") api_selected = cls_in_api
185 if (api_selected ==
"true") {
187 structs
[struct_size
] = cls
"::" $
2
194 /^
( *)public
/ { if (cls_level ==
1) public =
"true"; next; }
195 /^
( *)protected
/ { if (cls_level ==
1) public =
"false"; next; }
196 /^
( *)private
/ { if (cls_level ==
1) public =
"false"; next; }
198 # Ignore the comments
200 /\
/\
*.
*\
*\
// { comment =
"false"; next; }
201 /\
/\
*/ { comment =
"true"; next; }
202 /\
*\
// { comment =
"false"; next; }
203 { if (comment ==
"true") next }
205 # We need to make specialized conversions for structs
209 # Check if we want to publish this struct
210 if (api_selected ==
"") api_selected = cls_in_api
211 if (api_selected ==
"false") {
217 if (public ==
"false") next
218 if (cls_level
!= 1) next
221 structs
[struct_size
] = cls
"::" $
2
226 # We need to make specialized conversions for enums
230 # Check if we want to publish this enum
231 if (api_selected ==
"") api_selected = cls_in_api
232 if (api_selected ==
"false") {
238 if (public ==
"false") next
242 enums
[enum_size
] = cls
"::" $
2
247 # Maybe the end of the class, if so we can start with the Squirrel export pretty soon
250 if (cls_level
!= 0) {
257 start_squirrel_define_on_next_line =
"true"
261 # Empty/white lines. When we may do the Squirrel export, do that export.
263 if (start_squirrel_define_on_next_line ==
"false") next
265 if (cls_in_api
!= "true") {
269 if (has_fileheader ==
"false") {
271 has_fileheader =
"true"
276 namespace_opened =
"false"
279 gsub("^Script", api
, api_cls
)
280 api_super_cls = super_cls
281 gsub("^Script", api
, api_super_cls
)
285 if (api ==
"Template") {
286 # First check whether we have enums to print
287 if (enum_size
!= 0) {
288 if (namespace_opened ==
"false") {
289 print "namespace SQConvert {"
290 namespace_opened =
"true"
292 print " /* Allow enums to be used as Squirrel parameters */"
293 for (i =
1; i
<= enum_size
; i
++) {
294 print " template <> inline " enums
[i
] " GetParam(ForceType<" enums
[i
] ">, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (" enums
[i
] ")tmp; }"
295 print " template <> inline int Return<" enums
[i
] ">(HSQUIRRELVM vm, " enums
[i
] " res) { sq_pushinteger(vm, (int32)res); return 1; }"
300 # Then check whether we have structs/classes to print
301 if (struct_size
!= 0) {
302 if (namespace_opened ==
"false") {
303 print "namespace SQConvert {"
304 namespace_opened =
"true"
306 print " /* Allow inner classes/structs to be used as Squirrel parameters */"
307 for (i =
1; i
<= struct_size
; i
++) {
308 dump_class_templates
(structs
[i
])
313 if (namespace_opened ==
"false") {
314 print "namespace SQConvert {"
315 namespace_opened =
"true"
319 print " /* Allow " cls
" to be used as Squirrel parameter */"
320 dump_class_templates
(cls
)
322 print "} // namespace SQConvert"
329 print "template <> const char *GetClassName<" cls
", ST_" toupper(api
) ">() { return \"" api_cls
"\"; }"
332 # Then do the registration functions of the class. */
333 print "void SQ" api_cls
"_Register(Squirrel *engine)"
335 print " DefSQClass<" cls
", ST_" toupper(api
) "> SQ" api_cls
"(\"" api_cls
"\");"
336 if (super_cls ==
"Text" || super_cls ==
"ScriptObject" || super_cls ==
"AIAbstractList::Valuator") {
337 print " SQ" api_cls
".PreRegister(engine);"
339 print " SQ" api_cls
".PreRegister(engine, \"" api_super_cls
"\");"
341 if (super_cls
!= "ScriptEvent") {
342 if (cls_param
[2] ==
"v") {
343 print " SQ" api_cls
".AddSQAdvancedConstructor(engine);"
345 print " SQ" api_cls
".AddConstructor<void (" cls
"::*)(" cls_param
[0] "), " cls_param
[1]">(engine, \"" cls_param
[2] "\");"
352 for (i =
1; i
<= enum_value_size
; i
++) {
353 if (mlen
<=
length(enum_value
[i
])) mlen =
length(enum_value
[i
])
355 for (i =
1; i
<= enum_value_size
; i
++) {
356 print " SQ" api_cls
".DefSQConst(engine, " cls
"::" enum_value
[i
] ", " substr(spaces
, 1, mlen
- length(enum_value
[i
])) "\"" enum_value
[i
] "\");"
359 if (enum_value_size
!= 0) print ""
363 for (i =
1; i
<= const_size
; i
++) {
364 if (mlen
<=
length(const_value
[i
])) mlen =
length(const_value
[i
])
366 for (i =
1; i
<= const_size
; i
++) {
367 print " SQ" api_cls
".DefSQConst(engine, " cls
"::" const_value
[i
] ", " substr(spaces
, 1, mlen
- length(const_value
[i
])) "\"" const_value
[i
] "\");"
368 delete const_value
[i
]
370 if (const_size
!= 0) print ""
372 # Mapping of OTTD strings to errors
374 for (i =
1; i
<= enum_string_to_error_size
; i
++) {
375 if (mlen
<=
length(enum_string_to_error_mapping_string
[i
])) mlen =
length(enum_string_to_error_mapping_string
[i
])
377 for (i =
1; i
<= enum_string_to_error_size
; i
++) {
378 print " ScriptError::RegisterErrorMap(" enum_string_to_error_mapping_string
[i
] ", " substr(spaces
, 1, mlen
- length(enum_string_to_error_mapping_string
[i
])) cls
"::" enum_string_to_error_mapping_error
[i
] ");"
380 delete enum_string_to_error_mapping_string
[i
]
382 if (enum_string_to_error_size
!= 0) print ""
384 # Mapping of errors to human 'readable' strings.
386 for (i =
1; i
<= enum_error_to_string_size
; i
++) {
387 if (mlen
<=
length(enum_error_to_string_mapping
[i
])) mlen =
length(enum_error_to_string_mapping
[i
])
389 for (i =
1; i
<= enum_error_to_string_size
; i
++) {
390 print " ScriptError::RegisterErrorMapString(" cls
"::" enum_error_to_string_mapping
[i
] ", " substr(spaces
, 1, mlen
- length(enum_error_to_string_mapping
[i
])) "\"" enum_error_to_string_mapping
[i
] "\");"
391 delete enum_error_to_string_mapping
[i
]
393 if (enum_error_to_string_size
!= 0) print ""
397 for (i =
1; i
<= static_method_size
; i
++) {
398 if (mlen
<=
length(static_methods
[i
, 0])) mlen =
length(static_methods
[i
, 0])
400 for (i =
1; i
<= static_method_size
; i
++) {
401 if (static_methods
[i
, 2] ==
"v") {
402 print " SQ" api_cls
".DefSQAdvancedStaticMethod(engine, &" cls
"::" static_methods
[i
, 0] ", " substr(spaces
, 1, mlen
- length(static_methods
[i
, 0]) - 8) "\"" static_methods
[i
, 0] "\");"
404 print " SQ" api_cls
".DefSQStaticMethod(engine, &" cls
"::" static_methods
[i
, 0] ", " substr(spaces
, 1, mlen
- length(static_methods
[i
, 0])) "\"" static_methods
[i
, 0] "\", " substr(spaces
, 1, mlen
- length(static_methods
[i
, 0])) "" static_methods
[i
, 1] ", \"" static_methods
[i
, 2] "\");"
406 delete static_methods
[i
]
408 if (static_method_size
!= 0) print ""
412 for (i =
1; i
<= method_size
; i
++) {
413 if (mlen
<=
length(methods
[i
, 0])) mlen =
length(methods
[i
, 0])
415 for (i =
1; i
<= method_size
; i
++) {
416 if (methods
[i
, 2] ==
"v") {
417 print " SQ" api_cls
".DefSQAdvancedMethod(engine, &" cls
"::" methods
[i
, 0] ", " substr(spaces
, 1, mlen
- length(methods
[i
, 0]) - 8) "\"" methods
[i
, 0] "\");"
419 print " SQ" api_cls
".DefSQMethod(engine, &" cls
"::" methods
[i
, 0] ", " substr(spaces
, 1, mlen
- length(methods
[i
, 0])) "\"" methods
[i
, 0] "\", " substr(spaces
, 1, mlen
- length(methods
[i
, 0])) "" methods
[i
, 1] ", \"" methods
[i
, 2] "\");"
423 if (method_size
!= 0) print ""
425 print " SQ" api_cls
".PostRegister(engine);"
433 # Skip non-public functions
434 { if (public ==
"false") next }
438 if (in_enum ==
"true") {
441 enum_value
[enum_value_size
] = $
1
443 # Check if this a special error enum
444 if (match(enums
[enum_size
], ".*::ErrorMessages") != 0) {
446 # enum ErrorMessages {
447 # ERR_SOME_ERROR, // [STR_ITEM1, STR_ITEM2, ...]
451 if (match($
0, "\\[.*\\]") != 0) {
452 mappings =
substr($
0, RSTART, RLENGTH);
453 gsub("([\\[[:space:]\\]])", "", mappings
);
455 split(mappings
, mapitems
, ",");
456 for (i =
1; i
<=
length(mapitems
); i
++) {
457 enum_string_to_error_size
++
458 enum_string_to_error_mapping_string
[enum_string_to_error_size
] = mapitems
[i
]
459 enum_string_to_error_mapping_error
[enum_string_to_error_size
] = $
1
462 enum_error_to_string_size
++
463 enum_error_to_string_mapping
[enum_error_to_string_size
] = $
1
470 # Add a const (non-enum) value
471 /^
[ ]*static const \w
+ \w
+ =
-?\
(?\w
*\
)?\w
+;/ {
473 const_value
[const_size
] = $
4
477 # Add a method to the list
479 if (cls_level
!= 1) next
480 if (match($
0, "~")) {
481 if (api_selected
!= "") {
482 print "Destructor for '"cls
"' has @api. Tag ignored." > "/dev/stderr"
488 is_static =
match($
0, "static")
489 gsub("\\yvirtual\\y", "", $
0)
490 gsub("\\ystatic\\y", "", $
0)
491 gsub("\\yconst\\y", "", $
0)
495 gsub("\\(.*", "", $
0)
497 sub(".*\\(", "", param_s
)
498 sub("\\).*", "", param_s
)
501 if ($
1 == cls
&& funcname ==
"") {
502 if (api_selected
!= "") {
503 print "Constructor for '"cls
"' has @api. Tag ignored." > "/dev/stderr"
506 cls_param
[0] = param_s
507 if (param_s ==
"") next
508 } else if (funcname ==
"") next
510 split(param_s
, params
, ",")
516 for (len =
1; params
[len
] != ""; len
++) {
517 sub("^[ ]*", "", params
[len
])
518 if (match(params
[len
], "\\*") || match(params
[len
], "&")) {
519 if (match(params
[len
], "^char")) {
520 # Many types can be converted to string, so use '.', not 's'. (handled by our glue code)
522 } else if (match(params
[len
], "^void")) {
524 } else if (match(params
[len
], "^Array")) {
526 } else if (match(params
[len
], "^struct Array")) {
528 } else if (match(params
[len
], "^Text")) {
533 } else if (match(params
[len
], "^bool")) {
535 } else if (match(params
[len
], "^HSQUIRRELVM")) {
542 # Check if we want to publish this function
543 if (api_selected ==
"") api_selected = cls_in_api
544 if (api_selected ==
"false") {
550 if ($
1 == cls
&& funcname ==
"") {
552 cls_param
[2] = types
;
553 } else if (substr(funcname
, 0, 1) ==
"_" && types
!= "v") {
554 } else if (is_static
) {
556 static_methods
[static_method_size
, 0] = funcname
557 static_methods
[static_method_size
, 1] = len
558 static_methods
[static_method_size
, 2] = types
561 methods
[method_size
, 0] = funcname
562 methods
[method_size
, 1] = len
563 methods
[method_size
, 2] = types