Hey Peter, Just a few notes after quickly testing this: - spice_xpi_generator is a bit unusual, command names generally use '-' instead of '_' - spice_xpi_generator --help tells me an arg is required (?) - we should update data/test.html with a version generated with spice_xpi_generator. Are they identical? - I'd add a comment at the beginning of the autogenerated page saying that it's autogenerated from idl using the generator found in spice-xpi and that it shouldn't be modified by hand. Christophe On Mon, Jul 02, 2012 at 01:01:20AM -0700, Peter Hatina wrote: > .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@xxxxxxxxxx> > 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@xxxxxxxxxx> > +* > +* 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@xxxxxxxxxx> > +* > +* 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@xxxxxxxxxx> > +* > +* 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@xxxxxxxxxx> > +* > +* 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@xxxxxxxxxx> > +* > +* 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@xxxxxxxxxx> > +* > +* 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@xxxxxxxxxx> > +* > +* 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@xxxxxxxxxx> > +* > +* 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@xxxxxxxxxx> > +* > +* 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@xxxxxxxxxx> > +* > +* 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@xxxxxxxxxx> > +* > +* 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@xxxxxxxxxx> > +* > +* 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@xxxxxxxxxx> > +* > +* 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@xxxxxxxxxx> > +* > +* 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 > _______________________________________________ > Spice-commits mailing list > Spice-commits@xxxxxxxxxxxxxxxxxxxxx > http://lists.freedesktop.org/mailman/listinfo/spice-commits
Attachment:
pgpYTTZqKVvyl.pgp
Description: PGP signature
_______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/spice-devel