apfl/src/apfl.h
Laria Carolin Chabowski 0f6f136873 Push formatted parser error on stack during evaluation
This way we can see the parse errors again in evaluation mode

Not fully fleshed out yet: We simply use apfl_debug_print_val to dump the
top of the stack (the formatted error) to stderr but don't nicely handle
if there is nothing on the stack (apfl_debug_print_val will print a rather
cryptic "stack index invalid" error). Also the whole dance we need to do to
put the formatted error onto the stack feels rather awkward (there should
probably a function for this) and we also should probably try to push an
error description on the stack in case this moving-string-to-stack business
fails.

Now "only" all other errors need to be put on the stack as a string :)
2022-04-21 22:55:11 +02:00

623 lines
20 KiB
C

#ifndef APFL_H
#define APFL_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
// 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;
};
void apfl_string_builder_init(struct apfl_allocator allocator, struct apfl_string_builder *);
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);
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))))
// 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,
APFL_ERR_NOT_IMPLEMENTED,
};
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);
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;
};
void 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,
};
typedef int apfl_stackidx;
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,
};
apfl_ctx apfl_ctx_new(struct apfl_allocator);
void apfl_ctx_destroy(apfl_ctx);
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);
void apfl_iterative_runner_destroy(apfl_iterative_runner);
// Get the type of a value on the stack
enum apfl_value_type apfl_get_type(apfl_ctx, apfl_stackidx);
// Create a new empty list on the stack
enum apfl_result apfl_list_create(apfl_ctx, size_t initial_capacity);
// Append a value to a list. The value will be dropped.
enum apfl_result apfl_list_append(apfl_ctx, apfl_stackidx list, apfl_stackidx value);
// Append a list to another list. The second list will be dropped.
enum apfl_result apfl_list_append_list(apfl_ctx, apfl_stackidx a, apfl_stackidx b);
// Create a new empty dict on the stack
enum apfl_result apfl_dict_create(apfl_ctx);
// Set a value in a dictionary. k and v will be dropped.
enum apfl_result 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.
enum apfl_result apfl_get_member(apfl_ctx, apfl_stackidx container, apfl_stackidx k);
void apfl_debug_print_val(apfl_ctx, apfl_stackidx, FILE *);
#ifdef __cplusplus
}
#endif
#endif