diff --git a/.gitignore b/.gitignore index d5c9ee0..73a8501 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,10 @@ _deps .clangd .cache/ .vscode/ +CMakeDoxyfile.in +CMakeDoxygenDefaults.cmake +DartConfiguration.tcl +bin/serenec_CXX_cotire.cmake +/config.h + +docs/Doxyfile.docs diff --git a/examples/hello_world/hello_world.srn b/examples/hello_world/hello_world.srn index 7445594..ed81ff9 100644 --- a/examples/hello_world/hello_world.srn +++ b/examples/hello_world/hello_world.srn @@ -1,2 +1,4 @@ (print 4) +(print -43) +(print 3.4) (println asd) diff --git a/include/serene/error.hpp b/include/serene/error.hpp index 2576c6a..c01010c 100644 --- a/include/serene/error.hpp +++ b/include/serene/error.hpp @@ -40,7 +40,7 @@ public: const std::string &message() const; - ExprId id() const override { return error; } + SereneType getType() const override { return SereneType::Error; } std::string string_repr() const override; std::string dumpAST() const override; }; diff --git a/include/serene/expr.hpp b/include/serene/expr.hpp index 6641119..39faa9a 100644 --- a/include/serene/expr.hpp +++ b/include/serene/expr.hpp @@ -37,8 +37,13 @@ namespace serene { -// TODO: Rename this enum and move it to a namespace -enum ExprId : unsigned char { aexpr = 0, symbol, list, def, error }; +enum class SereneType { + Expression, + Symbol, + List, + Error, + Number, +}; class AExpr { public: @@ -46,7 +51,7 @@ public: virtual ~AExpr() = default; - virtual ExprId id() const = 0; + virtual SereneType getType() const = 0; virtual std::string string_repr() const = 0; virtual std::string dumpAST() const = 0; }; diff --git a/include/serene/list.hpp b/include/serene/list.hpp index f26f7b0..19be18c 100644 --- a/include/serene/list.hpp +++ b/include/serene/list.hpp @@ -35,22 +35,24 @@ namespace serene { class List : public AExpr { - std::list nodes_; + std::vector nodes_; public: List(){}; + List(std::vector elements); List(reader::Location start); - ExprId id() const override { return list; } + SereneType getType() const override { return SereneType::List; } std::string dumpAST() const override; std::string string_repr() const override; - size_t length() const; - - void cons(ast_node f); + size_t count() const; void append(ast_node t); + std::unique_ptr from(uint begin); llvm::Optional at(uint index) const; + + static bool classof(const AExpr *); }; std::unique_ptr makeList(reader::Location); diff --git a/include/serene/number.hpp b/include/serene/number.hpp new file mode 100644 index 0000000..3ff98c6 --- /dev/null +++ b/include/serene/number.hpp @@ -0,0 +1,58 @@ +/** + * 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 NUMBER_H +#define NUMBER_H + +#include "serene/expr.hpp" +#include "serene/llvm/IR/Value.h" +#include "serene/reader/location.hpp" +#include "serene/state.hpp" +#include + +namespace serene { +class Number : public AExpr { + const std::string num_; + +public: + bool isNeg; + bool isFloat; + + Number(reader::LocationRange loc, const std::string &, bool, bool); + ~Number(); + + SereneType getType() const override { return SereneType::Number; } + std::string string_repr() const override; + std::string dumpAST() const override; + + int64_t toI64(); + + static bool classof(const AExpr *); +}; + +std::unique_ptr makeNumber(reader::LocationRange, std::string, bool, + bool); +} // namespace serene + +#endif diff --git a/include/serene/reader/reader.hpp b/include/serene/reader/reader.hpp index bb849de..1908921 100644 --- a/include/serene/reader/reader.hpp +++ b/include/serene/reader/reader.hpp @@ -64,16 +64,16 @@ private: std::stringstream input_stream; Location current_location{0, 0, 0}; - char get_char(bool skip_whitespace); - void unget_char(); - bool is_valid_for_identifier(char c); + char getChar(bool skip_whitespace); + void ungetChar(); + bool isValidForIdentifier(char c); // The property to store the ast tree ast_tree ast; - - ast_node read_symbol(); - ast_list_node read_list(); - ast_node read_expr(); + ast_node readSymbol(); + ast_node readNumber(bool); + ast_list_node readList(); + ast_node readExpr(); public: Reader() : input_stream(""){}; diff --git a/include/serene/sir/generator.hpp b/include/serene/sir/generator.hpp index 708734e..a9a287a 100644 --- a/include/serene/sir/generator.hpp +++ b/include/serene/sir/generator.hpp @@ -28,7 +28,10 @@ #include "mlir/IR/BuiltinOps.h" #include "mlir/IR/MLIRContext.h" #include "serene/expr.hpp" +#include "serene/list.hpp" #include "serene/namespace.hpp" +#include "serene/number.hpp" +#include "serene/symbol.hpp" namespace serene { namespace sir { @@ -36,11 +39,16 @@ namespace sir { class Generator { private: ::mlir::OpBuilder builder; + ::serene::Namespace &ns; public: - Generator(mlir::MLIRContext &context) : builder(&context) {} + Generator(mlir::MLIRContext &context, ::serene::Namespace &ns) + : builder(&context), ns(ns) {} - mlir::ModuleOp generate(::serene::Namespace &ns); + mlir::Operation *generateNumber(Number *); + mlir::Operation *generateExpression(AExpr *); + mlir::Operation *generateList(List *); + mlir::ModuleOp generate(); ~Generator(); }; diff --git a/include/serene/symbol.hpp b/include/serene/symbol.hpp index 71cbf22..fa3889a 100644 --- a/include/serene/symbol.hpp +++ b/include/serene/symbol.hpp @@ -41,9 +41,11 @@ public: const std::string &name() const; - ExprId id() const override { return symbol; } + SereneType getType() const override { return SereneType::Symbol; } std::string string_repr() const override; std::string dumpAST() const override; + + static bool classof(const AExpr *); }; std::unique_ptr makeSymbol(reader::LocationRange, std::string); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7ffab84..3f06a34 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,6 +1,11 @@ set(HEADER_LIST "${INCLUDE_DIR}/serene/expr.hpp" "${INCLUDE_DIR}/serene/serene.hpp" + "${INCLUDE_DIR}/serene/number.hpp" + "${INCLUDE_DIR}/serene/symbol.hpp" + "${INCLUDE_DIR}/serene/list.hpp" + "${INCLUDE_DIR}/serene/error.hpp" + "${INCLUDE_DIR}/serene/state.hpp" # Reader "${INCLUDE_DIR}/serene/reader/reader.hpp" @@ -19,6 +24,8 @@ add_library(serene list.cpp namespace.cpp state.cpp + error.cpp + number.cpp # Reader reader/reader.cpp diff --git a/src/list.cpp b/src/list.cpp index 7a16b7d..e36a287 100644 --- a/src/list.cpp +++ b/src/list.cpp @@ -39,18 +39,21 @@ List::List(reader::Location startLoc) { this->location.reset(new reader::LocationRange(startLoc)); } +List::List(std::vector elements) { + auto startLoc = elements[0]->location->start; + auto endLoc = elements[elements.size() - 1]->location->end; + this->location.reset(new reader::LocationRange(startLoc, endLoc)); + this->nodes_ = elements; +} + llvm::Optional List::at(uint index) const { if (index >= nodes_.size()) { return llvm::None; } - auto itr = cbegin(nodes_); - std::advance(itr, index); - return llvm::Optional(*itr); + return llvm::Optional(this->nodes_[index]); } -void List::cons(ast_node node) { nodes_.push_front(std::move(node)); } - void List::append(ast_node node) { nodes_.push_back(std::move(node)); } std::string List::string_repr() const { @@ -76,7 +79,35 @@ std::string List::dumpAST() const { this->location->end.toString(), s); } -inline size_t List::length() const { return nodes_.size(); } +/** + * Return a sub set of elements starting from the `begin` index to the end + * and an empty list otherwise. + */ + +std::unique_ptr List::from(uint begin) { + if (this->count() - begin < 1) { + return makeList(this->location->end); + } + + std::vector::const_iterator first = this->nodes_.begin() + begin; + std::vector::const_iterator last = this->nodes_.end(); + fmt::print("#### {} {} \n", this->nodes_.size(), this->nodes_.max_size()); + fmt::print("MM {}\n", this->string_repr()); + + std::vector newCopy(first, last); + + return std::make_unique(newCopy); +}; + +size_t List::count() const { return nodes_.size(); } + +/** + * `classof` is a enabler static method that belongs to the LLVM RTTI interface + * `llvm::isa`, `llvm::cast` and `llvm::dyn_cast` use this method. + */ +bool List::classof(const AExpr *expr) { + return expr->getType() == SereneType::List; +} /** * Make an empty List in starts at the given location `loc`. diff --git a/src/number.cpp b/src/number.cpp new file mode 100644 index 0000000..39da1fc --- /dev/null +++ b/src/number.cpp @@ -0,0 +1,71 @@ +/** + * 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/number.hpp" +#include "serene/expr.hpp" +#include "serene/llvm/IR/Value.h" +#include "serene/namespace.hpp" +#include "serene/state.hpp" +#include +#include +#include +#include +#include + +namespace serene { + +std::string Number::string_repr() const { return num_; } + +std::string Number::dumpAST() const { + return fmt::format("", + this->location->start.toString(), + this->location->end.toString(), this->num_); +} + +Number::Number(reader::LocationRange loc, const std::string &num, bool isNeg, + bool isFloat) + : num_(num), isNeg(isNeg), isFloat(isFloat) { + this->location.reset(new reader::LocationRange(loc)); +} +int64_t Number::toI64() { + // TODO: Handle float case as well + return std::stoi(num_); +}; + +/** + * `classof` is a enabler static method that belongs to the LLVM RTTI interface + * `llvm::isa`, `llvm::cast` and `llvm::dyn_cast` use this method. + */ +bool Number::classof(const AExpr *expr) { + return expr->getType() == SereneType::Number; +} + +Number::~Number() { EXPR_LOG("Destroying number"); } + +std::unique_ptr makeNumber(reader::LocationRange loc, std::string num, + bool isNeg, bool isFloat) { + return std::make_unique(loc, num, isNeg, isFloat); +} + +} // namespace serene diff --git a/src/reader/reader.cpp b/src/reader/reader.cpp index e3d211e..4aa8e5a 100644 --- a/src/reader/reader.cpp +++ b/src/reader/reader.cpp @@ -25,6 +25,7 @@ #include "serene/reader/reader.hpp" #include "serene/error.hpp" #include "serene/list.hpp" +#include "serene/number.hpp" #include "serene/symbol.hpp" #include "llvm/Support/ErrorOr.h" #include "llvm/Support/MemoryBuffer.h" @@ -55,7 +56,7 @@ Reader::~Reader() { READER_LOG("Destroying the reader"); } * Return the next character in the buffer. * @param skip_whitespace If true it will skip whitespaces and EOL chars */ -char Reader::get_char(bool skip_whitespace) { +char Reader::getChar(bool skip_whitespace) { for (;;) { char c = input_stream.get(); @@ -71,16 +72,32 @@ char Reader::get_char(bool skip_whitespace) { } }; -void Reader::unget_char() { +void Reader::ungetChar() { input_stream.unget(); // The char that we just unget dec_location(current_location, this->current_char == '\n'); }; -bool Reader::is_valid_for_identifier(char c) { +bool Reader::isValidForIdentifier(char c) { switch (c) { - case '!' | '$' | '%' | '&' | '*' | '+' | '-' | '.' | '~' | '/' | ':' | '<' | - '=' | '>' | '?' | '@' | '^' | '_': + case '!': + case '$': + case '%': + case '&': + case '*': + case '+': + case '-': + case '.': + case '~': + case '/': + case ':': + case '<': + case '=': + case '>': + case '?': + case '@': + case '^': + case '_': return true; } @@ -91,31 +108,82 @@ bool Reader::is_valid_for_identifier(char c) { return false; } -ast_node Reader::read_symbol() { +ast_node Reader::readNumber(bool neg) { + std::string number(neg ? "-" : ""); + bool floatNum = false; + bool empty = false; + + LocationRange loc; + char c = getChar(false); + + loc.start = current_location; + + while (c != EOF && + ((!(isspace(c)) && ((c >= '0' && c <= '9') | (c == '.'))))) { + + if (c == '.' && floatNum == true) { + + llvm::errs() << "Two float points in a number?\n"; + // TODO: Return a proper error + return nullptr; + } + + if (c == '.') { + floatNum = true; + } + number += c; + c = getChar(false); + empty = false; + } + + if (!empty) { + ungetChar(); + loc.end = current_location; + return makeNumber(loc, number, neg, floatNum); + } + + return nullptr; +}; + +ast_node Reader::readSymbol() { bool empty = true; - char c = get_char(false); + char c = getChar(false); READER_LOG("Reading symbol"); - if (!this->is_valid_for_identifier(c)) { + if (!this->isValidForIdentifier(c)) { // TODO: Replece this with a tranceback function or something to raise // synatx error. - fmt::print("Invalid character at the start of a symbol: '{}'\n", c); + // fmt::print("Invalid character at the start of a symbol: '{}'\n", c); + llvm::errs() << fmt::format( + "Invalid character at the start of a symbol: '{}'\n", c); exit(1); } + if (c == '-') { + char next = getChar(false); + if (next >= '0' && next <= '9') { + ungetChar(); + return readNumber(true); + } + } + if (c >= '0' && c <= '9') { + ungetChar(); + return readNumber(false); + } + std::string sym(""); LocationRange loc; loc.start = current_location; - while (c != EOF && ((!(isspace(c)) && this->is_valid_for_identifier(c)))) { + while (c != EOF && ((!(isspace(c)) && this->isValidForIdentifier(c)))) { sym += c; - c = get_char(false); + c = getChar(false); empty = false; } if (!empty) { - unget_char(); + ungetChar(); loc.end = current_location; return makeSymbol(loc, sym); } @@ -123,17 +191,18 @@ ast_node Reader::read_symbol() { // TODO: it should never happens return nullptr; }; + // std::unique_ptr list -ast_list_node Reader::read_list() { +ast_list_node Reader::readList() { auto list = makeList(current_location); - char c = get_char(true); + char c = getChar(true); assert(c == '('); bool list_terminated = false; do { - char c = get_char(true); + char c = getChar(true); switch (c) { case EOF: @@ -145,8 +214,8 @@ ast_list_node Reader::read_list() { break; default: - unget_char(); - list->append(read_expr()); + ungetChar(); + list->append(readExpr()); } } while (!list_terminated); @@ -154,35 +223,35 @@ ast_list_node Reader::read_list() { return list; } -ast_node Reader::read_expr() { - char c = get_char(false); +ast_node Reader::readExpr() { + char c = getChar(false); READER_LOG("CHAR: {}", c); - unget_char(); + ungetChar(); switch (c) { case '(': { - return read_list(); + return readList(); } case EOF: return nullptr; default: - return read_symbol(); + return readSymbol(); } } std::unique_ptr Reader::read() { - char c = get_char(true); + char c = getChar(true); while (c != EOF) { - unget_char(); - auto tmp{read_expr()}; + ungetChar(); + auto tmp{readExpr()}; if (tmp) { this->ast.push_back(move(tmp)); } - c = get_char(true); + c = getChar(true); } return std::make_unique(this->ast); diff --git a/src/sir/generator.cpp b/src/sir/generator.cpp index 2c238c0..256abfb 100644 --- a/src/sir/generator.cpp +++ b/src/sir/generator.cpp @@ -32,17 +32,53 @@ namespace serene { namespace sir { -mlir::ModuleOp Generator::generate(::serene::Namespace &ns) { +mlir::ModuleOp Generator::generate() { auto module = mlir::ModuleOp::create(builder.getUnknownLoc()); - // for (auto &x : ns.Tree()) { - // this->generate(x); - // } - module.push_back( - builder.create(builder.getUnknownLoc(), (uint64_t)3)); + for (auto x : ns.Tree()) { + module.push_back(generateExpression(x.get())); + } + return module; }; +mlir::Operation *Generator::generateExpression(AExpr *x) { + switch (x->getType()) { + case SereneType::Number: { + return generateNumber(llvm::cast(x)); + } + + case SereneType::List: { + return generateList(llvm::cast(x)); + } + + default: { + return builder.create(builder.getUnknownLoc(), (uint64_t)3); + } + } +}; + +mlir::Operation *Generator::generateList(List *l) { + auto first = l->at(0); + + if (!first) { + // Empty list. + // TODO: Return Nil or empty list. + + // Just for now. + return builder.create(builder.getUnknownLoc(), (uint64_t)0); + } + + // for (auto x : l->from(1)) { + // generateExpression(x); + // } + return builder.create(builder.getUnknownLoc(), (uint64_t)0); +}; + +mlir::Operation *Generator::generateNumber(Number *x) { + return builder.create(builder.getUnknownLoc(), x->toI64()); +}; + Generator::~Generator(){}; } // namespace sir diff --git a/src/sir/sir.cpp b/src/sir/sir.cpp index 8e515f3..a8a0cba 100644 --- a/src/sir/sir.cpp +++ b/src/sir/sir.cpp @@ -33,9 +33,9 @@ namespace sir { SIR::SIR() { context.getOrLoadDialect<::serene::sir::SereneDialect>(); } mlir::OwningModuleRef SIR::generate(::serene::Namespace &ns) { - Generator g{context}; + Generator g{context, ns}; - return g.generate(ns); + return g.generate(); }; SIR::~SIR() {} diff --git a/src/symbol.cpp b/src/symbol.cpp index 8577bae..e8b11a5 100644 --- a/src/symbol.cpp +++ b/src/symbol.cpp @@ -52,6 +52,14 @@ Symbol::Symbol(reader::LocationRange loc, const string &name) : name_(name) { this->location.reset(new reader::LocationRange(loc)); } +/** + * `classof` is a enabler static method that belongs to the LLVM RTTI interface + * `llvm::isa`, `llvm::cast` and `llvm::dyn_cast` use this method. + */ +bool Symbol::classof(const AExpr *expr) { + return expr->getType() == SereneType::Symbol; +} + Symbol::~Symbol() { EXPR_LOG("Destroying symbol"); } std::unique_ptr makeSymbol(reader::LocationRange loc,