diff --git a/src/main.cpp b/src/main.cpp index a3f4d87..1ffcc93 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -867,17 +868,137 @@ 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 output = {}; + std::optional outputtype = {}; + std::optional 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) { - if (argc < 2) { - std::cout << "Usage: " << argv[0] << " (file)\n"; - exit(1); - } - std::ifstream file(argv[1]); + auto args = ArgParser::parseArgs(argc, argv); + std::ifstream file(args.file->value); std::ostringstream ss; ss << file.rdbuf(); auto lexed = Solstice::Lexer(ss.str()).lex(); auto parsed = Solstice::Parser::Parser(lexed).parse(); 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); + } }