Fall back to the OOP design and setup the test env

This commit is contained in:
Sameer Rahmani 2021-04-10 15:36:16 +01:00
parent 944efcd1a9
commit e4b0823e49
13 changed files with 352 additions and 192 deletions

View File

@ -40,104 +40,127 @@ enum class ExprType {
List,
};
/// An abstract class which locatable expressions should inherit from
class Locatable {
public:
Locatable(reader::LocationRange loc) : location(loc){};
reader::LocationRange location;
};
/// 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**
class Expression {
public:
/// 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 <typename T> Expression(T e) : self(new Impl<T>(std::move(e))){};
reader::LocationRange location;
/// 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(const reader::LocationRange &loc) : location(loc){};
virtual ~Expression() = default;
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;
virtual ExprType getType() const = 0;
virtual std::string toString() const = 0;
};
/// 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> std::shared_ptr<T> make(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)...));
};
using node = std::shared_ptr<Expression>;
using ast = llvm::SmallVector<node, 0>;
// /// 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 <typename T> Expression(T e) : self(new Impl<T>(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<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 serene

View File

@ -33,25 +33,21 @@ namespace serene {
namespace exprs {
class List {
class List : public Expression {
public:
reader::LocationRange location;
llvm::SmallVector<Expression, 0> elements;
ast elements;
List(const reader::LocationRange &loc) : location(loc), elements({}){};
List(const reader::LocationRange &loc, Expression e) : location(loc) {
elements.push_back(e);
};
List(const List &l); // Copy ctor
List(List &&e) noexcept = default; // Move ctor
List(const reader::LocationRange &loc, llvm::ArrayRef<Expression> elems)
: location(loc), elements(elems.begin(), elems.end()){};
List(const reader::LocationRange &loc) : Expression(loc){};
List(const reader::LocationRange &loc, node e);
List(const reader::LocationRange &loc, llvm::ArrayRef<node> elems);
ExprType getType() { return ExprType::List; };
std::string toString();
ExprType getType() const;
std::string toString() const;
static bool classof(const Expression *e) {
return e->getType() == ExprType::List;
};
static bool classof(const Expression *e);
~List() = default;
};

View File

@ -33,13 +33,18 @@ namespace serene {
namespace exprs {
struct Symbol : public Locatable {
struct Symbol : public Expression {
std::string name;
Symbol(reader::LocationRange loc, llvm::StringRef name)
: Locatable(loc), name(name){};
ExprType getType() { return ExprType::Symbol; };
std::string toString();
Symbol(reader::LocationRange &loc, llvm::StringRef name)
: Expression(loc), name(name){};
ExprType getType() const;
std::string toString() const;
static bool classof(const Expression *e);
~Symbol() = default;
};
} // namespace exprs

View File

@ -40,7 +40,7 @@ struct Location {
int line;
int col;
::std::string toString();
::std::string toString() const;
};
class LocationRange {

View File

@ -27,18 +27,10 @@
namespace serene {
namespace exprs {
Expression &Expression::operator=(const Expression &e) {
Expression tmp(e);
*this = std::move(tmp);
return *this;
};
// template <typename T, typename... Args>
// T* make(Args &&...args) {
// return new T(std::forward<Args>(args)...);
// };
ExprType Expression::getType() { return self->getType(); };
std::string Expression::toString() { return self->toString(); }
template <typename T> std::unique_ptr<T> *Expression::to() {
return &this->self;
}
} // namespace exprs
} // namespace serene

View File

@ -27,16 +27,31 @@
namespace serene {
namespace exprs {
std::string List::toString() {
List::List(const List &l) : Expression(l.location){};
List::List(const reader::LocationRange &loc, node e) : Expression(loc) {
elements.push_back(std::move(e));
};
List::List(const reader::LocationRange &loc, llvm::ArrayRef<node> elems)
: Expression(loc), elements(elems.begin(), elems.end()){};
ExprType List::getType() const { return ExprType::List; };
std::string List::toString() const {
std::string s{this->elements.empty() ? "-" : ""};
for (auto &n : this->elements) {
s = llvm::formatv("{0} {1}", s, n.toString());
s = llvm::formatv("{0} {1}", s, n->toString());
}
return llvm::formatv("<List [loc: {0} | {1}]: {2}>",
this->location.start.toString(),
this->location.end.toString(), s);
}
};
bool List::classof(const Expression *e) {
return e->getType() == ExprType::List;
};
} // namespace exprs
} // namespace serene

View File

@ -27,11 +27,19 @@
namespace serene {
namespace exprs {
std::string Symbol::toString() {
ExprType Symbol::getType() const { return ExprType::Symbol; };
std::string Symbol::toString() const {
return llvm::formatv("<Symbol [loc: {0} | {1}]: {2}>",
this->location.start.toString(),
this->location.end.toString(), this->name);
}
bool Symbol::classof(const Expression *e) {
return e->getType() == ExprType::List;
};
} // namespace exprs
} // namespace serene

View File

@ -35,7 +35,7 @@ LocationRange::LocationRange(const LocationRange &loc) {
}
/// Return the string represenation of the location.
std::string Location::toString() {
std::string Location::toString() const {
return llvm::formatv("{0}:{1}:{2}", line, col, pos);
};

View File

@ -0,0 +1,69 @@
/* -*- 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/expression.h"
#include "serene/exprs/list.h"
#include "serene/exprs/symbol.h"
#include "llvm/ADT/ArrayRef.h"
namespace serene {
namespace exprs {
TEST_CASE("Public Expression API", "[expression]") {
std::unique_ptr<reader::LocationRange> range(dummyLocation());
auto sym = make<Symbol>(*range.get(), "example");
REQUIRE(sym->getType() == ExprType::Symbol);
REQUIRE(sym->toString() == "<Symbol [loc: 2:20:40 | 3:30:80]: example>");
};
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>>");
list->elements.push_back(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

View File

@ -0,0 +1,40 @@
/* -*- 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/expression.h"
#include "serene/exprs/symbol.h"
namespace serene {
namespace exprs {
TEST_CASE("Public Symbol API", "[expression]") {
std::unique_ptr<reader::LocationRange> range(dummyLocation());
auto sym = make<Symbol>(*range.get(), "example");
REQUIRE(sym->getType() == ExprType::Symbol);
REQUIRE(sym->toString() == "<Symbol [loc: 2:20:40 | 3:30:80]: example>");
};
} // namespace exprs
} // namespace serene

View File

@ -1,65 +0,0 @@
#include "catch2/catch.hpp"
#include "serene/exprs/expression.h"
#include "serene/exprs/list.h"
#include "serene/exprs/symbol.h"
#include "llvm/ADT/ArrayRef.h"
#include <iostream>
namespace serene {
namespace exprs {
reader::LocationRange *dummyLocation() {
reader::Location start;
reader::Location end;
start.line = 2;
start.col = 20;
start.pos = 40;
end.line = 3;
end.col = 30;
end.pos = 80;
return new reader::LocationRange(start, end);
}
TEST_CASE("Symbol Expression", "[expression]") {
// return new reader::LocationRange(start, end);
std::unique_ptr<reader::LocationRange> range(dummyLocation());
Expression sym = Expression::make<Symbol>(*range.get(), "example");
REQUIRE(sym.getType() == ExprType::Symbol);
REQUIRE(sym.toString() == "<Symbol [loc: 2:20:40 | 3:30:80]: example>");
};
TEST_CASE("List Expression", "[expression]") {
// return new reader::LocationRange(start, end);
std::unique_ptr<reader::LocationRange> range(dummyLocation());
Expression sym = Expression::make<Symbol>(*range.get(), "example");
Expression list = Expression::make<List>(*range.get());
Expression list2 = Expression::make<List>(*range.get(), list);
Expression list3 = Expression::make<List>(
*range.get(), llvm::ArrayRef<Expression>{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 = list.to<List>();
l->get()->elements.push_back(sym);
REQUIRE(list.getType() == ExprType::List);
REQUIRE(list.toString() == "<List [loc: 2:20:40 | 3:30:80]: ->");
};
} // namespace exprs
} // namespace serene

View File

@ -1,3 +1,28 @@
/* -*- 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.
*/
#define CATCH_CONFIG_MAIN
#include "./exprs/tests.cpp"
#include "./exprs/expression_tests.cpp.inc"
#include "./exprs/symbol_tests.cpp.inc"
#include "catch2/catch.hpp"

View File

@ -0,0 +1,52 @@
/* -*- 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 TEST_HEALPERS_H
#define TEST_HEALPERS_H
#include "catch2/catch.hpp"
#include "serene/reader/location.h"
namespace serene {
namespace exprs {
reader::LocationRange *dummyLocation() {
reader::Location start;
reader::Location end;
start.line = 2;
start.col = 20;
start.pos = 40;
end.line = 3;
end.col = 30;
end.pos = 80;
return new reader::LocationRange(start, end);
};
} // namespace exprs
} // namespace serene
#endif