[Spice-devel] [PATCH] spice-xpi test page generator
Alon Levy
alevy at redhat.com
Wed Jun 27 07:20:54 PDT 2012
On Wed, Jun 27, 2012 at 02:00:48PM +0200, Peter Hatina wrote:
> Hi,
>
> David Jasa gave me an input to create some tool for automatic generation
> of spice-xpi test page. So here we are. I tried to be as simplistic, as
> possible. There is a huge context free grammar described at mozilla dev
> pages, but for our purposes, it exceeds the needs (also I've found, that
> there are some mistakes in the rules, that do not correspond with the
> reality). The grammar has been adapted for the needs of spice-xpi IDL
> file. Not to drag other dependencies for spice-xpi, I used C++ and STL
> only. It could have been done by flex/lex + yacc/bison, but why to use
> other deps for a simple plugin.
Insane :)
Didn't test it, just glossed over, I don't really understand why we need
it but it looks great.
>
> Please, have a look at this and give me your opinions. If we are OK,
> I would like to push this to the spice-xpi (fdo) repo.
>
> Footnote: there is a simple makefile included for local try. It will
> be replaced by the build system used by spice-xpi project.
>
> Cheers,
>
> Peter Hatina
> EMEA ENG-Desktop Development
> Red Hat Czech, Brno
>
> ---
> attribute.h | 59 +++++++
> generator.cpp | 281 ++++++++++++++++++++++++++++++++++
> generator.h | 59 +++++++
> main.cpp | 47 ++++++
> makefile | 24 +++
> method.h | 100 ++++++++++++
> options.cpp | 68 +++++++++
> options.h | 42 +++++
> parser.cpp | 430 ++++++++++++++++++++++++++++++++++++++++++++++++++++
> parser.h | 56 +++++++
> redirecthelper.cpp | 77 ++++++++++
> redirecthelper.h | 44 ++++++
> scanner.cpp | 233 ++++++++++++++++++++++++++++
> scanner.h | 50 ++++++
> token.h | 77 ++++++++++
> 15 files changed, 1647 insertions(+)
> create mode 100644 attribute.h
> create mode 100644 generator.cpp
> create mode 100644 generator.h
> create mode 100644 main.cpp
> create mode 100644 makefile
> create mode 100644 method.h
> create mode 100644 options.cpp
> create mode 100644 options.h
> create mode 100644 parser.cpp
> create mode 100644 parser.h
> create mode 100644 redirecthelper.cpp
> create mode 100644 redirecthelper.h
> create mode 100644 scanner.cpp
> create mode 100644 scanner.h
> create mode 100644 token.h
>
> diff --git a/attribute.h b/attribute.h
> new file mode 100644
> index 0000000..04dfb98
> --- /dev/null
> +++ b/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.cpp b/generator.cpp
> new file mode 100644
> index 0000000..c15a8c0
> --- /dev/null
> +++ b/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\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.h b/generator.h
> new file mode 100644
> index 0000000..0a9bd3f
> --- /dev/null
> +++ b/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/main.cpp b/main.cpp
> new file mode 100644
> index 0000000..61f8db2
> --- /dev/null
> +++ b/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/makefile b/makefile
> new file mode 100644
> index 0000000..2f4d516
> --- /dev/null
> +++ b/makefile
> @@ -0,0 +1,24 @@
> +CXX = g++
> +CXXFLAGS = -std=c++98 -g -pedantic -Wall
> +
> +SRCS = $(wildcard *.cpp)
> +OBJS = $(SRCS:.cpp=.o)
> +DEPS = $(SRCS:.cpp=.d)
> +BIN = generator
> +OUT = $(BIN) $(OBJS)
> +
> +$(BIN): $(OBJS)
> + $(CXX) $(CXXFLAGS) -o $@ $^
> +
> +%.o: %.cpp
> + $(CXX) $(CXXFLAGS) -c $<
> +
> +%.d: %.cpp
> + $(CXX) $(CXXFLAGS) -MM $< > $@
> +
> +clean:
> + rm -f $(OBJS) $(DEPS) $(BIN)
> +
> +ifeq ($(strip $(findstring $(MAKECMDGOALS), clean pack)),)
> +-include $(DEPS)
> +endif
> diff --git a/method.h b/method.h
> new file mode 100644
> index 0000000..4b63461
> --- /dev/null
> +++ b/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/options.cpp b/options.cpp
> new file mode 100644
> index 0000000..86ea867
> --- /dev/null
> +++ b/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/options.h b/options.h
> new file mode 100644
> index 0000000..7c990e5
> --- /dev/null
> +++ b/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/parser.cpp b/parser.cpp
> new file mode 100644
> index 0000000..637a2cb
> --- /dev/null
> +++ b/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/parser.h b/parser.h
> new file mode 100644
> index 0000000..18ebed1
> --- /dev/null
> +++ b/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/redirecthelper.cpp b/redirecthelper.cpp
> new file mode 100644
> index 0000000..4455216
> --- /dev/null
> +++ b/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/redirecthelper.h b/redirecthelper.h
> new file mode 100644
> index 0000000..786c91e
> --- /dev/null
> +++ b/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/scanner.cpp b/scanner.cpp
> new file mode 100644
> index 0000000..f475e73
> --- /dev/null
> +++ b/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/scanner.h b/scanner.h
> new file mode 100644
> index 0000000..d6150a2
> --- /dev/null
> +++ b/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/token.h b/token.h
> new file mode 100644
> index 0000000..3676449
> --- /dev/null
> +++ b/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
> --
> 1.7.10.2
>
> _______________________________________________
> Spice-devel mailing list
> Spice-devel at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/spice-devel
More information about the Spice-devel
mailing list