diff --git a/src/main.c b/src/main.c index 2f6cdde..c96f525 100644 --- a/src/main.c +++ b/src/main.c @@ -2,10 +2,88 @@ #include "lexer/lexer.h" #include "parser/parser.h" #include "codegen/codegen.h" +#include "include/error.h" +#include "include/estr.h" #include +#ifdef _WIN32 +#include +#else +#include +#endif #include +typedef enum SolsAction { + SA_PRINT, SA_EXEC, SA_BYTECODE, SA_COMPILE +} SolsAction; + +typedef struct Args { + SolsAction action; + char* inputFile; + char* outputFile; +} Args; + +Args parseArgs(int argc, char** argv) { + Args args = { + .action = SA_EXEC, + .inputFile = NULL, + .outputFile = NULL + }; + for (int i = 0; i < argc; i++) { + if (strcmp("-h", argv[i]) == 0 || strcmp("--help", argv[i]) == 0) { + printf("Solstice programming language\n"); + printf("Usage: %s [-h] [--help] [-p] [--print] [-b ] [--bytecode ] [-c ] [--compile ]\n", argv[0]); + printf("Args:\n"); + printf(" : Solstice source file\n"); + printf(" -h or --help: Prints this help message and exits\n"); + printf(" -p or --print: Prints textual version of Ground bytecode to console\n"); + printf(" -b or --bytecode : Generates Ground bytecode (.grbc) and saves it to the provided filename\n"); + printf(" -c or --compile : Compiles Ground to Linux x86_64 assembly, outputs a binary to the provided filename (experimental)\n"); + printf(" If no extra arguments are provided, the generated Ground bytecode will be executed.\n"); + exit(0); + } + else if (strcmp("-p", argv[i]) == 0 || strcmp("--print", argv[i]) == 0) { + args.action = SA_PRINT; + } + else if (strcmp("-b", argv[i]) == 0 || strcmp("--bytecode", argv[i]) == 0) { + if (args.action != SA_EXEC) { + printf("Expecting only one action\n"); + exit(1); + } + args.action = SA_BYTECODE; + if (i + 1 >= argc) { + printf("Expecting file name after %s\n", argv[i]); + exit(1); + } + i++; + args.outputFile = argv[i]; + } + else if (strcmp("-c", argv[i]) == 0 || strcmp("--compile", argv[i]) == 0) { + if (args.action != SA_EXEC) { + printf("Expecting only one action\n"); + exit(1); + } + args.action = SA_COMPILE; + if (i + 1 >= argc) { + printf("Expecting file name after %s\n", argv[i]); + exit(1); + } + i++; + args.outputFile = argv[i]; + } + else { + args.inputFile = argv[i]; + } + } + + if (args.inputFile == NULL) { + printf("Usage: %s [-h] [--help] [-p] [--print] [-b ] [--bytecode ] [-c ] [--compile ]\n", argv[0]); + exit(1); + } + + return args; +} + char* getFileContents(const char* filename) { // https://stackoverflow.com/questions/3747086/reading-the-whole-text-file-into-a-char-array-in-c FILE* fp; @@ -41,24 +119,8 @@ char* getFileContents(const char* filename) { } int main(int argc, char** argv) { - if (argc < 2) { - printf("Usage: %s [file]\n", argv[0]); - exit(1); - } - - char* fileContents = NULL; - bool printProgram = false; - - if (strcmp(argv[1], "-p") == 0) { - if (argc < 3) { - printf("Usage: %s -p [file]\n", argv[0]); - exit(1); - } - printProgram = true; - fileContents = getFileContents(argv[2]); - } else { - fileContents = getFileContents(argv[1]); - } + Args args = parseArgs(argc, argv); + char* fileContents = getFileContents(args.inputFile); if (fileContents == NULL) { printf("Couldn't read that file :(\n"); @@ -102,16 +164,81 @@ int main(int argc, char** argv) { exit(1); } - if (printProgram) { - groundPrintProgram(&codegen.as.success); - exit(0); + switch (args.action) { + case SA_PRINT: { + groundPrintProgram(&codegen.as.success); + break; + } + case SA_EXEC: { + GroundValue retval = groundRunProgram(&codegen.as.success); + if (retval.type == INT) { + return retval.data.intVal; + } else { + return 0; + } + break; + } + case SA_BYTECODE: { + serializeProgramToFile(args.outputFile, &codegen.as.success); + break; + } + case SA_COMPILE: { + char* compiled = groundCompileProgram(&codegen.as.success); + + // Make work directory + Estr dir = CREATE_ESTR(".solsbuild_"); + APPEND_ESTR(dir, args.outputFile); + + #ifdef _WIN32 + int dirstatus = _mkdir(estr.str); + #else + int dirstatus = mkdir(dir.str, 0755); + #endif + if (dirstatus != 0) { + printf("Couldn't create temporary work directory\n"); + exit(1); + } + + // Write generated asm to .s + Estr tmpasm = CREATE_ESTR(dir.str); + APPEND_ESTR(tmpasm, "/generated.s"); + + FILE* file = fopen(tmpasm.str, "w"); + if (file == NULL) { + printf("Couldn't write to temporary assembly file"); + } + fprintf(file, "%s", compiled); + fclose(file); + + // Assemble with nasm + Estr nasmcmd = CREATE_ESTR("nasm -f elf64 -o "); + APPEND_ESTR(nasmcmd, dir.str); + APPEND_ESTR(nasmcmd, "/generated.o "); + APPEND_ESTR(nasmcmd, tmpasm.str); + + int nasmstatus = system(nasmcmd.str); + if (nasmstatus != 0) { + printf("command \"%s\" exited with code %d\n", nasmcmd.str, nasmstatus); + exit(1); + } + + // Link with ld + Estr ldcmd = CREATE_ESTR("ld -o "); + APPEND_ESTR(ldcmd, args.outputFile); + APPEND_ESTR(ldcmd, " "); + APPEND_ESTR(ldcmd, dir.str); + APPEND_ESTR(ldcmd, "/generated.o"); + + int ldstatus = system(ldcmd.str); + if (ldstatus != 0) { + printf("command \"%s\" exited with code %d\n", ldcmd.str, ldstatus); + exit(1); + } + + // Yay we compiled it + break; + } } - // Run program on GroundVM - GroundValue retval = groundRunProgram(&codegen.as.success); - if (retval.type == INT) { - return retval.data.intVal; - } else { - return 0; - } + return 0; }