diff --git a/CMakeLists.txt b/CMakeLists.txt index 2816408..43c1cde 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,6 +7,7 @@ project(Serene LANGUAGES CXX) set(CMAKE_EXPORT_COMPILE_COMMANDS 1) + # Only do these if this is the main project, and not if it is included through add_subdirectory if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME) @@ -34,6 +35,8 @@ if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME) set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") set(MemoryCheckCommand "valgrind") + add_compile_options(-fno-rtti) + configure_file(${INCLUDE_DIR}/config.h.in config.h) # Let's nicely support folders in IDEs @@ -93,4 +96,5 @@ if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME) add_subdirectory(tests) endif() + endif() diff --git a/bin/serene.cpp b/bin/serene.cpp index ea83d41..87bb0ad 100644 --- a/bin/serene.cpp +++ b/bin/serene.cpp @@ -24,6 +24,7 @@ #include "serene/serene.hpp" #include "serene/compiler.hpp" +#include "serene/reader.hpp" #include #include @@ -33,27 +34,31 @@ using namespace serene; namespace cl = llvm::cl; namespace { -enum Action { None, DumpAST } +enum Action { None, DumpAST }; } +static cl::opt inputFile(cl::Positional, + cl::desc("The Serene file to compile"), + cl::init("-"), + cl::value_desc("filename")); + static cl::opt emitAction("emit", cl::desc("Select what to dump."), cl::values(clEnumValN(DumpAST, "ast", "Output the AST only"))); - - int main(int argc, char *argv[]) { cl::ParseCommandLineOptions(argc, argv, "Serene compiler \n"); - switch (emitAction) { - case Action::DumpAST: - Reader r = Reader(input); - r->dump(); + case Action::DumpAST: { + FileReader *r = new FileReader(inputFile); + r->dumpAST(); + return 0; + } + default: { + llvm::errs() << "No action specified. TODO: Print out help here"; + } } - string input_file(argv[1]); - Compiler c; - c.compile(input_file); - return 0; + return 1; } diff --git a/examples/hello_world/hello_world.srn b/examples/hello_world/hello_world.srn new file mode 100644 index 0000000..1c64dee --- /dev/null +++ b/examples/hello_world/hello_world.srn @@ -0,0 +1 @@ +(println asd) \ No newline at end of file diff --git a/include/serene/error.hpp b/include/serene/error.hpp new file mode 100644 index 0000000..467a4e0 --- /dev/null +++ b/include/serene/error.hpp @@ -0,0 +1,52 @@ +/** + * Serene programming language. + * + * Copyright (c) 2020 Sameer Rahmani + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef ERROR_H +#define ERROR_H + +#include "serene/compiler.hpp" +#include "serene/expr.hpp" +#include "serene/llvm/IR/Value.h" +#include "serene/state.hpp" +#include + +namespace serene { +class Error : public AExpr { + const std::string msg; + +public: + Error(const std::string &msg) : msg(msg) {} + virtual ~Error(); + + const std::string &message() const; + + ExprId id() const override { return error; } + std::string string_repr() const override; + std::string dumpAST() const override; + + llvm::Value *codegen(Compiler &compiler, State &state) override; +}; +} // namespace serene + +#endif diff --git a/include/serene/expr.hpp b/include/serene/expr.hpp index 233362f..0dff9c4 100644 --- a/include/serene/expr.hpp +++ b/include/serene/expr.hpp @@ -39,7 +39,8 @@ namespace serene { -enum ExprId : unsigned char { aexpr = 0, symbol, list, def }; +// TODO: Rename this enum and move it to a namespace +enum ExprId : unsigned char { aexpr = 0, symbol, list, def, error }; class AExpr { public: @@ -48,6 +49,7 @@ public: virtual ExprId id() const = 0; virtual std::string string_repr() const = 0; virtual llvm::Value *codegen(Compiler &compiler, State &state) = 0; + virtual std::string dumpAST() const = 0; }; using ast_node = std::shared_ptr; diff --git a/include/serene/list.hpp b/include/serene/list.hpp index 546c866..11001ca 100644 --- a/include/serene/list.hpp +++ b/include/serene/list.hpp @@ -38,6 +38,7 @@ class List : public AExpr { public: ExprId id() const override { return list; } + std::string dumpAST() const override; std::string string_repr() const override; size_t length() const; diff --git a/include/serene/reader.hpp b/include/serene/reader.hpp index 90b1b77..a2d3ef7 100644 --- a/include/serene/reader.hpp +++ b/include/serene/reader.hpp @@ -72,11 +72,33 @@ private: ast_node read_expr(); public: - Reader(const std::string &); + Reader() : input_stream(""){}; + Reader(const std::string); + + void setInput(const std::string); + ast_tree &read(); + // Dumps the AST data to stdout + void dumpAST(); + ~Reader(); }; + +class FileReader { + std::string file; + Reader *reader; + +public: + FileReader(const std::string file_name) + : file(file_name), reader(new Reader()) {} + // Dumps the AST data to stdout + void dumpAST(); + + ast_tree &read(); + + ~FileReader(); +}; } // namespace serene #endif diff --git a/include/serene/special_forms/def.hpp b/include/serene/special_forms/def.hpp index 048ffb1..7be3ff0 100644 --- a/include/serene/special_forms/def.hpp +++ b/include/serene/special_forms/def.hpp @@ -51,6 +51,7 @@ public: ExprId id() const override { return def; } Def(serene::Symbol *symbol_, AExpr *value_); + std::string dumpAST() const override; std::string string_repr() const override; llvm::Value *codegen(Compiler &compiler, State &state) override; static ast_node make(Compiler &compiler, State &state, const List *args); diff --git a/include/serene/symbol.hpp b/include/serene/symbol.hpp index e9e1024..de64f0e 100644 --- a/include/serene/symbol.hpp +++ b/include/serene/symbol.hpp @@ -43,6 +43,7 @@ public: ExprId id() const override { return symbol; } std::string string_repr() const override; + std::string dumpAST() const override; llvm::Value *codegen(Compiler &compiler, State &state) override; }; diff --git a/src/error.cpp b/src/error.cpp new file mode 100644 index 0000000..14d00ce --- /dev/null +++ b/src/error.cpp @@ -0,0 +1,49 @@ +/** + * Serene programming language. + * + * Copyright (c) 2020 Sameer Rahmani + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "serene/error.hpp" +#include "serene/compiler.hpp" +#include "serene/expr.hpp" +#include "serene/llvm/IR/Value.h" +#include "serene/namespace.hpp" +#include "serene/state.hpp" +#include +#include +#include +#include +#include + +using namespace std; +using namespace llvm; + +namespace serene { + +string Error::string_repr() const { return fmt::format("Error: {}", msg); } + +string Error::dumpAST() const { return fmt::format("", this->msg); } + +Value *Error::codegen(Compiler &compiler, State &state) { return nullptr; } + +Error::~Error() { EXPR_LOG("Destroying Error"); } +} // namespace serene diff --git a/src/list.cpp b/src/list.cpp index db3f798..b98e4ee 100644 --- a/src/list.cpp +++ b/src/list.cpp @@ -60,35 +60,17 @@ std::string List::string_repr() const { return fmt::format("({})", s); } +std::string List::dumpAST() const { + std::string s; + + for (auto &n : nodes_) { + s = fmt::format("{} {}", s, n->dumpAST()); + } + + return fmt::format("", s); +} + inline size_t List::length() const { return nodes_.size(); } -Value *List::codegen(Compiler &compiler, State &state) { - if (length() == 0) { - compiler.log_error("Can't eveluate empty list."); - return nullptr; - } - - auto def_ptr = at(0).value_or(nullptr); - auto name_ptr = at(1).value_or(nullptr); - auto body_ptr = at(2).value_or(nullptr); - - if (def_ptr && def_ptr->id() == symbol && - static_cast(def_ptr.get())->name() == "def") { - - if (!name_ptr && def_ptr->id() != symbol) { - return compiler.log_error("First argument of 'def' has to be a symbol."); - } - - if (!body_ptr) { - return compiler.log_error("'def' needs 3 arguments, two has been given."); - } - - special_forms::Def def(static_cast(name_ptr.get()), - body_ptr.get()); - return def.codegen(compiler, state); - } - - EXPR_LOG("Not implemented in list."); - return nullptr; -} +Value *List::codegen(Compiler &compiler, State &state) { return nullptr; } } // namespace serene diff --git a/src/reader.cpp b/src/reader.cpp index ddb6878..e1c81fd 100644 --- a/src/reader.cpp +++ b/src/reader.cpp @@ -23,9 +23,11 @@ */ #include "serene/reader.hpp" +#include "serene/error.hpp" #include "serene/list.hpp" #include "serene/symbol.hpp" #include +#include #include #include #include @@ -33,7 +35,9 @@ using namespace std; namespace serene { -Reader::Reader(const string &input) { +Reader::Reader(const string input) { this->setInput(input); }; + +void Reader::setInput(const string input) { input_stream.write(input.c_str(), input.size()); }; @@ -153,5 +157,48 @@ ast_tree &Reader::read() { } return this->ast; -}; +} + +void Reader::dumpAST() { + ast_tree &ast = this->read(); + std::string result = ""; + for (auto &node : ast) { + result = fmt::format("{0} {1}", result, node->dumpAST()); + } +} + +ast_tree &FileReader::read() { + std::string buffer; + + std::ifstream f(file.c_str()); + + if (f) { + f.seekg(0, std::ios::end); + buffer.resize(f.tellg()); + f.seekg(0); + f.read(buffer.data(), buffer.size()); + f.close(); + + reader->setInput(buffer); + + return reader->read(); + } + + throw ReadError((char *)fmt::format("Can't find file '{}'", file).c_str()); +} + +void FileReader::dumpAST() { + ast_tree &ast = this->read(); + std::string result = ""; + for (auto &node : ast) { + result = fmt::format("{0} {1}", result, node->dumpAST()); + } + cout << result << endl; +} + +FileReader::~FileReader() { + delete this->reader; + READER_LOG("Destroying the file reader"); +} + } // namespace serene diff --git a/src/special_forms/def.cpp b/src/special_forms/def.cpp index 572f12d..0c16680 100644 --- a/src/special_forms/def.cpp +++ b/src/special_forms/def.cpp @@ -72,6 +72,8 @@ string Def::string_repr() const { return "Def"; } +string Def::dumpAST() const { return ""; } + Value *Def::codegen(Compiler &compiler, State &state) { state.set_in_current_ns_root_scope(m_sym->name(), m_value->codegen(compiler, state)); diff --git a/src/symbol.cpp b/src/symbol.cpp index 4b7181a..8a169fa 100644 --- a/src/symbol.cpp +++ b/src/symbol.cpp @@ -41,6 +41,10 @@ namespace serene { string Symbol::string_repr() const { return name_; } +string Symbol::dumpAST() const { + return fmt::format("", this->name()); +} + const string &Symbol::name() const { return name_; } Symbol::Symbol(const string &name) : name_(name) {}