diff --git a/libserene/include/serene/errors.h b/libserene/include/serene/errors.h index 74cff16..94f1f2e 100644 --- a/libserene/include/serene/errors.h +++ b/libserene/include/serene/errors.h @@ -24,24 +24,29 @@ #include "serene/export.h" #include - -#define GET_CLASS_DEFS -#include "serene/errors/errs.h.inc" - #include +namespace serene { +class SereneContext; +} // namespace serene + namespace serene::errors { /// Create and return a Serene flavored `llvm::Error` by passing the parameters /// directly to the constructor of type `E`. /// /// This is the official way of creating error objects in Serene. -template -SERENE_EXPORT llvm::Error makeError(Args &&...args) { - return llvm::make_error(std::forward(args)...); +template +SERENE_EXPORT llvm::Error makeError(SereneContext &ctx, ErrorType errtype, + Args &&...args) { + return llvm::make_error(ctx, errtype, std::forward(args)...); }; +/// Returns the messange that the given error \p e is holding. It doesn't cast +/// the error to a concrete error type. SERENE_EXPORT std::string getMessage(const llvm::Error &e); + +SERENE_EXPORT const ErrorVariant *getVariant(ErrorType t); } // namespace serene::errors #endif diff --git a/libserene/include/serene/errors/base.h b/libserene/include/serene/errors/base.h index 75151f5..99fafad 100644 --- a/libserene/include/serene/errors/base.h +++ b/libserene/include/serene/errors/base.h @@ -19,8 +19,12 @@ #ifndef SERENE_ERRORS_BASE_H #define SERENE_ERRORS_BASE_H +#include "serene/export.h" #include "serene/reader/location.h" +#define GET_CLASS_DEFS +#include "serene/errors/errs.h.inc" + #include #include @@ -28,59 +32,31 @@ namespace serene::errors { -// This class is used in the generated code -struct ErrorVariant { - const int id; - const std::string title; - const std::string desc; - const std::string help; +class SERENE_EXPORT Error : public llvm::ErrorInfo { +public: + static char ID; + ErrorType errorType; - static ErrorVariant make(const int id, const char *t, const char *d, - const char *h) { - return ErrorVariant(id, t, d, h); - }; - -private: - ErrorVariant(const int id, const char *t, const char *d, const char *h) - : id(id), title(t), desc(d), help(h){}; -}; - -class SereneError : public llvm::ErrorInfoBase { + SereneContext &ctx; reader::LocationRange location; std::string msg; -public: - constexpr static const int ID = -1; - - virtual void log(llvm::raw_ostream &os) const override { os << msg; }; - virtual std::string message() const override { return msg; }; + void log(llvm::raw_ostream &os) const override { os << msg; } std::error_code convertToErrorCode() const override { - return std::error_code(); - }; - - SereneError(reader::LocationRange &loc, std::string &msg) - : location(loc), msg(msg){}; - - SereneError(reader::LocationRange &loc, const char *msg) - : location(loc), msg(msg){}; - - SereneError(reader::LocationRange &loc, llvm::StringRef msg) - : location(loc), msg(msg.str()){}; - - SereneError(reader::LocationRange &loc) : location(loc){}; - - SereneError(SereneError &e) = delete; - - reader::LocationRange &where() { return location; }; - - static const void *classID() { return &ID; } - - bool isA(const void *const id) const override { - return id == classID() || llvm::ErrorInfoBase::isA(id); + // TODO: Fix this by creating a mapping from ErrorType to standard + // errc or return the ErrorType number instead + return std::make_error_code(std::errc::io_error); } - ~SereneError() = default; + Error(SereneContext &ctx, ErrorType errtype, reader::LocationRange &loc) + : errorType(errtype), ctx(ctx), location(loc){}; + + Error(SereneContext &ctx, ErrorType errtype, reader::LocationRange &loc, + llvm::StringRef msg) + : errorType(errtype), ctx(ctx), location(loc), msg(msg.str()){}; + + reader::LocationRange &where() { return location; }; }; }; // namespace serene::errors diff --git a/libserene/include/serene/errors/variant.h b/libserene/include/serene/errors/variant.h new file mode 100644 index 0000000..8c5df0c --- /dev/null +++ b/libserene/include/serene/errors/variant.h @@ -0,0 +1,43 @@ +/* -*- C++ -*- + * Serene Programming Language + * + * Copyright (c) 2019-2022 Sameer Rahmani + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef SERENE_ERRORS_VARIANT_H +#define SERENE_ERRORS_VARIANT_H + +#include + +namespace serene::errors { + +// This class is used in the generated code +struct ErrorVariant { + const int id; + const std::string title; + const std::string desc; + const std::string help; + + static ErrorVariant make(const int id, const char *t, const char *d, + const char *h) { + return ErrorVariant(id, t, d, h); + }; + +private: + ErrorVariant(const int id, const char *t, const char *d, const char *h) + : id(id), title(t), desc(d), help(h){}; +}; +} // namespace serene::errors +#endif diff --git a/libserene/include/serene/semantics.h b/libserene/include/serene/semantics.h index 0bff810..2313cc4 100644 --- a/libserene/include/serene/semantics.h +++ b/libserene/include/serene/semantics.h @@ -21,6 +21,7 @@ #include "serene/environment.h" #include "serene/errors.h" +#include "serene/export.h" #include "serene/utils.h" #include @@ -67,7 +68,7 @@ std::unique_ptr makeAnalysisState(Args &&...args) { /// /// \param state The semantic analysis state that keep track of the envs. /// \param form The actual AST in question. -AnalyzeResult analyze(AnalysisState &state, exprs::Ast &forms); +SERENE_EXPORT AnalyzeResult analyze(AnalysisState &state, exprs::Ast &forms); } // namespace semantics } // namespace serene diff --git a/libserene/lib/errors.cpp b/libserene/lib/errors.cpp index 0237c6b..5c83de7 100644 --- a/libserene/lib/errors.cpp +++ b/libserene/lib/errors.cpp @@ -18,12 +18,27 @@ #include "serene/errors.h" +#include "serene/errors/base.h" + +#include +#include + namespace serene::errors { +// We need this to make Error class a llvm::Error friendy implementation +char Error::ID; + std::string getMessage(const llvm::Error &e) { std::string msg; llvm::raw_string_ostream os(msg); os << e; return os.str(); }; + +const ErrorVariant *getVariant(ErrorType t) { + if ((0 <= (int)t) && (t < NUMBER_OF_ERRORS)) { + return &errorVariants[t]; + } + return nullptr; +}; } // namespace serene::errors diff --git a/libserene/lib/exprs/call.cpp b/libserene/lib/exprs/call.cpp index 1d25b1f..278ca04 100644 --- a/libserene/lib/exprs/call.cpp +++ b/libserene/lib/exprs/call.cpp @@ -52,6 +52,7 @@ bool Call::classof(const Expression *e) { MaybeNode Call::make(semantics::AnalysisState &state, List *list) { + auto &ctx = state.ns.getContext(); // TODO: replace this with a runtime check assert((list->count() != 0) && "Empty call? Seriously ?"); @@ -97,7 +98,8 @@ MaybeNode Call::make(semantics::AnalysisState &state, List *list) { if (!maybeResult.hasValue()) { std::string msg = llvm::formatv("Can't resolve the symbol '{0}'", sym->name); - return errors::makeError(sym->location, msg); + return errors::makeError(ctx, errors::CantResolveSymbol, sym->location, + msg); } targetNode = std::move(maybeResult.getValue()); @@ -122,8 +124,8 @@ MaybeNode Call::make(semantics::AnalysisState &state, List *list) { default: { std::string msg = llvm::formatv("Don't know how to call a '{0}'", stringifyExprType(first->getType())); - return errors::makeError(first->location, - msg); + return errors::makeError(ctx, errors::DontKnowHowToCallNode, + first->location, msg); } }; diff --git a/libserene/lib/exprs/def.cpp b/libserene/lib/exprs/def.cpp index 2cdb46e..2962d04 100644 --- a/libserene/lib/exprs/def.cpp +++ b/libserene/lib/exprs/def.cpp @@ -52,11 +52,13 @@ bool Def::classof(const Expression *e) { }; MaybeNode Def::make(semantics::AnalysisState &state, List *list) { + auto &ctx = state.ns.getContext(); + // TODO: Add support for docstring as the 3rd argument (4th element) if (list->count() != 3) { std::string msg = llvm::formatv("Expected 3 got {0}", list->count()); - return errors::makeError( - list->elements[0]->location, msg); + return errors::makeError(ctx, errors::DefWrongNumberOfArgs, + list->elements[0]->location, msg); } // Make sure that the list starts with a `def` @@ -68,8 +70,8 @@ MaybeNode Def::make(semantics::AnalysisState &state, List *list) { // Make sure that the first argument is a Symbol Symbol *binding = llvm::dyn_cast(list->elements[1].get()); if (binding == nullptr) { - return errors::makeError( - list->elements[1]->location); + return errors::makeError(ctx, errors::DefExpectSymbol, + list->elements[1]->location); } // Analyze the value diff --git a/libserene/lib/exprs/fn.cpp b/libserene/lib/exprs/fn.cpp index 755e3be..f2e6d25 100644 --- a/libserene/lib/exprs/fn.cpp +++ b/libserene/lib/exprs/fn.cpp @@ -66,8 +66,9 @@ MaybeNode Fn::make(semantics::AnalysisState &state, List *list) { // TODO: Add support for docstring as the 3rd argument (4th element) if (list->count() < 2) { - return errors::makeError( - list->elements[0]->location, "The argument list is mandatory."); + return errors::makeError(ctx, errors::FnNoArgsList, + list->elements[0]->location, + "The argument list is mandatory."); } Symbol *fnSym = llvm::dyn_cast(list->elements[0].get()); @@ -83,8 +84,8 @@ MaybeNode Fn::make(semantics::AnalysisState &state, List *list) { llvm::formatv("Arguments of a function has to be a list, got '{0}'", stringifyExprType(list->elements[1]->getType())); - return errors::makeError( - list->elements[1]->location, msg); + return errors::makeError(ctx, errors::FnArgsMustBeList, + list->elements[1]->location, msg); } Ast body; diff --git a/libserene/lib/jit/halley.cpp b/libserene/lib/jit/halley.cpp index 568565b..6fcd138 100644 --- a/libserene/lib/jit/halley.cpp +++ b/libserene/lib/jit/halley.cpp @@ -192,16 +192,17 @@ MaybeJITPtr Halley::lookup(exprs::Symbol &sym) const { auto *ns = ctx.getNS(sym.nsName); if (ns == nullptr) { - return errors::makeError( - sym.location, "Can't find the namespace in the context: " + sym.nsName); + return errors::makeError(ctx, errors::CantResolveSymbol, sym.location, + "Can't find the namespace in the context: " + + sym.nsName); } auto *dylib = ctx.getLatestJITDylib(*ns); // if (dylib == nullptr) { - return errors::makeError( - sym.location, "Don't know about namespace: " + sym.nsName); + return errors::makeError(ctx, errors::CantResolveSymbol, sym.location, + "Don't know about namespace: " + sym.nsName); } auto expectedSymbol = @@ -218,7 +219,8 @@ MaybeJITPtr Halley::lookup(exprs::Symbol &sym) const { llvm::raw_string_ostream os(errorMessage); llvm::handleAllErrors(expectedSymbol.takeError(), [&os](llvm::ErrorInfoBase &ei) { ei.log(os); }); - return errors::makeError(sym.location, os.str()); + return errors::makeError(ctx, errors::CantResolveSymbol, sym.location, + os.str()); } auto rawFPtr = expectedSymbol->getAddress(); @@ -226,8 +228,8 @@ MaybeJITPtr Halley::lookup(exprs::Symbol &sym) const { auto fptr = reinterpret_cast(rawFPtr); if (fptr == nullptr) { - return errors::makeError( - sym.location, "Lookup function is null!"); + return errors::makeError(ctx, errors::CantResolveSymbol, sym.location, + "Lookup function is null!"); } return fptr; @@ -277,8 +279,8 @@ llvm::Error Halley::addNS(Namespace &ns, reader::LocationRange &loc) { llvm::formatv("{0}#{1}", ns.name, ctx.getNumberOfJITDylibs(ns) + 1)); if (!newDylib) { - return errors::makeError( - loc, "Filed to create dylib for " + ns.name); + return errors::makeError(ctx, errors::CompilationError, loc, + "Filed to create dylib for " + ns.name); } ctx.pushJITDylib(ns, &(*newDylib)); @@ -287,7 +289,7 @@ llvm::Error Halley::addNS(Namespace &ns, reader::LocationRange &loc) { auto maybeModule = ns.compileToLLVM(); if (!maybeModule.hasValue()) { - return errors::makeError(loc); + return errors::makeError(ctx, errors::CompilationError, loc); } auto tsm = std::move(maybeModule.getValue()); diff --git a/libserene/lib/reader/reader.cpp b/libserene/lib/reader/reader.cpp index 5034836..0aa354d 100644 --- a/libserene/lib/reader/reader.cpp +++ b/libserene/lib/reader/reader.cpp @@ -227,7 +227,7 @@ exprs::MaybeNode Reader::readNumber(bool neg) { LocationRange loc(getCurrentLocation()); if (isdigit(*c) == 0) { - return errors::makeError(loc); + return errors::makeError(ctx, errors::InvalidDigitForNumber, loc); } for (;;) { @@ -238,7 +238,7 @@ exprs::MaybeNode Reader::readNumber(bool neg) { if ((isdigit(*c) != 0) || *c == '.') { if (*c == '.' && floatNum) { loc = LocationRange(getCurrentLocation()); - return errors::makeError(loc); + return errors::makeError(ctx, errors::TwoFloatPoints, loc); } if (*c == '.') { @@ -254,7 +254,7 @@ exprs::MaybeNode Reader::readNumber(bool neg) { if (((std::isalpha(*c) != 0) && !empty) || empty) { advance(); loc.start = getCurrentLocation(); - return errors::makeError(loc); + return errors::makeError(ctx, errors::InvalidDigitForNumber, loc); } loc.end = getCurrentLocation(); @@ -278,7 +278,7 @@ exprs::MaybeNode Reader::readSymbol() { msg = "An extra ')' is detected."; } - return errors::makeError(loc, msg); + return errors::makeError(ctx, errors::InvalidCharacterForSymbol, loc, msg); } if (*c == '-') { @@ -338,7 +338,8 @@ exprs::MaybeNode Reader::readList() { advance(true); advance(); list->location.end = getCurrentLocation(); - return errors::makeError(list->location); + return errors::makeError(ctx, errors::EOFWhileScaningAList, + list->location); } switch (*c) { diff --git a/libserene/lib/source_mgr.cpp b/libserene/lib/source_mgr.cpp index e408c35..163d750 100644 --- a/libserene/lib/source_mgr.cpp +++ b/libserene/lib/source_mgr.cpp @@ -84,7 +84,7 @@ MaybeNS SourceMgr::readNamespace(SereneContext &ctx, std::string name, if (newBufOrErr == nullptr) { auto msg = llvm::formatv("Couldn't find namespace '{0}'", name).str(); - return errors::makeError(importLoc, msg); + return errors::makeError(ctx, errors::NSLoadError, importLoc, msg); } auto bufferId = AddNewSourceBuffer(std::move(newBufOrErr), importLoc); @@ -93,7 +93,7 @@ MaybeNS SourceMgr::readNamespace(SereneContext &ctx, std::string name, if (bufferId == 0) { auto msg = llvm::formatv("Couldn't add namespace '{0}'", name).str(); - return errors::makeError(importLoc, msg); + return errors::makeError(ctx, errors::NSAddToSMError, importLoc, msg); } // Since we moved the buffer to be added as the source storage we diff --git a/libserene/tests/errors/error_tests.cpp.inc b/libserene/tests/errors/error_tests.cpp.inc index 7f1c07c..49efbb5 100644 --- a/libserene/tests/errors/error_tests.cpp.inc +++ b/libserene/tests/errors/error_tests.cpp.inc @@ -19,8 +19,10 @@ #ifndef SERENE_TEST_ERRORS_H #define SERENE_TEST_ERRORS_H +#include "serene/context.h" #include "serene/errors.h" +#include "../test_helpers.cpp.inc" #include #include @@ -33,25 +35,31 @@ namespace errors { TEST_CASE("Serene Error construction", "[errors]") { std::unique_ptr range(dummyLocation()); - llvm::Error err = llvm::make_error(*range, "test error"); + auto ctx = makeSereneContext(); + llvm::Error err = makeError(*ctx, PassFailureError, *range, "test error"); - auto unhandled = - llvm::handleErrors(std::move(err), [&](const PassFailureError &e) { - REQUIRE(e.message() == "test error"); - CHECK(errorVariants[e.ID].title == "PassFailureError"); - CHECK(errorVariants[e.ID].desc == "Pass Failure."); - CHECK(errorVariants[e.ID].help.empty()); - }); + auto unhandled = llvm::handleErrors(std::move(err), [&](const Error &e) { + REQUIRE(e.message() == "test error"); + const auto *v = getVariant(e.errorType); + REQUIRE(v != nullptr); + CHECK(v->title == "PassFailureError"); + CHECK(v->desc == "Pass Failure."); + CHECK(v->help.empty()); + }); CHECK(!unhandled); } TEST_CASE("getMessage function", "[errors]") { std::unique_ptr range(dummyLocation()); - if (auto err = llvm::make_error(*range, "test error")) { - CHECK(getMessage(err) == "test error"); - } + auto ctx = makeSereneContext(); + llvm::Error err = makeError(*ctx, PassFailureError, *range, "test error"); + + CHECK(getMessage(err) == "test error"); + + CHECK_ERR(PassFailureError, std::move(err)); } + }; // namespace errors } // namespace serene diff --git a/libserene/tests/exprs/list_tests.cpp.inc b/libserene/tests/exprs/list_tests.cpp.inc index 29a87ae..0fbcf50 100644 --- a/libserene/tests/exprs/list_tests.cpp.inc +++ b/libserene/tests/exprs/list_tests.cpp.inc @@ -16,6 +16,7 @@ * along with this program. If not, see . */ +#include "serene/errors.h" #include "serene/exprs/expression.h" #include "serene/exprs/list.h" #include "serene/exprs/symbol.h" @@ -33,8 +34,8 @@ namespace exprs { TEST_CASE("List Expression", "[expression]") { std::unique_ptr range(dummyLocation()); - Node sym = make(*range, "example"); - Node sym1 = make(*range, "example1"); + Node sym = make(*range, "example", "user"); + Node sym1 = make(*range, "example1", "user"); Node list = make(*range); @@ -52,21 +53,21 @@ TEST_CASE("List Expression", "[expression]") { auto list3 = make(*range, elements); CHECK(list3->toString() == - " > >"); + " > >"); auto l = llvm::dyn_cast(list.get()); l->append(sym1); REQUIRE(list->getType() == ExprType::List); - CHECK(list->toString() == ">"); + CHECK(list->toString() == ">"); l->append(sym); REQUIRE(l->count() == 2); auto expr = l->at(1); REQUIRE(expr.hasValue()); - CHECK(expr.getValue()->toString() == ""); + CHECK(expr.getValue()->toString() == ""); expr = l->at(2); REQUIRE_FALSE(expr.hasValue()); @@ -80,111 +81,116 @@ TEST_CASE("List semantic analysis of 'def'", "[semantic,expression,list]") { auto ctx = makeSereneContext(); auto ns = ctx->makeNamespace("user", llvm::None); - auto ast = - llvm::cantFail(reader::read(*ctx, "(def (a) b)", "user", llvm::None)); + auto ast = llvm::cantFail(READ("(def (a) b)")); SemanticEnv env; - semantics::AnalysisState state(ns, env); + semantics::AnalysisState state(*ns, env); auto afterAst = semantics::analyze(state, ast); REQUIRE_FALSE(afterAst); - // Fetch the first error - CHECK(afterAst.takeError() == ""); + CHECK_ERR(llvm::ErrorList, afterAst.takeError()); - ast = reader::read("(def a)"); - afterAst = reader::analyze(*ctx, ast.getValue()); + ast = llvm::cantFail(READ("(def a)")); + afterAst = semantics::analyze(state, ast); REQUIRE_FALSE(afterAst); - CHECK(afterAst.getError()[0]->toString() == ""); + CHECK(errors::getMessage(afterAst.takeError()) == + ""); - ast = reader::read("(def a b c)"); - afterAst = reader::analyze(*ctx, ast.getValue()); + ast = llvm::cantFail(READ("(def a b c)")); + afterAst = semantics::analyze(state, ast); REQUIRE_FALSE(afterAst); - CHECK(afterAst.getError()[0]->toString() == ""); + CHECK(errors::getMessage(afterAst.takeError()) == + ""); - ast = reader::read("(def a b)"); - afterAst = reader::analyze(*ctx, ast.getValue()); + ast = llvm::cantFail(READ("(def a b)")); + afterAst = semantics::analyze(state, ast); REQUIRE(afterAst); - CHECK(astToString(&afterAst.getValue()) == " >"); + CHECK(astToString(&(*afterAst)) == " >"); - ast = reader::read("(def a (fn () a))"); - afterAst = reader::analyze(*ctx, ast.getValue()); + ast = llvm::cantFail(READ("(def a (fn () a))")); + afterAst = semantics::analyze(state, ast); REQUIRE(afterAst); - CHECK(astToString(&afterAst.getValue()) == + CHECK(astToString(&(*afterAst)) == " to >>"); } -TEST_CASE("List semantic analysis for 'fn'", "[semantic]") { - auto ctx = makeSereneContext(); - auto ns = makeNamespace(*ctx, "user", llvm::None); - auto ast = reader::read("(fn)"); - auto afterAst = reader::analyze(*ctx, ast.getValue()); - REQUIRE_FALSE(afterAst); - REQUIRE(afterAst.getError().size() == 1); - CHECK(afterAst.getError()[0]->toString() == - ""); +// TEST_CASE("List semantic analysis for 'fn'", "[semantic]") { +// auto ctx = makeSereneContext(); +// auto ns = ctx->makeNamespace("user", llvm::None); +// auto ast = READ("(fn)"); +// auto afterAst = semantics::analyze(*ctx, *ast); - ast = reader::read("(fn ())"); - afterAst = reader::analyze(*ctx, ast.getValue()); - REQUIRE(afterAst); - CHECK(astToString(&afterAst.getValue()) == " to <>>"); +// REQUIRE_FALSE(afterAst); +// REQUIRE(afterAst.takeError().size() == 1); +// CHECK(afterAst.getError()[0]->toString() == +// ""); - ast = reader::read("(fn (a b c) a a a)"); - afterAst = reader::analyze(*ctx, ast.getValue()); - REQUIRE(afterAst); - CHECK(astToString(&afterAst.getValue()) == - " > to " - " >"); +// ast = reader::read("(fn ())"); +// afterAst = semantics::analyze(*ctx, ast.getValue()); +// REQUIRE(afterAst); +// CHECK(astToString(&afterAst.getValue()) == " to +// <>>"); - ast = reader::read("(fn () a b)"); - afterAst = reader::analyze(*ctx, ast.getValue()); - REQUIRE(afterAst); - CHECK(astToString(&afterAst.getValue()) == - " to >"); +// ast = reader::read("(fn (a b c) a a a)"); +// afterAst = semantics::analyze(*ctx, ast.getValue()); +// REQUIRE(afterAst); +// CHECK(astToString(&afterAst.getValue()) == +// " > to +// " +// " >"); - ast = reader::read("(fn (x) (fn (y) x) z)"); - afterAst = reader::analyze(*ctx, ast.getValue()); - REQUIRE(afterAst); - CHECK(astToString(&afterAst.getValue()) == - "> to > " - "to > >"); +// ast = reader::read("(fn () a b)"); +// afterAst = semantics::analyze(*ctx, ast.getValue()); +// REQUIRE(afterAst); +// CHECK(astToString(&afterAst.getValue()) == +// " to >"); - ast = reader::read("(fn (x) (def a b) (def b c))"); - afterAst = reader::analyze(*ctx, ast.getValue()); - REQUIRE(afterAst); - CHECK(astToString(&afterAst.getValue()) == - "> to > " - ">>"); -} +// ast = reader::read("(fn (x) (fn (y) x) z)"); +// afterAst = semantics::analyze(*ctx, ast.getValue()); +// REQUIRE(afterAst); +// CHECK(astToString(&afterAst.getValue()) == +// "> to > +// " "to > >"); -TEST_CASE("Complex semantic analysis", "[semantic]") { - auto ctx = makeSereneContext(); - auto ns = makeNamespace(*ctx, "user", llvm::None); - auto ast = - reader::read("(def a (fn (x) x))\n((def b (fn (x) (fn (y) y))))\n\n"); - auto afterAst = reader::analyze(*ctx, ast.getValue()); - REQUIRE(afterAst); - CHECK(astToString(&afterAst.getValue()) == - " > to >> " - "> to > to " - ">>> >"); +// ast = reader::read("(fn (x) (def a b) (def b c))"); +// afterAst = semantics::analyze(*ctx, ast.getValue()); +// REQUIRE(afterAst); +// CHECK(astToString(&afterAst.getValue()) == +// "> to > +// " +// ">>"); +// } - ctx = makeSereneContext(); - ns = makeNamespace(*ctx, "user", llvm::None); - ast = reader::read("((a b))"); - afterAst = reader::analyze(*ctx, ast.getValue()); - REQUIRE_FALSE(afterAst); - auto errs = afterAst.getError(); - CHECK(errs[0]->toString() == ""); +// TEST_CASE("Complex semantic analysis", "[semantic]") { +// auto ctx = makeSereneContext(); +// auto ns = makeNamespace(*ctx, "user", llvm::None); +// auto ast = +// reader::read("(def a (fn (x) x))\n((def b (fn (x) (fn (y) y))))\n\n"); +// auto afterAst = semantics::analyze(*ctx, ast.getValue()); +// REQUIRE(afterAst); +// CHECK(astToString(&afterAst.getValue()) == +// " > to >> " +// "> to > to " +// ">>> >"); - ctx = makeSereneContext(); - ns = makeNamespace(*ctx, "user", llvm::None); - ast = reader::read("(def a (fn (x) x)) (a b)"); - afterAst = reader::analyze(*ctx, ast.getValue()); - REQUIRE(afterAst); +// ctx = makeSereneContext(); +// ns = makeNamespace(*ctx, "user", llvm::None); +// ast = reader::read("((a b))"); +// afterAst = semantics::analyze(*ctx, ast.getValue()); +// REQUIRE_FALSE(afterAst); +// auto errs = afterAst.getError(); +// CHECK(errs[0]->toString() == ""); - CHECK(astToString(&afterAst.getValue()) == - " > to >> > to > >"); -} +// ctx = makeSereneContext(); +// ns = makeNamespace(*ctx, "user", llvm::None); +// ast = reader::read("(def a (fn (x) x)) (a b)"); +// afterAst = semantics::analyze(*ctx, ast.getValue()); +// REQUIRE(afterAst); + +// CHECK(astToString(&afterAst.getValue()) == +// " > to >> > to > >"); +// } } // namespace exprs } // namespace serene diff --git a/libserene/tests/reader/reader_tests.cpp.inc b/libserene/tests/reader/reader_tests.cpp.inc index c9290c9..45616c3 100644 --- a/libserene/tests/reader/reader_tests.cpp.inc +++ b/libserene/tests/reader/reader_tests.cpp.inc @@ -21,13 +21,9 @@ #include "serene/reader/reader.h" +#include "../test_helpers.cpp.inc" #include -// *IMPORTANT NOTE:* The `READ` macro is just a quick way to eliminate -// the overhead of writing the same function signature -// over and over again. Nothing special about it. -#define READ(input) reader::read(*ctx, input, "user", llvm::None) - namespace serene { namespace reader { diff --git a/libserene/tests/test_helpers.cpp.inc b/libserene/tests/test_helpers.cpp.inc index 14d0681..e321069 100644 --- a/libserene/tests/test_helpers.cpp.inc +++ b/libserene/tests/test_helpers.cpp.inc @@ -21,7 +21,19 @@ #include "serene/reader/location.h" -#include +// *IMPORTANT NOTE:* The `READ` macro is just a quick way to eliminate +// the overhead of writing the same function signature +// over and over again. Nothing special about it. +#define READ(input) reader::read(*ctx, input, "user", llvm::None) + +#define CHECK_ERR_MSG(e, s) CHECK(serene::errors::getMessage(e) == s) +// `llvm::Error`s has to be checked in the same scope. This macro makes +// the check easy while were testing the other aspects of the error. +// `t` is the concrete error type and `e` is the error instance. +#define CHECK_ERR(t, e) \ + auto unhandled = \ + llvm::handleErrors(e, [&](const Error &x) { CHECK(x.errorType == t); }); \ + CHECK(!unhandled); namespace serene { diff --git a/serene-tblgen/serene/errors-backend.cpp b/serene-tblgen/serene/errors-backend.cpp index ac7496d..ba7bc3b 100644 --- a/serene-tblgen/serene/errors-backend.cpp +++ b/serene-tblgen/serene/errors-backend.cpp @@ -65,15 +65,18 @@ static void inNamespace(llvm::StringRef name, llvm::raw_ostream &os, void ErrorsBackend::createErrorClass(const int id, llvm::Record &defRec, llvm::raw_ostream &os) { (void)records; + (void)id; const auto recName = defRec.getName(); - os << "class " << recName << " : public llvm::ErrorInfo<" << recName << ", " - << "SereneError> {\n" - << "public:\n" - << " using llvm::ErrorInfo<" << recName << ", " - << "SereneError>::ErrorInfo;\n" - << " constexpr static const int ID = " << id << ";\n};\n\n"; + os << " " << recName << ",\n"; + // os << "class " << recName << " : public llvm::ErrorInfo<" << recName << ", + // " + // << "SereneError> {\n" + // << "public:\n" + // << " using llvm::ErrorInfo<" << recName << ", " + // << "SereneError>::ErrorInfo;\n" + // << " constexpr static const int ID = " << id << ";\n};\n\n"; }; void ErrorsBackend::createNSBody(llvm::raw_ostream &os) { @@ -93,6 +96,7 @@ void ErrorsBackend::createNSBody(llvm::raw_ostream &os) { os << "#ifdef GET_CLASS_DEFS\n"; inNamespace("serene::errors", os, [&](llvm::raw_ostream &os) { + os << "enum ErrorType {\n"; for (size_t i = 0; i < indexList->size(); i++) { llvm::Record *defRec = indexList->getElementAsRecord(i); @@ -102,7 +106,9 @@ void ErrorsBackend::createNSBody(llvm::raw_ostream &os) { createErrorClass(i, *defRec, os); } + os << "};\n\n"; + os << "#define NUMBER_OF_ERRORS " << indexList->size() << "\n"; os << "static const ErrorVariant errorVariants[" << indexList->size() << "] = {\n"; @@ -164,7 +170,7 @@ void ErrorsBackend::run(llvm::raw_ostream &os) { llvm::emitSourceFileHeader("Serene's Errors collection", os); // DO NOT GUARD THE HEADER WITH #ifndef ... - os << "#include \"serene/errors/base.h\"\n\n#include " + os << "#include \"serene/errors/variant.h\"\n\n#include " "\n\n"; createNSBody(os);