Initial commit

This commit is contained in:
Laria 2021-12-10 21:22:16 +01:00
commit d094ed7bd5
28 changed files with 5852 additions and 0 deletions

19
.gitignore vendored Normal file
View file

@ -0,0 +1,19 @@
*.o
*.trs
*.log
*.test
depcomp
config.status
Makefile.in
missing
test-driver
ar-lib
compile
configure
*.m4
autom4te.cache/
.deps/
Makefile
install-sh
src/libapfl.a
src/apfl

1
Makefile.am Normal file
View file

@ -0,0 +1 @@
SUBDIRS = src

10
configure.ac Normal file
View file

@ -0,0 +1,10 @@
AC_INIT([apfl], [0.0.1])
AM_INIT_AUTOMAKE([-Wall -Werror foreign])
AC_PROG_CC
AM_PROG_AR
AC_PROG_RANLIB
AC_CONFIG_FILES([
Makefile
src/Makefile
])
AC_OUTPUT

60
examples/examples.apfl Normal file
View file

@ -0,0 +1,60 @@
pipe := {
x -> x
x f?callable ~more ->
pipe (f x) ~more
x [f?callable ~args] ~more ->
pipe (f ~args x) ~more
}
map := {
_ [] -> []
f [x ~xs] ->
[(f x) ~(map f xs)]
}
filter := {
_ [] -> []
f [x ~xs] ->
if (f x) {
[x ~(filter f xs)]
} {
filter f xs
}
}
is-odd := { x ->
== 1 (mod x 2)
}
partial := { f ~a1 ->
{ ~a2 -> f ~a1 ~a2 }
}
reduce := {
_ carry [] -> carry
f carry [x ~xs] ->
reduce f (f x carry) xs
}
sum := partial reduce + 0
pipe ~[
(range 1 10) # [1 2 3 4 5 6 7 8 9]
[filter is-odd] # [1 3 5 7 9]
[map (partial * 10)] # [10 30 50 70 90]
sum # 250
]
#####
# As each closure is unique, a closure can be used as a symbol, i.e. a unique
# value which only usage is to be able to be compared with itself.
symbol := { ->
sym := { -> sym }
sym # technically not neccessary, but this way it's more obvious we're returning sym
}
a := (symbol)
b := (symbol)
assert{ != a b }

View file

@ -0,0 +1,14 @@
=== script ===
adder = { a ->
{ b -> + a b }
}
add10 = adder 10
inc = adder 1
print (add10 32)
print (inc 665)
=== output ===
42
666

View file

@ -0,0 +1,25 @@
=== script ===
counter = {
i = 0
{ i = i + 1 }
}
c1 = (counter)
c2 = (counter)
print (c1)
print (c2)
print (c1)
print (c2)
print (c2)
print (c1)
print (c2)
=== output ===
1
1
2
2
3
3
4

View file

@ -0,0 +1,5 @@
=== script ===
print "Hello World!"
=== output ===
Hello World!

View file

@ -0,0 +1,10 @@
=== script ===
fac = {
0 -> 1
n -> * n (fac (--n ))
}
print (fac 10)
=== output ===
3628800

35
src/Makefile.am Normal file
View file

@ -0,0 +1,35 @@
AM_CFLAGS=--std=c11 -Wall -Werror -Wextra -pedantic
lib_LIBRARIES = libapfl.a
libapfl_a_SOURCES =
libapfl_a_SOURCES += error.c
libapfl_a_SOURCES += expr.c
libapfl_a_SOURCES += position.c
libapfl_a_SOURCES += resizable.c
libapfl_a_SOURCES += strings.c
libapfl_a_SOURCES += token.c
libapfl_a_SOURCES += tokenizer.c
apfl_internal_headers =
apfl_internal_headers += common.h
apfl_internal_headers += internal.h
apfl_internal_headers += resizable.h
EXTRA_DIST = $(apfl_internal_headers) apfl.h
apflincludesdir = $(pkgincludedir)/apfl
apflincludes_HEADERS = apfl.h
bin_PROGRAMS = apfl
apfl_SOURCES = main.c apfl.h
apfl_LDADD = libapfl.a
TESTS =
check_PROGRAMS =
TESTS += tokenizer.test
check_PROGRAMS += tokenizer.test
tokenizer_test_SOURCES = tokenizer_test.c test.h
tokenizer_test_LDADD = libapfl.a

454
src/apfl.h Normal file
View file

@ -0,0 +1,454 @@
#ifndef APFL_H
#define APFL_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
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;
};
#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))
void apfl_string_deinit(struct apfl_string *);
bool apfl_string_copy(struct apfl_string *dst, struct apfl_string_view src);
struct apfl_string apfl_string_move(struct apfl_string *src);
struct apfl_string_builder {
char *bytes;
size_t len;
size_t cap;
};
void apfl_string_builder_init(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_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_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,
};
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;
};
void apfl_error_print(struct apfl_error, FILE *);
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,
};
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;
};
struct apfl_expr_call {
struct apfl_expr *callee;
struct apfl_expr_list arguments;
};
struct apfl_expr_body {
struct apfl_expr *items;
size_t len;
};
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;
};
struct apfl_expr_param_list {
struct apfl_expr_param *children;
size_t len;
};
enum apfl_expr_param_type {
APFL_EXPR_PARAM_VAR,
APFL_EXPR_PARAM_CONSTANT,
APFL_EXPR_PARAM_PREDICATE,
APFL_EXPR_PARAM_EXPAND,
APFL_EXPR_PARAM_LIST,
};
struct apfl_expr_params {
struct apfl_expr_param *params;
size_t len;
};
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_param *expand;
struct apfl_expr_params list;
};
};
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;
};
enum apfl_expr_assignable_type {
APFL_EXPR_ASSIGNABLE_VAR,
APFL_EXPR_ASSIGNABLE_CONSTANT,
APFL_EXPR_ASSIGNABLE_PREDICATE,
APFL_EXPR_ASSIGNABLE_EXPAND,
APFL_EXPR_ASSIGNABLE_DOT,
APFL_EXPR_ASSIGNABLE_AT,
APFL_EXPR_ASSIGNABLE_LIST,
};
struct apfl_expr_assignable_predicate {
struct apfl_expr_assignable *lhs;
struct apfl_expr *rhs;
};
struct apfl_expr_assignable_dot {
struct apfl_expr_assignable *lhs;
struct apfl_string rhs;
};
struct apfl_expr_assignable_at {
struct apfl_expr_assignable *lhs;
struct apfl_expr *rhs;
};
struct apfl_expr_assignable_list {
struct apfl_expr_assignable *children;
size_t len;
};
struct apfl_expr_assignable {
enum apfl_expr_assignable_type type;
union {
struct apfl_string var;
struct apfl_expr_const constant;
struct apfl_expr_assignable_predicate predicate;
struct apfl_expr_assignable *expand;
struct apfl_expr_assignable_dot dot;
struct apfl_expr_assignable_at at;
struct apfl_expr_assignable_list list;
};
};
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;
};
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_expr *);
void apfl_expr_list_deinit(struct apfl_expr_list *);
void apfl_expr_list_item_deinit(struct apfl_expr_list_item *);
void apfl_expr_dict_pair_deinit(struct apfl_expr_dict_pair *);
void apfl_expr_dict_deinit(struct apfl_expr_dict *);
void apfl_expr_call_deinit(struct apfl_expr_call *);
void apfl_expr_body_deinit(struct apfl_expr_body *);
void apfl_expr_const_deinit(struct apfl_expr_const *);
void apfl_expr_param_predicate_deinit(struct apfl_expr_param_predicate *);
void apfl_expr_param_list_deinit(struct apfl_expr_param_list *);
void apfl_expr_params_deinit(struct apfl_expr_params *);
void apfl_expr_param_deinit(struct apfl_expr_param *);
void apfl_expr_subfunc_deinit(struct apfl_expr_subfunc *);
void apfl_expr_complex_func_deinit(struct apfl_expr_complex_func *);
void apfl_expr_assignable_predicate_deinit(struct apfl_expr_assignable_predicate *);
void apfl_expr_assignable_dot_deinit(struct apfl_expr_assignable_dot *);
void apfl_expr_assignable_at_deinit(struct apfl_expr_assignable_at *);
void apfl_expr_assignable_list_deinit(struct apfl_expr_assignable_list *);
void apfl_expr_assignable_deinit(struct apfl_expr_assignable *);
void apfl_expr_assignment_deinit(struct apfl_expr_assignment *);
void apfl_expr_dot_deinit(struct apfl_expr_dot *);
void apfl_expr_at_deinit(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_param_list apfl_expr_param_list_move(struct apfl_expr_param_list *);
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_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;
typedef bool (*apfl_source_reader_cb)(void *context, char *buf, size_t *len, bool need);
apfl_tokenizer_ptr apfl_tokenizer_new(apfl_source_reader_cb, void *context);
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);
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;
typedef struct apfl_parser *apfl_parser_ptr;
apfl_parser_ptr apfl_parser_new(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);
#ifdef __cplusplus
}
#endif
#endif

27
src/common.h Normal file
View file

@ -0,0 +1,27 @@
#ifndef APFL_COMMON_H
#define APFL_COMMON_H
#ifdef __cplusplus
extern "C" {
#endif
// APFL_DESTROY destroys a dynamically allocated value.
// It will first deinit the value using deiniter,
// free the memory and then set the variable to NULL.
// It is always allowed to destroy an already destroyed
// or deinited value.
#define APFL_DESTROY(var, deiniter) \
do { \
if ((var) == NULL) { \
break; \
} \
deiniter(var); \
free(var); \
(var) = NULL; \
} while(0)
#ifdef __cplusplus
}
#endif
#endif

117
src/error.c Normal file
View file

@ -0,0 +1,117 @@
#include <stdio.h>
#include "apfl.h"
#define POSFMT "%d:%d"
#define POSARGS error.position.line, error.position.col
#define POS2ARGS error.position2.line, error.position2.col
void
apfl_error_print(struct apfl_error error, FILE *file)
{
switch (error.type) {
case APFL_ERR_MALLOC_FAILED:
fprintf(file, "Could not allocate memory\n");
return;
case APFL_ERR_INPUT_ERROR:
fprintf(file, "Input error while parsing\n");
return;
case APFL_ERR_UNEXPECTED_EOF:
fprintf(file, "Unexpected end of file\n");
return;
case APFL_ERR_EXPECTED_EQ_AFTER_COLON:
fprintf(file, "Expected '=' after ':' at " POSFMT "\n", POSARGS);
return;
case APFL_ERR_UNEXPECTED_BYTE_IN_NUMBER:
fprintf(file, "Unexpected byte '%c' while parsing number at " POSFMT "\n", error.byte, POSARGS);
return;
case APFL_ERR_EXPECTED_DIGIT:
fprintf(file, "Expected a digit at " POSFMT "\n", POSARGS);
return;
case APFL_ERR_EXPECTED_HEX_IN_HEX_ESCAPE:
fprintf(file, "Expected a hex-digit in hex escape at " POSFMT "\n", POSARGS);
return;
case APFL_ERR_INVALID_ESCAPE_SEQUENCE:
fprintf(file, "Invalid escape sequence \\%c at " POSFMT "\n", error.byte, POSARGS);
return;
case APFL_ERR_NO_LINEBREAK_AFTER_CONTINUE_LINE:
fprintf(file, "No line break (after optional comments) after \\ at " POSFMT "\n", POSARGS);
return;
case APFL_ERR_UNEXPECTED_TOKEN:
fprintf(file, "Unexpected `%s` token at " POSFMT "\n", apfl_token_type_name(error.token_type), POSARGS);
return;
case APFL_ERR_MISMATCHING_CLOSING_BRACKET:
fprintf(
file,
"Closing `%s` token at " POSFMT " does not match opening `%s` at " POSFMT "\n",
apfl_token_type_name(error.token_type),
POSARGS,
apfl_token_type_name(error.token_type2),
POS2ARGS
);
return;
case APFL_ERR_UNEXPECTED_EOF_AFTER_TOKEN:
fprintf(
file,
"Unexpected end of file after `%s` token at " POSFMT "\n",
apfl_token_type_name(error.token_type),
POSARGS
);
return;
case APFL_ERR_STATEMENTS_BEFORE_PARAMETERS:
fprintf(
file,
"Unexpected statements before parameters near " POSFMT "\n",
POSARGS
);
return;
case APFL_ERR_EMPTY_ASSIGNMENT_BEFORE_PARAMETERS:
fprintf(
file,
"Unexpected empty assignment before parameters near " POSFMT "\n",
POSARGS
);
return;
case APFL_ERR_UNEXPECTED_EXPRESSION:
fprintf(
file,
"Unexpected expression near " POSFMT "\n",
POSARGS
);
return;
case APFL_ERR_INVALID_ASSIGNMENT_LHS:
fprintf(
file,
"Invalid left hand side of assignment near " POSFMT "\n",
POSARGS
);
return;
case APFL_ERR_EMPTY_ASSIGNMENT:
fprintf(
file,
"Empty assignment at " POSFMT "\n",
POSARGS
);
return;
}
fprintf(file, "Unknown error %d\n", (int)error.type);
}
struct apfl_error
apfl_error_simple(enum apfl_error_type type)
{
return (struct apfl_error) { .type = type };
}
bool
apfl_error_is_fatal_type(enum apfl_error_type type)
{
switch (type) {
case APFL_ERR_MALLOC_FAILED:
case APFL_ERR_INPUT_ERROR:
return true;
default:
return false;
}
}

919
src/expr.c Normal file
View file

@ -0,0 +1,919 @@
#include <assert.h>
#include <stdarg.h>
#include <stdio.h>
#include "apfl.h"
#include "common.h"
#include "internal.h"
void
apfl_expr_deinit(struct apfl_expr *expr)
{
switch (expr->type) {
case APFL_EXPR_LIST:
apfl_expr_list_deinit(&expr->list);
break;
case APFL_EXPR_DICT:
apfl_expr_dict_deinit(&expr->dict);
break;
case APFL_EXPR_CALL:
apfl_expr_call_deinit(&expr->call);
break;
case APFL_EXPR_SIMPLE_FUNC:
apfl_expr_body_deinit(&expr->simple_func);
break;
case APFL_EXPR_COMPLEX_FUNC:
apfl_expr_complex_func_deinit(&expr->complex_func);
break;
case APFL_EXPR_ASSIGNMENT:
apfl_expr_assignment_deinit(&expr->assignment);
break;
case APFL_EXPR_DOT:
apfl_expr_dot_deinit(&expr->dot);
break;
case APFL_EXPR_AT:
apfl_expr_at_deinit(&expr->at);
break;
case APFL_EXPR_CONSTANT:
apfl_expr_const_deinit(&expr->constant);
break;
case APFL_EXPR_VAR:
apfl_string_deinit(&expr->var);
break;
}
}
void
apfl_expr_list_deinit(struct apfl_expr_list *list)
{
DEINIT_LIST(list->items, list->len, apfl_expr_list_item_deinit);
}
void
apfl_expr_list_item_deinit(struct apfl_expr_list_item *item)
{
DESTROY(item->expr, apfl_expr_deinit);
}
void
apfl_expr_dict_pair_deinit(struct apfl_expr_dict_pair *pair)
{
DESTROY(pair->k, apfl_expr_deinit);
DESTROY(pair->v, apfl_expr_deinit);
}
void
apfl_expr_dict_deinit(struct apfl_expr_dict *dict)
{
DEINIT_LIST(dict->items, dict->len, apfl_expr_dict_pair_deinit);
}
void
apfl_expr_call_deinit(struct apfl_expr_call *call)
{
DESTROY(call->callee, apfl_expr_deinit);
}
void
apfl_expr_body_deinit(struct apfl_expr_body *body)
{
DEINIT_LIST(body->items, body->len, apfl_expr_deinit);
}
void
apfl_expr_const_deinit(struct apfl_expr_const *constant)
{
switch (constant->type) {
case APFL_EXPR_CONST_NIL:
case APFL_EXPR_CONST_BOOLEAN:
case APFL_EXPR_CONST_NUMBER:
// nop
break;
case APFL_EXPR_CONST_STRING:
apfl_string_deinit(&constant->string);
break;
}
}
#define DEINIT_GENERIC_LHS_RHS_EXPR(x, lhs_deiniter) \
do { \
DESTROY(x->lhs, lhs_deiniter); \
DESTROY(x->rhs, apfl_expr_deinit); \
} while (0)
void
apfl_expr_param_predicate_deinit(struct apfl_expr_param_predicate *pred)
{
DEINIT_GENERIC_LHS_RHS_EXPR(pred, apfl_expr_param_deinit);
}
void
apfl_expr_param_list_deinit(struct apfl_expr_param_list *list)
{
DEINIT_LIST(list->children, list->len, apfl_expr_param_deinit);
}
void
apfl_expr_params_deinit(struct apfl_expr_params *params)
{
DEINIT_LIST(params->params, params->len, apfl_expr_param_deinit);
}
void
apfl_expr_param_deinit(struct apfl_expr_param *param)
{
switch (param->type) {
case APFL_EXPR_PARAM_VAR:
apfl_string_deinit(&param->var);
break;
case APFL_EXPR_PARAM_CONSTANT:
apfl_expr_const_deinit(&param->constant);
break;
case APFL_EXPR_PARAM_PREDICATE:
apfl_expr_param_predicate_deinit(&param->predicate);
break;
case APFL_EXPR_PARAM_EXPAND:
DESTROY(param->expand, apfl_expr_param_deinit);
break;
case APFL_EXPR_PARAM_LIST:
apfl_expr_params_deinit(&param->list);
break;
}
}
void
apfl_expr_subfunc_deinit(struct apfl_expr_subfunc *subfunc)
{
apfl_expr_params_deinit(&subfunc->params);
apfl_expr_body_deinit(&subfunc->body);
}
void
apfl_expr_complex_func_deinit(struct apfl_expr_complex_func *cf)
{
DEINIT_LIST(cf->subfuncs, cf->len, apfl_expr_subfunc_deinit);
}
void
apfl_expr_assignable_predicate_deinit(struct apfl_expr_assignable_predicate *pred)
{
DEINIT_GENERIC_LHS_RHS_EXPR(pred, apfl_expr_assignable_deinit);
}
#define GENERIC_DOT_DEINIT(dot, lhs_deiniter) \
do { \
DESTROY(dot->lhs, lhs_deiniter); \
apfl_string_deinit(&dot->rhs); \
} while (0)
void
apfl_expr_assignable_dot_deinit(struct apfl_expr_assignable_dot *dot)
{
GENERIC_DOT_DEINIT(dot, apfl_expr_assignable_deinit);
}
void
apfl_expr_assignable_at_deinit(struct apfl_expr_assignable_at *at)
{
DEINIT_GENERIC_LHS_RHS_EXPR(at, apfl_expr_assignable_deinit);
}
void
apfl_expr_assignable_list_deinit(struct apfl_expr_assignable_list *list)
{
DEINIT_LIST(list->children, list->len, apfl_expr_assignable_deinit);
}
void
apfl_expr_assignable_deinit(struct apfl_expr_assignable *a)
{
switch (a->type) {
case APFL_EXPR_ASSIGNABLE_VAR:
apfl_string_deinit(&a->var);
break;
case APFL_EXPR_ASSIGNABLE_CONSTANT:
apfl_expr_const_deinit(&a->constant);
break;
case APFL_EXPR_ASSIGNABLE_PREDICATE:
apfl_expr_assignable_predicate_deinit(&a->predicate);
break;
case APFL_EXPR_ASSIGNABLE_EXPAND:
DESTROY(a->expand, apfl_expr_assignable_deinit);
break;
case APFL_EXPR_ASSIGNABLE_DOT:
apfl_expr_assignable_dot_deinit(&a->dot);
break;
case APFL_EXPR_ASSIGNABLE_AT:
apfl_expr_assignable_at_deinit(&a->at);
break;
case APFL_EXPR_ASSIGNABLE_LIST:
apfl_expr_assignable_list_deinit(&a->list);
break;
}
}
void
apfl_expr_assignment_deinit(struct apfl_expr_assignment *a)
{
apfl_expr_assignable_deinit(&a->lhs);
DESTROY(a->rhs, apfl_expr_deinit);
}
void
apfl_expr_dot_deinit(struct apfl_expr_dot *dot)
{
GENERIC_DOT_DEINIT(dot, apfl_expr_deinit);
}
void
apfl_expr_at_deinit(struct apfl_expr_at *at)
{
DEINIT_GENERIC_LHS_RHS_EXPR(at, apfl_expr_deinit);
}
// Move functions
struct apfl_expr
apfl_expr_move(struct apfl_expr *in)
{
struct apfl_expr out = *in;
switch (in->type) {
case APFL_EXPR_LIST:
out.list = apfl_expr_list_move(&in->list);
break;
case APFL_EXPR_DICT:
out.dict = apfl_expr_dict_move(&in->dict);
break;
case APFL_EXPR_CALL:
out.call = apfl_expr_call_move(&in->call);
break;
case APFL_EXPR_SIMPLE_FUNC:
out.simple_func = apfl_expr_body_move(&in->simple_func);
break;
case APFL_EXPR_COMPLEX_FUNC:
out.complex_func = apfl_expr_complex_func_move(&in->complex_func);
break;
case APFL_EXPR_ASSIGNMENT:
out.assignment = apfl_expr_assignment_move(&in->assignment);
break;
case APFL_EXPR_DOT:
out.dot = apfl_expr_dot_move(&in->dot);
break;
case APFL_EXPR_AT:
out.at = apfl_expr_at_move(&in->at);
break;
case APFL_EXPR_CONSTANT:
out.constant = apfl_expr_const_move(&in->constant);
break;
case APFL_EXPR_VAR:
out.var = apfl_string_move(&in->var);
break;
}
return out;
}
#define MOVE_LIST(out, in, items, len) \
do { \
MOVEPTR(out.items, in->items); \
out.len = in->len; \
out.len = 0; \
} while (0)
struct apfl_expr_list
apfl_expr_list_move(struct apfl_expr_list *in)
{
struct apfl_expr_list out;
MOVE_LIST(out, in, items, len);
return out;
}
struct apfl_expr_list_item
apfl_expr_list_item_move(struct apfl_expr_list_item *in)
{
struct apfl_expr_list_item out = *in;
in->expr = NULL;
return out;
}
struct apfl_expr_dict_pair
apfl_expr_dict_pair_move(struct apfl_expr_dict_pair *in)
{
struct apfl_expr_dict_pair out = *in;
in->k = NULL;
in->v = NULL;
return out;
}
struct apfl_expr_dict
apfl_expr_dict_move(struct apfl_expr_dict *in)
{
struct apfl_expr_dict out;
MOVE_LIST(out, in, items, len);
return out;
}
struct apfl_expr_call
apfl_expr_call_move(struct apfl_expr_call *in)
{
struct apfl_expr_call out;
MOVEPTR(out.callee, in->callee);
out.arguments = apfl_expr_list_move(&in->arguments);
return out;
}
struct apfl_expr_body
apfl_expr_body_move(struct apfl_expr_body *in)
{
struct apfl_expr_body out;
MOVE_LIST(out, in, items, len);
return out;
}
struct apfl_expr_const
apfl_expr_const_move(struct apfl_expr_const *in)
{
struct apfl_expr_const out = *in;
switch (in->type) {
case APFL_EXPR_CONST_NIL:
case APFL_EXPR_CONST_BOOLEAN:
case APFL_EXPR_CONST_NUMBER:
// nop
break;
case APFL_EXPR_CONST_STRING:
out.string = apfl_string_move(&in->string);
}
return out;
}
#define GENERIC_LHS_RHS_PTRS_MOVE(out, in) \
do { \
MOVEPTR(out.lhs, in->lhs); \
MOVEPTR(out.rhs, in->rhs); \
} while (0)
struct apfl_expr_param_predicate
apfl_expr_param_predicate_move(struct apfl_expr_param_predicate *in)
{
struct apfl_expr_param_predicate out;
GENERIC_LHS_RHS_PTRS_MOVE(out, in);
return out;
}
struct apfl_expr_param_list
apfl_expr_param_list_move(struct apfl_expr_param_list *in)
{
struct apfl_expr_param_list out;
MOVE_LIST(out, in, children, len);
return out;
}
struct apfl_expr_params
apfl_expr_params_move(struct apfl_expr_params *in)
{
struct apfl_expr_params out;
MOVE_LIST(out, in, params, len);
return out;
}
struct apfl_expr_param
apfl_expr_param_move(struct apfl_expr_param *in)
{
struct apfl_expr_param out = *in;
switch (in->type) {
case APFL_EXPR_PARAM_VAR:
out.var = apfl_string_move(&in->var);
break;
case APFL_EXPR_PARAM_CONSTANT:
out.constant = apfl_expr_const_move(&in->constant);
break;
case APFL_EXPR_PARAM_PREDICATE:
out.predicate = apfl_expr_param_predicate_move(&in->predicate);
break;
case APFL_EXPR_PARAM_EXPAND:
MOVEPTR(out.expand, in->expand);
break;
case APFL_EXPR_PARAM_LIST:
out.list = apfl_expr_params_move(&in->list);
break;
}
return out;
}
struct apfl_expr_subfunc
apfl_expr_subfunc_move(struct apfl_expr_subfunc *in)
{
return (struct apfl_expr_subfunc) {
.params = apfl_expr_params_move(&in->params),
.body = apfl_expr_body_move(&in->body),
};
}
struct apfl_expr_complex_func
apfl_expr_complex_func_move(struct apfl_expr_complex_func *in)
{
struct apfl_expr_complex_func out;
MOVE_LIST(out, in, subfuncs, len);
return out;
}
struct apfl_expr_assignable_predicate
apfl_expr_assignable_predicate_move(struct apfl_expr_assignable_predicate *in)
{
struct apfl_expr_assignable_predicate out;
GENERIC_LHS_RHS_PTRS_MOVE(out, in);
return out;
}
#define GENERIC_DOT_MOVE(out, in) \
do { \
MOVEPTR(out.lhs, in->lhs); \
out.rhs = apfl_string_move(&in->rhs); \
} while (0)
struct apfl_expr_assignable_dot
apfl_expr_assignable_dot_move(struct apfl_expr_assignable_dot *in)
{
struct apfl_expr_assignable_dot out;
GENERIC_DOT_MOVE(out, in);
return out;
}
struct apfl_expr_assignable_at
apfl_expr_assignable_at_move(struct apfl_expr_assignable_at *in)
{
struct apfl_expr_assignable_at out;
GENERIC_LHS_RHS_PTRS_MOVE(out, in);
return out;
}
struct apfl_expr_assignable_list
apfl_expr_assignable_list_move(struct apfl_expr_assignable_list *in)
{
struct apfl_expr_assignable_list out;
MOVE_LIST(out, in, children, len);
return out;
}
struct apfl_expr_assignable
apfl_expr_assignable_move(struct apfl_expr_assignable *in)
{
struct apfl_expr_assignable out = *in;
switch (in->type) {
case APFL_EXPR_ASSIGNABLE_VAR:
out.var = apfl_string_move(&in->var);
break;
case APFL_EXPR_ASSIGNABLE_CONSTANT:
out.constant = apfl_expr_const_move(&in->constant);
break;
case APFL_EXPR_ASSIGNABLE_PREDICATE:
out.predicate = apfl_expr_assignable_predicate_move(&in->predicate);
break;
case APFL_EXPR_ASSIGNABLE_EXPAND:
MOVEPTR(out.expand, in->expand);
break;
case APFL_EXPR_ASSIGNABLE_DOT:
out.dot = apfl_expr_assignable_dot_move(&in->dot);
break;
case APFL_EXPR_ASSIGNABLE_AT:
out.at = apfl_expr_assignable_at_move(&in->at);
break;
case APFL_EXPR_ASSIGNABLE_LIST:
out.list = apfl_expr_assignable_list_move(&in->list);
break;
}
return out;
}
struct apfl_expr_assignment
apfl_expr_assignment_move(struct apfl_expr_assignment *in)
{
struct apfl_expr_assignment out = *in;
out.lhs = apfl_expr_assignable_move(&in->lhs);
MOVEPTR(out.rhs, in->rhs);
return out;
}
struct apfl_expr_dot
apfl_expr_dot_move(struct apfl_expr_dot *in)
{
struct apfl_expr_dot out;
GENERIC_DOT_MOVE(out, in);
return out;
}
struct apfl_expr_at
apfl_expr_at_move(struct apfl_expr_at *in)
{
struct apfl_expr_at out;
GENERIC_LHS_RHS_PTRS_MOVE(out, in);
return out;
}
static void
print_indented(unsigned indent, FILE *f, const char* fmt, ...)
{
va_list ap;
va_start(ap, fmt);
while (indent--) {
fputs(" ", f);
}
vfprintf(f, fmt, ap);
va_end(ap);
}
#define POSFMT "%d:%d"
#define POSARGS(p) (p).line, (p).col
static void print_expr(struct apfl_expr *expr, unsigned indent, FILE *f);
static void
print_expr_list(struct apfl_expr_list *list, unsigned indent, FILE *f)
{
for (size_t i = 0; i < list->len; i++) {
unsigned item_indent = indent;
if (list->items[i].expand) {
print_indented(indent, f, "Expand\n");
item_indent++;
}
print_expr(list->items[i].expr, item_indent, f);
}
}
static void
print_body(struct apfl_expr_body *body, unsigned indent, FILE *f)
{
for (size_t i = 0; i < body->len; i++) {
print_expr(&body->items[i], indent, f);
}
}
static void
print_constant(struct apfl_expr_const constant, unsigned indent, FILE *f)
{
switch (constant.type) {
case APFL_EXPR_CONST_NIL:
print_indented(indent, f, "Const (nil)\n");
break;
case APFL_EXPR_CONST_BOOLEAN:
print_indented(indent, f, "Const (%s)\n", constant.boolean ? "true" : "false");
break;
case APFL_EXPR_CONST_STRING:
print_indented(indent, f, "Const (" APFL_STR_FMT ")\n", APFL_STR_FMT_ARGS(constant.string));
break;
case APFL_EXPR_CONST_NUMBER:
print_indented(indent, f, "Const (%f)\n", constant.number);
break;
}
}
static void
print_param(struct apfl_expr_param *param, unsigned indent, FILE *f)
{
switch (param->type) {
case APFL_EXPR_PARAM_VAR:
print_indented(indent, f, "Var \"" APFL_STR_FMT "\"\n", APFL_STR_FMT_ARGS(param->var));
break;
case APFL_EXPR_PARAM_CONSTANT:
print_constant(param->constant, indent, f);
break;
case APFL_EXPR_PARAM_PREDICATE:
print_indented(indent, f, "Predicate\n");
print_indented(indent+1, f, "LHS\n");
print_param(param->predicate.lhs, indent+2, f);
print_indented(indent+1, f, "RHS\n");
print_expr(param->predicate.rhs, indent+2, f);
break;
case APFL_EXPR_PARAM_EXPAND:
print_indented(indent, f, "Expand\n");
print_param(param->expand, indent+1, f);
break;
case APFL_EXPR_PARAM_LIST:
print_indented(indent, f, "List\n");
for (size_t i = 0; i < param->list.len; i++) {
print_param(&param->list.params[i], indent+1, f);
}
break;
}
}
static void
print_assignable(struct apfl_expr_assignable assignable, unsigned indent, FILE *f)
{
switch(assignable.type) {
case APFL_EXPR_ASSIGNABLE_VAR:
print_indented(indent, f, "Var (" APFL_STR_FMT ")\n", APFL_STR_FMT_ARGS(assignable.var));
break;
case APFL_EXPR_ASSIGNABLE_CONSTANT:
print_constant(assignable.constant, indent, f);
break;
case APFL_EXPR_ASSIGNABLE_PREDICATE:
print_indented(indent, f, "Predicate\n");
print_indented(indent+1, f, "LHS\n");
print_assignable(*assignable.predicate.lhs, indent+2, f);
print_indented(indent+1, f, "RHS\n");
print_expr(assignable.predicate.rhs, indent+2, f);
break;
case APFL_EXPR_ASSIGNABLE_EXPAND:
print_indented(indent, f, "Expand\n");
print_assignable(*assignable.expand, indent+1, f);
break;
case APFL_EXPR_ASSIGNABLE_DOT:
print_indented(indent, f, "Dot (" APFL_STR_FMT ")\n", APFL_STR_FMT_ARGS(assignable.dot.rhs));
print_assignable(*assignable.dot.lhs, indent+1, f);
break;
case APFL_EXPR_ASSIGNABLE_AT:
print_indented(indent, f, "At\n");
print_indented(indent+1, f, "LHS\n");
print_assignable(*assignable.at.lhs, indent+2, f);
print_indented(indent+1, f, "RHS\n");
print_expr(assignable.at.rhs, indent+2, f);
break;
case APFL_EXPR_ASSIGNABLE_LIST:
print_indented(indent, f, "List\n");
for (size_t i = 0; i < assignable.list.len; i++) {
print_assignable(assignable.list.children[i], indent+1, f);
}
break;
}
}
static void
print_expr(struct apfl_expr *expr, unsigned indent, FILE *f)
{
switch (expr->type) {
case APFL_EXPR_LIST:
print_indented(indent, f, "List @ " POSFMT "\n", POSARGS(expr->position));
print_expr_list(&expr->list, indent+1, f);
break;
case APFL_EXPR_DICT:
print_indented(indent, f, "Dict @ " POSFMT "\n", POSARGS(expr->position));
for (size_t i = 0; i < expr->dict.len; i++) {
print_indented(indent+1, f, "Dict item\n");
print_indented(indent+2, f, "Key\n");
print_expr(expr->dict.items[i].k, indent+3, f);
print_indented(indent+2, f, "Value\n");
print_expr(expr->dict.items[i].v, indent+3, f);
}
break;
case APFL_EXPR_CALL:
print_indented(indent, f, "Call @ " POSFMT "\n", POSARGS(expr->position));
print_indented(indent+1, f, "Callee\n");
print_expr(expr->call.callee, indent+2, f);
print_indented(indent+1, f, "Args\n");
print_expr_list(&expr->call.arguments, indent+2, f);
break;
case APFL_EXPR_SIMPLE_FUNC:
print_indented(indent, f, "Simple function @ " POSFMT "\n", POSARGS(expr->position));
print_body(&expr->simple_func, indent+1, f);
break;
case APFL_EXPR_COMPLEX_FUNC:
print_indented(indent, f, "Complex function @ " POSFMT "\n", POSARGS(expr->position));
for (size_t i = 0; i < expr->complex_func.len; i++) {
struct apfl_expr_subfunc *sub = &expr->complex_func.subfuncs[i];
print_indented(indent+1, f, "Parameters\n");
for (size_t j = 0; j < sub->params.len; j++) {
print_param(&sub->params.params[j], indent+2, f);
}
print_indented(indent+1, f, "Body\n");
print_body(&sub->body, indent+2, f);
}
break;
case APFL_EXPR_ASSIGNMENT:
print_indented(indent, f, "Assignment");
print_indented(indent+1, f, "LHS");
print_assignable(expr->assignment.lhs, indent+2, f);
print_indented(indent+1, f, "RHS");
print_expr(expr->assignment.rhs, indent+2, f);
break;
case APFL_EXPR_DOT:
print_indented(indent, f, "Dot (" APFL_STR_FMT ")\n", APFL_STR_FMT_ARGS(expr->dot.rhs));
print_expr(expr->dot.lhs, indent+1, f);
break;
case APFL_EXPR_AT:
print_indented(indent, f, "At\n");
print_indented(indent+1, f, "LHS\n");
print_expr(expr->at.lhs, indent+2, f);
print_indented(indent+1, f, "RHS\n");
print_expr(expr->at.rhs, indent+2, f);
break;
case APFL_EXPR_CONSTANT:
print_constant(expr->constant, indent, f);
break;
case APFL_EXPR_VAR:
print_indented(indent, f, "Var (" APFL_STR_FMT ")\n", APFL_STR_FMT_ARGS(expr->var));
break;
}
}
void
apfl_expr_print(struct apfl_expr expr, FILE *f)
{
print_expr(&expr, 0, f);
}
static bool
expr_list_eq(struct apfl_expr_list a, struct apfl_expr_list b)
{
if (a.len != b.len) {
return false;
}
for (size_t i = 0; i < a.len; i++) {
if (
a.items[i].expand != b.items[i].expand
|| !apfl_expr_eq(*a.items[i].expr, *b.items[i].expr)
) {
return false;
}
}
return true;
}
static bool
body_eq(struct apfl_expr_body a, struct apfl_expr_body b)
{
if (a.len != b.len) {
return false;
}
for (size_t i = 0; i < a.len; i++) {
if (!apfl_expr_eq(a.items[i], b.items[i])) {
return false;
}
}
return true;
}
static bool param_eq(struct apfl_expr_param, struct apfl_expr_param);
static bool
params_eq(struct apfl_expr_params a, struct apfl_expr_params b)
{
if (a.len != b.len) {
return false;
}
for (size_t i = 0; i < a.len; i++) {
if (!param_eq(a.params[i], b.params[i])) {
return false;
}
}
return true;
}
static bool
const_eq(struct apfl_expr_const a, struct apfl_expr_const b)
{
if (a.type != b.type) {
return false;
}
switch (a.type) {
case APFL_EXPR_CONST_NIL:
return true;
case APFL_EXPR_CONST_BOOLEAN:
return a.boolean == b.boolean;
case APFL_EXPR_CONST_STRING:
return apfl_string_cmp(a.string, b.string) == 0;
case APFL_EXPR_CONST_NUMBER:
return a.number == b.number;
}
assert(false);
return false;
}
static bool
param_eq(struct apfl_expr_param a, struct apfl_expr_param b)
{
if (a.type != b.type) {
return false;
}
switch (a.type) {
case APFL_EXPR_PARAM_VAR:
return apfl_string_cmp(a.var, b.var) == 0;
case APFL_EXPR_PARAM_CONSTANT:
return const_eq(a.constant, b.constant);
case APFL_EXPR_PARAM_PREDICATE:
return param_eq(*a.predicate.lhs, *b.predicate.lhs)
&& apfl_expr_eq(*a.predicate.rhs, *b.predicate.rhs);
case APFL_EXPR_PARAM_EXPAND:
return param_eq(*a.expand, *b.expand);
case APFL_EXPR_PARAM_LIST:
return params_eq(a.list, b.list);
}
assert(false);
return false;
}
static bool
assignable_eq(struct apfl_expr_assignable a, struct apfl_expr_assignable b)
{
if (a.type != b.type) {
return false;
}
switch (a.type) {
case APFL_EXPR_ASSIGNABLE_VAR:
return apfl_string_cmp(a.var, b.var) == 0;
case APFL_EXPR_ASSIGNABLE_CONSTANT:
return const_eq(a.constant, b.constant);
case APFL_EXPR_ASSIGNABLE_PREDICATE:
return assignable_eq(*a.predicate.lhs, *b.predicate.lhs)
&& apfl_expr_eq(*a.predicate.rhs, *b.predicate.rhs);
case APFL_EXPR_ASSIGNABLE_EXPAND:
return assignable_eq(*a.expand, *b.expand);
case APFL_EXPR_ASSIGNABLE_DOT:
return assignable_eq(*a.dot.lhs, *b.dot.lhs)
&& apfl_string_cmp(a.dot.rhs, b.dot.rhs) == 0;
case APFL_EXPR_ASSIGNABLE_AT:
return assignable_eq(*a.at.lhs, *b.at.lhs)
&& apfl_expr_eq(*a.at.rhs, *b.at.rhs);
case APFL_EXPR_ASSIGNABLE_LIST:
if (a.list.len != b.list.len) {
return false;
}
for (size_t i = 0; i < a.list.len; i++) {
if (!assignable_eq(a.list.children[i], b.list.children[i])) {
return false;
}
}
return true;
}
assert(false);
return false;
}
bool
apfl_expr_eq(struct apfl_expr a, struct apfl_expr b)
{
if (a.type != b.type) {
return false;
}
if (apfl_position_eq(a.position, b.position)) {
return false;
}
switch (a.type) {
case APFL_EXPR_LIST:
return expr_list_eq(a.list, b.list);
case APFL_EXPR_DICT:
if (a.dict.len != b.dict.len) {
return false;
}
for (size_t i = 0; i < a.dict.len; i++) {
if (
!apfl_expr_eq(*a.dict.items[i].k, *b.dict.items[i].k)
|| !apfl_expr_eq(*a.dict.items[i].v, *b.dict.items[i].v)
) {
return false;
}
}
return true;
case APFL_EXPR_CALL:
return apfl_expr_eq(*a.call.callee, *b.call.callee)
&& expr_list_eq(a.call.arguments, b.call.arguments);
case APFL_EXPR_SIMPLE_FUNC:
return body_eq(a.simple_func, b.simple_func);
case APFL_EXPR_COMPLEX_FUNC:
if (a.complex_func.len != b.complex_func.len) {
return false;
}
for (size_t i = 0; i < a.complex_func.len; i++) {
if (
!params_eq(a.complex_func.subfuncs[i].params, b.complex_func.subfuncs[i].params)
|| !body_eq(a.complex_func.subfuncs[i].body, b.complex_func.subfuncs[i].body)
) {
return false;
}
}
return true;
case APFL_EXPR_ASSIGNMENT:
return assignable_eq(a.assignment.lhs, b.assignment.lhs)
&& apfl_expr_eq(*a.assignment.rhs, *b.assignment.rhs);
case APFL_EXPR_DOT:
return apfl_expr_eq(*a.dot.lhs, *b.dot.lhs)
&& apfl_string_cmp(a.dot.rhs, b.dot.rhs) == 0;
case APFL_EXPR_AT:
return apfl_expr_eq(*a.at.lhs, *b.at.lhs)
&& apfl_expr_eq(*a.at.rhs, *b.at.rhs);
case APFL_EXPR_CONSTANT:
return const_eq(a.constant, b.constant);
case APFL_EXPR_VAR:
return apfl_string_cmp(a.var, b.var) == 0;
}
assert(false);
return false;
}

406
src/hashmap.c Normal file
View file

@ -0,0 +1,406 @@
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "hashmap.h"
#include "resizable.h"
struct bucket {
void *keys;
void *values;
size_t len;
size_t cap;
};
struct apfl_hashmap_struct {
struct apfl_hashmap_callbacks callbacks;
size_t keysize;
size_t valsize;
size_t nbuckets;
struct bucket *buckets;
};
struct apfl_hashmap_cursor_struct {
apfl_hashmap map;
size_t bucket;
size_t i;
};
#define FNV_PRIME 1099511628211U
apfl_hash
apfl_hash_fnv1a_add(const void *data, size_t len, apfl_hash hash)
{
for (size_t i = 0; i < len; i++) {
uint8_t byte = ((uint8_t *)data)[i];
hash ^= byte;
hash *= FNV_PRIME;
}
return hash;
}
apfl_hash
apfl_hash_fnv1a(const void *data, size_t len)
{
return apfl_hash_fnv1a_add(data, len, APFL_HASH_FNV1A_INIT);
}
#define HAS_CALLBACK(map, cb) ((map)->callbacks.cb != NULL)
#define INVOKE_CALLBACK(map, cb, ...) (map)->callbacks.cb((map)->callbacks.opaque, __VA_ARGS__)
static bool
keys_eq(apfl_hashmap map, const void *a, const void *b)
{
if (HAS_CALLBACK(map, keys_eq)) {
return INVOKE_CALLBACK(map, keys_eq, a, b);
} else {
return memcmp(a, b, map->keysize) == 0;
}
}
static apfl_hash
calc_hash(apfl_hashmap map, const void *key)
{
if (HAS_CALLBACK(map, calc_hash)) {
return INVOKE_CALLBACK(map, calc_hash, key);
} else {
return apfl_hash_fnv1a(key, map->keysize);
}
}
static void
destroy_key(apfl_hashmap map, void *key)
{
if (HAS_CALLBACK(map, destroy_key)) {
INVOKE_CALLBACK(map, destroy_key, key);
}
}
static void
destroy_value(apfl_hashmap map, void *value)
{
if (HAS_CALLBACK(map, destroy_value)) {
INVOKE_CALLBACK(map, destroy_value, value);
}
}
static bool
copy_key(apfl_hashmap map, void *dest, const void *src)
{
if (HAS_CALLBACK(map, copy_key)) {
return INVOKE_CALLBACK(map, copy_key, dest, src);
} else {
memcpy(dest, src, map->keysize);
return true;
}
}
static bool
copy_value(apfl_hashmap map, void *dest, const void *src)
{
if (HAS_CALLBACK(map, copy_value)) {
return INVOKE_CALLBACK(map, copy_value, dest, src);
} else {
memcpy(dest, src, map->valsize);
return true;
}
}
#define CAP_GROW 5
static_assert(CAP_GROW >= 1, "CAP_GROW must be at least 1");
static size_t
calc_new_cap(size_t old_cap)
{
return old_cap + CAP_GROW;
}
#define KVADDR(base, elemsize, off) (((char*)(base)) + ((elemsize)*(off)))
static bool
find_key_in_bucket(apfl_hashmap map, struct bucket *bucket, const void *key, size_t *off)
{
size_t keysize = map->keysize;
for (size_t i = 0; i < bucket->len; i++) {
if (keys_eq(map, key, KVADDR(bucket->keys, keysize, i))) {
*off = i;
return true;
}
}
return false;
}
static bool
set_in_bucket(apfl_hashmap map, struct bucket *bucket, const void *key, const void *value)
{
size_t keysize = map->keysize;
size_t valsize = map->valsize;
size_t i;
if (find_key_in_bucket(map, bucket, key, &i)) {
void *dest = KVADDR(bucket->values, valsize, i);
destroy_value(map, dest);
return copy_value(map, dest, value);
}
if (bucket->len <= bucket->cap) {
size_t new_cap = calc_new_cap(bucket->cap);
void *newmem;
newmem = realloc(bucket->keys, new_cap * keysize);
if (newmem == NULL) {
return false;
}
bucket->keys = newmem;
newmem = realloc(bucket->values, new_cap * valsize);
if (newmem == NULL) {
return false;
}
bucket->values = newmem;
bucket->cap = new_cap;
}
if (!copy_key(map, KVADDR(bucket->keys, keysize, bucket->len), key)) {
return false;
}
if (!copy_value(map, KVADDR(bucket->values, valsize, bucket->len), value)) {
destroy_key(map, KVADDR(bucket->keys, keysize, bucket->len));
return false;
}
bucket->len++;
return true;
}
static bool
get_in_bucket(apfl_hashmap map, struct bucket *bucket, const void *key, void *value)
{
size_t i;
if (!find_key_in_bucket(map, bucket, key, &i)) {
return false;
}
if (value != NULL) {
size_t valsize = map->valsize;
if (!copy_value(map, value, KVADDR(bucket->values, valsize, i))) {
return false; // TODO: This way, we cant distinguish an error in copy_value from a non-set key
}
}
return true;
}
static struct bucket *
bucket_by_key(apfl_hashmap map, const void *key)
{
apfl_hash hash = calc_hash(map, key);
return &map->buckets[hash % map->nbuckets];
}
static void
delete_in_bucket(apfl_hashmap map, struct bucket *bucket, const void *key)
{
size_t i;
if (!find_key_in_bucket(map, bucket, key, &i)) {
return;
}
size_t keysize = map->keysize;
size_t valsize = map->valsize;
destroy_key(map, KVADDR(bucket->keys, keysize, i));
destroy_value(map, KVADDR(bucket->values, valsize, i));
assert(bucket->len >= (i+1));
memmove(
KVADDR(bucket->keys, keysize, i),
KVADDR(bucket->keys, keysize, i+1),
(bucket->len - (i+1)) * keysize
);
memmove(
KVADDR(bucket->values, valsize, i),
KVADDR(bucket->values, valsize, i+1),
(bucket->len - (i+1)) * valsize
);
assert(bucket->len > 0); // if len == 0, we would not have found an entry
bucket->len--;
}
#define INITIAL_NBUCKETS 16 // Must be a power of 2
apfl_hashmap
apfl_hashmap_new(struct apfl_hashmap_callbacks callbacks, size_t keysize, size_t valsize)
{
apfl_hashmap map = malloc(sizeof(struct apfl_hashmap_struct));
if (map == NULL) {
goto fail;
}
map->callbacks = callbacks;
map->keysize = keysize;
map->valsize = valsize;
map->nbuckets = INITIAL_NBUCKETS;
map->buckets = malloc(sizeof(struct bucket) * INITIAL_NBUCKETS);
if (map->buckets == NULL) {
goto fail;
}
for (size_t i = 0; i < INITIAL_NBUCKETS; i++) {
map->buckets[i] = (struct bucket) {
.keys = NULL,
.values = NULL,
.len = 0,
.cap = 0,
};
}
return map;
fail:
free(map);
return NULL;
}
void
apfl_hashmap_delete(apfl_hashmap map, const void *key)
{
delete_in_bucket(map, bucket_by_key(map, key), key);
}
bool
apfl_hashmap_get(apfl_hashmap map, const void *key, void *value)
{
return get_in_bucket(map, bucket_by_key(map, key), key, value);
}
bool
apfl_hashmap_set(apfl_hashmap map, const void *key, const void *value)
{
return set_in_bucket(map, bucket_by_key(map, key), key, value);
}
static void
destroy_bucket(apfl_hashmap map, struct bucket *bucket)
{
for (size_t i = 0; i < bucket->len; i++) {
destroy_key(map, KVADDR(bucket->keys, map->keysize, i));
destroy_value(map, KVADDR(bucket->values, map->valsize, i));
}
free(bucket->keys);
free(bucket->values);
bucket->len = 0;
bucket->cap = 0;
}
void
apfl_hashmap_destroy(apfl_hashmap map)
{
if (map == NULL) {
return;
}
if (map->buckets != NULL) {
for (size_t i = 0; i < map->nbuckets; i++) {
destroy_bucket(map, &map->buckets[i]);
}
free(map->buckets);
}
free(map);
}
static void
cursor_skip_empty_buckets(apfl_hashmap_cursor cur)
{
apfl_hashmap map = cur->map;
while (cur->bucket < map->nbuckets && map->buckets[cur->bucket].len == 0) {
cur->bucket++;
}
}
apfl_hashmap_cursor
apfl_hashmap_get_cursor(apfl_hashmap map)
{
apfl_hashmap_cursor cursor = malloc(sizeof(struct apfl_hashmap_cursor_struct));
if (cursor != NULL) {
cursor->map = map;
cursor->i = 0;
cursor->bucket = 0;
cursor_skip_empty_buckets(cursor);
}
return cursor;
}
bool
apfl_hashmap_cursor_is_end(apfl_hashmap_cursor cursor)
{
return cursor->bucket >= cursor->map->nbuckets;
}
static struct bucket *
cursor_get_bucket(apfl_hashmap_cursor cursor)
{
return apfl_hashmap_cursor_is_end(cursor)
? NULL
: &cursor->map->buckets[cursor->bucket];
}
void
apfl_hashmap_cursor_next(apfl_hashmap_cursor cursor)
{
struct bucket *bucket = cursor_get_bucket(cursor);
if (bucket == NULL) {
return; // End already reached
}
cursor->i++;
if (cursor->i < bucket->len) {
return;
}
cursor->bucket++;
cursor->i = 0;
cursor_skip_empty_buckets(cursor);
}
#define CURSOR_GET(cursor, out, bucketmemb, sizememb, copy) \
struct bucket *bucket = cursor_get_bucket(cursor); \
\
if (bucket == NULL) { \
return false; /* End already reached */ \
} \
\
if (cursor->i >= bucket->len) { \
return false; \
} \
\
size_t size = cursor->map->sizememb; \
\
return copy( \
cursor->map, \
out, \
KVADDR(bucket->bucketmemb, size, bucket->len) \
); \
bool
apfl_hashmap_cursor_get_key(apfl_hashmap_cursor cursor, void *key)
{
CURSOR_GET(cursor, key, keys, keysize, copy_key)
}
bool
apfl_hashmap_cursor_get_value(apfl_hashmap_cursor cursor, void *value)
{
CURSOR_GET(cursor, value, values, valsize, copy_value)
}

69
src/hashmap.h Normal file
View file

@ -0,0 +1,69 @@
#ifndef APFL_HASHMAP_H
#define APFL_HASHMAP_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#define APFL_HASH_FNV1A_INIT 14695981039346656037U // offset_basis for 64bit FNV-1(a)
// TODO: <stdint.h> is not required to provide uint64_t
typedef uint64_t apfl_hash;
typedef struct apfl_hashmap_struct *apfl_hashmap;
typedef struct apfl_hashmap_cursor_struct *apfl_hashmap_cursor;
struct apfl_hashmap_callbacks {
void *opaque;
// Compare keys a and b. If not provided, they will be compared with memcmp
bool (*keys_eq) (void *opaque, const void *a, const void *b);
// Calculate a hash value of a key.
// If not provided, a hash will be calculated based on the bytes of the key.
apfl_hash (*calc_hash) (void *opaque, const void *key);
// Destroy a key. Does nothing, if not provided.
void (*destroy_key) (void *opaque, void *key);
// Destroy a value. Does nothing, if not provided.
void (*destroy_value)(void *opaque, void *key);
// Copies a key. Returns true on success, false on failure.
// If not provided, the bytes will be cpiled with memcpy.
bool (*copy_key) (void *opaque, void *dest, const void *src);
// Copies a value. Returns true on success, false on failure.
// If not provided, the bytes will be cpiled with memcpy.
bool (*copy_value) (void *opaque, void *dest, const void *src);
};
apfl_hash apfl_hash_fnv1a_add(const void *, size_t len, apfl_hash);
apfl_hash apfl_hash_fnv1a(const void *, size_t len);
apfl_hashmap apfl_hashmap_new(struct apfl_hashmap_callbacks, size_t keysize, size_t valsize);
void apfl_hashmap_delete(apfl_hashmap, const void *key);
bool apfl_hashmap_get(apfl_hashmap, const void *key, void *value);
bool apfl_hashmap_set(apfl_hashmap, const void *key, const void *value);
void apfl_hashmap_destroy(apfl_hashmap);
#define apfl_hashmap_isset(m, k) (apfl_hashmap_get((m), (k), NULL))
apfl_hashmap_cursor apfl_hashmap_get_cursor(apfl_hashmap);
bool apfl_hashmap_cursor_is_end(apfl_hashmap_cursor);
void apfl_hashmap_cursor_next(apfl_hashmap_cursor);
bool apfl_hashmap_cursor_get_key(apfl_hashmap_cursor, void *key);
bool apfl_hashmap_cursor_get_value(apfl_hashmap_cursor, void *value);
#define apfl_hashmap_cursor_destroy(cur) (free(cur))
#ifdef __cplusplus
}
#endif
#endif

142
src/hashmap_foo.c Normal file
View file

@ -0,0 +1,142 @@
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "hashmap.h"
bool keys_eq_impl(void *opaque, const void *_a, const void *_b)
{
(void)opaque;
const char * const *a = _a;
const char * const *b = _b;
return strcmp(*a, *b) == 0;
}
apfl_hash calc_hash_impl(void *opaque, const void *_key)
{
(void)opaque;
const char * const *key = _key;
return apfl_hash_fnv1a(key, strlen(*key));
}
void destroy_kv_impl(void *opaque, void *_data)
{
(void)opaque;
char **data = _data;
free(*data);
}
bool copy_kv_impl(void *opaque, void *_dest, const void *_src)
{
(void)opaque;
char **dest = _dest;
const char * const *src = _src;
*dest = malloc(strlen(*src) + 1);
if (*dest == NULL) {
return false;
}
strcpy(*dest, *src);
return true;
}
#define BUFSIZE 10000
int
main(int argc, char **argv)
{
(void)argc;
(void)argv;
struct apfl_hashmap_callbacks callbacks = {
.opaque = NULL,
.keys_eq = keys_eq_impl,
.calc_hash = calc_hash_impl,
.destroy_key = destroy_kv_impl,
.destroy_value = destroy_kv_impl,
.copy_key = copy_kv_impl,
.copy_value = copy_kv_impl,
};
apfl_hashmap map = apfl_hashmap_new(callbacks, sizeof(char *), sizeof(char *));
if (map == NULL) {
return 1;
}
char line[BUFSIZE];
char key[BUFSIZE];
char *keyp = &key[0];
char value[BUFSIZE];
char *valuep = &value[0];
char *retreived = NULL;
for (;;) {
if (fgets(line, BUFSIZE, stdin) == NULL) {
break;
}
char *tok = strtok(line, " ");
if (tok == NULL) {
continue;
}
if (strcmp(tok, "set") == 0) {
tok = strtok(NULL, " \n");
strcpy(key, tok);
tok = strtok(NULL, " \n");
strcpy(value, tok);
if (!apfl_hashmap_set(map, &keyp, &valuep)) {
return 2;
}
} else if (strcmp(tok, "get") == 0 ) {
tok = strtok(NULL, " \n");
strcpy(key, tok);
if (apfl_hashmap_get(map, &keyp, &retreived)) {
printf("%s => %s\n", key, retreived);
free(retreived);
continue;
}
} else if (strcmp(tok, "del") == 0 ) {
tok = strtok(NULL, " \n");
strcpy(key, tok);
apfl_hashmap_delete(map, &keyp);
} else if (strcmp(tok, "list") == 0) {
apfl_hashmap_cursor cur = apfl_hashmap_get_cursor(map);
if (cur == NULL) {
return 3;
}
for (; !apfl_hashmap_cursor_is_end(cur); apfl_hashmap_cursor_next(cur)) {
char *k = NULL;
char *v = NULL;
if (
apfl_hashmap_cursor_get_key(cur, &k)
&& apfl_hashmap_cursor_get_value(cur, &v)
) {
printf("%s => %s\n", k, v);
}
free(k);
free(v);
}
apfl_hashmap_cursor_destroy(cur);
}
}
apfl_hashmap_destroy(map);
return 0;
}

45
src/internal.h Normal file
View file

@ -0,0 +1,45 @@
#ifndef APFL_INTERNAL_H
#define APFL_INTERNAL_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdlib.h>
#include "common.h"
#define DEINIT_LIST(items, len, item_deinit) \
do { \
for (size_t i = 0; i < (len); i++) { \
item_deinit(&((items)[i])); \
} \
len = 0; \
free(items); \
(items) = NULL; \
} while(0)
#define MOVEPTR(out, in) \
do { \
out = in; \
in = NULL; \
} while(0)
// ALLOC_LIST allocates memory for a list of n values of type T.
// n == 0 will always result in NULL (not guaranteed by calloc()) and the
// result will be cast into a pointer to T (this way the compiler can warn us,
// if we try to allocate memory for a wrong type). Also if we always use
// calloc(), the allocated memory is in a defined state.
#define ALLOC_LIST(T, n) (T *)((n) == 0 ? NULL : calloc((n), sizeof(T)))
#define ALLOC(T) ALLOC_LIST(T, 1)
// Aliases to commonly used functions / macros
#define DESTROY APFL_DESTROY
#ifdef __cplusplus
}
#endif
#endif

74
src/main.c Normal file
View file

@ -0,0 +1,74 @@
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include "apfl.h"
static bool
repl_source_reader(void *context, char *buf, size_t *len, bool need)
{
(void)context;
printf(need ? "... " : "> ");
fflush(stdout);
size_t maxlen = *len;
if (fgets(buf, maxlen, stdin) == NULL) {
if (feof(stdin)) {
*len = 0;
return true;
} else {
return false;
}
}
*len = strlen(buf);
return true;
}
int
main(int argc, const char **argv)
{
(void)argc;
(void)argv;
int rv = 0;
apfl_tokenizer_ptr tokenizer = NULL;
if (!(tokenizer = apfl_tokenizer_new(repl_source_reader, NULL))) {
fprintf(stderr, "Failed initializing tokenizer\n");
goto exit;
}
while (true) {
struct apfl_error err;
struct apfl_token token;
switch (apfl_tokenizer_next(tokenizer, false)) {
case APFL_PARSE_OK:
token = apfl_tokenizer_get_token(tokenizer);
apfl_token_print(token, stdout);
apfl_token_deinit(&token);
break;
case APFL_PARSE_EOF:
goto exit;
case APFL_PARSE_ERROR:
err = apfl_tokenizer_get_error(tokenizer);
apfl_error_print(err, stderr);
if (APFL_ERROR_IS_FATAL(err)) {
rv = 1;
goto exit;
}
break;
}
}
exit:
apfl_tokenizer_destroy(tokenizer);
return rv;
}

1717
src/parser.c Normal file

File diff suppressed because it is too large Load diff

9
src/position.c Normal file
View file

@ -0,0 +1,9 @@
#include <stdbool.h>
#include "apfl.h"
bool
apfl_position_eq(struct apfl_position a, struct apfl_position b)
{
return a.line == b.line && a.col == b.col;
}

74
src/resizable.c Normal file
View file

@ -0,0 +1,74 @@
#include <assert.h>
#include <stdlib.h>
#include <stddef.h>
#include <stdbool.h>
#include <string.h>
#include "resizable.h"
void
apfl_resizable_init(void **mem, size_t *len, size_t *cap)
{
*mem = NULL;
*len = 0;
*cap = 0;
}
bool
apfl_resizable_resize(size_t elem_size, void **mem, size_t *len, size_t *cap, size_t newlen)
{
// TODO: We're wasteful here by never actually shrinking the memory.
if (newlen <= *len || newlen < *cap) {
*len = newlen;
return true;
}
assert(newlen >= *cap);
if (!apfl_resizable_grow_cap(elem_size, mem, len, cap, newlen - *cap)) {
return false;
}
*len = newlen;
return true;
}
bool
apfl_resizable_grow_cap(size_t elem_size, void **mem, size_t *len, size_t *cap, size_t inc_cap)
{
(void)len;// \mystuff\TODO:better not even have the arg
if (inc_cap == 0) {
return true;
}
size_t newcap = *cap + elem_size * inc_cap;
// TODO: We currently simply grow the memory to have space for exactly
// inc_cap more elements. It would probably be smarter to grow the
// memory a bit larger to reduce calls to realloc.
void *newmem = realloc(*mem, newcap);
if (newmem == NULL) {
return false;
}
*mem = newmem;
*cap = newcap;
return true;
}
bool
apfl_resizable_append(size_t elem_size, void **mem, size_t *len, size_t *cap, const void *other_mem, size_t other_len)
{
size_t newlen = *len + other_len;
if (newlen > *cap) {
if (!apfl_resizable_grow_cap(elem_size, mem, len, cap, newlen - *cap)) {
return false;
}
}
memcpy(*((char**)mem) + (elem_size * *len), other_mem, other_len * elem_size);
*len += other_len;
return true;
}

22
src/resizable.h Normal file
View file

@ -0,0 +1,22 @@
#ifndef APFL_RESIZABLE
#define APFL_RESIZABLE 1
#include <stddef.h>
#include <stdbool.h>
#define APFL_RESIZABLE_TRAIT(T, N) \
T* N; \
size_t len; \
size_t cap;
#define APFL_RESIZABLE_ARGS(S, N) (void **)(&(S).N), &(S).len, &(S).cap
void apfl_resizable_init(void **mem, size_t *len, size_t *cap);
bool apfl_resizable_resize(size_t elem_size, void **mem, size_t *len, size_t *cap, size_t newlen);
bool apfl_resizable_grow_cap(size_t elem_size, void **mem, size_t *len, size_t *cap, size_t inc_cap);
bool apfl_resizable_append(size_t elem_size, void **mem, size_t *len, size_t *cap, const void *other_mem, size_t other_len);
#endif

134
src/strings.c Normal file
View file

@ -0,0 +1,134 @@
#include <stdbool.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include "apfl.h"
#include "resizable.h"
struct apfl_string_view
apfl_string_view_from_view(struct apfl_string_view view)
{
return view;
}
struct apfl_string_view
apfl_string_view_from_cstr(char *cstr)
{
return (struct apfl_string_view) {
.bytes = cstr,
.len = strlen(cstr),
};
}
struct apfl_string_view
apfl_string_view_from_const_cstr(const char *cstr)
{
return (struct apfl_string_view) {
.bytes = cstr,
.len = strlen(cstr),
};
}
struct apfl_string_view
apfl_string_view_from_string(struct apfl_string string)
{
return (struct apfl_string_view) {
.bytes = string.bytes,
.len = string.len,
};
}
int
apfl_string_view_cmp(struct apfl_string_view a, struct apfl_string_view b)
{
size_t n = a.len > b.len ? b.len : a.len;
int cmp = memcmp(a.bytes, b.bytes, n);
if (cmp != 0) {
return cmp;
}
if (a.len == b.len) {
return 0;
}
return a.len > b.len ? 1 : -1;
}
void
apfl_string_deinit(struct apfl_string *string)
{
free(string->bytes);
string->len = 0;
string->bytes = NULL;
}
bool
apfl_string_copy(struct apfl_string *dst, struct apfl_string_view src)
{
apfl_string_deinit(dst);
if ((dst->bytes = malloc(src.len)) == NULL) {
return false;
}
memcpy(dst->bytes, src.bytes, src.len);
dst->len = src.len;
return true;
}
struct apfl_string
apfl_string_move(struct apfl_string *src)
{
struct apfl_string out = *src;
src->bytes = NULL;
return out;
}
void
apfl_string_builder_init(struct apfl_string_builder *builder)
{
apfl_resizable_init((void **)&(builder->bytes), &(builder->len), &(builder->cap));
}
void
apfl_string_builder_deinit(struct apfl_string_builder *builder)
{
free(builder->bytes);
apfl_string_builder_init(builder);
}
static bool
append_bytes(struct apfl_string_builder *builder, const char *bytes, size_t len)
{
return apfl_resizable_append(
sizeof(char),
APFL_RESIZABLE_ARGS(*builder, bytes),
bytes,
len
);
}
bool
apfl_string_builder_append(struct apfl_string_builder *builder, struct apfl_string_view view)
{
return append_bytes(builder, view.bytes, view.len);
}
bool
apfl_string_builder_append_byte(struct apfl_string_builder *builder, char byte)
{
return append_bytes(builder, &byte, 1);
}
struct apfl_string
apfl_string_builder_move_string(struct apfl_string_builder *builder)
{
struct apfl_string str;
str.bytes = builder->bytes;
str.len = builder->len;
apfl_string_builder_init(builder);
return str;
}

149
src/test.h Normal file
View file

@ -0,0 +1,149 @@
#ifndef APFL_TEST_H
#define APFL_TEST_H
#ifdef __cplusplus
extern "C" {
#endif
#include <setjmp.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
struct testctx_struct {
jmp_buf *fataljmp;
const char *name;
const char *prefix;
bool ok;
};
typedef struct testctx_struct *testctx;
typedef void (*testfunc)(testctx);
struct testdef {
const char *name;
testfunc func;
};
void
test_fatal(testctx t) {
t->ok = false;
longjmp(*(t->fataljmp), 1);
}
static void
test_vfailf(testctx t, const char* fmt, va_list varargs) {
t->ok = false;
char* newfmt = malloc(strlen(fmt) + strlen(t->prefix) + 2); // +2: newline + terminating zero byte
if(newfmt == NULL) {
fprintf(stderr, "Could not print failure message in test '%s': could not allocate memory.\n", t->name);
test_fatal(t);
}
strcpy(newfmt, t->prefix);
strcat(newfmt, fmt);
strcat(newfmt, "\n");
vfprintf(stderr, newfmt, varargs);
free(newfmt);
}
void
test_failf(testctx t, const char* fmt, ...) {
va_list varargs;
va_start(varargs, fmt);
test_vfailf(t, fmt, varargs);
va_end(varargs);
}
void
test_fatalf(testctx t, const char* fmt, ...) {
va_list varargs;
va_start(varargs, fmt);
test_vfailf(t, fmt, varargs);
va_end(varargs);
test_fatal(t);
}
#define TESTPREFIXSIZE 1024
bool
test_run_test(struct testdef test)
{
char* prefix = malloc(TESTPREFIXSIZE);
testctx t = malloc(sizeof(struct testctx_struct));
jmp_buf* here = malloc(sizeof(jmp_buf));
if(prefix == NULL || t == NULL) {
fprintf(stderr, "Could not execute test '%s': could not allocate memory.\n", test.name);
free(t);
free(prefix);
free(here);
return false;
}
snprintf(prefix, TESTPREFIXSIZE, "%s: ", test.name);
if(setjmp(*here) == 0) {
t->fataljmp = here;
t->name = test.name;
t->prefix = prefix;
t->ok = true;
test.func(t);
}
bool ok = t->ok;
free(prefix);
free(t);
free(here);
if(ok) {
printf("%s: \x1b[32mOK\x1b[0m\n", test.name);
} else {
printf("%s: \x1b[31mFAIL\x1b[0m\n", test.name);
}
return ok;
}
int
test_main(int argc, const char **argv, struct testdef tests[])
{
(void)argc;
(void)argv;
bool allok = true;
for (struct testdef *t = tests; t->name != NULL && t->func != NULL; t++) {
allok = test_run_test(*t) && allok;
}
return allok ? 0 : 1;
}
#define TESTS_BEGIN \
int main(int argc, const char **argv) \
{ \
return test_main(argc, argv, (struct testdef[]) { \
#define TESTS_END \
{NULL, NULL}, \
}); \
} \
#define ADDTEST(name) {#name, name##_test}
#define TEST(name, t) void name##_test(testctx t)
#ifdef __cplusplus
}
#endif
#endif

122
src/token.c Normal file
View file

@ -0,0 +1,122 @@
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include "apfl.h"
static bool
has_text_data(enum apfl_token_type type)
{
switch (type) {
case APFL_TOK_NAME:
case APFL_TOK_STRING:
case APFL_TOK_COMMENT:
return true;
default:
return false;
}
}
static bool
has_numeric_data(enum apfl_token_type type)
{
switch (type) {
case APFL_TOK_NUMBER:
return true;
default:
return false;
}
}
void
apfl_token_deinit(struct apfl_token *token)
{
if (has_text_data(token->type)) {
apfl_string_deinit(&token->text);
}
}
const char *
apfl_token_type_name(enum apfl_token_type type)
{
switch (type) {
case APFL_TOK_LPAREN:
return "(";
case APFL_TOK_RPAREN:
return ")";
case APFL_TOK_LBRACKET:
return "[";
case APFL_TOK_RBRACKET:
return "]";
case APFL_TOK_LBRACE:
return "{";
case APFL_TOK_RBRACE:
return "}";
case APFL_TOK_MAPSTO:
return "->";
case APFL_TOK_EXPAND:
return "~";
case APFL_TOK_DOT:
return ".";
case APFL_TOK_AT:
return "@";
case APFL_TOK_SEMICOLON:
return ";";
case APFL_TOK_LINEBREAK:
return "LINEBREAK";
case APFL_TOK_CONTINUE_LINE:
return "\\";
case APFL_TOK_COMMENT:
return "COMMENT";
case APFL_TOK_COMMA:
return ",";
case APFL_TOK_QUESTION_MARK:
return "?";
case APFL_TOK_STRINGIFY:
return "'";
case APFL_TOK_ASSIGN:
return "=";
case APFL_TOK_LOCAL_ASSIGN:
return ":=";
case APFL_TOK_NUMBER:
return "NUMBER";
case APFL_TOK_NAME:
return "NAME";
case APFL_TOK_STRING:
return "STRING";
}
return "(unknown token)";
}
void
apfl_token_print(struct apfl_token token, FILE *file)
{
if (has_text_data(token.type)) {
fprintf(
file,
"%s (" APFL_STR_FMT ") @ (%d:%d)\n",
apfl_token_type_name(token.type),
APFL_STR_FMT_ARGS(token.text),
token.position.line,
token.position.col
);
} else if (has_numeric_data(token.type)) {
fprintf(
file,
"%s (%f) @ (%d:%d)\n",
apfl_token_type_name(token.type),
token.number,
token.position.line,
token.position.col
);
} else {
fprintf(
file,
"%s @ (%d:%d)\n",
apfl_token_type_name(token.type),
token.position.line,
token.position.col
);
}
}

909
src/tokenizer.c Normal file
View file

@ -0,0 +1,909 @@
#include <assert.h>
#include <ctype.h>
#include <limits.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include "apfl.h"
#define BUFSIZE 4096
typedef int buf_offset;
static_assert(INT_MAX >= BUFSIZE, "BUFSIZE is too large for type buf_offset");
struct apfl_tokenizer {
apfl_source_reader_cb source_reader;
void *source_reader_context;
char *buf;
buf_offset buf_pos;
buf_offset buf_len;
enum {
NM_REGULAR,
NM_NEGATIVE_NUMBER,
NM_MAPSTO,
NM_ASSIGN,
NM_EOF,
} next_mode;
struct apfl_position pos_for_mapsto;
char first_digit_for_negative_number;
struct apfl_position position;
bool last_byte_was_linebreak;
union {
struct apfl_token token;
struct apfl_error error;
};
};
apfl_tokenizer_ptr
apfl_tokenizer_new(apfl_source_reader_cb source_reader, void *context)
{
apfl_tokenizer_ptr tokenizer = malloc(sizeof(struct apfl_tokenizer));
if (tokenizer == NULL) {
return NULL;
}
tokenizer->source_reader = source_reader;
tokenizer->source_reader_context = context;
if ((tokenizer->buf = malloc(BUFSIZE)) == NULL) {
free(tokenizer);
return NULL;
}
tokenizer->buf_pos = 0;
tokenizer->buf_len = 0;
tokenizer->position = (struct apfl_position) {
.line = 1,
.col = 0, // The first character was not yet read
};
tokenizer->last_byte_was_linebreak = false;
tokenizer->next_mode = NM_REGULAR;
return tokenizer;
}
void
apfl_tokenizer_destroy(apfl_tokenizer_ptr tokenizer)
{
if (tokenizer == NULL) {
return;
}
free(tokenizer->buf);
free(tokenizer);
}
struct apfl_token
apfl_tokenizer_get_token(apfl_tokenizer_ptr tokenizer)
{
return tokenizer->token;
}
struct apfl_error
apfl_tokenizer_get_error(apfl_tokenizer_ptr tokenizer)
{
return tokenizer->error;
}
enum read_result {
RR_OK,
RR_ERR,
RR_EOF,
};
static enum read_result
read_byte(apfl_tokenizer_ptr tokenizer, char *byte, bool need)
{
if (tokenizer->buf_pos >= tokenizer->buf_len) {
size_t len = BUFSIZE;
tokenizer->buf_pos = 0;
tokenizer->buf_len = 0;
if (!tokenizer->source_reader(tokenizer->source_reader_context, tokenizer->buf, &len, need)) {
tokenizer->error.type = APFL_ERR_INPUT_ERROR;
return RR_ERR;
}
tokenizer->buf_len = len;
if (len == 0) {
return RR_EOF;
}
}
if (tokenizer->last_byte_was_linebreak) {
tokenizer->position.line++;
tokenizer->position.col = 0;
}
*byte = tokenizer->buf[tokenizer->buf_pos];
tokenizer->buf_pos++;
tokenizer->last_byte_was_linebreak = (*byte == '\n');
tokenizer->position.col++;
return RR_OK;
}
// Only at most 1 unread_byte() call is allowed after a read_byte() call!
static void
unread_byte(apfl_tokenizer_ptr tokenizer, struct apfl_position pos)
{
tokenizer->position = pos;
tokenizer->buf_pos--;
tokenizer->last_byte_was_linebreak = false;
}
static enum apfl_parse_result
yield_simple_token(
apfl_tokenizer_ptr tokenizer,
enum apfl_token_type type,
struct apfl_position pos
) {
tokenizer->token.type = type;
tokenizer->token.position = pos;
return APFL_PARSE_OK;
}
static enum apfl_parse_result comment(apfl_tokenizer_ptr);
static enum apfl_parse_result colon(apfl_tokenizer_ptr);
static enum apfl_parse_result string(apfl_tokenizer_ptr);
static enum apfl_parse_result maybe_name(apfl_tokenizer_ptr, bool, char);
static enum apfl_parse_result number(apfl_tokenizer_ptr, bool, struct apfl_position, char, bool);
enum apfl_parse_result
apfl_tokenizer_next(apfl_tokenizer_ptr tokenizer, bool need)
{
switch (tokenizer->next_mode) {
case NM_REGULAR:
break;
case NM_MAPSTO:
tokenizer->next_mode = NM_REGULAR;
return yield_simple_token(tokenizer, APFL_TOK_MAPSTO, tokenizer->pos_for_mapsto);
case NM_NEGATIVE_NUMBER:
tokenizer->next_mode = NM_REGULAR;
return number(tokenizer, need, tokenizer->position, tokenizer->first_digit_for_negative_number, true);
case NM_ASSIGN:
tokenizer->next_mode = NM_REGULAR;
return yield_simple_token(tokenizer, APFL_TOK_ASSIGN, tokenizer->position);
case NM_EOF:
return APFL_PARSE_EOF;
}
char byte;
for (;;) {
switch (read_byte(tokenizer, &byte, need)) {
case RR_OK:
break;
case RR_ERR:
return APFL_PARSE_ERROR;
case RR_EOF:
tokenizer->next_mode = NM_EOF;
return APFL_PARSE_EOF;
}
switch (byte) {
case '(':
return yield_simple_token(tokenizer, APFL_TOK_LPAREN, tokenizer->position);
case ')':
return yield_simple_token(tokenizer, APFL_TOK_RPAREN, tokenizer->position);
case '[':
return yield_simple_token(tokenizer, APFL_TOK_LBRACKET, tokenizer->position);
case ']':
return yield_simple_token(tokenizer, APFL_TOK_RBRACKET, tokenizer->position);
case '{':
return yield_simple_token(tokenizer, APFL_TOK_LBRACE, tokenizer->position);
case '}':
return yield_simple_token(tokenizer, APFL_TOK_RBRACE, tokenizer->position);
case '~':
return yield_simple_token(tokenizer, APFL_TOK_EXPAND, tokenizer->position);
case '.':
return yield_simple_token(tokenizer, APFL_TOK_DOT, tokenizer->position);
case '@':
return yield_simple_token(tokenizer, APFL_TOK_AT, tokenizer->position);
case ';':
return yield_simple_token(tokenizer, APFL_TOK_SEMICOLON, tokenizer->position);
case '\n':
return yield_simple_token(tokenizer, APFL_TOK_LINEBREAK, tokenizer->position);
case '\\':
return yield_simple_token(tokenizer, APFL_TOK_CONTINUE_LINE, tokenizer->position);
case ',':
return yield_simple_token(tokenizer, APFL_TOK_COMMA, tokenizer->position);
case '?':
return yield_simple_token(tokenizer, APFL_TOK_QUESTION_MARK, tokenizer->position);
case '\'':
return yield_simple_token(tokenizer, APFL_TOK_STRINGIFY, tokenizer->position);
case '#':
return comment(tokenizer);
case ':':
return colon(tokenizer);
case '"':
return string(tokenizer);
case ' ':
case '\r':
case '\t':
// Skip whitespace
break;
default:
if (isdigit(byte))
return number(tokenizer, need, tokenizer->position, byte, false);
else
return maybe_name(tokenizer, need, byte);
}
}
}
static enum apfl_parse_result
comment(apfl_tokenizer_ptr tokenizer)
{
char byte;
struct apfl_position pos = tokenizer->position;
struct apfl_position last_pos;
struct apfl_string_builder text;
apfl_string_builder_init(&text);
for (;;) {
last_pos = tokenizer->position;
switch (read_byte(tokenizer, &byte, true)) {
case RR_OK:
break;
case RR_ERR:
return APFL_PARSE_ERROR;
case RR_EOF:
tokenizer->next_mode = NM_EOF;
tokenizer->token = (struct apfl_token) {
.type = APFL_TOK_COMMENT,
.position = pos,
.text = apfl_string_builder_move_string(&text),
};
return APFL_PARSE_OK;
}
if (byte == '\n') {
unread_byte(tokenizer, last_pos);
tokenizer->token = (struct apfl_token) {
.type = APFL_TOK_COMMENT,
.position = pos,
.text = apfl_string_builder_move_string(&text),
};
return APFL_PARSE_OK;
}
if (!apfl_string_builder_append_byte(&text, byte)) {
tokenizer->error = apfl_error_simple(APFL_ERR_MALLOC_FAILED);
return APFL_PARSE_ERROR;
}
}
}
static enum apfl_parse_result
colon(apfl_tokenizer_ptr tokenizer)
{
char byte;
struct apfl_position pos = tokenizer->position;
switch (read_byte(tokenizer, &byte, true)) {
case RR_OK:
break;
case RR_ERR:
return APFL_PARSE_ERROR;
case RR_EOF:
tokenizer->next_mode = NM_EOF;
tokenizer->error = (struct apfl_error) { .type = APFL_ERR_UNEXPECTED_EOF };
return APFL_PARSE_ERROR;
}
if (byte != '=') {
tokenizer->error = (struct apfl_error) {
.type = APFL_ERR_EXPECTED_EQ_AFTER_COLON,
.position = tokenizer->position,
};
return APFL_PARSE_ERROR;
}
return yield_simple_token(tokenizer, APFL_TOK_LOCAL_ASSIGN, pos);
}
static enum apfl_parse_result
append_single_byte(
apfl_tokenizer_ptr tokenizer,
struct apfl_string_builder *text,
char byte
) {
if (!apfl_string_builder_append_byte(text, byte)) {
tokenizer->error = apfl_error_simple(APFL_ERR_MALLOC_FAILED);
return APFL_PARSE_ERROR;
}
return APFL_PARSE_OK;
}
static int
unhex(char byte)
{
switch (byte) {
case '0':
return 0x0;
case '1':
return 0x1;
case '2':
return 0x2;
case '3':
return 0x3;
case '4':
return 0x4;
case '5':
return 0x5;
case '6':
return 0x6;
case '7':
return 0x7;
case '8':
return 0x8;
case '9':
return 0x9;
case 'a':
case 'A':
return 0xA;
case 'b':
case 'B':
return 0xB;
case 'c':
case 'C':
return 0xC;
case 'd':
case 'D':
return 0xD;
case 'e':
case 'E':
return 0xE;
case 'f':
case 'F':
return 0xF;
}
return -1;
}
static int
undec(char byte)
{
switch (byte) {
case '0':
return 0;
case '1':
return 1;
case '2':
return 2;
case '3':
return 3;
case '4':
return 4;
case '5':
return 5;
case '6':
return 6;
case '7':
return 7;
case '8':
return 8;
case '9':
return 9;
}
return -1;
}
static int
unoct(char byte)
{
switch (byte) {
case '0':
return 0;
case '1':
return 1;
case '2':
return 2;
case '3':
return 3;
case '4':
return 4;
case '5':
return 5;
case '6':
return 6;
case '7':
return 7;
}
return -1;
}
static int
unbin(char byte)
{
switch (byte) {
case '0':
return 0;
case '1':
return 1;
}
return -1;
}
static enum apfl_parse_result
hex_escape(
apfl_tokenizer_ptr tokenizer,
struct apfl_string_builder *text
) {
char escaped_byte = 0;
for (int i = 0; i < 2; i++) {
char byte;
switch (read_byte(tokenizer, &byte, true)) {
case RR_OK:
break;
case RR_ERR:
return APFL_PARSE_ERROR;
case RR_EOF:
tokenizer->next_mode = NM_EOF;
tokenizer->error = (struct apfl_error) { .type = APFL_ERR_UNEXPECTED_EOF };
return APFL_PARSE_ERROR;
}
int nibble = unhex(byte);
if (nibble < 0) {
tokenizer->error = (struct apfl_error) {
.type = APFL_ERR_EXPECTED_HEX_IN_HEX_ESCAPE,
.position = tokenizer->position,
};
return APFL_PARSE_ERROR;
}
escaped_byte <<= 4;
escaped_byte |= 0xF & nibble;
}
return append_single_byte(tokenizer, text, escaped_byte);
}
static enum apfl_parse_result
escape_sequence(apfl_tokenizer_ptr tokenizer, struct apfl_string_builder *text)
{
struct apfl_position pos = tokenizer->position;
char byte;
switch (read_byte(tokenizer, &byte, true)) {
case RR_OK:
break;
case RR_ERR:
return APFL_PARSE_ERROR;
case RR_EOF:
tokenizer->next_mode = NM_EOF;
tokenizer->error = (struct apfl_error) { .type = APFL_ERR_UNEXPECTED_EOF };
return APFL_PARSE_ERROR;
}
switch (byte) {
case 'x':
case 'X':
return hex_escape(tokenizer, text);
// case 'u':
// case 'U':
// return unicode_escape(tokenizer, pos, text);
case '\\':
return append_single_byte(tokenizer, text, '\\');
case 'n':
return append_single_byte(tokenizer, text, '\n');
case 'r':
return append_single_byte(tokenizer, text, '\r');
case 't':
return append_single_byte(tokenizer, text, '\t');
case '"':
return append_single_byte(tokenizer, text, '"');
case '0':
return append_single_byte(tokenizer, text, 0);
default:
tokenizer->error = (struct apfl_error) {
.type = APFL_ERR_INVALID_ESCAPE_SEQUENCE,
.position = pos,
.byte = byte,
};
return APFL_PARSE_ERROR;
}
}
static enum apfl_parse_result
inner_string(apfl_tokenizer_ptr tokenizer, struct apfl_string_builder *text)
{
struct apfl_position pos = tokenizer->position;
char byte;
enum apfl_parse_result subresult;
for (;;) {
switch (read_byte(tokenizer, &byte, true)) {
case RR_OK:
break;
case RR_ERR:
return APFL_PARSE_ERROR;
case RR_EOF:
tokenizer->next_mode = NM_EOF;
tokenizer->error = (struct apfl_error) { .type = APFL_ERR_UNEXPECTED_EOF };
return APFL_PARSE_ERROR;
}
switch (byte) {
case '"':
tokenizer->token = (struct apfl_token) {
.type = APFL_TOK_STRING,
.position = pos,
.text = apfl_string_builder_move_string(text),
};
return APFL_PARSE_OK;
case '\\':
if ((subresult = escape_sequence(tokenizer, text)) != APFL_PARSE_OK) {
return subresult;
}
break;
default:
if (!apfl_string_builder_append_byte(text, byte)) {
tokenizer->error = apfl_error_simple(APFL_ERR_MALLOC_FAILED);
return APFL_PARSE_ERROR;
}
}
}
}
static enum apfl_parse_result
string(apfl_tokenizer_ptr tokenizer)
{
struct apfl_string_builder text;
apfl_string_builder_init(&text);
enum apfl_parse_result out = inner_string(tokenizer, &text);
apfl_string_builder_deinit(&text);
return out;
}
static enum apfl_parse_result
finalize_maybe_name(
apfl_tokenizer_ptr tokenizer,
struct apfl_string_builder *text,
struct apfl_position pos
) {
assert(text->len > 0);
if (text->len == 1 && text->bytes[0] == '=') {
tokenizer->token = (struct apfl_token) {
.type = APFL_TOK_ASSIGN,
.position = pos,
};
} else {
tokenizer->token = (struct apfl_token) {
.type = APFL_TOK_NAME,
.position = pos,
.text = apfl_string_builder_move_string(text),
};
}
return APFL_PARSE_OK;
}
static bool
is_word_byte(unsigned char byte)
{
return isalnum(byte) || byte > 0x7F;
}
static enum apfl_parse_result
maybe_name_inner(
apfl_tokenizer_ptr tokenizer,
bool need,
char byte,
struct apfl_string_builder *text
) {
struct apfl_position pos = tokenizer->position;
struct apfl_position last_pos;
char last_byte;
if (!apfl_string_builder_append_byte(text, byte)) {
tokenizer->error = apfl_error_simple(APFL_ERR_MALLOC_FAILED);
return APFL_PARSE_ERROR;
}
for (;;) {
last_byte = byte;
last_pos = tokenizer->position;
switch (read_byte(tokenizer, &byte, need)) {
case RR_OK:
break;
case RR_ERR:
return APFL_PARSE_ERROR;
case RR_EOF:
tokenizer->next_mode = NM_EOF;
return finalize_maybe_name(tokenizer, text, pos);
}
switch (byte) {
case '(':
case ')':
case '[':
case ']':
case '{':
case '}':
case '~':
case '.':
case '@':
case ';':
case '\n':
case '\\':
case ',':
case '?':
case '\'':
case '#':
case ':':
case '"':
case ' ':
case '\r':
case '\t':
unread_byte(tokenizer, last_pos);
return finalize_maybe_name(tokenizer, text, pos);
case '=':
if (is_word_byte(last_byte)) {
tokenizer->next_mode = NM_ASSIGN;
return finalize_maybe_name(tokenizer, text, pos);
}
break;
case '>':
if (last_byte == '-') {
text->len--; // This removes the '-' from the end of text
if (text->len == 0) {
return yield_simple_token(tokenizer, APFL_TOK_MAPSTO, last_pos);
}
tokenizer->next_mode = NM_MAPSTO;
tokenizer->pos_for_mapsto = last_pos;
return finalize_maybe_name(tokenizer, text, pos);
}
break;
default:
if (isdigit(byte) && last_byte == '-') {
text->len--; // This removes the '-' from the end of text
if (text->len == 0) {
return number(tokenizer, need, pos, byte, true);
}
tokenizer->next_mode = NM_NEGATIVE_NUMBER;
tokenizer->first_digit_for_negative_number = byte;
return finalize_maybe_name(tokenizer, text, pos);
}
break;
}
if (!apfl_string_builder_append_byte(text, byte)) {
tokenizer->error = apfl_error_simple(APFL_ERR_MALLOC_FAILED);
return APFL_PARSE_ERROR;
}
}
}
static enum apfl_parse_result
maybe_name(apfl_tokenizer_ptr tokenizer, bool need, char first_byte)
{
struct apfl_string_builder text;
apfl_string_builder_init(&text);
enum apfl_parse_result out = maybe_name_inner(tokenizer, need, first_byte, &text);
apfl_string_builder_deinit(&text);
return out;
}
static struct apfl_token
build_number_token(double number, struct apfl_position position, bool negative)
{
if (negative) {
number *= -1;
}
return (struct apfl_token) {
.type = APFL_TOK_NUMBER,
.position = position,
.number = (apfl_number)number,
};
}
static enum apfl_parse_result
non_decimal_number(
apfl_tokenizer_ptr tokenizer,
bool need,
struct apfl_position position,
bool negative,
int shift,
int (*byte_to_digit)(char))
{
struct apfl_position last_pos;
bool no_digits_yet = true;
char byte;
uint64_t num = 0;
for (;;) {
last_pos = tokenizer->position;
switch (read_byte(tokenizer, &byte, no_digits_yet || need)) {
case RR_OK:
break;
case RR_ERR:
return APFL_PARSE_ERROR;
case RR_EOF:
tokenizer->next_mode = NM_EOF;
if (no_digits_yet) {
tokenizer->error = (struct apfl_error) {
.type = APFL_ERR_UNEXPECTED_EOF,
};
return APFL_PARSE_ERROR;
} else {
tokenizer->token = build_number_token((double)num, position, negative);
return APFL_PARSE_OK;
}
}
int digit = byte_to_digit(byte);
if (digit >= 0) {
num <<= shift;
num |= digit;
no_digits_yet = false;
continue;
}
if (no_digits_yet) {
tokenizer->error = (struct apfl_error) {
.type = APFL_ERR_EXPECTED_DIGIT,
.position = tokenizer->position,
};
return APFL_PARSE_ERROR;
}
if (is_word_byte(byte)) {
tokenizer->error = (struct apfl_error) {
.type = APFL_ERR_UNEXPECTED_BYTE_IN_NUMBER,
.position = tokenizer->position,
.byte = byte,
};
return APFL_PARSE_ERROR;
}
unread_byte(tokenizer, last_pos);
tokenizer->token = build_number_token((double)num, position, negative);
return APFL_PARSE_OK;
}
}
#define BUILD_NON_DECIMAL_TOKENIZER(name, shift, byte_to_digit) \
static enum apfl_parse_result \
name( \
apfl_tokenizer_ptr tokenizer, \
bool need, \
struct apfl_position position, \
bool negative \
) { \
return non_decimal_number( \
tokenizer, \
need, \
position, \
negative, \
shift, \
byte_to_digit \
); \
}
BUILD_NON_DECIMAL_TOKENIZER(hex_number, 4, unhex)
BUILD_NON_DECIMAL_TOKENIZER(oct_number, 3, unoct)
BUILD_NON_DECIMAL_TOKENIZER(bin_number, 1, unbin)
static enum apfl_parse_result
number(
apfl_tokenizer_ptr tokenizer,
bool need,
struct apfl_position position,
char first_digit,
bool negative
) {
double num = (double)undec(first_digit);
double divider = 1;
bool first_iteration = true;
bool seen_dot = false;
struct apfl_position last_pos;
for (;; first_iteration = false) {
char byte;
last_pos = tokenizer->position;
switch (read_byte(tokenizer, &byte, need)) {
case RR_OK:
break;
case RR_ERR:
return APFL_PARSE_ERROR;
case RR_EOF:
tokenizer->next_mode = NM_EOF;
tokenizer->token = build_number_token(num / divider, position, negative);
return APFL_PARSE_OK;
}
if (first_iteration && first_digit == '0') {
switch (byte) {
case 'x':
case 'X':
return hex_number(tokenizer, need, position, negative);
case 'b':
case 'B':
return bin_number(tokenizer, need, position, negative);
case 'o':
case 'O':
return oct_number(tokenizer, need, position, negative);
}
}
int digit = undec(byte);
if (digit >= 0) {
num *= 10;
num += (double)digit;
if (seen_dot) {
divider *= 10;
}
continue;
}
if (byte == '.') {
if (seen_dot) {
unread_byte(tokenizer, last_pos);
tokenizer->token = build_number_token(num / divider, position, negative);
return APFL_PARSE_OK;
} else {
seen_dot = true;
continue;
}
}
if (is_word_byte(byte)) {
tokenizer->error = (struct apfl_error) {
.type = APFL_ERR_UNEXPECTED_BYTE_IN_NUMBER,
.position = tokenizer->position,
.byte = byte,
};
return APFL_PARSE_ERROR;
}
unread_byte(tokenizer, last_pos);
tokenizer->token = build_number_token(num / divider, position, negative);
return APFL_PARSE_OK;
}
}

284
src/tokenizer_test.c Normal file
View file

@ -0,0 +1,284 @@
#include <assert.h>
#include "apfl.h"
#include "test.h"
struct string_src_reader_ctx {
char *text;
char *remain_text;
size_t remain_len;
};
static void *
must_alloc(testctx t, size_t size)
{
void *out = malloc(size);
if (out == NULL) {
test_fatalf(t, "Failed allocating %d bytes of memory", size);
}
return out;
}
static bool
string_src_reader(void *_ctx, char *buf, size_t *len, bool need)
{
(void)need;
struct string_src_reader_ctx *ctx = _ctx;
size_t maxlen = *len;
*len = maxlen < ctx->remain_len ? maxlen : ctx->remain_len;
memcpy(buf, ctx->remain_text, *len);
ctx->remain_text += *len;
assert(*len <= ctx->remain_len);
ctx->remain_len -= *len;
return true;
}
struct tokenizer_test {
testctx t;
apfl_tokenizer_ptr tokenizer;
struct string_src_reader_ctx *ctx;
};
static struct tokenizer_test *
new_tokenizer_test(testctx t, const char *text)
{
struct string_src_reader_ctx *ctx = must_alloc(t, sizeof(struct string_src_reader_ctx));
ctx->remain_len = strlen(text);
ctx->text = must_alloc(t, ctx->remain_len + 1);
strcpy(ctx->text, text);
ctx->remain_text = ctx->text;
apfl_tokenizer_ptr tokenizer = apfl_tokenizer_new(string_src_reader, ctx);
if (tokenizer == NULL) {
test_fatalf(t, "Failed to initialize the tokenizer");
}
struct tokenizer_test *tt = must_alloc(t, sizeof(struct tokenizer_test));
*tt = (struct tokenizer_test) {
.t = t,
.tokenizer = tokenizer,
.ctx = ctx,
};
return tt;
}
static void
destroy_tokenizer_test(struct tokenizer_test *tt)
{
free(tt->ctx->text);
free(tt->ctx);
apfl_tokenizer_destroy(tt->tokenizer);
free(tt);
}
static void
expect_eof(struct tokenizer_test *tt)
{
switch (apfl_tokenizer_next(tt->tokenizer, false)) {
case APFL_PARSE_OK:
test_fatalf(tt->t, "Expected EOF but got a token");
break;
case APFL_PARSE_EOF:
break;
case APFL_PARSE_ERROR:
test_failf(tt->t, "Got an error instead of an EOF");
apfl_error_print(apfl_tokenizer_get_error(tt->tokenizer), stderr);
test_fatal(tt->t);
break;
}
}
static bool
expect_token(struct tokenizer_test *tt, int line, int col, enum apfl_token_type type, struct apfl_token *tok)
{
switch (apfl_tokenizer_next(tt->tokenizer, false)) {
case APFL_PARSE_OK:
break;
case APFL_PARSE_EOF:
test_fatalf(tt->t, "Got an EOF instead of a token");
break;
case APFL_PARSE_ERROR:
test_failf(tt->t, "Got an error instead of a token");
apfl_error_print(apfl_tokenizer_get_error(tt->tokenizer), stderr);
test_fatal(tt->t);
break;
}
*tok = apfl_tokenizer_get_token(tt->tokenizer);
if (tok->type != type) {
test_failf(
tt->t,
"Got wrong token type %s (wanted %s)",
apfl_token_type_name(tok->type),
apfl_token_type_name(type)
);
apfl_token_deinit(tok);
return false;
}
if (tok->position.line != line || tok->position.col != col) {
test_failf(tt->t, "Got token at wrong position %d:%d (wanted %d:%d)", tok->position.line, tok->position.col, line, col);
}
return true;
}
static void
expect_simple_token(struct tokenizer_test *tt, int line, int col, enum apfl_token_type type)
{
struct apfl_token tok;
if (expect_token(tt, line, col, type, &tok)) {
apfl_token_deinit(&tok);
}
}
static void
expect_text_token(struct tokenizer_test *tt, int line, int col, enum apfl_token_type type, const char *text)
{
struct apfl_token tok;
if (expect_token(tt, line, col, type, &tok)) {
if (apfl_string_cmp(text, tok.text) != 0) {
test_failf(tt->t, "Token has wrong content. have=\"" APFL_STR_FMT "\", want=\"%s\"", APFL_STR_FMT_ARGS(tok.text), text);
}
apfl_token_deinit(&tok);
}
}
static void
expect_number_token(struct tokenizer_test *tt, int line, int col, enum apfl_token_type type, apfl_number num)
{
struct apfl_token tok;
if (expect_token(tt, line, col, type, &tok)) {
if (tok.number != num) {
test_failf(tt->t, "Token has wrong content. have=%f, want=%f", tok.number, num);
}
apfl_token_deinit(&tok);
}
}
TEST(empty, t) {
struct tokenizer_test *tt = new_tokenizer_test(t, "");
expect_eof(tt);
destroy_tokenizer_test(tt);
}
TEST(simple_variable, t) {
struct tokenizer_test *tt = new_tokenizer_test(t, "hello");
expect_text_token(tt, 1, 1, APFL_TOK_NAME, "hello");
expect_eof(tt);
destroy_tokenizer_test(tt);
}
TEST(numbers, t) {
struct tokenizer_test *tt = new_tokenizer_test(t,
// 1 2
// 12345678901234567890123456789
"0 1 -1 1.5 -2.25 666 0xfe 0o15"
);
expect_number_token(tt, 1, 1, APFL_TOK_NUMBER, 0);
expect_number_token(tt, 1, 3, APFL_TOK_NUMBER, 1);
expect_number_token(tt, 1, 5, APFL_TOK_NUMBER, -1);
expect_number_token(tt, 1, 8, APFL_TOK_NUMBER, 1.5);
expect_number_token(tt, 1, 12, APFL_TOK_NUMBER, -2.25);
expect_number_token(tt, 1, 18, APFL_TOK_NUMBER, 666);
expect_number_token(tt, 1, 22, APFL_TOK_NUMBER, 0xfe);
expect_number_token(tt, 1, 27, APFL_TOK_NUMBER, 015);
expect_eof(tt);
destroy_tokenizer_test(tt);
}
TEST(names, t) {
struct tokenizer_test *tt = new_tokenizer_test(t, "foo bar --->-->-> Δv == a= x12=x+=");
expect_text_token (tt, 1, 1, APFL_TOK_NAME, "foo");
expect_text_token (tt, 1, 5, APFL_TOK_NAME, "bar");
expect_text_token (tt, 1, 9, APFL_TOK_NAME, "--");
expect_simple_token(tt, 1, 11, APFL_TOK_MAPSTO);
expect_text_token (tt, 1, 13, APFL_TOK_NAME, "-");
expect_simple_token(tt, 1, 14, APFL_TOK_MAPSTO);
expect_simple_token(tt, 1, 16, APFL_TOK_MAPSTO);
expect_text_token (tt, 1, 19, APFL_TOK_NAME, "Δv");
expect_text_token (tt, 1, 23, APFL_TOK_NAME, "==");
expect_text_token (tt, 1, 26, APFL_TOK_NAME, "a");
expect_simple_token(tt, 1, 27, APFL_TOK_ASSIGN);
expect_text_token (tt, 1, 29, APFL_TOK_NAME, "x12");
expect_simple_token(tt, 1, 32, APFL_TOK_ASSIGN);
destroy_tokenizer_test(tt);
}
TEST(all_tokens, t) {
struct tokenizer_test *tt = new_tokenizer_test(t,
// 1234567
"# test\n"
// 1 2345 678901234567
"\"abc\" def g-h*=i\n"
// 123456789012345678901234567890
"1234.5 -10 0x2A 0b101010 0o52\n"
// 12345678901 2
"'foo ;; , \\\n"
// 1234567890123456
"@ . ? ~ -> = :=\n"
// 123456
"({[]})"
);
expect_text_token (tt, 1, 1, APFL_TOK_COMMENT, " test");
expect_simple_token(tt, 1, 7, APFL_TOK_LINEBREAK);
expect_text_token (tt, 2, 1, APFL_TOK_STRING, "abc");
expect_text_token (tt, 2, 7, APFL_TOK_NAME, "def");
expect_text_token (tt, 2, 11, APFL_TOK_NAME, "g-h*=i");
expect_simple_token(tt, 2, 17, APFL_TOK_LINEBREAK);
expect_number_token(tt, 3, 1, APFL_TOK_NUMBER, 1234.5);
expect_number_token(tt, 3, 8, APFL_TOK_NUMBER, -10);
expect_number_token(tt, 3, 12, APFL_TOK_NUMBER, 42);
expect_number_token(tt, 3, 17, APFL_TOK_NUMBER, 42);
expect_number_token(tt, 3, 26, APFL_TOK_NUMBER, 42);
expect_simple_token(tt, 3, 30, APFL_TOK_LINEBREAK);
expect_simple_token(tt, 4, 1, APFL_TOK_STRINGIFY);
expect_text_token (tt, 4, 2, APFL_TOK_NAME, "foo");
expect_simple_token(tt, 4, 6, APFL_TOK_SEMICOLON);
expect_simple_token(tt, 4, 7, APFL_TOK_SEMICOLON);
expect_simple_token(tt, 4, 9, APFL_TOK_COMMA);
expect_simple_token(tt, 4, 11, APFL_TOK_CONTINUE_LINE);
expect_simple_token(tt, 4, 12, APFL_TOK_LINEBREAK);
expect_simple_token(tt, 5, 1, APFL_TOK_AT);
expect_simple_token(tt, 5, 3, APFL_TOK_DOT);
expect_simple_token(tt, 5, 5, APFL_TOK_QUESTION_MARK);
expect_simple_token(tt, 5, 7, APFL_TOK_EXPAND);
expect_simple_token(tt, 5, 9, APFL_TOK_MAPSTO);
expect_simple_token(tt, 5, 12, APFL_TOK_ASSIGN);
expect_simple_token(tt, 5, 14, APFL_TOK_LOCAL_ASSIGN);
expect_simple_token(tt, 5, 16, APFL_TOK_LINEBREAK);
expect_simple_token(tt, 6, 1, APFL_TOK_LPAREN);
expect_simple_token(tt, 6, 2, APFL_TOK_LBRACE);
expect_simple_token(tt, 6, 3, APFL_TOK_LBRACKET);
expect_simple_token(tt, 6, 4, APFL_TOK_RBRACKET);
expect_simple_token(tt, 6, 5, APFL_TOK_RBRACE);
expect_simple_token(tt, 6, 6, APFL_TOK_RPAREN);
expect_eof(tt);
destroy_tokenizer_test(tt);
}
TESTS_BEGIN
ADDTEST(empty),
ADDTEST(simple_variable),
ADDTEST(numbers),
ADDTEST(names),
ADDTEST(all_tokens),
TESTS_END

0
src/value.h Normal file
View file