Implement a simpler error model
ci/woodpecker/push/build Pipeline was successful Details
ci/woodpecker/push/docs Pipeline was successful Details

This commit is contained in:
Sameer Rahmani 2023-08-11 22:41:20 +01:00
parent b1cca14433
commit 9d894b662b
Signed by: lxsameer
GPG Key ID: B0A4AF28AB9FD90B
12 changed files with 229 additions and 63 deletions

View File

@ -185,11 +185,11 @@ if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
add_definitions(${LLVM_DEFINITIONS})
# /LLVM setup =================================================================
#add_subdirectory(serene-tblgen)
add_subdirectory(serene)
# include(tablegen-serene)
# Create the tools we use to compile Serene
#add_subdirectory(serene-tblgen)
# The compiled library code is here
# add_subdirectory(libserene)
# The static library containing builtin special forms and functions

View File

@ -14,7 +14,6 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
include_directories(${PROJECT_SOURCE_DIR}/serene-tblgen)
add_executable(serene-tblgen
serene/errors-backend.cpp
@ -28,8 +27,10 @@ set(SERENE_TABLEGEN $<TARGET_FILE:serene-tblgen> CACHE
set(SERENE_TABLEGEN_EXE SereneTablegen
CACHE STRING "Where to find the tbl-srn binary")
set(SERENE_TABLEGEN_TARGET SereneTablegen CACHE
STRING "Target name for the tbl-srn")
target_link_libraries(serene-tblgen PRIVATE LLVMTableGenGlobalISel ${llvm_libs})
target_include_directories(${PROJECT_SOURCE_DIR}/serene-tblgen)
set_target_properties(serene-tblgen PROPERTIES FOLDER "serene-tblgen")

View File

@ -20,6 +20,7 @@ target_sources(serene PRIVATE
commands/commands.cpp
jit/jit.cpp
errors.cpp
ast.cpp
namespace.cpp

47
serene/src/_errors.h Normal file
View File

@ -0,0 +1,47 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef _ERRORS_H
#define _ERRORS_H
#include <string>
// TODO: Autogenerate this file
namespace serene::errors {
enum class Type {
NSLoadError = 0,
NSAddToSMError,
InvalidDigitForNumber,
TwoFloatPoints,
InvalidCharacterForSymbol,
EOFWhileScaningAList,
// This error has to be the final error at all time. DO NOT CHANGE IT!
FINALERROR,
};
static std::string errorMessages[static_cast<int>(Type::FINALERROR) + 1] = {
"Faild to load the namespace", // NSLoadError
"Faild to add the namespace to the source manager", // NSAddToSMError
"Invalid number format", // InvalidDigitForNumber
"Invalid float number format", // TwoFloatPoints,
"Invalid symbol format", // InvalidCharacterForSymbol
"Reached the end of the file while scanning for a list", // EOFWhileScaningAList
};
} // namespace serene::errors
#endif

View File

@ -104,6 +104,7 @@ bool List::classof(const Expression *e) {
return e->getType() == TypeID::LIST;
};
void List::append(Node &n) { elements.push_back(std::move(n)); }
// ============================================================================
// String
// ============================================================================

View File

@ -134,6 +134,7 @@ struct List : public Expression {
std::string toString() const override;
~List() = default;
void append(Node &n);
static bool classof(const Expression *e);
};
@ -221,7 +222,7 @@ Node make(Args &&...args) {
/// passed to the constructor of type T.
/// \return A unique pointer to a value of type T.
template <typename T, typename... Args>
std::shared_ptr<T> makeAndCast(Args &&...args) {
std::unique_ptr<T> makeAndCast(Args &&...args) {
return std::make_unique<T>(std::forward<Args>(args)...);
};

35
serene/src/errors.cpp Normal file
View File

@ -0,0 +1,35 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "errors.h"
#include <llvm/Support/Casting.h>
#include <llvm/Support/Error.h>
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();
};
} // namespace serene::errors

66
serene/src/errors.h Normal file
View File

@ -0,0 +1,66 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef ERRORS_H
#define ERRORS_H
#include "_errors.h"
#include "location.h"
#include <system_error>
#include <llvm/Support/Error.h>
#include <llvm/Support/FormatVariadic.h>
namespace serene::errors {
class Error : public llvm::ErrorInfo<Error> {
public:
// We need this to make Error class a llvm::Error friendy implementation
static char ID;
Type type;
LocationRange location;
std::string msg;
void log(llvm::raw_ostream &os) const override { os << msg; }
std::error_code convertToErrorCode() const override {
// 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);
}
Error(Type errtype, LocationRange &loc) : type(errtype), location(loc){};
Error(Type errtype, LocationRange &loc, llvm::StringRef msg)
: type(errtype), location(loc), msg(msg.str()){};
LocationRange &where() { return location; };
};
llvm::Error make(Type t, LocationRange &loc, llvm::StringRef msg) {
return llvm::make_error<Error>(t, loc, msg);
}
llvm::Error make(Type t, LocationRange &loc) {
return llvm::make_error<Error>(t, loc);
}
}; // namespace serene::errors
#endif

View File

@ -18,7 +18,9 @@
#include "reader.h"
// #include "serene/errors.h"
#include "errors.h"
#include "jit/jit.h"
#include "utils.h"
// #include "serene/exprs/expression.h"
// #include "serene/exprs/list.h"
// #include "serene/exprs/number.h"
@ -46,7 +48,6 @@
namespace serene {
namespace reader {
// LocationRange::LocationRange(const LocationRange &loc) {
// start = loc.start.clone();
// end = loc.end.clone();
@ -98,11 +99,11 @@ void decLocation(Location &loc, const char *c) {
}
}
Reader::Reader(SereneContext &ctx, llvm::StringRef buffer, llvm::StringRef ns,
Reader::Reader(jit::JIT &engine, llvm::StringRef buffer, llvm::StringRef ns,
std::optional<llvm::StringRef> filename)
: ctx(ctx), ns(ns), filename(filename), buf(buffer),
: engine(engine), ns(ns), filename(filename), buf(buffer),
currentLocation(Location(ns, filename)) {
UNUSED(this->ctx);
UNUSED(this->engine);
READER_LOG("Setting the first char of the buffer");
currentChar = buf.begin() - 1;
currentPos = 1;
@ -110,9 +111,9 @@ Reader::Reader(SereneContext &ctx, llvm::StringRef buffer, llvm::StringRef ns,
currentLocation.col = 1;
};
Reader::Reader(SereneContext &ctx, llvm::MemoryBufferRef buffer,
Reader::Reader(jit::JIT &engine, llvm::MemoryBufferRef buffer,
llvm::StringRef ns, std::optional<llvm::StringRef> filename)
: Reader(ctx, buffer.getBuffer(), ns, filename){};
: Reader(engine, buffer.getBuffer(), ns, filename){};
Reader::~Reader() { READER_LOG("Destroying the reader"); }
@ -173,7 +174,8 @@ const char *Reader::nextChar(bool skipWhitespace, unsigned count) {
};
bool Reader::isEndOfBuffer(const char *c) {
return *c == '\0' || currentPos > buf.size() || ((const int)*c == EOF);
return *c == '\0' || currentPos > buf.size() ||
(static_cast<const int>(*c) == EOF);
};
Location Reader::getCurrentLocation() { return currentLocation.clone(); };
@ -208,7 +210,7 @@ bool Reader::isValidForIdentifier(char c) {
/// Reads a number,
/// \param neg whether to read a negative number or not.
exprs::MaybeNode Reader::readNumber(bool neg) {
ast::MaybeNode Reader::readNumber(bool neg) {
READER_LOG("Reading a number...");
std::string number(neg ? "-" : "");
bool floatNum = false;
@ -220,7 +222,7 @@ exprs::MaybeNode Reader::readNumber(bool neg) {
LocationRange loc(getCurrentLocation());
if (isdigit(*c) == 0) {
return errors::makeError(ctx, errors::InvalidDigitForNumber, loc);
return errors::make(errors::Type::InvalidDigitForNumber, loc);
}
for (;;) {
@ -231,7 +233,7 @@ exprs::MaybeNode Reader::readNumber(bool neg) {
if ((isdigit(*c) != 0) || *c == '.') {
if (*c == '.' && floatNum) {
loc = LocationRange(getCurrentLocation());
return errors::makeError(ctx, errors::TwoFloatPoints, loc);
return errors::make(errors::Type::TwoFloatPoints, loc);
}
if (*c == '.') {
@ -247,16 +249,16 @@ exprs::MaybeNode Reader::readNumber(bool neg) {
if (((std::isalpha(*c) != 0) && !empty) || empty) {
advance();
loc.start = getCurrentLocation();
return errors::makeError(ctx, errors::InvalidDigitForNumber, loc);
return errors::make(errors::Type::InvalidDigitForNumber, loc);
}
loc.end = getCurrentLocation();
return exprs::make<exprs::Number>(loc, number, neg, floatNum);
return ast::make<ast::Number>(loc, number, neg, floatNum);
};
/// Reads a symbol. If the symbol looks like a number
/// If reads it as number
exprs::MaybeNode Reader::readSymbol() {
ast::MaybeNode Reader::readSymbol() {
READER_LOG("Reading a symbol...");
LocationRange loc;
const auto *c = nextChar();
@ -271,7 +273,7 @@ exprs::MaybeNode Reader::readSymbol() {
msg = "An extra ')' is detected.";
}
return errors::makeError(ctx, errors::InvalidCharacterForSymbol, loc, msg);
return errors::make(errors::Type::InvalidCharacterForSymbol, loc, msg);
}
if (*c == '-') {
@ -307,17 +309,17 @@ exprs::MaybeNode Reader::readSymbol() {
// TODO: Make sure that `/` is not at the start or at the end of the symbol
loc.end = getCurrentLocation();
return exprs::makeSuccessfulNode<exprs::Symbol>(loc, sym, this->ns);
return ast::makeSuccessfulNode<ast::Symbol>(loc, sym, this->ns);
};
/// Reads a list recursively
exprs::MaybeNode Reader::readList() {
ast::MaybeNode Reader::readList() {
READER_LOG("Reading a list...");
const auto *c = nextChar();
advance();
auto list = exprs::makeAndCast<exprs::List>(getCurrentLocation());
auto list = ast::makeAndCast<ast::List>(getCurrentLocation());
// TODO: Replace the assert with an actual check.
assert(*c == '(');
@ -331,8 +333,7 @@ exprs::MaybeNode Reader::readList() {
advance(true);
advance();
list->location.end = getCurrentLocation();
return errors::makeError(ctx, errors::EOFWhileScaningAList,
list->location);
return errors::make(errors::Type::EOFWhileScaningAList, list->location);
}
switch (*c) {
@ -359,13 +360,13 @@ exprs::MaybeNode Reader::readList() {
};
/// Reads an expression by dispatching to the proper reader function.
exprs::MaybeNode Reader::readExpr() {
ast::MaybeNode Reader::readExpr() {
const auto *c = nextChar(true);
READER_LOG("Read char at `readExpr`: " << *c);
if (isEndOfBuffer(c)) {
return exprs::EmptyNode;
return ast::EmptyNode;
}
switch (*c) {
@ -383,7 +384,7 @@ exprs::MaybeNode Reader::readExpr() {
/// Reads all the expressions in the reader's buffer as an AST.
/// Each expression type (from the reader perspective) has a
/// reader function.
exprs::MaybeAst Reader::read() {
ast::MaybeAst Reader::read() {
for (size_t current_pos = 0; current_pos < buf.size();) {
const auto *c = nextChar(true);
@ -411,21 +412,21 @@ exprs::MaybeAst Reader::read() {
return std::move(this->ast);
};
exprs::MaybeAst read(SereneContext &ctx, const llvm::StringRef input,
llvm::StringRef ns,
std::optional<llvm::StringRef> filename) {
reader::Reader r(ctx, input, ns, filename);
ast::MaybeAst read(jit::JIT &engine, const llvm::StringRef input,
llvm::StringRef ns,
std::optional<llvm::StringRef> filename) {
Reader r(engine, input, ns, filename);
auto ast = r.read();
return ast;
}
exprs::MaybeAst read(SereneContext &ctx, const llvm::MemoryBufferRef input,
llvm::StringRef ns,
std::optional<llvm::StringRef> filename) {
reader::Reader r(ctx, input, ns, filename);
ast::MaybeAst read(jit::JIT &engine, const llvm::MemoryBufferRef input,
llvm::StringRef ns,
std::optional<llvm::StringRef> filename) {
Reader r(engine, input, ns, filename);
auto ast = r.read();
return ast;
}
} // namespace reader
} // namespace serene

View File

@ -43,10 +43,19 @@
#include <llvm/ADT/StringRef.h>
#include <llvm/Support/MemoryBufferRef.h>
#define READER_LOG(...) \
DEBUG_WITH_TYPE("READER", llvm::dbgs() \
<< "[READER]: " << __VA_ARGS__ << "\n");
namespace serene {
namespace jit {
class JIT;
} // namespace jit
/// Base reader class which reads from a string directly.
class Reader {
private:
jit::JIT &engine;
llvm::StringRef ns;
std::optional<llvm::StringRef> filename;
@ -77,36 +86,36 @@ private:
static bool isValidForIdentifier(char c);
// The property to store the ast tree
Ast ast;
ast::Ast ast;
MaybeNode readSymbol();
MaybeNode readNumber(bool);
MaybeNode readList();
MaybeNode readExpr();
ast::MaybeNode readSymbol();
ast::MaybeNode readNumber(bool);
ast::MaybeNode readList();
ast::MaybeNode readExpr();
bool isEndOfBuffer(const char *);
public:
Reader(llvm::StringRef buf, llvm::StringRef ns,
Reader(jit::JIT &engine, llvm::StringRef buf, llvm::StringRef ns,
std::optional<llvm::StringRef> filename);
Reader(llvm::MemoryBufferRef buf, llvm::StringRef ns,
Reader(jit::JIT &engine, llvm::MemoryBufferRef buf, llvm::StringRef ns,
std::optional<llvm::StringRef> filename);
// void setInput(const llvm::StringRef string);
/// Parses the the input and creates a possible AST out of it or errors
/// otherwise.
MaybeAst read();
ast::MaybeAst read();
~Reader();
};
/// Parses the given `input` string and returns a `Result<ast>`
/// which may contains an AST or an `llvm::Error`
MaybeAst read(llvm::StringRef input, llvm::StringRef ns,
std::optional<llvm::StringRef> filename);
MaybeAst read(llvm::MemoryBufferRef input, llvm::StringRef ns,
std::optional<llvm::StringRef> filename);
ast::MaybeAst read(llvm::StringRef input, llvm::StringRef ns,
std::optional<llvm::StringRef> filename);
ast::MaybeAst read(llvm::MemoryBufferRef input, llvm::StringRef ns,
std::optional<llvm::StringRef> filename);
} // namespace serene
#endif

View File

@ -16,12 +16,13 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "serene/source_mgr.h"
#include "source_mgr.h"
#include "serene/namespace.h"
#include "serene/reader/location.h"
#include "serene/reader/reader.h"
#include "serene/utils.h"
#include "errors.h"
#include "jit/jit.h"
#include "location.h"
#include "reader.h"
#include "utils.h"
#include <system_error>
@ -75,8 +76,8 @@ SourceMgr::MemBufPtr SourceMgr::findFileInLoadPath(const std::string &name,
return nullptr;
};
MaybeNS SourceMgr::readNamespace(SereneContext &ctx, std::string name,
reader::LocationRange importLoc) {
MaybeNS SourceMgr::readNamespace(jit::JIT &engine, std::string name,
LocationRange importLoc) {
std::string importedFile;
SMGR_LOG("Attempt to load namespace: " + name);
@ -84,7 +85,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(ctx, errors::NSLoadError, importLoc, msg);
return errors::make(errors::Type::NSLoadError, importLoc, msg);
}
auto bufferId = AddNewSourceBuffer(std::move(newBufOrErr), importLoc);
@ -93,7 +94,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(ctx, errors::NSAddToSMError, importLoc, msg);
return errors::make(errors::Type; : NSAddToSMError, importLoc, msg);
}
// Since we moved the buffer to be added as the source storage we
@ -101,8 +102,8 @@ MaybeNS SourceMgr::readNamespace(SereneContext &ctx, std::string name,
const auto *buf = getMemoryBuffer(bufferId);
// Read the content of the buffer by passing it the reader
auto maybeAst = reader::read(ctx, buf->getBuffer(), name,
std::optional(llvm::StringRef(importedFile)));
auto maybeAst = read(jit, buf->getBuffer(), name,
std::optional(llvm::StringRef(importedFile)));
if (!maybeAst) {
SMGR_LOG("Couldn't Read namespace: " + name);
@ -111,7 +112,7 @@ MaybeNS SourceMgr::readNamespace(SereneContext &ctx, std::string name,
// Create the NS and set the AST
auto ns =
ctx.makeNamespace(name, std::optional(llvm::StringRef(importedFile)));
engine.makeNamespace(name, std::optional(llvm::StringRef(importedFile)));
if (auto errs = ns->addTree(*maybeAst)) {
SMGR_LOG("Couldn't set the AST for namespace: " + name);
@ -122,7 +123,7 @@ MaybeNS SourceMgr::readNamespace(SereneContext &ctx, std::string name,
};
unsigned SourceMgr::AddNewSourceBuffer(std::unique_ptr<llvm::MemoryBuffer> f,
reader::LocationRange includeLoc) {
LocationRange includeLoc) {
SrcBuffer nb;
nb.buffer = std::move(f);
nb.importLoc = includeLoc;

View File

@ -20,6 +20,7 @@
#define SERENE_SOURCE_MGR_H
#include "location.h"
#include "namespace.h"
#include <llvm/ADT/SmallVector.h>
#include <llvm/ADT/StringMap.h>
@ -38,7 +39,9 @@
<< "[SMGR]: " << __VA_ARGS__ << "\n");
namespace serene {
class SereneContext;
namespace jit {
class JIT;
} // namespace jit
/// This class is quite similar to the `llvm::SourceMgr` in functionality. We
/// even borrowed some of the code from the original implementation but removed
@ -181,7 +184,7 @@ public:
///
/// \p importLoc is a location in the source code where the give namespace is
/// imported.
MaybeNS readNamespace(SereneContext &ctx, std::string name,
MaybeNS readNamespace(jit::JIT &engine, std::string name,
LocationRange importLoc);
};