1
0
mirror of https://github.com/exaloop/codon.git synced 2025-06-03 15:03:52 +08:00
codon/codon/app/main.cpp

405 lines
13 KiB
C++
Raw Normal View History

2021-09-27 14:02:44 -04:00
#include <algorithm>
2021-11-01 19:10:33 -04:00
#include <cstdio>
2021-09-27 14:02:44 -04:00
#include <cstdlib>
2022-01-18 21:19:52 -08:00
#include <fstream>
2021-11-01 19:10:33 -04:00
#include <iostream>
2021-11-16 15:40:00 -05:00
#include <sstream>
2021-09-27 14:02:44 -04:00
#include <string>
#include <unordered_map>
#include <vector>
2021-10-30 14:16:40 -04:00
#include "codon/compiler/compiler.h"
2021-11-02 14:59:10 -04:00
#include "codon/compiler/error.h"
2021-11-01 19:10:33 -04:00
#include "codon/compiler/jit.h"
2021-10-30 14:16:40 -04:00
#include "codon/util/common.h"
#include "llvm/Support/CommandLine.h"
2021-09-27 14:02:44 -04:00
namespace {
void versMsg(llvm::raw_ostream &out) {
2021-09-30 15:55:17 -04:00
out << CODON_VERSION_MAJOR << "." << CODON_VERSION_MINOR << "." << CODON_VERSION_PATCH
<< "\n";
2021-09-27 14:02:44 -04:00
}
const std::vector<std::string> &supportedExtensions() {
2021-10-01 09:56:35 -04:00
static const std::vector<std::string> extensions = {".codon", ".py"};
2021-09-27 14:02:44 -04:00
return extensions;
}
bool hasExtension(const std::string &filename, const std::string &extension) {
return filename.size() >= extension.size() &&
filename.compare(filename.size() - extension.size(), extension.size(),
extension) == 0;
}
std::string trimExtension(const std::string &filename, const std::string &extension) {
if (hasExtension(filename, extension)) {
return filename.substr(0, filename.size() - extension.size());
} else {
return filename;
}
}
std::string makeOutputFilename(const std::string &filename,
const std::string &extension) {
for (const auto &ext : supportedExtensions()) {
if (hasExtension(filename, ext))
return trimExtension(filename, ext) + extension;
}
return filename + extension;
}
2021-11-02 14:59:10 -04:00
void display(const codon::error::ParserErrorInfo &e) {
for (auto &msg : e) {
codon::compilationError(msg.getMessage(), msg.getFile(), msg.getLine(),
msg.getColumn(),
/*terminate=*/false);
}
}
2022-01-20 11:22:32 -05:00
void initLogFlags(const llvm::cl::opt<std::string> &log) {
codon::getLogger().parse(log);
if (auto *d = getenv("CODON_DEBUG"))
codon::getLogger().parse(std::string(d));
}
2021-09-27 14:02:44 -04:00
enum BuildKind { LLVM, Bitcode, Object, Executable, Detect };
enum OptMode { Debug, Release };
} // namespace
int docMode(const std::vector<const char *> &args, const std::string &argv0) {
llvm::cl::ParseCommandLineOptions(args.size(), args.data());
2021-11-01 19:10:33 -04:00
std::vector<std::string> files;
for (std::string line; std::getline(std::cin, line);)
files.push_back(line);
auto compiler = std::make_unique<codon::Compiler>(args[0]);
2021-11-02 14:59:10 -04:00
bool failed = false;
2021-11-03 10:34:33 -04:00
auto result = compiler->docgen(files);
llvm::handleAllErrors(result.takeError(),
2021-11-02 14:59:10 -04:00
[&failed](const codon::error::ParserErrorInfo &e) {
display(e);
failed = true;
});
if (failed)
2021-11-01 19:10:33 -04:00
return EXIT_FAILURE;
2021-11-03 10:34:33 -04:00
fmt::print("{}\n", *result);
2021-09-27 14:02:44 -04:00
return EXIT_SUCCESS;
}
2021-11-11 11:50:00 -05:00
std::unique_ptr<codon::Compiler> processSource(const std::vector<const char *> &args,
bool standalone) {
2021-09-27 14:02:44 -04:00
llvm::cl::opt<std::string> input(llvm::cl::Positional, llvm::cl::desc("<input file>"),
llvm::cl::init("-"));
auto regs = llvm::cl::getRegisteredOptions();
2021-09-27 14:02:44 -04:00
llvm::cl::opt<OptMode> optMode(
llvm::cl::desc("optimization mode"),
llvm::cl::values(
clEnumValN(Debug, regs.find("debug") != regs.end() ? "default" : "debug",
2021-09-27 14:02:44 -04:00
"Turn off compiler optimizations and show backtraces"),
clEnumValN(Release, "release",
"Turn on compiler optimizations and disable debug info")),
llvm::cl::init(Debug));
llvm::cl::list<std::string> defines(
"D", llvm::cl::Prefix,
llvm::cl::desc("Add static variable definitions. The syntax is <name>=<value>"));
llvm::cl::list<std::string> disabledOpts(
"disable-opt", llvm::cl::desc("Disable the specified IR optimization"));
2021-10-17 10:30:52 -04:00
llvm::cl::list<std::string> plugins("plugin",
llvm::cl::desc("Load specified plugin"));
2021-10-30 19:02:12 -04:00
llvm::cl::opt<std::string> log("log", llvm::cl::desc("Enable given log streams"));
2021-09-27 14:02:44 -04:00
llvm::cl::ParseCommandLineOptions(args.size(), args.data());
2022-01-20 11:22:32 -05:00
initLogFlags(log);
2021-09-27 14:02:44 -04:00
auto &exts = supportedExtensions();
if (input != "-" && std::find_if(exts.begin(), exts.end(), [&](auto &ext) {
return hasExtension(input, ext);
}) == exts.end())
codon::compilationError(
2021-10-01 09:56:35 -04:00
"input file is expected to be a .codon/.py file, or '-' for stdin");
2021-09-27 14:02:44 -04:00
std::unordered_map<std::string, std::string> defmap;
for (const auto &define : defines) {
auto eq = define.find('=');
if (eq == std::string::npos || !eq) {
codon::compilationWarning("ignoring malformed definition: " + define);
2021-09-27 14:02:44 -04:00
continue;
}
auto name = define.substr(0, eq);
auto value = define.substr(eq + 1);
if (defmap.find(name) != defmap.end()) {
codon::compilationWarning("ignoring duplicate definition: " + define);
2021-09-27 14:02:44 -04:00
continue;
}
defmap.emplace(name, value);
}
const bool isDebug = (optMode == OptMode::Debug);
std::vector<std::string> disabledOptsVec(disabledOpts);
2021-10-30 14:16:40 -04:00
auto compiler = std::make_unique<codon::Compiler>(args[0], isDebug, disabledOptsVec);
2021-11-11 11:50:00 -05:00
compiler->getLLVMVisitor()->setStandalone(standalone);
2021-09-27 14:02:44 -04:00
2021-10-30 14:16:40 -04:00
// load plugins
2021-10-17 10:30:52 -04:00
for (const auto &plugin : plugins) {
2021-11-03 15:04:01 -04:00
bool failed = false;
llvm::handleAllErrors(
compiler->load(plugin), [&failed](const codon::error::PluginErrorInfo &e) {
codon::compilationError(e.getMessage(), /*file=*/"", /*line=*/0, /*col=*/0,
/*terminate=*/false);
failed = true;
});
if (failed)
2021-10-30 14:16:40 -04:00
return {};
2021-09-27 14:02:44 -04:00
}
2021-10-16 13:16:57 -04:00
2021-11-02 14:59:10 -04:00
bool failed = false;
llvm::handleAllErrors(compiler->parseFile(input, /*isTest=*/0, defmap),
[&failed](const codon::error::ParserErrorInfo &e) {
display(e);
failed = true;
});
if (failed)
2021-10-30 14:16:40 -04:00
return {};
2021-09-27 14:02:44 -04:00
2021-10-30 19:02:12 -04:00
{
TIME("compile");
2021-11-03 15:04:01 -04:00
llvm::cantFail(compiler->compile());
2021-10-30 19:02:12 -04:00
}
2021-10-30 14:16:40 -04:00
return compiler;
2021-09-27 14:02:44 -04:00
}
int runMode(const std::vector<const char *> &args) {
llvm::cl::list<std::string> libs(
"l", llvm::cl::desc("Load and link the specified library"));
llvm::cl::list<std::string> seqArgs(llvm::cl::ConsumeAfter,
llvm::cl::desc("<program arguments>..."));
2021-11-11 11:50:00 -05:00
auto compiler = processSource(args, /*standalone=*/false);
2021-10-30 14:16:40 -04:00
if (!compiler)
2021-09-27 14:02:44 -04:00
return EXIT_FAILURE;
std::vector<std::string> libsVec(libs);
std::vector<std::string> argsVec(seqArgs);
2021-10-30 14:16:40 -04:00
argsVec.insert(argsVec.begin(), compiler->getInput());
compiler->getLLVMVisitor()->run(argsVec, libsVec);
2021-09-27 14:02:44 -04:00
return EXIT_SUCCESS;
}
2021-11-02 14:59:10 -04:00
namespace {
std::string jitExec(codon::jit::JIT *jit, const std::string &code) {
auto result = jit->exec(code);
if (auto err = result.takeError()) {
2021-11-06 12:57:09 -04:00
std::string output;
llvm::handleAllErrors(
std::move(err), [](const codon::error::ParserErrorInfo &e) { display(e); },
2021-11-16 15:40:00 -05:00
[&output](const codon::error::RuntimeErrorInfo &e) {
std::stringstream buf;
buf << e.getOutput();
buf << "\n\033[1mBacktrace:\033[0m\n";
for (const auto &line : e.getBacktrace()) {
buf << " " << line << "\n";
}
output = buf.str();
});
2021-11-06 12:57:09 -04:00
return output;
}
return *result;
2021-11-02 14:59:10 -04:00
}
void jitLoop(codon::jit::JIT *jit, std::istream &fp) {
std::string code;
for (std::string line; std::getline(fp, line);) {
if (line != "#%%") {
code += line + "\n";
} else {
fmt::print("{}[done]\n", jitExec(jit, code));
code = "";
fflush(stdout);
}
}
if (!code.empty())
fmt::print("{}[done]\n", jitExec(jit, code));
}
2021-11-02 14:59:10 -04:00
} // namespace
2021-11-01 19:10:33 -04:00
int jitMode(const std::vector<const char *> &args) {
llvm::cl::opt<std::string> input(llvm::cl::Positional, llvm::cl::desc("<input file>"),
llvm::cl::init("-"));
2021-11-30 08:44:36 -05:00
llvm::cl::list<std::string> plugins("plugin",
llvm::cl::desc("Load specified plugin"));
2022-01-20 11:22:32 -05:00
llvm::cl::opt<std::string> log("log", llvm::cl::desc("Enable given log streams"));
2021-11-30 08:44:36 -05:00
llvm::cl::ParseCommandLineOptions(args.size(), args.data());
2022-01-20 11:22:32 -05:00
initLogFlags(log);
2021-11-01 19:10:33 -04:00
codon::jit::JIT jit(args[0]);
2021-11-30 08:44:36 -05:00
// load plugins
for (const auto &plugin : plugins) {
bool failed = false;
llvm::handleAllErrors(jit.getCompiler()->load(plugin),
[&failed](const codon::error::PluginErrorInfo &e) {
codon::compilationError(e.getMessage(), /*file=*/"",
/*line=*/0, /*col=*/0,
/*terminate=*/false);
failed = true;
});
if (failed)
return EXIT_FAILURE;
}
2021-11-02 14:59:10 -04:00
llvm::cantFail(jit.init());
2021-11-01 19:10:33 -04:00
fmt::print(">>> Codon JIT v{} <<<\n", CODON_VERSION);
if (input == "-") {
jitLoop(&jit, std::cin);
} else {
std::ifstream fileInput(input);
jitLoop(&jit, fileInput);
2021-11-01 19:10:33 -04:00
}
return EXIT_SUCCESS;
}
2021-10-23 10:42:32 -07:00
2021-09-27 14:02:44 -04:00
int buildMode(const std::vector<const char *> &args) {
llvm::cl::list<std::string> libs(
"l", llvm::cl::desc("Link the specified library (only for executables)"));
llvm::cl::opt<BuildKind> buildKind(
llvm::cl::desc("output type"),
llvm::cl::values(clEnumValN(LLVM, "llvm", "Generate LLVM IR"),
clEnumValN(Bitcode, "bc", "Generate LLVM bitcode"),
clEnumValN(Object, "obj", "Generate native object file"),
clEnumValN(Executable, "exe", "Generate executable"),
clEnumValN(Detect, "detect",
"Detect output type based on output file extension")),
llvm::cl::init(Detect));
llvm::cl::opt<std::string> output(
"o",
llvm::cl::desc(
"Write compiled output to specified file. Supported extensions: "
"none (executable), .o (object file), .ll (LLVM IR), .bc (LLVM bitcode)"));
2021-11-11 11:50:00 -05:00
auto compiler = processSource(args, /*standalone=*/true);
2021-10-30 14:16:40 -04:00
if (!compiler)
2021-09-27 14:02:44 -04:00
return EXIT_FAILURE;
std::vector<std::string> libsVec(libs);
2021-10-30 14:16:40 -04:00
if (output.empty() && compiler->getInput() == "-")
codon::compilationError("output file must be specified when reading from stdin");
2021-09-27 14:02:44 -04:00
std::string extension;
switch (buildKind) {
case BuildKind::LLVM:
extension = ".ll";
break;
case BuildKind::Bitcode:
extension = ".bc";
break;
case BuildKind::Object:
extension = ".o";
break;
case BuildKind::Executable:
case BuildKind::Detect:
extension = "";
break;
default:
2021-11-03 10:27:47 -04:00
seqassert(0, "unknown build kind");
2021-09-27 14:02:44 -04:00
}
const std::string filename =
2021-10-30 14:16:40 -04:00
output.empty() ? makeOutputFilename(compiler->getInput(), extension) : output;
2021-09-27 14:02:44 -04:00
switch (buildKind) {
case BuildKind::LLVM:
2021-10-30 14:16:40 -04:00
compiler->getLLVMVisitor()->writeToLLFile(filename);
2021-09-27 14:02:44 -04:00
break;
case BuildKind::Bitcode:
2021-10-30 14:16:40 -04:00
compiler->getLLVMVisitor()->writeToBitcodeFile(filename);
2021-09-27 14:02:44 -04:00
break;
case BuildKind::Object:
2021-10-30 14:16:40 -04:00
compiler->getLLVMVisitor()->writeToObjectFile(filename);
2021-09-27 14:02:44 -04:00
break;
case BuildKind::Executable:
2021-10-30 14:16:40 -04:00
compiler->getLLVMVisitor()->writeToExecutable(filename, libsVec);
2021-09-27 14:02:44 -04:00
break;
case BuildKind::Detect:
2021-10-30 14:16:40 -04:00
compiler->getLLVMVisitor()->compile(filename, libsVec);
2021-09-27 14:02:44 -04:00
break;
default:
2021-11-03 10:27:47 -04:00
seqassert(0, "unknown build kind");
2021-09-27 14:02:44 -04:00
}
return EXIT_SUCCESS;
}
2021-11-09 03:47:41 -08:00
#ifdef CODON_JUPYTER
namespace codon {
2021-12-06 01:59:32 -08:00
int startJupyterKernel(const std::string &argv0,
const std::vector<std::string> &plugins,
const std::string &configPath);
2021-11-09 03:47:41 -08:00
}
#endif
int jupyterMode(const std::vector<const char *> &args) {
#ifdef CODON_JUPYTER
2021-12-06 01:59:32 -08:00
llvm::cl::list<std::string> plugins("plugin",
llvm::cl::desc("Load specified plugin"));
llvm::cl::opt<std::string> input(llvm::cl::Positional,
llvm::cl::desc("<connection file>"),
llvm::cl::init("connection.json"));
llvm::cl::ParseCommandLineOptions(args.size(), args.data());
int code = codon::startJupyterKernel(args[0], plugins, input);
2021-11-09 03:47:41 -08:00
return code;
#else
2021-11-10 11:10:44 -05:00
fmt::print("Jupyter support not included. Please recompile with "
"-DCODON_JUPYTER.");
2021-11-09 03:47:41 -08:00
return EXIT_FAILURE;
#endif
}
2021-09-27 14:02:44 -04:00
void showCommandsAndExit() {
codon::compilationError("Available commands: seqc <run|build|doc>");
2021-09-27 14:02:44 -04:00
}
int otherMode(const std::vector<const char *> &args) {
llvm::cl::opt<std::string> input(llvm::cl::Positional, llvm::cl::desc("<mode>"));
llvm::cl::extrahelp("\nMODES:\n\n"
" run - run a program interactively\n"
" build - build a program\n"
" doc - generate program documentation\n");
llvm::cl::ParseCommandLineOptions(args.size(), args.data());
if (!input.empty())
showCommandsAndExit();
return EXIT_SUCCESS;
}
int main(int argc, const char **argv) {
if (argc < 2)
showCommandsAndExit();
llvm::cl::SetVersionPrinter(versMsg);
std::vector<const char *> args{argv[0]};
for (int i = 2; i < argc; i++)
args.push_back(argv[i]);
std::string mode(argv[1]);
std::string argv0 = std::string(args[0]) + " " + mode;
if (mode == "run") {
args[0] = argv0.data();
return runMode(args);
}
if (mode == "build") {
args[0] = argv0.data();
return buildMode(args);
}
if (mode == "doc") {
const char *oldArgv0 = args[0];
args[0] = argv0.data();
return docMode(args, oldArgv0);
}
2021-10-23 10:42:32 -07:00
if (mode == "jit") {
2021-11-01 19:10:33 -04:00
args[0] = argv0.data();
return jitMode(args);
2021-10-23 10:42:32 -07:00
}
2021-11-09 03:47:41 -08:00
if (mode == "jupyter") {
args[0] = argv0.data();
return jupyterMode(args);
}
2021-09-27 14:02:44 -04:00
return otherMode({argv, argv + argc});
}