Improve enum assertions generation.
[qtgstreamer:qtgstreamer.git] / codegen / generator.cpp
1 /*
2     Copyright (C) 2010  George Kiagiadakis <kiagiadakis.george@gmail.com>
3
4     This library is free software; you can redistribute it and/or modify
5     it under the terms of the GNU Lesser General Public License as published
6     by the Free Software Foundation; either version 2.1 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU Lesser General Public License
15     along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 */
17 #include "generator.h"
18 #include <cstdio>
19 #include <cstdlib>
20 #include <QtCore/QCoreApplication>
21 #include <QtCore/QFile>
22
23 extern int yylineno;
24 int yyparse(CodeGen *codegen);
25 void yyrestart(FILE *file);
26
27 int main(int argc, char *argv[])
28 {
29     QCoreApplication a(argc, argv);
30     QTextStream(stdout) << "// Autogenerated by the QtGstreamer helper code generator" << endl
31                         << "#include <gst/gst.h>" << endl
32                         << "#include <boost/static_assert.hpp>" << endl << endl;
33
34     for (int i=1; i<argc; ++i) {
35         QString fileName(QFile::decodeName(argv[i]));
36         if (fileName.endsWith(".h") && QFile::exists(fileName)) {
37             CodeGen::parse(fileName);
38         } else {
39             QTextStream(stderr) << "Skipping " << fileName << ": Not an existing header" << endl;
40         }
41     }
42
43     return 0;
44 }
45
46 void CodeGen::parse(const QString & fileName)
47 {
48     CodeGen codegen(fileName);
49
50     FILE *fp = std::fopen(QFile::encodeName(fileName), "r");
51     if (!fp) {
52         std::perror("fopen");
53         QTextStream(stderr) << "Could not open " << fileName << endl;
54         return;
55     }
56
57     yylineno = 1;
58     yyrestart(fp);
59     yyparse(&codegen);
60     std::fclose(fp);
61
62     codegen.generateOutput();
63 }
64
65 void CodeGen::generateOutput()
66 {
67     QTextStream outStream(stdout);
68     outStream << "#include \"" << m_fileName << "\"" << endl << endl;
69
70     foreach(const TypeRegistration & typeReg, m_typeRegistrations) {
71         printTypeRegistration(outStream, typeReg);
72         outStream << endl;
73     }
74
75     foreach(const Enum & enumDef, m_enums) {
76         if (!enumDef.options.contains("skip")) {
77             printEnumAssertions(outStream, enumDef);
78         }
79         outStream << endl;
80     }
81 }
82
83 void CodeGen::printTypeRegistration(QTextStream & outStream, const TypeRegistration & typeReg)
84 {
85     outStream << "QGLIB_REGISTER_TYPE_IMPLEMENTATION(";
86
87     outStream << typeReg["namespace"] << "::" << typeReg["class"];
88     if (!typeReg["enum"].isEmpty()) {
89         outStream << "::" << typeReg["enum"];
90     }
91
92     outStream << ',';
93
94     if (typeReg.contains("GType")) {
95         outStream << typeReg["GType"];
96     } else {
97         outStream << (typeReg["namespace"] == "QGst" ? "GST_TYPE_" : "G_TYPE_");
98         outStream << toGstStyle(typeReg["enum"].isEmpty() ? typeReg["class"] : typeReg["enum"]);
99     }
100     outStream << ")" << endl;
101 }
102
103 void CodeGen::printEnumAssertions(QTextStream& outStream, const Enum & enumDef)
104 {
105     outStream << "namespace " << enumDef.options["namespace"] << " {" << endl;
106
107     foreach(const QByteArray & value, enumDef.values) {
108         outStream << "    BOOST_STATIC_ASSERT(static_cast<int>(";
109         if (enumDef.options.contains("class") && !enumDef.options["class"].isEmpty()) {
110             outStream << enumDef.options["class"] << "::";
111         }
112         outStream << value;
113         outStream << ") == static_cast<int>(";
114
115         if (enumDef.options.contains("prefix")) {
116             outStream << enumDef.options["prefix"];
117         } else {
118             outStream << (enumDef.options["namespace"] == "QGst" ? "GST_" : "G_");
119             if (enumDef.options.contains("class") && !enumDef.options["class"].isEmpty()) {
120                 outStream << toGstStyle(enumDef.options["class"]) << "_";
121             }
122         }
123
124         if (enumDef.options.contains(value)) {
125             outStream << enumDef.options[value];
126         } else {
127             outStream << toGstStyle(value);
128         }
129         outStream << "));" << endl;
130     }
131     outStream << "}" << endl;
132 }
133
134 QByteArray CodeGen::toGstStyle(const QByteArray & str)
135 {
136     QByteArray output;
137     foreach(const char currentChar, str) {
138         if (isupper(currentChar)) {
139             if (!output.isEmpty()) { //if this is not the first char
140                 output.append('_');
141             }
142             output.append(currentChar);
143         } else {
144             output.append(toupper(currentChar));
145         }
146     }
147     return output;
148 }
149
150
151 void CodeGen::addEnum(const QList<QByteArray> & values, const QHash<QByteArray, QByteArray> & options)
152 {
153     Enum e;
154     e.values = values;
155     e.options = options;
156     e.options["namespace"] = m_currentNamespace;
157     e.options["class"] = m_currentClass;
158     m_enums.append(e);
159 }
160
161 void CodeGen::addTypeRegistration(const QByteArray& namespaceId, const QByteArray& classId,
162                                  const QByteArray& enumId, const QHash<QByteArray, QByteArray>& options)
163 {
164     TypeRegistration typeReg(options);
165     typeReg["namespace"] = namespaceId;
166     typeReg["class"] = classId;
167     typeReg["enum"] = enumId;
168     m_typeRegistrations.append(typeReg);
169 }
170
171 //called by yyerror()
172 void CodeGen::fatalError(const char* msg)
173 {
174     QTextStream(stderr) << "codegen: " << m_fileName << ":" << yylineno << ": error: " << msg << endl;
175     std::exit(1);
176 }
177