Change the reader interface to return errors instead of emiting them
* Change the `Result` interface to let the compiler use the concrete type in place of a `Result`
This commit is contained in:
parent
3725d15521
commit
3fb6fb3740
|
@ -47,8 +47,11 @@ class Error
|
|||
std::string message;
|
||||
|
||||
public:
|
||||
Error(reader::LocationRange &loc, ErrorVariant *err, llvm::StringRef msg)
|
||||
: location(loc), variant(err), message(msg){};
|
||||
Error(reader::LocationRange &loc, ErrorVariant &err, llvm::StringRef msg)
|
||||
: location(loc), variant(&err), message(msg){};
|
||||
|
||||
Error(reader::LocationRange &loc, ErrorVariant &err)
|
||||
: location(loc), variant(&err){};
|
||||
|
||||
std::string toString() const;
|
||||
reader::LocationRange &where();
|
||||
|
|
|
@ -123,9 +123,9 @@ Result<Node, ErrorTree> makeSuccessfulNode(Args &&...args) {
|
|||
};
|
||||
|
||||
/// The hlper function to create an Errorful `Result<T,...>` (`T` would be
|
||||
/// either
|
||||
/// `Node` or `Ast` most of the time) with just one error creating from passing
|
||||
/// any argument to this function to the `serene::errors::Error` constructor.
|
||||
/// either `Node` or `Ast` most of the time) with just one error created from
|
||||
/// passing any argument to this function to the `serene::errors::Error`
|
||||
/// constructor.
|
||||
template <typename T, typename... Args>
|
||||
Result<T, ErrorTree> makeErrorful(Args &&...args) {
|
||||
std::vector<ErrorPtr> v{
|
||||
|
@ -133,6 +133,16 @@ Result<T, ErrorTree> makeErrorful(Args &&...args) {
|
|||
return Result<T, ErrorTree>::error(v);
|
||||
};
|
||||
|
||||
/// The hlper function to create an Error node (The failure case of a MaybeNod)
|
||||
/// with just one error created from passing any argument to this function to
|
||||
/// the `serene::errors::Error` constructor.
|
||||
template <typename... Args>
|
||||
MaybeNode makeErrorNode(Args &&...args) {
|
||||
std::vector<ErrorPtr> v{
|
||||
std::move(makeAndCast<errors::Error>(std::forward<Args>(args)...))};
|
||||
return MaybeNode::error(v);
|
||||
};
|
||||
|
||||
/// Convert the given AST to string by calling the `toString` method
|
||||
/// of each node.
|
||||
SERENE_EXPORT std::string astToString(const Ast *);
|
||||
|
|
|
@ -27,8 +27,11 @@
|
|||
* one char at a time till we reach the end of the input. Please note that
|
||||
* when we call the `advance` function to move forward in the buffer, we
|
||||
* can't go back. In order to look ahead in the buffer without moving in the
|
||||
* buffer we use the `nextChar` method-
|
||||
|
||||
* buffer we use the `nextChar` method.
|
||||
*
|
||||
* We have dedicated methods to read different forms like `list`, `symbol`
|
||||
* `number` and etc. Each of them return a `MaybeNode` that in the success
|
||||
* case contains the node and an `Error` on the failure case.
|
||||
*/
|
||||
|
||||
#ifndef SERENE_READER_READER_H
|
||||
|
@ -97,10 +100,10 @@ private:
|
|||
// The property to store the ast tree
|
||||
exprs::Ast ast;
|
||||
|
||||
exprs::Node readSymbol();
|
||||
exprs::Node readNumber(bool);
|
||||
exprs::Node readList();
|
||||
exprs::Node readExpr();
|
||||
exprs::MaybeNode readSymbol();
|
||||
exprs::MaybeNode readNumber(bool);
|
||||
exprs::MaybeNode readList();
|
||||
exprs::MaybeNode readExpr();
|
||||
|
||||
bool isEndOfBuffer(const char *);
|
||||
|
||||
|
@ -114,19 +117,19 @@ public:
|
|||
|
||||
/// Parses the the input and creates a possible AST out of it or errors
|
||||
/// otherwise.
|
||||
Result<exprs::Ast> read();
|
||||
exprs::MaybeAst read();
|
||||
|
||||
~Reader();
|
||||
};
|
||||
|
||||
/// Parses the given `input` string and returns a `Result<ast>`
|
||||
/// which may contains an AST or an `llvm::Error`
|
||||
Result<exprs::Ast> read(SereneContext &ctx, llvm::StringRef input,
|
||||
llvm::StringRef ns,
|
||||
llvm::Optional<llvm::StringRef> filename);
|
||||
Result<exprs::Ast> read(SereneContext &ctx, llvm::MemoryBufferRef input,
|
||||
llvm::StringRef ns,
|
||||
llvm::Optional<llvm::StringRef> filename);
|
||||
exprs::MaybeAst read(SereneContext &ctx, llvm::StringRef input,
|
||||
llvm::StringRef ns,
|
||||
llvm::Optional<llvm::StringRef> filename);
|
||||
exprs::MaybeAst read(SereneContext &ctx, llvm::MemoryBufferRef input,
|
||||
llvm::StringRef ns,
|
||||
llvm::Optional<llvm::StringRef> filename);
|
||||
} // namespace serene::reader
|
||||
|
||||
#endif
|
||||
|
|
|
@ -48,6 +48,15 @@ namespace serene {
|
|||
///
|
||||
/// In order check for a value being errorful or successful checkout the `ok`
|
||||
/// method or simply use the value as a conditiona.
|
||||
///
|
||||
/// This class is setup in a way tha you can us a value of type `T` in places
|
||||
/// that the compiler expects a `Result<T>`. So for example:
|
||||
///
|
||||
/// \code
|
||||
/// Result<int> fn() {return 2;}
|
||||
/// \endcode
|
||||
///
|
||||
/// works perfectly.
|
||||
template <typename T, typename E = llvm::Error>
|
||||
class SERENE_EXPORT Result {
|
||||
|
||||
|
@ -60,6 +69,33 @@ class SERENE_EXPORT Result {
|
|||
Result(InPlace i, Content &&c) : contents(i, std::forward<Content>(c)){};
|
||||
|
||||
public:
|
||||
constexpr Result(const T &v)
|
||||
: Result(std::in_place_index_t<0>(), std::move(v)){};
|
||||
|
||||
/// Return a pointer to the success case value of the result. It is
|
||||
/// important to check for the success case before calling this function.
|
||||
constexpr const T *getPointer() const { return &getValue(); }
|
||||
|
||||
/// Return a pointer to the success case value of the result. It is
|
||||
/// important to check for the success case before calling this function.
|
||||
T *getPointer() { return &getValue(); }
|
||||
|
||||
/// Return a pointer to the success case value of the result. It is
|
||||
/// important to check for the success case before calling this function.
|
||||
T *operator->() { return getPointer(); }
|
||||
|
||||
/// Return a pointer to the success case value of the result. It is
|
||||
/// important to check for the success case before calling this function.
|
||||
constexpr const T *operator->() const { return getPointer(); }
|
||||
|
||||
/// Dereference the success case and returns the value. It is
|
||||
/// important to check for the success case before calling this function.
|
||||
constexpr const T &operator*() const & { return getValue(); }
|
||||
|
||||
/// Dereference the success case and returns the value. It is
|
||||
/// important to check for the success case before calling this function.
|
||||
T &operator*() & { return getValue(); }
|
||||
|
||||
/// Create a succesfull result with the given value of type `T`.
|
||||
static Result success(T v) {
|
||||
return Result(std::in_place_index_t<0>(), std::move(v));
|
||||
|
|
|
@ -97,7 +97,7 @@ MaybeNode Call::make(SereneContext &ctx, List *list) {
|
|||
if (!maybeResult.hasValue()) {
|
||||
std::string msg =
|
||||
llvm::formatv("Can't resolve the symbol '{0}'", sym->name);
|
||||
return makeErrorful<Node>(sym->location, &errors::CantResolveSymbol, msg);
|
||||
return makeErrorful<Node>(sym->location, errors::CantResolveSymbol, msg);
|
||||
}
|
||||
|
||||
targetNode = maybeResult.getValue();
|
||||
|
@ -122,7 +122,7 @@ MaybeNode Call::make(SereneContext &ctx, List *list) {
|
|||
default: {
|
||||
std::string msg = llvm::formatv("Don't know how to call a '{0}'",
|
||||
stringifyExprType(first->getType()));
|
||||
return makeErrorful<Node>(first->location, &errors::DontKnowHowToCallNode,
|
||||
return makeErrorful<Node>(first->location, errors::DontKnowHowToCallNode,
|
||||
msg);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -53,7 +53,7 @@ MaybeNode Def::make(SereneContext &ctx, List *list) {
|
|||
if (list->count() != 3) {
|
||||
std::string msg = llvm::formatv("Expected 3 got {0}", list->count());
|
||||
return makeErrorful<Node>(list->elements[0]->location,
|
||||
&errors::DefWrongNumberOfArgs, msg);
|
||||
errors::DefWrongNumberOfArgs, msg);
|
||||
}
|
||||
|
||||
// Make sure that the list starts with a `def`
|
||||
|
@ -67,7 +67,7 @@ MaybeNode Def::make(SereneContext &ctx, List *list) {
|
|||
Symbol *binding = llvm::dyn_cast<Symbol>(list->elements[1].get());
|
||||
if (binding == nullptr) {
|
||||
return makeErrorful<Node>(list->elements[1]->location,
|
||||
&errors::DefExpectSymbol, "");
|
||||
errors::DefExpectSymbol, "");
|
||||
}
|
||||
|
||||
// Analyze the value
|
||||
|
|
|
@ -63,8 +63,7 @@ void Fn::setName(std::string n) { this->name = std::move(n); };
|
|||
MaybeNode Fn::make(SereneContext &ctx, List *list) {
|
||||
// TODO: Add support for docstring as the 3rd argument (4th element)
|
||||
if (list->count() < 2) {
|
||||
return makeErrorful<Node>(list->elements[0]->location,
|
||||
&errors::FnNoArgsList,
|
||||
return makeErrorful<Node>(list->elements[0]->location, errors::FnNoArgsList,
|
||||
"The argument list is mandatory.");
|
||||
}
|
||||
|
||||
|
@ -82,7 +81,7 @@ MaybeNode Fn::make(SereneContext &ctx, List *list) {
|
|||
stringifyExprType(list->elements[1]->getType()));
|
||||
|
||||
return makeErrorful<Node>(list->elements[1]->location,
|
||||
&errors::FnArgsMustBeList, msg);
|
||||
errors::FnArgsMustBeList, msg);
|
||||
}
|
||||
|
||||
Ast body;
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "serene/reader/reader.h"
|
||||
|
||||
#include "serene/errors/constants.h"
|
||||
#include "serene/exprs/expression.h"
|
||||
#include "serene/exprs/list.h"
|
||||
#include "serene/exprs/number.h"
|
||||
#include "serene/exprs/symbol.h"
|
||||
|
@ -154,7 +155,7 @@ bool Reader::isValidForIdentifier(char c) {
|
|||
|
||||
/// Reads a number,
|
||||
/// \param neg whether to read a negative number or not.
|
||||
exprs::Node Reader::readNumber(bool neg) {
|
||||
exprs::MaybeNode Reader::readNumber(bool neg) {
|
||||
READER_LOG("Reading a number...");
|
||||
std::string number(neg ? "-" : "");
|
||||
bool floatNum = false;
|
||||
|
@ -166,8 +167,7 @@ exprs::Node Reader::readNumber(bool neg) {
|
|||
LocationRange loc(getCurrentLocation());
|
||||
|
||||
if (isdigit(*c) == 0) {
|
||||
ctx.diagEngine->emitSyntaxError(loc, errors::InvalidDigitForNumber);
|
||||
terminate(ctx, 1);
|
||||
return exprs::makeErrorNode(loc, errors::InvalidDigitForNumber);
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
|
@ -205,7 +205,7 @@ exprs::Node Reader::readNumber(bool neg) {
|
|||
|
||||
/// Reads a symbol. If the symbol looks like a number
|
||||
/// If reads it as number
|
||||
exprs::Node Reader::readSymbol() {
|
||||
exprs::MaybeNode Reader::readSymbol() {
|
||||
READER_LOG("Reading a symbol...");
|
||||
LocationRange loc;
|
||||
const auto *c = nextChar();
|
||||
|
@ -220,9 +220,7 @@ exprs::Node Reader::readSymbol() {
|
|||
msg = "An extra ')' is detected.";
|
||||
}
|
||||
|
||||
ctx.diagEngine->emitSyntaxError(loc, errors::InvalidCharacterForSymbol,
|
||||
msg);
|
||||
terminate(ctx, 1);
|
||||
return exprs::makeErrorNode(loc, errors::InvalidCharacterForSymbol, msg);
|
||||
}
|
||||
|
||||
if (*c == '-') {
|
||||
|
@ -254,11 +252,11 @@ exprs::Node Reader::readSymbol() {
|
|||
}
|
||||
|
||||
loc.end = getCurrentLocation();
|
||||
return exprs::make<exprs::Symbol>(loc, sym);
|
||||
return exprs::makeSuccessfulNode<exprs::Symbol>(loc, sym);
|
||||
};
|
||||
|
||||
/// Reads a list recursively
|
||||
exprs::Node Reader::readList() {
|
||||
exprs::MaybeNode Reader::readList() {
|
||||
READER_LOG("Reading a list...");
|
||||
|
||||
const auto *c = nextChar();
|
||||
|
@ -278,9 +276,7 @@ exprs::Node Reader::readList() {
|
|||
advance(true);
|
||||
advance();
|
||||
list->location.end = getCurrentLocation();
|
||||
ctx.diagEngine->emitSyntaxError(list->location,
|
||||
errors::EOFWhileScaningAList);
|
||||
terminate(ctx, 1);
|
||||
return exprs::makeErrorNode(list->location, errors::EOFWhileScaningAList);
|
||||
}
|
||||
|
||||
switch (*c) {
|
||||
|
@ -293,22 +289,27 @@ exprs::Node Reader::readList() {
|
|||
|
||||
default:
|
||||
advance(true);
|
||||
list->append(readExpr());
|
||||
auto expr = readExpr();
|
||||
if (!expr) {
|
||||
return expr;
|
||||
}
|
||||
|
||||
list->append(expr.getValue());
|
||||
}
|
||||
|
||||
} while (!list_terminated);
|
||||
|
||||
return list;
|
||||
return exprs::MaybeNode::success(list);
|
||||
};
|
||||
|
||||
/// Reads an expression by dispatching to the proper reader function.
|
||||
exprs::Node Reader::readExpr() {
|
||||
exprs::MaybeNode Reader::readExpr() {
|
||||
const auto *c = nextChar(true);
|
||||
|
||||
READER_LOG("Read char at `readExpr`: " << *c);
|
||||
|
||||
if (isEndOfBuffer(c)) {
|
||||
return nullptr;
|
||||
return exprs::EmptyNode;
|
||||
}
|
||||
|
||||
switch (*c) {
|
||||
|
@ -326,7 +327,7 @@ exprs::Node 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.
|
||||
Result<exprs::Ast> Reader::read() {
|
||||
exprs::MaybeAst Reader::read() {
|
||||
|
||||
for (size_t current_pos = 0; current_pos < buf.size();) {
|
||||
const auto *c = nextChar(true);
|
||||
|
@ -337,29 +338,34 @@ Result<exprs::Ast> Reader::read() {
|
|||
|
||||
advance(true);
|
||||
|
||||
auto tmp{readExpr()};
|
||||
auto tmp = readExpr();
|
||||
|
||||
if (tmp) {
|
||||
this->ast.push_back(move(tmp));
|
||||
if (tmp.getValue() == nullptr) {
|
||||
break;
|
||||
}
|
||||
|
||||
this->ast.push_back(move(tmp.getValue()));
|
||||
|
||||
} else {
|
||||
break;
|
||||
return exprs::MaybeAst::error(tmp.getError());
|
||||
}
|
||||
}
|
||||
|
||||
return Result<exprs::Ast>::success(std::move(this->ast));
|
||||
return exprs::MaybeAst::success(std::move(this->ast));
|
||||
};
|
||||
|
||||
Result<exprs::Ast> read(SereneContext &ctx, const llvm::StringRef input,
|
||||
llvm::StringRef ns,
|
||||
llvm::Optional<llvm::StringRef> filename) {
|
||||
exprs::MaybeAst read(SereneContext &ctx, const llvm::StringRef input,
|
||||
llvm::StringRef ns,
|
||||
llvm::Optional<llvm::StringRef> filename) {
|
||||
reader::Reader r(ctx, input, ns, filename);
|
||||
auto ast = r.read();
|
||||
return ast;
|
||||
}
|
||||
|
||||
Result<exprs::Ast> read(SereneContext &ctx, const llvm::MemoryBufferRef input,
|
||||
llvm::StringRef ns,
|
||||
llvm::Optional<llvm::StringRef> filename) {
|
||||
exprs::MaybeAst read(SereneContext &ctx, const llvm::MemoryBufferRef input,
|
||||
llvm::StringRef ns,
|
||||
llvm::Optional<llvm::StringRef> filename) {
|
||||
reader::Reader r(ctx, input, ns, filename);
|
||||
|
||||
auto ast = r.read();
|
||||
|
|
Loading…
Reference in New Issue