#ifndef APFL_H #define APFL_H #ifdef __cplusplus extern "C" { #endif #include #include #include // 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