aboutsummaryrefslogtreecommitdiff
path: root/tools/driver/main.cc
blob: 4df8562e6f3a3893aac6edaaf518a4eab3c0daf3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153

#include "cps/Lexer.h"
#include "cps/Parser.h"
#include "cps/CodeGen.h"
#ifdef CPSC_RUN_EMBEDDED
#include "EmbeddedRunner.h"
#endif
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/FileUtilities.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Program.h"
#include "llvm/Support/raw_ostream.h"
#include <fstream>
#include <string>

int main(int argc, char **argv) {
    if (argc == 1) {
        // TODO: Print help here when help text is available.
        return 0;
    }

    std::string InputPath;
    std::string OutputPath;
    bool Run = false;
    for (int I = 1; I < argc; ++I) {
        std::string Arg = argv[I];
        if (Arg == "-r") {
            Run = true;
        } else if (Arg == "-o") {
            if (!OutputPath.empty()) {
                llvm::errs() << "Error: -o may only be specified once\n";
                return 1;
            }
            if (I + 1 == argc || argv[I + 1][0] == '-' || argv[I + 1][0] == '\0') {
                llvm::errs() << "Error: -o requires an output path\n";
                return 1;
            }
            OutputPath = argv[++I];
        } else if (Arg.empty() || Arg[0] == '-') {
            llvm::errs() << "Error: unknown argument '" << Arg << "'\n";
            return 1;
        } else if (!InputPath.empty()) {
            llvm::errs() << "Error: only one source file is supported\n";
            return 1;
        } else {
            InputPath = Arg;
        }
    }

    if (InputPath.empty()) {
        llvm::errs() << "Error: no source file specified\n";
        return 1;
    }
#ifdef CPSC_RUN_PATH
    std::string LliPath;
#endif
    if (Run) {
#ifdef CPSC_RUN_PATH
        auto Lli = llvm::sys::findProgramByName("lli");
        if (!Lli) {
            llvm::errs() << "Error: -r requires lli in PATH: " << Lli.getError().message() << "\n";
            return 1;
        }
        LliPath = *Lli;
#elif defined(CPSC_RUN_NONE)
        llvm::errs() << "Error: -r is disabled in this build (CPSC_RUN_MODE=NONE)\n";
        return 1;
#endif
    }
    if (OutputPath.empty() && !Run) {
        llvm::SmallString<256> DefaultPath(InputPath);
        llvm::sys::path::replace_extension(DefaultPath, ".ir");
        OutputPath = std::string(DefaultPath);
    }

    std::ifstream Input(InputPath, std::ios::binary);
    if (!Input) {
        llvm::errs() << "Error: cannot open source file '" << InputPath << "'\n";
        return 1;
    }
    if (llvm::sys::fs::equivalent(InputPath, OutputPath)) {
        llvm::errs() << "Error: output file must not be the source file\n";
        return 1;
    }

    cps::Lexer Lex(Input);

    cps::Parser Parser(Lex);
    auto Statements = Parser.Parse();
    if (Input.bad()) {
        llvm::errs() << "Error: cannot read source file '" << InputPath << "'\n";
        return 1;
    }
    if (Lex.hadError() || Parser.hadError()) return 1;

    cps::CodeGen CG;
    CG.compile(Statements);

    if (CG.hadError()) return 1;

#ifdef CPSC_RUN_EMBEDDED
    if (Run && OutputPath.empty()) return runEmbedded(CG);
#endif
    std::error_code EC;
    int OutputFD;
    llvm::FileRemover TemporaryOutput;
    if (OutputPath.empty()) {
        llvm::SmallString<256> TemporaryPath;
        EC = llvm::sys::fs::createTemporaryFile("cpsc", "ir", OutputFD, TemporaryPath);
        if (!EC) {
            OutputPath = std::string(TemporaryPath);
            TemporaryOutput.setFile(OutputPath);
        }
    } else {
        EC = llvm::sys::fs::openFileForWrite(OutputPath, OutputFD);
    }
    if (EC) {
        llvm::errs() << "Error: cannot open output file '" << OutputPath
                     << "': " << EC.message() << "\n";
        return 1;
    }
    llvm::raw_fd_ostream Output(OutputFD, true);
    CG.print(Output);
    Output.close();
    if (Output.has_error()) {
        llvm::errs() << "Error: cannot write output file '" << OutputPath
                     << "': " << Output.error().message() << "\n";
        Output.clear_error();
        return 1;
    }
    if (!Run) return 0;

#ifdef CPSC_RUN_PATH
    llvm::SmallString<256> RunPath(OutputPath);
    if (auto PathError = llvm::sys::fs::make_absolute(RunPath)) {
        llvm::errs() << "Error: cannot resolve IR path: " << PathError.message() << "\n";
        return 1;
    }
    std::string ExecutionError;
    int Result = llvm::sys::ExecuteAndWait(LliPath, {LliPath, RunPath},
                                         std::nullopt, {}, 0, 0, &ExecutionError);
    if (Result < 0) {
        llvm::errs() << "Error: lli execution failed (status " << Result << "): "
                     << ExecutionError << "\n";
        return 1;
    }
    return Result;
#elif defined(CPSC_RUN_EMBEDDED)
    return runEmbedded(CG);
#else
    return 0;
#endif
}