437 lines
8.1 KiB
C
437 lines
8.1 KiB
C
#include <assert.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include "debug.h"
|
|
#include "o_builtins.h"
|
|
#include "compiler/parse.h"
|
|
#include "compiler/serialize.h"
|
|
#include "compiler/compile.h"
|
|
#include "runtime/runtime.h"
|
|
#include "runtime/runtime_error.h"
|
|
|
|
/* $ noja -f <file>
|
|
* $ noja -c <text>
|
|
* $ noja -f <file> -o <file>
|
|
*/
|
|
|
|
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;
|
|
}
|
|
|
|
#include "buildvalue.h"
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
Error error;
|
|
Error_Init(&error);
|
|
|
|
{
|
|
Heap *heap = Heap_New(-1);
|
|
|
|
if(heap == NULL)
|
|
{
|
|
fprintf(stderr, "ERROR: Failed to create heap.\n");
|
|
return -1;
|
|
}
|
|
|
|
Object *o = buildValue(heap, &error, "${ return 3+1; }");
|
|
|
|
if(o == NULL)
|
|
{
|
|
fprintf(stderr, "ERROR: %s.\n", error.message);
|
|
return -1;
|
|
}
|
|
|
|
Object_Print(o, stdout);
|
|
|
|
Heap_Free(heap);
|
|
}
|
|
|
|
|
|
// 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 *builtins = Object_NewBuiltinsMap(runtime, Runtime_GetHeap(runtime), (Error*) &error);
|
|
|
|
if(builtins == NULL)
|
|
{
|
|
fprintf(stderr, "Couldn't initialize runtime.\n");
|
|
Debug_Free(dbg);
|
|
Source_Free(src);
|
|
Executable_Free(exe);
|
|
RuntimeError_Free(&error);
|
|
Runtime_Free(runtime);
|
|
return 1;
|
|
}
|
|
|
|
Runtime_SetBuiltins(runtime, builtins);
|
|
|
|
Object *result = run(runtime, (Error*) &error, exe, 0, NULL, NULL, 0);
|
|
|
|
// NOTE: The `builtins` pointer is invalidated now.
|
|
|
|
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;
|
|
}
|
|
|