Move the semantic analysis process into the namespace
This commit is contained in:
parent
dc051797ee
commit
6ef69d329a
127
bin/serene.cpp
127
bin/serene.cpp
|
@ -243,13 +243,6 @@ int main(int argc, char *argv[]) {
|
|||
applyPassManagerCLOptions(ctx->pm);
|
||||
ctx->sourceManager.setLoadPaths(loadPaths);
|
||||
|
||||
auto runLoc = reader::LocationRange::UnknownLocation(inputNS);
|
||||
auto ns = ctx->sourceManager.readNamespace(*ctx, inputNS, runLoc);
|
||||
|
||||
if (!ns) {
|
||||
return (int)std::errc::no_such_file_or_directory;
|
||||
}
|
||||
|
||||
// TODO: handle the outputDir by not forcing it. it should be
|
||||
// default to the current working dir
|
||||
if (outputDir == "-") {
|
||||
|
@ -268,23 +261,13 @@ int main(int argc, char *argv[]) {
|
|||
|
||||
// Just print out the raw AST
|
||||
case Action::DumpAST: {
|
||||
auto ast = ns->getTree();
|
||||
llvm::outs() << exprs::astToString(&ast) << "\n";
|
||||
return 0;
|
||||
ctx->setOperationPhase(CompilationPhase::Parse);
|
||||
break;
|
||||
};
|
||||
|
||||
case Action::DumpSemantic: {
|
||||
auto ast = ns->getTree();
|
||||
auto afterAst = reader::analyze(*ctx, ast);
|
||||
|
||||
if (!afterAst) {
|
||||
throw std::move(afterAst.getError());
|
||||
}
|
||||
|
||||
ast = afterAst.getValue();
|
||||
|
||||
llvm::outs() << exprs::astToString(&ast) << "\n";
|
||||
return 0;
|
||||
ctx->setOperationPhase(CompilationPhase::Analysis);
|
||||
break;
|
||||
};
|
||||
|
||||
case Action::DumpSLIR: {
|
||||
|
@ -323,61 +306,63 @@ int main(int argc, char *argv[]) {
|
|||
}
|
||||
}
|
||||
|
||||
// Perform the semantic analytics
|
||||
auto afterAst = reader::analyze(*ctx, ns->getTree());
|
||||
if (!afterAst) {
|
||||
throw std::move(afterAst.getError());
|
||||
auto runLoc = reader::LocationRange::UnknownLocation(inputNS);
|
||||
auto ns = ctx->sourceManager.readNamespace(*ctx, inputNS, runLoc);
|
||||
|
||||
if (!ns) {
|
||||
return (int)std::errc::no_such_file_or_directory;
|
||||
}
|
||||
|
||||
auto isSet = ns->setTree(afterAst.getValue());
|
||||
if (isSet.succeeded()) {
|
||||
ctx->insertNS(ns);
|
||||
ctx->insertNS(ns);
|
||||
|
||||
switch (emitAction) {
|
||||
case Action::DumpSLIR:
|
||||
case Action::DumpMLIR:
|
||||
case Action::DumpLIR: {
|
||||
ns->dump();
|
||||
break;
|
||||
};
|
||||
case Action::DumpIR: {
|
||||
auto maybeModule = ns->compileToLLVM();
|
||||
switch (emitAction) {
|
||||
case Action::DumpAST:
|
||||
case Action::DumpSemantic: {
|
||||
auto ast = ns->getTree();
|
||||
llvm::outs() << exprs::astToString(&ast) << "\n";
|
||||
return 0;
|
||||
}
|
||||
case Action::DumpSLIR:
|
||||
case Action::DumpMLIR:
|
||||
case Action::DumpLIR: {
|
||||
ns->dump();
|
||||
break;
|
||||
};
|
||||
case Action::DumpIR: {
|
||||
auto maybeModule = ns->compileToLLVM();
|
||||
|
||||
if (!maybeModule) {
|
||||
llvm::errs() << "Failed to generate the IR.\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
maybeModule.getValue()->dump();
|
||||
break;
|
||||
};
|
||||
|
||||
case Action::RunJIT: {
|
||||
auto maybeJIT = JIT::make(*ns.get());
|
||||
if (!maybeJIT) {
|
||||
// TODO: panic in here: "Couldn't creat the JIT!"
|
||||
return -1;
|
||||
}
|
||||
auto jit = std::move(maybeJIT.getValue());
|
||||
|
||||
if (jit->invoke("main")) {
|
||||
llvm::errs() << "Faild to invoke the 'main' function.\n";
|
||||
return 1;
|
||||
}
|
||||
llvm::outs() << "Done!";
|
||||
break;
|
||||
};
|
||||
|
||||
case Action::Compile:
|
||||
case Action::CompileToObject: {
|
||||
return dumpAsObject(*ns);
|
||||
};
|
||||
default: {
|
||||
llvm::errs() << "Action is not supported yet!\n";
|
||||
};
|
||||
if (!maybeModule) {
|
||||
llvm::errs() << "Failed to generate the IR.\n";
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
llvm::errs() << "Can't set the tree of the namespace!\n";
|
||||
|
||||
maybeModule.getValue()->dump();
|
||||
break;
|
||||
};
|
||||
|
||||
case Action::RunJIT: {
|
||||
auto maybeJIT = JIT::make(*ns.get());
|
||||
if (!maybeJIT) {
|
||||
// TODO: panic in here: "Couldn't creat the JIT!"
|
||||
return -1;
|
||||
}
|
||||
auto jit = std::move(maybeJIT.getValue());
|
||||
|
||||
if (jit->invoke("main")) {
|
||||
llvm::errs() << "Faild to invoke the 'main' function.\n";
|
||||
return 1;
|
||||
}
|
||||
llvm::outs() << "Done!";
|
||||
break;
|
||||
};
|
||||
|
||||
case Action::Compile:
|
||||
case Action::CompileToObject: {
|
||||
return dumpAsObject(*ns);
|
||||
};
|
||||
default: {
|
||||
llvm::errs() << "Action is not supported yet!\n";
|
||||
};
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -53,6 +53,8 @@ using Node = std::shared_ptr<Expression>;
|
|||
} // namespace exprs
|
||||
|
||||
enum class CompilationPhase {
|
||||
Parse,
|
||||
Analysis,
|
||||
SLIR,
|
||||
MLIR, // Lowered slir to other dialects
|
||||
LIR, // Lowered to the llvm ir dialect
|
||||
|
|
|
@ -83,6 +83,7 @@ class DiagnosticEngine {
|
|||
public:
|
||||
DiagnosticEngine(SereneContext &ctx);
|
||||
|
||||
void enqueueError(llvm::StringRef msg);
|
||||
void emitSyntaxError(reader::LocationRange loc, errors::ErrorVariant &e,
|
||||
llvm::StringRef msg = "");
|
||||
};
|
||||
|
|
|
@ -22,6 +22,15 @@
|
|||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Commentary:
|
||||
* Rules of a namespace:
|
||||
* - A namespace has have a name and it has to own it.
|
||||
* - 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
|
||||
*/
|
||||
|
||||
#ifndef SERENE_NAMESPACE_H
|
||||
#define SERENE_NAMESPACE_H
|
||||
|
||||
|
@ -33,6 +42,7 @@
|
|||
#include <atomic>
|
||||
#include <llvm/ADT/SmallString.h>
|
||||
#include <llvm/ADT/StringRef.h>
|
||||
#include <llvm/ADT/Twine.h>
|
||||
#include <llvm/IR/Module.h>
|
||||
#include <memory>
|
||||
#include <mlir/IR/Builders.h>
|
||||
|
@ -63,17 +73,18 @@ using MaybeModuleOp = llvm::Optional<mlir::OwningOpRef<mlir::ModuleOp>>;
|
|||
class Namespace {
|
||||
private:
|
||||
SereneContext &ctx;
|
||||
bool initialized = false;
|
||||
|
||||
// Anonymous function counter. We need to assing a unique name to each
|
||||
// anonymous function and we use this counter to generate those names
|
||||
std::atomic<uint> fn_counter = 0;
|
||||
|
||||
// The content of the namespace
|
||||
/// The content of the namespace. It should alway hold a semantically
|
||||
/// correct AST. It means thet the AST that we want to stor here has
|
||||
/// to pass the semantic analyzer.
|
||||
exprs::Ast tree;
|
||||
|
||||
public:
|
||||
mlir::StringRef name;
|
||||
std::string name;
|
||||
llvm::Optional<std::string> filename;
|
||||
|
||||
/// The root environment of the namespace on the semantic analysis phase.
|
||||
|
@ -88,7 +99,11 @@ public:
|
|||
llvm::Optional<llvm::StringRef> filename);
|
||||
|
||||
exprs::Ast &getTree();
|
||||
mlir::LogicalResult setTree(exprs::Ast &);
|
||||
|
||||
/// 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 emit an error.
|
||||
mlir::LogicalResult expandTree(exprs::Ast &ast);
|
||||
|
||||
/// Increase the function counter by one
|
||||
uint nextFnCounter();
|
||||
|
@ -110,6 +125,8 @@ public:
|
|||
/// Dumps the namespace with respect to the compilation phase
|
||||
void dump();
|
||||
|
||||
void enqueueError(llvm::StringRef e);
|
||||
|
||||
~Namespace();
|
||||
};
|
||||
|
||||
|
|
|
@ -24,9 +24,10 @@
|
|||
#ifndef SERENE_SLIR_DIALECT_H_
|
||||
#define SERENE_SLIR_DIALECT_H_
|
||||
|
||||
#include "mlir/IR/BuiltinOps.h"
|
||||
#include "mlir/IR/Dialect.h"
|
||||
#include "mlir/Interfaces/SideEffectInterfaces.h"
|
||||
#include <mlir/IR/BuiltinOps.h>
|
||||
#include <mlir/IR/Dialect.h>
|
||||
#include <mlir/Interfaces/ControlFlowInterfaces.h>
|
||||
#include <mlir/Interfaces/SideEffectInterfaces.h>
|
||||
|
||||
// Include the auto-generated header file containing the declaration of the
|
||||
// serene's dialect.
|
||||
|
|
|
@ -2,10 +2,15 @@
|
|||
#define SERENE_DIALECT
|
||||
|
||||
include "mlir/IR/OpBase.td"
|
||||
include "mlir/IR/OpAsmInterface.td"
|
||||
include "mlir/IR/SymbolInterfaces.td"
|
||||
include "mlir/Interfaces/SideEffectInterfaces.td"
|
||||
include "mlir/Interfaces/CallInterfaces.td"
|
||||
include "mlir/Interfaces/CastInterfaces.td"
|
||||
include "mlir/Interfaces/SideEffectInterfaces.td"
|
||||
include "mlir/Interfaces/ControlFlowInterfaces.td"
|
||||
include "mlir/Interfaces/DataLayoutInterfaces.td"
|
||||
include "mlir/Interfaces/VectorInterfaces.td"
|
||||
|
||||
|
||||
// Dialect definition. It will directly generate the SereneDialect class
|
||||
|
@ -32,49 +37,17 @@ class Serene_Op<string mnemonic, list<OpTrait> traits = []> :
|
|||
class Serene_Type<string name> : TypeDef<Serene_Dialect, name> { }
|
||||
|
||||
|
||||
// def SymbolType : Serene_Type<"Symbol"> {
|
||||
|
||||
// let mnemonic = "symbol";
|
||||
|
||||
// let summary = "A typical Lisp symbol";
|
||||
|
||||
// let description = [{
|
||||
// A symbol is just a name and nothing more. Just a name
|
||||
// to give to a value or to use it as it is.
|
||||
// }];
|
||||
|
||||
// // let cppNamespace = "::serene::sir";
|
||||
// let parameters = (ins "std::string":$name);
|
||||
|
||||
// // We define the printer inline.
|
||||
// let printer = [{
|
||||
// $_printer << "Symbol<" << getImpl()->name << ">";
|
||||
// }];
|
||||
|
||||
// // The parser is defined here also.
|
||||
// let parser = [{
|
||||
// if ($_parser.parseLess())
|
||||
// return Type();
|
||||
|
||||
// std::string name;
|
||||
// if ($_parser.parseInteger(name))
|
||||
// return Type();
|
||||
|
||||
// return get($_ctxt, name);
|
||||
// }];
|
||||
// }
|
||||
|
||||
def ValueOp: Serene_Op<"value"> {
|
||||
|
||||
let summary = "This operation represent a value";
|
||||
let description = [{
|
||||
some description
|
||||
ValueOp
|
||||
}];
|
||||
|
||||
let arguments = (ins I64Attr:$value);
|
||||
let results = (outs I64);
|
||||
|
||||
// let verifier = [{ return serene::sir::verify(*this); }];
|
||||
//let verifier = [{ return serene::sir::verify(*this); }];
|
||||
|
||||
let builders = [
|
||||
OpBuilder<(ins "int":$value), [{
|
||||
|
@ -85,9 +58,12 @@ def ValueOp: Serene_Op<"value"> {
|
|||
];
|
||||
}
|
||||
|
||||
|
||||
|
||||
// TODO: Add the FunctionLike trait here and include its header file in dialect.h
|
||||
def FnOp: Serene_Op<"fn", [
|
||||
AffineScope, AutomaticAllocationScope,
|
||||
IsolatedFromAbove
|
||||
IsolatedFromAbove,
|
||||
]> {
|
||||
|
||||
let summary = "This operation is just a place holder for a function";
|
||||
|
@ -102,23 +78,21 @@ def FnOp: Serene_Op<"fn", [
|
|||
OptionalAttr<StrAttr>:$sym_visibility);
|
||||
|
||||
let regions = (region AnyRegion:$body);
|
||||
let results = (outs NoneType);
|
||||
let results = (outs I64);
|
||||
|
||||
// let builders = [
|
||||
// OpBuilder<(ins
|
||||
// "llvm::StringRef":$name, "mlir::FunctionType":$type,
|
||||
// CArg<"llvm::ArrayRef<mlir::NamedAttribute>", "{}">:$attrs,
|
||||
// CArg<"llvm::ArrayRef<mlir::DictionaryAttr>", "{}">:$argAttrs)
|
||||
// >];
|
||||
|
||||
// let extraClassDeclaration = [{
|
||||
// static FnOp create(mlir::Location location, llvm::StringRef name, mlir::FunctionType type,
|
||||
// llvm::ArrayRef<mlir::NamedAttribute> attrs = {});
|
||||
// static FnOp create(mlir::Location location, llvm::StringRef name, mlir::FunctionType type,
|
||||
// mlir::Operation::dialect_attr_range attrs);
|
||||
// static FnOp create(mlir::Location location, llvm::StringRef name, mlir::FunctionType type,
|
||||
// llvm::ArrayRef<mlir::NamedAttribute> attrs,
|
||||
// llvm::ArrayRef<mlir::DictionaryAttr> argAttrs);
|
||||
// }];
|
||||
}
|
||||
|
||||
def ReturnOp: Serene_Op<"return", [NoSideEffect, HasParent<"FnOp">,
|
||||
ReturnLike, Terminator]> {
|
||||
|
||||
let summary = "This operation marks the return value of a function";
|
||||
let description = [{
|
||||
ReturnOp
|
||||
}];
|
||||
|
||||
let arguments = (ins AnyType:$operand);
|
||||
let assemblyFormat =
|
||||
[{ attr-dict $operand `:` type($operand) }];
|
||||
}
|
||||
|
||||
#endif // SERENE_DIALECT
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
namespace serene {
|
||||
|
||||
void SereneContext::insertNS(std::shared_ptr<Namespace> ns) {
|
||||
namespaces[ns->name.str()] = ns;
|
||||
namespaces[ns->name] = ns;
|
||||
};
|
||||
|
||||
std::shared_ptr<Namespace> SereneContext::getNS(llvm::StringRef ns_name) {
|
||||
|
|
|
@ -28,12 +28,11 @@
|
|||
#include "serene/reader/location.h"
|
||||
#include "serene/source_mgr.h"
|
||||
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/Support/WithColor.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
|
||||
#include <llvm/ADT/StringRef.h>
|
||||
#include <llvm/Support/FormatAdapters.h>
|
||||
#include <llvm/Support/FormatVariadic.h>
|
||||
#include <llvm/Support/WithColor.h>
|
||||
#include <llvm/Support/raw_ostream.h>
|
||||
#include <memory>
|
||||
|
||||
namespace serene {
|
||||
|
@ -166,6 +165,11 @@ Diagnostic DiagnosticEngine::toDiagnostic(reader::LocationRange loc,
|
|||
return Diagnostic(ctx, loc, &e, msg, fn);
|
||||
};
|
||||
|
||||
void DiagnosticEngine::enqueueError(llvm::StringRef msg) {
|
||||
llvm::errs() << llvm::formatv("FIX ME (better emit error): {0}\n", msg);
|
||||
exit(1);
|
||||
};
|
||||
|
||||
void DiagnosticEngine::emitSyntaxError(reader::LocationRange loc,
|
||||
errors::ErrorVariant &e,
|
||||
llvm::StringRef msg) {
|
||||
|
|
|
@ -28,6 +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/slir/slir.h"
|
||||
|
||||
#include <llvm/ADT/StringRef.h>
|
||||
|
@ -36,6 +37,8 @@
|
|||
#include <memory>
|
||||
#include <mlir/IR/Builders.h>
|
||||
#include <mlir/IR/BuiltinOps.h>
|
||||
#include <mlir/IR/Verifier.h>
|
||||
#include <mlir/Support/LogicalResult.h>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
|
@ -54,15 +57,31 @@ Namespace::Namespace(SereneContext &ctx, llvm::StringRef ns_name,
|
|||
}
|
||||
};
|
||||
|
||||
void Namespace::enqueueError(llvm::StringRef e) {
|
||||
ctx.diagEngine->enqueueError(e);
|
||||
}
|
||||
|
||||
exprs::Ast &Namespace::getTree() { return this->tree; }
|
||||
|
||||
mlir::LogicalResult Namespace::setTree(exprs::Ast &t) {
|
||||
if (initialized) {
|
||||
mlir::LogicalResult Namespace::expandTree(exprs::Ast &ast) {
|
||||
|
||||
if (ctx.getTargetPhase() == CompilationPhase::Parse) {
|
||||
// we just want the raw AST
|
||||
this->tree.insert(this->tree.end(), ast.begin(), ast.end());
|
||||
return mlir::success();
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
if (!maybeAst) {
|
||||
enqueueError("Semantic analysis failed!");
|
||||
return mlir::failure();
|
||||
}
|
||||
|
||||
this->tree = std::move(t);
|
||||
this->initialized = true;
|
||||
auto semanticAst = std::move(maybeAst.getValue());
|
||||
this->tree.insert(this->tree.end(), semanticAst.begin(), semanticAst.end());
|
||||
return mlir::success();
|
||||
}
|
||||
|
||||
|
@ -73,7 +92,8 @@ SereneContext &Namespace::getContext() { return this->ctx; };
|
|||
MaybeModuleOp Namespace::generate() {
|
||||
mlir::OpBuilder builder(&ctx.mlirContext);
|
||||
// TODO: Fix the unknown location by pointing to the `ns` form
|
||||
auto module = mlir::ModuleOp::create(builder.getUnknownLoc(), name);
|
||||
auto module = mlir::ModuleOp::create(builder.getUnknownLoc(),
|
||||
llvm::Optional<llvm::StringRef>(name));
|
||||
|
||||
// Walk the AST and call the `generateIR` function of each node.
|
||||
// Since nodes will have access to the a reference of the
|
||||
|
@ -83,6 +103,12 @@ MaybeModuleOp Namespace::generate() {
|
|||
x->generateIR(*this, module);
|
||||
}
|
||||
|
||||
if (mlir::failed(mlir::verify(module))) {
|
||||
module.emitError("Can't verify the module");
|
||||
module.erase();
|
||||
return llvm::None;
|
||||
}
|
||||
|
||||
if (mlir::failed(runPasses(module))) {
|
||||
// TODO: Report a proper error
|
||||
module.emitError("Failure in passes!");
|
||||
|
@ -105,7 +131,10 @@ void Namespace::dump() {
|
|||
return;
|
||||
}
|
||||
|
||||
maybeModuleOp.getValue()->dump();
|
||||
mlir::OpPrintingFlags flags;
|
||||
flags.enableDebugInfo();
|
||||
|
||||
maybeModuleOp.getValue()->print(llvm::outs(), flags);
|
||||
};
|
||||
|
||||
MaybeModule Namespace::compileToLLVM() {
|
||||
|
|
|
@ -55,6 +55,7 @@ NSPtr SourceMgr::readNamespace(SereneContext &ctx, std::string name,
|
|||
std::string importedFile;
|
||||
auto path = convertNamespaceToPath(name);
|
||||
|
||||
// TODO: Fix this to enqueue a proper error instead
|
||||
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> newBufOrErr(
|
||||
std::make_error_code(std::errc::no_such_file_or_directory));
|
||||
|
||||
|
@ -84,6 +85,7 @@ NSPtr SourceMgr::readNamespace(SereneContext &ctx, std::string name,
|
|||
|
||||
if (bufferId == 0) {
|
||||
auto msg = llvm::formatv("Couldn't add namespace '{0}'", name);
|
||||
// TODO: enqueue the error here
|
||||
ctx.diagEngine->emitSyntaxError(importLoc, errors::NSAddToSMError,
|
||||
llvm::StringRef(msg));
|
||||
|
||||
|
@ -99,14 +101,9 @@ NSPtr SourceMgr::readNamespace(SereneContext &ctx, std::string name,
|
|||
llvm::Optional(llvm::StringRef(importedFile)));
|
||||
|
||||
if (!maybeAst) {
|
||||
SMGR_LOG("Couldn't Read namespace: " + name)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Perform the semantic analytics
|
||||
auto afterAst = reader::analyze(ctx, maybeAst.getValue());
|
||||
if (!afterAst) {
|
||||
// TODO: Do we need raise an error here too?
|
||||
SMGR_LOG("Couldn't Read namespace: " + name);
|
||||
// The code that is calling this function has to flush the dia engine, so we
|
||||
// don't have to emit an error
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -114,8 +111,11 @@ NSPtr SourceMgr::readNamespace(SereneContext &ctx, std::string name,
|
|||
auto ns =
|
||||
makeNamespace(ctx, name, llvm::Optional(llvm::StringRef(importedFile)));
|
||||
|
||||
if (mlir::failed(ns->setTree(afterAst.getValue()))) {
|
||||
SMGR_LOG("Couldn't set the AST for namespace: " + name)
|
||||
if (mlir::failed(ns->expandTree(maybeAst.getValue()))) {
|
||||
SMGR_LOG("Couldn't set the AST for namespace: " + name);
|
||||
// The code that is calling this function has to flush the dia engine, so we
|
||||
// don't have to emit an error
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue