mirror of https://github.com/janet-lang/janet
237 lines
7.1 KiB
C
237 lines
7.1 KiB
C
/*
|
|
* Copyright (c) 2017 Calvin Rose
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to
|
|
* deal in the Software without restriction, including without limitation the
|
|
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
* sell copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
* IN THE SOFTWARE.
|
|
*/
|
|
|
|
#include <dst/dst.h>
|
|
#include <dst/dstcompile.h>
|
|
#include <dst/dstparse.h>
|
|
|
|
#define CHUNKSIZE 1024
|
|
|
|
/* read input for a repl */
|
|
static int replread(DstContext *c, enum DstParserStatus status) {
|
|
if (status == DST_PARSE_PENDING)
|
|
printf(">> ");
|
|
else
|
|
printf("> ");
|
|
for (;;) {
|
|
int x = fgetc(stdin);
|
|
if (x == EOF) {
|
|
dst_buffer_push_u8(&c->buffer, '\n');
|
|
printf("\n");
|
|
break;
|
|
}
|
|
dst_buffer_push_u8(&c->buffer, x);
|
|
if (x == '\n') break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int bytesread(DstContext *c, enum DstParserStatus status) {
|
|
const uint8_t *src = (const uint8_t *)(c->user);
|
|
(void) status;
|
|
int32_t n = CHUNKSIZE;
|
|
DstBuffer *b = &c->buffer;
|
|
b->count = 0;
|
|
if (!c->bufsize) {
|
|
dst_buffer_push_u8(b, '\n');
|
|
return 1;
|
|
}
|
|
if (c->bufsize < n) n = c->bufsize;
|
|
dst_buffer_push_bytes(b, src, n);
|
|
c->bufsize -= n;
|
|
c->user = (void *)(src + n);
|
|
return 0;
|
|
}
|
|
|
|
/* Chunk readers */
|
|
static void filedeinit(DstContext *c) {
|
|
fclose((FILE *) (c->user));
|
|
}
|
|
|
|
/* Read chunk from file */
|
|
static int32_t fileread(DstContext *c, enum DstParserStatus status) {
|
|
size_t nread;
|
|
FILE *f = (FILE *) c->user;
|
|
(void) status;
|
|
c->buffer.count = 0;
|
|
dst_buffer_ensure(&c->buffer, CHUNKSIZE);
|
|
nread = fread(c->buffer.data, 1, CHUNKSIZE, f);
|
|
if (nread != CHUNKSIZE && ferror(f)) {
|
|
return -1;
|
|
}
|
|
c->buffer.count = (int32_t) nread;
|
|
return 0;
|
|
}
|
|
|
|
/* Output for a repl */
|
|
static void replonvalue(DstContext *c, Dst value) {
|
|
(void) c;
|
|
dst_puts(dst_formatc("%v\n", value));
|
|
dst_env_def(c->env, "_", value);
|
|
}
|
|
|
|
/* Handle errors on repl */
|
|
static void simpleerror(DstContext *c, enum DstContextErrorType type, Dst err, size_t start, size_t end) {
|
|
const char *errtype = "";
|
|
(void) c;
|
|
(void) start;
|
|
(void) end;
|
|
switch (type) {
|
|
case DST_CONTEXT_ERROR_PARSE:
|
|
errtype = "parse";
|
|
break;
|
|
case DST_CONTEXT_ERROR_RUNTIME:
|
|
errtype = "runtime";
|
|
break;
|
|
case DST_CONTEXT_ERROR_COMPILE:
|
|
errtype = "compile";
|
|
break;
|
|
}
|
|
dst_puts(dst_formatc("%s error: %s\n", errtype, dst_to_string(err)));
|
|
}
|
|
|
|
void dst_context_init(DstContext *c, DstTable *env) {
|
|
dst_buffer_init(&c->buffer, CHUNKSIZE);
|
|
c->env = env;
|
|
dst_gcroot(dst_wrap_table(env));
|
|
c->index = 0;
|
|
c->read_chunk = NULL;
|
|
c->on_error = NULL;
|
|
c->on_value = NULL;
|
|
c->deinit = NULL;
|
|
}
|
|
|
|
void dst_context_deinit(DstContext *c) {
|
|
dst_buffer_deinit(&c->buffer);
|
|
if (c->deinit) c->deinit(c);
|
|
dst_gcunroot(dst_wrap_table(c->env));
|
|
}
|
|
|
|
int dst_context_repl(DstContext *c, DstTable *env) {
|
|
dst_context_init(c, env);
|
|
c->user = NULL;
|
|
dst_env_def(c->env, "_", dst_wrap_nil());
|
|
c->read_chunk = replread;
|
|
c->on_error = simpleerror;
|
|
c->on_value = replonvalue;
|
|
return 0;
|
|
}
|
|
|
|
int dst_context_file(DstContext *c, DstTable *env, const char *path) {
|
|
FILE *f = fopen(path, "r");
|
|
if (!f) {
|
|
return -1;
|
|
}
|
|
dst_context_init(c, env);
|
|
c->user = f;
|
|
c->read_chunk = fileread;
|
|
c->on_error = simpleerror;
|
|
c->deinit = filedeinit;
|
|
return 0;
|
|
}
|
|
|
|
int dst_context_bytes(DstContext *c, DstTable *env, const uint8_t *bytes, int32_t len) {
|
|
dst_context_init(c, env);
|
|
c->user = (void *) bytes;
|
|
c->bufsize = len;
|
|
c->read_chunk = bytesread;
|
|
return 0;
|
|
}
|
|
|
|
/* Do something on an error. Return flags to or with current flags. */
|
|
static int doerror(
|
|
DstContext *c,
|
|
enum DstContextErrorType type,
|
|
Dst err,
|
|
int32_t bstart,
|
|
int32_t bend) {
|
|
if (c->on_error) {
|
|
c->on_error(c, type,
|
|
err,
|
|
bstart,
|
|
bend);
|
|
}
|
|
return 1 << type;
|
|
}
|
|
|
|
/* Start a context */
|
|
int dst_context_run(DstContext *c, int flags) {
|
|
int done = 0;
|
|
int errflags = 0;
|
|
DstParser parser;
|
|
enum DstParserStatus status;
|
|
dst_parser_init(&parser, flags);
|
|
while (!done) {
|
|
int bufferdone = 0;
|
|
while (!bufferdone) {
|
|
status = dst_parser_status(&parser);
|
|
switch (status) {
|
|
case DST_PARSE_FULL:
|
|
{
|
|
DstCompileResult cres = dst_compile(dst_parser_produce(&parser), c->env, flags);
|
|
if (cres.status == DST_COMPILE_OK) {
|
|
DstFunction *f = dst_function(cres.funcdef, NULL);
|
|
Dst ret;
|
|
if (dst_run(dst_wrap_function(f), &ret)) {
|
|
/* Get location from stacktrace? */
|
|
errflags |= doerror(c, DST_CONTEXT_ERROR_RUNTIME, ret, -1, -1);
|
|
} else {
|
|
if (c->on_value) {
|
|
c->on_value(c, ret);
|
|
}
|
|
}
|
|
} else {
|
|
errflags |= doerror(c, DST_CONTEXT_ERROR_COMPILE,
|
|
dst_wrap_string(cres.error),
|
|
cres.error_start,
|
|
cres.error_end);
|
|
}
|
|
}
|
|
break;
|
|
case DST_PARSE_ERROR:
|
|
doerror(c, DST_CONTEXT_ERROR_PARSE,
|
|
dst_cstringv(dst_parser_error(&parser)),
|
|
parser.index,
|
|
parser.index);
|
|
break;
|
|
case DST_PARSE_PENDING:
|
|
case DST_PARSE_ROOT:
|
|
if (c->index >= c->buffer.count) {
|
|
bufferdone = 1;
|
|
break;
|
|
}
|
|
dst_parser_consume(&parser, c->buffer.data[c->index++]);
|
|
break;
|
|
}
|
|
}
|
|
/* Refill the buffer */
|
|
c->buffer.count = 0;
|
|
c->index = 0;
|
|
if (c->read_chunk(c, status) || c->buffer.count == 0) {
|
|
done = 1;
|
|
}
|
|
}
|
|
|
|
dst_parser_deinit(&parser);
|
|
return errflags;
|
|
}
|