Finalize migration to new expression interface

This commit is contained in:
Sameer Rahmani 2021-04-11 00:02:56 +01:00
parent e4b0823e49
commit 555e770c01
28 changed files with 561 additions and 216 deletions

View File

@ -60,7 +60,13 @@ int main(int argc, char *argv[]) {
case Action::DumpIR: { case Action::DumpIR: {
reader::FileReader *r = new reader::FileReader(inputFile); 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; delete r;
return 0; return 0;
} }

31
include/serene/errors.h Normal file
View File

@ -0,0 +1,31 @@
/* -*- C++ -*-
* Serene programming language.
*
* Copyright (c) 2019-2021 Sameer Rahmani <lxsameer@gnu.org>
*
* 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

View File

@ -0,0 +1,83 @@
/* -*- C++ -*-
* Serene programming language.
*
* Copyright (c) 2019-2021 Sameer Rahmani <lxsameer@gnu.org>
*
* 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<int>(E), std::generic_category());
};
}; // namespace serene
#endif

View File

@ -0,0 +1,30 @@
/* -*- C++ -*-
* Serene programming language.
*
* Copyright (c) 2019-2021 Sameer Rahmani <lxsameer@gnu.org>
*
* 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

View File

@ -30,136 +30,67 @@
namespace serene { namespace serene {
/// Contains all the builtin expressions including those which do not appear in /// Contains all the builtin AST expressions including those which do not appear
/// the syntax directly. Like function definitions. /// in the syntax directly. Like function definitions.
namespace exprs { namespace exprs {
/// This enum represent the expression type and **not** the value type. /// This enum represent the expression type and **not** the value type.
enum class ExprType { enum class ExprType {
Symbol, Symbol,
List, List,
Number,
}; };
/// The polymorphic type that works as the entry point to the exprs system. /// The base class of the expressions which provides the common interface for
/// Each expression has to define the interface of the `ExpressionConcept` /// the expressions to implement.
/// class as generic functions. **REMEMBER TO NOT INHERIT FROM THESE CLASSES**
class Expression { class Expression {
public: public:
/// The location range provide information regarding to where in the input
/// string the current expression is used.
reader::LocationRange location; reader::LocationRange location;
Expression(const reader::LocationRange &loc) : location(loc){}; Expression(const reader::LocationRange &loc) : location(loc){};
virtual ~Expression() = default; 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; virtual ExprType getType() const = 0;
/// The AST representation of an expression
virtual std::string toString() const = 0; virtual std::string toString() const = 0;
}; };
using node = std::shared_ptr<Expression>;
using ast = llvm::SmallVector<node, 0>;
/// 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 /// to the constructor of type `T`. This is the **official way** to create
/// a new `Expression`. Here is an example: /// a new `Expression`. Here is an example:
/// \code /// \code
/// auto list = Expression::make<List>(); /// auto list = make<List>();
/// \endcode /// \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 /// \param[args] Any argument with any type passed to this function will be
/// passed to the constructor of type T. /// passed to the constructor of type T.
/// \return A new expression containing a value of type T and act as tyep T. /// \return A shared pointer to an Expression
// template <typename T, typename... Args> std::shared_ptr<T> make(Args template <typename T, typename... Args> node make(Args &&...args) {
// &&...args);
template <typename T, typename... Args>
std::shared_ptr<T> make(Args &&...args) {
return std::shared_ptr<T>(new T(std::forward<Args>(args)...)); return std::shared_ptr<T>(new T(std::forward<Args>(args)...));
}; };
using node = std::shared_ptr<Expression>; /// Create a new `node` of type `T` and forwards any given parameter
using ast = llvm::SmallVector<node, 0>; /// to the constructor of type `T`. This is the **official way** to create
// /// Creates a new expression by moving the given object of the type T into /// a new `Expression`. Here is an example:
// /// a new internal container. /// \code
// /// /// auto list = make<List>();
// /// \param e and expression of type T /// \endcode
// template <typename T> Expression(T e) : self(new Impl<T>(std::move(e))){}; ///
/// \param[args] Any argument with any type passed to this function will be
// /// The copy constructor which actually just move the other expression into /// passed to the constructor of type T.
// /// a new implementation container. /// \return A shared pointer to a value of type T.
// /// template <typename T, typename... Args>
// /// \param e is the other expression to copy from std::shared_ptr<T> makeAndCast(Args &&...args) {
// Expression(const Expression &e) : self(e.self->copy_()){}; // Copy ctor return std::shared_ptr<T>(new T(std::forward<Args>(args)...));
// 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<List>();
// /// \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 <typename T, typename... Args>
// static Expression make(Args &&...args) {
// return Expression(T(std::forward<Args>(args)...));
// };
// template <typename T> std::unique_ptr<T> *to();
// // template <typename T> static Expression make(reader::LocationRange &&loc)
// {
// // Expression e(T(std::forward<reader::LocationRange>(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 <typename T> 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<ExpressionConcept> self;
} // namespace exprs } // namespace exprs
} // namespace serene } // namespace serene

View File

@ -33,8 +33,12 @@ namespace serene {
namespace exprs { namespace exprs {
/// This class represents a List in the AST level and not the List as the data
/// type.
class List : public Expression { class List : public Expression {
public: public:
// Internal elements of the lest (small vector of shared pointers to
// expressions)
ast elements; ast elements;
List(const List &l); // Copy ctor List(const List &l); // Copy ctor
@ -47,6 +51,8 @@ public:
ExprType getType() const; ExprType getType() const;
std::string toString() const; std::string toString() const;
void append(node);
static bool classof(const Expression *e); static bool classof(const Expression *e);
~List() = default; ~List() = default;

View File

@ -0,0 +1,59 @@
/* -*- C++ -*-
* Serene programming language.
*
* Copyright (c) 2019-2021 Sameer Rahmani <lxsameer@gnu.org>
*
* 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

View File

@ -33,6 +33,8 @@ namespace serene {
namespace exprs { 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 { struct Symbol : public Expression {
std::string name; std::string name;

View File

@ -27,18 +27,15 @@
#include "mlir/IR/BuiltinOps.h" #include "mlir/IR/BuiltinOps.h"
#include "mlir/IR/Value.h" #include "mlir/IR/Value.h"
#include "serene/expr.hpp" #include "serene/exprs/expression.h"
#include "serene/llvm/IR/Value.h" #include "serene/llvm/IR/Value.h"
#include "serene/logger.hpp" #include "serene/logger.hpp"
#include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseMap.h"
#include <llvm/IR/Module.h> #include <llvm/IR/Module.h>
#include <string> #include <string>
#if defined(ENABLE_LOG) || defined(ENABLE_NAMESPACE_LOG) #define NAMESPACE_LOG(...) \
#define NAMESPACE_LOG(...) __LOG("NAMESPACE", __VA_ARGS__); DEBUG_WITH_TYPE("NAMESPACE", llvm::dbgs() << __VA_ARGS__ << "\n");
#else
#define NAMESPACE_LOG(...) ;
#endif
using ScopeMap = llvm::DenseMap<llvm::StringRef, mlir::Value>; using ScopeMap = llvm::DenseMap<llvm::StringRef, mlir::Value>;
using PairT = std::pair<llvm::StringRef, mlir::Value>; using PairT = std::pair<llvm::StringRef, mlir::Value>;
@ -48,7 +45,7 @@ class AExpr;
class Namespace { class Namespace {
private: private:
ast_tree tree{}; exprs::ast tree{};
bool initialized = false; bool initialized = false;
ScopeMap rootScope; ScopeMap rootScope;
@ -59,8 +56,8 @@ public:
Namespace(llvm::StringRef ns_name, llvm::Optional<llvm::StringRef> filename); Namespace(llvm::StringRef ns_name, llvm::Optional<llvm::StringRef> filename);
ast_tree &Tree(); exprs::ast &Tree();
mlir::LogicalResult setTree(ast_tree); mlir::LogicalResult setTree(exprs::ast &);
// TODO: Fix it to return llvm::Optional<mlir::Value> instead // TODO: Fix it to return llvm::Optional<mlir::Value> instead
llvm::Optional<mlir::Value> lookup(llvm::StringRef name); llvm::Optional<mlir::Value> lookup(llvm::StringRef name);
mlir::LogicalResult insert_symbol(llvm::StringRef name, mlir::Value v); mlir::LogicalResult insert_symbol(llvm::StringRef name, mlir::Value v);

View File

@ -0,0 +1,64 @@
/* -*- C++ -*-
* Serene programming language.
*
* Copyright (c) 2019-2021 Sameer Rahmani <lxsameer@gnu.org>
*
* 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<MissingFileError> {
using llvm::ErrorInfo<MissingFileError>::log;
using llvm::ErrorInfo<MissingFileError>::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

View File

@ -29,15 +29,19 @@
#include <sstream> #include <sstream>
#include <stdexcept> #include <stdexcept>
#include <string> #include <string>
#include <system_error>
#include <vector> #include <vector>
#include "serene/expr.hpp" #include "serene/errors.h"
#include "serene/list.hpp" #include "serene/exprs/expression.h"
#include "serene/exprs/list.h"
#include "serene/exprs/symbol.h"
#include "serene/logger.hpp" #include "serene/logger.hpp"
#include "serene/reader/errors.h"
#include "serene/reader/location.h" #include "serene/reader/location.h"
#include "serene/serene.h" #include "serene/serene.h"
#include "serene/symbol.hpp"
#include "llvm/Support/Debug.h" #include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
#define READER_LOG(...) \ #define READER_LOG(...) \
DEBUG_WITH_TYPE("READER", llvm::dbgs() << __VA_ARGS__ << "\n"); DEBUG_WITH_TYPE("READER", llvm::dbgs() << __VA_ARGS__ << "\n");
@ -45,15 +49,6 @@
namespace serene { namespace serene {
namespace reader { 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 { class Reader {
private: private:
char current_char = ';'; // Some arbitary char to begin with char current_char = ';'; // Some arbitary char to begin with
@ -65,19 +60,19 @@ private:
bool isValidForIdentifier(char c); bool isValidForIdentifier(char c);
// The property to store the ast tree // The property to store the ast tree
ast_tree ast; exprs::ast ast;
ast_node readSymbol(); exprs::node readSymbol();
ast_node readNumber(bool); exprs::node readNumber(bool);
ast_list_node readList(); exprs::node readList();
ast_node readExpr(); exprs::node readExpr();
public: public:
Reader() : input_stream(""){}; 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<ast_tree> read(); llvm::Expected<exprs::ast> read();
// Dumps the AST data to stdout // Dumps the AST data to stdout
void dumpAST(); void dumpAST();
@ -95,7 +90,7 @@ public:
// Dumps the AST data to stdout // Dumps the AST data to stdout
void dumpAST(); void dumpAST();
std::unique_ptr<ast_tree> read(); llvm::Expected<exprs::ast> read();
~FileReader(); ~FileReader();
}; };

View File

@ -30,7 +30,7 @@
#include "mlir/IR/MLIRContext.h" #include "mlir/IR/MLIRContext.h"
#include "serene/expr.hpp" #include "serene/expr.hpp"
#include "serene/list.hpp" #include "serene/list.hpp"
#include "serene/namespace.hpp" #include "serene/namespace.h"
#include "serene/number.hpp" #include "serene/number.hpp"
#include "serene/symbol.hpp" #include "serene/symbol.hpp"
#include "llvm/ADT/ScopedHashTable.h" #include "llvm/ADT/ScopedHashTable.h"

View File

@ -26,7 +26,7 @@
#include "mlir/IR/BuiltinOps.h" #include "mlir/IR/BuiltinOps.h"
#include "mlir/IR/MLIRContext.h" #include "mlir/IR/MLIRContext.h"
#include "serene/expr.hpp" #include "serene/exprs/expression.h"
#include "serene/sir/generator.hpp" #include "serene/sir/generator.hpp"
#include <memory> #include <memory>
@ -41,12 +41,12 @@ private:
public: public:
SIR(); SIR();
mlir::OwningModuleRef generate(::serene::Namespace *ns); mlir::OwningModuleRef generate(serene::Namespace *ns);
~SIR(); ~SIR();
}; };
void dumpSIR(ast_tree &t); void dumpSIR(exprs::ast &t);
} // namespace sir } // namespace sir
} // namespace serene } // namespace serene

View File

@ -27,7 +27,7 @@
#include "serene/llvm/IR/Value.h" #include "serene/llvm/IR/Value.h"
#include "serene/logger.hpp" #include "serene/logger.hpp"
//#include "serene/namespace.hpp" //#include "serene/namespace.h"
#include <llvm/IR/Module.h> #include <llvm/IR/Module.h>
#include <string> #include <string>

View File

@ -4,10 +4,16 @@ set(HEADER_LIST
"${INCLUDE_DIR}/serene/exprs/expression.h" "${INCLUDE_DIR}/serene/exprs/expression.h"
"${INCLUDE_DIR}/serene/exprs/symbol.h" "${INCLUDE_DIR}/serene/exprs/symbol.h"
"${INCLUDE_DIR}/serene/exprs/list.h" "${INCLUDE_DIR}/serene/exprs/list.h"
"${INCLUDE_DIR}/serene/exprs/number.h"
# Reader # Reader
"${INCLUDE_DIR}/serene/reader/reader.h" "${INCLUDE_DIR}/serene/reader/reader.h"
"${INCLUDE_DIR}/serene/reader/location.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" "${INCLUDE_DIR}/serene/expr.hpp"
@ -23,13 +29,15 @@ set(HEADER_LIST
"${INCLUDE_DIR}/serene/sir/sir.hpp" "${INCLUDE_DIR}/serene/sir/sir.hpp"
"${INCLUDE_DIR}/serene/sir/dialect.hpp" "${INCLUDE_DIR}/serene/sir/dialect.hpp"
"${INCLUDE_DIR}/serene/sir/generator.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 # Make an automatic library - will be static or dynamic based on user setting
add_library(serene add_library(serene
exprs/expression.cpp exprs/expression.cpp
exprs/symbol.cpp exprs/symbol.cpp
exprs/list.cpp exprs/list.cpp
exprs/number.cpp
serene.cpp serene.cpp
symbol.cpp symbol.cpp
@ -42,6 +50,7 @@ add_library(serene
# Reader # Reader
reader/reader.cpp reader/reader.cpp
reader/location.cpp reader/location.cpp
reader/errors.cpp
# IR # IR
sir/sir.cpp sir/sir.cpp

View File

@ -25,7 +25,7 @@
#include "serene/error.hpp" #include "serene/error.hpp"
#include "serene/expr.hpp" #include "serene/expr.hpp"
#include "serene/llvm/IR/Value.h" #include "serene/llvm/IR/Value.h"
#include "serene/namespace.hpp" #include "serene/namespace.h"
#include "serene/state.hpp" #include "serene/state.hpp"
#include <assert.h> #include <assert.h>
#include <fmt/core.h> #include <fmt/core.h>

View File

@ -53,5 +53,6 @@ bool List::classof(const Expression *e) {
return e->getType() == ExprType::List; return e->getType() == ExprType::List;
}; };
void List::append(node n) { elements.push_back(n); }
} // namespace exprs } // namespace exprs
} // namespace serene } // namespace serene

50
src/exprs/number.cpp Normal file
View File

@ -0,0 +1,50 @@
/*
* Serene programming language.
*
* Copyright (c) 2019-2021 Sameer Rahmani <lxsameer@gnu.org>
*
* 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("<Symbol [loc: {0} | {1}]: {2}>",
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

View File

@ -28,7 +28,6 @@
namespace serene { namespace serene {
namespace exprs { namespace exprs {
ExprType Symbol::getType() const { return ExprType::Symbol; }; ExprType Symbol::getType() const { return ExprType::Symbol; };
std::string Symbol::toString() const { std::string Symbol::toString() const {
@ -37,7 +36,7 @@ std::string Symbol::toString() const {
this->location.end.toString(), this->name); this->location.end.toString(), this->name);
} }
bool Symbol::classof(const Expression *e) { bool Symbol::classof(const Expression *e) {
return e->getType() == ExprType::List; return e->getType() == ExprType::List;
}; };

View File

@ -22,8 +22,8 @@
* SOFTWARE. * SOFTWARE.
*/ */
#include "serene/namespace.hpp" #include "serene/namespace.h"
#include "serene/expr.hpp" #include "serene/exprs/expression.h"
#include "serene/llvm/IR/Value.h" #include "serene/llvm/IR/Value.h"
#include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringRef.h"
#include <fmt/core.h> #include <fmt/core.h>
@ -41,7 +41,7 @@ Namespace::Namespace(llvm::StringRef ns_name,
this->name = ns_name; this->name = ns_name;
}; };
ast_tree &Namespace::Tree() { return this->tree; } exprs::ast &Namespace::Tree() { return this->tree; }
llvm::Optional<mlir::Value> Namespace::lookup(llvm::StringRef name) { llvm::Optional<mlir::Value> Namespace::lookup(llvm::StringRef name) {
if (auto value = rootScope.lookup(name)) { if (auto value = rootScope.lookup(name)) {
@ -51,7 +51,7 @@ llvm::Optional<mlir::Value> Namespace::lookup(llvm::StringRef name) {
return llvm::None; return llvm::None;
}; };
mlir::LogicalResult Namespace::setTree(ast_tree t) { mlir::LogicalResult Namespace::setTree(exprs::ast &t) {
if (initialized) { if (initialized) {
return mlir::failure(); return mlir::failure();
} }

View File

@ -25,7 +25,7 @@
#include "serene/number.hpp" #include "serene/number.hpp"
#include "serene/expr.hpp" #include "serene/expr.hpp"
#include "serene/llvm/IR/Value.h" #include "serene/llvm/IR/Value.h"
#include "serene/namespace.hpp" #include "serene/namespace.h"
#include "serene/state.hpp" #include "serene/state.hpp"
#include <assert.h> #include <assert.h>
#include <fmt/core.h> #include <fmt/core.h>

32
src/reader/errors.cpp Normal file
View File

@ -0,0 +1,32 @@
/* -*- C++ -*-
* Serene programming language.
*
* Copyright (c) 2019-2021 Sameer Rahmani <lxsameer@gnu.org>
*
* 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

View File

@ -24,49 +24,48 @@
#include "serene/reader/reader.h" #include "serene/reader/reader.h"
#include "serene/error.hpp" #include "serene/error.hpp"
#include "serene/list.hpp" #include "serene/exprs/list.h"
#include "serene/number.hpp" #include "serene/exprs/number.h"
#include "serene/symbol.hpp" #include "serene/exprs/symbol.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/ErrorOr.h" #include "llvm/Support/ErrorOr.h"
#include "llvm/Support/FormatVariadic.h" #include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/MemoryBuffer.h"
#include <assert.h> #include <assert.h>
#include <fstream> #include <fstream>
#include <iostream>
#include <memory> #include <memory>
#include <string> #include <string>
using namespace std;
namespace serene { namespace serene {
namespace reader { namespace reader {
Reader::Reader(const string input) { this->setInput(input); };
/** Reader::Reader(const llvm::StringRef input) { this->setInput(input); };
* Set the input of the reader.
* @param input Set the input to the given string /// Set the input of the reader.
*/ ///\param input Set the input to the given string
void Reader::setInput(const string input) { void Reader::setInput(const llvm::StringRef input) {
input_stream.write(input.c_str(), input.size()); input_stream.write(input.str().c_str(), input.size());
}; };
Reader::~Reader() { READER_LOG("Destroying the reader"); } Reader::~Reader() { READER_LOG("Destroying the reader"); }
/** /// Return the next character in the buffer and moves the location.
* Return the next character in the buffer. ///\param skip_whitespace If true it will skip whitespaces and EOL chars
* @param skip_whitespace If true it will skip whitespaces and EOL chars /// \return next char in the buffer.
*/
char Reader::getChar(bool skip_whitespace) { char Reader::getChar(bool skip_whitespace) {
for (;;) { for (;;) {
char c = input_stream.get(); char c = input_stream.get();
this->current_char = c; 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'); inc_location(current_location, c == '\n');
if (skip_whitespace == true && isspace(c)) { if (skip_whitespace == true && isspace(c)) {
continue; continue;
} else { } else {
return c; 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() { void Reader::ungetChar() {
input_stream.unget(); input_stream.unget();
// The char that we just unget // The char that we just unget
dec_location(current_location, this->current_char == '\n'); 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) { bool Reader::isValidForIdentifier(char c) {
switch (c) { switch (c) {
case '!': case '!':
@ -110,7 +112,7 @@ bool Reader::isValidForIdentifier(char c) {
return false; return false;
} }
ast_node Reader::readNumber(bool neg) { exprs::node Reader::readNumber(bool neg) {
std::string number(neg ? "-" : ""); std::string number(neg ? "-" : "");
bool floatNum = false; bool floatNum = false;
bool empty = false; bool empty = false;
@ -141,13 +143,13 @@ ast_node Reader::readNumber(bool neg) {
if (!empty) { if (!empty) {
ungetChar(); ungetChar();
loc.end = current_location; loc.end = current_location;
return makeNumber(loc, number, neg, floatNum); return exprs::make<exprs::Number>(loc, number, neg, floatNum);
} }
return nullptr; return nullptr;
}; };
ast_node Reader::readSymbol() { exprs::node Reader::readSymbol() {
bool empty = true; bool empty = true;
char c = getChar(false); char c = getChar(false);
@ -186,16 +188,15 @@ ast_node Reader::readSymbol() {
if (!empty) { if (!empty) {
ungetChar(); ungetChar();
loc.end = current_location; loc.end = current_location;
return makeSymbol(loc, sym); return exprs::make<exprs::Symbol>(loc, sym);
} }
// TODO: it should never happens // TODO: it should never happens
return nullptr; return nullptr;
}; };
// std::unique_ptr<List> list exprs::node Reader::readList() {
ast_list_node Reader::readList() { auto list = exprs::makeAndCast<exprs::List>(current_location);
auto list = makeList(current_location);
char c = getChar(true); char c = getChar(true);
assert(c == '('); assert(c == '(');
@ -210,7 +211,7 @@ ast_list_node Reader::readList() {
throw ReadError(const_cast<char *>("EOF reached before closing of list")); throw ReadError(const_cast<char *>("EOF reached before closing of list"));
case ')': case ')':
list_terminated = true; list_terminated = true;
list->location->end = current_location; list->location.end = current_location;
break; break;
@ -222,9 +223,9 @@ ast_list_node Reader::readList() {
} while (!list_terminated); } while (!list_terminated);
return list; return list;
} };
ast_node Reader::readExpr() { exprs::node Reader::readExpr() {
char c = getChar(false); char c = getChar(false);
READER_LOG("CHAR: " << c); READER_LOG("CHAR: " << c);
@ -241,9 +242,9 @@ ast_node Reader::readExpr() {
default: default:
return readSymbol(); return readSymbol();
} }
} };
std::unique_ptr<ast_tree> Reader::read() { llvm::Expected<exprs::ast> Reader::read() {
char c = getChar(true); char c = getChar(true);
while (c != EOF) { while (c != EOF) {
@ -255,18 +256,25 @@ std::unique_ptr<ast_tree> Reader::read() {
c = getChar(true); c = getChar(true);
} }
return std::make_unique<ast_tree>(this->ast); return this->ast;
} };
void Reader::dumpAST() { void Reader::dumpAST() {
ast_tree ast = *this->read(); auto maybeAst = read();
std::string result = ""; std::string result = "";
for (auto &node : ast) {
result = fmt::format("{0} {1}", result, node->dumpAST());
}
}
std::unique_ptr<ast_tree> 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<exprs::ast> FileReader::read() {
// TODO: Add support for relative path as well // TODO: Add support for relative path as well
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> fileOrErr = llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> fileOrErr =
@ -276,7 +284,7 @@ std::unique_ptr<ast_tree> FileReader::read() {
llvm::errs() << "Could not open input file: " << EC.message() << "\n"; llvm::errs() << "Could not open input file: " << EC.message() << "\n";
llvm::errs() << fmt::format("File: '{}'\n", file); llvm::errs() << fmt::format("File: '{}'\n", file);
llvm::errs() << "Use absolute path for now\n"; llvm::errs() << "Use absolute path for now\n";
return nullptr; return llvm::make_error<MissingFileError>(file);
} }
reader->setInput(fileOrErr.get()->getBuffer().str()); reader->setInput(fileOrErr.get()->getBuffer().str());
@ -285,17 +293,19 @@ std::unique_ptr<ast_tree> FileReader::read() {
void FileReader::dumpAST() { void FileReader::dumpAST() {
auto maybeAst = this->read(); auto maybeAst = this->read();
ast_tree ast; exprs::ast ast;
if (maybeAst) { if (!maybeAst) {
ast = *maybeAst; throw maybeAst.takeError();
} }
ast = *maybeAst;
std::string result = ""; std::string result = "";
for (auto &node : ast) { 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() { FileReader::~FileReader() {

View File

@ -24,7 +24,7 @@
#include "serene/sir/sir.hpp" #include "serene/sir/sir.hpp"
#include "mlir/IR/MLIRContext.h" #include "mlir/IR/MLIRContext.h"
#include "serene/expr.hpp" #include "serene/exprs/expression.h"
#include "serene/sir/dialect.hpp" #include "serene/sir/dialect.hpp"
#include "serene/sir/generator.hpp" #include "serene/sir/generator.hpp"
#include <memory> #include <memory>
@ -41,7 +41,7 @@ mlir::OwningModuleRef SIR::generate(::serene::Namespace *ns) {
SIR::~SIR() {} SIR::~SIR() {}
void dumpSIR(ast_tree &t) { void dumpSIR(exprs::ast &t) {
auto ns = new ::serene::Namespace("user", llvm::None); auto ns = new ::serene::Namespace("user", llvm::None);
SIR s{}; SIR s{};

View File

@ -24,7 +24,7 @@
#include "serene/state.hpp" #include "serene/state.hpp"
#include "serene/llvm/IR/Value.h" #include "serene/llvm/IR/Value.h"
#include "serene/namespace.hpp" #include "serene/namespace.h"
#include <fmt/core.h> #include <fmt/core.h>
#include <string> #include <string>

View File

@ -25,7 +25,7 @@
#include "serene/symbol.hpp" #include "serene/symbol.hpp"
#include "serene/expr.hpp" #include "serene/expr.hpp"
#include "serene/llvm/IR/Value.h" #include "serene/llvm/IR/Value.h"
#include "serene/namespace.hpp" #include "serene/namespace.h"
#include "serene/state.hpp" #include "serene/state.hpp"
#include <assert.h> #include <assert.h>
#include <fmt/core.h> #include <fmt/core.h>

View File

@ -37,30 +37,9 @@ TEST_CASE("Public Expression API", "[expression]") {
REQUIRE(sym->getType() == ExprType::Symbol); REQUIRE(sym->getType() == ExprType::Symbol);
REQUIRE(sym->toString() == "<Symbol [loc: 2:20:40 | 3:30:80]: example>"); REQUIRE(sym->toString() == "<Symbol [loc: 2:20:40 | 3:30:80]: example>");
};
TEST_CASE("List Expression", "[expression]") { auto list = makeAndCast<List>(*range.get(), sym);
std::unique_ptr<reader::LocationRange> range(dummyLocation());
auto sym = make<Symbol>(*range.get(), llvm::StringRef("example"));
auto list = make<List>(*range.get());
auto list2 = make<List>(*range.get(), list);
auto list3 = make<List>(*range.get(), llvm::ArrayRef<node>{list, list2, sym});
REQUIRE(list->toString() == "<List [loc: 2:20:40 | 3:30:80]: ->");
REQUIRE(list->getType() == ExprType::List);
REQUIRE(
list2->toString() ==
"<List [loc: 2:20:40 | 3:30:80]: <List [loc: 2:20:40 | 3:30:80]: ->>");
REQUIRE(list3->toString() ==
"<List [loc: 2:20:40 | 3:30:80]: <List [loc: 2:20:40 | 3:30:80]: -> "
"<List [loc: 2:20:40 | 3:30:80]: <List [loc: 2:20:40 | 3:30:80]: "
"->> <Symbol [loc: 2:20:40 | 3:30:80]: example>>");
list->elements.push_back(sym);
REQUIRE(list->getType() == ExprType::List);
REQUIRE(list->toString() == "<List [loc: 2:20:40 | 3:30:80]: <Symbol [loc: " REQUIRE(list->toString() == "<List [loc: 2:20:40 | 3:30:80]: <Symbol [loc: "
"2:20:40 | 3:30:80]: example>>"); "2:20:40 | 3:30:80]: example>>");
}; };

61
tests/exprs/list_test.cpp Normal file
View File

@ -0,0 +1,61 @@
/* -*- C++ -*-
* Serene programming language.
*
* Copyright (c) 2019-2021 Sameer Rahmani <lxsameer@gnu.org>
*
* 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<reader::LocationRange> range(dummyLocation());
auto sym = make<Symbol>(*range.get(), llvm::StringRef("example"));
auto list = make<List>(*range.get());
auto list2 = make<List>(*range.get(), list);
auto list3 = make<List>(*range.get(), llvm::ArrayRef<node>{list, list2, sym});
REQUIRE(list->toString() == "<List [loc: 2:20:40 | 3:30:80]: ->");
REQUIRE(list->getType() == ExprType::List);
REQUIRE(
list2->toString() ==
"<List [loc: 2:20:40 | 3:30:80]: <List [loc: 2:20:40 | 3:30:80]: ->>");
REQUIRE(list3->toString() ==
"<List [loc: 2:20:40 | 3:30:80]: <List [loc: 2:20:40 | 3:30:80]: -> "
"<List [loc: 2:20:40 | 3:30:80]: <List [loc: 2:20:40 | 3:30:80]: "
"->> <Symbol [loc: 2:20:40 | 3:30:80]: example>>");
auto l = llvm::dyn_cast<List>(list);
l.append(sym);
REQUIRE(list->getType() == ExprType::List);
REQUIRE(list->toString() == "<List [loc: 2:20:40 | 3:30:80]: <Symbol [loc: "
"2:20:40 | 3:30:80]: example>>");
};
} // namespace exprs
} // namespace serene