From e09823bfe8d5ae925128a8a81352d01595b7c888 Mon Sep 17 00:00:00 2001 From: Chris Cromer Date: Tue, 7 Feb 2023 21:26:25 -0300 Subject: [PATCH 1/6] ignore .obk files --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index f15b582..2a371bb 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ .vscode builddir *.kb +*.obk From 4a7cf9ec2edde6e95072c671160c5e4d74314f11 Mon Sep 17 00:00:00 2001 From: Chris Cromer Date: Wed, 8 Feb 2023 09:36:43 -0300 Subject: [PATCH 2/6] add command line options --- src/meson.build | 7 +++++ src/obelisk.cpp | 71 ++++++++++++++++++++++++++++++++---------------- src/obelisk.h | 33 +++++++++++++++++++++- src/version.h.in | 8 ++++++ 4 files changed, 95 insertions(+), 24 deletions(-) create mode 100644 src/version.h.in diff --git a/src/meson.build b/src/meson.build index 0f4d7b2..81adf96 100644 --- a/src/meson.build +++ b/src/meson.build @@ -1,3 +1,10 @@ +conf_data = configuration_data() +conf_data.set('version', meson.project_version()) +configure_file(input : 'version.h.in', + output : 'version.h', + configuration : conf_data +) + obelisk_sources = files( 'obelisk.cpp', 'lexer.cpp', diff --git a/src/obelisk.cpp b/src/obelisk.cpp index 8a110df..cb0cb54 100644 --- a/src/obelisk.cpp +++ b/src/obelisk.cpp @@ -2,6 +2,7 @@ #include "lexer.h" #include "obelisk.h" #include "parser.h" +#include "version.h" #include #include @@ -73,38 +74,62 @@ static int obelisk::mainLoop() return EXIT_SUCCESS; } +void obelisk::showUsage() +{ + std::cout << obelisk::usageMessage << std::endl; +} + int main(int argc, char** argv) { - for (int i = 1; i < argc; i++) + std::vector sourceFiles; + std::string kbFile = "obelisk.kb"; + + while (true) { - std::cout << argv[i] << std::endl; + int option_index = 0; + switch (getopt_long(argc, argv, "k:hv", obelisk::long_options, &option_index)) + { + case 'k' : + kbFile = std::string(optarg); + continue; + case 'h' : + obelisk::showUsage(); + return EXIT_SUCCESS; + break; + case 'v' : + std::cout << "obelisk " << obelisk::version << std::endl; + return EXIT_SUCCESS; + break; + default : + obelisk::showUsage(); + return EXIT_FAILURE; + break; + + case -1 : + break; + } + + break; } - // This can be used to store a double as 2 floats in the database, then restore it back to a double. - // Inspired by Godot's double precision on the GPU to render large worlds. - /*try + if (optind < argc) { - float first; - float second; - double var = 0.123456789012345; - - obelisk::KnowledgeBase* kb = new obelisk::KnowledgeBase("cromer.kb"); - kb->getFloat(first, second, var); - std::cout << std::setprecision(std::numeric_limits::digits10) - << "Double: " << var << std::endl - << "First: " << first << std::endl - << "Second: " << second << std::endl; - var = 0.0; - kb->getDouble(var, first, second); - std::cout << std::setprecision(std::numeric_limits::digits10) - << "Double: " << var << std::endl - << "First: " << first << std::endl - << "Second: " << second << std::endl; + while (optind < argc) + { + sourceFiles.push_back(argv[optind++]); + } } - catch (obelisk::KnowledgeBaseException& exception) + + if (sourceFiles.size() == 0) { + obelisk::showUsage(); return EXIT_FAILURE; - }*/ + } + + std::cout << sourceFiles[0] << std::endl; + std::cout << kbFile << std::endl; + + return 0; return obelisk::mainLoop(); } diff --git a/src/obelisk.h b/src/obelisk.h index 588182e..ec0d644 100644 --- a/src/obelisk.h +++ b/src/obelisk.h @@ -1,10 +1,41 @@ +#include + /** - * @brief The obelisk namespace contains everything needed to compile obelisk + * @brief The obelisk namespace contains everything needed to compile obelisk. * code. * */ namespace obelisk { + /** + * @brief The usage messsage displayed during help or incorrect usage. + * + */ + std::string usageMessage = R"(Usage: obelisk [OPTION]... [FILE]... +Compile the obelisk source FILE(s) into knoweldge base and library. + +Options: + -h, --help shows this help/usage message + -k, --kb=FILENAME output knowldege base filename + -v, --version shows the version of obelisk)"; + + /** + * @brief The command line arguments that obelisk accepts. + * + */ + static struct option long_options[] = { + {"help", no_argument, 0, 'h'}, + {"kb", required_argument, 0, 'k'}, + {"version", no_argument, 0, 'v'}, + {0, 0, 0, 0 } + }; + + /** + * @brief Prints out the usage of obelisk to the stdin. + * + */ + static void showUsage(); + /** * @brief This is the main loop for obelisk. * This loop handles lexing and parsing of obelisk source code. diff --git a/src/version.h.in b/src/version.h.in new file mode 100644 index 0000000..7e82845 --- /dev/null +++ b/src/version.h.in @@ -0,0 +1,8 @@ +namespace obelisk +{ + /** + * @brief The current version of obelisk. + * + */ + std::string version = "@version@"; +} // namespace obelisk From b9a82a8e3c2c028e98483cd84ec89cd1be2b4181 Mon Sep 17 00:00:00 2001 From: Chris Cromer Date: Wed, 8 Feb 2023 09:38:24 -0300 Subject: [PATCH 3/6] make documentation building optional --- meson.build | 21 +++++++++++---------- meson_options.txt | 4 ++++ 2 files changed, 15 insertions(+), 10 deletions(-) create mode 100644 meson_options.txt diff --git a/meson.build b/meson.build index dae4ed9..b8b534f 100644 --- a/meson.build +++ b/meson.build @@ -11,19 +11,20 @@ project('obelisk', llvm = dependency('llvm', version: '>= 14.0.0', modules : ['core', 'target', 'mcjit', 'nativecodegen'], required : true, method: 'config-tool') -doxygen = find_program('doxygen', required : false) - cdata = configuration_data() cdata.set('VERSION', meson.project_version()) -if find_program('dot', required : false).found() - cdata.set('HAVE_DOT', 'YES') -else - cdata.set('HAVE_DOT', 'NO') -endif - -if doxygen.found() - subdir('doc') +docs_enabled = get_option('docs') +if docs_enabled + doxygen = find_program('doxygen', required : false) + if doxygen.found() + if find_program('dot', required : false).found() + cdata.set('HAVE_DOT', 'YES') + else + cdata.set('HAVE_DOT', 'NO') + endif + subdir('doc') + endif endif subdir('src') diff --git a/meson_options.txt b/meson_options.txt new file mode 100644 index 0000000..a7ab105 --- /dev/null +++ b/meson_options.txt @@ -0,0 +1,4 @@ +option('docs', + type: 'boolean', + value: true, + description: 'Build documentation for obelisk') From 691b7963e3fbb204d32568199a6c7c3c25890c9c Mon Sep 17 00:00:00 2001 From: Chris Cromer Date: Wed, 8 Feb 2023 09:39:10 -0300 Subject: [PATCH 4/6] fix style --- meson_options.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/meson_options.txt b/meson_options.txt index a7ab105..4dff45a 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -1,4 +1,5 @@ option('docs', type: 'boolean', value: true, - description: 'Build documentation for obelisk') + description: 'Build documentation for obelisk' +) From e77fb73cd0ab693adf1de2e97f03d2de549af999 Mon Sep 17 00:00:00 2001 From: Chris Cromer Date: Wed, 8 Feb 2023 10:17:58 -0300 Subject: [PATCH 5/6] use kb passed in from args --- src/obelisk.cpp | 6 +++--- src/obelisk.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/obelisk.cpp b/src/obelisk.cpp index cb0cb54..d648d1b 100644 --- a/src/obelisk.cpp +++ b/src/obelisk.cpp @@ -9,14 +9,14 @@ #include #include -static int obelisk::mainLoop() +static int obelisk::mainLoop(const std::vector& sourceFiles, const std::string& kbFile) { auto parser = std::unique_ptr {new obelisk::Parser()}; std::unique_ptr kb; try { - kb = std::unique_ptr {new obelisk::KnowledgeBase("cromer.kb")}; + kb = std::unique_ptr {new obelisk::KnowledgeBase(kbFile.c_str())}; } catch (obelisk::KnowledgeBaseException& exception) { @@ -131,5 +131,5 @@ int main(int argc, char** argv) return 0; - return obelisk::mainLoop(); + return obelisk::mainLoop(sourceFiles, kbFile); } diff --git a/src/obelisk.h b/src/obelisk.h index ec0d644..514ba1b 100644 --- a/src/obelisk.h +++ b/src/obelisk.h @@ -42,5 +42,5 @@ Options: * * @return int Returns EXIT_SUCCESS or EXIT_FAILURE. */ - static int mainLoop(); + static int mainLoop(const std::vector &sourceFiles, const std::string &kbFile); } // namespace obelisk From 92ec34a00040b50c959682e4d2dbb2a82e8d7b9d Mon Sep 17 00:00:00 2001 From: Chris Cromer Date: Mon, 13 Feb 2023 23:03:31 -0300 Subject: [PATCH 6/6] read obelisk source files and compile the knowledge base --- src/lexer.cpp | 29 +++++++++++++++++++------- src/lexer.h | 22 ++++++++++++++++++- src/models/fact.cpp | 2 +- src/obelisk.cpp | 51 ++++++++++++++++++++++++++++++++------------- src/obelisk.h | 2 +- src/parser.cpp | 13 ++++++------ src/parser.h | 12 +++++++---- 7 files changed, 95 insertions(+), 36 deletions(-) diff --git a/src/lexer.cpp b/src/lexer.cpp index 5627689..32509b1 100644 --- a/src/lexer.cpp +++ b/src/lexer.cpp @@ -2,20 +2,33 @@ #include +obelisk::Lexer::Lexer(const std::string& sourceFile) +{ + fileStream_.open(sourceFile, std::ifstream::in); + if (!fileStream_) + { + throw obelisk::LexerException("could not open source file " + sourceFile); + } +} + +obelisk::Lexer::~Lexer() +{ + fileStream_.close(); + fileStream_.clear(); +} + int obelisk::Lexer::getToken() { - static int lastChar = ' '; - while (isspace(lastChar)) { - lastChar = std::getc(stdin); + lastChar = fileStream_.get(); } if (isalpha(lastChar)) { eraseIdentifier(); appendIdentifier(lastChar); - while (isalnum((lastChar = getchar()))) + while (isalnum((lastChar = fileStream_.get()))) { appendIdentifier(lastChar); } @@ -63,7 +76,7 @@ int obelisk::Lexer::getToken() firstPeriod = true; } numberStr += lastChar; - lastChar = getchar(); + lastChar = fileStream_.get(); } while (isdigit(lastChar) || lastChar == '.'); @@ -83,7 +96,7 @@ int obelisk::Lexer::getToken() } else if (lastChar == '/') { - lastChar = getchar(); + lastChar = fileStream_.get(); if (lastChar == '/') { commentLine(&lastChar); @@ -101,7 +114,7 @@ int obelisk::Lexer::getToken() } int thisChar = lastChar; - lastChar = getchar(); + lastChar = fileStream_.get(); return thisChar; } @@ -109,7 +122,7 @@ void obelisk::Lexer::commentLine(int* lastChar) { do { - *lastChar = getchar(); + *lastChar = fileStream_.get(); } while (*lastChar != EOF && *lastChar != '\n' && *lastChar != '\r'); } diff --git a/src/lexer.h b/src/lexer.h index 83ec706..4562611 100644 --- a/src/lexer.h +++ b/src/lexer.h @@ -1,6 +1,7 @@ #ifndef OBELISK_LEXER_H #define OBELISK_LEXER_H +#include #include namespace obelisk @@ -12,6 +13,12 @@ namespace obelisk class Lexer { private: + int lastChar = ' '; + /** + * @brief The stream of the source file being read. + * + */ + std::ifstream fileStream_; /** * @brief The last found identifier. * @@ -21,7 +28,7 @@ namespace obelisk * @brief The last found number. * */ - double numberValue_; + double numberValue_ = 0; /** * @brief Set the identifier. @@ -113,6 +120,19 @@ namespace obelisk kTokenString = -9 }; + /** + * @brief Construct a new Lexer object. + * + * @param[in] sourceFile The source file to read. + */ + Lexer(const std::string& sourceFile); + + /** + * @brief Destroy the Lexer object. + * + */ + ~Lexer(); + /** * @brief Gets the next token in the source code. * diff --git a/src/models/fact.cpp b/src/models/fact.cpp index d517286..8011bcc 100644 --- a/src/models/fact.cpp +++ b/src/models/fact.cpp @@ -7,8 +7,8 @@ const char* obelisk::Fact::createTable() CREATE TABLE "fact" ( "id" INTEGER NOT NULL UNIQUE, "left_entity" INTEGER NOT NULL, - "right_entity" INTEGER NOT NULL, "verb" INTEGER NOT NULL, + "right_entity" INTEGER NOT NULL, "is_true" INTEGER NOT NULL DEFAULT 0, PRIMARY KEY("id" AUTOINCREMENT), UNIQUE("left_entity", "right_entity", "verb") diff --git a/src/obelisk.cpp b/src/obelisk.cpp index d648d1b..8d64044 100644 --- a/src/obelisk.cpp +++ b/src/obelisk.cpp @@ -9,9 +9,8 @@ #include #include -static int obelisk::mainLoop(const std::vector& sourceFiles, const std::string& kbFile) +int obelisk::mainLoop(const std::vector& sourceFiles, const std::string& kbFile) { - auto parser = std::unique_ptr {new obelisk::Parser()}; std::unique_ptr kb; try @@ -24,8 +23,20 @@ static int obelisk::mainLoop(const std::vector& sourceFiles, const return EXIT_FAILURE; } - // Prime the first token. - fprintf(stderr, "ready> "); + size_t file = 0; + std::shared_ptr lexer; + try + { + lexer = std::shared_ptr {new obelisk::Lexer(sourceFiles[file++])}; + } + catch (obelisk::LexerException& exception) + { + std::cout << exception.what() << std::endl; + return EXIT_FAILURE; + } + auto parser = std::unique_ptr {new obelisk::Parser(lexer)}; + + // prime the first token try { parser->getNextToken(); @@ -38,14 +49,29 @@ static int obelisk::mainLoop(const std::vector& sourceFiles, const while (true) { - fprintf(stderr, "ready> "); switch (parser->getCurrentToken()) { case obelisk::Lexer::kTokenEof : - return EXIT_SUCCESS; - case ';' : // ignore top-level semicolons. - std::cout << "Identifier: " << parser->getLexer()->getIdentifier() << std::endl; - std::cout << "Num: " << parser->getLexer()->getNumberValue() << std::endl; + // end of source file found, create a new lexer and pass it to the parser to use + if (file >= sourceFiles.size()) + { + return EXIT_SUCCESS; + } + try + { + lexer = std::shared_ptr {new obelisk::Lexer(sourceFiles[file++])}; + parser->setLexer(lexer); + // prime the first token in the parser + parser->getNextToken(); + } + catch (obelisk::LexerException& exception) + { + std::cout << exception.what() << std::endl; + return EXIT_FAILURE; + } + break; + case ';' : + // semicolon found, the end of a statement try { parser->getNextToken(); @@ -74,7 +100,7 @@ static int obelisk::mainLoop(const std::vector& sourceFiles, const return EXIT_SUCCESS; } -void obelisk::showUsage() +static void obelisk::showUsage() { std::cout << obelisk::usageMessage << std::endl; } @@ -126,10 +152,5 @@ int main(int argc, char** argv) return EXIT_FAILURE; } - std::cout << sourceFiles[0] << std::endl; - std::cout << kbFile << std::endl; - - return 0; - return obelisk::mainLoop(sourceFiles, kbFile); } diff --git a/src/obelisk.h b/src/obelisk.h index 514ba1b..e3abfcb 100644 --- a/src/obelisk.h +++ b/src/obelisk.h @@ -42,5 +42,5 @@ Options: * * @return int Returns EXIT_SUCCESS or EXIT_FAILURE. */ - static int mainLoop(const std::vector &sourceFiles, const std::string &kbFile); + int mainLoop(const std::vector &sourceFiles, const std::string &kbFile); } // namespace obelisk diff --git a/src/parser.cpp b/src/parser.cpp index 41431db..c420a70 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -11,16 +11,17 @@ #include #include -obelisk::Parser::Parser() -{ - lexer_ = std::unique_ptr {new obelisk::Lexer()}; -} - -std::unique_ptr& obelisk::Parser::getLexer() +std::shared_ptr obelisk::Parser::getLexer() { return lexer_; } +void obelisk::Parser::setLexer(std::shared_ptr lexer) +{ + lexer_ = lexer; + currentToken_ = 0; +} + int obelisk::Parser::getNextToken() { try diff --git a/src/parser.h b/src/parser.h index 00aa208..19b4a8b 100644 --- a/src/parser.h +++ b/src/parser.h @@ -15,8 +15,8 @@ namespace obelisk class Parser { private: - std::unique_ptr lexer_; - int currentToken_; + std::shared_ptr lexer_; + int currentToken_ = 0; void setCurrentToken(int currentToken); @@ -37,9 +37,13 @@ namespace obelisk void parseFact(std::vector& facts); public: - Parser(); + Parser(std::shared_ptr lexer) : + lexer_(lexer) + { + } - std::unique_ptr& getLexer(); + std::shared_ptr getLexer(); + void setLexer(std::shared_ptr lexer); int getCurrentToken();