feat: part of the story api exposed via c bindings
This commit is contained in:
parent
a065e5bf46
commit
3afbbb6ec2
6 changed files with 748 additions and 16 deletions
330
examples/ink-c-driver.c
Normal file
330
examples/ink-c-driver.c
Normal file
|
|
@ -0,0 +1,330 @@
|
|||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include <ink.h>
|
||||
|
||||
struct option {
|
||||
char *name;
|
||||
int id;
|
||||
bool has_args;
|
||||
};
|
||||
|
||||
struct ink_source {
|
||||
uint8_t *bytes;
|
||||
size_t length;
|
||||
};
|
||||
|
||||
enum {
|
||||
OPTION_UNKNOWN = -2,
|
||||
OPTION_OPERAND = -3
|
||||
};
|
||||
|
||||
enum {
|
||||
OPT_COLORS = 1000,
|
||||
OPT_COMPILE_ONLY,
|
||||
OPT_USE_STDIN,
|
||||
OPT_DUMP_AST,
|
||||
OPT_DUMP_IR,
|
||||
OPT_DUMP_STORY,
|
||||
OPT_VM_TRACING,
|
||||
OPT_HELP,
|
||||
};
|
||||
|
||||
static const struct option cli_options[] = {
|
||||
{"--colors", OPT_COLORS, false},
|
||||
{"--compile-only", OPT_COMPILE_ONLY, false},
|
||||
//{"--dump-ast", OPT_DUMP_AST, false},
|
||||
//{"--dump-ir", OPT_DUMP_AST, false},
|
||||
//{"--dump-story", OPT_DUMP_STORY, false},
|
||||
//{"--trace", OPT_VM_TRACING, false},
|
||||
{"--stdin", OPT_USE_STDIN, false},
|
||||
{"--help", OPT_HELP, false},
|
||||
{"-h", OPT_HELP, false},
|
||||
{0},
|
||||
};
|
||||
|
||||
static const char *usage_msg =
|
||||
"Usage: %s [OPTION]... [FILE]\n"
|
||||
"Load and execute an Ink story.\n\n"
|
||||
" -h, --help Print this message\n"
|
||||
" --colors Enable color output\n"
|
||||
" --compile-only Compile the story without executing\n"
|
||||
" --stdin Read source file from standard input\n"
|
||||
"\n";
|
||||
|
||||
static const struct option *_g_option_opts;
|
||||
static char *_g_arg_ptr;
|
||||
static char **_g_option_argv;
|
||||
static char *option_operand;
|
||||
static char *option_unknown_opt;
|
||||
|
||||
static void print_usage(const char *name)
|
||||
{
|
||||
fprintf(stderr, usage_msg, name);
|
||||
}
|
||||
|
||||
static void option_setopts(const struct option *opts, char **argv)
|
||||
{
|
||||
_g_option_argv = argv;
|
||||
_g_option_opts = opts;
|
||||
}
|
||||
|
||||
static int option_nextopt(void)
|
||||
{
|
||||
if (*_g_option_argv != NULL) {
|
||||
++_g_option_argv;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
||||
char **optstr_p = _g_option_argv;
|
||||
if (*optstr_p == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct option *p = _g_option_opts;
|
||||
while (p->id != 0) {
|
||||
if (!strcmp(*optstr_p, p->name)) {
|
||||
if (p->has_args) {
|
||||
_g_arg_ptr = optstr_p[1];
|
||||
++_g_option_argv;
|
||||
}
|
||||
|
||||
return p->id;
|
||||
} else if (**optstr_p != '-') {
|
||||
option_operand = *optstr_p;
|
||||
return OPTION_OPERAND;
|
||||
}
|
||||
|
||||
++p;
|
||||
}
|
||||
|
||||
option_unknown_opt = *optstr_p;
|
||||
return OPTION_UNKNOWN;
|
||||
}
|
||||
|
||||
static char *option_nextarg(void)
|
||||
{
|
||||
char *arg = _g_arg_ptr;
|
||||
char *arg_p = arg;
|
||||
|
||||
while (*arg_p != '\0' && *arg_p != ',') {
|
||||
++arg_p;
|
||||
}
|
||||
if (*arg_p == '\0') {
|
||||
_g_arg_ptr = arg_p;
|
||||
return arg;
|
||||
}
|
||||
|
||||
_g_arg_ptr = arg_p + 1;
|
||||
*arg_p = '\0';
|
||||
return arg;
|
||||
}
|
||||
|
||||
#define INK_SOURCE_BUF_MAX 1024
|
||||
|
||||
static const char *INK_FILE_EXT = ".ink";
|
||||
static const size_t INK_FILE_EXT_LENGTH = 4;
|
||||
|
||||
static int ink_read_file(const char *file_path, uint8_t **bytes, size_t *length)
|
||||
{
|
||||
size_t sz = 0, nr = 0;
|
||||
uint8_t *b = NULL;
|
||||
FILE *const fp = fopen(file_path, "rb");
|
||||
|
||||
if (!fp) {
|
||||
fprintf(stderr, "Could not read file '%s'\n", file_path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
fseek(fp, 0u, SEEK_END);
|
||||
sz = (size_t)ftell(fp);
|
||||
fseek(fp, 0u, SEEK_SET);
|
||||
|
||||
b = malloc(sz + 1);
|
||||
if (!b) {
|
||||
fclose(fp);
|
||||
return -1;
|
||||
}
|
||||
|
||||
nr = fread(b, 1u, sz, fp);
|
||||
if (nr < sz) {
|
||||
fprintf(stderr, "Could not read file '%s'.\n", file_path);
|
||||
fclose(fp);
|
||||
free(b);
|
||||
return -1;
|
||||
}
|
||||
|
||||
b[nr] = '\0';
|
||||
*bytes = b;
|
||||
*length = sz;
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ink_source_free(struct ink_source *s)
|
||||
{
|
||||
free(s->bytes);
|
||||
s->bytes = NULL;
|
||||
s->length = 0;
|
||||
}
|
||||
|
||||
static int ink_source_load_stdin(struct ink_source *s)
|
||||
{
|
||||
uint8_t *tmp;
|
||||
char b[INK_SOURCE_BUF_MAX];
|
||||
|
||||
s->bytes = NULL;
|
||||
s->length = 0;
|
||||
|
||||
while (fgets(b, INK_SOURCE_BUF_MAX, stdin)) {
|
||||
const size_t len = s->length;
|
||||
const size_t buflen = strlen(b);
|
||||
|
||||
tmp = realloc(s->bytes, len + buflen + 1);
|
||||
if (!tmp) {
|
||||
ink_source_free(s);
|
||||
return -1;
|
||||
}
|
||||
|
||||
s->bytes = tmp;
|
||||
memcpy(s->bytes + len, b, buflen);
|
||||
s->length += buflen;
|
||||
s->bytes[s->length] = '\0';
|
||||
}
|
||||
if (ferror(stdin)) {
|
||||
ink_source_free(s);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ink_source_load(const char *file_path, struct ink_source *s)
|
||||
{
|
||||
const char *ext;
|
||||
const size_t namelen = strlen(file_path);
|
||||
|
||||
s->bytes = NULL;
|
||||
s->length = 0;
|
||||
|
||||
if (namelen < INK_FILE_EXT_LENGTH) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ext = file_path + namelen - INK_FILE_EXT_LENGTH;
|
||||
if (!(strncmp(ext, INK_FILE_EXT, INK_FILE_EXT_LENGTH) == 0)) {
|
||||
return -1;
|
||||
}
|
||||
return ink_read_file(file_path, &s->bytes, &s->length);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
bool compile_only = false;
|
||||
bool use_stdin = false;
|
||||
int opt = 0;
|
||||
int rc = -1;
|
||||
int flags = 0;
|
||||
const char *filename = NULL;
|
||||
struct ink_source source;
|
||||
struct ink_story *story = NULL;
|
||||
|
||||
option_setopts(cli_options, argv);
|
||||
|
||||
while ((opt = option_nextopt())) {
|
||||
switch (opt) {
|
||||
case OPT_COLORS:
|
||||
flags |= INK_F_USE_COLOR;
|
||||
break;
|
||||
case OPT_DUMP_AST:
|
||||
flags |= INK_F_DUMP_AST;
|
||||
break;
|
||||
case OPT_DUMP_IR:
|
||||
flags |= INK_F_DUMP_IR;
|
||||
break;
|
||||
case OPT_DUMP_STORY:
|
||||
flags |= INK_F_DUMP_CODE;
|
||||
break;
|
||||
case OPT_COMPILE_ONLY:
|
||||
compile_only = true;
|
||||
break;
|
||||
case OPT_USE_STDIN:
|
||||
use_stdin = true;
|
||||
break;
|
||||
case OPTION_UNKNOWN:
|
||||
fprintf(stderr, "Unrecognised option %s.\n\n", option_unknown_opt);
|
||||
print_usage(argv[0]);
|
||||
return EXIT_FAILURE;
|
||||
case OPT_HELP:
|
||||
print_usage(argv[0]);
|
||||
return EXIT_SUCCESS;
|
||||
case OPTION_OPERAND:
|
||||
filename = option_operand;
|
||||
break;
|
||||
default:
|
||||
print_usage(argv[0]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
if (filename == NULL || *filename == '\0') {
|
||||
if (use_stdin) {
|
||||
rc = ink_source_load_stdin(&source);
|
||||
filename = "<STDIN>";
|
||||
} else {
|
||||
print_usage(argv[0]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
} else {
|
||||
rc = ink_source_load(filename, &source);
|
||||
}
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "Error while loading source file.\n");
|
||||
return rc;
|
||||
}
|
||||
|
||||
story = ink_open();
|
||||
if (!story) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
const struct ink_story_options opts = {
|
||||
.source_bytes = source.bytes,
|
||||
.source_length = source.length,
|
||||
.filename = (uint8_t *)filename,
|
||||
.filename_length = strlen(filename),
|
||||
.flags = flags,
|
||||
};
|
||||
|
||||
rc = ink_load_story_options(story, &opts);
|
||||
if (rc < 0) {
|
||||
goto out;
|
||||
}
|
||||
if (!compile_only) {
|
||||
while (ink_story_can_continue(story)) {
|
||||
const char *line = NULL;
|
||||
size_t linelen = 0;
|
||||
size_t choice_index = 0;
|
||||
|
||||
while (ink_story_continue(story, &line, &linelen)) {
|
||||
if (line) {
|
||||
printf("%.*s\n", (int)linelen, line);
|
||||
}
|
||||
}
|
||||
while (ink_story_choice_next(story, &line, &linelen)) {
|
||||
printf("%zu: %.*s\n", ++choice_index, (int)linelen, line);
|
||||
}
|
||||
if (choice_index > 0) {
|
||||
printf("> ");
|
||||
scanf("%zu", &choice_index);
|
||||
ink_story_choose(story, choice_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
out:
|
||||
ink_close(story);
|
||||
ink_source_free(&source);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue