diff options
author | Peter Hatina <phatina@redhat.com> | 2012-06-28 12:29:21 +0200 |
---|---|---|
committer | Peter Hatina <phatina@redhat.com> | 2012-07-02 10:01:53 +0200 |
commit | fb109b6a95215d5cf855022be5d601ba07fd01a5 (patch) | |
tree | b2337b837234031c228fd5312c4b59b8435c27ee | |
parent | 59295182c102eb4e19cba3d4d641eb96f7c4aa75 (diff) |
introduce test page generator
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | Makefile.am | 3 | ||||
-rw-r--r-- | configure.ac | 22 | ||||
-rw-r--r-- | generator/Makefile.am | 8 | ||||
-rw-r--r-- | generator/README | 28 | ||||
-rw-r--r-- | generator/attribute.h | 59 | ||||
-rw-r--r-- | generator/generator.cpp | 281 | ||||
-rw-r--r-- | generator/generator.h | 59 | ||||
-rw-r--r-- | generator/main.cpp | 47 | ||||
-rw-r--r-- | generator/method.h | 100 | ||||
-rw-r--r-- | generator/options.cpp | 68 | ||||
-rw-r--r-- | generator/options.h | 42 | ||||
-rw-r--r-- | generator/parser.cpp | 430 | ||||
-rw-r--r-- | generator/parser.h | 56 | ||||
-rw-r--r-- | generator/redirecthelper.cpp | 77 | ||||
-rw-r--r-- | generator/redirecthelper.h | 44 | ||||
-rw-r--r-- | generator/scanner.cpp | 233 | ||||
-rw-r--r-- | generator/scanner.h | 50 | ||||
-rw-r--r-- | generator/token.h | 77 |
19 files changed, 1679 insertions, 6 deletions
@@ -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@redhat.com> +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see <http://www.gnu.org/licenses/>. +* ***** END LICENSE BLOCK ***** */ + +#ifndef ATTRIBUTE_H +#define ATTRIBUTE_H + +#include <string> +#include "token.h" + +class Attribute +{ +public: + Attribute(Token::TokenType type, const std::string &identifier, bool readonly = false): + m_type(type), + m_identifier(identifier), + m_readonly(readonly) + {} + + Attribute(const Attribute ©): + m_type(copy.m_type), + m_identifier(copy.m_identifier), + m_readonly(copy.m_readonly) + {} + + ~Attribute() + {} + + Token::TokenType getType() const { return m_type; } + std::string getIdentifier() const { return m_identifier; } + + Attribute &operator=(const Attribute &rhs) + { + m_type = rhs.m_type; + m_identifier = rhs.m_identifier; + m_readonly = rhs.m_readonly; + return *this; + } + +private: + Token::TokenType m_type; + std::string m_identifier; + bool m_readonly; +}; + +#endif // ATTRIBUTE_H diff --git a/generator/generator.cpp b/generator/generator.cpp new file mode 100644 index 0000000..fce228c --- /dev/null +++ b/generator/generator.cpp @@ -0,0 +1,281 @@ +/* ***** BEGIN LICENSE BLOCK ***** +* Copyright (C) 2012, Peter Hatina <phatina@redhat.com> +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see <http://www.gnu.org/licenses/>. +* ***** END LICENSE BLOCK ***** */ + +#include <algorithm> +#include <iostream> +#include <sstream> +#include <cctype> +#include "generator.h" + +std::set<std::string> Generator::s_default_attributes; +std::set<std::string> Generator::s_default_methods; +std::map<std::string, std::string> Generator::s_default_attribute_values; + +Generator::Generator(const std::list<Attribute> &attributes, + const std::list<Method> &methods): + m_attributes(attributes), + m_methods(methods) +{ + init(); +} + +Generator::~Generator() +{ +} + +void Generator::init() +{ + if (!s_default_attributes.empty()) + return; + + s_default_attributes.insert("hostip"); + s_default_attributes.insert("port"); + s_default_attributes.insert("adminconsole"); + s_default_attributes.insert("hotkey"); + s_default_attributes.insert("smartcard"); + s_default_methods.insert("setlanguagestringssection"); + s_default_methods.insert("setlanguagestringslang"); + s_default_methods.insert("setusbfilterfilter"); + s_default_attribute_values["adminconsole"] = " checked"; + s_default_attribute_values["hotkey"] = " value=\"toggle-fullscreen=shift+f11," \ + "release-cursor=shift+f12,smartcard-insert=shift+f8,smartcard-remove=shift+f9\""; + s_default_attribute_values["usblistenport"] = " value=\"32023\""; +} + +void Generator::generate() +{ + generateHeader(); + generateConnectVars(); + generateContent(); + generateFooter(); +} + +void Generator::generateHeader() +{ + std::cout << "<html>\n" + << "<head>\n" + << "<title>Spice-XPI test page (generated)</title>\n" + << "<style type=\"text/css\">\n" + << "caption {\n" + << " text-align: left;\n" + << " font-weight: bold;\n" + << "}\n\n" + << "th {\n" + << " text-align: left;\n" + << "}\n" + << "</style>\n" + << "</head>\n\n" + << "<body onload=\"bodyLoad()\" onunload=\"bodyUnload()\">\n\n" + << "<center>\n" + << "<h1>SPICE xpi test page (generated)</h1>\n" + << "SPICE xpi test page. Disabled (greyed out) values are passed\n" + << "to SPICE xpi as empty variables.\n</center>\n<br/>\n\n" + << "<embed type=\"application/x-spice\" width=\"0\" height=\"0\" id=\"spice-xpi\"/><br/>\n\n" + << "<script type=\"text/javascript\">\n\n" + << "var embed = document.getElementById(\"spice-xpi\");\n\n" + << "function bodyLoad()\n{\n log(\"Body Load\");\n};\n\n" + << "function bodyUnload()\n{\n log(\"Body Unload\");\n}\n\n" + << "function connect()\n{\n" + << " setConnectVars();\n" + << " setUsbFilter();\n" + << " embed.connect();\n" + << " log(\"Connect: host '\" + embed.hostIP + \"', port '\" + " + << "embed.port\n + \"', secure port '\" + embed.SecurePort + " + << "\"', USB port '\" +\n embed.UsbListenPort + \"'\");\n}\n\n" + << "function disconnect()\n{\n" + << " embed.disconnect();\n" + << " log(\"Disconnect\");\n}\n\n" + << "function OnDisconnected(msg)\n{\n log(\"Disconnected, return code: \" + msg);\n}\n\n" + << "function log(message)\n{\n" + << " var log = document.getElementById(\"log\");\n" + << " var ts = new Date().toString() + \": \";\n" + << " var newRow = document.createElement(\"tr\");\n" + << " var tsCell = document.createElement(\"td\");\n" + << " var msgCell = document.createElement(\"td\");\n\n" + << " tsCell.innerHTML = ts;\n" + << " msgCell.innerHTML = message;\n\n" + << " newRow.appendChild(tsCell);\n" + << " newRow.appendChild(msgCell);\n" + << " log.appendChild(newRow);\n}\n\n" + << "function setLanguageStrings()\n{\n" + << " section = document.getElementById(\"SetLanguageStringssectionToggled\").checked ?\n" + << " document.getElementById(\"SetLanguageStringssection\").value : \"\";\n" + << " lang = document.getElementById(\"SetLanguageStringslangToggled\").checked ?\n" + << " document.getElementById(\"SetLanguageStringslang\").value : \"\";\n" + << " embed.SetLanguageStrings(section, lang);\n" + << " log(\"Language Strings set to '\" + section + \"' '\" + lang + \"'\");\n}\n\n" + << "function setUsbFilter()\n{\n" + << " UsbFilterToggled = document.getElementById(\"SetUsbFilterfilterToggled\");\n" + << " if (!UsbFilterToggled)\n return;\n" + << " filter = UsbFilterToggled.checked ?\n" + << " document.getElementById(\"SetUsbFilterfilter\").value : \"\";\n" + << " embed.SetUsbFilter(filter);\n" + << " log(\"USB Filter String set to: '\" + filter + \"'\");\n}\n\n" + << "function show()\n{\n" + << " embed.show();\n" + << " log(\"Show\");\n}\n\n" + << "function ConnectedStatus()\n{\n" + << " log(\"Connected status = \" + embed.ConnectedStatus());\n}\n\n" + << "function toggle(checkboxID)\n{\n" + << " var checkbox = document.getElementById(checkboxID);\n" + << " var toggle = document.getElementById(arguments[1]);\n" + << " toggle.disabled = !checkbox.checked;\n}\n\n"; +} + +void Generator::generateFooter() +{ + std::cout << "<hr/>\n<table style=\"border: 1px; border-color: black;\">\n" + << "<caption>log:</caption>\n" + << "<thead><tr><th style=\"width: 22em;\">timestamp</th>" + << "<th>message</th></tr></thead>\n" + << "<tbody style=\"font-family: monospace;\" id=\"log\">\n" + << "</tbody>\n" + << "</table>\n" + << "</body>\n" + << "</html>\n"; +} + +void Generator::generateConnectVars() +{ + std::cout << "function setConnectVars()\n{\n"; + std::list<Attribute>::iterator it; + for (it = m_attributes.begin(); it != m_attributes.end(); ++it) { + std::cout << " embed." << it->getIdentifier() << " = " + << "document.getElementById(\"" + << it->getIdentifier() << "Toggled\").checked ? " + << "document.getElementById(\"" + << it->getIdentifier() << "\")." + << (it->getType() == Token::T_BOOLEAN ? "checked" : "value") + << " : \"\";\n"; + } + std::cout << "}\n\n</script>\n\n"; +} + +void Generator::generateContent() +{ + std::cout << "<center>\n\n" + << "<table id=\"values\">\n"; + + std::list<Attribute>::iterator ita; + for (ita = m_attributes.begin(); ita != m_attributes.end(); ++ita) { + std::cout << "<tr>\n<td><input type=\"checkbox\" id=\"" + << ita->getIdentifier() << "Toggled" + << "\" onclick=\"toggle('" + << ita->getIdentifier() << "Toggled" + <<"', '" << ita->getIdentifier() + << "')\" " << (attributeEnabled(*ita) ? "checked" : "") + << "/></td>\n" + << "<td>" << splitIdentifier(ita->getIdentifier()) << "</td>\n" + << "<td>" << attributeToHtmlElement(*ita) + << "</td>\n</tr>\n"; + } + + std::list<Method>::iterator itm; + for (itm = m_methods.begin(); itm != m_methods.end(); ++itm) { + std::list<Method::MethodParam>::iterator itp; + std::list<Method::MethodParam> params = itm->getParams(); + for (itp = params.begin(); itp != params.end(); ++itp) { + std::cout << "<tr>\n<td><input type=\"checkbox\" id=\"" + << itm->getIdentifier() << itp->getIdentifier() + << "Toggled\" onclick=\"toggle('" + << itm->getIdentifier() << itp->getIdentifier() + << "Toggled', '" << itm->getIdentifier() + << itp->getIdentifier() << "')\"" + << (methodEnabled(*itm, *itp) ? "checked" : "") + << "/></td>\n<td>" << splitIdentifier(itm->getIdentifier()) + << " - " << splitIdentifier(itp->getIdentifier()) << "</td>\n" + << "<td><input id=\"" << itm->getIdentifier() << itp->getIdentifier() + << "\" type=\"" << (itp->getType() == Token::T_BOOLEAN ? "checkbox" : "text") + << "\" size=\"30\" " << (methodEnabled(*itm, *itp) ? "" : "disabled ") + << "/></td>\n</tr>\n"; + } + } + + std::cout << "</table>\n\n<br/>\n"; + + int i = 1; + for (itm = m_methods.begin(); itm != m_methods.end(); ++itm, ++i) { + std::cout << "<input type=\"button\" value=\"" + << splitIdentifier(itm->getIdentifier()) + << "\" style=\"min-width: 180px\" onclick=\"" + << itm->getIdentifier() + << "()\"/>\n"; + if (i % 3 == 0 && i != static_cast<int>(m_methods.size())) + std::cout << "<br/>\n"; + } + + std::cout << "\n</center>\n\n"; +} + +std::string Generator::lowerString(const std::string &str) +{ + std::string s(str); + std::transform(s.begin(), s.end(), s.begin(), tolower); + return s; +} + +std::string Generator::splitIdentifier(const std::string &str) +{ + std::string result(str); + result[0] = toupper(result[0]); + for (size_t i = 1; i < result.size() - 1; ++i) { + if (isupper(result[i]) && islower(result[i + 1])) + result.insert(i++, " "); + else if (isupper(result[i]) && islower(result[i - 1])) + result.insert(i++, " "); + } + return result; +} + +std::string Generator::attributeToHtmlElement(const Attribute &attr) +{ + std::stringstream ss; + std::string id = lowerString(attr.getIdentifier()); + if (id == "truststore") { + ss << "<textarea id=\"" << attr.getIdentifier() + << "\" cols=\"66\" rows=\"33\" " + << (attributeEnabled(attr) ? "" : "disabled") + << "/></textarea>"; + } else { + ss << "<input id=\"" << attr.getIdentifier() << "\" type=\"" + << (attr.getType() == Token::T_BOOLEAN ? "checkbox" : "text") + <<"\" size=\"30\" " << attributeDefaultValue(attr) + << (attributeEnabled(attr) ? "" : "disabled ") << "/>"; + } + return ss.str(); +} + +std::string Generator::attributeDefaultValue(const Attribute &attr) +{ + std::string id(lowerString(attr.getIdentifier())); + std::map<std::string, std::string>::iterator found = s_default_attribute_values.find(id); + return found != s_default_attribute_values.end() ? found->second : ""; +} + +bool Generator::attributeEnabled(const Attribute &attr) +{ + std::string id(lowerString(attr.getIdentifier())); + std::set<std::string>::iterator found = s_default_attributes.find(id); + return found != s_default_attributes.end(); +} + +bool Generator::methodEnabled(const Method &method, const Method::MethodParam ¶m) +{ + std::string id(lowerString(method.getIdentifier() + param.getIdentifier())); + std::set<std::string>::iterator found = s_default_methods.find(id); + return found != s_default_methods.end(); +} diff --git a/generator/generator.h b/generator/generator.h new file mode 100644 index 0000000..0a9bd3f --- /dev/null +++ b/generator/generator.h @@ -0,0 +1,59 @@ +/* ***** BEGIN LICENSE BLOCK ***** +* Copyright (C) 2012, Peter Hatina <phatina@redhat.com> +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see <http://www.gnu.org/licenses/>. +* ***** END LICENSE BLOCK ***** */ + +#ifndef GENERATOR_H +#define GENERATOR_H + +#include <list> +#include <map> +#include <set> +#include "attribute.h" +#include "method.h" +#include "token.h" + +class Generator +{ +public: + Generator(const std::list<Attribute> &attributes, + const std::list<Method> &methods); + ~Generator(); + + void generate(); + +private: + void init(); + void generateHeader(); + void generateFooter(); + void generateConnectVars(); + void generateContent(); + + static std::string lowerString(const std::string &str); + static std::string splitIdentifier(const std::string &str); + static std::string attributeDefaultValue(const Attribute &attr); + static std::string attributeToHtmlElement(const Attribute &attr); + static bool attributeEnabled(const Attribute &attr); + static bool methodEnabled(const Method &method, const Method::MethodParam ¶m); + +private: + std::list<Attribute> m_attributes; + std::list<Method> m_methods; + static std::set<std::string> s_default_attributes; + static std::set<std::string> s_default_methods; + static std::map<std::string, std::string> s_default_attribute_values; +}; + +#endif // GENERATOR_H diff --git a/generator/main.cpp b/generator/main.cpp new file mode 100644 index 0000000..61f8db2 --- /dev/null +++ b/generator/main.cpp @@ -0,0 +1,47 @@ +/* ***** BEGIN LICENSE BLOCK ***** +* Copyright (C) 2012, Peter Hatina <phatina@redhat.com> +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see <http://www.gnu.org/licenses/>. +* ***** END LICENSE BLOCK ***** */ + +#include "generator.h" +#include "options.h" +#include "parser.h" +#include "redirecthelper.h" + +int main(int argc, char **argv) +{ + Options o(argc, argv); + if (!o.good()) + return 1; + + if (o.help()) { + o.printHelp(); + return 0; + } + + RedirectHelper rh(o); + if (!rh.redirect()) + return 1; + + Parser p; + if (!p.parse()) + return 1; + + Generator g(p.getAttributes(), p.getMethods()); + g.generate(); + + rh.restore(); + return 0; +} diff --git a/generator/method.h b/generator/method.h new file mode 100644 index 0000000..4b63461 --- /dev/null +++ b/generator/method.h @@ -0,0 +1,100 @@ +/* ***** BEGIN LICENSE BLOCK ***** +* Copyright (C) 2012, Peter Hatina <phatina@redhat.com> +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see <http://www.gnu.org/licenses/>. +* ***** END LICENSE BLOCK ***** */ + +#ifndef METHOD_H +#define METHOD_H + +#include <string> +#include <list> +#include "token.h" + +class Method +{ +public: + class MethodParam; + +public: + Method(Token::TokenType type, std::string &identifier, const std::list<MethodParam> ¶ms): + m_type(type), + m_identifier(identifier), + m_params(params) + {} + + Method(const Method ©): + m_type(copy.m_type), + m_identifier(copy.m_identifier), + m_params(copy.m_params) + {} + + ~Method() + {} + + Token::TokenType getType() const { return m_type; } + std::string getIdentifier() const { return m_identifier; } + std::list<MethodParam> getParams() const { return m_params; } + + Method &operator=(const Method &rhs) + { + m_type = rhs.m_type; + m_identifier = rhs.m_identifier; + m_params = rhs.m_params; + return *this; + } + +private: + Token::TokenType m_type; + std::string m_identifier; + std::list<MethodParam> m_params; +}; + +class Method::MethodParam +{ +public: + MethodParam(Token::TokenType dir, Token::TokenType type, const std::string &identifier): + m_dir(dir), + m_type(type), + m_identifier(identifier) + {} + + MethodParam(const MethodParam ©): + m_dir(copy.m_dir), + m_type(copy.m_type), + m_identifier(copy.m_identifier) + {} + + ~MethodParam() + {} + + Token::TokenType getType() const { return m_type; } + Token::TokenType getDir() const { return m_dir; } + std::string getIdentifier() const { return m_identifier; } + + MethodParam &operator=(const MethodParam &rhs) + { + m_dir = rhs.m_dir; + m_type = rhs.m_type; + m_identifier = rhs.m_identifier; + return *this; + } + +private: + Token::TokenType m_dir; + Token::TokenType m_type; + std::string m_identifier; +}; + +#endif // METHOD_H diff --git a/generator/options.cpp b/generator/options.cpp new file mode 100644 index 0000000..86ea867 --- /dev/null +++ b/generator/options.cpp @@ -0,0 +1,68 @@ +/* ***** BEGIN LICENSE BLOCK ***** +* Copyright (C) 2012, Peter Hatina <phatina@redhat.com> +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see <http://www.gnu.org/licenses/>. +* ***** END LICENSE BLOCK ***** */ + +#include <iostream> +extern "C" { +# include <getopt.h> +} +#include "options.h" + +Options::Options(int argc, char **argv): + m_help(false), + m_good(true), + m_input_filename(), + m_output_filename() +{ + static struct option longopts[] = { + { "input", required_argument, NULL, 'i' }, + { "output", required_argument, NULL, 'o' }, + { "help", required_argument, NULL, 'h' }, + { NULL, 0, NULL, 0 } + }; + + int c; + while ((c = getopt_long(argc, argv, "i:o:h", longopts, NULL)) != -1) { + switch (c) { + case 'i': + m_input_filename = optarg; + break; + case 'o': + m_output_filename = optarg; + break; + case 'h': + m_help = true; + break; + default: + m_good = false; + break; + } + } +} + +Options::~Options() +{ +} + +void Options::printHelp() const +{ + std::cout << "Spice-xpi test page generator\n\n" + << "Usage: ./generator [-h] [-i input] [-o output]\n\n" + << "Application options:\n" + << " -i, --input input filename (stdin used, if not specified)\n" + << " -o, --output output filename (stdout used, if not specified)\n" + << " -h, --help prints this help\n"; +} diff --git a/generator/options.h b/generator/options.h new file mode 100644 index 0000000..7c990e5 --- /dev/null +++ b/generator/options.h @@ -0,0 +1,42 @@ +/* ***** BEGIN LICENSE BLOCK ***** +* Copyright (C) 2012, Peter Hatina <phatina@redhat.com> +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see <http://www.gnu.org/licenses/>. +* ***** END LICENSE BLOCK ***** */ + +#ifndef OPTIONS_H +#define OPTIONS_H + +#include <string> + +class Options +{ +public: + Options(int argc, char **argv); + ~Options(); + + bool help() const { return m_help; } + bool good() const { return m_good; } + void printHelp() const; + std::string inputFilename() const { return m_input_filename; } + std::string outputFilename() const { return m_output_filename; } + +private: + bool m_help; + bool m_good; + std::string m_input_filename; + std::string m_output_filename; +}; + +#endif // OPTIONS_H diff --git a/generator/parser.cpp b/generator/parser.cpp new file mode 100644 index 0000000..637a2cb --- /dev/null +++ b/generator/parser.cpp @@ -0,0 +1,430 @@ +/* ***** BEGIN LICENSE BLOCK ***** +* Copyright (C) 2012, Peter Hatina <phatina@redhat.com> +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see <http://www.gnu.org/licenses/>. +* ***** END LICENSE BLOCK ***** */ + +#include <iostream> +#include <list> +#include "parser.h" + +Parser::Parser(): + m_scanner(), + m_token(), + m_attributes(), + m_methods() +{ +} + +Parser::~Parser() +{ +} + +bool Parser::parse() +{ + m_token = m_scanner.getNextToken(); + while (1) { + if (m_token == Token::T_INTERFACE || m_token == Token::T_OPEN_BRACKET) { + if (!parseDefinition()) + return false; + } else if (m_token == Token::T_HASH) { + if (!parseInclude()) + return false; + } else if (m_token == Token::T_EOF) { + return true; + } else { + handleError(); + return false; + } + } +} + +void Parser::handleError() const +{ + std::cerr << (m_token == Token::T_LEX_ERROR ? + "Lexical error near line: " : "Syntax error near line: "); + std::cerr << m_scanner.getLineNo() << std::endl; +} + +bool Parser::parseInclude() +{ + if (m_token != Token::T_HASH) { + handleError(); + return false; + } + + m_token = m_scanner.getNextToken(); + if (m_token != Token::T_INCLUDE) { + handleError(); + return false; + } + + m_token = m_scanner.getNextToken(); + if (m_token == Token::T_QUOTE) { + m_token = m_scanner.getNextToken(); + while (m_token == Token::T_IDENTIFIER || m_token == Token::T_DOT) + m_token = m_scanner.getNextToken(); + if (m_token != Token::T_QUOTE) { + handleError(); + return false; + } + m_token = m_scanner.getNextToken(); + } else if (m_token == Token::T_LESS) { + m_token = m_scanner.getNextToken(); + while (m_token == Token::T_IDENTIFIER || m_token == Token::T_DOT) + m_token = m_scanner.getNextToken(); + if (m_token != Token::T_GREATER) { + handleError(); + return false; + } + m_token = m_scanner.getNextToken(); + } else { + handleError(); + return false; + } + + return true; +} + +bool Parser::parseDefinitionParams() +{ + if (m_token != Token::T_OPEN_BRACKET) { + handleError(); + return false; + } + + m_token = m_scanner.getNextToken(); + while (1) { + if (m_token == Token::T_UUID) { + m_token = m_scanner.getNextToken(); + if (m_token != Token::T_OPEN_PARENTHESES) { + handleError(); + return false; + } + m_scanner.setAcceptUuids(); + m_token = m_scanner.getNextToken(); + if (m_token != Token::T_UUIDVAL) { + handleError(); + return false; + } + m_scanner.setAcceptUuids(false); + m_token = m_scanner.getNextToken(); + if (m_token != Token::T_CLOSE_PARENTHESES) { + handleError(); + return false; + } + } else if (m_token != Token::T_IDENTIFIER) { + handleError(); + return false; + } + + m_token = m_scanner.getNextToken(); + if (m_token == Token::T_CLOSE_BRACKET) { + m_token = m_scanner.getNextToken(); + return true; + } + else if (m_token != Token::T_COMMA) { + handleError(); + return false; + } + + m_token = m_scanner.getNextToken(); + } +} + +bool Parser::parseDefinition() +{ + if (m_token == Token::T_OPEN_BRACKET && !parseDefinitionParams()) + return false; + + if (m_token != Token::T_INTERFACE) { + handleError(); + return false; + } + + m_token = m_scanner.getNextToken(); + if (m_token != Token::T_IDENTIFIER) { + handleError(); + return false; + } + + m_token = m_scanner.getNextToken(); + if (m_token == Token::T_COLON) { + m_token = m_scanner.getNextToken(); + if (!parseBasicInterfaces()) + return false; + } + + if (m_token != Token::T_OPEN_BRACE) { + handleError(); + return false; + } + + m_token = m_scanner.getNextToken(); + if (!parseInterfaceBody()) + return false; + + if (m_token != Token::T_CLOSE_BRACE) { + handleError(); + return false; + } + + m_token = m_scanner.getNextToken(); + if (m_token != Token::T_SEMICOLON) { + handleError(); + return false; + } + + m_token = m_scanner.getNextToken(); + return true; +} + +bool Parser::parseBasicInterfaces() +{ + while (1) { + if (m_token != Token::T_IDENTIFIER) { + handleError(); + return false; + } + + m_token = m_scanner.getNextToken(); + if (m_token == Token::T_OPEN_BRACE) + return true; + + if (m_token != Token::T_COMMA) { + handleError(); + return false; + } + + m_token = m_scanner.getNextToken(); + } +} + +bool Parser::parseInterfaceBody() +{ + while (1) { + switch (m_token.getType()) { + case Token::T_READONLY: + case Token::T_ATTRIBUTE: + if (!parseAttribute()) + return false; + break; + case Token::T_FLOAT: + case Token::T_DOUBLE: + case Token::T_STRING: + case Token::T_WSTRING: + case Token::T_UNSIGNED: + case Token::T_SHORT: + case Token::T_LONG: + case Token::T_CHAR: + case Token::T_WCHAR: + case Token::T_BOOLEAN: + case Token::T_VOID: + case Token::T_OCTET: + if (!parseMethod()) + return false; + break; + case Token::T_CLOSE_BRACE: + break; + default: + handleError(); + return false; + } + + if (m_token == Token::T_LEX_ERROR) { + handleError(); + return false; + } + + if (m_token == Token::T_CLOSE_BRACE) + return true; + } + return true; +} + +bool Parser::parseAttribute() +{ + bool readonly = false; + if (m_token == Token::T_READONLY) { + readonly = true; + m_token = m_scanner.getNextToken(); + } + + if (m_token != Token::T_ATTRIBUTE) { + handleError(); + return false; + } + + m_token = m_scanner.getNextToken(); + if (!parseType()) + return false; + + Token type = m_token; + + m_token = m_scanner.getNextToken(); + if (m_token != Token::T_IDENTIFIER) { + handleError(); + return false; + } + + m_attributes.push_back(Attribute(type.getType(), m_token.getParameter(), readonly)); + + m_token = m_scanner.getNextToken(); + if (m_token == Token::T_COMMA) { + while (1) { + m_token = m_scanner.getNextToken(); + if (m_token != Token::T_IDENTIFIER) { + handleError(); + return false; + } + + m_attributes.push_back(Attribute(type.getType(), m_token.getParameter(), readonly)); + m_token = m_scanner.getNextToken(); + if (m_token == Token::T_SEMICOLON) { + break; + } else if (m_token != Token::T_COMMA) { + handleError(); + return false; + } + } + } + if (m_token != Token::T_SEMICOLON) { + handleError(); + return false; + } + + m_token = m_scanner.getNextToken(); + return true; +} + +bool Parser::parseMethod() +{ + if (!parseType()) + return false; + + Token type = m_token; + m_token = m_scanner.getNextToken(); + if (m_token != Token::T_IDENTIFIER) { + handleError(); + return false; + } + + std::string identifier = m_token.getParameter(); + + m_token = m_scanner.getNextToken(); + if (m_token != Token::T_OPEN_PARENTHESES) { + handleError(); + return false; + } + + std::list<Method::MethodParam> params; + m_token = m_scanner.getNextToken(); + if (m_token != Token::T_CLOSE_PARENTHESES) { + while (1) { + Token dir(Token::T_UNKNOWN); + if (m_token == Token::T_IN || + m_token == Token::T_OUT || + m_token == Token::T_INOUT) + { + dir = m_token; + m_token = m_scanner.getNextToken(); + } + if (!parseType()) + return false; + Token type = m_token; + m_token = m_scanner.getNextToken(); + if (m_token != Token::T_IDENTIFIER) { + handleError(); + return false; + } + + params.push_back(Method::MethodParam(dir.getType(), type.getType(), + m_token.getParameter())); + + m_token = m_scanner.getNextToken(); + if (m_token == Token::T_COMMA) { + m_token = m_scanner.getNextToken(); + } else if (m_token == Token::T_CLOSE_PARENTHESES) { + break; + } else { + handleError(); + return false; + } + } + } + + if (m_token != Token::T_CLOSE_PARENTHESES) { + handleError(); + return false; + } + + m_token = m_scanner.getNextToken(); + if (m_token != Token::T_SEMICOLON) { + handleError(); + return false; + } + + m_methods.push_back(Method(type.getType(), identifier, params)); + + m_token = m_scanner.getNextToken(); + return true; +} + +bool Parser::parseType() +{ + switch (m_token.getType()) { + case Token::T_UNSIGNED: { + Token new_token = m_scanner.getNextToken(); + if (new_token == Token::T_SHORT) + m_token = Token(Token::T_UNSIGNED_SHORT); + else if (new_token == Token::T_LONG) { + Token newest_token = m_scanner.getNextToken(); + if (newest_token == Token::T_LONG) { + m_token = Token(Token::T_UNSIGNED_LONG_LONG); + } else { + m_scanner.pushToken(newest_token); + m_token = Token(Token::T_UNSIGNED_LONG); + } + } else { + m_scanner.pushToken(new_token); + handleError(); + return false; + } + break; + } + case Token::T_LONG: { + Token new_token = m_scanner.getNextToken(); + if (new_token == Token::T_LONG) + m_token = Token(Token::T_LONG_LONG); + else + m_scanner.pushToken(new_token); + break; + } + case Token::T_FLOAT: + case Token::T_DOUBLE: + case Token::T_STRING: + case Token::T_WSTRING: + case Token::T_SHORT: + case Token::T_CHAR: + case Token::T_WCHAR: + case Token::T_BOOLEAN: + case Token::T_VOID: + case Token::T_OCTET: + return true; + default: + handleError(); + return false; + } + return true; +} diff --git a/generator/parser.h b/generator/parser.h new file mode 100644 index 0000000..18ebed1 --- /dev/null +++ b/generator/parser.h @@ -0,0 +1,56 @@ +/* ***** BEGIN LICENSE BLOCK ***** +* Copyright (C) 2012, Peter Hatina <phatina@redhat.com> +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see <http://www.gnu.org/licenses/>. +* ***** END LICENSE BLOCK ***** */ + +#ifndef PARSER_H +#define PARSER_H + +#include <list> +#include "attribute.h" +#include "method.h" +#include "scanner.h" +#include "token.h" + +class Parser +{ +public: + Parser(); + ~Parser(); + + bool parse(); + + std::list<Attribute> getAttributes() const { return m_attributes; } + std::list<Method> getMethods() const { return m_methods; } + +private: + void handleError() const; + bool parseInclude(); + bool parseDefinitionParams(); + bool parseDefinition(); + bool parseBasicInterfaces(); + bool parseInterfaceBody(); + bool parseAttribute(); + bool parseType(); + bool parseMethod(); + +private: + Scanner m_scanner; + Token m_token; + std::list<Attribute> m_attributes; + std::list<Method> m_methods; +}; + +#endif // PARSER_H diff --git a/generator/redirecthelper.cpp b/generator/redirecthelper.cpp new file mode 100644 index 0000000..4455216 --- /dev/null +++ b/generator/redirecthelper.cpp @@ -0,0 +1,77 @@ +/* ***** BEGIN LICENSE BLOCK ***** +* Copyright (C) 2012, Peter Hatina <phatina@redhat.com> +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see <http://www.gnu.org/licenses/>. +* ***** END LICENSE BLOCK ***** */ + +#include "redirecthelper.h" + +RedirectHelper::RedirectHelper(const Options &o): + m_input_file(o.inputFilename()), + m_output_file(o.outputFilename()), + m_ifb(), + m_ofb(), + m_cin_streambuf(NULL), + m_cout_streambuf(NULL) +{ +} + +RedirectHelper::~RedirectHelper() +{ + restore(); +} + +bool RedirectHelper::redirect() +{ + if (!m_cin_streambuf && !m_input_file.empty()) { + m_ifb.open(m_input_file.c_str(), std::ios::in); + if (!m_ifb.is_open()) { + std::cerr << "Unable to open '" << m_input_file << "' for reading!\n"; + return false; + } else { + m_cin_streambuf = std::cin.rdbuf(); + std::istream is(&m_ifb); + std::cin.rdbuf(is.rdbuf()); + } + } + + if (!m_cout_streambuf && !m_output_file.empty()) { + m_ofb.open(m_output_file.c_str(), std::ios::out); + if (!m_ofb.is_open()) { + std::cerr << "Unable to open '" << m_output_file << "' for writing!\n"; + return false; + } else { + m_cout_streambuf = std::cout.rdbuf(); + std::ostream os(&m_ofb); + std::cout.rdbuf(os.rdbuf()); + } + } + + return true; +} + +void RedirectHelper::restore() +{ + if (m_cin_streambuf) { + m_ifb.close(); + std::cin.rdbuf(m_cin_streambuf); + m_cin_streambuf = NULL; + } + + if (m_cout_streambuf) { + m_ofb.close(); + std::cout.rdbuf(m_cout_streambuf); + m_cout_streambuf = NULL; + } +} diff --git a/generator/redirecthelper.h b/generator/redirecthelper.h new file mode 100644 index 0000000..786c91e --- /dev/null +++ b/generator/redirecthelper.h @@ -0,0 +1,44 @@ +/* ***** BEGIN LICENSE BLOCK ***** +* Copyright (C) 2012, Peter Hatina <phatina@redhat.com> +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see <http://www.gnu.org/licenses/>. +* ***** END LICENSE BLOCK ***** */ + +#ifndef REDIRECTHELPER_H +#define REDIRECTHELPER_H + +#include <iostream> +#include <fstream> +#include <string> +#include "options.h" + +class RedirectHelper +{ +public: + RedirectHelper(const Options &o); + ~RedirectHelper(); + + bool redirect(); + void restore(); + +private: + std::string m_input_file; + std::string m_output_file; + std::filebuf m_ifb; + std::filebuf m_ofb; + std::streambuf *m_cin_streambuf; + std::streambuf *m_cout_streambuf; +}; + +#endif // REDIRECTHELPER_H diff --git a/generator/scanner.cpp b/generator/scanner.cpp new file mode 100644 index 0000000..f475e73 --- /dev/null +++ b/generator/scanner.cpp @@ -0,0 +1,233 @@ +/* ***** BEGIN LICENSE BLOCK ***** +* Copyright (C) 2012, Peter Hatina <phatina@redhat.com> +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see <http://www.gnu.org/licenses/>. +* ***** END LICENSE BLOCK ***** */ + +#include <fstream> +#include <cctype> +#include "scanner.h" + +std::map<std::string, Token::TokenType> Scanner::s_keywords; + +Scanner::Scanner(): + m_token(), + m_line_no_start(1), + m_line_no_end(m_line_no_start), + m_accept_uuids(false), + m_token_queue() +{ + if (s_keywords.empty()) + initKeywords(); +} + +Scanner::~Scanner() +{ +} + +void Scanner::initKeywords() +{ + s_keywords["attribute"] = Token::T_ATTRIBUTE; + s_keywords["const"] = Token::T_CONST; + s_keywords["in"] = Token::T_IN; + s_keywords["out"] = Token::T_OUT; + s_keywords["inout"] = Token::T_INOUT; + s_keywords["interface"] = Token::T_INTERFACE; + s_keywords["native"] = Token::T_NATIVE; + s_keywords["raises"] = Token::T_RAISES; + s_keywords["readonly"] = Token::T_READONLY; + s_keywords["typedef"] = Token::T_TYPEDEF; + s_keywords["uuid"] = Token::T_UUID; + s_keywords["float"] = Token::T_FLOAT; + s_keywords["double"] = Token::T_DOUBLE; + s_keywords["string"] = Token::T_STRING; + s_keywords["wstring"] = Token::T_WSTRING; + s_keywords["unsigned"] = Token::T_UNSIGNED; + s_keywords["short"] = Token::T_SHORT; + s_keywords["long"] = Token::T_LONG; + s_keywords["char"] = Token::T_CHAR; + s_keywords["wchar"] = Token::T_WCHAR; + s_keywords["boolean"] = Token::T_BOOLEAN; + s_keywords["void"] = Token::T_VOID; + s_keywords["octet"] = Token::T_OCTET; + s_keywords["include"] = Token::T_INCLUDE; +} + +Token Scanner::getNextToken() +{ + if (!m_token_queue.empty()) { + Token t(m_token_queue.front()); + m_token_queue.pop(); + return t; + } + + enum { S_INITIAL, S_IDENTIFIER, + S_NUMBER, S_COLON, + S_SHIFT_LEFT, S_SHIFT_RIGHT, + S_SLASH, S_BLOCK_COMMENT, + S_UUID } state = S_INITIAL; + std::string param; + int c = 0; + int old_c; + + m_line_no_start = m_line_no_end; + while (1) { + old_c = c; + c = std::cin.get(); + + if (std::cin.eof()) + return Token(state == S_INITIAL ? Token::T_EOF : Token::T_LEX_ERROR); + + if (c == '\n') + ++m_line_no_end; + + switch (state) { + case S_INITIAL: + if (isalpha(c)) { + param += c; + state = m_accept_uuids ? S_UUID : S_IDENTIFIER; + } else if (isdigit(c)) { + param += c; + state = m_accept_uuids ? S_UUID : S_NUMBER; + } + switch (c) { + case '[': + return Token(Token::T_OPEN_BRACKET); + case ']': + return Token(Token::T_CLOSE_BRACKET); + case '{': + return Token(Token::T_OPEN_BRACE); + case '}': + return Token(Token::T_CLOSE_BRACE); + case '(': + return Token(Token::T_OPEN_PARENTHESES); + case ')': + return Token(Token::T_CLOSE_PARENTHESES); + case ',': + return Token(Token::T_COMMA); + case '=': + return Token(Token::T_ASSIGN); + case ';': + return Token(Token::T_SEMICOLON); + case '|': + return Token(Token::T_PIPE); + case '^': + return Token(Token::T_CARET); + case '&': + return Token(Token::T_AMPERSAND); + case '+': + return Token(Token::T_PLUS); + case '-': + return Token(Token::T_MINUS); + case '%': + return Token(Token::T_PERCENT); + case '~': + return Token(Token::T_TILDE); + case '#': + return Token(Token::T_HASH); + case '"': + return Token(Token::T_QUOTE); + case '.': + return Token(Token::T_DOT); + case ':': + state = S_COLON; + break; + case '<': + state = S_SHIFT_LEFT; + break; + case '>': + state = S_SHIFT_RIGHT; + break; + case '/': + state = S_SLASH; + break; + case ' ': + case '\t': + break; + case '*': + return Token(Token::T_LEX_ERROR); + } + break; + + case S_IDENTIFIER: + if (!isalnum(c) && c != '-' && c != '_') { + std::cin.unget(); + std::map<std::string, Token::TokenType>::iterator it; + it = s_keywords.find(param); + if (it != s_keywords.end()) + return Token(it->second); + return Token(Token::T_IDENTIFIER, param); + } + param += c; + break; + + case S_NUMBER: + if (!isdigit(c)) { + std::cin.unget(); + return Token(Token::T_NUMBER, param); + } + param += c; + break; + + case S_UUID: { + if (!isdigit(c) && c != '-' && (c > 'f' || c < 'a')) { + std::cin.unget(); + return Token(param.size() != 36 ? Token::T_LEX_ERROR : Token::T_UUIDVAL, param); + } + const int len = param.size(); + if (c != '-' && len >= 36 && + (len == 8 || len == 13 || len == 18 || len == 23)) + { + return Token(Token::T_LEX_ERROR); + } + param += c; + break; + } + + case S_COLON: + if (c == ':') + return Token(Token::T_DOUBLECOLON); + std::cin.unget(); + return Token(Token::T_COLON); + + case S_SHIFT_LEFT: + if (c == '<') + return Token(Token::T_SHIFT_LEFT); + std::cin.unget(); + return Token(Token::T_LESS); + + case S_SHIFT_RIGHT: + if (c == '>') + return Token(Token::T_SHIFT_RIGHT); + std::cin.unget(); + return Token(Token::T_GREATER); + + case S_SLASH: + if (c == '/') { + while (c != '\n' && !std::cin.eof()) + c = std::cin.get(); + state = S_INITIAL; + } else if (c == '*') { + state = S_BLOCK_COMMENT; + } + break; + + case S_BLOCK_COMMENT: + if (old_c == '*' && c == '/') + state = S_INITIAL; + break; + } + } + return Token(Token::T_LEX_ERROR); +} diff --git a/generator/scanner.h b/generator/scanner.h new file mode 100644 index 0000000..d6150a2 --- /dev/null +++ b/generator/scanner.h @@ -0,0 +1,50 @@ +/* ***** BEGIN LICENSE BLOCK ***** +* Copyright (C) 2012, Peter Hatina <phatina@redhat.com> +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see <http://www.gnu.org/licenses/>. +* ***** END LICENSE BLOCK ***** */ + +#ifndef SCANNER_H +#define SCANNER_H + +#include <iostream> +#include <string> +#include <map> +#include <queue> +#include "token.h" + +class Scanner +{ +public: + Scanner(); + ~Scanner(); + + Token getNextToken(); + int getLineNo() const { return m_line_no_start; } + void pushToken(Token &token) { m_token_queue.push(token); } + void setAcceptUuids(bool accept = true) { m_accept_uuids = accept; } + +private: + static void initKeywords(); + +private: + Token m_token; + int m_line_no_start; + int m_line_no_end; + bool m_accept_uuids; + std::queue<Token> m_token_queue; + static std::map<std::string, Token::TokenType> s_keywords; +}; + +#endif // SCANNER_H diff --git a/generator/token.h b/generator/token.h new file mode 100644 index 0000000..3676449 --- /dev/null +++ b/generator/token.h @@ -0,0 +1,77 @@ +/* ***** BEGIN LICENSE BLOCK ***** +* Copyright (C) 2012, Peter Hatina <phatina@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 |