Merge pull request #25820 from hribz/master
[xbmc.git] / tools / codegenerator / SwigTypeParser.groovy
blob24dfa8f8abb06a9447b670bb16ad82a241100b43
1 /*
2 * Copyright (C) 2005-2013 Team XBMC
3 * http://xbmc.org
5 * This Program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
8 * any later version.
10 * This Program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with XBMC; see the file COPYING. If not, see
17 * <http://www.gnu.org/licenses/>.
21 import groovy.xml.XmlParser
23 /**
24 * These methods are somewhat ugly because they have been copied out of
25 * the Swig source code and simply made compilable with groovy. They could
26 * all be much cleaner and smaller if they were completely groovyfied but
27 * I have no intention of doing that since they are complicated and they work
28 * and I don't want to try to trace down problems that would be inevitable
29 * with such a refactor.
31 public class SwigTypeParser
33 /**
34 * This holds a mapping for typedefs from a type to it's base type.
36 private static Map typeTable = [:]
38 /**
39 * Add a typedef node to the global list of typedefs to be used later in
40 * parsing types.
42 public static void appendTypeTable(Node typetab) { typetab.each { typeTable[it.@namespace + it.@type] = it.@basetype } }
44 /**
45 * Convert the type to an ltype considering the overloaded conversions.
47 public static String convertTypeToLTypeForParam(String ty)
49 // in the case where we're converting from a type to an ltype for a parameter,
50 // and the type is a r.*, we are going to assume the ltype is
51 // a "pass-by-value" on the stack.
52 return (ty.trim().startsWith('r.') ? SwigTypeParser.SwigType_ltype(ty.trim().substring(2)) : SwigTypeParser.SwigType_ltype(ty.trim()))
55 /**
56 * This method will return the base type for the provided type string. For example,
57 * if the type string is p.MyType you will get MyType. If the string is
58 * p.q(const).int you will get 'int'
60 public static String getRootType(String ty)
62 int li = ty.lastIndexOf('.')
63 return li >= 0 ? ty.substring(li + 1) : ty
66 /**
67 * SwigType_str()
69 * Create a C string representation of a datatype.
71 public static String SwigType_str(String ty, String id = null)
73 String result = id ? id : ''
74 String nextelement
75 String forwardelement
76 List elements = SwigType_split(ty)
77 if (elements == null) elements = []
78 int nelements = elements.size()
79 String element = nelements > 0 ? elements[0] : null
81 /* Now, walk the type list and start emitting */
82 for (int i = 0; i < nelements; i++) {
83 if (i < (nelements - 1)) {
84 nextelement = elements[i + 1]
85 forwardelement = nextelement
86 if (nextelement.startsWith('q(')) {
87 if (i < (nelements - 2)) forwardelement = elements[i + 2]
89 } else {
90 nextelement = null
91 forwardelement = null
93 if (element.startsWith('q(')) {
94 String q = SwigType_parm(element)
95 result = q + ' ' + result
96 } else if (SwigType_ispointer(element)) {
97 result = "*" + result
98 if ((forwardelement) && ((SwigType_isfunction(forwardelement) || (SwigType_isarray(forwardelement))))) {
99 result = "(" + result + ")"
101 } else if (SwigType_ismemberpointer(element)) {
102 String q = SwigType_parm(element);
103 result = q + "::*" + result
104 if ((forwardelement) && ((SwigType_isfunction(forwardelement) || (SwigType_isarray(forwardelement))))) {
105 result = '(' + result + ')'
107 } else if (SwigType_isreference(element)) {
108 result = '&' + result
109 if ((forwardelement) && ((SwigType_isfunction(forwardelement) || (SwigType_isarray(forwardelement))))) {
110 result = '(' + result + ')'
112 } else if (SwigType_isarray(element)) {
113 result += '[' + SwigType_parm(element) + ']'
114 } else if (SwigType_isfunction(element)) {
115 result += '('
116 List parms = SwigType_parmlist(element)
117 boolean didOne = false
118 for (String cur : parms) {
119 String p = SwigType_str(cur)
120 result += (didOne ? ',' : '') + p
121 didOne = true
123 result += ')'
124 } else {
125 if (element.startsWith("v(...)")) result = result + "..."
126 else {
127 String bs = SwigType_namestr(element);
128 result = bs + ' ' + result
131 element = nextelement;
133 // convert template parameters
134 return result.replaceAll('<\\(', '<').replaceAll('\\)>', '>')
138 * This will resolve the typedefs given the parameter passed is a simple type.
139 * see SwigType_resolve_all_typedefs which will handle qualifiers, pointers,
140 * references, and typedef of typedefs to resolve all the way down to the
141 * most basic types.
143 public static String SwigType_typedef_resolve(String t)
145 String td = typeTable[t]
146 String ret = td == null ? t : td
147 return ret
151 * This will resolve typedefs and handle qualifiers, pointers,
152 * references, and typedef of typedefs to resolve all the way down to the
153 * most basic types.
155 public static String SwigType_resolve_all_typedefs(String s)
157 String result = ''
158 String tc = s
160 /* Nuke all leading qualifiers, appending them to the result*/
161 while (SwigType_isqualifier(tc)) {
162 List tmpl = SwigType_pop(tc)
163 tc = tmpl[1]
164 result += tmpl[0]
167 if (SwigType_issimple(tc)) {
168 /* Resolve any typedef definitions */
169 String tt = tc
170 String td
171 while ((td = SwigType_typedef_resolve(tt)) != tt) {
172 if (td != tt) {
173 tt = td
174 break
176 else if (td != tt) tt = td
178 tc = td
180 return tc
183 List tmpl = SwigType_pop(tc)
184 result += tmpl[0]
185 result += SwigType_resolve_all_typedefs(tmpl[1])
186 return result
190 * SwigType_ltype(const SwigType *ty)
192 * Create a locally assignable type
194 public static String SwigType_ltype(String s) {
195 String result = ''
196 String tc = s
198 /* Nuke all leading qualifiers */
199 while (SwigType_isqualifier(tc)) {
200 tc = SwigType_pop(tc)[1]
203 if (SwigType_issimple(tc)) {
204 /* Resolve any typedef definitions */
205 String tt = tc
206 String td
207 while ((td = SwigType_typedef_resolve(tt)) != tt) {
208 if ((td != tt) && (SwigType_isconst(td) || SwigType_isarray(td) || SwigType_isreference(td))) {
209 /* We need to use the typedef type */
210 tt = td
211 break
213 else if (td != tt) tt = td
215 tc = td
217 List elements = SwigType_split(tc)
218 int nelements = elements.size()
220 /* Now, walk the type list and start emitting */
221 boolean notypeconv = false
222 boolean firstarray = true
223 for (int i = 0; i < nelements; i++) {
224 String element = elements[i]
225 /* when we see a function, we need to preserve the following types */
226 if (SwigType_isfunction(element)) {
227 notypeconv = true
229 if (SwigType_isqualifier(element)) {
230 /* Do nothing. Ignore */
231 } else if (SwigType_ispointer(element)) {
232 result += element
233 // this is a bit of a short circuit to avoid having to import the entire SwigType_typedef_resolve method which
234 // handles pointers to typedefed types, etc.
235 // collapse the rest of the list
236 String tmps = ''
237 for (int j = i + 1; j < nelements; j++) tmps += elements[j]
238 return result + SwigType_ltype(tmps)
239 //firstarray = false
240 } else if (SwigType_ismemberpointer(element)) {
241 result += element
242 firstarray = false
243 } else if (SwigType_isreference(element)) {
244 if (notypeconv) {
245 result += element
246 } else {
247 result += "p."
249 firstarray = false
250 } else if (SwigType_isarray(element) && firstarray) {
251 if (notypeconv) {
252 result += element
253 } else {
254 result += "p."
256 firstarray = false;
257 } else if (SwigType_isenum(element)) {
258 boolean anonymous_enum = (element == "enum ")
259 if (notypeconv || !anonymous_enum) {
260 result += element
261 } else {
262 result += "int"
264 } else {
265 result += element
269 return result
270 // convert template parameters
271 //return result.replaceAll('<\\(', '<').replaceAll('\\)>', '>')
275 * SwigType_lrtype(const SwigType *ty)
277 * Create a locally assignable reference type
279 public static String SwigType_lrtype(String s) {
280 String ltype = SwigType_ltype(s);
281 if (SwigType_ispointer(s)) {
282 return ltype;
283 } else {
284 return "r." + ltype;
289 * This creates the C++ declaration for a valid ltype for the type string
290 * given. For example, if the type is a "const char*" which is equivalent
291 * to the type string 'p.q(const).char', the return value from this method
292 * will be "char *".
294 public static String SwigType_lstr(String type)
296 return SwigType_str(convertTypeToLTypeForParam(type))
299 public static boolean SwigType_ispointer(String t)
301 if (t.startsWith('q(')) t = t.substring(t.indexOf('.') + 1)
302 return t.startsWith('p.')
305 public static String SwigType_makepointer(String t)
307 String prefix = (t.startsWith('q(')) ? t.substring(0,t.indexOf('.') + 1) : ""
308 String remainder = (t.startsWith('q(')) ? t.substring(t.indexOf('.') + 1) : t
310 return prefix + "p." + remainder
313 public static boolean SwigType_isarray(String t) { return t.startsWith('a(') }
315 public static boolean SwigType_ismemberpointer(String t) { return t?.startsWith('m(') }
317 public static boolean SwigType_isqualifier(String t) { return t?.startsWith('q(') }
319 public static boolean SwigType_isreference(String t) { return t.startsWith('r.') }
321 public static boolean SwigType_isenum(String t) { return t.startsWith('enum') }
323 public static String SwigType_istemplate(String t) {
324 int c = t.indexOf("<(")
325 return (c >= 0 && t.indexOf(')>',c+2) >= 0)
328 public static boolean SwigType_isfunction(String t)
330 if (t.startsWith('q(')) t = t.substring(t.indexOf('.') + 1,)
331 return t.startsWith('f(')
334 public static boolean SwigType_isconst(String t) {
335 int c = 0
336 if (t == null) return false
337 if (t.substring(c).startsWith("q(")) {
338 String q = SwigType_parm(t)
339 if (q.indexOf("const") >= 0) return true
341 /* Hmmm. Might be const through a typedef */
342 if (SwigType_issimple(t)) {
343 String td = SwigType_typedef_resolve(t)
344 if (td != t) return SwigType_isconst(td)
346 return false
350 private static String SwigType_parm(String t) {
351 int start = t.indexOf("(")
352 if (start < 0) return null
353 start++
354 int nparens = 0
355 int c = start
356 while (c < t.length()) {
357 if (t.charAt(c) == ')') {
358 if (nparens == 0) break;
359 nparens--;
361 else if (t.charAt(c) == '(') nparens++
362 c++;
364 return t.substring(start,c)
367 public static List SwigType_templateparmlist(String t)
369 int i = t.indexOf('<');
370 return SwigType_parmlist(t.substring(i))
373 /* -----------------------------------------------------------------------------
374 * SwigType_parmlist()
376 * Splits a comma separated list of parameters into its component parts
377 * The input is expected to contain the parameter list within () brackets
378 * Returns 0 if no argument list in the input, ie there are no round brackets ()
379 * Returns an empty List if there are no parameters in the () brackets
380 * For example:
382 * Foo(std::string,p.f().Bar<(int,double)>)
384 * returns 2 elements in the list:
385 * std::string
386 * p.f().Bar<(int,double)>
387 * ----------------------------------------------------------------------------- */
389 private static List SwigType_parmlist(String p) {
390 List list = []
391 int itemstart
393 assert p, "Cannot pass null to SwigType_parmlist"
394 itemstart = p.indexOf('(')
395 assert p.indexOf('.') == -1 || p.indexOf('.') > itemstart, p + " is expected to contain sub elements of a type"
396 itemstart++
397 int c = itemstart
398 while (c < p.length()) {
399 if (p.charAt(c) == ',') {
400 list.add(p.substring(itemstart,c))
401 itemstart = c + 1
402 } else if (p.charAt(c) == '(') {
403 int nparens = 1
405 while (c < p.length()) {
406 if (p.charAt(c) == '(') nparens++
407 if (p.charAt(c) == ')') {
408 nparens--
409 if (nparens == 0) break
413 } else if (p.charAt(c) == ')') {
414 break;
416 if (c < p.length()) c++
419 if (c != itemstart) {
420 list.add(p.substring(itemstart,c))
422 return list;
425 /* -----------------------------------------------------------------------------
426 * SwigType_namestr()
428 * Returns a string of the base type. Takes care of template expansions
429 * ----------------------------------------------------------------------------- */
431 private static String SwigType_namestr(String t) {
432 int d = 0
433 int c = t.indexOf("<(")
435 if (c < 0 || t.indexOf(')>',c+2) < 0) return t
437 String r = t.substring(0,c)
438 if (t.charAt(c - 1) == '<') r += ' '
439 r += '<'
441 List p = SwigType_parmlist(t.substring(c + 1))
442 for (int i = 0; i < p.size(); i++) {
443 String str = SwigType_str(p[i], null);
444 /* Avoid creating a <: token, which is the same as [ in C++ - put a space after '<'. */
445 if (i == 0 && str.length() > 0) r += ' '
446 r += str
447 if ((i + 1) < p.size()) r += ','
449 r += ' >'
450 String suffix = SwigType_templatesuffix(t);
451 if (suffix.length() > 0) {
452 String suffix_namestr = SwigType_namestr(suffix);
453 r += suffix_namestr
454 } else {
455 r += suffix
457 return r;
460 /* -----------------------------------------------------------------------------
461 * SwigType_templatesuffix()
463 * Returns text after a template substitution. Used to handle scope names
464 * for example:
466 * Foo<(p.int)>::bar
468 * returns "::bar"
469 * ----------------------------------------------------------------------------- */
471 private static String SwigType_templatesuffix(String t) {
472 int c = 0
473 while (c < t.length()) {
474 if ((t.charAt(c) == '<') && (t.charAt(c + 1) == '(')) {
475 int nest = 1
477 while (c < t.length() && nest != 0) {
478 if (t.charAt(c) == '<') nest++
479 if (t.charAt(c) == '>') nest--
482 return t.substring(c)
486 return ''
489 /* -----------------------------------------------------------------------------
490 * SwigType_split()
492 * Splits a type into it's component parts and returns a list of string.
493 * ----------------------------------------------------------------------------- */
495 private static List SwigType_split(String t) {
496 List list = []
497 int c = 0
498 int len
500 while (c < t.length()) {
501 len = element_size(t.substring(c))
502 String item = t.substring(c,c + len)
503 list += item
504 c = c + len
505 if (c < t.length() && t.charAt(c) == '.') c++
507 return list;
510 /* -----------------------------------------------------------------------------
511 * static element_size()
513 * This utility function finds the size of a single type element in a type string.
514 * Type elements are always delimited by periods, but may be nested with
515 * parentheses. A nested element is always handled as a single item.
517 * Returns the integer size of the element (which can be used to extract a
518 * substring, to chop the element off, or for other purposes).
519 * ----------------------------------------------------------------------------- */
521 private static int element_size(String s) {
522 int nparen
523 int c = 0
524 while (c < s.length()) {
525 if (s.charAt(c) == '.') {
527 return c
528 } else if (s.charAt(c) == '(') {
529 nparen = 1
531 while (c < s.length()) {
532 if (s.charAt(c) == '(') nparen++
533 if (s.charAt(c) == ')') {
534 nparen--
535 if (nparen == 0) break
540 if (c < s.length()) c++
542 return c;
545 /* -----------------------------------------------------------------------------
546 * SwigType_pop()
548 * Pop one type element off the type.
549 * Example: t in: q(const).p.Integer
550 * t out: p.Integer
551 * result: q(const).
552 * ----------------------------------------------------------------------------- */
554 private static Tuple SwigType_pop(String t) {
555 String result
556 int c = 0
558 if (t == null)
559 return null
561 int sz = element_size(t.substring(c))
562 return [ t.substring(c,c + sz), t.substring(c+sz) ]
565 private static boolean SwigType_issimple(String t) {
566 int c = 0
567 if (!t) return false
568 while (c < t.length()) {
569 if (t.charAt(c) == '<') {
570 int nest = 1
572 while (c < t.length() && nest != 0) {
573 if (t.charAt(c) == '<') nest++
574 if (t.charAt(c) == '>') nest--
579 if (t.charAt(c) == '.')
580 return false
583 return true
587 public static void main(String[] args)
589 String xmlText = '''
590 <typetab>
591 <entry basetype="std::vector&lt;(p.XBMCAddon::xbmcgui::ListItem)&gt;" type="ListItemList" namespace="XBMCAddon::xbmcgui::"/>
592 </typetab>
594 Node xml = new XmlParser().parseText(xmlText)
596 SwigTypeParser.appendTypeTable(xml)
598 // testPrint('f(int,int,int)','foo')
599 // testPrint('p.a(10).p.f(int,p.f(int).int)','foo')
600 // testPrint('p.q(const).char','foo')
601 // testPrint('f(r.q(const).String,p.q(const).XBMCAddon::xbmcgui::ListItem,bool)','foo')
602 // testPrint('r.q(const).String','foo')
603 // testPrint('q(const).p.q(const).char','foo')
604 //testPrint('std::vector<(p.String)>','foo')
605 // testPrint('r.q(const).String')
606 //System.out.println "${convertTypeToLType('bool')}"
607 //testPrint('p.q(const).XBMCAddon::xbmcgui::ListItemList')
608 //testPrint('p.q(const).XBMCAddon::xbmcgui::ListItemList')
609 //testPrint(SwigTypeParser.SwigType_makepointer('r.q(const).std::map<(String,String)>'), 'foo')
610 testPrint(SwigTypeParser.SwigType_makepointer('q(const).p.q(const).char'),'bfoo')
613 private static void testPrint(String ty, String id = 'foo')
615 println SwigTypeParser.SwigType_ltype(ty) + "|" + SwigTypeParser.SwigType_str(SwigTypeParser.SwigType_ltype(ty),id) + ' ' + " = " + ty + '|' + SwigTypeParser.SwigType_str(ty,id)