serene/libserene.v0/lib/exprs/fn.cpp

167 lines
4.9 KiB
C++
Raw Normal View History

/*
2021-10-12 20:51:03 +01:00
* Serene Programming Language
*
2022-01-27 11:44:44 +00:00
* Copyright (c) 2019-2022 Sameer Rahmani <lxsameer@gnu.org>
*
2021-10-12 20:51:03 +01:00
* 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.
*
2021-10-12 20:51:03 +01:00
* 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.
*
2021-10-12 20:51:03 +01:00
* 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 "serene/exprs/fn.h"
#include "serene/errors.h"
#include "serene/exprs/expression.h"
#include "serene/exprs/list.h"
#include "serene/exprs/symbol.h"
2021-06-23 20:34:57 +01:00
#include "serene/slir/dialect.h"
#include "serene/slir/ops.h"
2021-06-23 20:34:57 +01:00
#include "serene/slir/utils.h"
2021-06-23 20:34:57 +01:00
#include <llvm/Support/Casting.h>
#include <llvm/Support/FormatVariadic.h>
#include <mlir/IR/Block.h>
2021-09-23 19:24:51 +01:00
#include <mlir/IR/BuiltinAttributes.h>
2021-10-17 14:33:16 +01:00
#include <utility>
namespace serene {
namespace exprs {
2021-06-01 20:47:46 +01:00
Fn::Fn(SereneContext &ctx, reader::LocationRange &loc, List &args, Ast body)
2021-10-17 14:33:16 +01:00
: Expression(loc), args(args), body(std::move(body)) {
2021-06-01 20:47:46 +01:00
this->setName(
llvm::formatv("___fn___{0}", ctx.getCurrentNS().nextFnCounter()));
2021-06-01 20:47:46 +01:00
};
ExprType Fn::getType() const { return ExprType::Fn; };
std::string Fn::toString() const {
return llvm::formatv("<Fn {0} {1} to {2}>",
this->name.empty() ? "Anonymous" : this->name,
this->args.toString(),
this->body.empty() ? "<>" : astToString(&this->body));
}
MaybeNode Fn::analyze(semantics::AnalysisState &state) {
UNUSED(state);
return EmptyNode;
};
bool Fn::classof(const Expression *e) { return e->getType() == ExprType::Fn; };
2021-10-17 14:33:16 +01:00
void Fn::setName(std::string n) { this->name = std::move(n); };
2021-06-01 20:47:46 +01:00
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 errors::makeError(ctx, errors::FnNoArgsList,
list->elements[0]->location,
"The argument list is mandatory.");
}
Symbol *fnSym = llvm::dyn_cast<Symbol>(list->elements[0].get());
// TODO: Replace this assert with a runtime check
assert((fnSym && fnSym->name == "fn") &&
"The first element of the list should be a 'fn'.");
List *args = llvm::dyn_cast<List>(list->elements[1].get());
2021-10-17 14:33:16 +01:00
if (args == nullptr) {
std::string msg =
llvm::formatv("Arguments of a function has to be a list, got '{0}'",
stringifyExprType(list->elements[1]->getType()));
return errors::makeError(ctx, errors::FnArgsMustBeList,
list->elements[1]->location, msg);
}
2021-04-25 23:07:08 +01:00
Ast body;
// 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());
// 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 maybeAst.takeError();
}
body = *maybeAst;
}
2021-06-01 20:47:46 +01:00
return makeSuccessfulNode<Fn>(ctx, list->location, *args, body);
};
2021-06-23 20:34:57 +01:00
void Fn::generateIR(serene::Namespace &ns, mlir::ModuleOp &m) {
2021-09-23 19:24:51 +01:00
auto loc = slir::toMLIRLocation(ns, location.start);
auto &ctx = ns.getContext();
2021-06-23 20:34:57 +01:00
mlir::OpBuilder builder(&ctx.mlirContext);
2021-06-25 21:47:24 +01:00
// llvm::SmallVector<mlir::Type, 4> arg_types;
llvm::SmallVector<mlir::NamedAttribute, 4> arguments;
2021-06-23 20:34:57 +01:00
// at the moment we only support integers
2021-06-25 21:47:24 +01:00
for (auto &arg : args) {
auto *argSym = llvm::dyn_cast<Symbol>(arg.get());
2021-10-17 14:33:16 +01:00
if (argSym == nullptr) {
m->emitError(llvm::formatv(
2021-06-25 21:47:24 +01:00
"Arguments of a function have to be symbols. Fn: '{0}'", name));
return;
}
arguments.push_back(builder.getNamedAttr(
argSym->name, mlir::TypeAttr::get(builder.getI64Type())));
2021-06-23 20:34:57 +01:00
}
auto fn = builder.create<slir::Fn1Op>(
2021-06-25 21:47:24 +01:00
loc, builder.getI64Type(), name,
mlir::DictionaryAttr::get(builder.getContext(), arguments),
builder.getStringAttr("public"));
2021-06-23 20:34:57 +01:00
if (!fn) {
m.emitError(llvm::formatv("Can't create the function '{0}'", name));
2021-09-23 19:24:51 +01:00
return;
2021-06-23 20:34:57 +01:00
}
auto &body = fn.body();
2021-09-23 19:24:51 +01:00
auto *entryBlock = new mlir::Block();
2021-06-23 20:34:57 +01:00
body.push_back(entryBlock);
2021-09-23 19:24:51 +01:00
2021-06-23 20:34:57 +01:00
builder.setInsertionPointToStart(entryBlock);
auto retVal = builder.create<slir::Value1Op>(loc, 0).getResult();
2021-06-23 20:34:57 +01:00
slir::Return1Op returnOp = builder.create<slir::Return1Op>(loc, retVal);
2021-06-23 20:34:57 +01:00
if (!returnOp) {
m.emitError(
2021-06-23 20:34:57 +01:00
llvm::formatv("Can't create the return value of function '{0}'", name));
2021-09-23 19:24:51 +01:00
fn.erase();
2021-06-23 20:34:57 +01:00
return;
}
2021-09-23 19:24:51 +01:00
m.push_back(fn);
2021-06-23 20:34:57 +01:00
};
} // namespace exprs
} // namespace serene