#include #include #include #include #include "debug.h" #include "compiler/parse.h" #include "compiler/serialize.h" #include "compiler/compile.h" #include "runtime/runtime.h" #include "runtime/runtime_error.h" /* $ noja -f * $ noja -c * $ noja -f -o */ typedef enum { RUN, HELP, PARSE, VERSION, DISASSEMBLY } Action; typedef struct { Action action; _Bool input_is_file, debug, verbose; const char *input, *output; } Options; static int parse_args(Options *opts, int argc, char **argv, Error *error) { assert(argc >= 0); assert(opts != NULL); opts->action = RUN; opts->input_is_file = 1; opts->debug = 0; opts->verbose = 0; opts->input = NULL; opts->output = NULL; int i; for(i = 1; i < argc; i += 1) { if(!strcmp("-r", argv[i]) || !strcmp("--run", argv[i])) { opts->action = RUN; } else if(!strcmp("-p", argv[i]) || !strcmp("--parse", argv[i])) { opts->action = PARSE; } else if(!strcmp("-d", argv[i]) || !strcmp("--disassembly", argv[i])) { opts->action = DISASSEMBLY; } else if(!strcmp("-h", argv[i]) || !strcmp("--help", argv[i])) { opts->action = HELP; } else if(!strcmp("-v", argv[i]) || !strcmp("--version", argv[i])) { opts->action = VERSION; } else if(!strcmp("--debug", argv[i])) { opts->debug = 1; } else if(!strcmp("--verbose", argv[i])) { opts->verbose = 1; } else if(!strcmp("-f", argv[i])) { opts->input_is_file = 1; } else if(!strcmp("-c", argv[i])) { opts->input_is_file = 0; } else if(!strcmp("-o", argv[i])) { i += 1; if(i == argc) { Error_Report(error, 0, "Option -o requires a file name after it"); return -1; } opts->output = argv[i]; } else break; } if(i == argc) { Error_Report(error, 0, "Input file is missing"); return -1; } opts->input = argv[i]; i += 1; return i; } int main(int argc, char **argv) { Error error; Error_Init(&error); // Parse command line options. Options opts; { int i = parse_args(&opts, argc, argv, &error); if(i < 0) { fprintf(stderr, "FATAL: %s.\n", error.message); Error_Free(&error); return 1; } } switch(opts.action) { case HELP: fprintf(stderr, "Not implemented yet.\n"); return 1; case VERSION: fprintf(stderr, "Not implemented yet.\n"); return 1; case RUN: { // Load Source *src; { if(opts.input_is_file) src = Source_FromFile(opts.input, &error); else src = Source_FromString(NULL, opts.input, -1, &error); if(src == NULL) { fprintf(stderr, "FATAL: %s.\n", error.message); return 1; } } // Compile Executable *exe; { BPAlloc *alloc = BPAlloc_Init(-1); if(alloc == NULL) return 1; AST *ast = parse(src, alloc, &error); if(ast == NULL) { fprintf(stderr, "PARSING ERROR: %s.\n", error.message); Error_Free(&error); BPAlloc_Free(alloc); Source_Free(src); return 1; } exe = compile(ast, alloc, &error); if(exe == NULL) { fprintf(stderr, "COMPILATION ERROR: %s.\n", error.message); Error_Free(&error); BPAlloc_Free(alloc); Source_Free(src); return 1; } BPAlloc_Free(alloc); } // Execute { Debug *dbg = NULL; Runtime *runtime; if(opts.debug) { dbg = Debug_New(); if(dbg == NULL) { fprintf(stderr, "Couldn't initialize debug state.\n"); Executable_Free(exe); Source_Free(src); return 1; } runtime = Runtime_New(-1, -1, dbg, Debug_Callback); } else runtime = Runtime_New(-1, -1, NULL, NULL); if(runtime == NULL) { fprintf(stderr, "Couldn't initialize runtime.\n"); Debug_Free(dbg); Executable_Free(exe); Source_Free(src); return 1; } RuntimeError error; RuntimeError_Init(&error, runtime); Object *result = run(runtime, (Error*) &error, exe, 0, NULL, 0); if(result == NULL) { fprintf(stderr, "RUNTIME ERROR: %s.\n", error.base.message); if(error.snapshot) Snapshot_Print(error.snapshot, stderr); else fprintf(stderr, "No snapshot available.\n"); Debug_Free(dbg); Source_Free(src); Executable_Free(exe); RuntimeError_Free(&error); Runtime_Free(runtime); return 1; } Runtime_Free(runtime); Debug_Free(dbg); } Executable_Free(exe); Source_Free(src); break; } case PARSE: { // Load Source *src; { if(opts.input_is_file) src = Source_FromFile(opts.input, &error); else src = Source_FromString(NULL, opts.input, -1, &error); if(src == NULL) { fprintf(stderr, "FATAL: %s.\n", error.message); return 1; } } BPAlloc *alloc = BPAlloc_Init(-1); if(alloc == NULL) return 1; Error error; Error_Init(&error); AST *ast = parse(src, alloc, &error); if(ast == NULL) { fprintf(stderr, "PARSING ERROR: %s.\n", error.message); Error_Free(&error); BPAlloc_Free(alloc); Source_Free(src); return 1; } int len; char *str = serialize(ast, &len); _Bool failed = 0; if(str == NULL) { fprintf(stderr, "Failed to serialize\n"); failed = 1; } else { if(opts.output == NULL) { fprintf(stdout, "%s\n", str); } else { FILE *fp = fopen(opts.output, "wb"); if(fp == NULL) { fprintf(stderr, "Couldn't write to \"%s\".\n", opts.output); failed = 1; } else { int k = fwrite(str, 1, len, fp); if(k != len) { fprintf(stderr, "Only %d bytes out of %d could be written to \"%s\".\n", k, len, opts.output); failed = 1; } } } free(str); } Source_Free(src); BPAlloc_Free(alloc); return failed; } case DISASSEMBLY: { // Load Source *src; { if(opts.input_is_file) src = Source_FromFile(opts.input, &error); else src = Source_FromString(NULL, opts.input, -1, &error); if(src == NULL) { fprintf(stderr, "FATAL: %s.\n", error.message); return 1; } } // Compile Executable *exe; { BPAlloc *alloc = BPAlloc_Init(-1); if(alloc == NULL) return 1; AST *ast = parse(src, alloc, &error); if(ast == NULL) { fprintf(stderr, "PARSING ERROR: %s.\n", error.message); Error_Free(&error); BPAlloc_Free(alloc); Source_Free(src); return 1; } exe = compile(ast, alloc, &error); if(exe == NULL) { fprintf(stderr, "COMPILATION ERROR: %s.\n", error.message); Error_Free(&error); BPAlloc_Free(alloc); Source_Free(src); return 1; } BPAlloc_Free(alloc); } Executable_Dump(exe); Executable_Free(exe); Source_Free(src); return 1; } } return 0; }