diff options
author | Jim Carroll <thecarrolls@jiminger.com> | 2012-09-09 17:15:11 -0400 |
---|---|---|
committer | Jim Carroll <thecarrolls@jiminger.com> | 2012-09-09 17:15:11 -0400 |
commit | 7850f2e6881107ee4efb705b1c675599495df70c (patch) | |
tree | e96598d6a75cb1336fecd1bcbad7cbf54f73e260 /tools | |
parent | cb2d3015c739ae05d3d86025edad2b1b995640c4 (diff) |
Add Groovy based codegenerator to tools.
Diffstat (limited to 'tools')
-rw-r--r-- | tools/codegenerator/GenerateSWIGBindings.bat | 33 | ||||
-rw-r--r-- | tools/codegenerator/Generator.groovy | 75 | ||||
-rw-r--r-- | tools/codegenerator/Helper.groovy | 756 | ||||
-rw-r--r-- | tools/codegenerator/SwigTypeParser.groovy | 552 | ||||
-rw-r--r-- | tools/codegenerator/example/AddonModuleXbmc.i | 24 | ||||
-rw-r--r-- | tools/codegenerator/example/Example-lval-rval.template | 18 | ||||
-rw-r--r-- | tools/codegenerator/example/Example.call.template | 48 | ||||
-rw-r--r-- | tools/codegenerator/example/Example.intypemap.template | 31 | ||||
-rw-r--r-- | tools/codegenerator/example/Example.returns.template | 42 | ||||
-rw-r--r-- | tools/codegenerator/example/native/ModuleXbmc.h | 1 |
10 files changed, 1580 insertions, 0 deletions
diff --git a/tools/codegenerator/GenerateSWIGBindings.bat b/tools/codegenerator/GenerateSWIGBindings.bat new file mode 100644 index 0000000000..35f1ffb40e --- /dev/null +++ b/tools/codegenerator/GenerateSWIGBindings.bat @@ -0,0 +1,33 @@ +@ECHO OFF + +SET cur_dir=%CD% + +SET base_dir=%cur_dir%\..\.. +SET groovy_dir=%base_dir%\lib\groovy +SET generator_dir=%base_dir%\tools\codegenerator +SET bin_dir=%cur_dir%\..\BuildDependencies\bin + +rem go into xbmc/interfaces/python +cd %1\..\python + +SET python_dir=%CD% +SET python_generated_dir=%python_dir%\generated +SET doxygen_dir=%python_generated_dir%\doxygenxml +SET swig_dir=%python_dir%\..\swig + +rem make sure all necessary directories exist and delete any old generated files +IF NOT EXIST %python_generated_dir% md %python_generated_dir% +IF EXIST %python_generated_dir%\%2.xml del %python_generated_dir%\%2.xml +IF EXIST %python_generated_dir%\%2.cpp del %python_generated_dir%\%2.cpp +IF NOT EXIST %doxygen_dir% md %doxygen_dir% + +rem run doxygen +%bin_dir%\doxygen\doxygen.exe > NUL 2>&1 + +rem run swig to generate the XML used by groovy to generate the python bindings +%bin_dir%\swig\swig.exe -w401 -c++ -outdir %python_generated_dir% -o %python_generated_dir%\%2.xml -xml -I"%base_Dir%\xbmc" -xmllang python %swig_dir%\%2.i +rem run groovy to generate the python bindings +java.exe -cp "%groovy_dir%\groovy-all-1.8.4.jar;%groovy_dir%\commons-lang-2.6.jar;%generator_dir%;%python_dir%" groovy.ui.GroovyMain %generator_dir%\Generator.groovy %python_generated_dir%\%2.xml %python_dir%\PythonSwig.cpp.template %python_generated_dir%\%2.cpp %doxygen_dir% + +rem go back to the initial directory +cd %cur_dir%
\ No newline at end of file diff --git a/tools/codegenerator/Generator.groovy b/tools/codegenerator/Generator.groovy new file mode 100644 index 0000000000..c7dc88a642 --- /dev/null +++ b/tools/codegenerator/Generator.groovy @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2005-2012 Team XBMC + * http://www.xbmc.org + * + * This Program 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; either version 2, or (at your option) + * any later version. + * + * This Program 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. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +import groovy.util.Node +import groovy.text.SimpleTemplateEngine +import groovy.xml.XmlUtil + +import Helper + +def usage() +{ + println "java/groovy -cp [...] " + getClass().getName() + " [-verbose] moduleSpecFile templateFile outputFile [doxygenoutputdir]"; + System.exit 1 +} + +def verbose = false; + +newargs = [] + +println args + +args.each { + if (it == '-verbose' || it == '--verbose' || it == '-v') + verbose = true + else + newargs.add(it) +} + +if (newargs.size() != 3 && newargs.size() != 4) + usage() + +// set the doxygen xml directory on the Helper assuming the output file will be placed +// in the same place the doxygen subdirectory is placed +if (newargs.size() > 3) + Helper.setDoxygenXmlDir(new File(newargs[3])) + +File moduleSpec = new File(newargs[0]) +assert moduleSpec.exists() && moduleSpec.isFile(), 'Cannot locate the spec file "' + moduleSpec.getCanonicalPath() + '."' + +spec = [ 'module' : Helper.transformSwigXml(new XmlParser().parse(moduleSpec)) ] + +if (verbose) + println XmlUtil.serialize(spec['module']) + +File templateFile = new File(newargs[1]) +assert templateFile.exists() && templateFile.isFile(), 'Cannot locate the template file "' + templateFile.getCanonicalPath() + '."' + +te = new SimpleTemplateEngine() +println 'Processing "' + templateFile + '" using the module specification for module "' + moduleSpec + '"' +if (verbose) te.setVerbose(true) +template = te.createTemplate(templateFile).make(spec) +String output = template.toString() +if (verbose) println output + +println 'writing' +new File(newargs[2]).write output + diff --git a/tools/codegenerator/Helper.groovy b/tools/codegenerator/Helper.groovy new file mode 100644 index 0000000000..7e54697770 --- /dev/null +++ b/tools/codegenerator/Helper.groovy @@ -0,0 +1,756 @@ +/* + * Copyright (C) 2005-2012 Team XBMC + * http://www.xbmc.org + * + * This Program 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; either version 2, or (at your option) + * any later version. + * + * This Program 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. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +import groovy.xml.XmlUtil +import org.apache.commons.lang.StringEscapeUtils + +import groovy.text.SimpleTemplateEngine +import groovy.text.SimpleTemplateEngine +import java.util.regex.Pattern + +/** + * This class contains a series of helper methods for parsing a xbmc addon spec xml file. It is intended to be + * used from a bindings template. + * + * @author jim + * + */ +public class Helper +{ + private static List classes + private static Map outTypemap = [:] + private static def defaultOutTypeConversion = null + private static Map inTypemap = [:] + private static def defaultInTypeConversion = null + private static def doxygenXmlDir = null + public static String newline = System.getProperty("line.separator"); + + /** + * In order to use any of the typemap helper features, the Helper class needs to be initialized with + * this information. + * @param pclasses is the list of all class nodes from the module + * @param poutTypemap is the typemap table for output return values to the scripting language + * @param defaultOutTypemap is the default typemap to use when the type conversion is unknown + * @param pinTypemap is the typemap table for input parameters from the scripting language + * @param defaultInTypemap is the default typemap for the input parameters from the scripting language + */ + public static void setup(List pclasses, Map poutTypemap, def defaultOutTypemap, + Map pinTypemap, def defaultInTypemap) + { + classes = pclasses ? pclasses : [] + if (poutTypemap) outTypemap.putAll(poutTypemap) + if (defaultOutTypemap) defaultOutTypeConversion = defaultOutTypemap + if (pinTypemap) inTypemap.putAll(pinTypemap) + if (defaultInTypemap) defaultInTypeConversion = defaultInTypemap + } + + public static class Sequence + { + private long cur = 0; + + public long increment() { return ++cur } + } + + private static ThreadLocal<Sequence> curSequence = new ThreadLocal<Sequence>(); + + public static void setDoxygenXmlDir(File dir) { doxygenXmlDir = dir } + + private static String retrieveDocStringFromDoxygen(Node methodOrClass) + { + if (doxygenXmlDir == null) + return null + + Node doc = null + def ret = '' + + // make the class name or namespave + String doxygenId = findFullClassName(methodOrClass,'_1_1') + boolean isInClass = doxygenId != null + if (!doxygenId) + doxygenId = findNamespace(methodOrClass,'_1_1',false) + doxygenId = (isInClass ? 'class' : 'namespace') + doxygenId + + String doxygenFilename = doxygenId + '.xml' + File doxygenXmlFile = new File(doxygenXmlDir,doxygenFilename) + if (! doxygenXmlFile.exists()) + { + System.out.println("WARNING: Cannot find doxygen file for ${methodOrClass.toString()} which should be \"${doxygenXmlFile}\"") + return null + } + + Node docspec = (new XmlParser().parse(doxygenXmlFile)) + if (methodOrClass.name() == 'class') + doc = docspec.compounddef[0].detaileddescription[0] + else // it's a method of some sort ... or it better be + { + Node memberdef = docspec.depthFirst().find { + return ((it.name() == 'memberdef' && it.@kind == 'function' && it.@id.startsWith(doxygenId)) && + (it.name != null && it.name.text().trim() == methodOrClass.@sym_name)) + } + + doc = memberdef != null ? memberdef.detaileddescription[0] : null + } + + if (doc != null) + { + def indent = ' ' + def curIndent = '' + def prevIndent = '' + + def handleDoc + handleDoc = { + if (it instanceof String) + ret += it + else // it's a Node + { + if (it.name() == 'detaileddescription') + it.children().each handleDoc + else if (it.name() == 'para') + { + it.children().each handleDoc + ret += (it.parent()?.name() == 'listitem') ? newline : (newline + newline) + } + else if (it.name() == 'ref' || it.name() == "ulink") + ret += (it.text() + ' ') + else if (it.name() == 'verbatim') + ret += it.text().denormalize() + else if (it.name() == 'itemizedlist') + { + ret += newline + prevIndent = curIndent + curIndent += indent + it.children().each handleDoc + curIndent = prevIndent + } + else if (it.name() == 'listitem') + { + ret += (curIndent + '- ') + it.children().each handleDoc + } + else if (it.name() == 'linebreak') + ret += newline + else if (it.name() == 'ndash') + ret += "--" + else + System.out.println("WARNING: Cannot parse the following as part of the doxygen processing:" + XmlUtil.serialize(it)) + } + } + + doc.children().each handleDoc + } + + return ret.denormalize() + } + + /** + * <p>This method uses the previously set outTypemap and defaultOutTypemap to produce the chunk of code + * that will be used to return the method invocation result to the scripting language. For example, in + * python, if the return type from the method is a long, then the OutConversion could look something like:</p> + * <code> + * result = PyInt_FromLong(thingReturnedFromMethod); + * </code> + * <p>This could have resulted from a mini-template stored as the way to handle 'long's in the outTypemap:</p> + * <code> + * ${result} = PyInt_FromLong(${api}); + * </code> + * @param apiType - is the Swig typecode that describes the return type from the native method + * @param method - is the node from the module xml that contains the method description + * @return the code chunk as a string ready to be placed into the generated code. + */ + public static String getOutConversion(String apiType, String apiName, Node method, Map overrideBindings = null, boolean recurse = true) + { + def convertTemplate = outTypemap[apiType] + + // String apiLType = SwigTypeParser.convertTypeToLType(apiType) + // if (convertTemplate == null) convertTemplate = outTypemap[apiLType] + + // is the returns a pointer to a known class + Node classNode = null + if (convertTemplate == null && apiType.startsWith('p.')) + { + classNode = findClassNodeByName(parents(method)[0], SwigTypeParser.getBaseType(apiType),method) + if (classNode) convertTemplate = defaultOutTypeConversion + } + + if (convertTemplate == null) + { + // Look for Pattern for keys that might fit + convertTemplate = outTypemap.find({ key, value -> (key instanceof Pattern && key.matcher(apiType).matches()) })?.value + } + + if (!convertTemplate) + { + if (recurse) + return getOutConversion(SwigTypeParser.SwigType_ltype(apiType),apiName,method,overrideBindings,false) + else + throw new RuntimeException("WARNING: Cannot convert the return value of swig type ${apiType} for the call ${Helper.findFullClassName(method) + '::' + Helper.callingName(method)}") + } + + boolean seqSetHere = false + Sequence seq = curSequence.get() + if (seq == null) + { + seqSetHere = true + seq = new Sequence() + curSequence.set(seq) + } + + Map bindings = ['result' : apiName, 'api' : 'apiResult', 'type' : "${apiType}", + 'method' : method, 'helper' : Helper.class, + 'swigTypeParser' : SwigTypeParser.class, + 'sequence' : seq ] + if (classNode) bindings['classnode'] = classNode + + if (overrideBindings) bindings.putAll(overrideBindings) + + if (convertTemplate instanceof List) /// then we expect the template string/file to be the first entry + { + Map additionalBindings = convertTemplate.size() > 1 ? convertTemplate[1] : [:] + bindings.putAll(additionalBindings) + convertTemplate = convertTemplate[0] + } + + if (seqSetHere) curSequence.set(null) + return new SimpleTemplateEngine().createTemplate(convertTemplate).make(bindings).toString() + } + + /** + * <p>This method uses the previously set inTypemap and defaultInTypemap to produce the chunk of code + * that will be used to convert input parameters from the scripting language to the native method invocation + * parameters. For example, if the native call takes a 'String' then the InConversion could look something like:</p> + * <code> + * if (pythonStringArgument) PyXBMCGetUnicodeString(cArgument,pythonStringArgument,"cArgumentName"); + * </code> + * <p>This could have resulted from a mini-template stored as the way to handle 'long's in the outTypemap:</p> + * <code> + * if (${slarg}) PyXBMCGetUnicodeString(${api},${slarg},"${api}"); + * </code> + * @param apiType - is the Swig typecode that describes the parameter type from the native method + * @param apiName - is the name of the parameter from the method parameter list in the api + * @param slName - is the name of the varialbe that holds the parameter from the scripting language + * @param method - is the node from the module xml that contains the method description + * @return the code chunk as a string ready to be placed into the generated code. + */ + public static String getInConversion(String apiType, String apiName, String slName, Node method, Map overrideBindings = null) + { + return getInConversion(apiType, apiName, apiName, slName, method, overrideBindings); + } + + /** + * <p>This method uses the previously set inTypemap and defaultInTypemap to produce the chunk of code + * that will be used to convert input parameters from the scripting language to the native method invocation + * parameters. For example, if the native call takes a 'String' then the InConversion could look something like:</p> + * <code> + * if (pythonStringArgument) PyXBMCGetUnicodeString(cArgument,pythonStringArgument,"cArgumentName"); + * </code> + * <p>This could have resulted from a mini-template stored as the way to handle 'long's in the outTypemap:</p> + * <code> + * if (${slarg}) PyXBMCGetUnicodeString(${api},${slarg},"${api}"); + * </code> + * @param apiType - is the Swig typecode that describes the parameter type from the native method + * @param apiName - is the name of the varialbe that holds the api parameter + * @param paramName - is the name of the parameter from the method parameter list in the api + * @param slName - is the name of the varialbe that holds the parameter from the scripting language + * @param method - is the node from the module xml that contains the method description + * @return the code chunk as a string ready to be placed into the generated code. + */ + public static String getInConversion(String apiType, String apiName, String paramName, String slName, Node method, Map overrideBindings = null) + { + def convertTemplate = inTypemap[apiType] + + String apiLType = SwigTypeParser.convertTypeToLTypeForParam(apiType) + + if (convertTemplate == null) convertTemplate = inTypemap[apiLType] + + // is the returns a pointer to a known class + if (convertTemplate == null && apiType.startsWith('p.')) + { + // strip off rval qualifiers + String thisNamespace = Helper.findNamespace(method) + Node clazz = classes.find { Helper.findFullClassName(it) == apiLType.substring(2) || + (it.@sym_name == apiLType.substring(2) && thisNamespace == Helper.findNamespace(it)) } + if (clazz != null) convertTemplate = defaultInTypeConversion + } + + // Look for Pattern for keys that might fit + if (convertTemplate == null) + convertTemplate = inTypemap.find({ key, value -> (key instanceof Pattern && key.matcher(apiType).matches()) })?.value + + // Try the LType + if (convertTemplate == null) + convertTemplate = inTypemap.find({ key, value -> (key instanceof Pattern && key.matcher(apiLType).matches()) })?.value + + if (!convertTemplate){ + // it's ok if this is a known type + if (!isKnownApiType(apiType,method) && !isKnownApiType(apiLType,method)) + System.out.println("WARNING: Unknown parameter type: ${apiType} (or ${apiLType}) for the call ${Helper.findFullClassName(method) + '::' + Helper.callingName(method)}") + convertTemplate = defaultInTypeConversion + } + + if (convertTemplate) + { + boolean seqSetHere = false + Sequence seq = curSequence.get() + if (seq == null) + { + seqSetHere = true + seq = new Sequence() + curSequence.set(seq) + } + + Map bindings = [ + 'type': "${apiType}", 'ltype': "${apiLType}", + 'slarg' : "${slName}", 'api' : "${apiName}", + 'param' : "${paramName}", + 'method' : method, 'helper' : Helper.class, + 'swigTypeParser' : SwigTypeParser.class, + 'sequence' : seq + ] + + if (overrideBindings) bindings.putAll(overrideBindings) + + if (convertTemplate instanceof List) /// then we expect the template string/file to be the first entry + { + Map additionalBindings = convertTemplate.size() > 1 ? convertTemplate[1] : [:] + bindings.putAll(additionalBindings) + convertTemplate = convertTemplate[0] + } + + if (seqSetHere) curSequence.set(null); + return new SimpleTemplateEngine().createTemplate(convertTemplate).make(bindings).toString() + } + + return '' + } + + static def ignoreAttributes = ['classes', 'symtab', 'sym_symtab', + 'sym_overname', 'options', 'sym_nextSibling', 'csym_nextSibling', + 'sym_previousSibling' ] + + /** + * <p>Transform a Swig generated xml file into something more manageable. For the most part this method will:</p> + * + * <li>1) Make all pertinent 'attributelist' elements actually be attributes of the parent element while + * an attribute with the name 'name' will become that parent element name.</li> + * <li>2) Filter out unused attributes</li> + * <li>3) Filter out the automatically included 'swig'swg'</li> + * <li>4) Flatten out the remaining 'include' elements</li> + * <li>5) Removes extraneous default argument function/method Node</li> + * <li>6) Converts all type tables to a single entry under the main module node removing all 1-1 mappings.</li> + * <li>7) Removes all non-public non-constructor methods.</li> + * @param swigxml is the raw swig output xml document + * @return the transformed document + */ + public static Node transformSwigXml(Node swigxml) + { + Node node = transform(swigxml, + { + // do not include the 'include' entry that references the default swig.swg file. + !(it.name() == 'include' && + // needs to also contain an attribute list with an attribute 'name' that matches the swig.swg file + it.find { + it.name() == 'attributelist' && it.find { + it.name() == 'attribute' && it.@name == 'name' && it.@value =~ /swig\.swg$/ + } + } || + // also don't include any typescope entries + it.name() == 'typescopesitem' || it.name() == 'typetabsitem' + ) + },{ key, value -> !ignoreAttributes.contains(key) }) + + // now make sure the outer most node is an include and there's only one + assert node.include?.size() == 1 && node.include[0].module?.size() == 1 && node.include[0].module[0]?.@name != null, + "Invalid xml doc result. Expected a single child node of the root node call 'include' with a single 'module' child node but got " + XmlUtil.serialize(node) + + // create an outermost 'module' node with the correct name + Node ret = new Node(null, 'module', [ 'name':node.include[0].module[0].@name] ) + node.include[0].children().each { if (it.name() != 'module') ret.append(it) } + + // flatten out all other 'include' elements, parmlists, and typescopes + flatten(ret,['include', 'parmlist', 'typescope' ]) + + // remove any function nodes with default arguments + List tmpl = [] + tmpl.addAll(ret.depthFirst()) + for (Node cur : tmpl) + { + if ((cur.name() == 'function' || cur.name() == 'constructor') && cur.@defaultargs != null) + cur.parent().remove(cur) + } + + // now validate that no other methods are overloaded since we can't handle those right now. + functionNodesByOverloads(ret).each { key, value -> assert value.size() == 1, "Cannot handle overloaded methods unless simply using defaulting: " + value } + + // now gather up all of the typetabs and add a nice single + // typetab entry with a better format in the main module + List allTypetabs = ret.depthFirst().findAll { it.name() == 'typetab' } + Node typenode = new Node(ret,'typetab') + allTypetabs.each { + it.attributes().each { key, value -> + if (key != 'id' && key != value) + { + Node typeentry = new Node(null,'entry') + String namespace = findNamespace(it) + typeentry.@namespace = namespace != null ? namespace.trim() : '' + typeentry.@type = key + typeentry.@basetype = value + + if (typenode.find({ it.@basetype == typeentry.@basetype && it.@namespace == typeentry.@namespace }) == null) + typenode.append(typeentry) + } + } + it.parent().remove(it) + } + + // now remove all non-public methods, but leave constructors + List allMethods = ret.depthFirst().findAll({ it.name() == 'function' || it.name() == 'destructor' || it.name() == 'constructor'}) + allMethods.each { + if (it.@access != null && it.@access != 'public' && it.name() != 'constructor') + it.parent().remove(it) + else + { + def doc = retrieveDocStringFromDoxygen(it) + if (doc != null && doc != '' && doc.trim() != ' ') + new Node(it,'doc',['value' : doc]) + } + } + + // add the doc string to the classes + List allClasses = ret.depthFirst().findAll({ it.name() == 'class'}) + allClasses.each { + def doc = retrieveDocStringFromDoxygen(it) + if (doc != null && doc != '' && doc.trim() != ' ') + new Node(it,'doc',['value' : doc]) + } + + return ret + } + + /** + * @return true if the class node has a defined constructor. false otherwise. + */ + public static boolean hasDefinedConstructor(Node clazz) + { + return (clazz.constructor != null && clazz.constructor.size() > 0) + } + + /** + * @return true id this Node has a docstring associated with it. + */ + public static boolean hasDoc(Node methodOrClass) + { + return methodOrClass.doc != null && methodOrClass.doc[0] != null && methodOrClass.doc[0].@value != null + } + + /** + * @return true of the class node has a constructor but it's hidden (not 'public'). false otherwise. + */ + public static boolean hasHiddenConstructor(Node clazz) + { + return (hasDefinedConstructor(clazz) && clazz.constructor[0].@access != null && clazz.constructor[0].@access != 'public') + } + + /** + * <p>This will look through the entire module and look up a class node by name. It will return null if + * that class node isn't found. It's meant to be used to look up base classes from a base class list + * so it's fairly robust. It goes through the following rules:</p> + * + * <li>Does the FULL classname (considering the namespace) match the name provided.</li> + * <li>Does the FULL classname match the reference nodes namespace + '::' + the provided classname.</li> + * <li>Does the class node's name (which may contain the full classname) match the classname provided.</li> + * + * <p>Note, this method is not likely to find the classnode if you just pass a simple name and + * no referenceNode in the case where namespaces are used extensively.</p> + */ + public static Node findClassNodeByName(Node module, String classname, Node referenceNode = null) + { + return module.depthFirst().findAll({ it.name() == 'class' }).find { + // first check to see if this FULL class name matches + if (findFullClassName(it).trim() == classname.trim()) return true + + // now check to see if it matches the straight name considering the reference node + if (referenceNode != null && (findNamespace(referenceNode) + classname) == findFullClassName(it)) return true + + // now just see if it matches the straight name + if (it.@name == classname) return true + + return false + } + } + + /** + * Find me the class node that this node either is, or is within. + * If this node is not within a class node then it will return null. + */ + public static Node findClassNode(Node node) + { + if (node.name() == 'class') return node + return node.parent() == null ? null : findClassNode(node.parent()) + } + + /** + * If this node is a class node, or a child of a class name (for example, a method) then + * the full classname, with the namespace will be returned. Otherwise, null. + */ + public static String findFullClassName(Node node, String separator = '::') + { + String ret = null + List rents = parents(node, { it.name() == 'class' }) + if (node.name() == 'class') rents.add(node) + rents.each { + if (ret == null) + ret = it.@sym_name + else + ret += separator + it.@sym_name + } + + return ret ? findNamespace(node,separator) + ret : null + } + + /** + * Given the Node this method looks to see if it occurs within namespace and returns + * the namespace as a String. It includes the trailing '::' + */ + public static String findNamespace(Node node, String separator = '::', boolean endingSeparator = true) + { + String ret = null + parents(node, { it.name() == 'namespace' }).each { + if (ret == null) + ret = it.@name + else + ret += separator + it.@name + } + + return ret == null ? '' : (ret + (endingSeparator ? separator : '')) + } + + /** + * Gather up all of the parent nodes in a list ordered from the top node, down to the + * node that's passed as a parameter. + */ + public static List parents(Node node, Closure filter = null, List ret = null) + { + Node parent = node.parent() + if (parent != null) + { + ret = parents(parent,filter,ret) + if (filter == null || filter.call(parent)) + ret += parent + } + else if (ret == null) + ret = [] + + return ret + } + + /** + * Group together overloaded methods into a map keyed by the first method's id. Each + * entry in this map contains a list of nodes that represent overloaded versions of + * the same method. + */ + public static Map functionNodesByOverloads(Node module) + { + // find function nodes + Map ret = [:] + module.depthFirst().each { + if (it.name() == 'function' || it.name() == 'constructor' || it.name() == 'destructor') + { + String id = it.@sym_overloaded != null ? it.@sym_overloaded : it.@id + if (ret[id] == null) ret[id] = [it] + else ret[id] += it + } + } + return ret + } + + /** + * Because the return type is a combination of the function 'decl' and the + * function 'type,' this method will construct a valid Swig typestring from + * the two. + */ + public static String getReturnSwigType(Node method) + { + // we're going to take a shortcut here because it appears only the pointer indicator + // ends up attached to the decl. + String prefix = (method.@decl != null && method.@decl.endsWith('.p.')) ? 'p.' : '' + return method.@type != null ? prefix + method.@type : 'void' + } + + /** + * Given the method node this will produce the actual name of the method as if + * it's being called. In the case of a constructor it will do a 'new.' In the + * case of a destructor it will produce a 'delete.' + */ + public static String callingName(Node method) + { + // if we're not in a class we need the fully qualified name + String clazz = findFullClassName(method) + // if we're in a class then we are going to assume we have a 'self' pointer + // that we are going to invoke this on. + if (clazz == null) + return method.@name + + if (method.name() == 'constructor') + return "new ${findNamespace(method)}${method.@sym_name}" + + if (method.name() == 'destructor') + return 'delete' + + // otherwise it's just a call on a class being invoked on an instance + return method.@name + } + + /** + * Swig has 'insert' nodes in it's parse tree that contain code chunks that are + * meant to be inserted into various positions in the generated code. This method + * will extract the nodes that refer to the specific section asked for. See the + * Swig documentation for more information. + */ + public static List getInsertNodes(Node module, String section) + { + return module.insert.findAll { section == it.@section || (section == 'header' && it.@section == null) } + } + + public static String unescape(Node insertSection) { return unescape(insertSection.@code) } + + public static String unescape(String insertSection) { return StringEscapeUtils.unescapeHtml(insertSection) } + + public static boolean isDirector(Node method) + { + Node clazz = findClassNode(method) + if (!clazz || !clazz.@feature_director) + return false + if (method.name() == 'destructor') + return false + if (method.name() == 'constructor') + return false + return method.@storage && method.@storage == 'virtual' + } + + /** + * This method will search from the 'searchFrom' Node up to the root + * looking for a %feature("knownbasetypes") declaration that the given 'type' is + * known for 'searchFrom' Node. + */ + public static boolean isKnownBaseType(String type, Node searchFrom) + { + return hasFeatureSetting(type,searchFrom,'feature_knownbasetypes',{ it.split(',').find({ it == type }) != null }) + } + + /** + * This method will search from the 'searchFrom' Node up to the root + * looking for a %feature("knownapitypes") declaration that the given 'type' is + * known for 'searchFrom' Node. + */ + public static boolean isKnownApiType(String type, Node searchFrom) + { + String rootType = SwigTypeParser.getBaseType(type) + return hasFeatureSetting(type,searchFrom,'feature_knownapitypes',{ it.split(',').find({ it == rootType }) != null }) + } + + private static boolean hasFeatureSetting(String type, Node searchFrom, String feature, Closure test) + { + if (!searchFrom) + return false + + if (searchFrom.attribute(feature) && test.call(searchFrom.attribute(feature))) + return true + + return hasFeatureSetting(type,searchFrom.parent(),feature,test) + } + + private static void flatten(Node node, List elementsToRemove) + { + for (boolean done = false; !done;) + { + done = true + for (Node child : node.breadthFirst()) + { + if (elementsToRemove.contains(child.name())) + { + Node parent = child.parent() + parent.remove(child) + child.each { parent.append(it) } + done = false + break + } + } + } + } + + private static Node transform(Node node, Closure nodeFilter, Closure attributeFilter) + { + // need to create a map and a list of nodes (which will be children) from the + // attribute list. + Map attributes = [:] + List nodes = [] + node.each { + if (nodeFilter == null || nodeFilter.call(it) == true) + { + if (it.name() == 'attributelist') + { + Tuple results = transformSwigAttributeList(it) + attributes.putAll(results[0].findAll(attributeFilter)) + List childNodes = results[1] + childNodes.each { + if (nodeFilter != null && nodeFilter.call(it) == true) + nodes.add(transform(it,nodeFilter,attributeFilter)) + } + } + else + nodes.add(transform(it,nodeFilter,attributeFilter)) + } + } + + // transfer the addr attribute from the original node over to the 'id' attribute of the + // new node by adding it to the attributes map + if (node.@addr) + { + // copy over the other attributes + node.attributes().findAll { key,value -> if (key != 'addr' && key != 'id') attributes[key] = value } + attributes['id'] = node.@addr + } + + // In the case when the Node is a cdecl, we really want to replace the node name + // with the 'kind' attribute value. + Node ret + if (node.name() == 'cdecl' && attributes.containsKey('kind')) + ret = new Node(null, attributes['kind'], attributes.findAll({key, value -> key != 'kind' } )) + else + ret = new Node(null, node.name(), attributes) + nodes.each { ret.append(it) } + return ret + } + + private static Tuple transformSwigAttributeList(Node attributeList) + { + Map attributes = [:] + List nodes = [] + attributeList.each { + if (it.name() == 'attribute') + attributes[it.@name] = it.@value + else + nodes.add(it) + } + return [attributes, nodes] + } +} + diff --git a/tools/codegenerator/SwigTypeParser.groovy b/tools/codegenerator/SwigTypeParser.groovy new file mode 100644 index 0000000000..7b086541ee --- /dev/null +++ b/tools/codegenerator/SwigTypeParser.groovy @@ -0,0 +1,552 @@ +/* + * Copyright (C) 2005-2012 Team XBMC + * http://www.xbmc.org + * + * This Program 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; either version 2, or (at your option) + * any later version. + * + * This Program 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. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +/** + * These methods are somewhat ugly because they have been copied out of + * the Swig source code and simply made compilable with groovy. They could + * all be much cleaner and smaller if they were completely groovyfied but + * I have no intention of doing that since they are complicated and they work + * and I don't want to try to trace down problems that would be inevitable + * with such a refactor. + */ +public class SwigTypeParser +{ + /** + * This holds a mapping for typedefs from a type to it's base type. + */ + private static Map typeTable = [:] + + public static void appendTypeTable(Node typetab) { typetab.each { typeTable[it.@namespace + it.@type] = it.@basetype } } + + /** + * Convert the type to an ltype considering the overloaded conversions. + */ + public static String convertTypeToLTypeForParam(String ty) + { + // in the case where we're converting from a type to an ltype for a parameter, + // and the type is a r.q(const).*, we are going to assume the ltype is + // a "pass-by-value" on the stack. + return (ty.trim().startsWith('r.q(const).') ? SwigTypeParser.SwigType_ltype(ty.trim().substring(11)) : SwigTypeParser.SwigType_ltype(ty.trim())) + } + + /** + * This method will return the base type for the provided type string. For example, + * if the type string is p.MyType you will get MyType. If the string is + * p.q(const).int you will get 'int' + */ + public static String getBaseType(String ty) + { + int li = ty.lastIndexOf('.') + return li >= 0 ? ty.substring(li + 1) : ty + } + + /** + * SwigType_str() + * + * Create a C string representation of a datatype. + */ + public static String SwigType_str(String ty, String id = null) + { + String result = id ? id : '' + String nextelement + String forwardelement + List elements = SwigType_split(ty) + if (elements == null) elements = [] + int nelements = elements.size() + String element = nelements > 0 ? elements[0] : null + + /* Now, walk the type list and start emitting */ + for (int i = 0; i < nelements; i++) { + if (i < (nelements - 1)) { + nextelement = elements[i + 1] + forwardelement = nextelement + if (nextelement.startsWith('q(')) { + if (i < (nelements - 2)) forwardelement = elements[i + 2] + } + } else { + nextelement = null + forwardelement = null + } + if (element.startsWith('q(')) { + String q = SwigType_parm(element) + result = q + ' ' + result + } else if (SwigType_ispointer(element)) { + result = "*" + result + if ((forwardelement) && ((SwigType_isfunction(forwardelement) || (SwigType_isarray(forwardelement))))) { + result = "(" + result + ")" + } + } else if (SwigType_ismemberpointer(element)) { + String q = SwigType_parm(element); + result = q + "::*" + result + if ((forwardelement) && ((SwigType_isfunction(forwardelement) || (SwigType_isarray(forwardelement))))) { + result = '(' + result + ')' + } + } else if (SwigType_isreference(element)) { + result = '&' + result + if ((forwardelement) && ((SwigType_isfunction(forwardelement) || (SwigType_isarray(forwardelement))))) { + result = '(' + result + ')' + } + } else if (SwigType_isarray(element)) { + result += '[' + SwigType_parm(element) + ']' + } else if (SwigType_isfunction(element)) { + result += '(' + List parms = SwigType_parmlist(element) + boolean didOne = false + for (String cur : parms) { + String p = SwigType_str(cur) + result += (didOne ? ',' : '') + p + didOne = true + } + result += ')' + } else { + if (element.startsWith("v(...)")) result = result + "..." + else { + String bs = SwigType_namestr(element); + result = bs + ' ' + result + } + } + element = nextelement; + } + // convert template parameters + return result.replaceAll('<\\(', '<').replaceAll('\\)>', '>') + } + + public static String SwigType_typedef_resolve(String t) + { + String td = typeTable[t] + String ret = td == null ? t : td +// System.out.println "trying to resolve ${t} and it appears to be typedefed to ${ret}" + return ret + } + + public static String SwigType_typedef_resolve_all(String t) + { + String prev = t + t = SwigType_typedef_resolve(prev) + while(prev != t) + { + String tmp = t + t = SwigType_typedef_resolve(prev) + prev = tmp + } + return t + } + + /** + * SwigType_ltype(const SwigType *ty) + * + * Create a locally assignable type + */ + public static String SwigType_ltype(String s) { + String result = '' + String tc = s + + /* Nuke all leading qualifiers */ + while (SwigType_isqualifier(tc)) { + tc = SwigType_pop(tc)[1] + } + + if (SwigType_issimple(tc)) { + /* Resolve any typedef definitions */ + String tt = tc + String td + while ((td = SwigType_typedef_resolve(tt)) != tt) { + if ((td != tt) && (SwigType_isconst(td) || SwigType_isarray(td) || SwigType_isreference(td))) { + /* We need to use the typedef type */ + tt = td + break + } + else if (td != tt) tt = td + } + tc = td + } + List elements = SwigType_split(tc) + int nelements = elements.size() + + /* Now, walk the type list and start emitting */ + boolean notypeconv = false + boolean firstarray = true + for (int i = 0; i < nelements; i++) { + String element = elements[i] + /* when we see a function, we need to preserve the following types */ + if (SwigType_isfunction(element)) { + notypeconv = true + } + if (SwigType_isqualifier(element)) { + /* Do nothing. Ignore */ + } else if (SwigType_ispointer(element)) { + result += element + // this is a bit of a short circuit to avoid having to import the entire SwigType_typedef_resolve method which + // handles pointers to typedefed types, etc. + // collapse the rest of the list + String tmps = '' + for (int j = i + 1; j < nelements; j++) tmps += elements[j] + return result + SwigType_ltype(tmps) + //firstarray = false + } else if (SwigType_ismemberpointer(element)) { + result += element + firstarray = false + } else if (SwigType_isreference(element)) { + if (notypeconv) { + result == element + } else { + result += "p." + } + firstarray = false + } else if (SwigType_isarray(element) && firstarray) { + if (notypeconv) { + result += element + } else { + result += "p." + } + firstarray = 0; + } else if (SwigType_isenum(element)) { + boolean anonymous_enum = (element == "enum ") + if (notypeconv || !anonymous_enum) { + result += element + } else { + result += "int" + } + } else { + result += element + } + } + + return result + // convert template parameters + //return result.replaceAll('<\\(', '<').replaceAll('\\)>', '>') + } + + /** + * This creates the C++ declaration for a valid ltype for the type string + * given. For example, if the type is a "const char*" which is equivalent + * to the type string 'p.q(const).char', the return value from this method + * will be "char *". + */ + public static String SwigType_lstr(String type) + { + return SwigType_str(convertTypeToLTypeForParam(type)) + } + + public static boolean SwigType_ispointer(String t) + { + if (t.startsWith('q(')) t = t.substring(t.indexOf('.') + 1,) + return t.startsWith('p.') + } + + public static boolean SwigType_isarray(String t) { return t.startsWith('a(') } + + public static boolean SwigType_ismemberpointer(String t) { return t?.startsWith('m(') } + + public static boolean SwigType_isqualifier(String t) { return t?.startsWith('q(') } + + public static boolean SwigType_isreference(String t) { return t.startsWith('r.') } + + public static boolean SwigType_isenum(String t) { return t.startsWith('enum') } + + public static String SwigType_istemplate(String t) { + int c = t.indexOf("<(") + return (c >= 0 && t.indexOf(')>',c+2) >= 0) + } + + public static boolean SwigType_isfunction(String t) + { + if (t.startsWith('q(')) t = t.substring(t.indexOf('.') + 1,) + return t.startsWith('f(') + } + + public static boolean SwigType_isconst(String t) { + int c = 0 + if (t == null) return false + if (t.substring(c).startsWith("q(")) { + String q = SwigType_parm(t) + if (q.indexOf("const") >= 0) return true + } + /* Hmmm. Might be const through a typedef */ + if (SwigType_issimple(t)) { + String td = SwigType_typedef_resolve(t) + if (td != t) return SwigType_isconst(td) + } + return false + } + + + private static String SwigType_parm(String t) { + int start = t.indexOf("(") + if (start < 0) return null + start++ + int nparens = 0 + int c = start + while (c < t.length()) { + if (t.charAt(c) == ')') { + if (nparens == 0) break; + nparens--; + } + else if (t.charAt(c) == '(') nparens++ + c++; + } + return t.substring(start,c) + } + + /* ----------------------------------------------------------------------------- + * SwigType_parmlist() + * + * Splits a comma separated list of parameters into its component parts + * The input is expected to contain the parameter list within () brackets + * Returns 0 if no argument list in the input, ie there are no round brackets () + * Returns an empty List if there are no parameters in the () brackets + * For example: + * + * Foo(std::string,p.f().Bar<(int,double)>) + * + * returns 2 elements in the list: + * std::string + * p.f().Bar<(int,double)> + * ----------------------------------------------------------------------------- */ + + private static List SwigType_parmlist(String p) { + List list = [] + int itemstart + + assert p, "Cannot pass null to SwigType_parmlist" + itemstart = p.indexOf('(') + assert p.indexOf('.') == -1 || p.indexOf('.') > itemstart, p + " is expected to contain sub elements of a type" + itemstart++ + int c = itemstart + while (c < p.length()) { + if (p.charAt(c) == ',') { + list.add(p.substring(itemstart,c)) + itemstart = c + 1 + } else if (p.charAt(c) == '(') { + int nparens = 1 + c++ + while (c < p.length()) { + if (p.charAt(c) == '(') nparens++ + if (p.charAt(c) == ')') { + nparens-- + if (nparens == 0) break + } + c++ + } + } else if (p.charAt(c) == ')') { + break; + } + if (c < p.length()) c++ + } + + if (c != itemstart) { + list.add(p.substring(itemstart,c)) + } + return list; + } + + /* ----------------------------------------------------------------------------- + * SwigType_namestr() + * + * Returns a string of the base type. Takes care of template expansions + * ----------------------------------------------------------------------------- */ + + private static String SwigType_namestr(String t) { + int d = 0 + int c = t.indexOf("<(") + + if (c < 0 || t.indexOf(')>',c+2) < 0) return t + + String r = t.substring(0,c) + if (t.charAt(c - 1) == '<') r += ' ' + r += '<' + + List p = SwigType_parmlist(t.substring(c + 1)) + for (int i = 0; i < p.size(); i++) { + String str = SwigType_str(p[i], null); + /* Avoid creating a <: token, which is the same as [ in C++ - put a space after '<'. */ + if (i == 0 && str.length() > 0) r += ' ' + r += str + if ((i + 1) < p.size()) r += ',' + } + r += ' >' + String suffix = SwigType_templatesuffix(t); + if (suffix.length() > 0) { + String suffix_namestr = SwigType_namestr(suffix); + r += suffix_namestr + } else { + r += suffix + } + return r; + } + + /* ----------------------------------------------------------------------------- + * SwigType_templatesuffix() + * + * Returns text after a template substitution. Used to handle scope names + * for example: + * + * Foo<(p.int)>::bar + * + * returns "::bar" + * ----------------------------------------------------------------------------- */ + + private static String SwigType_templatesuffix(String t) { + int c = 0 + while (c < t.length()) { + if ((t.charAt(c) == '<') && (t.charAt(c + 1) == '(')) { + int nest = 1 + c++ + while (c < t.length() && nest != 0) { + if (t.charAt(c) == '<') nest++ + if (t.charAt(c) == '>') nest-- + c++ + } + return t.substring(c) + } + c++ + } + return '' + } + + /* ----------------------------------------------------------------------------- + * SwigType_split() + * + * Splits a type into it's component parts and returns a list of string. + * ----------------------------------------------------------------------------- */ + + private static List SwigType_split(String t) { + List list = [] + int c = 0 + int len + + while (c < t.length()) { + len = element_size(t.substring(c)) + String item = t.substring(c,c + len) + list += item + c = c + len + if (c < t.length() && t.charAt(c) == '.') c++ + } + return list; + } + + /* ----------------------------------------------------------------------------- + * static element_size() + * + * This utility function finds the size of a single type element in a type string. + * Type elements are always delimited by periods, but may be nested with + * parentheses. A nested element is always handled as a single item. + * + * Returns the integer size of the element (which can be used to extract a + * substring, to chop the element off, or for other purposes). + * ----------------------------------------------------------------------------- */ + + private static int element_size(String s) { + int nparen + int c = 0 + while (c < s.length()) { + if (s.charAt(c) == '.') { + c++ + return c + } else if (s.charAt(c) == '(') { + nparen = 1 + c++ + while (c < s.length()) { + if (s.charAt(c) == '(') nparen++ + if (s.charAt(c) == ')') { + nparen-- + if (nparen == 0) break + } + c++ + } + } + if (c < s.length()) c++ + } + return c; + } + + /* ----------------------------------------------------------------------------- + * SwigType_pop() + * + * Pop one type element off the type. + * Example: t in: q(const).p.Integer + * t out: p.Integer + * result: q(const). + * ----------------------------------------------------------------------------- */ + + private static Tuple SwigType_pop(String t) { + String result + int c = 0 + + if (t == null) + return null + + int sz = element_size(t.substring(c)) + return [ t.substring(c,c + sz), t.substring(c+sz) ] + } + + private static boolean SwigType_issimple(String t) { + int c = 0 + if (!t) return false + while (c < t.length()) { + if (t.charAt(c) == '<') { + int nest = 1 + c++ + while (c < t.length() && nest != 0) { + if (t.charAt(c) == '<') nest++ + if (t.charAt(c) == '>') nest-- + c++ + } + c-- + } + if (t.charAt(c) == '.') + return false + c++ + } + return true + } + + + public static void main(String[] args) + { + String xmlText = ''' + <typetab> + <entry basetype="std::vector<(p.XBMCAddon::xbmcgui::ListItem)>" type="ListItemList" namespace="XBMCAddon::xbmcgui::"/> + </typetab> + ''' + Node xml = new XmlParser().parseText(xmlText) + + SwigTypeParser.appendTypeTable(xml) + + // testPrint('f(int,int,int)','foo') + // testPrint('p.a(10).p.f(int,p.f(int).int)','foo') + // testPrint('p.q(const).char','foo') + // testPrint('f(r.q(const).String,p.q(const).XBMCAddon::xbmcgui::ListItem,bool)','foo') + // testPrint('r.q(const).String','foo') + // testPrint('q(const).p.q(const).char','foo') + //testPrint('std::vector<(p.String)>','foo') + // testPrint('r.q(const).String') + //System.out.println "${convertTypeToLType('bool')}" + //testPrint('p.q(const).XBMCAddon::xbmcgui::ListItemList') + //testPrint('p.q(const).XBMCAddon::xbmcgui::ListItemList') + testPrint('r.q(const).std::map<(String,String)>', 'foo') + } + + private static void testPrint(String ty, String id = 'foo') + { + println SwigTypeParser.SwigType_ltype(ty) + "|" + SwigTypeParser.SwigType_str(SwigTypeParser.SwigType_ltype(ty),id) + ' ' + " = " + SwigTypeParser.SwigType_str(ty,id) + } +} diff --git a/tools/codegenerator/example/AddonModuleXbmc.i b/tools/codegenerator/example/AddonModuleXbmc.i new file mode 100644 index 0000000000..e4670c0054 --- /dev/null +++ b/tools/codegenerator/example/AddonModuleXbmc.i @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2005-2010 Team XBMC + * http://www.xbmc.org + * + * This Program 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; either version 2, or (at your option) + * any later version. + * + * This Program 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. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +%module xbmc + +%include "native/ModuleXbmc.h" diff --git a/tools/codegenerator/example/Example-lval-rval.template b/tools/codegenerator/example/Example-lval-rval.template new file mode 100644 index 0000000000..77d506e4ac --- /dev/null +++ b/tools/codegenerator/example/Example-lval-rval.template @@ -0,0 +1,18 @@ +<% +import SwigTypeParser +%> +Module Name: ${module.@name} +<% +module.function.each { functionNode -> +%> + function: ${functionNode.@name} +<% + functionNode.parm.eachWithIndex { param, index -> +%> + parameter ${index}= name:${param.@name}, type:${param.@type}<% if (param.@value) { %>, default value: ${param.@value} <% } %> + type:${SwigTypeParser.SwigType_str(param.@type)} + lvalue-type:${SwigTypeParser.SwigType_lstr(param.@type)} +<% + } +} +%> diff --git a/tools/codegenerator/example/Example.call.template b/tools/codegenerator/example/Example.call.template new file mode 100644 index 0000000000..f9e84ed185 --- /dev/null +++ b/tools/codegenerator/example/Example.call.template @@ -0,0 +1,48 @@ +<% +import SwigTypeParser +import Helper + +Helper.setup(null, + [ 'void': 'Py_INCREF(Py_None); ${result} = Py_None;' ], + null, + [ 'p.q(const).char':'${api} = convertSlString(${slarg});', + 'int':'${api} = convertSlInt(${slarg});'], + null) +%> +Module Name: ${module.@name} +<% +module.function.each { functionNode -> +%> + function: ${functionNode.@name} + +<% + functionNode.parm.eachWithIndex { param, index -> +%> + parameter ${index}= name:${param.@name}, type:${param.@type}<% if (param.@value) { %>, default value: ${param.@value} <% } %> + lvalue:${SwigTypeParser.SwigType_lstr(param.@type)} + rvalue:${SwigTypeParser.SwigType_str(param.@type)} + code to handle parameter ${index} { + // declare and set the value that came in from the scripting languge + ScriptingLanguageType sl_${param.@name} = /* set the value from the scripting language */; + // declare and set the variable that will contain the api parameter + ${SwigTypeParser.SwigType_lstr(param.@type)} p_${param.@name}; + ${Helper.getInConversion(param.@type,'p_' + param.@name,'sl_' + param.@name, functionNode)} + } +<% + } +%> + code to invoke the api method { + ${Helper.callingName(functionNode)}( <% + functionNode.parm.eachWithIndex { param, i -> + %> p_${param.@name}${i < functionNode.parm.size() - 1 ? "," : ""} <% } %> ); + } + + code to handle return value { + // This is an example of how Python handles return values + Py_Object* result; + ${Helper.getOutConversion(Helper.getReturnSwigType(functionNode),'result',functionNode)} + return result; + } +<% +} +%> diff --git a/tools/codegenerator/example/Example.intypemap.template b/tools/codegenerator/example/Example.intypemap.template new file mode 100644 index 0000000000..de90963b60 --- /dev/null +++ b/tools/codegenerator/example/Example.intypemap.template @@ -0,0 +1,31 @@ +<% +import SwigTypeParser +import Helper + +Helper.setup(null,null,null, + [ 'p.q(const).char':'${api} = convertSlString(${slarg});', + 'int':'${api} = convertSlInt(${slarg});'], + null) +%> +Module Name: ${module.@name} +<% +module.function.each { functionNode -> +%> + function: ${functionNode.@name} +<% + functionNode.parm.eachWithIndex { param, index -> +%> + parameter ${index}= name:${param.@name}, type:${param.@type}<% if (param.@value) { %>, default value: ${param.@value} <% } %> + type:${SwigTypeParser.SwigType_str(param.@type)} + lvalue-type:${SwigTypeParser.SwigType_lstr(param.@type)} + code to handle parameter ${index} { + // declare and set the value that came in from the scripting languge + ScriptingLanguageType sl_${param.@name} = /* set the value from the scripting language */; + // declare and set the variable that will contain the api parameter + ${SwigTypeParser.SwigType_lstr(param.@type)} p_${param.@name}; + ${Helper.getInConversion(param.@type,'p_' + param.@name,'sl_' + param.@name, functionNode)} + } +<% + } +} +%> diff --git a/tools/codegenerator/example/Example.returns.template b/tools/codegenerator/example/Example.returns.template new file mode 100644 index 0000000000..074d30f8c5 --- /dev/null +++ b/tools/codegenerator/example/Example.returns.template @@ -0,0 +1,42 @@ +<% +import SwigTypeParser +import Helper + +Helper.setup(null, + [ 'void': 'Py_INCREF(Py_None); ${result} = Py_None;' ], + null, + [ 'p.q(const).char':'${api} = convertSlString(${slarg});', + 'int':'${api} = convertSlInt(${slarg});'], + null) +%> +Module Name: ${module.@name} +<% +module.function.each { functionNode -> +%> + function: ${functionNode.@name} + +<% + functionNode.parm.eachWithIndex { param, index -> +%> + parameter ${index}= name:${param.@name}, type:${param.@type}<% if (param.@value) { %>, default value: ${param.@value} <% } %> + type:${SwigTypeParser.SwigType_str(param.@type)} + lvalue-type:${SwigTypeParser.SwigType_lstr(param.@type)} + code to handle parameter ${index} { + // declare and set the value that came in from the scripting languge + ScriptingLanguageType sl_${param.@name} = /* set the value from the scripting language */; + // declare and set the variable that will contain the api parameter + ${SwigTypeParser.SwigType_lstr(param.@type)} p_${param.@name}; + ${Helper.getInConversion(param.@type,'p_' + param.@name,'sl_' + param.@name, functionNode)} + } +<% + } +%> + code to handle return value { + // This is an example of how Python handles return values + Py_Object* result; + ${Helper.getOutConversion(Helper.getReturnSwigType(functionNode),'result',functionNode)} + return result; + } +<% +} +%> diff --git a/tools/codegenerator/example/native/ModuleXbmc.h b/tools/codegenerator/example/native/ModuleXbmc.h new file mode 100644 index 0000000000..14dd80d762 --- /dev/null +++ b/tools/codegenerator/example/native/ModuleXbmc.h @@ -0,0 +1 @@ +void log(const char* msg, int level = LOGNOTICE); |