diff --git a/bin/serene.cpp b/bin/serene.cpp index 78f3275..f4ff2c5 100644 --- a/bin/serene.cpp +++ b/bin/serene.cpp @@ -60,7 +60,13 @@ int main(int argc, char *argv[]) { case Action::DumpIR: { reader::FileReader *r = new reader::FileReader(inputFile); - serene::sir::dumpSIR(*r->read()); + auto ast = r->read(); + + if (!ast) { + throw ast.takeError(); + } + + serene::sir::dumpSIR(*ast); delete r; return 0; } diff --git a/include/serene/errors.h b/include/serene/errors.h new file mode 100644 index 0000000..0188302 --- /dev/null +++ b/include/serene/errors.h @@ -0,0 +1,31 @@ +/* -*- C++ -*- + * Serene programming language. + * + * Copyright (c) 2019-2021 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 SERENE_ERRORS_H +#define SERENE_ERRORS_H + +#include "serene/errors/errc.h" +#include "serene/errors/error.h" + +#endif diff --git a/include/serene/errors/errc.h b/include/serene/errors/errc.h new file mode 100644 index 0000000..0238cdf --- /dev/null +++ b/include/serene/errors/errc.h @@ -0,0 +1,83 @@ +/* -*- C++ -*- + * Serene programming language. + * + * Copyright (c) 2019-2021 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 SERENE_ERRORS_ERRC_H +#define SERENE_ERRORS_ERRC_H + +#include "llvm/Support/Errc.h" + +namespace serene { + +/// A collection of common error codes in Serene +enum class errc { + argument_list_too_long = int(std::errc::argument_list_too_long), + argument_out_of_domain = int(std::errc::argument_out_of_domain), + bad_address = int(std::errc::bad_address), + bad_file_descriptor = int(std::errc::bad_file_descriptor), + broken_pipe = int(std::errc::broken_pipe), + device_or_resource_busy = int(std::errc::device_or_resource_busy), + directory_not_empty = int(std::errc::directory_not_empty), + executable_format_error = int(std::errc::executable_format_error), + file_exists = int(std::errc::file_exists), + file_too_large = int(std::errc::file_too_large), + filename_too_long = int(std::errc::filename_too_long), + function_not_supported = int(std::errc::function_not_supported), + illegal_byte_sequence = int(std::errc::illegal_byte_sequence), + inappropriate_io_control_operation = + int(std::errc::inappropriate_io_control_operation), + interrupted = int(std::errc::interrupted), + invalid_argument = int(std::errc::invalid_argument), + invalid_seek = int(std::errc::invalid_seek), + io_error = int(std::errc::io_error), + is_a_directory = int(std::errc::is_a_directory), + no_child_process = int(std::errc::no_child_process), + no_lock_available = int(std::errc::no_lock_available), + no_space_on_device = int(std::errc::no_space_on_device), + no_such_device_or_address = int(std::errc::no_such_device_or_address), + no_such_device = int(std::errc::no_such_device), + no_such_file_or_directory = int(std::errc::no_such_file_or_directory), + no_such_process = int(std::errc::no_such_process), + not_a_directory = int(std::errc::not_a_directory), + not_enough_memory = int(std::errc::not_enough_memory), + not_supported = int(std::errc::not_supported), + operation_not_permitted = int(std::errc::operation_not_permitted), + permission_denied = int(std::errc::permission_denied), + read_only_file_system = int(std::errc::read_only_file_system), + resource_deadlock_would_occur = int(std::errc::resource_deadlock_would_occur), + resource_unavailable_try_again = + int(std::errc::resource_unavailable_try_again), + result_out_of_range = int(std::errc::result_out_of_range), + too_many_files_open_in_system = int(std::errc::too_many_files_open_in_system), + too_many_files_open = int(std::errc::too_many_files_open), + too_many_links = int(std::errc::too_many_links) +}; + +/// The **official way** to create `std::error_code` in context of Serene. +inline std::error_code make_error_code(errc E) { + return std::error_code(static_cast(E), std::generic_category()); +}; + +}; // namespace serene + +#endif diff --git a/include/serene/errors/error.h b/include/serene/errors/error.h new file mode 100644 index 0000000..26c6d9f --- /dev/null +++ b/include/serene/errors/error.h @@ -0,0 +1,30 @@ +/* -*- C++ -*- + * Serene programming language. + * + * Copyright (c) 2019-2021 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 SERENE_ERRORS_ERROR_H +#define SERENE_ERRORS_ERROR_H + +#include "llvm/Support/Error.h" + +#endif diff --git a/include/serene/exprs/expression.h b/include/serene/exprs/expression.h index 1ef6ea1..dd23194 100644 --- a/include/serene/exprs/expression.h +++ b/include/serene/exprs/expression.h @@ -30,136 +30,67 @@ namespace serene { -/// Contains all the builtin expressions including those which do not appear in -/// the syntax directly. Like function definitions. +/// Contains all the builtin AST expressions including those which do not appear +/// in the syntax directly. Like function definitions. namespace exprs { /// This enum represent the expression type and **not** the value type. enum class ExprType { Symbol, List, + Number, }; -/// The polymorphic type that works as the entry point to the exprs system. -/// Each expression has to define the interface of the `ExpressionConcept` -/// class as generic functions. **REMEMBER TO NOT INHERIT FROM THESE CLASSES** +/// The base class of the expressions which provides the common interface for +/// the expressions to implement. class Expression { public: + /// The location range provide information regarding to where in the input + /// string the current expression is used. reader::LocationRange location; Expression(const reader::LocationRange &loc) : location(loc){}; virtual ~Expression() = default; + /// Returns the type of the expression. We need this funciton to perform + /// dynamic casting of expression object to implementations such as lisp or + /// symbol. virtual ExprType getType() const = 0; + + /// The AST representation of an expression virtual std::string toString() const = 0; }; +using node = std::shared_ptr; +using ast = llvm::SmallVector; -/// Create a new Expression of type `T` and forwards any given parameter +/// Create a new `node` of type `T` and forwards any given parameter /// to the constructor of type `T`. This is the **official way** to create /// a new `Expression`. Here is an example: /// \code -/// auto list = Expression::make(); +/// auto list = make(); /// \endcode /// -/// \param loc A `serene::reader::LocationRange` instance to point to exact -/// location of the expression in the input string. /// \param[args] Any argument with any type passed to this function will be /// passed to the constructor of type T. -/// \return A new expression containing a value of type T and act as tyep T. -// template std::shared_ptr make(Args -// &&...args); -template -std::shared_ptr make(Args &&...args) { +/// \return A shared pointer to an Expression +template node make(Args &&...args) { return std::shared_ptr(new T(std::forward(args)...)); }; -using node = std::shared_ptr; -using ast = llvm::SmallVector; -// /// Creates a new expression by moving the given object of the type T into -// /// a new internal container. -// /// -// /// \param e and expression of type T -// template Expression(T e) : self(new Impl(std::move(e))){}; - -// /// The copy constructor which actually just move the other expression into -// /// a new implementation container. -// /// -// /// \param e is the other expression to copy from -// Expression(const Expression &e) : self(e.self->copy_()){}; // Copy ctor -// Expression(Expression &&e) noexcept = default; // Move ctor - -// Expression &operator=(const Expression &e); -// Expression &operator=(Expression &&e) noexcept = default; - -// /// Returns the type of the expression. More precisely, It returns the type -// /// of the expression that it contains. -// /// -// /// \return The type of expression. -// ExprType getType(); - -// /// Return the string representation of the expression in the context -// /// of the AST. Think of it as dump of the AST for each expression. -// /// -// /// \return the exoression in string format. -// std::string toString(); - -// /// Create a new Expression of type `T` and forwards any given parameter -// /// to the constructor of type `T`. This is the **official way** to create -// /// a new `Expression`. Here is an example: -// /// \code -// /// auto list = Expression::make(); -// /// \endcode -// /// -// /// \param loc A `serene::reader::LocationRange` instance to point to exact -// /// location of the expression in the input string. -// /// \param[args] Any argument with any type passed to this function will be -// /// passed to the constructor of type T. -// /// \return A new expression containing a value of type T and act as tyep T. -// template -// static Expression make(Args &&...args) { -// return Expression(T(std::forward(args)...)); -// }; - -// template std::unique_ptr *to(); -// // template static Expression make(reader::LocationRange &&loc) -// { -// // Expression e(T(std::forward(loc))); -// // return e; -// // }; - -// /// The generic interface which each type of expression has to implement -// /// in order to act like an `Expression` -// class ExpressionConcept { -// public: -// virtual ~ExpressionConcept() = default; -// virtual ExpressionConcept *copy_() const = 0; - -// /// Return the type of the expression -// virtual ExprType getType() = 0; - -// /// Return the string representation of the expression in the context -// /// of the AST. Think of it as dump of the AST for each expression -// virtual std::string toString() = 0; -// }; - -// /// The generic implementation of `ExpressionConcept` which acts as the -// /// dispatcher on type. -// template struct Impl : ExpressionConcept { -// Impl(T e) : expr(std::move(e)){}; - -// ExpressionConcept *copy_() const { return new Impl(*this); } - -// /// In order to make llvm's RTTI to work we need this method. -// ExprType getType() const { return expr.getType(); } - -// std::string toString() { return expr.toString(); } - -// T expr; -// }; - -// /// The internal container to keep the object implementing the -// /// `ExpressionConcept`. This might be a `List` for example or a `Symbol`. -// std::unique_ptr self; +/// Create a new `node` of type `T` and forwards any given parameter +/// to the constructor of type `T`. This is the **official way** to create +/// a new `Expression`. Here is an example: +/// \code +/// auto list = make(); +/// \endcode +/// +/// \param[args] Any argument with any type passed to this function will be +/// passed to the constructor of type T. +/// \return A shared pointer to a value of type T. +template +std::shared_ptr makeAndCast(Args &&...args) { + return std::shared_ptr(new T(std::forward(args)...)); +}; } // namespace exprs } // namespace serene diff --git a/include/serene/exprs/list.h b/include/serene/exprs/list.h index 8bef7e5..95b6c0a 100644 --- a/include/serene/exprs/list.h +++ b/include/serene/exprs/list.h @@ -33,8 +33,12 @@ namespace serene { namespace exprs { +/// This class represents a List in the AST level and not the List as the data +/// type. class List : public Expression { public: + // Internal elements of the lest (small vector of shared pointers to + // expressions) ast elements; List(const List &l); // Copy ctor @@ -47,6 +51,8 @@ public: ExprType getType() const; std::string toString() const; + void append(node); + static bool classof(const Expression *e); ~List() = default; diff --git a/include/serene/exprs/number.h b/include/serene/exprs/number.h new file mode 100644 index 0000000..ebed3e7 --- /dev/null +++ b/include/serene/exprs/number.h @@ -0,0 +1,59 @@ +/* -*- C++ -*- + * Serene programming language. + * + * Copyright (c) 2019-2021 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 EXPRS_NUMBER_H +#define EXPRS_NUMBER_H + +#include "serene/exprs/expression.h" + +namespace serene { + +namespace exprs { + +/// This data structure represent the Lisp symbol. Just a symbol +/// in the context of the AST and nothing else. +struct Number : public Expression { + std::string value; + + bool isNeg; + bool isFloat; + + Number(reader::LocationRange &loc, const std::string &num, bool isNeg, + bool isFloat) + : Expression(loc), value(num), isNeg(isNeg), isFloat(isFloat){}; + + ExprType getType() const; + std::string toString() const; + + int64_t toI64(); + + static bool classof(const Expression *e); + + ~Number() = default; +}; + +} // namespace exprs +} // namespace serene + +#endif diff --git a/include/serene/exprs/symbol.h b/include/serene/exprs/symbol.h index 68b2078..91fc3d6 100644 --- a/include/serene/exprs/symbol.h +++ b/include/serene/exprs/symbol.h @@ -33,6 +33,8 @@ namespace serene { namespace exprs { +/// This data structure represent the Lisp symbol. Just a symbol +/// in the context of the AST and nothing else. struct Symbol : public Expression { std::string name; diff --git a/include/serene/namespace.hpp b/include/serene/namespace.h similarity index 88% rename from include/serene/namespace.hpp rename to include/serene/namespace.h index 7b20670..d1cd9eb 100644 --- a/include/serene/namespace.hpp +++ b/include/serene/namespace.h @@ -27,18 +27,15 @@ #include "mlir/IR/BuiltinOps.h" #include "mlir/IR/Value.h" -#include "serene/expr.hpp" +#include "serene/exprs/expression.h" #include "serene/llvm/IR/Value.h" #include "serene/logger.hpp" #include "llvm/ADT/DenseMap.h" #include #include -#if defined(ENABLE_LOG) || defined(ENABLE_NAMESPACE_LOG) -#define NAMESPACE_LOG(...) __LOG("NAMESPACE", __VA_ARGS__); -#else -#define NAMESPACE_LOG(...) ; -#endif +#define NAMESPACE_LOG(...) \ + DEBUG_WITH_TYPE("NAMESPACE", llvm::dbgs() << __VA_ARGS__ << "\n"); using ScopeMap = llvm::DenseMap; using PairT = std::pair; @@ -48,7 +45,7 @@ class AExpr; class Namespace { private: - ast_tree tree{}; + exprs::ast tree{}; bool initialized = false; ScopeMap rootScope; @@ -59,8 +56,8 @@ public: Namespace(llvm::StringRef ns_name, llvm::Optional filename); - ast_tree &Tree(); - mlir::LogicalResult setTree(ast_tree); + exprs::ast &Tree(); + mlir::LogicalResult setTree(exprs::ast &); // TODO: Fix it to return llvm::Optional instead llvm::Optional lookup(llvm::StringRef name); mlir::LogicalResult insert_symbol(llvm::StringRef name, mlir::Value v); diff --git a/include/serene/reader/errors.h b/include/serene/reader/errors.h new file mode 100644 index 0000000..01288fe --- /dev/null +++ b/include/serene/reader/errors.h @@ -0,0 +1,64 @@ +/* -*- C++ -*- + * Serene programming language. + * + * Copyright (c) 2019-2021 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 SERENE_READER_ERRORS_H +#define SERENE_READER_ERRORS_H +#include "serene/errors.h" + +namespace serene { +namespace reader { + +class ReadError : public std::exception { +private: + char *message; + +public: + ReadError(char *msg) : message(msg){}; + const char *what() const throw() { return message; } +}; + +class MissingFileError : public llvm::ErrorInfo { + + using llvm::ErrorInfo::log; + using llvm::ErrorInfo::convertToErrorCode; + +public: + static char ID; + std::string path; + + // TODO: Move this to an error namespace somewhere. + int file_is_missing = int(); + + void log(llvm::raw_ostream &os) const { + os << "File does not exist: " << path << "\n"; + } + + MissingFileError(llvm::StringRef path) : path(path.str()){}; + std::error_code convertToErrorCode() const { + return make_error_code(errc::no_such_file_or_directory); + } +}; +} // namespace reader +} // namespace serene +#endif diff --git a/include/serene/reader/reader.h b/include/serene/reader/reader.h index e23d9f0..8640e78 100644 --- a/include/serene/reader/reader.h +++ b/include/serene/reader/reader.h @@ -29,15 +29,19 @@ #include #include #include +#include #include -#include "serene/expr.hpp" -#include "serene/list.hpp" +#include "serene/errors.h" +#include "serene/exprs/expression.h" +#include "serene/exprs/list.h" +#include "serene/exprs/symbol.h" #include "serene/logger.hpp" +#include "serene/reader/errors.h" #include "serene/reader/location.h" #include "serene/serene.h" -#include "serene/symbol.hpp" #include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" #define READER_LOG(...) \ DEBUG_WITH_TYPE("READER", llvm::dbgs() << __VA_ARGS__ << "\n"); @@ -45,15 +49,6 @@ namespace serene { namespace reader { -class ReadError : public std::exception { -private: - char *message; - -public: - ReadError(char *msg) : message(msg){}; - const char *what() const throw() { return message; } -}; - class Reader { private: char current_char = ';'; // Some arbitary char to begin with @@ -65,19 +60,19 @@ private: bool isValidForIdentifier(char c); // The property to store the ast tree - ast_tree ast; - ast_node readSymbol(); - ast_node readNumber(bool); - ast_list_node readList(); - ast_node readExpr(); + exprs::ast ast; + exprs::node readSymbol(); + exprs::node readNumber(bool); + exprs::node readList(); + exprs::node readExpr(); public: Reader() : input_stream(""){}; - Reader(const std::string); + Reader(const llvm::StringRef string); - void setInput(const std::string); + void setInput(const llvm::StringRef string); - std::unique_ptr read(); + llvm::Expected read(); // Dumps the AST data to stdout void dumpAST(); @@ -95,7 +90,7 @@ public: // Dumps the AST data to stdout void dumpAST(); - std::unique_ptr read(); + llvm::Expected read(); ~FileReader(); }; diff --git a/include/serene/sir/generator.hpp b/include/serene/sir/generator.hpp index fe913f4..be8ad7c 100644 --- a/include/serene/sir/generator.hpp +++ b/include/serene/sir/generator.hpp @@ -30,7 +30,7 @@ #include "mlir/IR/MLIRContext.h" #include "serene/expr.hpp" #include "serene/list.hpp" -#include "serene/namespace.hpp" +#include "serene/namespace.h" #include "serene/number.hpp" #include "serene/symbol.hpp" #include "llvm/ADT/ScopedHashTable.h" diff --git a/include/serene/sir/sir.hpp b/include/serene/sir/sir.hpp index 1dcd797..a2ed24a 100644 --- a/include/serene/sir/sir.hpp +++ b/include/serene/sir/sir.hpp @@ -26,7 +26,7 @@ #include "mlir/IR/BuiltinOps.h" #include "mlir/IR/MLIRContext.h" -#include "serene/expr.hpp" +#include "serene/exprs/expression.h" #include "serene/sir/generator.hpp" #include @@ -41,12 +41,12 @@ private: public: SIR(); - mlir::OwningModuleRef generate(::serene::Namespace *ns); + mlir::OwningModuleRef generate(serene::Namespace *ns); ~SIR(); }; -void dumpSIR(ast_tree &t); +void dumpSIR(exprs::ast &t); } // namespace sir } // namespace serene diff --git a/include/serene/state.hpp b/include/serene/state.hpp index 979edae..5d4e0b5 100644 --- a/include/serene/state.hpp +++ b/include/serene/state.hpp @@ -27,7 +27,7 @@ #include "serene/llvm/IR/Value.h" #include "serene/logger.hpp" -//#include "serene/namespace.hpp" +//#include "serene/namespace.h" #include #include diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0368fda..c8e916e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -4,10 +4,16 @@ set(HEADER_LIST "${INCLUDE_DIR}/serene/exprs/expression.h" "${INCLUDE_DIR}/serene/exprs/symbol.h" "${INCLUDE_DIR}/serene/exprs/list.h" + "${INCLUDE_DIR}/serene/exprs/number.h" # Reader "${INCLUDE_DIR}/serene/reader/reader.h" "${INCLUDE_DIR}/serene/reader/location.h" + "${INCLUDE_DIR}/serene/reader/errors.h" + + "${INCLUDE_DIR}/serene/errors.h" + "${INCLUDE_DIR}/serene/errors/error.h" + "${INCLUDE_DIR}/serene/errors/errc.h" "${INCLUDE_DIR}/serene/expr.hpp" @@ -23,13 +29,15 @@ set(HEADER_LIST "${INCLUDE_DIR}/serene/sir/sir.hpp" "${INCLUDE_DIR}/serene/sir/dialect.hpp" "${INCLUDE_DIR}/serene/sir/generator.hpp" - "${INCLUDE_DIR}/serene/namespace.hpp") + "${INCLUDE_DIR}/serene/namespace.h") # Make an automatic library - will be static or dynamic based on user setting add_library(serene exprs/expression.cpp exprs/symbol.cpp exprs/list.cpp + exprs/number.cpp + serene.cpp symbol.cpp @@ -42,6 +50,7 @@ add_library(serene # Reader reader/reader.cpp reader/location.cpp + reader/errors.cpp # IR sir/sir.cpp diff --git a/src/error.cpp b/src/error.cpp index 56631e6..708ac9b 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -25,7 +25,7 @@ #include "serene/error.hpp" #include "serene/expr.hpp" #include "serene/llvm/IR/Value.h" -#include "serene/namespace.hpp" +#include "serene/namespace.h" #include "serene/state.hpp" #include #include diff --git a/src/exprs/list.cpp b/src/exprs/list.cpp index 58027f9..47eb092 100644 --- a/src/exprs/list.cpp +++ b/src/exprs/list.cpp @@ -53,5 +53,6 @@ bool List::classof(const Expression *e) { return e->getType() == ExprType::List; }; +void List::append(node n) { elements.push_back(n); } } // namespace exprs } // namespace serene diff --git a/src/exprs/number.cpp b/src/exprs/number.cpp new file mode 100644 index 0000000..004b11e --- /dev/null +++ b/src/exprs/number.cpp @@ -0,0 +1,50 @@ +/* + * Serene programming language. + * + * Copyright (c) 2019-2021 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/exprs/number.h" +#include "llvm/Support/FormatVariadic.h" + +namespace serene { +namespace exprs { + +int64_t Number::toI64() { + // TODO: Handle float case as well + // TODO: Cache the value + return std::stoi(value); +}; + +ExprType Number::getType() const { return ExprType::Number; }; + +std::string Number::toString() const { + return llvm::formatv("", + this->location.start.toString(), + this->location.end.toString(), this->value); +} + +bool Number::classof(const Expression *e) { + return e->getType() == ExprType::Number; +}; + +} // namespace exprs +} // namespace serene diff --git a/src/exprs/symbol.cpp b/src/exprs/symbol.cpp index 5c036cf..85d021c 100644 --- a/src/exprs/symbol.cpp +++ b/src/exprs/symbol.cpp @@ -28,7 +28,6 @@ namespace serene { namespace exprs { - ExprType Symbol::getType() const { return ExprType::Symbol; }; std::string Symbol::toString() const { @@ -37,7 +36,7 @@ std::string Symbol::toString() const { this->location.end.toString(), this->name); } - bool Symbol::classof(const Expression *e) { +bool Symbol::classof(const Expression *e) { return e->getType() == ExprType::List; }; diff --git a/src/namespace.cpp b/src/namespace.cpp index 085cc77..1a93dde 100644 --- a/src/namespace.cpp +++ b/src/namespace.cpp @@ -22,8 +22,8 @@ * SOFTWARE. */ -#include "serene/namespace.hpp" -#include "serene/expr.hpp" +#include "serene/namespace.h" +#include "serene/exprs/expression.h" #include "serene/llvm/IR/Value.h" #include "llvm/ADT/StringRef.h" #include @@ -41,7 +41,7 @@ Namespace::Namespace(llvm::StringRef ns_name, this->name = ns_name; }; -ast_tree &Namespace::Tree() { return this->tree; } +exprs::ast &Namespace::Tree() { return this->tree; } llvm::Optional Namespace::lookup(llvm::StringRef name) { if (auto value = rootScope.lookup(name)) { @@ -51,7 +51,7 @@ llvm::Optional Namespace::lookup(llvm::StringRef name) { return llvm::None; }; -mlir::LogicalResult Namespace::setTree(ast_tree t) { +mlir::LogicalResult Namespace::setTree(exprs::ast &t) { if (initialized) { return mlir::failure(); } diff --git a/src/number.cpp b/src/number.cpp index 39da1fc..4977d92 100644 --- a/src/number.cpp +++ b/src/number.cpp @@ -25,7 +25,7 @@ #include "serene/number.hpp" #include "serene/expr.hpp" #include "serene/llvm/IR/Value.h" -#include "serene/namespace.hpp" +#include "serene/namespace.h" #include "serene/state.hpp" #include #include diff --git a/src/reader/errors.cpp b/src/reader/errors.cpp new file mode 100644 index 0000000..965e828 --- /dev/null +++ b/src/reader/errors.cpp @@ -0,0 +1,32 @@ +/* -*- C++ -*- + * Serene programming language. + * + * Copyright (c) 2019-2021 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/reader/errors.h" + +namespace serene { +namespace reader { +/// This one should be here, llvm's rules +char MissingFileError::ID; +}; // namespace reader +}; // namespace serene diff --git a/src/reader/reader.cpp b/src/reader/reader.cpp index 032422c..1d3ebe9 100644 --- a/src/reader/reader.cpp +++ b/src/reader/reader.cpp @@ -24,49 +24,48 @@ #include "serene/reader/reader.h" #include "serene/error.hpp" -#include "serene/list.hpp" -#include "serene/number.hpp" -#include "serene/symbol.hpp" +#include "serene/exprs/list.h" +#include "serene/exprs/number.h" +#include "serene/exprs/symbol.h" +#include "llvm/Support/Error.h" #include "llvm/Support/ErrorOr.h" #include "llvm/Support/FormatVariadic.h" #include "llvm/Support/MemoryBuffer.h" #include #include -#include #include #include -using namespace std; - namespace serene { namespace reader { -Reader::Reader(const string input) { this->setInput(input); }; -/** - * Set the input of the reader. - * @param input Set the input to the given string - */ -void Reader::setInput(const string input) { - input_stream.write(input.c_str(), input.size()); +Reader::Reader(const llvm::StringRef input) { this->setInput(input); }; + +/// Set the input of the reader. +///\param input Set the input to the given string +void Reader::setInput(const llvm::StringRef input) { + input_stream.write(input.str().c_str(), input.size()); }; 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 - */ +/// Return the next character in the buffer and moves the location. +///\param skip_whitespace If true it will skip whitespaces and EOL chars +/// \return next char in the buffer. char Reader::getChar(bool skip_whitespace) { for (;;) { char c = input_stream.get(); this->current_char = c; + + // TODO: Handle the end of line with respect to the OS. + // increase the current position in the buffer with respect to the end + // of line. inc_location(current_location, c == '\n'); if (skip_whitespace == true && isspace(c)) { - continue; } else { return c; @@ -74,12 +73,15 @@ char Reader::getChar(bool skip_whitespace) { } }; +/// Moves back the location by one char. Basically unreads the last character. void Reader::ungetChar() { input_stream.unget(); // The char that we just unget dec_location(current_location, this->current_char == '\n'); }; +/// A predicate function indicating whether the given char `c` is a valid +/// char for the starting point of a symbol or not. bool Reader::isValidForIdentifier(char c) { switch (c) { case '!': @@ -110,7 +112,7 @@ bool Reader::isValidForIdentifier(char c) { return false; } -ast_node Reader::readNumber(bool neg) { +exprs::node Reader::readNumber(bool neg) { std::string number(neg ? "-" : ""); bool floatNum = false; bool empty = false; @@ -141,13 +143,13 @@ ast_node Reader::readNumber(bool neg) { if (!empty) { ungetChar(); loc.end = current_location; - return makeNumber(loc, number, neg, floatNum); + return exprs::make(loc, number, neg, floatNum); } return nullptr; }; -ast_node Reader::readSymbol() { +exprs::node Reader::readSymbol() { bool empty = true; char c = getChar(false); @@ -186,16 +188,15 @@ ast_node Reader::readSymbol() { if (!empty) { ungetChar(); loc.end = current_location; - return makeSymbol(loc, sym); + return exprs::make(loc, sym); } // TODO: it should never happens return nullptr; }; -// std::unique_ptr list -ast_list_node Reader::readList() { - auto list = makeList(current_location); +exprs::node Reader::readList() { + auto list = exprs::makeAndCast(current_location); char c = getChar(true); assert(c == '('); @@ -210,7 +211,7 @@ ast_list_node Reader::readList() { throw ReadError(const_cast("EOF reached before closing of list")); case ')': list_terminated = true; - list->location->end = current_location; + list->location.end = current_location; break; @@ -222,9 +223,9 @@ ast_list_node Reader::readList() { } while (!list_terminated); return list; -} +}; -ast_node Reader::readExpr() { +exprs::node Reader::readExpr() { char c = getChar(false); READER_LOG("CHAR: " << c); @@ -241,9 +242,9 @@ ast_node Reader::readExpr() { default: return readSymbol(); } -} +}; -std::unique_ptr Reader::read() { +llvm::Expected Reader::read() { char c = getChar(true); while (c != EOF) { @@ -255,18 +256,25 @@ std::unique_ptr Reader::read() { c = getChar(true); } - return std::make_unique(this->ast); -} + return this->ast; +}; void Reader::dumpAST() { - ast_tree ast = *this->read(); + auto maybeAst = read(); std::string result = ""; - for (auto &node : ast) { - result = fmt::format("{0} {1}", result, node->dumpAST()); - } -} -std::unique_ptr FileReader::read() { + if (!maybeAst) { + throw maybeAst.takeError(); + } + + exprs::ast ast = *maybeAst; + + for (auto &node : ast) { + result = llvm::formatv("{0} {1}", result, node->toString()); + } +}; + +llvm::Expected FileReader::read() { // TODO: Add support for relative path as well llvm::ErrorOr> fileOrErr = @@ -276,7 +284,7 @@ std::unique_ptr FileReader::read() { llvm::errs() << "Could not open input file: " << EC.message() << "\n"; llvm::errs() << fmt::format("File: '{}'\n", file); llvm::errs() << "Use absolute path for now\n"; - return nullptr; + return llvm::make_error(file); } reader->setInput(fileOrErr.get()->getBuffer().str()); @@ -285,17 +293,19 @@ std::unique_ptr FileReader::read() { void FileReader::dumpAST() { auto maybeAst = this->read(); - ast_tree ast; + exprs::ast ast; - if (maybeAst) { - ast = *maybeAst; + if (!maybeAst) { + throw maybeAst.takeError(); } + ast = *maybeAst; + std::string result = ""; for (auto &node : ast) { - result = fmt::format("{0} {1}", result, node->dumpAST()); + result = llvm::formatv("{0} {1}", result, node->toString()); } - cout << result << endl; + llvm::outs() << result << "\n"; } FileReader::~FileReader() { diff --git a/src/sir/sir.cpp b/src/sir/sir.cpp index 465afb9..a965910 100644 --- a/src/sir/sir.cpp +++ b/src/sir/sir.cpp @@ -24,7 +24,7 @@ #include "serene/sir/sir.hpp" #include "mlir/IR/MLIRContext.h" -#include "serene/expr.hpp" +#include "serene/exprs/expression.h" #include "serene/sir/dialect.hpp" #include "serene/sir/generator.hpp" #include @@ -41,7 +41,7 @@ mlir::OwningModuleRef SIR::generate(::serene::Namespace *ns) { SIR::~SIR() {} -void dumpSIR(ast_tree &t) { +void dumpSIR(exprs::ast &t) { auto ns = new ::serene::Namespace("user", llvm::None); SIR s{}; diff --git a/src/state.cpp b/src/state.cpp index 4d05882..ffc59e4 100644 --- a/src/state.cpp +++ b/src/state.cpp @@ -24,7 +24,7 @@ #include "serene/state.hpp" #include "serene/llvm/IR/Value.h" -#include "serene/namespace.hpp" +#include "serene/namespace.h" #include #include diff --git a/src/symbol.cpp b/src/symbol.cpp index 4c972f2..5a644c7 100644 --- a/src/symbol.cpp +++ b/src/symbol.cpp @@ -25,7 +25,7 @@ #include "serene/symbol.hpp" #include "serene/expr.hpp" #include "serene/llvm/IR/Value.h" -#include "serene/namespace.hpp" +#include "serene/namespace.h" #include "serene/state.hpp" #include #include diff --git a/tests/exprs/expression_tests.cpp.inc b/tests/exprs/expression_tests.cpp.inc index dfb6a9c..4820774 100644 --- a/tests/exprs/expression_tests.cpp.inc +++ b/tests/exprs/expression_tests.cpp.inc @@ -37,30 +37,9 @@ TEST_CASE("Public Expression API", "[expression]") { REQUIRE(sym->getType() == ExprType::Symbol); REQUIRE(sym->toString() == ""); -}; -TEST_CASE("List Expression", "[expression]") { - std::unique_ptr range(dummyLocation()); - auto sym = make(*range.get(), llvm::StringRef("example")); + auto list = makeAndCast(*range.get(), sym); - auto list = make(*range.get()); - auto list2 = make(*range.get(), list); - auto list3 = make(*range.get(), llvm::ArrayRef{list, list2, sym}); - - REQUIRE(list->toString() == ""); - REQUIRE(list->getType() == ExprType::List); - - REQUIRE( - list2->toString() == - ">"); - REQUIRE(list3->toString() == - " " - "> >"); - - list->elements.push_back(sym); - - REQUIRE(list->getType() == ExprType::List); REQUIRE(list->toString() == ">"); }; diff --git a/tests/exprs/list_test.cpp b/tests/exprs/list_test.cpp new file mode 100644 index 0000000..8a06f8a --- /dev/null +++ b/tests/exprs/list_test.cpp @@ -0,0 +1,61 @@ +/* -*- C++ -*- + * Serene programming language. + * + * Copyright (c) 2019-2021 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 "../test_helpers.cpp.inc" +#include "serene/exprs/list.h" +#include "serene/exprs/symbol.h" + +namespace serene { +namespace exprs { + +TEST_CASE("List Expression", "[expression]") { + std::unique_ptr range(dummyLocation()); + + auto sym = make(*range.get(), llvm::StringRef("example")); + auto list = make(*range.get()); + auto list2 = make(*range.get(), list); + auto list3 = make(*range.get(), llvm::ArrayRef{list, list2, sym}); + + REQUIRE(list->toString() == ""); + REQUIRE(list->getType() == ExprType::List); + + REQUIRE( + list2->toString() == + ">"); + REQUIRE(list3->toString() == + " " + "> >"); + + auto l = llvm::dyn_cast(list); + + l.append(sym); + + REQUIRE(list->getType() == ExprType::List); + REQUIRE(list->toString() == ">"); +}; + +} // namespace exprs +} // namespace serene