This implements the last remaining feature of the language syntax! :) The implementation of this was delightfully simple, since all the heavy lifting is done by already implemented functions.
756 lines
26 KiB
C
756 lines
26 KiB
C
#ifndef APFL_H
|
|
#define APFL_H
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
#include <stdbool.h>
|
|
#include <stddef.h>
|
|
#include <stdio.h>
|
|
|
|
#ifdef __GNUC__
|
|
# define APFL_NORETURN __attribute__((noreturn))
|
|
#else
|
|
# define APFL_NORETURN
|
|
#endif
|
|
|
|
// Allocator
|
|
|
|
// apfl_allocator_cb is called to (re)allocate and free memory.
|
|
//
|
|
// If called with newsize = 0, the memory of oldsize bytes pointed to by
|
|
// oldptr should be freed. If the NULL-pointer is freed, nothing should
|
|
// happen. A freeing must return the NULL-pointer and is not allowed to
|
|
// fail.
|
|
//
|
|
// If oldptr = NULL and oldsize = 0, memory of size newsize should be
|
|
// allocated and the pointer to it returned. If the allocation fails, NULL
|
|
// should be returned.
|
|
//
|
|
// Reallocation is done by calling with the old pointer, old size and new
|
|
// size. On success the new pointer should be returned and the old pointer
|
|
// (unless they're the same) no longer used. On failure, NULL should be
|
|
// returned and the oldptr should stay valid.
|
|
typedef void * (*apfl_allocator_cb) (void *opaque, void *oldptr, size_t oldsize, size_t newsize);
|
|
|
|
struct apfl_allocator {
|
|
void *opaque;
|
|
apfl_allocator_cb alloc;
|
|
};
|
|
|
|
struct apfl_allocator apfl_allocator_default(void);
|
|
struct apfl_allocator apfl_allocator_wrap_verifying(struct apfl_allocator *wrapped);
|
|
|
|
typedef double apfl_number;
|
|
|
|
struct apfl_position {
|
|
int line;
|
|
int col;
|
|
};
|
|
|
|
bool apfl_position_eq(struct apfl_position, struct apfl_position);
|
|
|
|
// Strings
|
|
|
|
struct apfl_string_view {
|
|
const char *bytes;
|
|
size_t len;
|
|
};
|
|
|
|
struct apfl_string {
|
|
char *bytes;
|
|
size_t len;
|
|
|
|
// TODO: Not sure, if it's a good idea to expose this. We now need the actual
|
|
// size of the underlying allocation though. Maybe we should better
|
|
// shrink the cap to len wen we're done building a string?
|
|
size_t cap;
|
|
};
|
|
|
|
#define APFL_STR_FMT "%.*s"
|
|
#define APFL_STR_FMT_ARGS(s) (int)(s).len,(s).bytes
|
|
|
|
struct apfl_string_view apfl_string_view_from_view(struct apfl_string_view);
|
|
struct apfl_string_view apfl_string_view_from_cstr(char *);
|
|
struct apfl_string_view apfl_string_view_from_const_cstr(const char *);
|
|
struct apfl_string_view apfl_string_view_from_string(struct apfl_string);
|
|
|
|
#define apfl_string_view_from(s) _Generic((s), \
|
|
struct apfl_string: apfl_string_view_from_string, \
|
|
struct apfl_string_view: apfl_string_view_from_view, \
|
|
char *: apfl_string_view_from_cstr, \
|
|
const char *: apfl_string_view_from_const_cstr \
|
|
)(s)
|
|
|
|
int apfl_string_view_cmp(struct apfl_string_view, struct apfl_string_view);
|
|
|
|
#define apfl_string_cmp(a, b) apfl_string_view_cmp(apfl_string_view_from(a), apfl_string_view_from(b))
|
|
#define apfl_string_eq(a, b) (apfl_string_cmp((a), (b)) == 0)
|
|
|
|
struct apfl_string apfl_string_blank(void);
|
|
void apfl_string_deinit(struct apfl_allocator allocator, struct apfl_string *);
|
|
struct apfl_string apfl_string_move(struct apfl_string *src);
|
|
|
|
/**
|
|
* Copies a string from src to dst. dst must point to a blank string.
|
|
* Returns true on success, false otherwise (if the necessary memory could not
|
|
* be allocated).
|
|
*/
|
|
bool apfl_string_copy(struct apfl_allocator allocator, struct apfl_string *dst, struct apfl_string_view src);
|
|
|
|
struct apfl_string_builder {
|
|
struct apfl_allocator allocator;
|
|
char *bytes;
|
|
size_t len;
|
|
size_t cap;
|
|
};
|
|
|
|
struct apfl_string_builder apfl_string_builder_init(struct apfl_allocator allocator);
|
|
void apfl_string_builder_deinit(struct apfl_string_builder *);
|
|
bool apfl_string_builder_append(struct apfl_string_builder *, struct apfl_string_view);
|
|
bool apfl_string_builder_append_byte(struct apfl_string_builder *, char byte);
|
|
bool apfl_string_builder_append_bytes(struct apfl_string_builder *, const char *bytes, size_t len);
|
|
struct apfl_string apfl_string_builder_move_string(struct apfl_string_builder *);
|
|
|
|
#define apfl_string_builder_append_cstr(builder, cstr) (apfl_string_builder_append((builder), apfl_string_view_from_cstr((cstr))))
|
|
|
|
struct apfl_string_view apfl_string_view_offset(struct apfl_string_view sv, size_t off);
|
|
struct apfl_string_view apfl_string_view_trunc(struct apfl_string_view sv, size_t newlen);
|
|
struct apfl_string_view apfl_string_view_substr(struct apfl_string_view sv, size_t off, size_t newlen);
|
|
|
|
ptrdiff_t apfl_string_view_search(struct apfl_string_view haystack, struct apfl_string_view needle);
|
|
|
|
struct apfl_string_view apfl_string_view_ltrim(struct apfl_string_view sv);
|
|
struct apfl_string_view apfl_string_view_rtrim(struct apfl_string_view sv);
|
|
struct apfl_string_view apfl_string_view_trim(struct apfl_string_view sv);
|
|
|
|
struct apfl_format_writer {
|
|
bool (*write)(void *, const char *buf, size_t len);
|
|
void *opaque;
|
|
};
|
|
|
|
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);
|
|
bool apfl_format_put_poiner(struct apfl_format_writer, void *);
|
|
|
|
// Tokens
|
|
|
|
enum apfl_token_type {
|
|
APFL_TOK_LPAREN,
|
|
APFL_TOK_RPAREN,
|
|
APFL_TOK_LBRACKET,
|
|
APFL_TOK_RBRACKET,
|
|
APFL_TOK_LBRACE,
|
|
APFL_TOK_RBRACE,
|
|
APFL_TOK_MAPSTO,
|
|
APFL_TOK_EXPAND,
|
|
APFL_TOK_DOT,
|
|
APFL_TOK_AT,
|
|
APFL_TOK_SEMICOLON,
|
|
APFL_TOK_LINEBREAK,
|
|
APFL_TOK_CONTINUE_LINE,
|
|
APFL_TOK_COMMENT,
|
|
APFL_TOK_COMMA,
|
|
APFL_TOK_QUESTION_MARK,
|
|
APFL_TOK_STRINGIFY,
|
|
APFL_TOK_ASSIGN,
|
|
APFL_TOK_LOCAL_ASSIGN,
|
|
APFL_TOK_NUMBER,
|
|
APFL_TOK_NAME,
|
|
APFL_TOK_STRING,
|
|
};
|
|
|
|
struct apfl_token {
|
|
enum apfl_token_type type;
|
|
struct apfl_position position;
|
|
union {
|
|
struct apfl_string text;
|
|
apfl_number number;
|
|
};
|
|
};
|
|
|
|
void apfl_token_deinit(struct apfl_allocator allocator, struct apfl_token *);
|
|
|
|
const char *apfl_token_type_name(enum apfl_token_type);
|
|
|
|
void apfl_token_print(struct apfl_token, FILE *);
|
|
|
|
// Errors
|
|
|
|
enum apfl_error_type {
|
|
APFL_ERR_MALLOC_FAILED,
|
|
APFL_ERR_INPUT_ERROR,
|
|
APFL_ERR_UNEXPECTED_EOF,
|
|
APFL_ERR_EXPECTED_EQ_AFTER_COLON,
|
|
APFL_ERR_UNEXPECTED_BYTE,
|
|
APFL_ERR_UNEXPECTED_BYTE_IN_NUMBER,
|
|
APFL_ERR_EXPECTED_DIGIT,
|
|
APFL_ERR_EXPECTED_HEX_IN_HEX_ESCAPE,
|
|
APFL_ERR_INVALID_ESCAPE_SEQUENCE,
|
|
APFL_ERR_NO_LINEBREAK_AFTER_CONTINUE_LINE,
|
|
APFL_ERR_UNEXPECTED_TOKEN,
|
|
APFL_ERR_MISMATCHING_CLOSING_BRACKET,
|
|
APFL_ERR_UNEXPECTED_EOF_AFTER_TOKEN,
|
|
APFL_ERR_STATEMENTS_BEFORE_PARAMETERS,
|
|
APFL_ERR_EMPTY_ASSIGNMENT_BEFORE_PARAMETERS,
|
|
APFL_ERR_UNEXPECTED_EXPRESSION,
|
|
APFL_ERR_INVALID_ASSIGNMENT_LHS,
|
|
APFL_ERR_EMPTY_ASSIGNMENT,
|
|
APFL_ERR_ONLY_ONE_EXPAND_ALLOWED,
|
|
APFL_ERR_UNEXPECTED_CONSTANT_IN_MEMBER_ACCESS,
|
|
APFL_ERR_UNEXPECTED_EXPR_IN_MEMBER_ACCESS,
|
|
APFL_ERR_UNEXPECTED_BLANK_IN_MEMBER_ACCESS,
|
|
};
|
|
|
|
const char *apfl_error_type_name(enum apfl_error_type);
|
|
|
|
struct apfl_error {
|
|
enum apfl_error_type type;
|
|
|
|
// Optional data
|
|
struct apfl_position position;
|
|
struct apfl_position position2;
|
|
enum apfl_token_type token_type;
|
|
enum apfl_token_type token_type2;
|
|
char byte;
|
|
};
|
|
|
|
bool apfl_error_print(struct apfl_error, FILE *);
|
|
bool apfl_error_as_string(struct apfl_error, struct apfl_allocator, struct apfl_string *out);
|
|
|
|
// Get a constant string for the error, if available (returns NULL otherwise)
|
|
const char *apfl_error_as_const_string(struct apfl_error error);
|
|
|
|
struct apfl_error apfl_error_simple(enum apfl_error_type);
|
|
bool apfl_error_is_fatal_type(enum apfl_error_type);
|
|
|
|
#define APFL_ERROR_IS_FATAL(err) (apfl_error_is_fatal_type((err).type))
|
|
|
|
enum apfl_expr_type {
|
|
APFL_EXPR_LIST,
|
|
APFL_EXPR_DICT,
|
|
APFL_EXPR_CALL,
|
|
APFL_EXPR_SIMPLE_FUNC,
|
|
APFL_EXPR_COMPLEX_FUNC,
|
|
APFL_EXPR_ASSIGNMENT,
|
|
APFL_EXPR_DOT,
|
|
APFL_EXPR_AT,
|
|
APFL_EXPR_CONSTANT,
|
|
APFL_EXPR_VAR,
|
|
APFL_EXPR_BLANK,
|
|
};
|
|
|
|
struct apfl_expr_list_item {
|
|
struct apfl_expr *expr;
|
|
bool expand;
|
|
};
|
|
|
|
struct apfl_expr_list {
|
|
struct apfl_expr_list_item *items;
|
|
size_t len;
|
|
};
|
|
|
|
struct apfl_expr_dict_pair {
|
|
struct apfl_expr *k;
|
|
struct apfl_expr *v;
|
|
};
|
|
|
|
struct apfl_expr_dict {
|
|
struct apfl_expr_dict_pair *items;
|
|
size_t len;
|
|
size_t cap;
|
|
};
|
|
|
|
struct apfl_expr_call {
|
|
struct apfl_expr *callee;
|
|
struct apfl_expr_list arguments;
|
|
};
|
|
|
|
struct apfl_expr_body {
|
|
struct apfl_expr *items;
|
|
size_t len;
|
|
size_t cap;
|
|
};
|
|
|
|
enum apfl_expr_const_type {
|
|
APFL_EXPR_CONST_NIL,
|
|
APFL_EXPR_CONST_BOOLEAN,
|
|
APFL_EXPR_CONST_STRING,
|
|
APFL_EXPR_CONST_NUMBER,
|
|
};
|
|
|
|
struct apfl_expr_const {
|
|
enum apfl_expr_const_type type;
|
|
|
|
union {
|
|
// variant nil is without data
|
|
bool boolean;
|
|
struct apfl_string string;
|
|
apfl_number number;
|
|
};
|
|
};
|
|
|
|
struct apfl_expr_param_predicate {
|
|
struct apfl_expr_param *lhs;
|
|
struct apfl_expr *rhs;
|
|
};
|
|
|
|
enum apfl_expr_param_type {
|
|
APFL_EXPR_PARAM_VAR,
|
|
APFL_EXPR_PARAM_CONSTANT,
|
|
APFL_EXPR_PARAM_PREDICATE,
|
|
APFL_EXPR_PARAM_LIST,
|
|
APFL_EXPR_PARAM_BLANK,
|
|
};
|
|
|
|
struct apfl_expr_params {
|
|
struct apfl_expr_params_item *params;
|
|
size_t len;
|
|
size_t cap;
|
|
};
|
|
|
|
struct apfl_expr_param {
|
|
enum apfl_expr_param_type type;
|
|
|
|
union {
|
|
struct apfl_string var;
|
|
struct apfl_expr_const constant;
|
|
struct apfl_expr_param_predicate predicate;
|
|
struct apfl_expr_params list;
|
|
};
|
|
};
|
|
|
|
struct apfl_expr_params_item {
|
|
bool expand;
|
|
struct apfl_expr_param param;
|
|
};
|
|
|
|
struct apfl_expr_subfunc {
|
|
struct apfl_expr_params params;
|
|
struct apfl_expr_body body;
|
|
};
|
|
|
|
struct apfl_expr_complex_func {
|
|
struct apfl_expr_subfunc *subfuncs;
|
|
size_t len;
|
|
size_t cap;
|
|
};
|
|
|
|
enum apfl_expr_assignable_type {
|
|
APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER,
|
|
APFL_EXPR_ASSIGNABLE_CONSTANT,
|
|
APFL_EXPR_ASSIGNABLE_PREDICATE,
|
|
APFL_EXPR_ASSIGNABLE_LIST,
|
|
APFL_EXPR_ASSIGNABLE_BLANK,
|
|
|
|
};
|
|
|
|
enum apfl_expr_assignable_var_or_member_type {
|
|
APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER_VAR,
|
|
APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER_DOT,
|
|
APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER_AT,
|
|
};
|
|
|
|
struct apfl_expr_assignable_var_or_member_dot {
|
|
struct apfl_expr_assignable_var_or_member *lhs;
|
|
struct apfl_string rhs;
|
|
};
|
|
struct apfl_expr_assignable_var_or_member_at {
|
|
struct apfl_expr_assignable_var_or_member *lhs;
|
|
struct apfl_expr *rhs;
|
|
};
|
|
|
|
struct apfl_expr_assignable_var_or_member {
|
|
enum apfl_expr_assignable_var_or_member_type type;
|
|
|
|
union {
|
|
struct apfl_string var;
|
|
struct apfl_expr_assignable_var_or_member_dot dot;
|
|
struct apfl_expr_assignable_var_or_member_at at;
|
|
};
|
|
};
|
|
|
|
struct apfl_expr_assignable_predicate {
|
|
struct apfl_expr_assignable *lhs;
|
|
struct apfl_expr *rhs;
|
|
};
|
|
struct apfl_expr_assignable_list {
|
|
struct apfl_expr_assignable_list_item *items;
|
|
size_t len;
|
|
};
|
|
|
|
struct apfl_expr_assignable {
|
|
enum apfl_expr_assignable_type type;
|
|
|
|
union {
|
|
struct apfl_expr_assignable_var_or_member var_or_member;
|
|
struct apfl_expr_const constant;
|
|
struct apfl_expr_assignable_predicate predicate;
|
|
struct apfl_expr_assignable_list list;
|
|
};
|
|
};
|
|
|
|
struct apfl_expr_assignable_list_item {
|
|
struct apfl_expr_assignable assignable;
|
|
bool expand;
|
|
};
|
|
|
|
struct apfl_expr_assignment {
|
|
bool local;
|
|
struct apfl_expr_assignable lhs;
|
|
struct apfl_expr *rhs;
|
|
};
|
|
|
|
struct apfl_expr_dot {
|
|
struct apfl_expr *lhs;
|
|
struct apfl_string rhs;
|
|
};
|
|
|
|
struct apfl_expr_at {
|
|
struct apfl_expr *lhs;
|
|
struct apfl_expr *rhs;
|
|
};
|
|
|
|
struct apfl_expr {
|
|
enum apfl_expr_type type;
|
|
|
|
union {
|
|
struct apfl_expr_list list;
|
|
struct apfl_expr_dict dict;
|
|
struct apfl_expr_call call;
|
|
struct apfl_expr_body simple_func;
|
|
struct apfl_expr_complex_func complex_func;
|
|
struct apfl_expr_assignment assignment;
|
|
struct apfl_expr_dot dot;
|
|
struct apfl_expr_at at;
|
|
struct apfl_expr_const constant;
|
|
struct apfl_string var;
|
|
// blank has no further data
|
|
};
|
|
|
|
struct apfl_position position;
|
|
};
|
|
|
|
bool apfl_expr_print(struct apfl_expr, FILE *);
|
|
|
|
bool apfl_expr_eq(struct apfl_expr, struct apfl_expr);
|
|
|
|
// Begin deinit functions
|
|
|
|
void apfl_expr_deinit(struct apfl_allocator allocator, struct apfl_expr *);
|
|
void apfl_expr_list_deinit(struct apfl_allocator allocator, struct apfl_expr_list *);
|
|
void apfl_expr_list_item_deinit(struct apfl_allocator allocator, struct apfl_expr_list_item *);
|
|
void apfl_expr_dict_pair_deinit(struct apfl_allocator allocator, struct apfl_expr_dict_pair *);
|
|
void apfl_expr_dict_deinit(struct apfl_allocator allocator, struct apfl_expr_dict *);
|
|
void apfl_expr_call_deinit(struct apfl_allocator allocator, struct apfl_expr_call *);
|
|
void apfl_expr_body_deinit(struct apfl_allocator allocator, struct apfl_expr_body *);
|
|
void apfl_expr_const_deinit(struct apfl_allocator allocator, struct apfl_expr_const *);
|
|
void apfl_expr_param_predicate_deinit(struct apfl_allocator allocator, struct apfl_expr_param_predicate *);
|
|
void apfl_expr_params_deinit(struct apfl_allocator allocator, struct apfl_expr_params *);
|
|
void apfl_expr_params_item_deinit(struct apfl_allocator allocator, struct apfl_expr_params_item *);
|
|
void apfl_expr_param_deinit(struct apfl_allocator allocator, struct apfl_expr_param *);
|
|
void apfl_expr_subfunc_deinit(struct apfl_allocator allocator, struct apfl_expr_subfunc *);
|
|
void apfl_expr_complex_func_deinit(struct apfl_allocator allocator, struct apfl_expr_complex_func *);
|
|
void apfl_expr_assignable_predicate_deinit(struct apfl_allocator allocator, struct apfl_expr_assignable_predicate *);
|
|
void apfl_expr_assignable_list_item_deinit(struct apfl_allocator allocator, struct apfl_expr_assignable_list_item *);
|
|
void apfl_expr_assignable_list_deinit(struct apfl_allocator allocator, struct apfl_expr_assignable_list *);
|
|
void apfl_expr_assignable_var_or_member_dot_deinit(struct apfl_allocator allocator, struct apfl_expr_assignable_var_or_member_dot *);
|
|
void apfl_expr_assignable_var_or_member_at_deinit(struct apfl_allocator allocator, struct apfl_expr_assignable_var_or_member_at *);
|
|
void apfl_expr_assignable_var_or_member_deinit(struct apfl_allocator allocator, struct apfl_expr_assignable_var_or_member *);
|
|
void apfl_expr_assignable_deinit(struct apfl_allocator allocator, struct apfl_expr_assignable *);
|
|
void apfl_expr_assignment_deinit(struct apfl_allocator allocator, struct apfl_expr_assignment *);
|
|
void apfl_expr_dot_deinit(struct apfl_allocator allocator, struct apfl_expr_dot *);
|
|
void apfl_expr_at_deinit(struct apfl_allocator allocator, struct apfl_expr_at *);
|
|
|
|
// End deinit functions
|
|
|
|
// Begin move functions
|
|
|
|
struct apfl_expr apfl_expr_move(struct apfl_expr *);
|
|
struct apfl_expr_list apfl_expr_list_move(struct apfl_expr_list *);
|
|
struct apfl_expr_list_item apfl_expr_list_item_move(struct apfl_expr_list_item *);
|
|
struct apfl_expr_dict_pair apfl_expr_dict_pair_move(struct apfl_expr_dict_pair *);
|
|
struct apfl_expr_dict apfl_expr_dict_move(struct apfl_expr_dict *);
|
|
struct apfl_expr_call apfl_expr_call_move(struct apfl_expr_call *);
|
|
struct apfl_expr_body apfl_expr_body_move(struct apfl_expr_body *);
|
|
struct apfl_expr_const apfl_expr_const_move(struct apfl_expr_const *);
|
|
struct apfl_expr_param_predicate apfl_expr_param_predicate_move(struct apfl_expr_param_predicate *);
|
|
struct apfl_expr_params apfl_expr_params_move(struct apfl_expr_params *);
|
|
struct apfl_expr_param apfl_expr_param_move(struct apfl_expr_param *);
|
|
struct apfl_expr_subfunc apfl_expr_subfunc_move(struct apfl_expr_subfunc *);
|
|
struct apfl_expr_complex_func apfl_expr_complex_func_move(struct apfl_expr_complex_func *);
|
|
struct apfl_expr_assignable_var_or_member apfl_expr_assignable_var_or_member_move(struct apfl_expr_assignable_var_or_member *);
|
|
struct apfl_expr_assignable_predicate apfl_expr_assignable_predicate_move(struct apfl_expr_assignable_predicate *);
|
|
struct apfl_expr_assignable_dot apfl_expr_assignable_dot_move(struct apfl_expr_assignable_dot *);
|
|
struct apfl_expr_assignable_at apfl_expr_assignable_at_move(struct apfl_expr_assignable_at *);
|
|
struct apfl_expr_assignable_list apfl_expr_assignable_list_move(struct apfl_expr_assignable_list *);
|
|
struct apfl_expr_assignable apfl_expr_assignable_move(struct apfl_expr_assignable *);
|
|
struct apfl_expr_assignment apfl_expr_assignment_move(struct apfl_expr_assignment *);
|
|
struct apfl_expr_dot apfl_expr_dot_move(struct apfl_expr_dot *);
|
|
struct apfl_expr_at apfl_expr_at_move(struct apfl_expr_at *);
|
|
|
|
// End move functions
|
|
|
|
enum apfl_parse_result {
|
|
APFL_PARSE_OK,
|
|
APFL_PARSE_EOF,
|
|
APFL_PARSE_ERROR,
|
|
};
|
|
|
|
struct apfl_tokenizer;
|
|
|
|
typedef struct apfl_tokenizer *apfl_tokenizer_ptr;
|
|
|
|
/* An apfl_source_reader is used to read source code for parsing / evaluation.
|
|
*/
|
|
struct apfl_source_reader {
|
|
/* callback gets called repeatedly to get the source code.
|
|
* buf points to a buffer to fill that has a size of *len.
|
|
* The callback must set len to the number of read bytes and return true
|
|
* on success and false on failure.
|
|
*
|
|
* Setting len to 0 indicates and end of file.
|
|
*
|
|
* need is true if more input is required for parsing, this is useful for
|
|
* implementing a reader for a REPL to indicate to the user if they need
|
|
* to type more code (e.g. by changing the prompt).
|
|
*/
|
|
bool (*callback)(void *opaque, char *buf, size_t *len, bool need);
|
|
|
|
void *opaque;
|
|
};
|
|
|
|
apfl_tokenizer_ptr apfl_tokenizer_new(struct apfl_allocator allocator, struct apfl_source_reader);
|
|
|
|
void apfl_tokenizer_destroy(apfl_tokenizer_ptr);
|
|
|
|
enum apfl_parse_result apfl_tokenizer_next(apfl_tokenizer_ptr, bool need);
|
|
|
|
/* Get the current token.
|
|
* Return value is undefined when the last call to apfl_tokenizer_next did not
|
|
* return APFL_PARSE_OK.
|
|
*/
|
|
struct apfl_token apfl_tokenizer_get_token(apfl_tokenizer_ptr);
|
|
|
|
/* Get the current error.
|
|
* Return value is undefined when the last call to apfl_tokenizer_next did not
|
|
* return APFL_PARSE_ERROR.
|
|
*/
|
|
struct apfl_error apfl_tokenizer_get_error(apfl_tokenizer_ptr);
|
|
|
|
/* apfl_string_source_reader_* implements an apfl_source_reader that reads
|
|
* source code from a string view.
|
|
*/
|
|
struct apfl_string_source_reader_data {
|
|
struct apfl_string_view sv;
|
|
size_t off;
|
|
};
|
|
|
|
struct apfl_string_source_reader_data apfl_string_source_reader_create(struct apfl_string_view);
|
|
|
|
/* Creates a source reader for apfl_string_source_reader_data.
|
|
* The pointed to apfl_string_source_reader_data and the underlying string view
|
|
* must be alive while the reader is in use.
|
|
*/
|
|
struct apfl_source_reader apfl_string_source_reader(struct apfl_string_source_reader_data *);
|
|
|
|
struct apfl_parser_token_source {
|
|
enum apfl_parse_result (*next)(void *, bool need);
|
|
struct apfl_token (*get_token)(void *);
|
|
struct apfl_error (*get_error)(void *);
|
|
void *opaque;
|
|
};
|
|
|
|
struct apfl_parser_token_source apfl_tokenizer_as_token_source(apfl_tokenizer_ptr);
|
|
|
|
struct apfl_parser;
|
|
|
|
typedef struct apfl_parser *apfl_parser_ptr;
|
|
|
|
apfl_parser_ptr apfl_parser_new(struct apfl_allocator allocator, struct apfl_parser_token_source);
|
|
|
|
/* Destroys the parser.
|
|
* Note that if the token source needs it's own destruction, you'll have to do
|
|
* that yourself after destroying the parser.
|
|
*/
|
|
void apfl_parser_destroy(apfl_parser_ptr);
|
|
|
|
enum apfl_parse_result apfl_parser_next(apfl_parser_ptr);
|
|
|
|
/* Get the current error.
|
|
* Return value is undefined when the last call to apfl_parser_next did not
|
|
* return APFL_PARSE_ERROR.
|
|
*/
|
|
struct apfl_error apfl_parser_get_error(apfl_parser_ptr);
|
|
|
|
/* Get the current expression.
|
|
* Return value is undefined when the last call to apfl_parser_next did not
|
|
* return APFL_PARSE_OK.
|
|
*/
|
|
struct apfl_expr apfl_parser_get_expr(apfl_parser_ptr);
|
|
|
|
struct apfl_ctx_data;
|
|
typedef struct apfl_ctx_data *apfl_ctx;
|
|
|
|
enum apfl_value_type {
|
|
APFL_VALUE_NIL,
|
|
APFL_VALUE_BOOLEAN,
|
|
APFL_VALUE_NUMBER,
|
|
APFL_VALUE_STRING,
|
|
APFL_VALUE_LIST,
|
|
APFL_VALUE_DICT,
|
|
APFL_VALUE_FUNC,
|
|
APFL_VALUE_USERDATA,
|
|
};
|
|
|
|
typedef int apfl_stackidx;
|
|
typedef size_t apfl_slotidx;
|
|
|
|
enum apfl_result {
|
|
APFL_RESULT_OK, // Evaluation succeeded, value is on the stack
|
|
|
|
// TODO: No further details are yet provided on (fatal) error. Not quite
|
|
// sure what to return / how errors/exceptions should work. Maybe a
|
|
// value + a backtrace?
|
|
APFL_RESULT_ERR,
|
|
APFL_RESULT_ERR_FATAL,
|
|
};
|
|
|
|
struct apfl_config {
|
|
struct apfl_allocator allocator;
|
|
struct apfl_format_writer output_writer;
|
|
};
|
|
|
|
apfl_ctx apfl_ctx_new(struct apfl_config);
|
|
|
|
void apfl_ctx_destroy(apfl_ctx);
|
|
|
|
typedef void (*apfl_panic_callback)(apfl_ctx, bool with_error_on_stack, void *);
|
|
|
|
void apfl_ctx_set_panic_callback(apfl_ctx, apfl_panic_callback, void *);
|
|
|
|
typedef struct apfl_iterative_runner_data *apfl_iterative_runner;
|
|
|
|
apfl_iterative_runner apfl_iterative_runner_new(apfl_ctx, struct apfl_source_reader);
|
|
bool apfl_iterative_runner_next(apfl_iterative_runner);
|
|
enum apfl_result apfl_iterative_runner_get_result(apfl_iterative_runner);
|
|
bool apfl_iterative_runner_has_error_on_stack(apfl_iterative_runner);
|
|
void apfl_iterative_runner_destroy(apfl_iterative_runner);
|
|
|
|
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);
|
|
// Move a value to the top of the stack
|
|
void apfl_move_to_top_of_stack(apfl_ctx, apfl_stackidx);
|
|
// Copy a value to the top of the stack
|
|
void apfl_copy(apfl_ctx ctx, apfl_stackidx index);
|
|
// Push a nil onto the stack
|
|
void apfl_push_nil(apfl_ctx);
|
|
// Push a boolean onto the stack
|
|
void apfl_push_bool(apfl_ctx, bool);
|
|
// Push a number onto the stack
|
|
void apfl_push_number(apfl_ctx, apfl_number);
|
|
// Push a string onto the stack
|
|
void apfl_push_string_view_copy(apfl_ctx, struct apfl_string_view);
|
|
// Push a constant string.
|
|
void apfl_push_const_string(apfl_ctx, const char *);
|
|
// Create a new empty list on the stack
|
|
void apfl_list_create(apfl_ctx, size_t initial_capacity);
|
|
// Append a value to a list. The value will be dropped.
|
|
void apfl_list_append(apfl_ctx, apfl_stackidx list, apfl_stackidx value);
|
|
// Append a list to another list. The second list will be dropped.
|
|
void apfl_list_append_list(apfl_ctx, apfl_stackidx a, apfl_stackidx b);
|
|
// Create a new empty dict on the stack
|
|
void apfl_dict_create(apfl_ctx);
|
|
// Set a value in a dictionary. k and v will be dropped.
|
|
void apfl_dict_set(apfl_ctx, apfl_stackidx dict, apfl_stackidx k, apfl_stackidx v);
|
|
// Get a value from a container (list or dict) and push it on the stack. container and k will be dropped.
|
|
void apfl_get_member(apfl_ctx, apfl_stackidx container, apfl_stackidx k);
|
|
// Get a value from a container (list or dict) and push it on the stack, if it exists. container and k will be dropped.
|
|
bool apfl_get_member_if_exists(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);
|
|
// Pops a value off the stack and turns it into a string.
|
|
void apfl_tostring(apfl_ctx, apfl_stackidx);
|
|
// Pops a value from the stack and returns whether is was truthy. All values except false and nil are truthy.
|
|
bool apfl_is_truthy(apfl_ctx, apfl_stackidx);
|
|
// Pops a number from the stack and returns it.
|
|
apfl_number apfl_get_number(apfl_ctx, apfl_stackidx);
|
|
// Pops two values from the stack and returns whether they are equal.
|
|
bool apfl_eq(apfl_ctx, apfl_stackidx, 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);
|
|
|
|
void apfl_cfunc_getslot(apfl_ctx, apfl_stackidx cfunc, apfl_slotidx);
|
|
void apfl_cfunc_self_getslot(apfl_ctx, apfl_slotidx);
|
|
void apfl_cfunc_setslot(apfl_ctx, apfl_stackidx cfunc, apfl_slotidx, apfl_stackidx value);
|
|
void apfl_cfunc_self_setslot(apfl_ctx, apfl_slotidx, apfl_stackidx value);
|
|
|
|
void apfl_push_userdata(apfl_ctx, void *);
|
|
void *apfl_get_userdata(apfl_ctx, apfl_stackidx);
|
|
|
|
void apfl_call(apfl_ctx, apfl_stackidx func, apfl_stackidx args);
|
|
enum apfl_result apfl_call_protected(apfl_ctx, apfl_stackidx func, apfl_stackidx args, bool *with_error_on_stack);
|
|
|
|
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;
|
|
const char *input_error_while_parsing;
|
|
const char *unexpected_end_of_file;
|
|
const char *corrupted_bytecode;
|
|
const char *not_a_list;
|
|
const char *not_a_dict;
|
|
const char *key_doesnt_exist;
|
|
const char *value_is_not_a_container;
|
|
const char *wrong_key_type;
|
|
const char *variable_doesnt_exist;
|
|
const char *not_a_c_function;
|
|
const char *invalid_slotidx;
|
|
const char *not_a_function;
|
|
const char *wrong_type;
|
|
const char *io_error;
|
|
const char *value_doesnt_match;
|
|
const char *invalid_matcher_state;
|
|
const char *no_matching_subfunction;
|
|
};
|
|
extern const struct apfl_messages apfl_messages;
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
#endif
|