Setup the Environment test cases

This commit is contained in:
Sameer Rahmani 2022-03-05 14:28:37 +00:00
parent 6f4c6b3398
commit 500f366bab
7 changed files with 89 additions and 37 deletions

View File

@ -55,6 +55,8 @@ Then here is the list or parsers that we have considered
- [[https://julialang.org/research/julia-fresh-approach-BEKS.pdf][Julia: A Fresh Approach toNumerical Computing]]
** Cranelift
- [[https://github.com/bytecodealliance/wasmtime/tree/master/cranelift][Source tree]]
** Type Systems
- [[https://www.cs.cmu.edu/~rwh/courses/hott/][Homotopy Type Theory]]
** Memory management
- [[https://deepu.tech/memory-management-in-golang/][Visualizing memory management in Golang]]
- [[http://goog-perftools.sourceforge.net/doc/tcmalloc.html][TCMalloc : Thread-Caching Malloc]]

View File

@ -50,7 +50,7 @@ constexpr static auto EmptyNode = nullptr;
/// The base class of the expressions which provides the common interface for
/// the expressions to implement.
class Expression {
class SERENE_EXPORT Expression {
public:
/// The location range provide information regarding to where in the input
/// string the current expression is used.

View File

@ -23,6 +23,8 @@
#include "serene/exprs/expression.h"
#include "serene/namespace.h"
#include <serene/export.h>
#include <llvm/ADT/StringRef.h>
#include <string>
@ -33,25 +35,25 @@ namespace exprs {
/// This data structure represent the Lisp symbol. Just a symbol
/// in the context of the AST and nothing else.
class Symbol : public Expression {
class SERENE_EXPORT Symbol : public Expression {
public:
std::string name;
std::string nsName;
Symbol(reader::LocationRange &loc, llvm::StringRef name,
Symbol(const reader::LocationRange &loc, llvm::StringRef name,
llvm::StringRef currentNS)
: Expression(loc) {
// IMPORTANT NOTE: the `name` and `currentNS` should be valid string and
// already validated.
auto partDelimiter = name.find('/');
if (partDelimiter == std::string::npos) {
nsName = currentNS;
this->name = name;
nsName = currentNS.str();
this->name = name.str();
} else {
this->name = name.substr(partDelimiter + 1, name.size());
nsName = name.substr(0, partDelimiter);
this->name = name.substr(partDelimiter + 1, name.size()).str();
nsName = name.substr(0, partDelimiter).str();
}
};

View File

@ -28,7 +28,7 @@
*
* How to create a namespace ?
* The official way to create a namespace object is to use the `SereneContext`
* object and call `readNamespace` or `importNamespace`.
* object and call `readNamespace`, `importNamespace` or `makeNamespace`.
*/
// TODO: Add a mechanism to figure out whether a namespace has changed or not
@ -88,7 +88,8 @@ using Forms = std::vector<Form>;
/// Serene's namespaces are the unit of compilation. Any code that needs to be
/// compiled has to be in a namespace. The official way to create a new
/// namespace is to use the `makeNamespace` member function of `SereneContext`.
/// namespace is to use the `readNamespace`, `importNamespace` and
/// `makeNamespace` member functions of `SereneContext`.
class SERENE_EXPORT Namespace {
friend SereneContext;
@ -100,14 +101,18 @@ private:
std::atomic<uint> fn_counter = 0;
/// The content of the namespace. It should alway hold a semantically
/// correct AST. It means thet the AST that we want to stor here has
/// to pass the semantic analyzer.
/// correct AST. It means thet the AST that we want to store here has
/// to pass the semantic analyzer checks.
exprs::Ast tree;
SemanticEnvironments environments;
std::vector<llvm::StringRef> symbolList;
public:
std::string name;
llvm::Optional<std::string> filename;
/// Create a naw namespace with the given `name` and optional `filename` and
/// return a shared pointer to it in the given Serene context.
static NSPtr make(SereneContext &ctx, llvm::StringRef name,
@ -116,10 +121,6 @@ private:
Namespace(SereneContext &ctx, llvm::StringRef ns_name,
llvm::Optional<llvm::StringRef> filename);
public:
std::string name;
llvm::Optional<std::string> filename;
/// Create a new environment with the give \p parent as the parent,
/// push the environment to the internal environment storage and
/// return a reference to it. The namespace itself is the owner of
@ -136,13 +137,12 @@ public:
mlir::LogicalResult define(std::string &name, exprs::Node &node);
/// Add the given \p ast to the namespace and return any possible error.
/// The given \p ast will be added to a vector of ASTs that the namespace
/// have. In a normal compilation a Namespace will have a vector of ASTs
/// with only one element, but in a REPL like environment it might have
/// many elements.
/// The given \p ast will be added to a vector of ASTs by expanding
/// the tree vector to contain \p ast.
///
/// This function runs the semantic analyzer on the \p ast as well.
llvm::Error addTree(exprs::Ast &ast);
exprs::Ast &getTree();
const std::vector<llvm::StringRef> &getSymList() { return symbolList; };
@ -170,8 +170,6 @@ public:
/// Dumps the namespace with respect to the compilation phase
void dump();
void enqueueError(llvm::StringRef e) const;
~Namespace();
};

View File

@ -60,10 +60,6 @@ Namespace::Namespace(SereneContext &ctx, llvm::StringRef ns_name,
createEnv(nullptr);
};
void Namespace::enqueueError(llvm::StringRef e) const {
ctx.diagEngine->enqueueError(e);
}
SemanticEnv &Namespace::createEnv(SemanticEnv *parent) {
auto env = std::make_unique<SemanticEnv>(parent);
environments.push_back(std::move(env));
@ -89,14 +85,17 @@ mlir::LogicalResult Namespace::define(std::string &name, exprs::Node &node) {
}
exprs::Ast &Namespace::getTree() { return this->tree; }
llvm::Error Namespace::addTree(exprs::Ast &ast) {
// TODO: Remove the parse phase
// If the target phase is just parsing we don't want
// to run the semantic analyzer or anything beyond parser
if (ctx.getTargetPhase() == CompilationPhase::Parse) {
// we just want the raw AST
this->tree.insert(this->tree.end(), ast.begin(), ast.end());
return llvm::Error::success();
}
auto &rootEnv = getRootEnv();
auto state = semantics::makeAnalysisState(*this, rootEnv);
@ -119,7 +118,13 @@ uint Namespace::nextFnCounter() { return fn_counter++; };
SereneContext &Namespace::getContext() { return this->ctx; };
MaybeModuleOp Namespace::generate(unsigned offset) {
// The reason why we return an optional value instead of Errors
// is the way MLIR's diagnostic engine works. Passes may use
// the `emit` function of operations to report errors to the
// diagnostic engine. So we can't return any error diractly.
mlir::OpBuilder builder(&ctx.mlirContext);
// TODO: Fix the unknown location by pointing to the `ns` form
auto module = mlir::ModuleOp::create(builder.getUnknownLoc(),
llvm::Optional<llvm::StringRef>(name));
@ -160,6 +165,7 @@ void Namespace::dump() {
auto maybeModuleOp = generate();
if (!maybeModuleOp) {
llvm::errs() << "Failed to generate the IR.\n";
return;
}
@ -171,6 +177,11 @@ void Namespace::dump() {
};
MaybeModule Namespace::compileToLLVM() {
// The reason why we return an optional value instead of Errors
// is the way MLIR's diagnostic engine works. Passes may use
// the `emit` function of operations to report errors to the
// diagnostic engine. So we can't return any error diractly.
auto maybeModule = generate();
if (!maybeModule) {
@ -187,6 +198,11 @@ MaybeModule Namespace::compileToLLVM() {
};
MaybeModule Namespace::compileToLLVMFromOffset(unsigned offset) {
// The reason why we return an optional value instead of Errors
// is the way MLIR's diagnostic engine works. Passes may use
// the `emit` function of operations to report errors to the
// diagnostic engine. So we can't return any error diractly.
auto maybeModule = generate(offset);
if (!maybeModule) {

View File

@ -25,19 +25,21 @@
#include "serene/environment.h"
#include "serene/exprs/expression.h"
#include "serene/exprs/symbol.h"
#include "serene/reader/location.h"
#include "serene/serene.h"
#include "./test_helpers.cpp.inc"
#include <catch2/catch_all.hpp>
#include <catch2/catch_test_macros.hpp>
#include <llvm/ADT/StringRef.h>
#include <llvm/Support/Casting.h>
namespace serene {
TEST_CASE("Environment tests", "[environment]") {
std::unique_ptr<reader::LocationRange> range(dummyLocation());
exprs::Node sym =
exprs::make<exprs::Symbol>(*range.get(), llvm::StringRef("example"));
exprs::Node sym = exprs::make<exprs::Symbol>(*range, "example", "ns");
Environment<llvm::StringRef, exprs::Node> e;
llvm::Optional<exprs::Node> result = e.lookup("a");
@ -53,15 +55,47 @@ TEST_CASE("Environment tests", "[environment]") {
REQUIRE(result.hasValue());
CHECK(result.getValue() == sym);
Environment<llvm::StringRef, exprs::Node> e1(&e);
auto *fetchedSym = llvm::dyn_cast<exprs::Symbol>(result.getValue().get());
REQUIRE(fetchedSym != nullptr);
CHECK(fetchedSym->name == "example");
CHECK(fetchedSym->nsName == "ns");
result = e1.lookup("b");
REQUIRE_FALSE(result.hasValue());
SECTION("Testing the environment copy") {
Environment<llvm::StringRef, exprs::Node> e1(&e);
// It should lookup the value in the parent environment
result = e1.lookup("a");
REQUIRE(result.hasValue());
CHECK(result.getValue() == sym);
result = e1.lookup("b");
REQUIRE_FALSE(result.hasValue());
// It should lookup the value in the parent environment
result = e1.lookup("a");
REQUIRE(result.hasValue());
CHECK(result.getValue() == sym);
};
};
TEST_CASE("Test generic Environment usage", "[environment]") {
Environment<int, char> env;
auto result = env.lookup(3);
REQUIRE_FALSE(result);
auto status = env.insert_symbol(3, 'A');
REQUIRE(status.succeeded());
result = env.lookup(3);
REQUIRE(result);
CHECK(*result == 'A');
result = env.lookup(3);
REQUIRE(result);
CHECK(*result == 'A');
// Test the overwrite functionality
status = env.insert_symbol(3, 'B');
REQUIRE(status.succeeded());
result = env.lookup(3);
REQUIRE(result);
CHECK(*result == 'B');
}
} // namespace serene

View File

@ -18,8 +18,8 @@
#define CATCH_CONFIG_MAIN
#include "./context_tests.cpp.inc"
#include "./environment_tests.cpp.inc"
#include "./setup.cpp.inc"
//#include "./environment_tests.cpp.inc"
// #include "./errors/error_tests.cpp.inc"
// #include "./exprs/expression_tests.cpp.inc"
// #include "./exprs/list_tests.cpp.inc"