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