//************************************************************************** // Chart Director 4.0 to CodeWorker: main script // // // Here is the main script for the development process of te porting to // CodeWorker, leading the parsing of the C++ API and the translation of C++ // examples to CodeWorker's scripts and the execution of examples and the // translation of the HTML documentation of the C++ API. // // It illustrates the strength of a generative programming approach on // projects where a business heart splits to multiple derivatives. // // Here, the business heart leading to derivatives is the C++ API. The // multiple derivatives are the porting to programming languages and to // documentations. // // Rationale of a generative programming approach in Chart Director: // a change of the C++ API should have repercussions on the multiple // derivatives automatically. Further: a change of a C++ example // should too. // // Descriptions of scripts called here: // - "ChartDirector.cwp": parses the C++ API, // - "chartDirector.cwt": generates the C++ binding to CodeWorker, // - "ChartDirectorSample.cwp": translates C++ examples to CodeWorker, // - "ChartDirectorHTML.cwp": translates the HTML doc of the C++ API // // The command line expects only one argument (except this script itself): // the complete path leading to "chartdir.h". Example: // codeworker ChartDirectorSample.cws c:/win32App/ChartDirector/include/chartdir.h //************************************************************************** //-------------------------------------------------------------------------- // Useful functions, used here and in other scripts //-------------------------------------------------------------------------- // Given a *Array type, return the corresponding C++ underlying base type function getBaseTypeOfArray(sArrayType : value) { switch(sArrayType) { case "IntArray": return "int"; case "DoubleArray": return "double"; case "StringArray": return "char"; } } // Check whether the class 'sClass1Name' is an instance of 'sClass2Name'. // Note that if a class was declared as a typedef of anther class, // we consider an inheritance link. // Note that a class inherits from itself. // Note that the cast operator of a ArrayMath to a DoubleArray gives // rise to an implicit inheritance relationship. function instanceOf(sClass1Name : value, sClass2Name : value) { if !project.classes.findElement(sClass1Name) return false; localref theClass1 = project.classes[sClass1Name]; if theClass1.name = sClass2Name return true; if theClass1.typedef return instanceOf(theClass1.typedef, sClass2Name); if sClass1Name == "ArrayMath" && sClass2Name == "DoubleArray" return true; return instanceOf(theClass1.inheritance, sClass2Name); } // Given a type name (class name or enum type name), returns the complete // definition of it: // - attributes + methods for a class, // - enum values for an enum type function getType(scope : node, sType : value, definedType : reference) { if scope.classes.findElement(sType) { ref definedType = scope.classes[sType]; return true; } if scope.enums.findElement(sType) { ref definedType = scope.enums[sType]; return true; } if scope.parent.existVariable() return getType(scope.parent, sType, definedType); return false; } // Given a type definition, returns whether it must be translated to a CodeWorker's // tree node or not. If not, it is a value (always a string in CodeWorker, even numbers). function isNodeType(theType : node) { if theType == "base" { if theType.is_reference return true; if !theType.pointers return false; if theType.name == "char" && $theType.pointers.length() <= 1$ return false; } else if theType == "enum" { if theType.is_reference return true; if !theType.pointers return false; } return true; } // Given a type definition, return its translation for the HTML documentation // of CodeWorker. Example: the C++ types 'int*' or 'IntArray' are translated // to 'int[]'. It is purely informational: CodeWorker is an untyped language. function getHTMLType(theType : node) { local sType; if theType == "class" { sType = "" + theType.name + ""; } else if theType == "enum" { if theType.name sType = "enum " + theType.name; else sType = "int"; } else if theType == "array" || theType == "special" { sType = rsubString(theType.name.toLowerString(), 5) + "[]"; } else if theType == "special" { sType = "" + theType.name + ""; } else { if theType.name == "char" { if theType.pointers { if theType.pointers == "*" { sType = "string"; } else { sType = "string[]"; } } else { sType = "char"; } } else if theType.pointers { sType = theType.name + "[]"; } else { sType = theType.name; } } return sType; } // Given a type definition, return the corresponding C++ writing for the // CodeWorker's plugin, such as one can assign a value to a variable // declared of this type. // It means that we ignore the 'const' specifier. // Note that a class name maps to a C++ wrapper, which embeds the // Chart Director's instance. // Note that '*Array' classes and 'MemBlock' (called 'array' and 'special' // types respectively), are specialized respectively to 'CWPlugin*Array' // and 'CWPluginMemBlock', so as to free memory automatically once the // container isn't necessary anymore. function getCppValueType(theType : node) { local sType; if theType == "class" { sType = "External" + theType.name + "ValueNode*"; } else if theType == "enum" { if theType.name sType = "Chart::" + theType.name; else sType = "int"; } else if theType == "array" || theType == "special" { sType = "CWPlugin" + theType.name; } else { sType = theType.name + theType.pointers + theType.is_reference; } return sType; } // Given a type definition, return the corresponding C++ writing for the // CodeWorker's plugin. // Note that 'special' and 'array' types are never given by value, to // avoid a useless implicit copy. // See 'getCppValueType' for more information. function getCppType(theType : node) { local sType; if theType.const sType = "const "; sType += getCppValueType(theType); if (theType == "array" || theType == "special") && !theType.pointers && !theType.is_reference { sType += '&'; } return sType; } // Given a type definition, return the corresponding C++ writing for the // CodeWorker's plugin, intended to return the result of a function. // Here, 'special' and 'array' types have to be returned without managing // the memory, to avoid freeing twice the internal array. function getCppReturnType(theType : node) { local sType; if theType.const sType = "const "; if theType == "class" { sType += "External" + theType.name + "ValueNode*"; } else if theType == "enum" { if theType.name sType += "Chart::" + theType.name; else sType += "int"; } else if theType == "array" || theType == "special" { sType += theType.name; } else { sType += theType.name + theType.pointers + theType.is_reference; } return sType; } //-------------------------------------------------------------------------- // Parsing of the C++ API //-------------------------------------------------------------------------- // Parses "chartdir.h", passed as the first and lonely argument of the // command line. The static parse tree 'project' (accessible from anywhere) // is populated with the parse data. parseAsBNF("chartDirector.cwp", project, _ARGS#front); // The local variable 'sPath' is worth the root directory for Chart Director. // It is assigned to the working path of CodeWorker, avalaible by calling // 'getWorkingPath()' anywhere in scripts. local sPath = _ARGS#front; sPath = sPath.replaceString('\\', '/'); local iIndex = sPath.findLastString("/include/"); if $iIndex <= 0$ error("\"chartdir.h\" expected in a \"include/\" directory!"); sPath = sPath.leftString($iIndex + 1$); setWorkingPath(sPath); // Parses "FinanceChart.h", adding parse data the static tree node 'project'. parseAsBNF("chartDirector.cwp", project, sPath + "include/FinanceChart.h"); //-------------------------------------------------------------------------- // Generation of the CodeWorker's plugin //-------------------------------------------------------------------------- // Generate the binding between the C++ API and CodeWorker: the source code // of the plugin. generate("chartDirector.cwt", project, sPath + "plugins/CodeWorker/CHARTcw.cpp"); //-------------------------------------------------------------------------- // Translation of the C++ examples to CodeWorker //-------------------------------------------------------------------------- // The generation of the plugin must have been lauched before: the parse tree // is decorated during this phase with some information useful for translating // the C++ to CodeWorker correctly. // Load the complete directory of C++ examples recursively. traceLine("=============================================================================="); local theDirectory; if !exploreDirectory(theDirectory, sPath + "cppdemo", true) error("unable to find the directory"); // Iterate on all C++ examples, for creating the CodeWorker examples and // testing them. local badFiles; // will contain CW files, which don't compile under CodeWorker local badExecs; // will contain those, which throw an error during the execution local iTotalFiles = 0; // number of examples found foreach i in theDirectory.directories { // extract the name of the C++ example local sExample = getShortFilename(i.rsubString(1)); // compose the directory name of the corresponding CodeWorker example local sCWDemo = sPath + "cwdemo/" + sExample + '/'; local bCppExample = false; // true if it is really an example directory foreach j in i.files { if j.endString(".cpp") { // a C++ file found: it is an example directory bCppExample = true; } else if !j.endString(".dsp") { // copy all image files necessary for the correct // execution of the example copyFile(i + j, sCWDemo + j); } } if bCppExample { // this directory was containing a C++ example traceLine("- example '" + sExample + "':"); local sError; try { increment(iTotalFiles); // Translate the C++ example to a CodeWorker script translate("ChartDirectorSample.cwp", project, i + sExample + ".cpp", sCWDemo + sExample + ".cws"); try { local dontCare; // execute the CodeWorker example into its directory directly changeDirectory(sCWDemo); executeString(dontCare, loadFile(sCWDemo + sExample + ".cws")); } catch(sError) { // error during the execution of the script: // we store the file name and the error encountered insert badExecs[sExample] = sError; } } catch(sError) { // error during the translation to CodeWorker: // we store the file name and the error encountered insert badFiles[sExample] = sError; } } } if !badFiles.empty() { // Some C++ examples were unable to translate: // we display their name and their error message traceLine("**** bad files = " + badFiles.size() + '/' + iTotalFiles + " (" + floor($100 * badFiles.size() / iTotalFiles$) + "%):"); foreach i in badFiles { traceLine('\t' + i.key()); traceLine(i); } } if !badExecs.empty() { // Some C++ examples were unable to execute correctly: // we display their name and their error message traceLine("**** bad executions = " + badExecs.size() + '/' + iTotalFiles + " (" + floor($100 * badExecs.size() / iTotalFiles$) + "%):"); foreach i in badExecs { traceLine('\t' + i.key()); traceLine(i); } } //-------------------------------------------------------------------------- // Generation of an HTML file for manual regression tests, // waiting for a license key before automating it. //-------------------------------------------------------------------------- // Generation of an HTML file, which shows all charts, the C++ one on the // left-hand side and the CodeWorker one on the right-hand side, for a manual // comparison of the results. generate({
@j@ |