Add argument parser, output files

This commit is contained in:
2025-12-21 11:42:18 +11:00
parent a3a9553189
commit 869f71466e

View File

@@ -1,5 +1,6 @@
#include <cctype> #include <cctype>
#include <cstdint> #include <cstdint>
#include <cstdio>
#include <groundvm.h> #include <groundvm.h>
#include <vector> #include <vector>
#include <string> #include <string>
@@ -867,17 +868,137 @@ namespace Solstice {
} // namespace Solstice } // namespace Solstice
namespace ArgParser {
enum class ArgType {
FILE, OUTPUT, OUTPUTTYPE
};
class Argument {
public:
ArgType type;
std::string value;
Argument() = default;
Argument(ArgType type, std::string value) : type(type), value(value) {}
};
class Args {
public:
std::optional<Argument> output = {};
std::optional<Argument> outputtype = {};
std::optional<Argument> file = {};
};
void printHelp() {
std::cout << "Solstice compiler\n";
std::cout << "This program takes a Solstice source file (.sols) and compiles it to Ground\n";
std::cout << "Usage:\n";
std::cout << " solstice inputFile [-h] [--help] [-o file] [--output file] [-t type] [--type type]\n";
std::cout << "Options:\n";
std::cout << " -h or --help\n";
std::cout << " Shows this help message\n";
std::cout << " -o file or --output file\n";
std::cout << " Specifies where to output the compiled Ground program.\n";
std::cout << " If this option is omitted, the program is automatically run.\n";
std::cout << " -t type or --type type\n";
std::cout << " Specifies the type of output.\n";
std::cout << " Currently supported options:\n";
std::cout << " text\n";
}
Args parseArgs(int argc, char** argv) {
if (argc < 2) {
printHelp();
exit(1);
}
Args args;
for (int i = 1; i < argc; i++) {
if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
printHelp();
exit(1);
}
if (strcmp(argv[i], "-o") == 0 || strcmp(argv[i], "--output") == 0) {
i++;
if (i < argc) {
if (args.output.has_value()) {
std::cout << "(error) Multiple output values provided\n";
exit(1);
}
args.output = Argument(ArgType::OUTPUT, std::string(argv[i]));
continue;
} else {
std::cout << "(error) Please provide a filename to output to\n";
exit(1);
}
}
else if (strcmp(argv[i], "-t") == 0 || strcmp(argv[i], "--type") == 0) {
i++;
if (i < argc) {
if (args.output.has_value()) {
std::cout << "(error) Multiple type values provided\n";
exit(1);
}
args.outputtype = Argument(ArgType::OUTPUTTYPE, std::string(argv[i]));
continue;
} else {
std::cout << "(error) Please provide an output type\n";
exit(1);
}
}
else {
if (args.file.has_value()) {
std::cout << "(error) Multiple input files provided\n";
exit(1);
}
args.file = Argument(ArgType::FILE, std::string(argv[i]));
}
}
if (!args.file.has_value()) {
std::cout << "(error) No input file provided\n";
exit(1);
}
return args;
}
} // namespace ArgParser
int main(int argc, char** argv) { int main(int argc, char** argv) {
if (argc < 2) { auto args = ArgParser::parseArgs(argc, argv);
std::cout << "Usage: " << argv[0] << " (file)\n"; std::ifstream file(args.file->value);
exit(1);
}
std::ifstream file(argv[1]);
std::ostringstream ss; std::ostringstream ss;
ss << file.rdbuf(); ss << file.rdbuf();
auto lexed = Solstice::Lexer(ss.str()).lex(); auto lexed = Solstice::Lexer(ss.str()).lex();
auto parsed = Solstice::Parser::Parser(lexed).parse(); auto parsed = Solstice::Parser::Parser(lexed).parse();
GroundProgram program = Solstice::Parser::assembleProgram(parsed); GroundProgram program = Solstice::Parser::assembleProgram(parsed);
groundRunProgram(&program); if (args.output.has_value()) {
std::FILE* originalStdout = stdout;
std::FILE* tmp = std::tmpfile();
if (!tmp) {
std::cout << "Failed to create tmp file\n";
exit(1);
}
stdout = tmp;
groundPrintProgram(&program);
std::fflush(tmp);
std::fseek(tmp, 0, SEEK_END);
long size = std::ftell(tmp);
std::fseek(tmp, 0, SEEK_SET);
std::string result(size, '\0');
std::fread(&result[0], 1, size, tmp);
stdout = originalStdout;
std::fclose(tmp);
std::ofstream outputFile(args.output.value().value);
if (!outputFile) {
std::cout << "Failed to write to " << args.output.value().value << "\n";
}
outputFile << result;
outputFile.close();
exit(0);
} else {
groundRunProgram(&program);
}
} }