bolt/src/main.cc

251 lines
6.9 KiB
C++
Raw Normal View History

#include <cwchar>
#include <iostream>
#include <fstream>
#include <algorithm>
#include <map>
#include "llvm/IR/Module.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/MC/TargetRegistry.h"
#include "llvm/Support/Path.h"
#include "zen/po.hpp"
#include "zen/fs/io.hpp"
#include "bolt/CST.hpp"
#include "bolt/CSTVisitor.hpp"
#include "bolt/ConsolePrinter.hpp"
#include "bolt/DiagnosticEngine.hpp"
#include "bolt/Diagnostics.hpp"
#include "bolt/Scanner.hpp"
#include "bolt/Parser.hpp"
2022-08-21 16:25:52 +02:00
#include "bolt/Checker.hpp"
#include "bolt/Evaluator.hpp"
2024-04-08 19:58:00 +02:00
#include "bolt/Program.hpp"
#include "LLVMCodeGen.hpp"
using namespace bolt;
/**
* Status code that can be returned and should according to documentation
* terminate xargs's looping.
*/
const constexpr int XARGS_STOP_LOOP = 255;
namespace po = zen::po;
auto getAllTokens(Stream<Token*>& S) {
std::vector<Token*> Tokens;
for (;;) {
auto Tok = S.get();
Tokens.push_back(Tok);
if (Tok->getKind() == NodeKind::EndOfFile) {
break;
}
}
return Tokens;
}
int main(int Argc, const char* Argv[]) {
auto Match = po::program("bolt", "The offical compiler for the Bolt programming language")
.flag(po::flag<bool>("additional-syntax", "Enable additional Bolt syntax for asserting compiler state"))
.flag(po::flag<bool>("direct-diagnostics", "Immediately print diagnostics without sorting them first")) // TODO support default values in zen::po
.subcommand(
po::command("check", "Check sources for programming mistakes")
.pos_arg("file", po::some))
.subcommand(
po::command("verify", "Verify integrity of the compiler on selected file(s)")
.pos_arg("file", po::some))
.subcommand(
po::command("build", "Build sources into a library or executable")
.pos_arg("file", po::some))
.subcommand(
po::command("eval", "Run sources")
.pos_arg("file", po::some))
.parse_args(Argc, Argv)
.unwrap();
ZEN_ASSERT(Match.has_subcommand());
auto [Name, Submatch] = Match.subcommand();
auto IsVerify = Name == "verify";
auto DirectDiagnostics = Match.has_flag("direct-diagnostics") && Match.get_flag<bool>("direct-diagnostics") && !IsVerify;
auto AdditionalSyntax = Match.has_flag("additional-syntax") && Match.get_flag<bool>("additional-syntax");
ConsolePrinter ThePrinter;
ConsoleDiagnostics DE(ThePrinter);
2024-04-08 19:58:00 +02:00
DiagnosticStore DS;
LanguageConfig Config;
2022-08-21 20:56:58 +02:00
2024-04-08 19:58:00 +02:00
Program Prog { DirectDiagnostics ? static_cast<DiagnosticEngine&>(DE) : DS, Config };
for (auto Filename: Submatch->get_pos_args()) {
auto ReadResult = zen::fs::read_file(Filename);
if (!ReadResult) {
DE.add<OpenFileFailedDiagnostic>(Filename, ReadResult.left());
continue;
}
ByteString Text { ReadResult->c_str(), ReadResult->size() };
TextFile File { Filename, Text };
VectorStream<ByteString, Char> Chars { Text, EOF };
Scanner TheScanner(DE, File, Chars);
Punctuator ThePunctuator(TheScanner);
auto Buffer = getAllTokens(ThePunctuator);
Parser TheParser(File, DE);
TokenStream Tokens { Buffer };
auto SF = TheParser.parseSourceFile(Tokens);
if (SF == nullptr) {
continue;
}
SF->setParents();
2024-04-08 19:58:00 +02:00
Prog.addSourceFile(Filename, SF);
}
2024-04-08 19:58:00 +02:00
Prog.check();
if (IsVerify) {
// TODO make this work with mulitple source files at once
bool HasError = 0;
struct AssertVisitor : public CSTVisitor<AssertVisitor> {
2024-04-08 19:58:00 +02:00
Checker& C;
DiagnosticEngine& DE;
2024-04-08 19:58:00 +02:00
void visitExpression(Expression* N) {
for (auto A: N->Annotations) {
if (A->getKind() == NodeKind::TypeAssertAnnotation) {
auto TA = static_cast<TypeAssertAnnotation*>(A);
auto Left = C.getTypeOfNode(N);
auto Right = TA->getTypeExpression()->getType();
std::cerr << "verify " << Left->toString() << " == " << Right->toString() << std::endl;
if (*Left != *Right) {
DE.add<TypeMismatchError>(Left, Right, TA->getTypeExpression());
}
}
}
visitEachChild(N);
}
2024-04-08 19:58:00 +02:00
};
2024-04-08 19:58:00 +02:00
for (auto SF: Prog.getSourceFiles()) {
AssertVisitor V { {}, Prog.getTypeChecker(SF), DE };
V.visit(SF);
}
struct ExpectDiagnosticVisitor : public CSTVisitor<ExpectDiagnosticVisitor> {
2024-04-08 19:58:00 +02:00
std::multimap<std::size_t, unsigned> Expected;
2024-04-08 19:58:00 +02:00
void visitExpressionAnnotation(ExpressionAnnotation* N) {
if (isa<CallExpression>(N->getExpression())) {
auto CE = static_cast<CallExpression*>(N->getExpression());
if (isa<ReferenceExpression>(CE->Function)) {
auto RE = static_cast<ReferenceExpression*>(CE->Function);
if (RE->getNameAsString() == "expect_diagnostic") {
ZEN_ASSERT(CE->Args.size() == 1 && isa<LiteralExpression>(CE->Args[0]));
Expected.emplace(N->Parent->getStartLine(), static_cast<LiteralExpression*>(CE->Args[0])->getAsInt());
}
}
}
}
2024-04-08 19:58:00 +02:00
};
ExpectDiagnosticVisitor V1;
2024-04-08 19:58:00 +02:00
for (auto SF: Prog.getSourceFiles()) {
V1.visit(SF);
}
for (auto D: DS.Diagnostics) {
auto N = D->getNode();
if (N) {
auto Line = N->getStartLine();
auto Match = V1.Expected.find(Line);
if (Match != V1.Expected.end() && Match->second == D->getCode()) {
std::cerr << "caught 1 diagnostic" << std::endl;
continue;
}
}
// Whenever D did not succeed to match we have to print the diagnostic error
ThePrinter.writeDiagnostic(*D);
HasError = true;
}
if (HasError) {
return XARGS_STOP_LOOP;
}
} else {
DS.sort();
for (auto D: DS.Diagnostics) {
ThePrinter.writeDiagnostic(*D);
}
}
if (DE.hasError()) {
return 255;
}
if (Name == "build") {
// auto HostABI = "x86_64";
// auto TripleStr = "x86_64-pc-linux-gnu";
// std::string Error;
// auto Target = llvm::TargetRegistry::lookupTarget(TripleStr, Error);
// if (!Target) {
// error("failed to create codegen target: {}\n", Error);
// return 255;
// }
llvm::LLVMContext TheContext;
for (auto SF: Prog.getSourceFiles()) {
LLVMCodeGen CG { TheContext, Prog.getTypeChecker(SF) };
auto Module = CG.generate(SF);
auto SourcePath = SF->getFilePath();
auto IRPath = SourcePath.parent_path() / (SourcePath.stem().string() + ".ll");
std::cerr << IRPath << "\n";
// std::error_code EC;
// llvm::raw_fd_ostream OS { IRPath, EC };
// Module->print(OS, nullptr);
}
} else if (Name == "eval") {
Evaluator E;
Env GlobalEnv;
GlobalEnv.add("print", Value::binding([](auto Args) {
ZEN_ASSERT(Args.size() == 1)
std::cerr << Args[0].asString() << "\n";
return Value::unit();
}));
2024-04-08 19:58:00 +02:00
for (auto SF: Prog.getSourceFiles()) {
// TODO add a SourceFile-local env that inherits from GlobalEnv
E.evaluate(SF, GlobalEnv);
}
}
return 0;
}