From 41decb9a671e4d0fbbe12533372435ec6ede2246 Mon Sep 17 00:00:00 2001 From: Kelly Rauchenberger Date: Wed, 9 Mar 2016 23:30:14 -0500 Subject: Started verbly rewrite verbly is intended to be a general use natural language generation library. Here, I'm using it to simply generate random verbs or adjectives. A schema for the sqlite database is provided, and for testing I manually added data. A generator program is being written that will generate a database from WordNet, VerbNet, PropBank, and AGID data. --- CMakeLists.txt | 7 +- furries.cpp | 243 +++++++++++++++----------------------- schema.sql | 38 ++++++ verbly/adjective.h | 21 ++++ verbly/c++14.h | 35 ++++++ verbly/data.h | 201 ++++++++++++++++++++++++++++++++ verbly/token.h | 336 +++++++++++++++++++++++++++++++++++++++++++++++++++++ verbly/verb.h | 67 +++++++++++ verbly/verbly.h | 10 ++ 9 files changed, 809 insertions(+), 149 deletions(-) create mode 100644 schema.sql create mode 100644 verbly/adjective.h create mode 100644 verbly/c++14.h create mode 100644 verbly/data.h create mode 100644 verbly/token.h create mode 100644 verbly/verb.h create mode 100644 verbly/verbly.h diff --git a/CMakeLists.txt b/CMakeLists.txt index a754833..054c972 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,7 +5,10 @@ add_subdirectory(vendor/twitcurl/libtwitcurl) find_package(PkgConfig) pkg_check_modules(YamlCpp yaml-cpp REQUIRED) +pkg_check_modules(sqlite3 sqlite3 REQUIRED) -include_directories(vendor/twitcurl/libtwitcurl) +include_directories(vendor/twitcurl/libtwitcurl ${sqlite3_INCLUDE_DIR}) add_executable(furries furries.cpp) -target_link_libraries(furries ${YamlCpp_LIBRARIES} mysqlclient twitcurl curl) +set_property(TARGET furries PROPERTY CXX_STANDARD 11) +set_property(TARGET furries PROPERTY CXX_STANDARD_REQUIRED ON) +target_link_libraries(furries ${sqlite3_LIBRARIES} ${YamlCpp_LIBRARIES} twitcurl curl) diff --git a/furries.cpp b/furries.cpp index 15c4657..2d0fb23 100644 --- a/furries.cpp +++ b/furries.cpp @@ -1,16 +1,85 @@ #include #include -#include #include #include #include #include +#include "verbly/verbly.h" -int db_error(MYSQL* driver, const char* error) -{ - std::cout << error << ": " << mysql_error(driver) << std::endl; - return 1; -} +class fill_blanks { + private: + verbly::data& database; + + public: + fill_blanks(verbly::data& database) : database(database) + { + + } + + void visit(std::unique_ptr& it) + { + switch (it->token_type()) + { + case verbly::type::utterance: + { + auto& action = *dynamic_cast(it.get()); + for (auto& tkn : action) + { + if (!tkn->complete()) + { + visit(tkn); + + break; + } + } + + break; + } + + case verbly::type::fillin: + { + auto& tkn = *dynamic_cast(it.get()); + switch (tkn.fillin_type()) + { + case verbly::fillin_type::participle_phrase: + { + const verbly::verb& v = database.verbs().random(true).limit(1).run().front(); + /*verbly::utterance_token phrase = verbly::random(v.frames).make_utterance(); + while (std::begin(phrase)->token_type() != verbly::type::verb) + { + phrase.erase(std::begin(phrase)); + } + + *std::begin(phrase) = verbly::verb_token(v).conjugate(verbly::conjugation::present_participle); + *it = phrase;*/ + auto avt = std::make_unique(v); + avt->conjugate(verbly::conjugation::present_participle); + it = std::move(avt); + + break; + } + + case verbly::fillin_type::adjective: + { + const verbly::adjective& adj = database.adjectives().random(true).limit(1).run().front(); + it = std::make_unique(adj.value); + + break; + } + + default: + { + it = std::make_unique("*the reality of the situation*"); + + break; + } + } + + break; + } + } + } +}; int main(int argc, char** argv) { @@ -21,13 +90,7 @@ int main(int argc, char** argv) const char* user = config["user"].as().c_str(); const char* pass = config["pass"].as().c_str(); const char* db = config["db"].as().c_str(); - - MYSQL* driver = mysql_init(NULL); - if (!mysql_real_connect(driver, host, user, pass, db, 0, NULL, 0)) - { - return db_error(driver, "Error connecting to database"); - } - + twitCurl twitter; twitter.getOAuth().setConsumerKey(config["consumer_key"].as()); twitter.getOAuth().setConsumerSecret(config["consumer_secret"].as()); @@ -38,141 +101,29 @@ int main(int argc, char** argv) { std::cout << "Generating tweet" << std::endl; - std::stringstream output; - output << "the furries are "; - - //if (rand() % 2 == 0) - { - // Adverb(s) + Adjective - while (rand() % 4 == 0) - { - const char* getadverb = "SELECT ss1.word FROM wn_pertainym INNER JOIN wn_synset AS ss1 ON wn_pertainym.synset_id_1 = ss1.synset_id AND wn_pertainym.wnum_1 = ss1.w_num INNER JOIN wn_synset AS ss2 ON wn_pertainym.synset_id_2 = ss2.synset_id AND wn_pertainym.wnum_2 = ss2.w_num WHERE ss1.ss_type = 'r' ORDER BY RAND() LIMIT 1"; - if (mysql_query(driver, getadverb)) return db_error(driver, "Query failed"); - MYSQL_RES* getadverb2 = mysql_use_result(driver); if (getadverb2 == NULL) return db_error(driver, "Query failed"); - MYSQL_ROW getadverb3 = mysql_fetch_row(getadverb2); if (getadverb3 == NULL) return db_error(driver, "Query failed"); - output << getadverb3[0] << " "; - mysql_free_result(getadverb2); - } - - const char* getword = "SELECT word FROM wn_synset WHERE ss_type = 'a' OR ss_type = 's' ORDER BY RAND() LIMIT 1"; - if (mysql_query(driver, getword)) return db_error(driver, "Query failed"); - MYSQL_RES* getword2 = mysql_use_result(driver); if (getword2 == NULL) return db_error(driver, "Query failed"); - MYSQL_ROW getword3 = mysql_fetch_row(getword2); if (getword3 == NULL) return db_error(driver, "Query failed"); - output << getword3[0]; - mysql_free_result(getword2); - } /*else { - // Verb phrase - const char* getword = "SELECT word FROM wn_synset WHERE ss_type = 'a ' OR ss_type = 's' ORDER BY RAND() LIMIT 1"; - if (mysql_query(driver, getword)) return db_error(driver, "Query failed"); - MYSQL_RES* getword2 = mysql_use_result(driver); if (getword2 == NULL) return db_error(driver, "Query failed"); - MYSQL_ROW getword3 = mysql_fetch_row(getword2); if (getword3 == NULL) return db_error(driver, "Query failed"); - } + std::vector forms; + forms.push_back({ + new verbly::string_token("the furries are"), + new verbly::fillin_token(verbly::fillin_type::participle_phrase) + }); + forms.push_back({ + new verbly::string_token("the furries are"), + new verbly::fillin_token(verbly::fillin_type::adjective) + }); - - - - if (rand() % 2 == 0) + verbly::data database {"data.sqlite3"}; + fill_blanks yeah {database}; + std::unique_ptr action = std::make_unique(forms[rand() % forms.size()]); + while (!action->complete()) { - std::stringstream ispart; - ispart << "SELECT wn_verb_frame.f_num FROM wn_participle INNER JOIN wn_verb_frame ON wn_verb_frame.synset_id_1 = synset_id_2 AND (wn_verb_frame.w_num = wnum_2 OR wn_verb_frame.w_num = 0) WHERE wn_participle.synset_id_1 = " << wordssid << " AND wn_participle.wnum_1 = " << wordnum; - if (mysql_query(driver, ispart.str().c_str())) return db_error(driver, "Query failed"); - MYSQL_RES* ispart2 = mysql_use_result(driver); if (ispart2 == NULL) return db_error(driver, "Query failed"); - MYSQL_ROW ispart3 = mysql_fetch_row(ispart2); - mysql_free_result(ispart2); - - if (ispart3 != NULL) - { - int frame = atoi(ispart3[0]); - std::cout << "frame " << frame << std::endl; - - if (frame == 4 || frame == 22) - { - // the furries are ----ing *prepositional phrase* - // ex: the furries are laughing over there - } else if (frame == 5) - { - // the furries are ----ing something *adj/n* - // ex: the furries are regarding life inconsequential - } else if (frame == 6 || frame == 7) - { - // the furries are ----ing *adj/n* - // ex: the furries are turning gay - } else if (frame == 8 || frame == 9 || frame == 10 || frame == 11) - { - // the furries are ----ing something/somebody - // ex: the furries are holding hostages - } else if (frame == 12 || frame == 27) - { - // the furries are ----ing to somebody - // ex: the furries are appealing to God - } else if (frame == 13) - { - // the furries are ----ing on something - // ex: the furries are lecturing on heterosexuality - } else if (frame == 14) - { - // the furries are ----ing somebody something - // ex: the furries are reading your mom the menu - } else if (frame == 15) - { - // the furries are ----ing something to somebody - // ex: the furries are pitching a product to Apple - } else if (frame == 16) - { - // the furries are ----ing something from somebody - // ex: the furries are separating your money from you - } else if (frame == 17) - { - // the furries are ----ing somebody with something - // ex: the furries are injecting me with solemnity - } else if (frame == 18) - { - // the furries are ----ing somebody of something - // ex: the furries are depriving me of hope - } else if (frame == 19) - { - // the furries are ----ing something on somebody - // ex: the furries are forcing pervision on us - } else if (frame == 20 || frame == 21) - { - // the furries are ----ing somebody/something *prepositional phrase* - // ex: the furries are leaving us behind - } else if (frame == 24) - { - // the furries are ----ing somebody to INF - // ex: the furries are getting us to eat - } else if (frame == 25) - { - // the furries are ----ing somebody INF - // ex: the furries are making us procreate - } else if (frame == 26) - { - // the furries are ----ing that CLAUSE - // ex: the furries are understaing that life is precious - } else if (frame == 28) - { - // the furries are ----ing to INF - // ex: the furries are beginning to understand - } else if (frame == 29) - { - // the furries are ----ing whether to INF - // ex: the furries are deciding whether to hallucinate - } else if (frame == 30) - { - // the furries are ----ing somebody into V-ing something - // ex: the furries are tempting me into consecrating a magnet - } else if (frame == 31) - { - // the furries are ----ing something with something - // ex: the furries are replacing existence with solidarity - } - } + yeah.visit(action); } - */ - std::string result = output.str(); + std::string result = action->compile(); result.resize(140); - + + std::cout << result << std::endl; + /* std::string replyMsg; if (twitter.statusUpdate(result)) { @@ -181,11 +132,9 @@ int main(int argc, char** argv) } else { twitter.getLastCurlError(replyMsg); std::cout << "Curl error: " << replyMsg << std::endl; - } + }*/ std::cout << "Waiting" << std::endl; - sleep(60 * 60 * 3); + sleep(/*60 * 60 * */ 3); } - - mysql_close(driver); } diff --git a/schema.sql b/schema.sql new file mode 100644 index 0000000..7c1b52c --- /dev/null +++ b/schema.sql @@ -0,0 +1,38 @@ +DROP TABLE IF EXISTS `verbs`; +CREATE TABLE `verbs` ( + `verb_id` INTEGER PRIMARY KEY, + `infinitive` VARCHAR(32) NOT NULL, + `past_tense` VARCHAR(32) NOT NULL, + `past_participle` VARCHAR(32) NOT NULL, + `ing_form` VARCHAR(32) NOT NULL, + `s_form` VARCHAR(32) NOT NULL +); + +DROP TABLE IF EXISTS `groups`; +CREATE TABLE `groups` ( + `group_id` INTEGER PRIMARY KEY, + `parent_id` INTEGER, + FOREIGN KEY (`parent_id`) REFERENCES `groups`(`group_id`) +); + +DROP TABLE IF EXISTS `frames`; +CREATE TABLE `frames` ( + `frame_id` INTEGER PRIMARY KEY, + `group_id` INTEGER NOT NULL, + `data` BLOB NOT NULL, + FOREIGN KEY (`group_id`) REFERENCES `groups`(`group_id`) +); + +DROP TABLE IF EXISTS `verb_groups`; +CREATE TABLE `verb_groups` ( + `verb_id` INTEGER NOT NULL, + `group_id` INTEGER NOT NULL, + FOREIGN KEY (`verb_id`) REFERENCES `verbs`(`verb_id`), + FOREIGN KEY (`group_id`) REFERENCES `groups`(`group_id`) +); + +DROP TABLE IF EXISTS `adjectives`; +CREATE TABLE `adjectives` ( + `adjective_id` INTEGER PRIMARY KEY, + `adjective` VARCHAR(32) NOT NULL +); \ No newline at end of file diff --git a/verbly/adjective.h b/verbly/adjective.h new file mode 100644 index 0000000..58c490e --- /dev/null +++ b/verbly/adjective.h @@ -0,0 +1,21 @@ +#ifndef ADJECTIVE_H_87B3FB75 +#define ADJECTIVE_H_87B3FB75 + +namespace verbly { + + class adjective { + private: + int id; + + public: + std::string value; + + adjective(int id) : id(id) + { + + } + }; + +}; + +#endif /* end of include guard: ADJECTIVE_H_87B3FB75 */ diff --git a/verbly/c++14.h b/verbly/c++14.h new file mode 100644 index 0000000..b3efbe2 --- /dev/null +++ b/verbly/c++14.h @@ -0,0 +1,35 @@ +#include +#include +#include +#include + +namespace std { + template struct _Unique_if { + typedef unique_ptr _Single_object; + }; + + template struct _Unique_if { + typedef unique_ptr _Unknown_bound; + }; + + template struct _Unique_if { + typedef void _Known_bound; + }; + + template + typename _Unique_if::_Single_object + make_unique(Args&&... args) { + return unique_ptr(new T(std::forward(args)...)); + } + + template + typename _Unique_if::_Unknown_bound + make_unique(size_t n) { + typedef typename remove_extent::type U; + return unique_ptr(new U[n]()); + } + + template + typename _Unique_if::_Known_bound + make_unique(Args&&...) = delete; +} diff --git a/verbly/data.h b/verbly/data.h new file mode 100644 index 0000000..2c23c15 --- /dev/null +++ b/verbly/data.h @@ -0,0 +1,201 @@ +#ifndef DATA_H_C4AEC3DD +#define DATA_H_C4AEC3DD + +#include "verb.h" +#include +#include + +namespace verbly { + + class data { + private: + sqlite3* ppdb; + + public: + class verb_query { + public: + const static int unlimited = -1; + + private: + const data& database; + int m_limit = unlimited; + bool m_random = false; + + public: + verb_query(const data& database) : database(database) + { + + } + + verb_query& limit(int m_limit) + { + if ((m_limit > 0) || (m_limit == unlimited)) + { + this->m_limit = m_limit; + } + + return *this; + } + + verb_query& random(bool m_random) + { + this->m_random = m_random; + + return *this; + } + + std::list run() const + { + std::stringstream construct; + construct << "SELECT verb_id, infinitive, past_tense, past_participle, ing_form, s_form FROM verbs"; + + if (m_random) + { + construct << " ORDER BY RANDOM()"; + } + + if (m_limit != unlimited) + { + construct << " LIMIT " << m_limit; + } + + sqlite3_stmt* ppstmt; + std::string query = construct.str(); + if (sqlite3_prepare_v2(database.ppdb, query.c_str(), query.length(), &ppstmt, NULL) != SQLITE_OK) + { + throw std::runtime_error(sqlite3_errmsg(database.ppdb)); + } + + std::list output; + while (sqlite3_step(ppstmt) == SQLITE_ROW) + { + verb tnc {sqlite3_column_int(ppstmt, 0)}; + tnc.infinitive = std::string(reinterpret_cast(sqlite3_column_text(ppstmt, 1))); + tnc.past_tense = std::string(reinterpret_cast(sqlite3_column_text(ppstmt, 2))); + tnc.past_participle = std::string(reinterpret_cast(sqlite3_column_text(ppstmt, 3))); + tnc.ing_form = std::string(reinterpret_cast(sqlite3_column_text(ppstmt, 4))); + tnc.s_form = std::string(reinterpret_cast(sqlite3_column_text(ppstmt, 5))); + + output.push_back(tnc); + } + + sqlite3_finalize(ppstmt); + + return output; + } + + }; + + class adjective_query { + public: + const static int unlimited = -1; + + private: + const data& database; + int m_limit = unlimited; + bool m_random = false; + + public: + adjective_query(const data& database) : database(database) + { + + } + + adjective_query& limit(int m_limit) + { + if ((m_limit > 0) || (m_limit == unlimited)) + { + this->m_limit = m_limit; + } + + return *this; + } + + adjective_query& random(bool m_random) + { + this->m_random = m_random; + + return *this; + } + + std::list run() const + { + std::stringstream construct; + construct << "SELECT adjective_id, adjective FROM adjectives"; + + if (m_random) + { + construct << " ORDER BY RANDOM()"; + } + + if (m_limit != unlimited) + { + construct << " LIMIT " << m_limit; + } + + sqlite3_stmt* ppstmt; + std::string query = construct.str(); + if (sqlite3_prepare_v2(database.ppdb, query.c_str(), query.length(), &ppstmt, NULL) != SQLITE_OK) + { + throw std::runtime_error(sqlite3_errmsg(database.ppdb)); + } + + std::list output; + while (sqlite3_step(ppstmt) == SQLITE_ROW) + { + adjective tnc {sqlite3_column_int(ppstmt, 0)}; + tnc.value = std::string(reinterpret_cast(sqlite3_column_text(ppstmt, 1))); + + output.push_back(tnc); + } + + sqlite3_finalize(ppstmt); + + return output; + } + + }; + + data(std::string datafile) + { + if (sqlite3_open_v2(datafile.c_str(), &ppdb, SQLITE_OPEN_READONLY, NULL) != SQLITE_OK) + { + throw std::invalid_argument(sqlite3_errmsg(ppdb)); + } + } + + data(const data& other) = delete; + data& operator=(const data& other) = delete; + + data(data&& other) + { + ppdb = other.ppdb; + } + + data& operator=(data&& other) + { + ppdb = other.ppdb; + + return *this; + } + + ~data() + { + sqlite3_close_v2(ppdb); + } + + verb_query verbs() const + { + return verb_query(*this); + } + + adjective_query adjectives() const + { + return adjective_query(*this); + } + + }; + +}; + +#endif /* end of include guard: DATA_H_C4AEC3DD */ diff --git a/verbly/token.h b/verbly/token.h new file mode 100644 index 0000000..bbe7c2d --- /dev/null +++ b/verbly/token.h @@ -0,0 +1,336 @@ +#ifndef TOKEN_H_AD62C505 +#define TOKEN_H_AD62C505 + +#include +#include +#include +#include "verb.h" + +namespace verbly { + + enum class type { + verb, + fillin, + string, + utterance + }; + + class selrestr { + }; + + class synrestr { + }; + + enum class fillin_type { + noun_phrase, + participle_phrase, + adjective + }; + + class token { + protected: + // General + type type; + + token(enum type type) : type(type) + { + + } + + public: + enum type token_type() const + { + return type; + } + + virtual bool complete() const = 0; + virtual std::string compile() const = 0; + virtual token* copy() const = 0; + }; + + class verb_token : public token { + private: + // Verb + const verb* m_verb; + conjugation verb_infl = conjugation::infinitive; + + public: + verb_token(const class verb& verb) : token(type::verb), m_verb(&verb) + { + + } + + const class verb& verb() const + { + return *m_verb; + } + + verb_token& conjugate(conjugation infl) + { + verb_infl = infl; + return *this; + } + + bool complete() const + { + return true; + } + + std::string compile() const + { + return m_verb->conjugate(verb_infl); + } + + token* copy() const + { + return new verb_token(*this); + } + }; + + class utterance_token : public token { + private: + // Utterance + std::list> utterance; + + public: + typedef std::list>::iterator iterator; + /*class iterator { + private: + friend class utterance_token; + + std::list>::iterator it; + + public: + iterator(std::list>::iterator it) : it(it) + { + + } + + iterator& operator++() + { + ++it; + return *this; + } + + iterator& operator--() + { + --it; + return *this; + } + + bool operator==(const iterator& other) const + { + return it == other.it; + } + + bool operator!=(const iterator& other) const + { + return it != other.it; + } + + token* operator*() + { + return *it->get(); + } + + token* operator->() + { + return *it->get(); + } + };*/ + + utterance_token(std::initializer_list tkns) : token(type::utterance) + { + for (auto tkn : tkns) + { + utterance.push_back(std::unique_ptr(tkn)); + } + } + + utterance_token(const utterance_token& other) : token(type::utterance) + { + for (auto& tkn : other.utterance) + { + utterance.push_back(std::unique_ptr(tkn->copy())); + } + } + + utterance_token(utterance_token&& other) : token(type::utterance), utterance(std::move(other.utterance)) + { + + } + + utterance_token& operator=(const utterance_token& other) + { + utterance.clear(); + + for (auto& tkn : other.utterance) + { + utterance.push_back(std::unique_ptr(tkn->copy())); + } + + return *this; + } + + utterance_token& operator=(utterance_token&& other) + { + utterance = std::move(other.utterance); + + return *this; + } + + iterator begin() + { + return std::begin(utterance); + } + + iterator end() + { + return std::end(utterance); + } + + const iterator begin() const + { + return std::begin(utterance); + } + + const iterator end() const + { + return std::end(utterance); + } + + void erase(iterator it) + { + utterance.erase(it); + } + + bool complete() const + { + return std::all_of(std::begin(utterance), std::end(utterance), [] (const std::unique_ptr& tkn) { + return tkn->complete(); + }); + } + + std::string compile() const + { + std::stringstream result; + for (auto& t : utterance) + { + if (t->complete()) + { + result << t->compile() << " "; + } else { + return "Could not compile!"; + } + } + + std::string output = result.str(); + if (output != "") + { + output.pop_back(); + } + + return output; + } + + token* copy() const + { + return new utterance_token(*this); + } + }; + + class fillin_token : public token { + private: + // Fillin + std::string m_theme; + fillin_type m_fillin_type; + + public: + fillin_token(fillin_type ft) : token(type::fillin), m_fillin_type(ft) + { + + } + +/* void synrestrs(std::initializer_list ins) + { + m_synrestrs = std::set(ins); + } + + std::set& synrestrs() + { + return m_synrestrs; + } + + void selrestrs(std::initializer_list ins) + { + m_selrestrs = std::set(ins); + } + + std::set& selrestrs() + { + return m_selrestrs; + }*/ + + fillin_token theme(std::string theme) + { + m_theme = theme; + + return *this; + } + + std::string& theme() + { + return m_theme; + } + + fillin_type fillin_type() const + { + return m_fillin_type; + } + + bool complete() const + { + return false; + } + + std::string compile() const + { + return ""; + } + + token* copy() const + { + return new fillin_token(*this); + } + }; + + class string_token : public token { + private: + // String + std::string str; + + public: + string_token(std::string str) : token(type::string), str(str) + { + + } + + bool complete() const + { + return true; + } + + std::string compile() const + { + return str; + } + + token* copy() const + { + return new string_token(*this); + } + }; + +}; + +#endif /* end of include guard: TOKEN_H_AD62C505 */ diff --git a/verbly/verb.h b/verbly/verb.h new file mode 100644 index 0000000..42c8dc2 --- /dev/null +++ b/verbly/verb.h @@ -0,0 +1,67 @@ +#ifndef VERB_H_BCC929AD +#define VERB_H_BCC929AD + +#include + +namespace verbly { + + /*class frame_part { + + }; + + class frame { + private: + std::list content; + std::map::iterator>> predicates; + + public: + frame(std::list content) : content(content) + { + + } + + std::unique_ptr make_utterance() const + { + + } + };*/ + + enum class conjugation { + present_participle, + past_participle, + infinitive + }; + + class verb { + private: + int id; + + public: + verb(int id) : id(id) + { + + } + + std::string infinitive; + std::string past_tense; + std::string past_participle; + std::string ing_form; + std::string s_form; + //std::vector frames; + + std::string conjugate(conjugation infl) const + { + switch (infl) + { + case conjugation::infinitive: return infinitive; + case conjugation::past_participle: return past_participle; + case conjugation::present_participle: return ing_form; + } + } + }; + +}; + +#include "token.h" + +#endif /* end of include guard: VERB_H_BCC929AD */ diff --git a/verbly/verbly.h b/verbly/verbly.h new file mode 100644 index 0000000..139d8f8 --- /dev/null +++ b/verbly/verbly.h @@ -0,0 +1,10 @@ +#ifndef VERBLY_H_5B39CE50 +#define VERBLY_H_5B39CE50 + +#include "c++14.h" +#include "token.h" +#include "verb.h" +#include "adjective.h" +#include "data.h" + +#endif /* end of include guard: VERBLY_H_5B39CE50 */ -- cgit 1.4.1