Refactor the semantic analyzer to use the analysis state
* Refactor the semantic analyzer to use the new state struct which is just bunch of refs to the current ns and current env * Add the `environments` vector to ns to store the environments and few helper functions to use it * Add the `symbolList` (need a better name) to keep track of what symbols might be added to the namespace after calling `addTree`.
This commit is contained in:
parent
800105dbea
commit
b3d1151a8b
|
@ -50,9 +50,11 @@ public:
|
|||
|
||||
ExprType getType() const override;
|
||||
std::string toString() const override;
|
||||
MaybeNode analyze(SereneContext & /*ctx*/) override;
|
||||
void generateIR(serene::Namespace & /*ns*/,
|
||||
mlir::ModuleOp & /*m*/) override{};
|
||||
MaybeNode analyze(semantics::AnalysisState &state) override;
|
||||
void generateIR(serene::Namespace &ns, mlir::ModuleOp &m) override {
|
||||
UNUSED(ns);
|
||||
UNUSED(m);
|
||||
};
|
||||
|
||||
static bool classof(const Expression *e);
|
||||
|
||||
|
@ -61,9 +63,9 @@ public:
|
|||
/// is supposed to be used in the semantic analysis phase.
|
||||
///
|
||||
/// \param ctx The semantic analysis context object.
|
||||
/// \param env The environment that this node exists in.
|
||||
/// \param list the list in question.
|
||||
|
||||
static MaybeNode make(SereneContext &ctx, List *list);
|
||||
static MaybeNode make(semantics::AnalysisState &state, List *list);
|
||||
|
||||
~Call() = default;
|
||||
};
|
||||
|
|
|
@ -49,8 +49,9 @@ public:
|
|||
|
||||
ExprType getType() const override;
|
||||
std::string toString() const override;
|
||||
MaybeNode analyze(SereneContext & /*ctx*/) override;
|
||||
void generateIR(serene::Namespace & /*ns*/, mlir::ModuleOp & /*m*/) override;
|
||||
|
||||
MaybeNode analyze(semantics::AnalysisState &state) override;
|
||||
void generateIR(serene::Namespace &ns, mlir::ModuleOp &m) override;
|
||||
|
||||
static bool classof(const Expression *e);
|
||||
|
||||
|
@ -58,9 +59,9 @@ public:
|
|||
/// correct `def` form like `(def blah value)`. This function
|
||||
/// is supposed to be used in the semantic analysis phase.
|
||||
///
|
||||
/// \param ctx The semantic analysis context object.
|
||||
/// \param state is the semantic analysis state to use in creation time.
|
||||
/// \param list the list containing the `def` form
|
||||
static MaybeNode make(SereneContext &ctx, List *list);
|
||||
static MaybeNode make(semantics::AnalysisState &state, List *list);
|
||||
|
||||
~Def() = default;
|
||||
};
|
||||
|
|
|
@ -22,7 +22,9 @@
|
|||
#include "serene/context.h"
|
||||
#include "serene/errors/error.h"
|
||||
#include "serene/exprs/traits.h"
|
||||
#include "serene/namespace.h"
|
||||
#include "serene/reader/location.h"
|
||||
#include "serene/semantics.h"
|
||||
#include "serene/utils.h"
|
||||
|
||||
#include <mlir/IR/BuiltinOps.h>
|
||||
|
@ -69,8 +71,8 @@ public:
|
|||
/// another node. For example to change from a List containing `(def a b)`
|
||||
/// to a `Def` node that represents defining a new binding.
|
||||
///
|
||||
/// \param ctx is the context object of the semantic analyzer.
|
||||
virtual MaybeNode analyze(SereneContext &ctx) = 0;
|
||||
/// \param state is the analysis state object of the semantic analyzer.
|
||||
virtual MaybeNode analyze(semantics::AnalysisState &state) = 0;
|
||||
|
||||
/// Genenates the correspondig SLIR of the expressoin and attach it to the
|
||||
/// given module.
|
||||
|
|
|
@ -53,8 +53,8 @@ public:
|
|||
|
||||
ExprType getType() const override;
|
||||
std::string toString() const override;
|
||||
MaybeNode analyze(SereneContext & /*ctx*/) override;
|
||||
void generateIR(serene::Namespace & /*ns*/, mlir::ModuleOp & /*m*/) override;
|
||||
MaybeNode analyze(semantics::AnalysisState &state) override;
|
||||
void generateIR(serene::Namespace &ns, mlir::ModuleOp &m) override;
|
||||
|
||||
static bool classof(const Expression *e);
|
||||
|
||||
|
@ -63,9 +63,9 @@ public:
|
|||
/// of a function, for exmaple: `(fn (args1 arg2) body)`.This function
|
||||
/// is supposed to be used in the semantic analysis phase.
|
||||
///
|
||||
/// \param ctx The semantic analysis context object.
|
||||
/// \param state is the semantic analysis state to use.
|
||||
/// \param list the list containing the `fn` form
|
||||
static MaybeNode make(SereneContext &ctx, List *list);
|
||||
static MaybeNode make(semantics::AnalysisState &state, List *list);
|
||||
|
||||
void setName(std::string);
|
||||
~Fn() = default;
|
||||
|
|
|
@ -72,7 +72,7 @@ public:
|
|||
/// by the for loop.
|
||||
std::vector<Node>::iterator end();
|
||||
|
||||
MaybeNode analyze(SereneContext &ctx) override;
|
||||
MaybeNode analyze(semantics::AnalysisState &state) override;
|
||||
// NOLINTNEXTLINE(readability-named-parameter)
|
||||
void generateIR(serene::Namespace &, mlir::ModuleOp &) override{};
|
||||
|
||||
|
|
|
@ -47,8 +47,8 @@ struct Number : public Expression {
|
|||
ExprType getType() const override;
|
||||
std::string toString() const override;
|
||||
|
||||
MaybeNode analyze(SereneContext &ctx) override;
|
||||
void generateIR(serene::Namespace & /*ns*/, mlir::ModuleOp & /*m*/) override;
|
||||
MaybeNode analyze(semantics::AnalysisState &state) override;
|
||||
void generateIR(serene::Namespace &ns, mlir::ModuleOp &m) override;
|
||||
|
||||
// TODO: This is horrible, we need to fix it after the mvp
|
||||
int toI64() const;
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
|
||||
#include "serene/context.h"
|
||||
#include "serene/exprs/expression.h"
|
||||
#include "serene/namespace.h"
|
||||
|
||||
#include <llvm/ADT/StringRef.h>
|
||||
|
||||
|
@ -45,9 +46,11 @@ public:
|
|||
ExprType getType() const override;
|
||||
std::string toString() const override;
|
||||
|
||||
MaybeNode analyze(SereneContext & /*ctx*/) override;
|
||||
void generateIR(serene::Namespace & /*ns*/,
|
||||
mlir::ModuleOp & /*m*/) override{};
|
||||
MaybeNode analyze(semantics::AnalysisState &state) override;
|
||||
void generateIR(serene::Namespace &ns, mlir::ModuleOp &m) override {
|
||||
UNUSED(ns);
|
||||
UNUSED(m);
|
||||
};
|
||||
|
||||
~Symbol() = default;
|
||||
|
||||
|
|
|
@ -23,6 +23,8 @@
|
|||
* - A namespace may or may not be assiciated with a file
|
||||
* - The internal AST of a namespace is an evergrowing tree which may expand at
|
||||
* any given time. For example via iteration of a REPL
|
||||
* - `environments` vector is the owner of all the semantic envs
|
||||
* - The first env in the `environments` is the root env.
|
||||
*/
|
||||
|
||||
#ifndef SERENE_NAMESPACE_H
|
||||
|
@ -62,8 +64,13 @@ using Node = std::shared_ptr<Expression>;
|
|||
using Ast = std::vector<Node>;
|
||||
} // namespace exprs
|
||||
|
||||
using MaybeModule = llvm::Optional<llvm::orc::ThreadSafeModule>;
|
||||
using MaybeModuleOp = llvm::Optional<mlir::OwningOpRef<mlir::ModuleOp>>;
|
||||
using MaybeModule = llvm::Optional<llvm::orc::ThreadSafeModule>;
|
||||
using MaybeModuleOp = llvm::Optional<mlir::OwningOpRef<mlir::ModuleOp>>;
|
||||
using SemanticEnv = Environment<std::string, exprs::Node>;
|
||||
using SemanticEnvPtr = std::unique_ptr<SemanticEnv>;
|
||||
using SemanticEnvironments = std::vector<SemanticEnvPtr>;
|
||||
using Form = std::pair<SemanticEnv, exprs::Ast>;
|
||||
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
|
||||
|
@ -81,27 +88,41 @@ private:
|
|||
/// to pass the semantic analyzer.
|
||||
exprs::Ast tree;
|
||||
|
||||
SemanticEnvironments environments;
|
||||
|
||||
std::vector<llvm::StringRef> symbolList;
|
||||
|
||||
public:
|
||||
std::string name;
|
||||
llvm::Optional<std::string> filename;
|
||||
|
||||
/// The root environment of the namespace on the semantic analysis phase.
|
||||
/// Which is a mapping from names to AST nodes ( no evaluation ).
|
||||
Environment<std::string, exprs::Node> semanticEnv;
|
||||
|
||||
/// Th root environmanet to store the MLIR value during the IR generation
|
||||
/// phase.
|
||||
Environment<llvm::StringRef, mlir::Value> symbolTable;
|
||||
|
||||
Namespace(SereneContext &ctx, llvm::StringRef ns_name,
|
||||
llvm::Optional<llvm::StringRef> filename);
|
||||
|
||||
exprs::Ast &getTree();
|
||||
/// 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
|
||||
/// environments.
|
||||
SemanticEnv &createEnv(SemanticEnv *parent);
|
||||
|
||||
/// Expand the current tree of the namespace with the given \p ast by
|
||||
/// semantically analazing it first. If the give \p ast in not valid
|
||||
/// it will return analysis errors.
|
||||
errors::OptionalErrors expandTree(exprs::Ast &ast);
|
||||
/// Return a referenece to the top level (root) environment of ns.
|
||||
SemanticEnv &getRootEnv();
|
||||
|
||||
/// Define a new binding in the root environment with the given \p name
|
||||
/// and the given \p node. Defining a new binding with a name that
|
||||
/// already exists in legal and will overwrite the previous binding and
|
||||
/// the given name will point to a new value from now on.
|
||||
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.
|
||||
///
|
||||
/// This function runs the semantic analyzer on the \p ast as well.
|
||||
errors::OptionalErrors addTree(exprs::Ast &ast);
|
||||
exprs::Ast &getTree();
|
||||
|
||||
/// Increase the function counter by one
|
||||
uint nextFnCounter();
|
||||
|
|
|
@ -1,43 +0,0 @@
|
|||
/* -*- C++ -*-
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2021 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 SERENE_READER_SEMANTICS_H
|
||||
#define SERENE_READER_SEMANTICS_H
|
||||
|
||||
#include "serene/context.h"
|
||||
#include "serene/errors/error.h"
|
||||
#include "serene/exprs/expression.h"
|
||||
|
||||
namespace serene::reader {
|
||||
using AnalyzeResult = Result<exprs::Ast, std::vector<errors::ErrorPtr>>;
|
||||
/// The entry point to the Semantic analysis phase. It calls the `analyze`
|
||||
/// method of each node in the given AST and creates a new AST that contains a
|
||||
/// more comprehensive set of nodes in a semantically correct AST. If the
|
||||
/// `analyze` method of a node return a `nullptr` value as the `success` result
|
||||
/// (Checkout the `Result` type in `utils.h`) then the original node will be
|
||||
/// used instead. Also please note that in **Serene** Semantic errors
|
||||
/// represented as AST nodes as well. So you should expect an `analyze` method
|
||||
/// of a node to return a `Result<node>::Success(Error...)` in case of a
|
||||
/// semantic error.
|
||||
///
|
||||
/// \param ctx The semantic analysis context
|
||||
/// \param inputAst The raw AST to analyze and possibly rewrite.
|
||||
AnalyzeResult analyze(serene::SereneContext &ctx, exprs::Ast &inputAst);
|
||||
}; // namespace serene::reader
|
||||
|
||||
#endif
|
|
@ -0,0 +1,75 @@
|
|||
/* -*- C++ -*-
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2021 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 SERENE_SEMANTICS_H
|
||||
#define SERENE_SEMANTICS_H
|
||||
|
||||
#include "serene/environment.h"
|
||||
#include "serene/errors/error.h"
|
||||
#include "serene/utils.h"
|
||||
|
||||
#include <llvm/ADT/StringRef.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace serene {
|
||||
|
||||
namespace exprs {
|
||||
class Expression;
|
||||
using Node = std::shared_ptr<Expression>;
|
||||
using Ast = std::vector<Node>;
|
||||
}; // namespace exprs
|
||||
|
||||
class Namespace;
|
||||
using SemanticEnv = Environment<std::string, exprs::Node>;
|
||||
namespace semantics {
|
||||
|
||||
using AnalyzeResult = Result<exprs::Ast, std::vector<errors::ErrorPtr>>;
|
||||
|
||||
/// This struct represent the state necessary for each analysis job.
|
||||
struct AnalysisState {
|
||||
Namespace &ns;
|
||||
SemanticEnv &env;
|
||||
|
||||
AnalysisState(Namespace &ns, SemanticEnv &env) : ns(ns), env(env){};
|
||||
|
||||
std::unique_ptr<AnalysisState> moveToNewEnv();
|
||||
};
|
||||
|
||||
/// Create an new `AnalysisState` by forwarding all parameters off this
|
||||
/// function directly to the ctor of `AnalysisState` and returns a
|
||||
/// unique pointer to the state.
|
||||
template <typename... Args>
|
||||
std::unique_ptr<AnalysisState> makeAnalysisState(Args &&...args) {
|
||||
return std::make_unique<AnalysisState>(std::forward<Args>(args)...);
|
||||
};
|
||||
|
||||
/// The entry point to the Semantic analysis phase. It calls the `analyze`
|
||||
/// method of each node in the given \p form and creates a new AST that
|
||||
/// contains a more comprehensive set of nodes in a semantically correct
|
||||
/// AST. If the `analyze` method of a node return a `nullptr` value as the
|
||||
/// `success` result (Checkout the `Result` type in `utils.h`) then the
|
||||
/// original node will be used instead. Any possible error will return as
|
||||
/// the `error` case of the `Result` type.
|
||||
/// \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);
|
||||
|
||||
} // namespace semantics
|
||||
} // namespace serene
|
||||
#endif
|
|
@ -43,6 +43,7 @@ add_library(serene
|
|||
namespace.cpp
|
||||
source_mgr.cpp
|
||||
diagnostics.cpp
|
||||
semantics.cpp
|
||||
|
||||
# jit.cpp
|
||||
jit/engine.cpp
|
||||
|
@ -50,7 +51,6 @@ add_library(serene
|
|||
|
||||
# Reader
|
||||
reader/reader.cpp
|
||||
reader/semantics.cpp
|
||||
|
||||
# Errors
|
||||
errors/error.cpp
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
#include "serene/exprs/expression.h"
|
||||
#include "serene/exprs/list.h"
|
||||
#include "serene/exprs/symbol.h"
|
||||
#include "serene/reader/semantics.h"
|
||||
#include "serene/namespace.h"
|
||||
#include "serene/utils.h"
|
||||
|
||||
#include <llvm/Support/Casting.h>
|
||||
|
@ -40,8 +40,9 @@ std::string Call::toString() const {
|
|||
astToString(&this->params));
|
||||
}
|
||||
|
||||
MaybeNode Call::analyze(SereneContext &ctx) {
|
||||
UNUSED(ctx);
|
||||
MaybeNode Call::analyze(semantics::AnalysisState &state) {
|
||||
UNUSED(state);
|
||||
|
||||
return EmptyNode;
|
||||
};
|
||||
|
||||
|
@ -49,12 +50,13 @@ bool Call::classof(const Expression *e) {
|
|||
return e->getType() == ExprType::Call;
|
||||
};
|
||||
|
||||
MaybeNode Call::make(SereneContext &ctx, List *list) {
|
||||
MaybeNode Call::make(semantics::AnalysisState &state, List *list) {
|
||||
|
||||
// TODO: replace this with a runtime check
|
||||
assert((list->count() != 0) && "Empty call? Seriously ?");
|
||||
|
||||
// Let's find out what is the first element of the list
|
||||
auto maybeFirst = list->elements[0]->analyze(ctx);
|
||||
auto maybeFirst = list->elements[0]->analyze(state);
|
||||
|
||||
if (!maybeFirst) {
|
||||
// There's something wrong with the first element. Return the error
|
||||
|
@ -90,9 +92,7 @@ MaybeNode Call::make(SereneContext &ctx, List *list) {
|
|||
llvm_unreachable("Couldn't case to Symbol while the type is symbol!");
|
||||
}
|
||||
|
||||
// TODO: Lookup the symbol in the namespace via a method that looks
|
||||
// into the current environment.
|
||||
auto maybeResult = ctx.getCurrentNS().semanticEnv.lookup(sym->name);
|
||||
auto maybeResult = state.env.lookup(sym->name);
|
||||
|
||||
if (!maybeResult.hasValue()) {
|
||||
std::string msg =
|
||||
|
@ -100,7 +100,7 @@ MaybeNode Call::make(SereneContext &ctx, List *list) {
|
|||
return makeErrorful<Node>(sym->location, errors::CantResolveSymbol, msg);
|
||||
}
|
||||
|
||||
targetNode = maybeResult.getValue();
|
||||
targetNode = std::move(maybeResult.getValue());
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -127,7 +127,7 @@ MaybeNode Call::make(SereneContext &ctx, List *list) {
|
|||
}
|
||||
};
|
||||
|
||||
auto analyzedParams = reader::analyze(ctx, rawParams);
|
||||
auto analyzedParams = semantics::analyze(state, rawParams);
|
||||
|
||||
if (!analyzedParams) {
|
||||
return MaybeNode::error(analyzedParams.getError());
|
||||
|
|
|
@ -39,8 +39,9 @@ std::string Def::toString() const {
|
|||
this->value->toString());
|
||||
}
|
||||
|
||||
MaybeNode Def::analyze(SereneContext &ctx) {
|
||||
UNUSED(ctx);
|
||||
MaybeNode Def::analyze(semantics::AnalysisState &state) {
|
||||
UNUSED(state);
|
||||
|
||||
return EmptyNode;
|
||||
};
|
||||
|
||||
|
@ -48,7 +49,7 @@ bool Def::classof(const Expression *e) {
|
|||
return e->getType() == ExprType::Def;
|
||||
};
|
||||
|
||||
MaybeNode Def::make(SereneContext &ctx, List *list) {
|
||||
MaybeNode Def::make(semantics::AnalysisState &state, List *list) {
|
||||
// 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());
|
||||
|
@ -70,7 +71,7 @@ MaybeNode Def::make(SereneContext &ctx, List *list) {
|
|||
}
|
||||
|
||||
// Analyze the value
|
||||
MaybeNode value = list->elements[2]->analyze(ctx);
|
||||
MaybeNode value = list->elements[2]->analyze(state);
|
||||
Node analyzedValue;
|
||||
|
||||
// TODO: To refactor this logic into a generic function
|
||||
|
@ -99,8 +100,7 @@ MaybeNode Def::make(SereneContext &ctx, List *list) {
|
|||
tmp->setName(binding->name);
|
||||
}
|
||||
|
||||
auto result = ctx.getCurrentNS().semanticEnv.insert_symbol(binding->name,
|
||||
analyzedValue);
|
||||
auto result = state.ns.define(binding->name, analyzedValue);
|
||||
|
||||
if (result.succeeded()) {
|
||||
return makeSuccessfulNode<Def>(list->location, binding->name,
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
#include "serene/exprs/expression.h"
|
||||
#include "serene/exprs/list.h"
|
||||
#include "serene/exprs/symbol.h"
|
||||
#include "serene/reader/semantics.h"
|
||||
#include "serene/slir/dialect.h"
|
||||
#include "serene/slir/utils.h"
|
||||
|
||||
|
@ -51,8 +50,9 @@ std::string Fn::toString() const {
|
|||
this->body.empty() ? "<>" : astToString(&this->body));
|
||||
}
|
||||
|
||||
MaybeNode Fn::analyze(SereneContext &ctx) {
|
||||
UNUSED(ctx);
|
||||
MaybeNode Fn::analyze(semantics::AnalysisState &state) {
|
||||
UNUSED(state);
|
||||
|
||||
return EmptyNode;
|
||||
};
|
||||
|
||||
|
@ -60,7 +60,10 @@ bool Fn::classof(const Expression *e) { return e->getType() == ExprType::Fn; };
|
|||
|
||||
void Fn::setName(std::string n) { this->name = std::move(n); };
|
||||
|
||||
MaybeNode Fn::make(SereneContext &ctx, List *list) {
|
||||
MaybeNode Fn::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() < 2) {
|
||||
return makeErrorful<Node>(list->elements[0]->location, errors::FnNoArgsList,
|
||||
|
@ -89,8 +92,12 @@ MaybeNode Fn::make(SereneContext &ctx, List *list) {
|
|||
// If there is a body for this function analyze the body and set
|
||||
// the retuned ast as the final body
|
||||
if (list->count() > 2) {
|
||||
body = std::vector<Node>(list->begin() + 2, list->end());
|
||||
auto maybeAst = reader::analyze(ctx, body);
|
||||
body = std::vector<Node>(list->begin() + 2, list->end());
|
||||
|
||||
// TODO: call state.moveToNewEnv to create a new env and set the
|
||||
// arguments into the new env.
|
||||
|
||||
auto maybeAst = semantics::analyze(state, body);
|
||||
|
||||
if (!maybeAst) {
|
||||
return MaybeNode::error(std::move(maybeAst.getError()));
|
||||
|
|
|
@ -58,7 +58,8 @@ std::string List::toString() const {
|
|||
return llvm::formatv("<List {0}>", s);
|
||||
};
|
||||
|
||||
MaybeNode List::analyze(SereneContext &ctx) {
|
||||
MaybeNode List::analyze(semantics::AnalysisState &state) {
|
||||
|
||||
if (!elements.empty()) {
|
||||
auto *first = elements[0].get();
|
||||
|
||||
|
@ -67,16 +68,16 @@ MaybeNode List::analyze(SereneContext &ctx) {
|
|||
|
||||
if (sym != nullptr) {
|
||||
if (sym->name == "def") {
|
||||
return Def::make(ctx, this);
|
||||
return Def::make(state, this);
|
||||
}
|
||||
|
||||
if (sym->name == "fn") {
|
||||
return Fn::make(ctx, this);
|
||||
return Fn::make(state, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Call::make(ctx, this);
|
||||
return Call::make(state, this);
|
||||
}
|
||||
|
||||
return EmptyNode;
|
||||
|
|
|
@ -38,8 +38,9 @@ std::string Number::toString() const {
|
|||
return llvm::formatv("<Number {0}>", value);
|
||||
}
|
||||
|
||||
MaybeNode Number::analyze(SereneContext &ctx) {
|
||||
UNUSED(ctx);
|
||||
MaybeNode Number::analyze(semantics::AnalysisState &state) {
|
||||
UNUSED(state);
|
||||
|
||||
return EmptyNode;
|
||||
};
|
||||
|
||||
|
|
|
@ -32,8 +32,9 @@ std::string Symbol::toString() const {
|
|||
return llvm::formatv("<Symbol {0}>", this->name);
|
||||
}
|
||||
|
||||
MaybeNode Symbol::analyze(SereneContext &ctx) {
|
||||
UNUSED(ctx);
|
||||
MaybeNode Symbol::analyze(semantics::AnalysisState &state) {
|
||||
UNUSED(state);
|
||||
|
||||
return EmptyNode;
|
||||
};
|
||||
|
||||
|
|
|
@ -95,7 +95,7 @@ llvm::Error NSLayer::add(orc::ResourceTrackerSP &rt, llvm::StringRef nsname,
|
|||
orc::SymbolFlagsMap NSLayer::getInterface(serene::Namespace &ns) {
|
||||
orc::SymbolFlagsMap Symbols;
|
||||
|
||||
for (auto &k : ns.semanticEnv) {
|
||||
for (auto &k : ns.getRootEnv()) {
|
||||
auto flags = llvm::JITSymbolFlags::Exported;
|
||||
auto name = k.getFirst();
|
||||
auto expr = k.getSecond();
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
#include "serene/errors/constants.h"
|
||||
#include "serene/exprs/expression.h"
|
||||
#include "serene/llvm/IR/Value.h"
|
||||
#include "serene/reader/semantics.h"
|
||||
#include "serene/semantics.h"
|
||||
#include "serene/slir/slir.h"
|
||||
|
||||
#include <llvm/ADT/StringRef.h>
|
||||
|
@ -54,32 +54,62 @@ Namespace::Namespace(SereneContext &ctx, llvm::StringRef ns_name,
|
|||
if (filename.hasValue()) {
|
||||
this->filename.emplace(filename.getValue().str());
|
||||
}
|
||||
|
||||
// Create the root environment
|
||||
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));
|
||||
|
||||
return *environments.back();
|
||||
};
|
||||
|
||||
SemanticEnv &Namespace::getRootEnv() {
|
||||
assert(environments.empty() && "Root env is not created!");
|
||||
|
||||
return *environments.front();
|
||||
};
|
||||
|
||||
mlir::LogicalResult Namespace::define(std::string &name, exprs::Node &node) {
|
||||
auto &rootEnv = getRootEnv();
|
||||
|
||||
if (failed(rootEnv.insert_symbol(name, node))) {
|
||||
return mlir::failure();
|
||||
}
|
||||
|
||||
symbolList.push_back(name);
|
||||
return mlir::success();
|
||||
}
|
||||
|
||||
exprs::Ast &Namespace::getTree() { return this->tree; }
|
||||
errors::OptionalErrors Namespace::addTree(exprs::Ast &ast) {
|
||||
|
||||
errors::OptionalErrors Namespace::expandTree(exprs::Ast &ast) {
|
||||
|
||||
// TODO: Remove the parse phase
|
||||
if (ctx.getTargetPhase() == CompilationPhase::Parse) {
|
||||
// we just want the raw AST
|
||||
this->tree.insert(this->tree.end(), ast.begin(), ast.end());
|
||||
return llvm::None;
|
||||
}
|
||||
auto &rootEnv = getRootEnv();
|
||||
|
||||
auto state = semantics::makeAnalysisState(*this, rootEnv);
|
||||
// Run the semantic analyer on the ast and then if everything
|
||||
// is ok expand the currnt tree by the semantically correct ast.
|
||||
auto maybeAst = reader::analyze(ctx, ast);
|
||||
// is ok add the form to the tree and forms
|
||||
auto maybeForm = semantics::analyze(*state, ast);
|
||||
|
||||
if (!maybeAst) {
|
||||
return maybeAst.getError();
|
||||
if (!maybeForm) {
|
||||
return maybeForm.getError();
|
||||
}
|
||||
|
||||
auto semanticAst = std::move(maybeAst.getValue());
|
||||
auto semanticAst = std::move(maybeForm.getValue());
|
||||
this->tree.insert(this->tree.end(), semanticAst.begin(), semanticAst.end());
|
||||
|
||||
return llvm::None;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,22 +16,25 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "serene/reader/semantics.h"
|
||||
#include "serene/semantics.h"
|
||||
|
||||
#include "serene/context.h"
|
||||
#include "serene/exprs/expression.h"
|
||||
#include "serene/namespace.h"
|
||||
|
||||
namespace serene::reader {
|
||||
namespace serene::semantics {
|
||||
|
||||
AnalyzeResult analyze(serene::SereneContext &ctx, exprs::Ast &inputAst) {
|
||||
// TODO: Fetch the current namespace from the JIT engine later and if it is
|
||||
// `nil` then the given `ast` has to start with a namespace definition.
|
||||
std::unique_ptr<AnalysisState> AnalysisState::moveToNewEnv() {
|
||||
auto &newEnv = ns.createEnv(&env);
|
||||
return makeAnalysisState(ns, newEnv);
|
||||
};
|
||||
|
||||
AnalyzeResult analyze(AnalysisState &state, exprs::Ast &forms) {
|
||||
errors::ErrorTree errors;
|
||||
exprs::Ast ast;
|
||||
|
||||
for (auto &element : inputAst) {
|
||||
auto maybeNode = element->analyze(ctx);
|
||||
for (auto &element : forms) {
|
||||
auto maybeNode = element->analyze(state);
|
||||
|
||||
// Is it a `success` result
|
||||
if (maybeNode) {
|
||||
|
@ -61,4 +64,4 @@ AnalyzeResult analyze(serene::SereneContext &ctx, exprs::Ast &inputAst) {
|
|||
|
||||
return AnalyzeResult::error(std::move(errors));
|
||||
};
|
||||
}; // namespace serene::reader
|
||||
}; // namespace serene::semantics
|
|
@ -28,7 +28,6 @@
|
|||
#include "serene/namespace.h"
|
||||
#include "serene/reader/location.h"
|
||||
#include "serene/reader/reader.h"
|
||||
#include "serene/reader/semantics.h"
|
||||
#include "serene/utils.h"
|
||||
|
||||
#include <system_error>
|
||||
|
@ -125,7 +124,7 @@ MaybeNS SourceMgr::readNamespace(SereneContext &ctx, std::string name,
|
|||
auto ns =
|
||||
makeNamespace(ctx, name, llvm::Optional(llvm::StringRef(importedFile)));
|
||||
|
||||
auto errs = ns->expandTree(maybeAst.getValue());
|
||||
auto errs = ns->addTree(maybeAst.getValue());
|
||||
if (errs) {
|
||||
SMGR_LOG("Couldn't set the AST for namespace: " + name);
|
||||
return MaybeNS::error(errs.getValue());
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
#include "serene/namespace.h"
|
||||
#include "serene/reader/location.h"
|
||||
#include "serene/reader/reader.h"
|
||||
#include "serene/reader/semantics.h"
|
||||
#include "serene/semantics.h"
|
||||
#include "serene/serene.h"
|
||||
#include "serene/slir/generatable.h"
|
||||
#include "serene/slir/slir.h"
|
||||
|
|
Loading…
Reference in New Issue