2021-12-21 01:18:22 +01:00
|
|
|
#include <errno.h>
|
|
|
|
#include <math.h>
|
|
|
|
#include <stdarg.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
#include "ir.h"
|
|
|
|
#include "lex.h"
|
|
|
|
#include "parse.h"
|
|
|
|
#include "util.h"
|
2021-12-22 17:23:24 +01:00
|
|
|
#include "vm.h"
|
2021-12-21 01:18:22 +01:00
|
|
|
|
|
|
|
static void usage(const char *prgname);
|
|
|
|
static void die(const char *fmt, ...);
|
|
|
|
|
|
|
|
static void usage(const char *prgname) {
|
|
|
|
fprintf(stderr, "Usage:\n"
|
|
|
|
" %s [OPTIONS] <FILENAME>\n"
|
|
|
|
"Options:\n"
|
|
|
|
" -emit-tokens\n"
|
|
|
|
" -emit-ir\n"
|
|
|
|
" -dry -- don't execute the script (just process it)\n"
|
|
|
|
, prgname);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void die(const char *fmt, ...) {
|
|
|
|
fprintf(stderr, C_IRED "Error: " C_RESET);
|
|
|
|
va_list va;
|
|
|
|
va_start(va, fmt);
|
|
|
|
vfprintf(stderr, fmt, va);
|
|
|
|
va_end(va);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
2021-12-23 21:26:53 +01:00
|
|
|
static Value fn_put(Value *args) {
|
2021-12-25 12:16:06 +01:00
|
|
|
print_value(&args[0], true);
|
2021-12-22 16:09:52 +01:00
|
|
|
return (Value){0};
|
|
|
|
}
|
|
|
|
|
2021-12-26 10:55:31 +01:00
|
|
|
static Value fn_putln(Value *args) {
|
2021-12-23 21:26:53 +01:00
|
|
|
fn_put(args);
|
|
|
|
printf("\n");
|
|
|
|
return (Value){0};
|
|
|
|
}
|
|
|
|
|
2021-12-22 16:09:52 +01:00
|
|
|
static Value fn_int(Value *args) {
|
|
|
|
Value ret = {
|
|
|
|
.type.kind = TypeInt,
|
|
|
|
.Int = 0,
|
|
|
|
};
|
|
|
|
switch (args[0].type.kind) {
|
|
|
|
case TypeVoid: break;
|
|
|
|
case TypeFloat: ret.Int = (ssize_t)args[0].Float; break;
|
|
|
|
case TypeInt: ret.Int = args[0].Int; break;
|
2021-12-26 11:36:52 +01:00
|
|
|
case TypeBool: ret.Int = (ssize_t)args[0].Bool; break;
|
|
|
|
case TypeArr:
|
|
|
|
if (args[0].Arr.is_string && args[0].Arr.type.kind == TypeChar) {
|
|
|
|
ssize_t endpos;
|
|
|
|
ret.Int = stoimax((char*)args[0].Arr.vals, args[0].Arr.len, 10, &endpos);
|
|
|
|
if (endpos != -1) {
|
|
|
|
set_err("Error converting from string to int");
|
|
|
|
return (Value){0};
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
ASSERT_UNREACHED();
|
|
|
|
break;
|
2021-12-22 16:09:52 +01:00
|
|
|
default: ASSERT_UNREACHED();
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Value fn_float(Value *args) {
|
|
|
|
Value ret = {
|
|
|
|
.type.kind = TypeFloat,
|
|
|
|
.Float = 0.0,
|
|
|
|
};
|
|
|
|
switch (args[0].type.kind) {
|
|
|
|
case TypeVoid: break;
|
2021-12-26 11:36:52 +01:00
|
|
|
case TypeFloat: ret.Float = args[0].Float; break;
|
|
|
|
case TypeInt: ret.Float = (double)args[0].Int; break;
|
|
|
|
case TypeBool: ret.Float = (double)args[0].Bool; break;
|
|
|
|
case TypeArr:
|
|
|
|
if (args[0].Arr.is_string && args[0].Arr.type.kind == TypeChar) {
|
|
|
|
ssize_t endpos;
|
|
|
|
ret.Float = stod((char*)args[0].Arr.vals, args[0].Arr.len, &endpos);
|
|
|
|
if (endpos != -1) {
|
|
|
|
set_err("Error converting from string to float");
|
|
|
|
return (Value){0};
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
ASSERT_UNREACHED();
|
|
|
|
break;
|
2021-12-22 16:09:52 +01:00
|
|
|
default: ASSERT_UNREACHED();
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Value fn_pow(Value *args) {
|
|
|
|
if (!(args[0].type.kind == TypeFloat && args[1].type.kind == TypeFloat)) {
|
|
|
|
set_err("pow() requires arguments of type float");
|
|
|
|
return (Value){0};
|
|
|
|
}
|
2021-12-25 14:06:20 +01:00
|
|
|
return (Value){
|
2021-12-22 16:09:52 +01:00
|
|
|
.type.kind = TypeFloat,
|
|
|
|
.Float = pow(args[0].Float, args[1].Float),
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2021-12-25 14:06:20 +01:00
|
|
|
static Value fn_sleep(Value *args) {
|
|
|
|
if (!(args[0].type.kind == TypeFloat && args[0].Float >= 0.0)) {
|
|
|
|
set_err("sleep() requires a positive float");
|
|
|
|
return (Value){0};
|
|
|
|
}
|
|
|
|
sleep_secs(args[0].Float);
|
|
|
|
return (Value){0};
|
|
|
|
}
|
|
|
|
|
2021-12-26 11:36:52 +01:00
|
|
|
static Value fn_getln(Value *args) {
|
|
|
|
(void)args;
|
|
|
|
|
|
|
|
char *line = xmalloc(64);
|
|
|
|
size_t len = 0, cap = 64;
|
|
|
|
for (;;) {
|
|
|
|
int c = fgetc(stdin);
|
|
|
|
if (c == EOF)
|
|
|
|
break;
|
|
|
|
else if (c == '\n')
|
|
|
|
break;
|
|
|
|
if (len+1 > cap)
|
|
|
|
line = xrealloc(line, (cap *= 2));
|
|
|
|
line[len++] = c;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (Value){
|
|
|
|
.type.kind = TypeArr,
|
|
|
|
.Arr = {
|
|
|
|
.is_string = true,
|
|
|
|
.type.kind = TypeChar,
|
|
|
|
.vals = line,
|
|
|
|
.len = len,
|
|
|
|
.cap = cap,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2021-12-21 01:18:22 +01:00
|
|
|
int main(int argc, const char **argv) {
|
|
|
|
/* parse arguments */
|
|
|
|
size_t nargs = argc - 1;
|
|
|
|
const char *prgname = argv[0];
|
|
|
|
const char **args = argv + 1;
|
|
|
|
bool opt_emit_tokens = false;
|
|
|
|
bool opt_emit_ir = false;
|
|
|
|
bool opt_dry = false;
|
|
|
|
const char *filename = NULL;
|
|
|
|
for (size_t i = 0; i < nargs; i++) {
|
|
|
|
if (args[i][0] == '-') {
|
|
|
|
if (streq(args[i], "-h") || streq(args[i], "-help") || streq(args[i], "--help")) {
|
|
|
|
usage(prgname);
|
|
|
|
return 0;
|
|
|
|
} else if (streq(args[i], "-emit-ir"))
|
|
|
|
opt_emit_ir = true;
|
|
|
|
else if (streq(args[i], "-emit-tokens"))
|
|
|
|
opt_emit_tokens = true;
|
|
|
|
else if (streq(args[i], "-dry"))
|
|
|
|
opt_dry = true;
|
|
|
|
else {
|
|
|
|
die("Unknown option: %s\n", args[i]);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (filename) {
|
|
|
|
die("Filename already set to '%s'\n", filename);
|
|
|
|
}
|
|
|
|
filename = args[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!filename) {
|
|
|
|
die("Please specify a filename\n");
|
|
|
|
}
|
|
|
|
/* read source file */
|
|
|
|
FILE *fp = fopen(filename, "r");
|
|
|
|
if (!fp) {
|
|
|
|
die("Failed to open '%s': %s\n", filename, strerror(errno));
|
|
|
|
}
|
|
|
|
char *file = mreadfile(fp);
|
|
|
|
if (!file) {
|
|
|
|
fclose(fp);
|
|
|
|
die("Failed to read '%s': %s\n", filename, strerror(errno));
|
|
|
|
}
|
|
|
|
fclose(fp);
|
|
|
|
/* lex source file */
|
2021-12-25 12:16:06 +01:00
|
|
|
Pool *static_vars = pool_new(4096);
|
|
|
|
TokList tokens = lex(file, static_vars);
|
2021-12-21 01:18:22 +01:00
|
|
|
if (err) {
|
|
|
|
toklist_term(&tokens);
|
2021-12-25 12:16:06 +01:00
|
|
|
pool_term(static_vars);
|
2021-12-21 01:18:22 +01:00
|
|
|
free(file);
|
|
|
|
fprintf(stderr, C_IRED "Lexer error" C_RESET " in " C_CYAN "%s" C_RESET ":%zu:%zu: %s\n", filename, err_ln, err_col, errbuf);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
free(file);
|
|
|
|
if (opt_emit_tokens)
|
|
|
|
print_toks(&tokens);
|
|
|
|
/* parse tokens into IR code */
|
2021-12-22 16:09:52 +01:00
|
|
|
BuiltinFunc funcs[] = {
|
2021-12-23 21:26:53 +01:00
|
|
|
{ .name = "put", .side_effects = true, .n_args = 1, .func = fn_put, },
|
2021-12-26 10:55:31 +01:00
|
|
|
{ .name = "putln", .side_effects = true, .n_args = 1, .func = fn_putln, },
|
2021-12-22 16:09:52 +01:00
|
|
|
{ .name = "int", .side_effects = false, .n_args = 1, .func = fn_int, },
|
|
|
|
{ .name = "float", .side_effects = false, .n_args = 1, .func = fn_float, },
|
|
|
|
{ .name = "pow", .side_effects = false, .n_args = 2, .func = fn_pow, },
|
2021-12-25 14:06:20 +01:00
|
|
|
{ .name = "sleep", .side_effects = true, .n_args = 1, .func = fn_sleep, },
|
2021-12-26 11:36:52 +01:00
|
|
|
{ .name = "getln", .side_effects = true, .n_args = 0, .func = fn_getln, },
|
2021-12-22 16:09:52 +01:00
|
|
|
};
|
|
|
|
IRToks ir = parse(&tokens, funcs, sizeof(funcs) / sizeof(funcs[0]));
|
2021-12-21 01:18:22 +01:00
|
|
|
if (err) {
|
|
|
|
irtoks_term(&ir);
|
|
|
|
toklist_term(&tokens);
|
2021-12-25 12:16:06 +01:00
|
|
|
pool_term(static_vars);
|
2021-12-21 01:18:22 +01:00
|
|
|
fprintf(stderr, C_IRED "Parser error" C_RESET " in " C_CYAN "%s" C_RESET ":%zu:%zu: %s\n", filename, err_ln, err_col, errbuf);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
toklist_term(&tokens);
|
2021-12-25 23:01:18 +01:00
|
|
|
optimize_ir(&ir);
|
2021-12-21 01:18:22 +01:00
|
|
|
if (opt_emit_ir)
|
2021-12-22 16:09:52 +01:00
|
|
|
print_ir(&ir, funcs);
|
2021-12-21 01:18:22 +01:00
|
|
|
/* run the IR */
|
2021-12-22 17:23:24 +01:00
|
|
|
if (!opt_dry) {
|
|
|
|
run(&ir, funcs);
|
|
|
|
if (err) {
|
|
|
|
irtoks_term(&ir);
|
2021-12-25 12:16:06 +01:00
|
|
|
pool_term(static_vars);
|
2021-12-22 17:23:24 +01:00
|
|
|
fprintf(stderr, C_IRED "Runtime error" C_RESET " in " C_CYAN "%s" C_RESET ":%zu:%zu: %s\n", filename, err_ln, err_col, errbuf);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
2021-12-21 01:18:22 +01:00
|
|
|
irtoks_term(&ir);
|
2021-12-25 12:16:06 +01:00
|
|
|
pool_term(static_vars);
|
2021-12-21 01:18:22 +01:00
|
|
|
}
|