PageRenderTime 68ms CodeModel.GetById 38ms RepoModel.GetById 1ms app.codeStats 0ms

/src/script/api/squirrel_export.awk

https://bitbucket.org/G-mot/openttd-for-winrt
AWK | 572 lines | 461 code | 65 blank | 46 comment | 0 complexity | 11d38d22f7db772daace0e7542e9e3a3 MD5 | raw file
Possible License(s): LGPL-2.0, GPL-2.0
  1. # $Id$
  2. # This file is part of OpenTTD.
  3. # 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.
  4. # 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.
  5. # 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/>.
  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. # Simple insertion sort.
  13. function array_sort(ARRAY, ELEMENTS, temp, i, j)
  14. {
  15. for (i = 2; i <= ELEMENTS; i++)
  16. for (j = i; ARRAY[j - 1] > ARRAY[j]; --j) {
  17. temp = ARRAY[j]
  18. ARRAY[j] = ARRAY[j - 1]
  19. ARRAY[j - 1] = temp
  20. }
  21. return
  22. }
  23. function dump_class_templates(name)
  24. {
  25. realname = name
  26. gsub("^Script", "", realname)
  27. print " template <> inline " name " *GetParam(ForceType<" name " *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (" name " *)instance; }"
  28. print " template <> inline " name " &GetParam(ForceType<" name " &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(" name " *)instance; }"
  29. 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; }"
  30. 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; }"
  31. if (name == "ScriptEvent") {
  32. 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; }"
  33. } else if (name == "ScriptText") {
  34. print ""
  35. print " template <> inline Text *GetParam(ForceType<Text *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) {"
  36. print " if (sq_gettype(vm, index) == OT_INSTANCE) {"
  37. print " return GetParam(ForceType<ScriptText *>(), vm, index, ptr);"
  38. print " }"
  39. print " if (sq_gettype(vm, index) == OT_STRING) {"
  40. print " return new RawText(GetParam(ForceType<const char *>(), vm, index, ptr));"
  41. print " }"
  42. print " return NULL;"
  43. print " }"
  44. } else {
  45. 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; }"
  46. }
  47. }
  48. function dump_fileheader()
  49. {
  50. # Break the Id tag, so SVN doesn't replace it
  51. print "/* $I" "d$ */"
  52. print ""
  53. print "/*"
  54. print " * This file is part of OpenTTD."
  55. 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."
  56. 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."
  57. 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/>."
  58. print " */"
  59. print ""
  60. print "/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */"
  61. print ""
  62. print "#include \"../" filename "\""
  63. if (api != "Template") {
  64. gsub("script_", "template_", filename)
  65. print "#include \"../template/" filename ".sq\""
  66. }
  67. }
  68. function reset_reader()
  69. {
  70. enum_size = 0
  71. enum_value_size = 0
  72. enum_string_to_error_size = 0
  73. enum_error_to_string_size = 0
  74. struct_size = 0
  75. method_size = 0
  76. static_method_size = 0
  77. virtual_class = "false"
  78. cls = ""
  79. start_squirrel_define_on_next_line = "false"
  80. cls_level = 0
  81. cls_in_api = ""
  82. }
  83. BEGIN {
  84. enum_size = 0
  85. enum_value_size = 0
  86. enum_string_to_error_size = 0
  87. enum_error_to_string_size = 0
  88. const_size = 0
  89. struct_size = 0
  90. method_size = 0
  91. static_method_size = 0
  92. virtual_class = "false"
  93. super_cls = ""
  94. cls = ""
  95. api_selected = ""
  96. cls_in_api = ""
  97. start_squirrel_define_on_next_line = "false"
  98. has_fileheader = "false"
  99. cls_level = 0
  100. RS = "\r|\n"
  101. apis = tolower(api)
  102. if (apis == "gs") apis = "game"
  103. }
  104. /@file/ {
  105. filename = $3
  106. gsub("^" apis "_", "script_", filename)
  107. }
  108. # Ignore special doxygen blocks
  109. /^#ifndef DOXYGEN_API/ { doxygen_skip = "next"; next; }
  110. /^#ifdef DOXYGEN_API/ { doxygen_skip = "true"; next; }
  111. /^#endif \/\* DOXYGEN_API \*\// { doxygen_skip = "false"; next; }
  112. /^#else/ {
  113. if (doxygen_skip == "next") {
  114. doxygen_skip = "true";
  115. } else {
  116. doxygen_skip = "false";
  117. }
  118. next;
  119. }
  120. { if (doxygen_skip == "true") next }
  121. /^([ ]*)\* @api/ {
  122. # By default, classes are not selected
  123. if (cls_level == 0) api_selected = "false"
  124. gsub("^([ ]*)", "", $0)
  125. gsub("* @api ", "", $0)
  126. if (api == "Template") {
  127. api_selected = "true"
  128. if ($0 == "none" || $0 == "-all") api_selected = "false"
  129. next
  130. }
  131. if ($0 == "none") {
  132. api_selected = "false"
  133. } else if ($0 == "-all") {
  134. api_selected = "false"
  135. } else if (match($0, "-" apis)) {
  136. api_selected = "false"
  137. } else if (match($0, apis)) {
  138. api_selected = "true"
  139. }
  140. next
  141. }
  142. # Remove the old squirrel stuff
  143. /#ifdef DEFINE_SQUIRREL_CLASS/ { squirrel_stuff = "true"; next; }
  144. /^#endif \/\* DEFINE_SQUIRREL_CLASS \*\// { if (squirrel_stuff == "true") { squirrel_stuff = "false"; next; } }
  145. { if (squirrel_stuff == "true") next; }
  146. # Ignore forward declarations of classes
  147. /^( *)class(.*);/ { next; }
  148. # We only want to have public functions exported for now
  149. /^( *)class/ {
  150. if (cls_level == 0) {
  151. if (api_selected == "") {
  152. print "Class '"$2"' has no @api. It won't be published to any API." > "/dev/stderr"
  153. api_selected = "false"
  154. }
  155. public = "false"
  156. cls_param[0] = ""
  157. cls_param[1] = 1
  158. cls_param[2] = "x"
  159. cls_in_api = api_selected
  160. api_selected = ""
  161. cls = $2
  162. if (match($4, "public") || match($4, "protected") || match($4, "private")) {
  163. super_cls = $5
  164. } else {
  165. super_cls = $4
  166. }
  167. } else if (cls_level == 1) {
  168. if (api_selected == "") api_selected = cls_in_api
  169. if (api_selected == "true") {
  170. struct_size++
  171. structs[struct_size] = cls "::" $2
  172. }
  173. api_selected = ""
  174. }
  175. cls_level++
  176. next
  177. }
  178. /^( *)public/ { if (cls_level == 1) public = "true"; next; }
  179. /^( *)protected/ { if (cls_level == 1) public = "false"; next; }
  180. /^( *)private/ { if (cls_level == 1) public = "false"; next; }
  181. # Ignore the comments
  182. /^#/ { next; }
  183. /\/\*.*\*\// { comment = "false"; next; }
  184. /\/\*/ { comment = "true"; next; }
  185. /\*\// { comment = "false"; next; }
  186. { if (comment == "true") next }
  187. # We need to make specialized conversions for structs
  188. /^( *)struct/ {
  189. cls_level++
  190. # Check if we want to publish this struct
  191. if (api_selected == "") api_selected = cls_in_api
  192. if (api_selected == "false") {
  193. api_selected = ""
  194. next
  195. }
  196. api_selected = ""
  197. if (public == "false") next
  198. if (cls_level != 1) next
  199. struct_size++
  200. structs[struct_size] = cls "::" $2
  201. next
  202. }
  203. # We need to make specialized conversions for enums
  204. /^( *)enum/ {
  205. cls_level++
  206. # Check if we want to publish this enum
  207. if (api_selected == "") api_selected = cls_in_api
  208. if (api_selected == "false") {
  209. api_selected = ""
  210. next
  211. }
  212. api_selected = ""
  213. if (public == "false") next
  214. in_enum = "true"
  215. enum_size++
  216. enums[enum_size] = cls "::" $2
  217. next
  218. }
  219. # Maybe the end of the class, if so we can start with the Squirrel export pretty soon
  220. /};/ {
  221. cls_level--
  222. if (cls_level != 0) {
  223. in_enum = "false";
  224. next;
  225. }
  226. if (cls == "") {
  227. next;
  228. }
  229. start_squirrel_define_on_next_line = "true"
  230. next;
  231. }
  232. # Empty/white lines. When we may do the Squirrel export, do that export.
  233. /^([ ]*)$/ {
  234. if (start_squirrel_define_on_next_line == "false") next
  235. if (cls_in_api != "true") {
  236. reset_reader()
  237. next
  238. }
  239. if (has_fileheader == "false") {
  240. dump_fileheader()
  241. has_fileheader = "true"
  242. }
  243. spaces = " ";
  244. public = "false"
  245. namespace_opened = "false"
  246. api_cls = cls
  247. gsub("^Script", api, api_cls)
  248. api_super_cls = super_cls
  249. gsub("^Script", api, api_super_cls)
  250. print ""
  251. if (api == "Template") {
  252. # First check whether we have enums to print
  253. if (enum_size != 0) {
  254. if (namespace_opened == "false") {
  255. print "namespace SQConvert {"
  256. namespace_opened = "true"
  257. }
  258. print " /* Allow enums to be used as Squirrel parameters */"
  259. for (i = 1; i <= enum_size; i++) {
  260. 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; }"
  261. print " template <> inline int Return<" enums[i] ">(HSQUIRRELVM vm, " enums[i] " res) { sq_pushinteger(vm, (int32)res); return 1; }"
  262. delete enums[i]
  263. }
  264. }
  265. # Then check whether we have structs/classes to print
  266. if (struct_size != 0) {
  267. if (namespace_opened == "false") {
  268. print "namespace SQConvert {"
  269. namespace_opened = "true"
  270. }
  271. print " /* Allow inner classes/structs to be used as Squirrel parameters */"
  272. for (i = 1; i <= struct_size; i++) {
  273. dump_class_templates(structs[i])
  274. delete structs[i]
  275. }
  276. }
  277. if (namespace_opened == "false") {
  278. print "namespace SQConvert {"
  279. namespace_opened = "true"
  280. } else {
  281. print ""
  282. }
  283. print " /* Allow " cls " to be used as Squirrel parameter */"
  284. dump_class_templates(cls)
  285. print "} // namespace SQConvert"
  286. reset_reader()
  287. next
  288. }
  289. print "";
  290. print "template <> const char *GetClassName<" cls ", ST_" toupper(api) ">() { return \"" api_cls "\"; }"
  291. print "";
  292. # Then do the registration functions of the class. */
  293. print "void SQ" api_cls "_Register(Squirrel *engine)"
  294. print "{"
  295. print " DefSQClass<" cls ", ST_" toupper(api) "> SQ" api_cls "(\"" api_cls "\");"
  296. if (super_cls == "Text" || super_cls == "ScriptObject" || super_cls == "AIAbstractList::Valuator") {
  297. print " SQ" api_cls ".PreRegister(engine);"
  298. } else {
  299. print " SQ" api_cls ".PreRegister(engine, \"" api_super_cls "\");"
  300. }
  301. if (virtual_class == "false" && super_cls != "ScriptEvent") {
  302. if (cls_param[2] == "v") {
  303. print " SQ" api_cls ".AddSQAdvancedConstructor(engine);"
  304. } else {
  305. print " SQ" api_cls ".AddConstructor<void (" cls "::*)(" cls_param[0] "), " cls_param[1]">(engine, \"" cls_param[2] "\");"
  306. }
  307. }
  308. print ""
  309. # Enum values
  310. mlen = 0
  311. for (i = 1; i <= enum_value_size; i++) {
  312. if (mlen <= length(enum_value[i])) mlen = length(enum_value[i])
  313. }
  314. for (i = 1; i <= enum_value_size; i++) {
  315. print " SQ" api_cls ".DefSQConst(engine, " cls "::" enum_value[i] ", " substr(spaces, 1, mlen - length(enum_value[i])) "\"" enum_value[i] "\");"
  316. delete enum_value[i]
  317. }
  318. if (enum_value_size != 0) print ""
  319. # Const values
  320. mlen = 0
  321. for (i = 1; i <= const_size; i++) {
  322. if (mlen <= length(const_value[i])) mlen = length(const_value[i])
  323. }
  324. for (i = 1; i <= const_size; i++) {
  325. print " SQ" api_cls ".DefSQConst(engine, " cls "::" const_value[i] ", " substr(spaces, 1, mlen - length(const_value[i])) "\"" const_value[i] "\");"
  326. delete const_value[i]
  327. }
  328. if (const_size != 0) print ""
  329. # Mapping of OTTD strings to errors
  330. mlen = 0
  331. for (i = 1; i <= enum_string_to_error_size; i++) {
  332. if (mlen <= length(enum_string_to_error_mapping_string[i])) mlen = length(enum_string_to_error_mapping_string[i])
  333. }
  334. for (i = 1; i <= enum_string_to_error_size; i++) {
  335. 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] ");"
  336. delete enum_string_to_error_mapping_string[i]
  337. }
  338. if (enum_string_to_error_size != 0) print ""
  339. # Mapping of errors to human 'readable' strings.
  340. mlen = 0
  341. for (i = 1; i <= enum_error_to_string_size; i++) {
  342. if (mlen <= length(enum_error_to_string_mapping[i])) mlen = length(enum_error_to_string_mapping[i])
  343. }
  344. for (i = 1; i <= enum_error_to_string_size; i++) {
  345. 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] "\");"
  346. delete enum_error_to_string_mapping[i]
  347. }
  348. if (enum_error_to_string_size != 0) print ""
  349. # Static methods
  350. mlen = 0
  351. for (i = 1; i <= static_method_size; i++) {
  352. if (mlen <= length(static_methods[i, 0])) mlen = length(static_methods[i, 0])
  353. }
  354. for (i = 1; i <= static_method_size; i++) {
  355. if (static_methods[i, 2] == "v") {
  356. 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] "\");"
  357. } else {
  358. 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] "\");"
  359. }
  360. delete static_methods[i]
  361. }
  362. if (static_method_size != 0) print ""
  363. if (virtual_class == "false") {
  364. # Non-static methods
  365. mlen = 0
  366. for (i = 1; i <= method_size; i++) {
  367. if (mlen <= length(methods[i, 0])) mlen = length(methods[i, 0])
  368. }
  369. for (i = 1; i <= method_size; i++) {
  370. if (methods[i, 2] == "v") {
  371. print " SQ" api_cls ".DefSQAdvancedMethod(engine, &" cls "::" methods[i, 0] ", " substr(spaces, 1, mlen - length(methods[i, 0]) - 8) "\"" methods[i, 0] "\");"
  372. } else {
  373. 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] "\");"
  374. }
  375. delete methods[i]
  376. }
  377. if (method_size != 0) print ""
  378. }
  379. print " SQ" api_cls ".PostRegister(engine);"
  380. print "}"
  381. reset_reader()
  382. next
  383. }
  384. # Skip non-public functions
  385. { if (public == "false") next }
  386. # Add enums
  387. {
  388. if (in_enum == "true") {
  389. enum_value_size++
  390. sub(",", "", $1)
  391. enum_value[enum_value_size] = $1
  392. # Check if this a special error enum
  393. if (match(enums[enum_size], ".*::ErrorMessages") != 0) {
  394. # syntax:
  395. # enum ErrorMessages {
  396. # ERR_SOME_ERROR, // [STR_ITEM1, STR_ITEM2, ...]
  397. # }
  398. # Set the mappings
  399. if (match($0, "\\[.*\\]") != 0) {
  400. mappings = substr($0, RSTART, RLENGTH);
  401. gsub("([\\[[:space:]\\]])", "", mappings);
  402. split(mappings, mapitems, ",");
  403. for (i = 1; i <= length(mapitems); i++) {
  404. enum_string_to_error_size++
  405. enum_string_to_error_mapping_string[enum_string_to_error_size] = mapitems[i]
  406. enum_string_to_error_mapping_error[enum_string_to_error_size] = $1
  407. }
  408. enum_error_to_string_size++
  409. enum_error_to_string_mapping[enum_error_to_string_size] = $1
  410. }
  411. }
  412. next
  413. }
  414. }
  415. # Add a const (non-enum) value
  416. /^[ ]*static const \w+ \w+ = -?\(?\w*\)?\w+;/ {
  417. const_size++
  418. const_value[const_size] = $4
  419. next
  420. }
  421. # Add a method to the list
  422. /^.*\(.*\).*$/ {
  423. if (cls_level != 1) next
  424. if (match($0, "~")) {
  425. if (api_selected != "") {
  426. print "Destructor for '"cls"' has @api. Tag ignored." > "/dev/stderr"
  427. api_selected = ""
  428. }
  429. next
  430. }
  431. is_static = match($0, "static")
  432. if (match($0, "virtual")) {
  433. virtual_class = "true"
  434. }
  435. gsub("\\yvirtual\\y", "", $0)
  436. gsub("\\ystatic\\y", "", $0)
  437. gsub("\\yconst\\y", "", $0)
  438. gsub("{.*", "", $0)
  439. param_s = $0
  440. gsub("\\*", "", $0)
  441. gsub("\\(.*", "", $0)
  442. sub(".*\\(", "", param_s)
  443. sub("\\).*", "", param_s)
  444. funcname = $2
  445. if ($1 == cls && funcname == "") {
  446. if (api_selected != "") {
  447. print "Constructor for '"cls"' has @api. Tag ignored." > "/dev/stderr"
  448. api_selected = ""
  449. }
  450. cls_param[0] = param_s
  451. if (param_s == "") next
  452. } else if (funcname == "") next
  453. split(param_s, params, ",")
  454. if (is_static) {
  455. types = "."
  456. } else {
  457. types = "x"
  458. }
  459. for (len = 1; params[len] != ""; len++) {
  460. sub("^[ ]*", "", params[len])
  461. if (match(params[len], "\\*") || match(params[len], "&")) {
  462. if (match(params[len], "^char")) {
  463. # Many types can be converted to string, so use '.', not 's'. (handled by our glue code)
  464. types = types "."
  465. } else if (match(params[len], "^void")) {
  466. types = types "p"
  467. } else if (match(params[len], "^Array")) {
  468. types = types "a"
  469. } else if (match(params[len], "^struct Array")) {
  470. types = types "a"
  471. } else if (match(params[len], "^Text")) {
  472. types = types "."
  473. } else {
  474. types = types "x"
  475. }
  476. } else if (match(params[len], "^bool")) {
  477. types = types "b"
  478. } else if (match(params[len], "^HSQUIRRELVM")) {
  479. types = "v"
  480. } else {
  481. types = types "i"
  482. }
  483. }
  484. # Check if we want to publish this function
  485. if (api_selected == "") api_selected = cls_in_api
  486. if (api_selected == "false") {
  487. api_selected = ""
  488. next
  489. }
  490. api_selected = ""
  491. if ($1 == cls && funcname == "") {
  492. cls_param[1] = len;
  493. cls_param[2] = types;
  494. } else if (substr(funcname, 0, 1) == "_" && types != "v") {
  495. } else if (is_static) {
  496. static_method_size++
  497. static_methods[static_method_size, 0] = funcname
  498. static_methods[static_method_size, 1] = len
  499. static_methods[static_method_size, 2] = types
  500. } else {
  501. method_size++
  502. methods[method_size, 0] = funcname
  503. methods[method_size, 1] = len
  504. methods[method_size, 2] = types
  505. }
  506. next
  507. }