Implement first global function "print"
This introduces the globals and moves some previously non-public functions into apfl.h.
This commit is contained in:
parent
a4f7f0f2ff
commit
4f9a33628c
10 changed files with 225 additions and 17 deletions
|
|
@ -13,6 +13,7 @@ libapfl_a_SOURCES += expr.c
|
|||
libapfl_a_SOURCES += format.c
|
||||
libapfl_a_SOURCES += gc.c
|
||||
libapfl_a_SOURCES += hashmap.c
|
||||
libapfl_a_SOURCES += globals.c
|
||||
libapfl_a_SOURCES += messages.c
|
||||
libapfl_a_SOURCES += parser.c
|
||||
libapfl_a_SOURCES += position.c
|
||||
|
|
@ -31,6 +32,7 @@ apfl_internal_headers += compile.h
|
|||
apfl_internal_headers += context.h
|
||||
apfl_internal_headers += format.h
|
||||
apfl_internal_headers += gc.h
|
||||
apfl_internal_headers += globals.h
|
||||
apfl_internal_headers += hashmap.h
|
||||
apfl_internal_headers += resizable.h
|
||||
apfl_internal_headers += scope.h
|
||||
|
|
|
|||
28
src/apfl.h
28
src/apfl.h
|
|
@ -123,6 +123,15 @@ struct apfl_format_writer apfl_format_file_writer(FILE *f);
|
|||
|
||||
struct apfl_format_writer apfl_format_string_writer(struct apfl_string_builder *sb);
|
||||
|
||||
bool apfl_format_put_string_view(struct apfl_format_writer, struct apfl_string_view);
|
||||
#define apfl_format_put_string(w, s) apfl_format_put_string_view((w), apfl_string_view_from(s))
|
||||
bool apfl_format_put_int(struct apfl_format_writer, int);
|
||||
bool apfl_format_put_char(struct apfl_format_writer, char);
|
||||
bool apfl_format_put_hexbyte(struct apfl_format_writer, unsigned char);
|
||||
bool apfl_format_put_pos(struct apfl_format_writer, struct apfl_position);
|
||||
bool apfl_format_put_indent(struct apfl_format_writer, unsigned);
|
||||
bool apfl_format_put_number(struct apfl_format_writer, apfl_number);
|
||||
|
||||
// Tokens
|
||||
|
||||
enum apfl_token_type {
|
||||
|
|
@ -628,6 +637,8 @@ typedef void (*apfl_cfunc)(apfl_ctx);
|
|||
// Get the type of a value on the stack
|
||||
enum apfl_value_type apfl_get_type(apfl_ctx, apfl_stackidx);
|
||||
|
||||
// Drop a value from the stack
|
||||
void apfl_drop(apfl_ctx, apfl_stackidx);
|
||||
// Push a nil onto the stack
|
||||
void apfl_push_nil(apfl_ctx);
|
||||
// Push a boolean onto the stack
|
||||
|
|
@ -652,6 +663,11 @@ void apfl_dict_set(apfl_ctx, apfl_stackidx dict, apfl_stackidx k, apfl_stackidx
|
|||
void apfl_get_member(apfl_ctx, apfl_stackidx container, apfl_stackidx k);
|
||||
// Get a value from a list and push it on the stack. The list will stay on the stack.
|
||||
void apfl_get_list_member_by_index(apfl_ctx, apfl_stackidx list, size_t index_in_list);
|
||||
// Get the length of a string/list/dict. The value stays on the stack,
|
||||
size_t apfl_len(apfl_ctx, apfl_stackidx);
|
||||
// Get a string view into a APFL_VALUE_STRING value. The value stays on the stack.
|
||||
// The returned string view is only guaranteed to be valid, as long as the value is on the stack.
|
||||
struct apfl_string_view apfl_get_string(apfl_ctx, apfl_stackidx);
|
||||
|
||||
// Push a C function onto the stack with nslots slots (initialized to nil)
|
||||
void apfl_push_cfunc(apfl_ctx, apfl_cfunc, size_t nslots);
|
||||
|
|
@ -669,6 +685,17 @@ enum apfl_result apfl_call_protected(apfl_ctx, apfl_stackidx func, apfl_stackidx
|
|||
|
||||
bool apfl_debug_print_val(apfl_ctx, apfl_stackidx, struct apfl_format_writer);
|
||||
|
||||
// Raise an error with a value from the stack as the message.
|
||||
APFL_NORETURN void apfl_raise_error_with_type(apfl_ctx, apfl_stackidx, enum apfl_result type);
|
||||
// Raise an error with a constant string as the message.
|
||||
APFL_NORETURN void apfl_raise_const_error(apfl_ctx, enum apfl_result type, const char *message);
|
||||
// Raise a memory allocation error.
|
||||
APFL_NORETURN void apfl_raise_alloc_error(apfl_ctx);
|
||||
// Raise an error for invalid stack indices.
|
||||
APFL_NORETURN void apfl_raise_invalid_stackidx(apfl_ctx);
|
||||
// Raise an error with a message derived from an struct apfl_error.
|
||||
APFL_NORETURN void apfl_raise_error_object(apfl_ctx, struct apfl_error);
|
||||
|
||||
struct apfl_messages {
|
||||
const char *invalid_stack_index;
|
||||
const char *could_not_alloc_mem;
|
||||
|
|
@ -686,6 +713,7 @@ struct apfl_messages {
|
|||
const char *invalid_slotidx;
|
||||
const char *not_a_function;
|
||||
const char *wrong_type;
|
||||
const char *io_error;
|
||||
};
|
||||
extern const struct apfl_messages apfl_messages;
|
||||
|
||||
|
|
|
|||
117
src/context.c
117
src/context.c
|
|
@ -7,6 +7,7 @@
|
|||
#include "alloc.h"
|
||||
#include "context.h"
|
||||
#include "gc.h"
|
||||
#include "globals.h"
|
||||
#include "hashmap.h"
|
||||
#include "resizable.h"
|
||||
#include "strings.h"
|
||||
|
|
@ -14,6 +15,7 @@
|
|||
|
||||
static bool try_push_const_string(apfl_ctx ctx, const char *string);
|
||||
static bool current_stack_move_to_top(apfl_ctx, apfl_stackidx);
|
||||
static struct apfl_string *new_copied_string(struct gc *, struct apfl_string_view);
|
||||
|
||||
APFL_NORETURN static void
|
||||
panic(apfl_ctx ctx, bool with_error_on_stack, enum apfl_result result)
|
||||
|
|
@ -384,6 +386,14 @@ apfl_stack_drop(apfl_ctx ctx, apfl_stackidx index)
|
|||
return apfl_stack_pop(ctx, &value, index);
|
||||
}
|
||||
|
||||
void
|
||||
apfl_drop(apfl_ctx ctx, apfl_stackidx index)
|
||||
{
|
||||
if (!apfl_stack_drop(ctx, index)) {
|
||||
apfl_raise_invalid_stackidx(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
current_stack_move_to_top(apfl_ctx ctx, apfl_stackidx index)
|
||||
{
|
||||
|
|
@ -521,6 +531,56 @@ iterative_runners_list_new(void)
|
|||
};
|
||||
}
|
||||
|
||||
static bool
|
||||
init_globals(apfl_ctx ctx)
|
||||
{
|
||||
struct gc *gc = &ctx->gc;
|
||||
|
||||
size_t tmproots = apfl_gc_tmproots_begin(gc);
|
||||
|
||||
for (
|
||||
const struct global_def *global = apfl_globals();
|
||||
global->name != NULL && global->func != NULL;
|
||||
global++
|
||||
) {
|
||||
struct cfunction *cfunc = apfl_cfunc_new(gc, global->func, 0);
|
||||
if (cfunc == NULL) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (!apfl_gc_tmproot_add(gc, GC_OBJECT_FROM(cfunc, GC_TYPE_CFUNC))) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
// TODO: It's silly that we need to copy the name into a new gc string.
|
||||
// It should be possible for the scope to also have a const string
|
||||
// as a name.
|
||||
struct apfl_string *name = new_copied_string(gc, apfl_string_view_from(global->name));
|
||||
if (name == NULL) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (!apfl_gc_tmproot_add(gc, GC_OBJECT_FROM(name, GC_TYPE_STRING))) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (!apfl_scope_set(gc, ctx->globals, name, (struct apfl_value) {
|
||||
.type = VALUE_CFUNC,
|
||||
.cfunc = cfunc,
|
||||
})) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
apfl_gc_tmproots_restore(gc, tmproots);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
error:
|
||||
apfl_gc_tmproots_restore(gc, tmproots);
|
||||
return false;
|
||||
}
|
||||
|
||||
apfl_ctx
|
||||
apfl_ctx_new(struct apfl_allocator base_allocator)
|
||||
{
|
||||
|
|
@ -544,6 +604,10 @@ apfl_ctx_new(struct apfl_allocator base_allocator)
|
|||
|
||||
ctx->iterative_runners = iterative_runners_list_new();
|
||||
|
||||
if (!init_globals(ctx)) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
return ctx;
|
||||
|
||||
error:
|
||||
|
|
@ -912,6 +976,59 @@ apfl_get_list_member_by_index(
|
|||
return;
|
||||
}
|
||||
|
||||
size_t
|
||||
apfl_len(apfl_ctx ctx, apfl_stackidx index)
|
||||
{
|
||||
struct apfl_value value = apfl_stack_must_get(ctx, index);
|
||||
switch (value.type) {
|
||||
case VALUE_NIL:
|
||||
case VALUE_BOOLEAN:
|
||||
case VALUE_NUMBER:
|
||||
case VALUE_FUNC:
|
||||
case VALUE_CFUNC:
|
||||
case VALUE_USERDATA:
|
||||
apfl_raise_const_error(ctx, APFL_RESULT_ERR, apfl_messages.wrong_type);
|
||||
return 0;
|
||||
case VALUE_STRING:
|
||||
return value.string->len;
|
||||
case VALUE_CONST_STRING:
|
||||
return value.const_string.len;
|
||||
case VALUE_LIST:
|
||||
return apfl_list_len(value.list);
|
||||
case VALUE_DICT:
|
||||
return apfl_dict_len(value.dict);
|
||||
}
|
||||
|
||||
assert(false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct apfl_string_view
|
||||
apfl_get_string(apfl_ctx ctx, apfl_stackidx index)
|
||||
{
|
||||
struct apfl_value value = apfl_stack_must_get(ctx, index);
|
||||
switch (value.type) {
|
||||
case VALUE_NIL:
|
||||
case VALUE_BOOLEAN:
|
||||
case VALUE_NUMBER:
|
||||
case VALUE_FUNC:
|
||||
case VALUE_CFUNC:
|
||||
case VALUE_USERDATA:
|
||||
case VALUE_LIST:
|
||||
case VALUE_DICT:
|
||||
apfl_raise_const_error(ctx, APFL_RESULT_ERR, apfl_messages.wrong_type);
|
||||
goto error;
|
||||
case VALUE_STRING:
|
||||
return apfl_string_view_from(*value.string);
|
||||
case VALUE_CONST_STRING:
|
||||
return value.const_string;
|
||||
}
|
||||
|
||||
error:
|
||||
assert(false);
|
||||
return (struct apfl_string_view) {.bytes = NULL, .len = 0};
|
||||
}
|
||||
|
||||
static struct scope *
|
||||
closure_scope_for_func_inner(apfl_ctx ctx)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -86,12 +86,6 @@ struct apfl_ctx_data {
|
|||
struct iterative_runners_list iterative_runners;
|
||||
};
|
||||
|
||||
APFL_NORETURN void apfl_raise_error_with_type(apfl_ctx, apfl_stackidx, enum apfl_result type);
|
||||
APFL_NORETURN void apfl_raise_const_error(apfl_ctx, enum apfl_result type, const char *message);
|
||||
APFL_NORETURN void apfl_raise_alloc_error(apfl_ctx);
|
||||
APFL_NORETURN void apfl_raise_invalid_stackidx(apfl_ctx);
|
||||
APFL_NORETURN void apfl_raise_error_object(apfl_ctx, struct apfl_error);
|
||||
|
||||
void apfl_call_stack_entry_deinit(struct apfl_allocator, struct call_stack_entry *);
|
||||
|
||||
struct stack apfl_stack_new(void);
|
||||
|
|
|
|||
11
src/format.h
11
src/format.h
|
|
@ -6,22 +6,11 @@ extern "C" {
|
|||
#endif
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "apfl.h"
|
||||
|
||||
#define FMT_TRY(x) do { if (!(x)) return false; } while (0)
|
||||
|
||||
bool apfl_format_put_string_view(struct apfl_format_writer, struct apfl_string_view);
|
||||
#define apfl_format_put_string(w, s) apfl_format_put_string_view((w), apfl_string_view_from(s))
|
||||
bool apfl_format_put_int(struct apfl_format_writer, int);
|
||||
bool apfl_format_put_char(struct apfl_format_writer, char);
|
||||
bool apfl_format_put_hexbyte(struct apfl_format_writer, unsigned char);
|
||||
bool apfl_format_put_pos(struct apfl_format_writer, struct apfl_position);
|
||||
bool apfl_format_put_indent(struct apfl_format_writer, unsigned);
|
||||
bool apfl_format_put_number(struct apfl_format_writer, apfl_number);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
49
src/globals.c
Normal file
49
src/globals.c
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
#include <stdio.h>
|
||||
|
||||
#include "apfl.h"
|
||||
|
||||
#include "context.h"
|
||||
#include "globals.h"
|
||||
#include "scope.h"
|
||||
|
||||
#define TRY_FORMAT(ctx, x) \
|
||||
do { \
|
||||
if (!(x)) { \
|
||||
apfl_raise_const_error((ctx), APFL_RESULT_ERR, apfl_messages.io_error); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
static void
|
||||
print(apfl_ctx ctx)
|
||||
{
|
||||
struct apfl_format_writer w = apfl_format_file_writer(stdout);
|
||||
|
||||
size_t len = apfl_len(ctx, 0);
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
if (i > 0) {
|
||||
TRY_FORMAT(ctx, apfl_format_put_string(w, " "));
|
||||
}
|
||||
|
||||
apfl_get_list_member_by_index(ctx, 0, i);
|
||||
struct apfl_string_view sv = apfl_get_string(ctx, -1);
|
||||
TRY_FORMAT(ctx, apfl_format_put_string(w, sv));
|
||||
apfl_drop(ctx, -1);
|
||||
}
|
||||
|
||||
if (len > 0) {
|
||||
TRY_FORMAT(ctx, apfl_format_put_string(w, "\n"));
|
||||
}
|
||||
|
||||
apfl_push_nil(ctx);
|
||||
}
|
||||
|
||||
static const struct global_def globals[] = {
|
||||
{"print", print},
|
||||
{NULL, NULL},
|
||||
};
|
||||
|
||||
const struct global_def *
|
||||
apfl_globals(void)
|
||||
{
|
||||
return globals;
|
||||
}
|
||||
21
src/globals.h
Normal file
21
src/globals.h
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
#ifndef APFL_GLOBALS_H
|
||||
#define APFL_GLOBALS_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "apfl.h"
|
||||
|
||||
struct global_def {
|
||||
const char *name;
|
||||
apfl_cfunc func;
|
||||
};
|
||||
|
||||
const struct global_def *apfl_globals(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
@ -17,4 +17,5 @@ const struct apfl_messages apfl_messages = {
|
|||
.invalid_slotidx = "Invalid slot index",
|
||||
.not_a_function = "Not a function",
|
||||
.wrong_type = "Wrong type",
|
||||
.io_error = "I/O error",
|
||||
};
|
||||
|
|
|
|||
|
|
@ -492,6 +492,12 @@ apfl_dict_set_raw(
|
|||
return apfl_hashmap_set(&dict->map, &k, &v);
|
||||
}
|
||||
|
||||
size_t
|
||||
apfl_dict_len(struct dict_header *dict)
|
||||
{
|
||||
return apfl_hashmap_count(dict->map);
|
||||
}
|
||||
|
||||
static bool
|
||||
list_get_item(struct list_header *list, size_t index, struct apfl_value *out)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -113,6 +113,7 @@ bool apfl_dict_set_raw(
|
|||
struct apfl_value k,
|
||||
struct apfl_value v
|
||||
);
|
||||
size_t apfl_dict_len(struct dict_header *);
|
||||
void apfl_dict_deinit(struct dict_header *);
|
||||
|
||||
struct function *apfl_func_new(struct gc *, struct instruction_list *, struct scope *);
|
||||
|
|
|
|||
Loading…
Reference in a new issue