[Spice-commits] configure.ac generator/attribute.h generator/generator.cpp generator/generator.h generator/main.cpp generator/Makefile.am generator/method.h generator/options.cpp generator/options.h generator/parser.cpp generator/parser.h generator/README generator/redirecthelper.cpp generator/redirecthelper.h generator/scanner.cpp generator/scanner.h generator/token.h .gitignore Makefile.am
Peter Hatina
phatina at kemper.freedesktop.org
Mon Jul 2 01:01:20 PDT 2012
.gitignore | 1
Makefile.am | 3
configure.ac | 22 +-
generator/Makefile.am | 8
generator/README | 28 ++
generator/attribute.h | 59 +++++
generator/generator.cpp | 281 ++++++++++++++++++++++++++++
generator/generator.h | 59 +++++
generator/main.cpp | 47 ++++
generator/method.h | 100 ++++++++++
generator/options.cpp | 68 ++++++
generator/options.h | 42 ++++
generator/parser.cpp | 430 +++++++++++++++++++++++++++++++++++++++++++
generator/parser.h | 56 +++++
generator/redirecthelper.cpp | 77 +++++++
generator/redirecthelper.h | 44 ++++
generator/scanner.cpp | 233 +++++++++++++++++++++++
generator/scanner.h | 50 +++++
generator/token.h | 77 +++++++
19 files changed, 1679 insertions(+), 6 deletions(-)
New commits:
commit fb109b6a95215d5cf855022be5d601ba07fd01a5
Author: Peter Hatina <phatina at redhat.com>
Date: Thu Jun 28 12:29:21 2012 +0200
introduce test page generator
diff --git a/.gitignore b/.gitignore
index 3fe6f83..3cc3a48 100644
--- a/.gitignore
+++ b/.gitignore
@@ -23,6 +23,7 @@ Makefile.in
nsISpicec.h
nsISpicec.xpt
SpiceXPI.xpi
+spice_xpi_generator
stamp-h1
*.o
*.lo
diff --git a/Makefile.am b/Makefile.am
index 6172c6e..db8e4ea 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,6 +1,9 @@
ACLOCAL_AMFLAGS = -I m4
SUBDIRS = SpiceXPI data
+if BUILD_GENERATOR
+SUBDIRS += generator
+endif
DIST_SUBDIRS = spice-protocol $(SUBDIRS)
EXTRA_DIST = m4
diff --git a/configure.ac b/configure.ac
index 4614083..9afb1f4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -88,6 +88,15 @@ fi
m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])])
+AC_ARG_ENABLE([generator],
+ [AS_HELP_STRING([--enable-generator],
+ [Enable compilation of a test page generator])],
+ [enable_generator="${enableval}"], [enable_generator="no"])
+AM_CONDITIONAL(BUILD_GENERATOR, test "x${enable_generator}" = "xyes")
+if test "x${enable_generator}" = "xyes"; then
+ AC_OUTPUT([generator/Makefile])
+fi
+
AC_OUTPUT([
Makefile
data/Makefile
@@ -102,13 +111,14 @@ AC_MSG_NOTICE([
Spice-XPI $VERSION
==============
- prefix: ${prefix}
- compiler: ${CC}
- xpidl: ${XPIDL}
- XUL includes: ${XUL_INCLUDEDIR}
- XUL IDL files: ${XUL_IDLDIR}
+ prefix: ${prefix}
+ compiler: ${CC}
+ xpidl: ${XPIDL}
+ XUL includes: ${XUL_INCLUDEDIR}
+ XUL IDL files: ${XUL_IDLDIR}
+ Build test page generator: ${enable_generator}
- Red target: ${red_target}
+ Red target: ${red_target}
Now type 'make' to build $PACKAGE
])
diff --git a/generator/Makefile.am b/generator/Makefile.am
new file mode 100644
index 0000000..b6a01f2
--- /dev/null
+++ b/generator/Makefile.am
@@ -0,0 +1,8 @@
+bin_PROGRAMS = spice_xpi_generator
+spice_xpi_generator_SOURCES = \
+ generator.cpp \
+ main.cpp \
+ options.cpp \
+ parser.cpp \
+ redirecthelper.cpp \
+ scanner.cpp
diff --git a/generator/README b/generator/README
new file mode 100644
index 0000000..bcc184e
--- /dev/null
+++ b/generator/README
@@ -0,0 +1,28 @@
+Spice-xpi test page generator
+=============================
+
+The main purpose of the generator is to automatically create
+a html page containing input elements and action buttons, that
+are read from interface description.
+
+Compilation
+===========
+
+To compile the generator, you have to enable it when configuring
+the while project (spice-xpi):
+
+./configure --enable-generator
+
+Usage
+=====
+
+The generator reads IDL from stdin or input file (supports the
+restricted part of an IDL grammar) and outputs the html content
+to stdout or to the file, if specified.
+
+The application supports these options:
+ -i, --input input filename (stdin used, if not specified)
+ -o, --output output filename (stdout used, if not specified)
+
+Example of the usage:
+ ./spice_xpi_generator -i nsISpicec.idl -o test-page.html
diff --git a/generator/attribute.h b/generator/attribute.h
new file mode 100644
index 0000000..04dfb98
--- /dev/null
+++ b/generator/attribute.h
@@ -0,0 +1,59 @@
+/* ***** BEGIN LICENSE BLOCK *****
+* Copyright (C) 2012, Peter Hatina <phatina at redhat.com>
+*
+* 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 of
+* the License, 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 this program. If not, see <http://www.gnu.org/licenses/>.
+* ***** END LICENSE BLOCK ***** */
+
+#ifndef ATTRIBUTE_H
+#define ATTRIBUTE_H
+
+#include <string>
+#include "token.h"
+
+class Attribute
+{
+public:
+ Attribute(Token::TokenType type, const std::string &identifier, bool readonly = false):
+ m_type(type),
+ m_identifier(identifier),
+ m_readonly(readonly)
+ {}
+
+ Attribute(const Attribute ©):
+ m_type(copy.m_type),
+ m_identifier(copy.m_identifier),
+ m_readonly(copy.m_readonly)
+ {}
+
+ ~Attribute()
+ {}
+
+ Token::TokenType getType() const { return m_type; }
+ std::string getIdentifier() const { return m_identifier; }
+
+ Attribute &operator=(const Attribute &rhs)
+ {
+ m_type = rhs.m_type;
+ m_identifier = rhs.m_identifier;
+ m_readonly = rhs.m_readonly;
+ return *this;
+ }
+
+private:
+ Token::TokenType m_type;
+ std::string m_identifier;
+ bool m_readonly;
+};
+
+#endif // ATTRIBUTE_H
diff --git a/generator/generator.cpp b/generator/generator.cpp
new file mode 100644
index 0000000..fce228c
--- /dev/null
+++ b/generator/generator.cpp
@@ -0,0 +1,281 @@
+/* ***** BEGIN LICENSE BLOCK *****
+* Copyright (C) 2012, Peter Hatina <phatina at redhat.com>
+*
+* 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 of
+* the License, 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 this program. If not, see <http://www.gnu.org/licenses/>.
+* ***** END LICENSE BLOCK ***** */
+
+#include <algorithm>
+#include <iostream>
+#include <sstream>
+#include <cctype>
+#include "generator.h"
+
+std::set<std::string> Generator::s_default_attributes;
+std::set<std::string> Generator::s_default_methods;
+std::map<std::string, std::string> Generator::s_default_attribute_values;
+
+Generator::Generator(const std::list<Attribute> &attributes,
+ const std::list<Method> &methods):
+ m_attributes(attributes),
+ m_methods(methods)
+{
+ init();
+}
+
+Generator::~Generator()
+{
+}
+
+void Generator::init()
+{
+ if (!s_default_attributes.empty())
+ return;
+
+ s_default_attributes.insert("hostip");
+ s_default_attributes.insert("port");
+ s_default_attributes.insert("adminconsole");
+ s_default_attributes.insert("hotkey");
+ s_default_attributes.insert("smartcard");
+ s_default_methods.insert("setlanguagestringssection");
+ s_default_methods.insert("setlanguagestringslang");
+ s_default_methods.insert("setusbfilterfilter");
+ s_default_attribute_values["adminconsole"] = " checked";
+ s_default_attribute_values["hotkey"] = " value=\"toggle-fullscreen=shift+f11," \
+ "release-cursor=shift+f12,smartcard-insert=shift+f8,smartcard-remove=shift+f9\"";
+ s_default_attribute_values["usblistenport"] = " value=\"32023\"";
+}
+
+void Generator::generate()
+{
+ generateHeader();
+ generateConnectVars();
+ generateContent();
+ generateFooter();
+}
+
+void Generator::generateHeader()
+{
+ std::cout << "<html>\n"
+ << "<head>\n"
+ << "<title>Spice-XPI test page (generated)</title>\n"
+ << "<style type=\"text/css\">\n"
+ << "caption {\n"
+ << " text-align: left;\n"
+ << " font-weight: bold;\n"
+ << "}\n\n"
+ << "th {\n"
+ << " text-align: left;\n"
+ << "}\n"
+ << "</style>\n"
+ << "</head>\n\n"
+ << "<body onload=\"bodyLoad()\" onunload=\"bodyUnload()\">\n\n"
+ << "<center>\n"
+ << "<h1>SPICE xpi test page (generated)</h1>\n"
+ << "SPICE xpi test page. Disabled (greyed out) values are passed\n"
+ << "to SPICE xpi as empty variables.\n</center>\n<br/>\n\n"
+ << "<embed type=\"application/x-spice\" width=\"0\" height=\"0\" id=\"spice-xpi\"/><br/>\n\n"
+ << "<script type=\"text/javascript\">\n\n"
+ << "var embed = document.getElementById(\"spice-xpi\");\n\n"
+ << "function bodyLoad()\n{\n log(\"Body Load\");\n};\n\n"
+ << "function bodyUnload()\n{\n log(\"Body Unload\");\n}\n\n"
+ << "function connect()\n{\n"
+ << " setConnectVars();\n"
+ << " setUsbFilter();\n"
+ << " embed.connect();\n"
+ << " log(\"Connect: host '\" + embed.hostIP + \"', port '\" + "
+ << "embed.port\n + \"', secure port '\" + embed.SecurePort + "
+ << "\"', USB port '\" +\n embed.UsbListenPort + \"'\");\n}\n\n"
+ << "function disconnect()\n{\n"
+ << " embed.disconnect();\n"
+ << " log(\"Disconnect\");\n}\n\n"
+ << "function OnDisconnected(msg)\n{\n log(\"Disconnected, return code: \" + msg);\n}\n\n"
+ << "function log(message)\n{\n"
+ << " var log = document.getElementById(\"log\");\n"
+ << " var ts = new Date().toString() + \": \";\n"
+ << " var newRow = document.createElement(\"tr\");\n"
+ << " var tsCell = document.createElement(\"td\");\n"
+ << " var msgCell = document.createElement(\"td\");\n\n"
+ << " tsCell.innerHTML = ts;\n"
+ << " msgCell.innerHTML = message;\n\n"
+ << " newRow.appendChild(tsCell);\n"
+ << " newRow.appendChild(msgCell);\n"
+ << " log.appendChild(newRow);\n}\n\n"
+ << "function setLanguageStrings()\n{\n"
+ << " section = document.getElementById(\"SetLanguageStringssectionToggled\").checked ?\n"
+ << " document.getElementById(\"SetLanguageStringssection\").value : \"\";\n"
+ << " lang = document.getElementById(\"SetLanguageStringslangToggled\").checked ?\n"
+ << " document.getElementById(\"SetLanguageStringslang\").value : \"\";\n"
+ << " embed.SetLanguageStrings(section, lang);\n"
+ << " log(\"Language Strings set to '\" + section + \"' '\" + lang + \"'\");\n}\n\n"
+ << "function setUsbFilter()\n{\n"
+ << " UsbFilterToggled = document.getElementById(\"SetUsbFilterfilterToggled\");\n"
+ << " if (!UsbFilterToggled)\n return;\n"
+ << " filter = UsbFilterToggled.checked ?\n"
+ << " document.getElementById(\"SetUsbFilterfilter\").value : \"\";\n"
+ << " embed.SetUsbFilter(filter);\n"
+ << " log(\"USB Filter String set to: '\" + filter + \"'\");\n}\n\n"
+ << "function show()\n{\n"
+ << " embed.show();\n"
+ << " log(\"Show\");\n}\n\n"
+ << "function ConnectedStatus()\n{\n"
+ << " log(\"Connected status = \" + embed.ConnectedStatus());\n}\n\n"
+ << "function toggle(checkboxID)\n{\n"
+ << " var checkbox = document.getElementById(checkboxID);\n"
+ << " var toggle = document.getElementById(arguments[1]);\n"
+ << " toggle.disabled = !checkbox.checked;\n}\n\n";
+}
+
+void Generator::generateFooter()
+{
+ std::cout << "<hr/>\n<table style=\"border: 1px; border-color: black;\">\n"
+ << "<caption>log:</caption>\n"
+ << "<thead><tr><th style=\"width: 22em;\">timestamp</th>"
+ << "<th>message</th></tr></thead>\n"
+ << "<tbody style=\"font-family: monospace;\" id=\"log\">\n"
+ << "</tbody>\n"
+ << "</table>\n"
+ << "</body>\n"
+ << "</html>\n";
+}
+
+void Generator::generateConnectVars()
+{
+ std::cout << "function setConnectVars()\n{\n";
+ std::list<Attribute>::iterator it;
+ for (it = m_attributes.begin(); it != m_attributes.end(); ++it) {
+ std::cout << " embed." << it->getIdentifier() << " = "
+ << "document.getElementById(\""
+ << it->getIdentifier() << "Toggled\").checked ? "
+ << "document.getElementById(\""
+ << it->getIdentifier() << "\")."
+ << (it->getType() == Token::T_BOOLEAN ? "checked" : "value")
+ << " : \"\";\n";
+ }
+ std::cout << "}\n\n</script>\n\n";
+}
+
+void Generator::generateContent()
+{
+ std::cout << "<center>\n\n"
+ << "<table id=\"values\">\n";
+
+ std::list<Attribute>::iterator ita;
+ for (ita = m_attributes.begin(); ita != m_attributes.end(); ++ita) {
+ std::cout << "<tr>\n<td><input type=\"checkbox\" id=\""
+ << ita->getIdentifier() << "Toggled"
+ << "\" onclick=\"toggle('"
+ << ita->getIdentifier() << "Toggled"
+ <<"', '" << ita->getIdentifier()
+ << "')\" " << (attributeEnabled(*ita) ? "checked" : "")
+ << "/></td>\n"
+ << "<td>" << splitIdentifier(ita->getIdentifier()) << "</td>\n"
+ << "<td>" << attributeToHtmlElement(*ita)
+ << "</td>\n</tr>\n";
+ }
+
+ std::list<Method>::iterator itm;
+ for (itm = m_methods.begin(); itm != m_methods.end(); ++itm) {
+ std::list<Method::MethodParam>::iterator itp;
+ std::list<Method::MethodParam> params = itm->getParams();
+ for (itp = params.begin(); itp != params.end(); ++itp) {
+ std::cout << "<tr>\n<td><input type=\"checkbox\" id=\""
+ << itm->getIdentifier() << itp->getIdentifier()
+ << "Toggled\" onclick=\"toggle('"
+ << itm->getIdentifier() << itp->getIdentifier()
+ << "Toggled', '" << itm->getIdentifier()
+ << itp->getIdentifier() << "')\""
+ << (methodEnabled(*itm, *itp) ? "checked" : "")
+ << "/></td>\n<td>" << splitIdentifier(itm->getIdentifier())
+ << " - " << splitIdentifier(itp->getIdentifier()) << "</td>\n"
+ << "<td><input id=\"" << itm->getIdentifier() << itp->getIdentifier()
+ << "\" type=\"" << (itp->getType() == Token::T_BOOLEAN ? "checkbox" : "text")
+ << "\" size=\"30\" " << (methodEnabled(*itm, *itp) ? "" : "disabled ")
+ << "/></td>\n</tr>\n";
+ }
+ }
+
+ std::cout << "</table>\n\n<br/>\n";
+
+ int i = 1;
+ for (itm = m_methods.begin(); itm != m_methods.end(); ++itm, ++i) {
+ std::cout << "<input type=\"button\" value=\""
+ << splitIdentifier(itm->getIdentifier())
+ << "\" style=\"min-width: 180px\" onclick=\""
+ << itm->getIdentifier()
+ << "()\"/>\n";
+ if (i % 3 == 0 && i != static_cast<int>(m_methods.size()))
+ std::cout << "<br/>\n";
+ }
+
+ std::cout << "\n</center>\n\n";
+}
+
+std::string Generator::lowerString(const std::string &str)
+{
+ std::string s(str);
+ std::transform(s.begin(), s.end(), s.begin(), tolower);
+ return s;
+}
+
+std::string Generator::splitIdentifier(const std::string &str)
+{
+ std::string result(str);
+ result[0] = toupper(result[0]);
+ for (size_t i = 1; i < result.size() - 1; ++i) {
+ if (isupper(result[i]) && islower(result[i + 1]))
+ result.insert(i++, " ");
+ else if (isupper(result[i]) && islower(result[i - 1]))
+ result.insert(i++, " ");
+ }
+ return result;
+}
+
+std::string Generator::attributeToHtmlElement(const Attribute &attr)
+{
+ std::stringstream ss;
+ std::string id = lowerString(attr.getIdentifier());
+ if (id == "truststore") {
+ ss << "<textarea id=\"" << attr.getIdentifier()
+ << "\" cols=\"66\" rows=\"33\" "
+ << (attributeEnabled(attr) ? "" : "disabled")
+ << "/></textarea>";
+ } else {
+ ss << "<input id=\"" << attr.getIdentifier() << "\" type=\""
+ << (attr.getType() == Token::T_BOOLEAN ? "checkbox" : "text")
+ <<"\" size=\"30\" " << attributeDefaultValue(attr)
+ << (attributeEnabled(attr) ? "" : "disabled ") << "/>";
+ }
+ return ss.str();
+}
+
+std::string Generator::attributeDefaultValue(const Attribute &attr)
+{
+ std::string id(lowerString(attr.getIdentifier()));
+ std::map<std::string, std::string>::iterator found = s_default_attribute_values.find(id);
+ return found != s_default_attribute_values.end() ? found->second : "";
+}
+
+bool Generator::attributeEnabled(const Attribute &attr)
+{
+ std::string id(lowerString(attr.getIdentifier()));
+ std::set<std::string>::iterator found = s_default_attributes.find(id);
+ return found != s_default_attributes.end();
+}
+
+bool Generator::methodEnabled(const Method &method, const Method::MethodParam ¶m)
+{
+ std::string id(lowerString(method.getIdentifier() + param.getIdentifier()));
+ std::set<std::string>::iterator found = s_default_methods.find(id);
+ return found != s_default_methods.end();
+}
diff --git a/generator/generator.h b/generator/generator.h
new file mode 100644
index 0000000..0a9bd3f
--- /dev/null
+++ b/generator/generator.h
@@ -0,0 +1,59 @@
+/* ***** BEGIN LICENSE BLOCK *****
+* Copyright (C) 2012, Peter Hatina <phatina at redhat.com>
+*
+* 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 of
+* the License, 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 this program. If not, see <http://www.gnu.org/licenses/>.
+* ***** END LICENSE BLOCK ***** */
+
+#ifndef GENERATOR_H
+#define GENERATOR_H
+
+#include <list>
+#include <map>
+#include <set>
+#include "attribute.h"
+#include "method.h"
+#include "token.h"
+
+class Generator
+{
+public:
+ Generator(const std::list<Attribute> &attributes,
+ const std::list<Method> &methods);
+ ~Generator();
+
+ void generate();
+
+private:
+ void init();
+ void generateHeader();
+ void generateFooter();
+ void generateConnectVars();
+ void generateContent();
+
+ static std::string lowerString(const std::string &str);
+ static std::string splitIdentifier(const std::string &str);
+ static std::string attributeDefaultValue(const Attribute &attr);
+ static std::string attributeToHtmlElement(const Attribute &attr);
+ static bool attributeEnabled(const Attribute &attr);
+ static bool methodEnabled(const Method &method, const Method::MethodParam ¶m);
+
+private:
+ std::list<Attribute> m_attributes;
+ std::list<Method> m_methods;
+ static std::set<std::string> s_default_attributes;
+ static std::set<std::string> s_default_methods;
+ static std::map<std::string, std::string> s_default_attribute_values;
+};
+
+#endif // GENERATOR_H
diff --git a/generator/main.cpp b/generator/main.cpp
new file mode 100644
index 0000000..61f8db2
--- /dev/null
+++ b/generator/main.cpp
@@ -0,0 +1,47 @@
+/* ***** BEGIN LICENSE BLOCK *****
+* Copyright (C) 2012, Peter Hatina <phatina at redhat.com>
+*
+* 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 of
+* the License, 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 this program. If not, see <http://www.gnu.org/licenses/>.
+* ***** END LICENSE BLOCK ***** */
+
+#include "generator.h"
+#include "options.h"
+#include "parser.h"
+#include "redirecthelper.h"
+
+int main(int argc, char **argv)
+{
+ Options o(argc, argv);
+ if (!o.good())
+ return 1;
+
+ if (o.help()) {
+ o.printHelp();
+ return 0;
+ }
+
+ RedirectHelper rh(o);
+ if (!rh.redirect())
+ return 1;
+
+ Parser p;
+ if (!p.parse())
+ return 1;
+
+ Generator g(p.getAttributes(), p.getMethods());
+ g.generate();
+
+ rh.restore();
+ return 0;
+}
diff --git a/generator/method.h b/generator/method.h
new file mode 100644
index 0000000..4b63461
--- /dev/null
+++ b/generator/method.h
@@ -0,0 +1,100 @@
+/* ***** BEGIN LICENSE BLOCK *****
+* Copyright (C) 2012, Peter Hatina <phatina at redhat.com>
+*
+* 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 of
+* the License, 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 this program. If not, see <http://www.gnu.org/licenses/>.
+* ***** END LICENSE BLOCK ***** */
+
+#ifndef METHOD_H
+#define METHOD_H
+
+#include <string>
+#include <list>
+#include "token.h"
+
+class Method
+{
+public:
+ class MethodParam;
+
+public:
+ Method(Token::TokenType type, std::string &identifier, const std::list<MethodParam> ¶ms):
+ m_type(type),
+ m_identifier(identifier),
+ m_params(params)
+ {}
+
+ Method(const Method ©):
+ m_type(copy.m_type),
+ m_identifier(copy.m_identifier),
+ m_params(copy.m_params)
+ {}
+
+ ~Method()
+ {}
+
+ Token::TokenType getType() const { return m_type; }
+ std::string getIdentifier() const { return m_identifier; }
+ std::list<MethodParam> getParams() const { return m_params; }
+
+ Method &operator=(const Method &rhs)
+ {
+ m_type = rhs.m_type;
+ m_identifier = rhs.m_identifier;
+ m_params = rhs.m_params;
+ return *this;
+ }
+
+private:
+ Token::TokenType m_type;
+ std::string m_identifier;
+ std::list<MethodParam> m_params;
+};
+
+class Method::MethodParam
+{
+public:
+ MethodParam(Token::TokenType dir, Token::TokenType type, const std::string &identifier):
+ m_dir(dir),
+ m_type(type),
+ m_identifier(identifier)
+ {}
+
+ MethodParam(const MethodParam ©):
+ m_dir(copy.m_dir),
+ m_type(copy.m_type),
+ m_identifier(copy.m_identifier)
+ {}
+
+ ~MethodParam()
+ {}
+
+ Token::TokenType getType() const { return m_type; }
+ Token::TokenType getDir() const { return m_dir; }
+ std::string getIdentifier() const { return m_identifier; }
+
+ MethodParam &operator=(const MethodParam &rhs)
+ {
+ m_dir = rhs.m_dir;
+ m_type = rhs.m_type;
+ m_identifier = rhs.m_identifier;
+ return *this;
+ }
+
+private:
+ Token::TokenType m_dir;
+ Token::TokenType m_type;
+ std::string m_identifier;
+};
+
+#endif // METHOD_H
diff --git a/generator/options.cpp b/generator/options.cpp
new file mode 100644
index 0000000..86ea867
--- /dev/null
+++ b/generator/options.cpp
@@ -0,0 +1,68 @@
+/* ***** BEGIN LICENSE BLOCK *****
+* Copyright (C) 2012, Peter Hatina <phatina at redhat.com>
+*
+* 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 of
+* the License, 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 this program. If not, see <http://www.gnu.org/licenses/>.
+* ***** END LICENSE BLOCK ***** */
+
+#include <iostream>
+extern "C" {
+# include <getopt.h>
+}
+#include "options.h"
+
+Options::Options(int argc, char **argv):
+ m_help(false),
+ m_good(true),
+ m_input_filename(),
+ m_output_filename()
+{
+ static struct option longopts[] = {
+ { "input", required_argument, NULL, 'i' },
+ { "output", required_argument, NULL, 'o' },
+ { "help", required_argument, NULL, 'h' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ int c;
+ while ((c = getopt_long(argc, argv, "i:o:h", longopts, NULL)) != -1) {
+ switch (c) {
+ case 'i':
+ m_input_filename = optarg;
+ break;
+ case 'o':
+ m_output_filename = optarg;
+ break;
+ case 'h':
+ m_help = true;
+ break;
+ default:
+ m_good = false;
+ break;
+ }
+ }
+}
+
+Options::~Options()
+{
+}
+
+void Options::printHelp() const
+{
+ std::cout << "Spice-xpi test page generator\n\n"
+ << "Usage: ./generator [-h] [-i input] [-o output]\n\n"
+ << "Application options:\n"
+ << " -i, --input input filename (stdin used, if not specified)\n"
+ << " -o, --output output filename (stdout used, if not specified)\n"
+ << " -h, --help prints this help\n";
+}
diff --git a/generator/options.h b/generator/options.h
new file mode 100644
index 0000000..7c990e5
--- /dev/null
+++ b/generator/options.h
@@ -0,0 +1,42 @@
+/* ***** BEGIN LICENSE BLOCK *****
+* Copyright (C) 2012, Peter Hatina <phatina at redhat.com>
+*
+* 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 of
+* the License, 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 this program. If not, see <http://www.gnu.org/licenses/>.
+* ***** END LICENSE BLOCK ***** */
+
+#ifndef OPTIONS_H
+#define OPTIONS_H
+
+#include <string>
+
+class Options
+{
+public:
+ Options(int argc, char **argv);
+ ~Options();
+
+ bool help() const { return m_help; }
+ bool good() const { return m_good; }
+ void printHelp() const;
+ std::string inputFilename() const { return m_input_filename; }
+ std::string outputFilename() const { return m_output_filename; }
+
+private:
+ bool m_help;
+ bool m_good;
+ std::string m_input_filename;
+ std::string m_output_filename;
+};
+
+#endif // OPTIONS_H
diff --git a/generator/parser.cpp b/generator/parser.cpp
new file mode 100644
index 0000000..637a2cb
--- /dev/null
+++ b/generator/parser.cpp
@@ -0,0 +1,430 @@
+/* ***** BEGIN LICENSE BLOCK *****
+* Copyright (C) 2012, Peter Hatina <phatina at redhat.com>
+*
+* 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 of
+* the License, 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 this program. If not, see <http://www.gnu.org/licenses/>.
+* ***** END LICENSE BLOCK ***** */
+
+#include <iostream>
+#include <list>
+#include "parser.h"
+
+Parser::Parser():
+ m_scanner(),
+ m_token(),
+ m_attributes(),
+ m_methods()
+{
+}
+
+Parser::~Parser()
+{
+}
+
+bool Parser::parse()
+{
+ m_token = m_scanner.getNextToken();
+ while (1) {
+ if (m_token == Token::T_INTERFACE || m_token == Token::T_OPEN_BRACKET) {
+ if (!parseDefinition())
+ return false;
+ } else if (m_token == Token::T_HASH) {
+ if (!parseInclude())
+ return false;
+ } else if (m_token == Token::T_EOF) {
+ return true;
+ } else {
+ handleError();
+ return false;
+ }
+ }
+}
+
+void Parser::handleError() const
+{
+ std::cerr << (m_token == Token::T_LEX_ERROR ?
+ "Lexical error near line: " : "Syntax error near line: ");
+ std::cerr << m_scanner.getLineNo() << std::endl;
+}
+
+bool Parser::parseInclude()
+{
+ if (m_token != Token::T_HASH) {
+ handleError();
+ return false;
+ }
+
+ m_token = m_scanner.getNextToken();
+ if (m_token != Token::T_INCLUDE) {
+ handleError();
+ return false;
+ }
+
+ m_token = m_scanner.getNextToken();
+ if (m_token == Token::T_QUOTE) {
+ m_token = m_scanner.getNextToken();
+ while (m_token == Token::T_IDENTIFIER || m_token == Token::T_DOT)
+ m_token = m_scanner.getNextToken();
+ if (m_token != Token::T_QUOTE) {
+ handleError();
+ return false;
+ }
+ m_token = m_scanner.getNextToken();
+ } else if (m_token == Token::T_LESS) {
+ m_token = m_scanner.getNextToken();
+ while (m_token == Token::T_IDENTIFIER || m_token == Token::T_DOT)
+ m_token = m_scanner.getNextToken();
+ if (m_token != Token::T_GREATER) {
+ handleError();
+ return false;
+ }
+ m_token = m_scanner.getNextToken();
+ } else {
+ handleError();
+ return false;
+ }
+
+ return true;
+}
+
+bool Parser::parseDefinitionParams()
+{
+ if (m_token != Token::T_OPEN_BRACKET) {
+ handleError();
+ return false;
+ }
+
+ m_token = m_scanner.getNextToken();
+ while (1) {
+ if (m_token == Token::T_UUID) {
+ m_token = m_scanner.getNextToken();
+ if (m_token != Token::T_OPEN_PARENTHESES) {
+ handleError();
+ return false;
+ }
+ m_scanner.setAcceptUuids();
+ m_token = m_scanner.getNextToken();
+ if (m_token != Token::T_UUIDVAL) {
+ handleError();
+ return false;
+ }
+ m_scanner.setAcceptUuids(false);
+ m_token = m_scanner.getNextToken();
+ if (m_token != Token::T_CLOSE_PARENTHESES) {
+ handleError();
+ return false;
+ }
+ } else if (m_token != Token::T_IDENTIFIER) {
+ handleError();
+ return false;
+ }
+
+ m_token = m_scanner.getNextToken();
+ if (m_token == Token::T_CLOSE_BRACKET) {
+ m_token = m_scanner.getNextToken();
+ return true;
+ }
+ else if (m_token != Token::T_COMMA) {
+ handleError();
+ return false;
+ }
+
+ m_token = m_scanner.getNextToken();
+ }
+}
+
+bool Parser::parseDefinition()
+{
+ if (m_token == Token::T_OPEN_BRACKET && !parseDefinitionParams())
+ return false;
+
+ if (m_token != Token::T_INTERFACE) {
+ handleError();
+ return false;
+ }
+
+ m_token = m_scanner.getNextToken();
+ if (m_token != Token::T_IDENTIFIER) {
+ handleError();
+ return false;
+ }
+
+ m_token = m_scanner.getNextToken();
+ if (m_token == Token::T_COLON) {
+ m_token = m_scanner.getNextToken();
+ if (!parseBasicInterfaces())
+ return false;
+ }
+
+ if (m_token != Token::T_OPEN_BRACE) {
+ handleError();
+ return false;
+ }
+
+ m_token = m_scanner.getNextToken();
+ if (!parseInterfaceBody())
+ return false;
+
+ if (m_token != Token::T_CLOSE_BRACE) {
+ handleError();
+ return false;
+ }
+
+ m_token = m_scanner.getNextToken();
+ if (m_token != Token::T_SEMICOLON) {
+ handleError();
+ return false;
+ }
+
+ m_token = m_scanner.getNextToken();
+ return true;
+}
+
+bool Parser::parseBasicInterfaces()
+{
+ while (1) {
+ if (m_token != Token::T_IDENTIFIER) {
+ handleError();
+ return false;
+ }
+
+ m_token = m_scanner.getNextToken();
+ if (m_token == Token::T_OPEN_BRACE)
+ return true;
+
+ if (m_token != Token::T_COMMA) {
+ handleError();
+ return false;
+ }
+
+ m_token = m_scanner.getNextToken();
+ }
+}
+
+bool Parser::parseInterfaceBody()
+{
+ while (1) {
+ switch (m_token.getType()) {
+ case Token::T_READONLY:
+ case Token::T_ATTRIBUTE:
+ if (!parseAttribute())
+ return false;
+ break;
+ case Token::T_FLOAT:
+ case Token::T_DOUBLE:
+ case Token::T_STRING:
+ case Token::T_WSTRING:
+ case Token::T_UNSIGNED:
+ case Token::T_SHORT:
+ case Token::T_LONG:
+ case Token::T_CHAR:
+ case Token::T_WCHAR:
+ case Token::T_BOOLEAN:
+ case Token::T_VOID:
+ case Token::T_OCTET:
+ if (!parseMethod())
+ return false;
+ break;
+ case Token::T_CLOSE_BRACE:
+ break;
+ default:
+ handleError();
+ return false;
+ }
+
+ if (m_token == Token::T_LEX_ERROR) {
+ handleError();
+ return false;
+ }
+
+ if (m_token == Token::T_CLOSE_BRACE)
+ return true;
+ }
+ return true;
+}
+
+bool Parser::parseAttribute()
+{
+ bool readonly = false;
+ if (m_token == Token::T_READONLY) {
+ readonly = true;
+ m_token = m_scanner.getNextToken();
+ }
+
+ if (m_token != Token::T_ATTRIBUTE) {
+ handleError();
+ return false;
+ }
+
+ m_token = m_scanner.getNextToken();
+ if (!parseType())
+ return false;
+
+ Token type = m_token;
+
+ m_token = m_scanner.getNextToken();
+ if (m_token != Token::T_IDENTIFIER) {
+ handleError();
+ return false;
+ }
+
+ m_attributes.push_back(Attribute(type.getType(), m_token.getParameter(), readonly));
+
+ m_token = m_scanner.getNextToken();
+ if (m_token == Token::T_COMMA) {
+ while (1) {
+ m_token = m_scanner.getNextToken();
+ if (m_token != Token::T_IDENTIFIER) {
+ handleError();
+ return false;
+ }
+
+ m_attributes.push_back(Attribute(type.getType(), m_token.getParameter(), readonly));
+ m_token = m_scanner.getNextToken();
+ if (m_token == Token::T_SEMICOLON) {
+ break;
+ } else if (m_token != Token::T_COMMA) {
+ handleError();
+ return false;
+ }
+ }
+ }
+ if (m_token != Token::T_SEMICOLON) {
+ handleError();
+ return false;
+ }
+
+ m_token = m_scanner.getNextToken();
+ return true;
+}
+
+bool Parser::parseMethod()
+{
+ if (!parseType())
+ return false;
+
+ Token type = m_token;
+ m_token = m_scanner.getNextToken();
+ if (m_token != Token::T_IDENTIFIER) {
+ handleError();
+ return false;
+ }
+
+ std::string identifier = m_token.getParameter();
+
+ m_token = m_scanner.getNextToken();
+ if (m_token != Token::T_OPEN_PARENTHESES) {
+ handleError();
+ return false;
+ }
+
+ std::list<Method::MethodParam> params;
+ m_token = m_scanner.getNextToken();
+ if (m_token != Token::T_CLOSE_PARENTHESES) {
+ while (1) {
+ Token dir(Token::T_UNKNOWN);
+ if (m_token == Token::T_IN ||
+ m_token == Token::T_OUT ||
+ m_token == Token::T_INOUT)
+ {
+ dir = m_token;
+ m_token = m_scanner.getNextToken();
+ }
+ if (!parseType())
+ return false;
+ Token type = m_token;
+ m_token = m_scanner.getNextToken();
+ if (m_token != Token::T_IDENTIFIER) {
+ handleError();
+ return false;
+ }
+
+ params.push_back(Method::MethodParam(dir.getType(), type.getType(),
+ m_token.getParameter()));
+
+ m_token = m_scanner.getNextToken();
+ if (m_token == Token::T_COMMA) {
+ m_token = m_scanner.getNextToken();
+ } else if (m_token == Token::T_CLOSE_PARENTHESES) {
+ break;
+ } else {
+ handleError();
+ return false;
+ }
+ }
+ }
+
+ if (m_token != Token::T_CLOSE_PARENTHESES) {
+ handleError();
+ return false;
+ }
+
+ m_token = m_scanner.getNextToken();
+ if (m_token != Token::T_SEMICOLON) {
+ handleError();
+ return false;
+ }
+
+ m_methods.push_back(Method(type.getType(), identifier, params));
+
+ m_token = m_scanner.getNextToken();
+ return true;
+}
+
+bool Parser::parseType()
+{
+ switch (m_token.getType()) {
+ case Token::T_UNSIGNED: {
+ Token new_token = m_scanner.getNextToken();
+ if (new_token == Token::T_SHORT)
+ m_token = Token(Token::T_UNSIGNED_SHORT);
+ else if (new_token == Token::T_LONG) {
+ Token newest_token = m_scanner.getNextToken();
+ if (newest_token == Token::T_LONG) {
+ m_token = Token(Token::T_UNSIGNED_LONG_LONG);
+ } else {
+ m_scanner.pushToken(newest_token);
+ m_token = Token(Token::T_UNSIGNED_LONG);
+ }
+ } else {
+ m_scanner.pushToken(new_token);
+ handleError();
+ return false;
+ }
+ break;
+ }
+ case Token::T_LONG: {
+ Token new_token = m_scanner.getNextToken();
+ if (new_token == Token::T_LONG)
+ m_token = Token(Token::T_LONG_LONG);
+ else
+ m_scanner.pushToken(new_token);
+ break;
+ }
+ case Token::T_FLOAT:
+ case Token::T_DOUBLE:
+ case Token::T_STRING:
+ case Token::T_WSTRING:
+ case Token::T_SHORT:
+ case Token::T_CHAR:
+ case Token::T_WCHAR:
+ case Token::T_BOOLEAN:
+ case Token::T_VOID:
+ case Token::T_OCTET:
+ return true;
+ default:
+ handleError();
+ return false;
+ }
+ return true;
+}
diff --git a/generator/parser.h b/generator/parser.h
new file mode 100644
index 0000000..18ebed1
--- /dev/null
+++ b/generator/parser.h
@@ -0,0 +1,56 @@
+/* ***** BEGIN LICENSE BLOCK *****
+* Copyright (C) 2012, Peter Hatina <phatina at redhat.com>
+*
+* 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 of
+* the License, 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 this program. If not, see <http://www.gnu.org/licenses/>.
+* ***** END LICENSE BLOCK ***** */
+
+#ifndef PARSER_H
+#define PARSER_H
+
+#include <list>
+#include "attribute.h"
+#include "method.h"
+#include "scanner.h"
+#include "token.h"
+
+class Parser
+{
+public:
+ Parser();
+ ~Parser();
+
+ bool parse();
+
+ std::list<Attribute> getAttributes() const { return m_attributes; }
+ std::list<Method> getMethods() const { return m_methods; }
+
+private:
+ void handleError() const;
+ bool parseInclude();
+ bool parseDefinitionParams();
+ bool parseDefinition();
+ bool parseBasicInterfaces();
+ bool parseInterfaceBody();
+ bool parseAttribute();
+ bool parseType();
+ bool parseMethod();
+
+private:
+ Scanner m_scanner;
+ Token m_token;
+ std::list<Attribute> m_attributes;
+ std::list<Method> m_methods;
+};
+
+#endif // PARSER_H
diff --git a/generator/redirecthelper.cpp b/generator/redirecthelper.cpp
new file mode 100644
index 0000000..4455216
--- /dev/null
+++ b/generator/redirecthelper.cpp
@@ -0,0 +1,77 @@
+/* ***** BEGIN LICENSE BLOCK *****
+* Copyright (C) 2012, Peter Hatina <phatina at redhat.com>
+*
+* 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 of
+* the License, 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 this program. If not, see <http://www.gnu.org/licenses/>.
+* ***** END LICENSE BLOCK ***** */
+
+#include "redirecthelper.h"
+
+RedirectHelper::RedirectHelper(const Options &o):
+ m_input_file(o.inputFilename()),
+ m_output_file(o.outputFilename()),
+ m_ifb(),
+ m_ofb(),
+ m_cin_streambuf(NULL),
+ m_cout_streambuf(NULL)
+{
+}
+
+RedirectHelper::~RedirectHelper()
+{
+ restore();
+}
+
+bool RedirectHelper::redirect()
+{
+ if (!m_cin_streambuf && !m_input_file.empty()) {
+ m_ifb.open(m_input_file.c_str(), std::ios::in);
+ if (!m_ifb.is_open()) {
+ std::cerr << "Unable to open '" << m_input_file << "' for reading!\n";
+ return false;
+ } else {
+ m_cin_streambuf = std::cin.rdbuf();
+ std::istream is(&m_ifb);
+ std::cin.rdbuf(is.rdbuf());
+ }
+ }
+
+ if (!m_cout_streambuf && !m_output_file.empty()) {
+ m_ofb.open(m_output_file.c_str(), std::ios::out);
+ if (!m_ofb.is_open()) {
+ std::cerr << "Unable to open '" << m_output_file << "' for writing!\n";
+ return false;
+ } else {
+ m_cout_streambuf = std::cout.rdbuf();
+ std::ostream os(&m_ofb);
+ std::cout.rdbuf(os.rdbuf());
+ }
+ }
+
+ return true;
+}
+
+void RedirectHelper::restore()
+{
+ if (m_cin_streambuf) {
+ m_ifb.close();
+ std::cin.rdbuf(m_cin_streambuf);
+ m_cin_streambuf = NULL;
+ }
+
+ if (m_cout_streambuf) {
+ m_ofb.close();
+ std::cout.rdbuf(m_cout_streambuf);
+ m_cout_streambuf = NULL;
+ }
+}
diff --git a/generator/redirecthelper.h b/generator/redirecthelper.h
new file mode 100644
index 0000000..786c91e
--- /dev/null
+++ b/generator/redirecthelper.h
@@ -0,0 +1,44 @@
+/* ***** BEGIN LICENSE BLOCK *****
+* Copyright (C) 2012, Peter Hatina <phatina at redhat.com>
+*
+* 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 of
+* the License, 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 this program. If not, see <http://www.gnu.org/licenses/>.
+* ***** END LICENSE BLOCK ***** */
+
+#ifndef REDIRECTHELPER_H
+#define REDIRECTHELPER_H
+
+#include <iostream>
+#include <fstream>
+#include <string>
+#include "options.h"
+
+class RedirectHelper
+{
+public:
+ RedirectHelper(const Options &o);
+ ~RedirectHelper();
+
+ bool redirect();
+ void restore();
+
+private:
+ std::string m_input_file;
+ std::string m_output_file;
+ std::filebuf m_ifb;
+ std::filebuf m_ofb;
+ std::streambuf *m_cin_streambuf;
+ std::streambuf *m_cout_streambuf;
+};
+
+#endif // REDIRECTHELPER_H
diff --git a/generator/scanner.cpp b/generator/scanner.cpp
new file mode 100644
index 0000000..f475e73
--- /dev/null
+++ b/generator/scanner.cpp
@@ -0,0 +1,233 @@
+/* ***** BEGIN LICENSE BLOCK *****
+* Copyright (C) 2012, Peter Hatina <phatina at redhat.com>
+*
+* 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 of
+* the License, 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 this program. If not, see <http://www.gnu.org/licenses/>.
+* ***** END LICENSE BLOCK ***** */
+
+#include <fstream>
+#include <cctype>
+#include "scanner.h"
+
+std::map<std::string, Token::TokenType> Scanner::s_keywords;
+
+Scanner::Scanner():
+ m_token(),
+ m_line_no_start(1),
+ m_line_no_end(m_line_no_start),
+ m_accept_uuids(false),
+ m_token_queue()
+{
+ if (s_keywords.empty())
+ initKeywords();
+}
+
+Scanner::~Scanner()
+{
+}
+
+void Scanner::initKeywords()
+{
+ s_keywords["attribute"] = Token::T_ATTRIBUTE;
+ s_keywords["const"] = Token::T_CONST;
+ s_keywords["in"] = Token::T_IN;
+ s_keywords["out"] = Token::T_OUT;
+ s_keywords["inout"] = Token::T_INOUT;
+ s_keywords["interface"] = Token::T_INTERFACE;
+ s_keywords["native"] = Token::T_NATIVE;
+ s_keywords["raises"] = Token::T_RAISES;
+ s_keywords["readonly"] = Token::T_READONLY;
+ s_keywords["typedef"] = Token::T_TYPEDEF;
+ s_keywords["uuid"] = Token::T_UUID;
+ s_keywords["float"] = Token::T_FLOAT;
+ s_keywords["double"] = Token::T_DOUBLE;
+ s_keywords["string"] = Token::T_STRING;
+ s_keywords["wstring"] = Token::T_WSTRING;
+ s_keywords["unsigned"] = Token::T_UNSIGNED;
+ s_keywords["short"] = Token::T_SHORT;
+ s_keywords["long"] = Token::T_LONG;
+ s_keywords["char"] = Token::T_CHAR;
+ s_keywords["wchar"] = Token::T_WCHAR;
+ s_keywords["boolean"] = Token::T_BOOLEAN;
+ s_keywords["void"] = Token::T_VOID;
+ s_keywords["octet"] = Token::T_OCTET;
+ s_keywords["include"] = Token::T_INCLUDE;
+}
+
+Token Scanner::getNextToken()
+{
+ if (!m_token_queue.empty()) {
+ Token t(m_token_queue.front());
+ m_token_queue.pop();
+ return t;
+ }
+
+ enum { S_INITIAL, S_IDENTIFIER,
+ S_NUMBER, S_COLON,
+ S_SHIFT_LEFT, S_SHIFT_RIGHT,
+ S_SLASH, S_BLOCK_COMMENT,
+ S_UUID } state = S_INITIAL;
+ std::string param;
+ int c = 0;
+ int old_c;
+
+ m_line_no_start = m_line_no_end;
+ while (1) {
+ old_c = c;
+ c = std::cin.get();
+
+ if (std::cin.eof())
+ return Token(state == S_INITIAL ? Token::T_EOF : Token::T_LEX_ERROR);
+
+ if (c == '\n')
+ ++m_line_no_end;
+
+ switch (state) {
+ case S_INITIAL:
+ if (isalpha(c)) {
+ param += c;
+ state = m_accept_uuids ? S_UUID : S_IDENTIFIER;
+ } else if (isdigit(c)) {
+ param += c;
+ state = m_accept_uuids ? S_UUID : S_NUMBER;
+ }
+ switch (c) {
+ case '[':
+ return Token(Token::T_OPEN_BRACKET);
+ case ']':
+ return Token(Token::T_CLOSE_BRACKET);
+ case '{':
+ return Token(Token::T_OPEN_BRACE);
+ case '}':
+ return Token(Token::T_CLOSE_BRACE);
+ case '(':
+ return Token(Token::T_OPEN_PARENTHESES);
+ case ')':
+ return Token(Token::T_CLOSE_PARENTHESES);
+ case ',':
+ return Token(Token::T_COMMA);
+ case '=':
+ return Token(Token::T_ASSIGN);
+ case ';':
+ return Token(Token::T_SEMICOLON);
+ case '|':
+ return Token(Token::T_PIPE);
+ case '^':
+ return Token(Token::T_CARET);
+ case '&':
+ return Token(Token::T_AMPERSAND);
+ case '+':
+ return Token(Token::T_PLUS);
+ case '-':
+ return Token(Token::T_MINUS);
+ case '%':
+ return Token(Token::T_PERCENT);
+ case '~':
+ return Token(Token::T_TILDE);
+ case '#':
+ return Token(Token::T_HASH);
+ case '"':
+ return Token(Token::T_QUOTE);
+ case '.':
+ return Token(Token::T_DOT);
+ case ':':
+ state = S_COLON;
+ break;
+ case '<':
+ state = S_SHIFT_LEFT;
+ break;
+ case '>':
+ state = S_SHIFT_RIGHT;
+ break;
+ case '/':
+ state = S_SLASH;
+ break;
+ case ' ':
+ case '\t':
+ break;
+ case '*':
+ return Token(Token::T_LEX_ERROR);
+ }
+ break;
+
+ case S_IDENTIFIER:
+ if (!isalnum(c) && c != '-' && c != '_') {
+ std::cin.unget();
+ std::map<std::string, Token::TokenType>::iterator it;
+ it = s_keywords.find(param);
+ if (it != s_keywords.end())
+ return Token(it->second);
+ return Token(Token::T_IDENTIFIER, param);
+ }
+ param += c;
+ break;
+
+ case S_NUMBER:
+ if (!isdigit(c)) {
+ std::cin.unget();
+ return Token(Token::T_NUMBER, param);
+ }
+ param += c;
+ break;
+
+ case S_UUID: {
+ if (!isdigit(c) && c != '-' && (c > 'f' || c < 'a')) {
+ std::cin.unget();
+ return Token(param.size() != 36 ? Token::T_LEX_ERROR : Token::T_UUIDVAL, param);
+ }
+ const int len = param.size();
+ if (c != '-' && len >= 36 &&
+ (len == 8 || len == 13 || len == 18 || len == 23))
+ {
+ return Token(Token::T_LEX_ERROR);
+ }
+ param += c;
+ break;
+ }
+
+ case S_COLON:
+ if (c == ':')
+ return Token(Token::T_DOUBLECOLON);
+ std::cin.unget();
+ return Token(Token::T_COLON);
+
+ case S_SHIFT_LEFT:
+ if (c == '<')
+ return Token(Token::T_SHIFT_LEFT);
+ std::cin.unget();
+ return Token(Token::T_LESS);
+
+ case S_SHIFT_RIGHT:
+ if (c == '>')
+ return Token(Token::T_SHIFT_RIGHT);
+ std::cin.unget();
+ return Token(Token::T_GREATER);
+
+ case S_SLASH:
+ if (c == '/') {
+ while (c != '\n' && !std::cin.eof())
+ c = std::cin.get();
+ state = S_INITIAL;
+ } else if (c == '*') {
+ state = S_BLOCK_COMMENT;
+ }
+ break;
+
+ case S_BLOCK_COMMENT:
+ if (old_c == '*' && c == '/')
+ state = S_INITIAL;
+ break;
+ }
+ }
+ return Token(Token::T_LEX_ERROR);
+}
diff --git a/generator/scanner.h b/generator/scanner.h
new file mode 100644
index 0000000..d6150a2
--- /dev/null
+++ b/generator/scanner.h
@@ -0,0 +1,50 @@
+/* ***** BEGIN LICENSE BLOCK *****
+* Copyright (C) 2012, Peter Hatina <phatina at redhat.com>
+*
+* 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 of
+* the License, 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 this program. If not, see <http://www.gnu.org/licenses/>.
+* ***** END LICENSE BLOCK ***** */
+
+#ifndef SCANNER_H
+#define SCANNER_H
+
+#include <iostream>
+#include <string>
+#include <map>
+#include <queue>
+#include "token.h"
+
+class Scanner
+{
+public:
+ Scanner();
+ ~Scanner();
+
+ Token getNextToken();
+ int getLineNo() const { return m_line_no_start; }
+ void pushToken(Token &token) { m_token_queue.push(token); }
+ void setAcceptUuids(bool accept = true) { m_accept_uuids = accept; }
+
+private:
+ static void initKeywords();
+
+private:
+ Token m_token;
+ int m_line_no_start;
+ int m_line_no_end;
+ bool m_accept_uuids;
+ std::queue<Token> m_token_queue;
+ static std::map<std::string, Token::TokenType> s_keywords;
+};
+
+#endif // SCANNER_H
diff --git a/generator/token.h b/generator/token.h
new file mode 100644
index 0000000..3676449
--- /dev/null
+++ b/generator/token.h
@@ -0,0 +1,77 @@
+/* ***** BEGIN LICENSE BLOCK *****
+* Copyright (C) 2012, Peter Hatina <phatina at redhat.com>
+*
+* 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 of
+* the License, 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 this program. If not, see <http://www.gnu.org/licenses/>.
+* ***** END LICENSE BLOCK ***** */
+
+#ifndef TOKEN_H
+#define TOKEN_H
+
+#include <string>
+
+class Token
+{
+public:
+ typedef enum {
+ T_UNKNOWN, T_ATTRIBUTE, T_CONST, T_IN, T_OUT, T_INOUT,
+ T_INTERFACE, T_NATIVE, T_RAISES, T_TYPEDEF, T_UUID, T_UUIDVAL,
+ T_OPEN_BRACKET, T_CLOSE_BRACKET, T_OPEN_BRACE, T_CLOSE_BRACE,
+ T_OPEN_PARENTHESES, T_CLOSE_PARENTHESES, T_COMMA, T_COLON,
+ T_ASSIGN, T_SEMICOLON, T_INCLUDE, T_READONLY, T_FLOAT, T_DOUBLE,
+ T_STRING, T_WSTRING, T_UNSIGNED, T_SHORT, T_LONG, T_CHAR, T_WCHAR,
+ T_BOOLEAN, T_VOID, T_OCTET, T_DOUBLECOLON, T_PIPE, T_CARET, T_AMPERSAND,
+ T_SHIFT_LEFT, T_SHIFT_RIGHT, T_PLUS, T_MINUS, T_ASTERISK, T_SLASH,
+ T_PERCENT, T_TILDE, T_IDENTIFIER, T_EOF, T_NUMBER, T_HASH,
+ T_LEX_ERROR, T_UNSIGNED_SHORT, T_UNSIGNED_LONG, T_UNSIGNED_LONG_LONG,
+ T_LONG_LONG, T_LESS, T_GREATER, T_QUOTE, T_DOT
+ } TokenType;
+
+public:
+ Token():
+ m_type(T_UNKNOWN),
+ m_param()
+ {}
+
+ Token(TokenType type, const std::string ¶meter = std::string()):
+ m_type(type),
+ m_param(parameter)
+ {}
+
+ Token(const Token ©):
+ m_type(copy.m_type),
+ m_param(copy.m_param)
+ {}
+
+ ~Token()
+ {}
+
+ TokenType getType() const { return m_type; }
+ std::string getParameter() const { return m_param; }
+
+ Token &operator= (const Token& rhs)
+ {
+ m_type = rhs.m_type;
+ m_param = rhs.m_param;
+ return *this;
+ }
+
+ bool operator==(Token::TokenType type) const { return m_type == type; }
+ bool operator!=(Token::TokenType type) const { return m_type != type; }
+
+private:
+ TokenType m_type;
+ std::string m_param;
+};
+
+#endif // TOKEN_H
More information about the Spice-commits
mailing list