2021-12-17 20:08:03 +00:00
|
|
|
#include <assert.h>
|
|
|
|
|
|
2021-12-16 22:42:37 +00:00
|
|
|
#include "apfl.h"
|
2021-12-17 20:08:03 +00:00
|
|
|
#include "resizable.h"
|
2021-12-16 22:42:37 +00:00
|
|
|
|
2022-02-08 21:53:13 +00:00
|
|
|
#include "alloc.h"
|
2021-12-16 22:42:37 +00:00
|
|
|
#include "test.h"
|
|
|
|
|
|
|
|
|
|
struct parser_test {
|
|
|
|
|
testctx t;
|
2022-02-08 21:53:13 +00:00
|
|
|
struct apfl_allocator allocator;
|
2021-12-16 22:42:37 +00:00
|
|
|
void *source_reader_ctx;
|
|
|
|
|
apfl_tokenizer_ptr tokenizer;
|
|
|
|
|
apfl_parser_ptr parser;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static struct parser_test *
|
|
|
|
|
new_parser_test(testctx t, const char *source)
|
|
|
|
|
{
|
2022-02-08 21:53:13 +00:00
|
|
|
struct apfl_allocator allocator = test_allocator(t);
|
|
|
|
|
|
2021-12-16 22:42:37 +00:00
|
|
|
struct parser_test *pt = must_alloc(t, sizeof(struct parser_test));
|
|
|
|
|
pt->t = t;
|
2022-02-08 21:53:13 +00:00
|
|
|
pt->allocator = allocator;
|
|
|
|
|
|
|
|
|
|
if ((pt->source_reader_ctx = apfl_string_source_reader_new(allocator, apfl_string_view_from(source))) == NULL) {
|
2021-12-16 22:42:37 +00:00
|
|
|
test_fatalf(t, "Failed initializing source reader");
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-08 21:53:13 +00:00
|
|
|
if ((pt->tokenizer = apfl_tokenizer_new(allocator, apfl_string_source_reader, pt->source_reader_ctx)) == NULL) {
|
2021-12-16 22:42:37 +00:00
|
|
|
test_fatalf(t, "Failed initializing the tokenizer");
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-08 21:53:13 +00:00
|
|
|
if ((pt->parser = apfl_parser_new(allocator, apfl_tokenizer_as_token_source(pt->tokenizer))) == NULL) {
|
2021-12-16 22:42:37 +00:00
|
|
|
test_fatalf(t, "Failed initializing the parser");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return pt;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
destroy_parser_test(struct parser_test *pt)
|
|
|
|
|
{
|
|
|
|
|
apfl_parser_destroy(pt->parser);
|
|
|
|
|
apfl_tokenizer_destroy(pt->tokenizer);
|
|
|
|
|
apfl_string_source_reader_destroy(pt->source_reader_ctx);
|
|
|
|
|
free(pt);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
expect_eof(struct parser_test *pt)
|
|
|
|
|
{
|
|
|
|
|
switch (apfl_parser_next(pt->parser)) {
|
|
|
|
|
case APFL_PARSE_OK:
|
|
|
|
|
test_fatalf(pt->t, "Expected EOF but got an expression");
|
|
|
|
|
break;
|
|
|
|
|
case APFL_PARSE_EOF:
|
|
|
|
|
break;
|
|
|
|
|
case APFL_PARSE_ERROR:
|
|
|
|
|
test_failf(pt->t, "Got an error instead of an EOF");
|
|
|
|
|
apfl_error_print(apfl_parser_get_error(pt->parser), stderr);
|
|
|
|
|
test_fatal(pt->t);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
expect_expr(struct parser_test *pt, struct apfl_expr expected)
|
|
|
|
|
{
|
|
|
|
|
struct apfl_expr expr;
|
|
|
|
|
|
|
|
|
|
switch (apfl_parser_next(pt->parser)) {
|
|
|
|
|
case APFL_PARSE_OK:
|
|
|
|
|
expr = apfl_parser_get_expr(pt->parser);
|
|
|
|
|
if (!apfl_expr_eq(expr, expected)) {
|
|
|
|
|
test_failf(pt->t, "Expected expression differs from actual expression");
|
|
|
|
|
test_failf(pt->t, "Expected:");
|
|
|
|
|
apfl_expr_print(expected, stderr);
|
|
|
|
|
test_failf(pt->t, "Have:");
|
|
|
|
|
apfl_expr_print(expr, stderr);
|
|
|
|
|
}
|
2022-02-08 21:53:13 +00:00
|
|
|
apfl_expr_deinit(pt->allocator, &expr);
|
|
|
|
|
apfl_expr_deinit(pt->allocator, &expected);
|
2021-12-16 22:42:37 +00:00
|
|
|
break;
|
|
|
|
|
case APFL_PARSE_EOF:
|
|
|
|
|
test_fatalf(pt->t, "Extected an expression but got EOF");
|
|
|
|
|
break;
|
|
|
|
|
case APFL_PARSE_ERROR:
|
2021-12-18 15:09:41 +00:00
|
|
|
test_failf(pt->t, "Got an error instead of an expression");
|
2021-12-16 22:42:37 +00:00
|
|
|
apfl_error_print(apfl_parser_get_error(pt->parser), stderr);
|
|
|
|
|
test_fatal(pt->t);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-18 23:27:34 +00:00
|
|
|
static void
|
|
|
|
|
expect_error_of_type(struct parser_test *pt, enum apfl_error_type want)
|
|
|
|
|
{
|
|
|
|
|
struct apfl_error have;
|
|
|
|
|
switch (apfl_parser_next(pt->parser)) {
|
|
|
|
|
case APFL_PARSE_OK:
|
|
|
|
|
test_failf(pt->t, "Expected error but got an OK");
|
|
|
|
|
break;
|
|
|
|
|
case APFL_PARSE_EOF:
|
|
|
|
|
test_fatalf(pt->t, "Expected error but got an EOF");
|
|
|
|
|
break;
|
|
|
|
|
case APFL_PARSE_ERROR:
|
|
|
|
|
have = apfl_parser_get_error(pt->parser);
|
|
|
|
|
if (have.type != want) {
|
|
|
|
|
test_failf(pt->t, "Expected error of type %s, got this error instead:", apfl_error_type_name(want));
|
|
|
|
|
apfl_error_print(have, stderr);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
Implement mark&sweep garbage collection and bytecode compilation
Instead of the previous refcount base garbage collection, we're now using
a basic tri-color mark&sweep collector. This is done to support cyclical
value relationships in the future (functions can form cycles, all values
implemented up to this point can not).
The collector maintains a set of roots and a set of objects (grouped into
blocks). The GC enabled objects are no longer allocated manually, but will
be allocated by the GC. The GC also wraps an allocator, this way the GC
knows, if we ran out of memory and will try to get out of this situation by
performing a full collection cycle.
The tri-color abstraction was chosen for two reasons:
- We don't have to maintain a list of objects that need to be marked, we
can simply grab the next grey one.
- It should allow us to later implement incremental collection (right now
we only do a stop-the-world collection).
This also switches to a bytecode based evaluation of the code: We no longer
directly evaluate the AST, but first compile it into a series of
instructions, that are evaluated in a separate step. This was done in
preparation for inplementing functions: We only need to turn a function
body into instructions instead of evaluating the node again with each call
of the function. Also, since an instruction list is implemented as a GC
object, this then removes manual memory management of the function body and
it's child nodes. Since the GC and the bytecode go hand in hand, this was
done in one (giant) commit.
As a downside, we've now lost the ability do do list matching on
assignments. I've already started to work on implementing this in the new
architecture, but left it out of this commit, as it's already quite a large
commit :)
2022-04-11 20:24:22 +00:00
|
|
|
static struct apfl_string
|
2021-12-16 22:42:37 +00:00
|
|
|
new_string(struct parser_test *pt, const char *in)
|
|
|
|
|
{
|
Implement mark&sweep garbage collection and bytecode compilation
Instead of the previous refcount base garbage collection, we're now using
a basic tri-color mark&sweep collector. This is done to support cyclical
value relationships in the future (functions can form cycles, all values
implemented up to this point can not).
The collector maintains a set of roots and a set of objects (grouped into
blocks). The GC enabled objects are no longer allocated manually, but will
be allocated by the GC. The GC also wraps an allocator, this way the GC
knows, if we ran out of memory and will try to get out of this situation by
performing a full collection cycle.
The tri-color abstraction was chosen for two reasons:
- We don't have to maintain a list of objects that need to be marked, we
can simply grab the next grey one.
- It should allow us to later implement incremental collection (right now
we only do a stop-the-world collection).
This also switches to a bytecode based evaluation of the code: We no longer
directly evaluate the AST, but first compile it into a series of
instructions, that are evaluated in a separate step. This was done in
preparation for inplementing functions: We only need to turn a function
body into instructions instead of evaluating the node again with each call
of the function. Also, since an instruction list is implemented as a GC
object, this then removes manual memory management of the function body and
it's child nodes. Since the GC and the bytecode go hand in hand, this was
done in one (giant) commit.
As a downside, we've now lost the ability do do list matching on
assignments. I've already started to work on implementing this in the new
architecture, but left it out of this commit, as it's already quite a large
commit :)
2022-04-11 20:24:22 +00:00
|
|
|
struct apfl_string out = apfl_string_blank();
|
|
|
|
|
if (!apfl_string_copy(pt->allocator, &out, apfl_string_view_from(in))) {
|
2021-12-16 22:42:37 +00:00
|
|
|
test_fatalf(pt->t, "Failed copying string in new_string");
|
|
|
|
|
}
|
|
|
|
|
return out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void *
|
|
|
|
|
new_helper(struct parser_test *pt, size_t size, void *data)
|
|
|
|
|
{
|
2022-02-08 21:53:13 +00:00
|
|
|
void *out = ALLOC_BYTES(pt->allocator, size);
|
2021-12-16 22:42:37 +00:00
|
|
|
memcpy(out, data, size);
|
|
|
|
|
return out;
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-17 20:08:03 +00:00
|
|
|
static struct apfl_expr_list_item
|
|
|
|
|
list_item(bool expand, struct apfl_expr *expr)
|
|
|
|
|
{
|
|
|
|
|
return (struct apfl_expr_list_item) {
|
|
|
|
|
.expand = expand,
|
|
|
|
|
.expr = expr,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-07 22:08:25 +00:00
|
|
|
static struct apfl_expr_assignable_list_item
|
|
|
|
|
assignable_list_item(bool expand, struct apfl_expr_assignable assignable)
|
|
|
|
|
{
|
|
|
|
|
return (struct apfl_expr_assignable_list_item) {
|
|
|
|
|
.expand = expand,
|
|
|
|
|
.assignable = assignable,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct apfl_expr_params_item
|
|
|
|
|
params_item(bool expand, struct apfl_expr_param param)
|
|
|
|
|
{
|
|
|
|
|
return (struct apfl_expr_params_item) {
|
|
|
|
|
.expand = expand,
|
|
|
|
|
.param = param,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-22 16:16:28 +00:00
|
|
|
struct test_pre_body {
|
|
|
|
|
struct apfl_expr *items;
|
|
|
|
|
size_t len;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static struct apfl_expr_body *
|
|
|
|
|
create_body(struct parser_test *pt, struct test_pre_body pre_body)
|
|
|
|
|
{
|
2022-02-08 21:53:13 +00:00
|
|
|
struct apfl_expr_body *body = ALLOC_OBJ(pt->allocator, struct apfl_expr_body);
|
2022-01-22 16:16:28 +00:00
|
|
|
body->items = pre_body.items;
|
|
|
|
|
body->len = pre_body.len;
|
2022-02-08 21:53:13 +00:00
|
|
|
body->cap = pre_body.len;
|
2022-01-22 16:16:28 +00:00
|
|
|
body->refcount = 1;
|
|
|
|
|
return body;
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-16 22:42:37 +00:00
|
|
|
// Fugly macros to make it a bit easier to create a heap allocated struct value
|
|
|
|
|
#define BEGIN_NEW(pt, T) ((T *)new_helper(pt, sizeof(T), &((T)
|
|
|
|
|
#define END_NEW )))
|
|
|
|
|
|
2021-12-17 20:08:03 +00:00
|
|
|
enum listbuilder_cmd {
|
|
|
|
|
LISTBUILDER_ADD,
|
|
|
|
|
LISTBUILDER_STOP,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#define LISTBUILDER_NAME(n) listbuilder_##n
|
2021-12-18 15:34:09 +00:00
|
|
|
#define LISTBUILDER_ITEM_NAME(n) listbuilder_item_##n
|
2022-02-08 21:53:13 +00:00
|
|
|
#define LISTBUILDER_FILL_LIST(pt, itemtype, items, list_items, list_len, cap) \
|
|
|
|
|
apfl_resizable_init((void **)&list_items, &list_len, &cap); \
|
2021-12-17 20:08:03 +00:00
|
|
|
\
|
2022-02-08 21:53:13 +00:00
|
|
|
for (items++; items->has_item; items++) { \
|
|
|
|
|
if (!apfl_resizable_append( \
|
|
|
|
|
pt->allocator, \
|
|
|
|
|
sizeof(itemtype), \
|
|
|
|
|
(void **)&list_items, \
|
|
|
|
|
&list_len, \
|
|
|
|
|
&cap, \
|
|
|
|
|
&items->item, \
|
|
|
|
|
1 \
|
|
|
|
|
)) { \
|
|
|
|
|
test_fatalf(pt->t, "Failed appending"); \
|
|
|
|
|
assert(false); \
|
2021-12-17 20:08:03 +00:00
|
|
|
} \
|
|
|
|
|
} \
|
|
|
|
|
|
2022-02-08 21:53:13 +00:00
|
|
|
#define MKLISTBUILDER(name, listtype, itemtype, items_memb) \
|
|
|
|
|
struct LISTBUILDER_ITEM_NAME(name) { \
|
|
|
|
|
bool has_item; \
|
|
|
|
|
itemtype item; \
|
|
|
|
|
}; \
|
|
|
|
|
listtype \
|
|
|
|
|
LISTBUILDER_NAME(name)( \
|
|
|
|
|
struct parser_test *pt, \
|
|
|
|
|
struct LISTBUILDER_ITEM_NAME(name) items[] \
|
|
|
|
|
) { \
|
|
|
|
|
listtype list; \
|
|
|
|
|
size_t cap = 0; \
|
|
|
|
|
\
|
|
|
|
|
LISTBUILDER_FILL_LIST(pt, itemtype, items, list.items_memb, list.len, cap) \
|
|
|
|
|
\
|
|
|
|
|
return list; \
|
|
|
|
|
} \
|
|
|
|
|
|
|
|
|
|
#define MKCAPLISTBUILDER(name, listtype, itemtype, items_memb) \
|
|
|
|
|
struct LISTBUILDER_ITEM_NAME(name) { \
|
|
|
|
|
bool has_item; \
|
|
|
|
|
itemtype item; \
|
|
|
|
|
}; \
|
|
|
|
|
listtype \
|
|
|
|
|
LISTBUILDER_NAME(name)( \
|
|
|
|
|
struct parser_test *pt, \
|
|
|
|
|
struct LISTBUILDER_ITEM_NAME(name) items[] \
|
|
|
|
|
) { \
|
|
|
|
|
listtype list; \
|
|
|
|
|
\
|
|
|
|
|
LISTBUILDER_FILL_LIST(pt, itemtype, items, list.items_memb, list.len, list.cap) \
|
|
|
|
|
\
|
|
|
|
|
return list; \
|
|
|
|
|
} \
|
|
|
|
|
|
2021-12-18 15:34:09 +00:00
|
|
|
#define LIST_BEGIN(pt, builder) (LISTBUILDER_NAME(builder)(pt, (struct LISTBUILDER_ITEM_NAME(builder)[]) {{.has_item=false
|
|
|
|
|
#define LIST_END }, {.has_item = false}}))
|
|
|
|
|
#define LIST_ADD }, {.has_item = true, .item =
|
2021-12-17 20:08:03 +00:00
|
|
|
|
2022-02-08 21:53:13 +00:00
|
|
|
MKLISTBUILDER(list, struct apfl_expr_list, struct apfl_expr_list_item, items)
|
|
|
|
|
MKLISTBUILDER(body, struct test_pre_body, struct apfl_expr, items)
|
|
|
|
|
MKCAPLISTBUILDER(dict, struct apfl_expr_dict, struct apfl_expr_dict_pair, items)
|
|
|
|
|
MKCAPLISTBUILDER(complex_func, struct apfl_expr_complex_func, struct apfl_expr_subfunc, subfuncs)
|
|
|
|
|
MKCAPLISTBUILDER(params, struct apfl_expr_params, struct apfl_expr_params_item, params)
|
|
|
|
|
MKLISTBUILDER(assignable_list, struct apfl_expr_assignable_list, struct apfl_expr_assignable_list_item, items)
|
2021-12-17 20:08:03 +00:00
|
|
|
|
|
|
|
|
#define POS(l, c) (struct apfl_position) { .line = l, .col = c }
|
|
|
|
|
|
|
|
|
|
static struct apfl_expr
|
|
|
|
|
var(struct parser_test *pt, int line, int col, const char *v)
|
|
|
|
|
{
|
|
|
|
|
return (struct apfl_expr) {
|
|
|
|
|
.type = APFL_EXPR_VAR,
|
|
|
|
|
.position = POS(line, col),
|
|
|
|
|
.var = new_string(pt, v),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct apfl_expr *
|
|
|
|
|
new_var(struct parser_test *pt, int line, int col, const char *v)
|
|
|
|
|
{
|
|
|
|
|
struct apfl_expr expr = var(pt, line, col, v);
|
|
|
|
|
return new_helper(pt, sizeof(struct apfl_expr), &expr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct apfl_expr
|
|
|
|
|
dot(struct parser_test *pt, int line, int col, const char *rhs, struct apfl_expr *lhs)
|
|
|
|
|
{
|
|
|
|
|
return (struct apfl_expr) {
|
|
|
|
|
.type = APFL_EXPR_DOT,
|
|
|
|
|
.position = POS(line, col),
|
|
|
|
|
.dot.lhs = lhs,
|
|
|
|
|
.dot.rhs = new_string(pt, rhs),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct apfl_expr *
|
|
|
|
|
new_dot(struct parser_test *pt, int line, int col, const char *rhs, struct apfl_expr *lhs)
|
|
|
|
|
{
|
|
|
|
|
struct apfl_expr expr = dot(pt, line, col, rhs, lhs);
|
|
|
|
|
return new_helper(pt, sizeof(struct apfl_expr), &expr);
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-18 15:11:23 +00:00
|
|
|
|
|
|
|
|
static struct apfl_expr_const
|
|
|
|
|
num_const(apfl_number n)
|
|
|
|
|
{
|
|
|
|
|
return (struct apfl_expr_const) {
|
|
|
|
|
.type = APFL_EXPR_CONST_NUMBER,
|
|
|
|
|
.number = n,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct apfl_expr_const
|
|
|
|
|
string_const(struct parser_test *pt, const char *s)
|
|
|
|
|
{
|
|
|
|
|
return (struct apfl_expr_const) {
|
|
|
|
|
.type = APFL_EXPR_CONST_STRING,
|
|
|
|
|
.string = new_string(pt, s),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct apfl_expr_const
|
|
|
|
|
bool_const(bool b)
|
|
|
|
|
{
|
|
|
|
|
return (struct apfl_expr_const) {
|
|
|
|
|
.type = APFL_EXPR_CONST_BOOLEAN,
|
|
|
|
|
.boolean = b,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct apfl_expr
|
|
|
|
|
const_expr(int line, int col, struct apfl_expr_const constant)
|
|
|
|
|
{
|
|
|
|
|
return (struct apfl_expr) {
|
|
|
|
|
.type = APFL_EXPR_CONSTANT,
|
|
|
|
|
.position = POS(line, col),
|
|
|
|
|
.constant = constant,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct apfl_expr *
|
|
|
|
|
new_const_expr(struct parser_test *pt, int line, int col, struct apfl_expr_const constant)
|
|
|
|
|
{
|
|
|
|
|
struct apfl_expr expr = const_expr(line, col, constant);
|
|
|
|
|
return new_helper(pt, sizeof(struct apfl_expr), &expr);
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-08 21:55:24 +00:00
|
|
|
static struct apfl_expr_assignable
|
|
|
|
|
assignable_var(struct parser_test *pt, const char *name)
|
|
|
|
|
{
|
|
|
|
|
return (struct apfl_expr_assignable) {
|
|
|
|
|
.type = APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER,
|
|
|
|
|
.var_or_member = {
|
|
|
|
|
.type = APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER_VAR,
|
|
|
|
|
.var = new_string(pt, name),
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct apfl_expr_assignable *
|
|
|
|
|
new_assignable_var(struct parser_test *pt, const char *name)
|
|
|
|
|
{
|
|
|
|
|
struct apfl_expr_assignable assignable = assignable_var(pt, name);
|
|
|
|
|
return new_helper(pt, sizeof(struct apfl_expr_assignable), &assignable);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2021-12-16 22:42:37 +00:00
|
|
|
TEST(empty, t) {
|
|
|
|
|
struct parser_test *pt = new_parser_test(t, "");
|
|
|
|
|
expect_eof(pt);
|
|
|
|
|
destroy_parser_test(pt);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(hello_world, t) {
|
|
|
|
|
struct parser_test *pt = new_parser_test(t, "print \"Hello World!\"");
|
|
|
|
|
expect_expr(pt, (struct apfl_expr) {
|
|
|
|
|
.type = APFL_EXPR_CALL,
|
2021-12-17 20:08:03 +00:00
|
|
|
.position = POS(1, 1),
|
2021-12-16 22:42:37 +00:00
|
|
|
.call = (struct apfl_expr_call) {
|
2021-12-17 20:08:03 +00:00
|
|
|
.callee = new_var(pt, 1, 1, "print"),
|
|
|
|
|
.arguments = LIST_BEGIN(pt, list)
|
|
|
|
|
LIST_ADD (struct apfl_expr_list_item) {
|
2021-12-16 22:42:37 +00:00
|
|
|
.expand = false,
|
2021-12-18 15:11:23 +00:00
|
|
|
.expr = new_const_expr(pt, 1, 7, string_const(pt, "Hello World!")),
|
2021-12-17 20:08:03 +00:00
|
|
|
},
|
|
|
|
|
LIST_END,
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
expect_eof(pt);
|
|
|
|
|
destroy_parser_test(pt);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(empty_function, t) {
|
|
|
|
|
struct parser_test *pt = new_parser_test(t, "{}");
|
|
|
|
|
expect_expr(pt, (struct apfl_expr) {
|
|
|
|
|
.type = APFL_EXPR_SIMPLE_FUNC,
|
|
|
|
|
.position = POS(1, 1),
|
2022-01-22 16:16:28 +00:00
|
|
|
.simple_func = create_body(pt, LIST_BEGIN(pt, body)
|
|
|
|
|
LIST_END),
|
2021-12-17 20:08:03 +00:00
|
|
|
});
|
|
|
|
|
expect_eof(pt);
|
|
|
|
|
destroy_parser_test(pt);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(empty_list, t) {
|
|
|
|
|
struct parser_test *pt = new_parser_test(t, "[]");
|
|
|
|
|
expect_expr(pt, (struct apfl_expr) {
|
|
|
|
|
.type = APFL_EXPR_LIST,
|
|
|
|
|
.position = POS(1, 1),
|
|
|
|
|
.list = LIST_BEGIN(pt, list)
|
|
|
|
|
LIST_END,
|
|
|
|
|
});
|
|
|
|
|
expect_eof(pt);
|
|
|
|
|
destroy_parser_test(pt);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(empty_dict, t) {
|
|
|
|
|
struct parser_test *pt = new_parser_test(t, "[->]");
|
|
|
|
|
expect_expr(pt, (struct apfl_expr) {
|
|
|
|
|
.type = APFL_EXPR_DICT,
|
|
|
|
|
.position = POS(1, 1),
|
|
|
|
|
.dict = LIST_BEGIN(pt, dict)
|
|
|
|
|
LIST_END,
|
|
|
|
|
});
|
|
|
|
|
expect_eof(pt);
|
|
|
|
|
destroy_parser_test(pt);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(simple_dot_access, t) {
|
|
|
|
|
struct parser_test *pt = new_parser_test(t, "foo.bar");
|
|
|
|
|
expect_expr(pt, dot(pt, 1, 4, "bar", new_var(pt, 1, 1, "foo")));
|
|
|
|
|
expect_eof(pt);
|
|
|
|
|
destroy_parser_test(pt);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(simple_at_access, t) {
|
|
|
|
|
struct parser_test *pt = new_parser_test(t, "foo@bar");
|
|
|
|
|
expect_expr(pt, (struct apfl_expr) {
|
|
|
|
|
.type = APFL_EXPR_AT,
|
|
|
|
|
.position = POS(1, 4),
|
|
|
|
|
.at.lhs = new_var(pt, 1, 1, "foo"),
|
|
|
|
|
.at.rhs = new_var(pt, 1, 5, "bar"),
|
|
|
|
|
});
|
|
|
|
|
expect_eof(pt);
|
|
|
|
|
destroy_parser_test(pt);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(parens, t) {
|
|
|
|
|
// 1 2 3 4
|
|
|
|
|
// 12345678901234567890123456789012345678901234567
|
|
|
|
|
struct parser_test *pt = new_parser_test(t, "foo (bar baz) (aaa ~bbb (ccc) ~(ddd (eee fff)))");
|
|
|
|
|
expect_expr(pt, (struct apfl_expr) {
|
|
|
|
|
.type = APFL_EXPR_CALL,
|
|
|
|
|
.position = POS(1, 1),
|
|
|
|
|
.call = (struct apfl_expr_call) {
|
|
|
|
|
.callee = new_var(pt, 1, 1, "foo"),
|
|
|
|
|
.arguments = LIST_BEGIN(pt, list)
|
|
|
|
|
LIST_ADD list_item(false, BEGIN_NEW(pt, struct apfl_expr) {
|
|
|
|
|
.type = APFL_EXPR_CALL,
|
|
|
|
|
.position = POS(1, 5),
|
|
|
|
|
.call = (struct apfl_expr_call) {
|
|
|
|
|
.callee = new_var(pt, 1, 6, "bar"),
|
|
|
|
|
.arguments = LIST_BEGIN(pt, list)
|
|
|
|
|
LIST_ADD list_item(false, new_var(pt, 1, 10, "baz")),
|
|
|
|
|
LIST_END
|
|
|
|
|
},
|
|
|
|
|
} END_NEW),
|
|
|
|
|
LIST_ADD list_item(false, BEGIN_NEW(pt, struct apfl_expr) {
|
|
|
|
|
.type = APFL_EXPR_CALL,
|
|
|
|
|
.position = POS(1, 15),
|
|
|
|
|
.call = (struct apfl_expr_call) {
|
|
|
|
|
.callee = new_var(pt, 1, 16, "aaa"),
|
|
|
|
|
.arguments = LIST_BEGIN(pt, list)
|
|
|
|
|
LIST_ADD list_item(true, new_var(pt, 1, 21, "bbb")),
|
|
|
|
|
LIST_ADD list_item(false, BEGIN_NEW(pt, struct apfl_expr) {
|
|
|
|
|
.type = APFL_EXPR_CALL,
|
|
|
|
|
.position = POS(1, 25),
|
|
|
|
|
.call = (struct apfl_expr_call) {
|
|
|
|
|
.callee = new_var(pt, 1, 26, "ccc"),
|
|
|
|
|
.arguments = LIST_BEGIN(pt, list)
|
|
|
|
|
LIST_END,
|
|
|
|
|
},
|
|
|
|
|
} END_NEW),
|
|
|
|
|
LIST_ADD list_item(true, BEGIN_NEW(pt, struct apfl_expr) {
|
|
|
|
|
.type = APFL_EXPR_CALL,
|
|
|
|
|
.position = POS(1, 32),
|
|
|
|
|
.call = (struct apfl_expr_call) {
|
|
|
|
|
.callee = new_var(pt, 1, 33, "ddd"),
|
|
|
|
|
.arguments = LIST_BEGIN(pt, list)
|
|
|
|
|
LIST_ADD list_item(false, BEGIN_NEW(pt, struct apfl_expr) {
|
|
|
|
|
.type = APFL_EXPR_CALL,
|
|
|
|
|
.position = POS(1, 37),
|
|
|
|
|
.call = (struct apfl_expr_call) {
|
|
|
|
|
.callee = new_var(pt, 1, 38, "eee"),
|
|
|
|
|
.arguments = LIST_BEGIN(pt, list)
|
|
|
|
|
LIST_ADD list_item(false, new_var(pt, 1, 42, "fff")),
|
|
|
|
|
LIST_END,
|
|
|
|
|
},
|
|
|
|
|
} END_NEW),
|
|
|
|
|
LIST_END,
|
|
|
|
|
},
|
|
|
|
|
} END_NEW),
|
|
|
|
|
LIST_END,
|
|
|
|
|
},
|
|
|
|
|
} END_NEW),
|
|
|
|
|
LIST_END,
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
expect_eof(pt);
|
|
|
|
|
destroy_parser_test(pt);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(complex_dot_at_access, t) {
|
|
|
|
|
// 1 2
|
|
|
|
|
// 123456789 01 23456789012345
|
|
|
|
|
struct parser_test *pt = new_parser_test(t, "a@b.c@1@\"d\"@(e@(f.g h).i)");
|
|
|
|
|
|
|
|
|
|
expect_expr(pt, (struct apfl_expr) {
|
|
|
|
|
.type = APFL_EXPR_AT,
|
|
|
|
|
.position = POS(1, 12),
|
|
|
|
|
.at = (struct apfl_expr_at) {
|
|
|
|
|
.lhs = BEGIN_NEW(pt, struct apfl_expr) {
|
|
|
|
|
.type = APFL_EXPR_AT,
|
|
|
|
|
.position = POS(1, 8),
|
|
|
|
|
.at = (struct apfl_expr_at) {
|
|
|
|
|
.lhs = BEGIN_NEW(pt, struct apfl_expr) {
|
|
|
|
|
.type = APFL_EXPR_AT,
|
|
|
|
|
.position = POS(1, 6),
|
|
|
|
|
.at = (struct apfl_expr_at) {
|
|
|
|
|
.lhs = new_dot(pt, 1, 4, "c", BEGIN_NEW(pt, struct apfl_expr) {
|
|
|
|
|
.type = APFL_EXPR_AT,
|
|
|
|
|
.position = POS(1, 2),
|
|
|
|
|
.at = (struct apfl_expr_at) {
|
|
|
|
|
.lhs = new_var(pt, 1, 1, "a"),
|
|
|
|
|
.rhs = new_var(pt, 1, 3, "b"),
|
|
|
|
|
}
|
|
|
|
|
} END_NEW),
|
2021-12-18 15:11:23 +00:00
|
|
|
.rhs = new_const_expr(pt, 1, 7, num_const(1)),
|
2021-12-17 20:08:03 +00:00
|
|
|
},
|
|
|
|
|
} END_NEW,
|
2021-12-18 15:11:23 +00:00
|
|
|
.rhs = new_const_expr(pt, 1, 9, string_const(pt, "d")),
|
2021-12-17 20:08:03 +00:00
|
|
|
},
|
|
|
|
|
} END_NEW,
|
|
|
|
|
.rhs = BEGIN_NEW(pt, struct apfl_expr) {
|
|
|
|
|
.type = APFL_EXPR_CALL,
|
|
|
|
|
.position = POS(1, 13),
|
|
|
|
|
.call = (struct apfl_expr_call) {
|
|
|
|
|
.callee = new_dot(pt, 1, 23, "i", BEGIN_NEW(pt, struct apfl_expr) {
|
|
|
|
|
.type = APFL_EXPR_AT,
|
|
|
|
|
.position = POS(1, 15),
|
|
|
|
|
.at = (struct apfl_expr_at) {
|
|
|
|
|
.lhs = new_var(pt, 1, 14, "e"),
|
|
|
|
|
.rhs = BEGIN_NEW(pt, struct apfl_expr) {
|
|
|
|
|
.type = APFL_EXPR_CALL,
|
|
|
|
|
.position = POS(1, 16),
|
|
|
|
|
.call = (struct apfl_expr_call) {
|
|
|
|
|
.callee = new_dot(pt, 1, 18, "g",
|
|
|
|
|
new_var(pt, 1, 17, "f")
|
|
|
|
|
),
|
|
|
|
|
.arguments = LIST_BEGIN(pt, list)
|
|
|
|
|
LIST_ADD (struct apfl_expr_list_item) {
|
|
|
|
|
.expr = new_var(pt, 1, 21, "h"),
|
|
|
|
|
.expand = false,
|
|
|
|
|
},
|
|
|
|
|
LIST_END,
|
|
|
|
|
},
|
|
|
|
|
} END_NEW,
|
|
|
|
|
},
|
|
|
|
|
} END_NEW),
|
|
|
|
|
.arguments = LIST_BEGIN(pt, list)
|
|
|
|
|
LIST_END,
|
|
|
|
|
},
|
|
|
|
|
} END_NEW,
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
expect_eof(pt);
|
|
|
|
|
destroy_parser_test(pt);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(factorial, t) {
|
|
|
|
|
struct parser_test *pt = new_parser_test(t,
|
|
|
|
|
// 1 2 3
|
|
|
|
|
// 12345678901234567890123456789012
|
|
|
|
|
"factorial := {\n"
|
|
|
|
|
" 0 -> 1\n"
|
|
|
|
|
" n -> * n (factorial (- n 1))\n"
|
|
|
|
|
"}\n"
|
|
|
|
|
"factorial 10"
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
expect_expr(pt, (struct apfl_expr) {
|
|
|
|
|
.type = APFL_EXPR_ASSIGNMENT,
|
|
|
|
|
.position = POS(1, 11),
|
|
|
|
|
.assignment = (struct apfl_expr_assignment) {
|
2022-01-08 21:55:24 +00:00
|
|
|
.lhs = assignable_var(pt, "factorial"),
|
2021-12-17 20:08:03 +00:00
|
|
|
.rhs = BEGIN_NEW(pt, struct apfl_expr) {
|
|
|
|
|
.type = APFL_EXPR_COMPLEX_FUNC,
|
|
|
|
|
.position = POS(1, 14),
|
|
|
|
|
.complex_func = LIST_BEGIN(pt, complex_func)
|
|
|
|
|
LIST_ADD (struct apfl_expr_subfunc) {
|
|
|
|
|
.params = LIST_BEGIN(pt, params)
|
2022-01-07 22:08:25 +00:00
|
|
|
LIST_ADD params_item(false, (struct apfl_expr_param) {
|
2021-12-17 20:08:03 +00:00
|
|
|
.type = APFL_EXPR_PARAM_CONSTANT,
|
2021-12-18 15:11:23 +00:00
|
|
|
.constant = num_const(0),
|
2022-01-07 22:08:25 +00:00
|
|
|
}),
|
2021-12-17 20:08:03 +00:00
|
|
|
LIST_END,
|
2022-01-22 16:16:28 +00:00
|
|
|
.body = create_body(pt, LIST_BEGIN(pt, body)
|
2021-12-18 15:11:23 +00:00
|
|
|
LIST_ADD const_expr(2, 10, num_const(1)),
|
2022-01-22 16:16:28 +00:00
|
|
|
LIST_END)
|
2021-12-17 20:08:03 +00:00
|
|
|
},
|
|
|
|
|
LIST_ADD (struct apfl_expr_subfunc) {
|
|
|
|
|
.params = LIST_BEGIN(pt, params)
|
2022-01-07 22:08:25 +00:00
|
|
|
LIST_ADD params_item(false, (struct apfl_expr_param) {
|
2021-12-17 20:08:03 +00:00
|
|
|
.type = APFL_EXPR_PARAM_VAR,
|
|
|
|
|
.var = new_string(pt, "n"),
|
2022-01-07 22:08:25 +00:00
|
|
|
}),
|
2021-12-17 20:08:03 +00:00
|
|
|
LIST_END,
|
2022-01-22 16:16:28 +00:00
|
|
|
.body = create_body(pt, LIST_BEGIN(pt, body)
|
2021-12-17 20:08:03 +00:00
|
|
|
LIST_ADD (struct apfl_expr) {
|
|
|
|
|
.type = APFL_EXPR_CALL,
|
|
|
|
|
.position = POS(3, 10),
|
|
|
|
|
.call = (struct apfl_expr_call) {
|
|
|
|
|
.callee = new_var(pt, 3, 10, "*"),
|
|
|
|
|
.arguments = LIST_BEGIN(pt, list)
|
|
|
|
|
LIST_ADD list_item(false, new_var(pt, 3, 12, "n")),
|
|
|
|
|
LIST_ADD list_item(false, BEGIN_NEW(pt, struct apfl_expr) {
|
|
|
|
|
.type = APFL_EXPR_CALL,
|
|
|
|
|
.position = POS(3, 14),
|
|
|
|
|
.call = (struct apfl_expr_call) {
|
|
|
|
|
.callee = new_var(pt, 3, 15, "factorial"),
|
|
|
|
|
.arguments = LIST_BEGIN(pt, list)
|
|
|
|
|
LIST_ADD list_item(false, BEGIN_NEW(pt, struct apfl_expr) {
|
|
|
|
|
.type = APFL_EXPR_CALL,
|
|
|
|
|
.position = POS(3, 25),
|
|
|
|
|
.call = (struct apfl_expr_call) {
|
|
|
|
|
.callee = new_var(pt, 3, 26, "-"),
|
|
|
|
|
.arguments = LIST_BEGIN(pt, list)
|
|
|
|
|
LIST_ADD list_item(false, new_var(pt, 3, 28, "n")),
|
2021-12-18 15:11:23 +00:00
|
|
|
LIST_ADD list_item(false, new_const_expr(pt, 3, 30,num_const(1))),
|
2021-12-17 20:08:03 +00:00
|
|
|
LIST_END,
|
|
|
|
|
},
|
|
|
|
|
} END_NEW),
|
|
|
|
|
LIST_END,
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
} END_NEW),
|
|
|
|
|
LIST_END,
|
|
|
|
|
},
|
|
|
|
|
},
|
2022-01-22 16:16:28 +00:00
|
|
|
LIST_END)
|
2021-12-17 20:08:03 +00:00
|
|
|
},
|
|
|
|
|
LIST_END
|
|
|
|
|
} END_NEW
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
expect_expr(pt, (struct apfl_expr) {
|
|
|
|
|
.type = APFL_EXPR_CALL,
|
|
|
|
|
.position = POS(5, 1),
|
|
|
|
|
.call = (struct apfl_expr_call) {
|
|
|
|
|
.callee = new_var(pt, 5, 1, "factorial"),
|
|
|
|
|
.arguments = LIST_BEGIN(pt, list)
|
2021-12-18 15:11:23 +00:00
|
|
|
LIST_ADD list_item(false, new_const_expr(pt, 5, 11, num_const(10))),
|
2021-12-17 20:08:03 +00:00
|
|
|
LIST_END,
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
expect_eof(pt);
|
|
|
|
|
|
|
|
|
|
destroy_parser_test(pt);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(map, t) {
|
|
|
|
|
struct parser_test *pt = new_parser_test(t,
|
|
|
|
|
// 123456789012345678901234
|
|
|
|
|
"map := {\n"
|
|
|
|
|
"_ [] -> []\n"
|
|
|
|
|
"f [x ~xs] ->\n"
|
|
|
|
|
" [(f x) ~(map f xs)]\n"
|
|
|
|
|
"}\n"
|
|
|
|
|
);
|
|
|
|
|
expect_expr(pt, (struct apfl_expr) {
|
|
|
|
|
.type = APFL_EXPR_ASSIGNMENT,
|
|
|
|
|
.position = POS(1, 5),
|
|
|
|
|
.assignment = (struct apfl_expr_assignment) {
|
2022-01-08 21:55:24 +00:00
|
|
|
.lhs = assignable_var(pt, "map"),
|
2021-12-17 20:08:03 +00:00
|
|
|
.rhs = BEGIN_NEW(pt, struct apfl_expr) {
|
|
|
|
|
.type = APFL_EXPR_COMPLEX_FUNC,
|
|
|
|
|
.position = POS(1, 8),
|
|
|
|
|
.complex_func = LIST_BEGIN(pt, complex_func)
|
|
|
|
|
LIST_ADD (struct apfl_expr_subfunc) {
|
|
|
|
|
.params = LIST_BEGIN(pt, params)
|
2022-01-07 22:08:25 +00:00
|
|
|
LIST_ADD params_item(false, (struct apfl_expr_param) {
|
2022-01-08 22:20:29 +00:00
|
|
|
.type = APFL_EXPR_PARAM_BLANK,
|
2022-01-07 22:08:25 +00:00
|
|
|
}),
|
|
|
|
|
LIST_ADD params_item(false, (struct apfl_expr_param) {
|
2021-12-17 20:08:03 +00:00
|
|
|
.type = APFL_EXPR_PARAM_LIST,
|
|
|
|
|
.list = LIST_BEGIN(pt, params)
|
|
|
|
|
LIST_END,
|
2022-01-07 22:08:25 +00:00
|
|
|
}),
|
2021-12-17 20:08:03 +00:00
|
|
|
LIST_END,
|
2022-01-22 16:16:28 +00:00
|
|
|
.body = create_body(pt, LIST_BEGIN(pt, body)
|
2021-12-17 20:08:03 +00:00
|
|
|
LIST_ADD (struct apfl_expr) {
|
|
|
|
|
.type = APFL_EXPR_LIST,
|
|
|
|
|
.position = POS(2, 9),
|
|
|
|
|
.list = LIST_BEGIN(pt, list)
|
|
|
|
|
LIST_END,
|
|
|
|
|
},
|
2022-01-22 16:16:28 +00:00
|
|
|
LIST_END),
|
2021-12-17 20:08:03 +00:00
|
|
|
},
|
|
|
|
|
LIST_ADD (struct apfl_expr_subfunc) {
|
|
|
|
|
.params = LIST_BEGIN(pt, params)
|
2022-01-07 22:08:25 +00:00
|
|
|
LIST_ADD params_item(false, (struct apfl_expr_param) {
|
2021-12-17 20:08:03 +00:00
|
|
|
.type = APFL_EXPR_PARAM_VAR,
|
|
|
|
|
.var = new_string(pt, "f"),
|
2022-01-07 22:08:25 +00:00
|
|
|
}),
|
|
|
|
|
LIST_ADD params_item(false, (struct apfl_expr_param) {
|
2021-12-17 20:08:03 +00:00
|
|
|
.type = APFL_EXPR_PARAM_LIST,
|
|
|
|
|
.list = LIST_BEGIN(pt, params)
|
2022-01-07 22:08:25 +00:00
|
|
|
LIST_ADD params_item(false, (struct apfl_expr_param) {
|
2021-12-17 20:08:03 +00:00
|
|
|
.type = APFL_EXPR_PARAM_VAR,
|
|
|
|
|
.var = new_string(pt, "x"),
|
2022-01-07 22:08:25 +00:00
|
|
|
}),
|
|
|
|
|
LIST_ADD params_item(true, (struct apfl_expr_param) {
|
|
|
|
|
.type = APFL_EXPR_PARAM_VAR,
|
|
|
|
|
.var = new_string(pt, "xs"),
|
|
|
|
|
}),
|
2021-12-17 20:08:03 +00:00
|
|
|
LIST_END,
|
2022-01-07 22:08:25 +00:00
|
|
|
}),
|
2021-12-17 20:08:03 +00:00
|
|
|
LIST_END,
|
2022-01-22 16:16:28 +00:00
|
|
|
.body = create_body(pt, LIST_BEGIN(pt, body)
|
2021-12-17 20:08:03 +00:00
|
|
|
LIST_ADD (struct apfl_expr) {
|
|
|
|
|
.type = APFL_EXPR_LIST,
|
|
|
|
|
.position = POS(4, 5),
|
|
|
|
|
.list = LIST_BEGIN(pt, list)
|
|
|
|
|
LIST_ADD list_item(false, BEGIN_NEW(pt, struct apfl_expr) {
|
|
|
|
|
.type = APFL_EXPR_CALL,
|
|
|
|
|
.position = POS(4, 6),
|
|
|
|
|
.call = (struct apfl_expr_call) {
|
|
|
|
|
.callee = new_var(pt, 4, 7, "f"),
|
|
|
|
|
.arguments = LIST_BEGIN(pt, list)
|
|
|
|
|
LIST_ADD list_item(false, new_var(pt, 4, 9, "x")),
|
|
|
|
|
LIST_END,
|
|
|
|
|
},
|
|
|
|
|
} END_NEW),
|
|
|
|
|
LIST_ADD list_item(true, BEGIN_NEW(pt, struct apfl_expr) {
|
|
|
|
|
.type = APFL_EXPR_CALL,
|
|
|
|
|
.position = POS(4, 13),
|
|
|
|
|
.call = (struct apfl_expr_call) {
|
|
|
|
|
.callee = new_var(pt, 4, 14, "map"),
|
|
|
|
|
.arguments = LIST_BEGIN(pt, list)
|
|
|
|
|
LIST_ADD list_item(false, new_var(pt, 4, 18, "f")),
|
|
|
|
|
LIST_ADD list_item(false, new_var(pt, 4, 20, "xs")),
|
|
|
|
|
LIST_END
|
|
|
|
|
},
|
|
|
|
|
} END_NEW),
|
|
|
|
|
LIST_END,
|
|
|
|
|
},
|
2022-01-22 16:16:28 +00:00
|
|
|
LIST_END)
|
2021-12-17 20:08:03 +00:00
|
|
|
},
|
|
|
|
|
LIST_END,
|
|
|
|
|
} END_NEW,
|
2021-12-16 22:42:37 +00:00
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
expect_eof(pt);
|
|
|
|
|
destroy_parser_test(pt);
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-18 15:12:37 +00:00
|
|
|
TEST(lists, t) {
|
|
|
|
|
struct parser_test *pt = new_parser_test(t,
|
|
|
|
|
// 123456789
|
|
|
|
|
"[1, 2 3,,# some comment\n"
|
|
|
|
|
",4 ~x ]"
|
|
|
|
|
);
|
|
|
|
|
expect_expr(pt, (struct apfl_expr) {
|
|
|
|
|
.type = APFL_EXPR_LIST,
|
|
|
|
|
.position = POS(1, 1),
|
|
|
|
|
.list = LIST_BEGIN(pt, list)
|
|
|
|
|
LIST_ADD list_item(false, new_const_expr(pt, 1, 2, num_const(1))),
|
|
|
|
|
LIST_ADD list_item(false, new_const_expr(pt, 1, 5, num_const(2))),
|
|
|
|
|
LIST_ADD list_item(false, new_const_expr(pt, 1, 7, num_const(3))),
|
|
|
|
|
LIST_ADD list_item(false, new_const_expr(pt, 2, 2, num_const(4))),
|
|
|
|
|
LIST_ADD list_item(true, new_var(pt, 2, 5, "x")),
|
|
|
|
|
LIST_END,
|
|
|
|
|
});
|
|
|
|
|
// expect_eof(pt);
|
|
|
|
|
destroy_parser_test(pt);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(stringification, t) {
|
|
|
|
|
struct parser_test *pt = new_parser_test(t, "'hello");
|
|
|
|
|
expect_expr(pt, const_expr(1, 1, string_const(pt, "hello")));
|
|
|
|
|
expect_eof(pt);
|
|
|
|
|
destroy_parser_test(pt);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(line_continuation, t) {
|
|
|
|
|
struct parser_test *pt = new_parser_test(t,
|
|
|
|
|
"foo \\ # A comment\n"
|
|
|
|
|
"bar \\\n"
|
|
|
|
|
"\\\n"
|
|
|
|
|
"baz\n"
|
|
|
|
|
"xyz"
|
|
|
|
|
);
|
|
|
|
|
expect_expr(pt, (struct apfl_expr) {
|
|
|
|
|
.type = APFL_EXPR_CALL,
|
|
|
|
|
.position = POS(1, 1),
|
|
|
|
|
.call = (struct apfl_expr_call) {
|
|
|
|
|
.callee = new_var(pt, 1, 1, "foo"),
|
|
|
|
|
.arguments = LIST_BEGIN(pt, list)
|
|
|
|
|
LIST_ADD list_item(false, new_var(pt, 2, 1, "bar")),
|
|
|
|
|
LIST_ADD list_item(false, new_var(pt, 4, 1, "baz")),
|
|
|
|
|
LIST_END,
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
expect_expr(pt, var(pt, 5, 1, "xyz"));
|
|
|
|
|
expect_eof(pt);
|
|
|
|
|
destroy_parser_test(pt);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(dict, t) {
|
|
|
|
|
struct parser_test *pt = new_parser_test(t,
|
|
|
|
|
// 1 2 3
|
|
|
|
|
// 12345678901234567890123456789012
|
|
|
|
|
"[ a -> b, 'c -> d\n"
|
|
|
|
|
"e ->f g ->h\n"
|
|
|
|
|
"(i j) -> [1 2 3],, 42 -> [x -> y]\n"
|
|
|
|
|
"true -> false]\n"
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
expect_expr(pt, (struct apfl_expr) {
|
|
|
|
|
.type = APFL_EXPR_DICT,
|
|
|
|
|
.position = POS(1, 1),
|
|
|
|
|
.dict = LIST_BEGIN(pt, dict)
|
|
|
|
|
LIST_ADD (struct apfl_expr_dict_pair) {
|
|
|
|
|
.k = new_var(pt, 1, 3, "a"),
|
|
|
|
|
.v = new_var(pt, 1, 8, "b"),
|
|
|
|
|
},
|
|
|
|
|
LIST_ADD (struct apfl_expr_dict_pair) {
|
|
|
|
|
.k = new_const_expr(pt, 1, 11, string_const(pt, "c")),
|
|
|
|
|
.v = new_var(pt, 1, 17, "d"),
|
|
|
|
|
},
|
|
|
|
|
LIST_ADD (struct apfl_expr_dict_pair) {
|
|
|
|
|
.k = new_var(pt, 2, 1, "e"),
|
|
|
|
|
.v = new_var(pt, 2, 5, "f"),
|
|
|
|
|
},
|
|
|
|
|
LIST_ADD (struct apfl_expr_dict_pair) {
|
|
|
|
|
.k = new_var(pt, 2, 7, "g"),
|
|
|
|
|
.v = new_var(pt, 2, 11, "h"),
|
|
|
|
|
},
|
|
|
|
|
LIST_ADD (struct apfl_expr_dict_pair) {
|
|
|
|
|
.k = BEGIN_NEW(pt, struct apfl_expr) {
|
|
|
|
|
.type = APFL_EXPR_CALL,
|
|
|
|
|
.position = POS(3, 1),
|
|
|
|
|
.call = (struct apfl_expr_call) {
|
|
|
|
|
.callee = new_var(pt, 3, 2, "i"),
|
|
|
|
|
.arguments = LIST_BEGIN(pt, list)
|
|
|
|
|
LIST_ADD list_item(false, new_var(pt, 3, 4, "j")),
|
|
|
|
|
LIST_END,
|
|
|
|
|
},
|
|
|
|
|
} END_NEW,
|
|
|
|
|
.v = BEGIN_NEW(pt, struct apfl_expr) {
|
|
|
|
|
.type = APFL_EXPR_LIST,
|
|
|
|
|
.position = POS(3, 10),
|
|
|
|
|
.list = LIST_BEGIN(pt, list)
|
|
|
|
|
LIST_ADD list_item(false, new_const_expr(pt, 3, 11, num_const(1))),
|
|
|
|
|
LIST_ADD list_item(false, new_const_expr(pt, 3, 13, num_const(2))),
|
|
|
|
|
LIST_ADD list_item(false, new_const_expr(pt, 3, 15, num_const(3))),
|
|
|
|
|
LIST_END,
|
|
|
|
|
} END_NEW,
|
|
|
|
|
},
|
|
|
|
|
LIST_ADD (struct apfl_expr_dict_pair) {
|
|
|
|
|
.k = new_const_expr(pt, 3, 20, num_const(42)),
|
|
|
|
|
.v = BEGIN_NEW(pt, struct apfl_expr) {
|
|
|
|
|
.type = APFL_EXPR_DICT,
|
|
|
|
|
.position = POS(3, 26),
|
|
|
|
|
.dict = LIST_BEGIN(pt, dict)
|
|
|
|
|
LIST_ADD (struct apfl_expr_dict_pair) {
|
|
|
|
|
.k = new_var(pt, 3, 27, "x"),
|
|
|
|
|
.v = new_var(pt, 3, 32, "y"),
|
|
|
|
|
},
|
|
|
|
|
LIST_END,
|
|
|
|
|
} END_NEW,
|
|
|
|
|
},
|
|
|
|
|
LIST_ADD (struct apfl_expr_dict_pair) {
|
|
|
|
|
.k = new_const_expr(pt, 4, 1, bool_const(true)),
|
|
|
|
|
.v = new_const_expr(pt, 4, 9, bool_const(false)),
|
|
|
|
|
},
|
|
|
|
|
LIST_END
|
|
|
|
|
});
|
|
|
|
|
expect_eof(pt);
|
|
|
|
|
|
|
|
|
|
destroy_parser_test(pt);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(assignment, t) {
|
|
|
|
|
struct parser_test *pt = new_parser_test(t,
|
|
|
|
|
// 123456
|
|
|
|
|
"a = b\n"
|
|
|
|
|
// 1 2
|
|
|
|
|
// 12345678901234567890123456
|
|
|
|
|
"foo.bar := bar?pred = baz\n"
|
|
|
|
|
// 1 2 3
|
|
|
|
|
// 12345678901234567890123456789012345
|
|
|
|
|
"[[1 2] ~xs] = bla@(a b) := abc ~xyz\n"
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
expect_expr(pt, (struct apfl_expr) {
|
|
|
|
|
.type = APFL_EXPR_ASSIGNMENT,
|
|
|
|
|
.position = POS(1, 3),
|
|
|
|
|
.assignment = (struct apfl_expr_assignment) {
|
|
|
|
|
.local = false,
|
2022-01-08 21:55:24 +00:00
|
|
|
.lhs = assignable_var(pt, "a"),
|
2021-12-18 15:12:37 +00:00
|
|
|
.rhs = new_var(pt, 1, 5, "b"),
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
expect_expr(pt, (struct apfl_expr) {
|
|
|
|
|
.type = APFL_EXPR_ASSIGNMENT,
|
|
|
|
|
.position = POS(2, 9),
|
|
|
|
|
.assignment = (struct apfl_expr_assignment) {
|
|
|
|
|
.local = true,
|
|
|
|
|
.lhs = (struct apfl_expr_assignable) {
|
2022-01-08 21:55:24 +00:00
|
|
|
.type = APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER,
|
|
|
|
|
.var_or_member = {
|
|
|
|
|
.type = APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER_DOT,
|
|
|
|
|
.dot = {
|
|
|
|
|
.lhs = BEGIN_NEW(pt, struct apfl_expr_assignable_var_or_member) {
|
|
|
|
|
.type = APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER_VAR,
|
|
|
|
|
.var = new_string(pt, "foo"),
|
|
|
|
|
} END_NEW,
|
|
|
|
|
.rhs = new_string(pt, "bar"),
|
|
|
|
|
},
|
2021-12-18 15:12:37 +00:00
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
.rhs = BEGIN_NEW(pt, struct apfl_expr) {
|
|
|
|
|
.type = APFL_EXPR_ASSIGNMENT,
|
|
|
|
|
.position = POS(2, 21),
|
|
|
|
|
.assignment = (struct apfl_expr_assignment) {
|
|
|
|
|
.local = false,
|
|
|
|
|
.lhs = (struct apfl_expr_assignable) {
|
|
|
|
|
.type = APFL_EXPR_ASSIGNABLE_PREDICATE,
|
|
|
|
|
.predicate = (struct apfl_expr_assignable_predicate) {
|
2022-01-08 21:55:24 +00:00
|
|
|
.lhs = new_assignable_var(pt, "bar"),
|
2021-12-18 15:12:37 +00:00
|
|
|
.rhs = new_var(pt, 2, 16, "pred"),
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
.rhs = new_var(pt, 2, 23, "baz"),
|
|
|
|
|
},
|
|
|
|
|
} END_NEW,
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
expect_expr(pt, (struct apfl_expr) {
|
|
|
|
|
.type = APFL_EXPR_ASSIGNMENT,
|
|
|
|
|
.position = POS(3, 13),
|
|
|
|
|
.assignment = (struct apfl_expr_assignment) {
|
|
|
|
|
.local = false,
|
|
|
|
|
.lhs = (struct apfl_expr_assignable) {
|
|
|
|
|
.type = APFL_EXPR_ASSIGNABLE_LIST,
|
|
|
|
|
.list = LIST_BEGIN(pt, assignable_list)
|
2022-01-07 22:08:25 +00:00
|
|
|
LIST_ADD assignable_list_item(false, (struct apfl_expr_assignable) {
|
2021-12-18 15:12:37 +00:00
|
|
|
.type = APFL_EXPR_ASSIGNABLE_LIST,
|
|
|
|
|
.list = LIST_BEGIN(pt, assignable_list)
|
2022-01-07 22:08:25 +00:00
|
|
|
LIST_ADD assignable_list_item(false, (struct apfl_expr_assignable) {
|
2021-12-18 15:12:37 +00:00
|
|
|
.type = APFL_EXPR_ASSIGNABLE_CONSTANT,
|
|
|
|
|
.constant = num_const(1),
|
2022-01-07 22:08:25 +00:00
|
|
|
}),
|
|
|
|
|
LIST_ADD assignable_list_item(false, (struct apfl_expr_assignable) {
|
2021-12-18 15:12:37 +00:00
|
|
|
.type = APFL_EXPR_ASSIGNABLE_CONSTANT,
|
|
|
|
|
.constant = num_const(2),
|
2022-01-07 22:08:25 +00:00
|
|
|
}),
|
2021-12-18 15:12:37 +00:00
|
|
|
LIST_END,
|
2022-01-07 22:08:25 +00:00
|
|
|
}),
|
2022-01-08 21:55:24 +00:00
|
|
|
LIST_ADD assignable_list_item(true, assignable_var(pt, "xs")),
|
2021-12-18 15:12:37 +00:00
|
|
|
LIST_END,
|
|
|
|
|
},
|
|
|
|
|
.rhs = BEGIN_NEW(pt, struct apfl_expr) {
|
|
|
|
|
.type = APFL_EXPR_ASSIGNMENT,
|
|
|
|
|
.position = POS(3, 25),
|
|
|
|
|
.assignment = (struct apfl_expr_assignment) {
|
|
|
|
|
.local = true,
|
|
|
|
|
.lhs = (struct apfl_expr_assignable) {
|
2022-01-08 21:55:24 +00:00
|
|
|
.type = APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER,
|
|
|
|
|
.var_or_member = {
|
|
|
|
|
.type = APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER_AT,
|
|
|
|
|
.at = {
|
|
|
|
|
.lhs = BEGIN_NEW(pt, struct apfl_expr_assignable_var_or_member) {
|
|
|
|
|
.type = APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER_VAR,
|
|
|
|
|
.var = new_string(pt, "bla"),
|
|
|
|
|
} END_NEW,
|
|
|
|
|
.rhs = BEGIN_NEW(pt, struct apfl_expr) {
|
|
|
|
|
.type = APFL_EXPR_CALL,
|
|
|
|
|
.position = POS(3, 19),
|
|
|
|
|
.call = (struct apfl_expr_call) {
|
|
|
|
|
.callee = new_var(pt, 3, 20, "a"),
|
|
|
|
|
.arguments = LIST_BEGIN(pt, list)
|
|
|
|
|
LIST_ADD list_item(false, new_var(pt, 3, 22, "b")),
|
|
|
|
|
LIST_END,
|
|
|
|
|
},
|
|
|
|
|
} END_NEW,
|
|
|
|
|
},
|
|
|
|
|
}
|
2021-12-18 15:12:37 +00:00
|
|
|
},
|
|
|
|
|
.rhs = BEGIN_NEW(pt, struct apfl_expr) {
|
|
|
|
|
.type = APFL_EXPR_CALL,
|
|
|
|
|
.position = POS(3, 28),
|
|
|
|
|
.call = (struct apfl_expr_call) {
|
|
|
|
|
.callee = new_var(pt, 3, 28, "abc"),
|
|
|
|
|
.arguments = LIST_BEGIN(pt, list)
|
|
|
|
|
LIST_ADD list_item(true, new_var(pt, 3, 33, "xyz")),
|
|
|
|
|
LIST_END,
|
|
|
|
|
},
|
|
|
|
|
} END_NEW,
|
|
|
|
|
},
|
|
|
|
|
} END_NEW,
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
expect_eof(pt);
|
|
|
|
|
|
|
|
|
|
destroy_parser_test(pt);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(simple_function, t) {
|
|
|
|
|
struct parser_test *pt = new_parser_test(t,
|
|
|
|
|
// 12345678901
|
|
|
|
|
"{{\n"
|
|
|
|
|
" }; foo \\\n"
|
|
|
|
|
"bar { # comment...\n"
|
|
|
|
|
" baz = 10\n"
|
|
|
|
|
" a\n"
|
|
|
|
|
" b\n"
|
|
|
|
|
"}; c}\n"
|
|
|
|
|
);
|
|
|
|
|
expect_expr(pt, (struct apfl_expr) {
|
|
|
|
|
.type = APFL_EXPR_SIMPLE_FUNC,
|
|
|
|
|
.position = POS(1, 1),
|
2022-01-22 16:16:28 +00:00
|
|
|
.simple_func = create_body(pt, LIST_BEGIN(pt, body)
|
2021-12-18 15:12:37 +00:00
|
|
|
LIST_ADD (struct apfl_expr) {
|
|
|
|
|
.type = APFL_EXPR_SIMPLE_FUNC,
|
|
|
|
|
.position = POS(1, 2),
|
2022-01-22 16:16:28 +00:00
|
|
|
.simple_func = create_body(pt, LIST_BEGIN(pt, body)
|
|
|
|
|
LIST_END),
|
2021-12-18 15:12:37 +00:00
|
|
|
},
|
|
|
|
|
LIST_ADD (struct apfl_expr) {
|
|
|
|
|
.type = APFL_EXPR_CALL,
|
|
|
|
|
.position = POS(2, 8),
|
|
|
|
|
.call = (struct apfl_expr_call) {
|
|
|
|
|
.callee = new_var(pt, 2, 8, "foo"),
|
|
|
|
|
.arguments = LIST_BEGIN(pt, list)
|
|
|
|
|
LIST_ADD list_item(false, new_var(pt, 3, 1, "bar")),
|
|
|
|
|
LIST_ADD list_item(false, BEGIN_NEW(pt, struct apfl_expr) {
|
|
|
|
|
.type = APFL_EXPR_SIMPLE_FUNC,
|
|
|
|
|
.position = POS(3, 5),
|
2022-01-22 16:16:28 +00:00
|
|
|
.simple_func = create_body(pt, LIST_BEGIN(pt, body)
|
2021-12-18 15:12:37 +00:00
|
|
|
LIST_ADD (struct apfl_expr) {
|
|
|
|
|
.type = APFL_EXPR_ASSIGNMENT,
|
|
|
|
|
.position = POS(4, 9),
|
|
|
|
|
.assignment = (struct apfl_expr_assignment) {
|
|
|
|
|
.local = false,
|
2022-01-08 21:55:24 +00:00
|
|
|
.lhs = assignable_var(pt, "baz"),
|
2021-12-18 15:12:37 +00:00
|
|
|
.rhs = new_const_expr(pt, 4, 11, num_const(10)),
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
LIST_ADD var(pt, 5, 5, "a"),
|
|
|
|
|
LIST_ADD var(pt, 6, 5, "b"),
|
2022-01-22 16:16:28 +00:00
|
|
|
LIST_END),
|
2021-12-18 15:12:37 +00:00
|
|
|
} END_NEW),
|
|
|
|
|
LIST_END,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
LIST_ADD var(pt, 7, 4, "c"),
|
2022-01-22 16:16:28 +00:00
|
|
|
LIST_END),
|
2021-12-18 15:12:37 +00:00
|
|
|
});
|
|
|
|
|
destroy_parser_test(pt);
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-18 16:06:17 +00:00
|
|
|
TEST(complex_function, t) {
|
|
|
|
|
struct parser_test *pt = new_parser_test(t,
|
|
|
|
|
// 1 2
|
|
|
|
|
// 12345678901234567890123456789
|
|
|
|
|
"{ a ~b c?d?(e f) -> foo; bar\n"
|
|
|
|
|
" baz\n"
|
|
|
|
|
"1 [] ->; x [y?x ~ys] ->\n"
|
|
|
|
|
" a\n"
|
|
|
|
|
" b\n"
|
|
|
|
|
" c { x -> y }\n"
|
|
|
|
|
" \n"
|
|
|
|
|
"}\n"
|
|
|
|
|
);
|
|
|
|
|
expect_expr(pt, (struct apfl_expr) {
|
|
|
|
|
.type = APFL_EXPR_COMPLEX_FUNC,
|
|
|
|
|
.position = POS(1, 1),
|
|
|
|
|
.complex_func = LIST_BEGIN(pt, complex_func)
|
|
|
|
|
LIST_ADD {
|
|
|
|
|
.params = LIST_BEGIN(pt, params)
|
2022-01-07 22:08:25 +00:00
|
|
|
LIST_ADD params_item(false, (struct apfl_expr_param) {
|
2021-12-18 16:06:17 +00:00
|
|
|
.type = APFL_EXPR_PARAM_VAR,
|
|
|
|
|
.var = new_string(pt, "a"),
|
2022-01-07 22:08:25 +00:00
|
|
|
}),
|
|
|
|
|
LIST_ADD params_item(true, (struct apfl_expr_param) {
|
|
|
|
|
.type = APFL_EXPR_PARAM_VAR,
|
|
|
|
|
.var = new_string(pt, "b"),
|
|
|
|
|
}),
|
|
|
|
|
LIST_ADD params_item(false, (struct apfl_expr_param) {
|
2021-12-18 16:06:17 +00:00
|
|
|
.type = APFL_EXPR_PARAM_PREDICATE,
|
|
|
|
|
.predicate = {
|
|
|
|
|
.lhs = BEGIN_NEW(pt, struct apfl_expr_param) {
|
|
|
|
|
.type = APFL_EXPR_PARAM_PREDICATE,
|
|
|
|
|
.predicate = {
|
|
|
|
|
.lhs = BEGIN_NEW(pt, struct apfl_expr_param) {
|
|
|
|
|
.type = APFL_EXPR_PARAM_VAR,
|
|
|
|
|
.var = new_string(pt, "c")
|
|
|
|
|
} END_NEW,
|
|
|
|
|
.rhs = new_var(pt, 1, 10, "d"),
|
|
|
|
|
},
|
|
|
|
|
} END_NEW,
|
|
|
|
|
.rhs = BEGIN_NEW(pt, struct apfl_expr) {
|
|
|
|
|
.type = APFL_EXPR_CALL,
|
|
|
|
|
.position = POS(1, 12),
|
|
|
|
|
.call = {
|
|
|
|
|
.callee = new_var(pt, 1, 13, "e"),
|
|
|
|
|
.arguments = LIST_BEGIN(pt, list)
|
|
|
|
|
LIST_ADD list_item(false, new_var(pt, 1, 15, "f")),
|
|
|
|
|
LIST_END,
|
|
|
|
|
},
|
|
|
|
|
} END_NEW,
|
|
|
|
|
},
|
2022-01-07 22:08:25 +00:00
|
|
|
}),
|
2021-12-18 16:06:17 +00:00
|
|
|
LIST_END,
|
2022-01-22 16:16:28 +00:00
|
|
|
.body = create_body(pt, LIST_BEGIN(pt, body)
|
2021-12-18 16:06:17 +00:00
|
|
|
LIST_ADD var(pt, 1, 21, "foo"),
|
|
|
|
|
LIST_ADD var(pt, 1, 26, "bar"),
|
|
|
|
|
LIST_ADD var(pt, 2, 5, "baz"),
|
2022-01-22 16:16:28 +00:00
|
|
|
LIST_END),
|
2021-12-18 16:06:17 +00:00
|
|
|
},
|
|
|
|
|
LIST_ADD {
|
|
|
|
|
.params = LIST_BEGIN(pt, params)
|
2022-01-07 22:08:25 +00:00
|
|
|
LIST_ADD params_item(false, (struct apfl_expr_param) {
|
2021-12-18 16:06:17 +00:00
|
|
|
.type = APFL_EXPR_PARAM_CONSTANT,
|
|
|
|
|
.constant = num_const(1),
|
2022-01-07 22:08:25 +00:00
|
|
|
}),
|
|
|
|
|
LIST_ADD params_item(false, (struct apfl_expr_param) {
|
2021-12-18 16:06:17 +00:00
|
|
|
.type = APFL_EXPR_PARAM_LIST,
|
|
|
|
|
.list = LIST_BEGIN(pt, params)
|
|
|
|
|
LIST_END,
|
2022-01-07 22:08:25 +00:00
|
|
|
}),
|
2021-12-18 16:06:17 +00:00
|
|
|
LIST_END,
|
2022-01-22 16:16:28 +00:00
|
|
|
.body = create_body(pt, LIST_BEGIN(pt, body)
|
|
|
|
|
LIST_END),
|
2021-12-18 16:06:17 +00:00
|
|
|
},
|
|
|
|
|
LIST_ADD {
|
|
|
|
|
.params = LIST_BEGIN(pt, params)
|
2022-01-07 22:08:25 +00:00
|
|
|
LIST_ADD params_item(false, (struct apfl_expr_param) {
|
2021-12-18 16:06:17 +00:00
|
|
|
.type = APFL_EXPR_PARAM_VAR,
|
|
|
|
|
.var = new_string(pt, "x"),
|
2022-01-07 22:08:25 +00:00
|
|
|
}),
|
|
|
|
|
LIST_ADD params_item(false, (struct apfl_expr_param) {
|
2021-12-18 16:06:17 +00:00
|
|
|
.type = APFL_EXPR_PARAM_LIST,
|
|
|
|
|
.list = LIST_BEGIN(pt, params)
|
2022-01-07 22:08:25 +00:00
|
|
|
LIST_ADD params_item(false, (struct apfl_expr_param) {
|
2021-12-18 16:06:17 +00:00
|
|
|
.type = APFL_EXPR_PARAM_PREDICATE,
|
|
|
|
|
.predicate = {
|
|
|
|
|
.lhs = BEGIN_NEW(pt, struct apfl_expr_param) {
|
|
|
|
|
.type = APFL_EXPR_PARAM_VAR,
|
|
|
|
|
.var = new_string(pt, "y"),
|
|
|
|
|
} END_NEW,
|
|
|
|
|
.rhs = new_var(pt, 3, 15, "x"),
|
|
|
|
|
},
|
2022-01-07 22:08:25 +00:00
|
|
|
}),
|
|
|
|
|
LIST_ADD params_item(true, (struct apfl_expr_param) {
|
|
|
|
|
.type = APFL_EXPR_PARAM_VAR,
|
|
|
|
|
.var = new_string(pt, "ys"),
|
|
|
|
|
}),
|
2021-12-18 16:06:17 +00:00
|
|
|
LIST_END
|
2022-01-07 22:08:25 +00:00
|
|
|
}),
|
2021-12-18 16:06:17 +00:00
|
|
|
LIST_END,
|
2022-01-22 16:16:28 +00:00
|
|
|
.body = create_body(pt, LIST_BEGIN(pt, body)
|
2021-12-18 16:06:17 +00:00
|
|
|
LIST_ADD var(pt, 4, 5, "a"),
|
|
|
|
|
LIST_ADD var(pt, 5, 5, "b"),
|
|
|
|
|
LIST_ADD {
|
|
|
|
|
.type = APFL_EXPR_CALL,
|
|
|
|
|
.position = POS(6, 5),
|
|
|
|
|
.call = {
|
|
|
|
|
.callee = new_var(pt, 6, 5, "c"),
|
|
|
|
|
.arguments = LIST_BEGIN(pt, list)
|
|
|
|
|
LIST_ADD list_item(false, BEGIN_NEW(pt, struct apfl_expr) {
|
|
|
|
|
.type = APFL_EXPR_COMPLEX_FUNC,
|
|
|
|
|
.position = POS(6, 7),
|
|
|
|
|
.complex_func = LIST_BEGIN(pt, complex_func)
|
|
|
|
|
LIST_ADD {
|
|
|
|
|
.params = LIST_BEGIN(pt, params)
|
2022-01-07 22:08:25 +00:00
|
|
|
LIST_ADD params_item(false, (struct apfl_expr_param) {
|
2021-12-18 16:06:17 +00:00
|
|
|
.type = APFL_EXPR_PARAM_VAR,
|
|
|
|
|
.var = new_string(pt, "x"),
|
2022-01-07 22:08:25 +00:00
|
|
|
}),
|
2021-12-18 16:06:17 +00:00
|
|
|
LIST_END,
|
2022-01-22 16:16:28 +00:00
|
|
|
.body = create_body(pt, LIST_BEGIN(pt, body)
|
2021-12-18 16:06:17 +00:00
|
|
|
LIST_ADD var(pt, 6, 14, "y"),
|
2022-01-22 16:16:28 +00:00
|
|
|
LIST_END),
|
2021-12-18 16:06:17 +00:00
|
|
|
}
|
|
|
|
|
LIST_END
|
|
|
|
|
} END_NEW),
|
|
|
|
|
LIST_END,
|
|
|
|
|
},
|
|
|
|
|
}
|
2022-01-22 16:16:28 +00:00
|
|
|
LIST_END),
|
2021-12-18 16:06:17 +00:00
|
|
|
},
|
|
|
|
|
LIST_END,
|
|
|
|
|
});
|
|
|
|
|
expect_eof(pt);
|
|
|
|
|
destroy_parser_test(pt);
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-06 21:50:14 +00:00
|
|
|
TEST(function_calls, t) {
|
|
|
|
|
struct parser_test *pt = new_parser_test(t,
|
|
|
|
|
"foo\n"
|
|
|
|
|
"(foo)\n"
|
|
|
|
|
"foo bar\n"
|
|
|
|
|
"(foo bar)\n"
|
|
|
|
|
"((foo bar))\n"
|
|
|
|
|
"foo bar ~baz\n"
|
|
|
|
|
);
|
|
|
|
|
expect_expr(pt, (struct apfl_expr) {
|
|
|
|
|
.type = APFL_EXPR_VAR,
|
|
|
|
|
.position = POS(1, 1),
|
|
|
|
|
.var = new_string(pt, "foo"),
|
|
|
|
|
});
|
|
|
|
|
expect_expr(pt, (struct apfl_expr) {
|
|
|
|
|
.type = APFL_EXPR_CALL,
|
|
|
|
|
.position = POS(2, 1),
|
|
|
|
|
.call = {
|
|
|
|
|
.callee = new_var(pt, 2, 2, "foo"),
|
|
|
|
|
.arguments = LIST_BEGIN(pt, list)
|
|
|
|
|
LIST_END,
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
expect_expr(pt, (struct apfl_expr) {
|
|
|
|
|
.type = APFL_EXPR_CALL,
|
|
|
|
|
.position = POS(3, 1),
|
|
|
|
|
.call = {
|
|
|
|
|
.callee = new_var(pt, 3, 1, "foo"),
|
|
|
|
|
.arguments = LIST_BEGIN(pt, list)
|
|
|
|
|
LIST_ADD list_item(false, new_var(pt, 3, 5, "bar")),
|
|
|
|
|
LIST_END,
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
expect_expr(pt, (struct apfl_expr) {
|
|
|
|
|
.type = APFL_EXPR_CALL,
|
|
|
|
|
.position = POS(4, 1),
|
|
|
|
|
.call = {
|
|
|
|
|
.callee = new_var(pt, 4, 2, "foo"),
|
|
|
|
|
.arguments = LIST_BEGIN(pt, list)
|
|
|
|
|
LIST_ADD list_item(false, new_var(pt, 4, 6, "bar")),
|
|
|
|
|
LIST_END,
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
expect_expr(pt, (struct apfl_expr) {
|
|
|
|
|
.type = APFL_EXPR_CALL,
|
|
|
|
|
.position = POS(5, 1),
|
|
|
|
|
.call = {
|
|
|
|
|
.callee = BEGIN_NEW(pt, struct apfl_expr) {
|
|
|
|
|
.type = APFL_EXPR_CALL,
|
|
|
|
|
.position = POS(5, 2),
|
|
|
|
|
.call = {
|
|
|
|
|
.callee = new_var(pt, 5, 3, "foo"),
|
|
|
|
|
.arguments = LIST_BEGIN(pt, list)
|
|
|
|
|
LIST_ADD list_item(false, new_var(pt, 5, 7, "bar")),
|
|
|
|
|
LIST_END,
|
|
|
|
|
},
|
|
|
|
|
} END_NEW,
|
|
|
|
|
.arguments = LIST_BEGIN(pt, list)
|
|
|
|
|
LIST_END
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
expect_expr(pt, (struct apfl_expr) {
|
|
|
|
|
.type = APFL_EXPR_CALL,
|
|
|
|
|
.position = POS(6, 1),
|
|
|
|
|
.call = {
|
|
|
|
|
.callee = new_var(pt, 6, 1, "foo"),
|
|
|
|
|
.arguments = LIST_BEGIN(pt, list)
|
|
|
|
|
LIST_ADD list_item(false, new_var(pt, 6, 5, "bar")),
|
|
|
|
|
LIST_ADD list_item(true, new_var(pt, 6, 10, "baz")),
|
|
|
|
|
LIST_END,
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
expect_eof(pt);
|
|
|
|
|
destroy_parser_test(pt);
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-08 21:55:24 +00:00
|
|
|
TEST(member_assignment_with_predicate, t) {
|
|
|
|
|
struct parser_test *pt = new_parser_test(t, "a.b@c?d?e = f");
|
|
|
|
|
expect_expr(pt, (struct apfl_expr) {
|
|
|
|
|
.type = APFL_EXPR_ASSIGNMENT,
|
|
|
|
|
.position = POS(1, 11),
|
|
|
|
|
.assignment = {
|
|
|
|
|
.lhs = {
|
|
|
|
|
.type = APFL_EXPR_ASSIGNABLE_PREDICATE,
|
|
|
|
|
.predicate = {
|
|
|
|
|
.lhs = BEGIN_NEW(pt, struct apfl_expr_assignable) {
|
|
|
|
|
.type = APFL_EXPR_ASSIGNABLE_PREDICATE,
|
|
|
|
|
.predicate = {
|
|
|
|
|
.lhs = BEGIN_NEW(pt, struct apfl_expr_assignable) {
|
|
|
|
|
.type = APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER,
|
|
|
|
|
.var_or_member = {
|
|
|
|
|
.type = APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER_AT,
|
|
|
|
|
.at = {
|
|
|
|
|
.lhs = BEGIN_NEW(pt, struct apfl_expr_assignable_var_or_member) {
|
|
|
|
|
.type = APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER_DOT,
|
|
|
|
|
.dot = {
|
|
|
|
|
.lhs = BEGIN_NEW(pt, struct apfl_expr_assignable_var_or_member) {
|
|
|
|
|
.type = APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER_VAR,
|
|
|
|
|
.var = new_string(pt, "a"),
|
|
|
|
|
} END_NEW,
|
|
|
|
|
.rhs = new_string(pt, "b"),
|
|
|
|
|
},
|
|
|
|
|
} END_NEW,
|
|
|
|
|
.rhs = new_var(pt, 1, 5, "c"),
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
} END_NEW,
|
|
|
|
|
.rhs = new_var(pt, 1, 7, "d"),
|
|
|
|
|
}
|
|
|
|
|
} END_NEW,
|
|
|
|
|
.rhs = new_var(pt, 1, 9, "e"),
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
.rhs = new_var(pt, 1, 13, "f")
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
expect_eof(pt);
|
|
|
|
|
destroy_parser_test(pt);
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-18 23:27:34 +00:00
|
|
|
TEST(err_empty_assignment, t) {
|
|
|
|
|
struct parser_test *pt = new_parser_test(t, "= foo bar");
|
|
|
|
|
expect_error_of_type(pt, APFL_ERR_EMPTY_ASSIGNMENT);
|
|
|
|
|
destroy_parser_test(pt);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(err_mismatching_parens, t) {
|
|
|
|
|
struct parser_test *pt = new_parser_test(t, "{[(}");
|
|
|
|
|
expect_error_of_type(pt, APFL_ERR_UNEXPECTED_TOKEN);
|
|
|
|
|
destroy_parser_test(pt);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(err_unclosed_func, t) {
|
|
|
|
|
struct parser_test *pt = new_parser_test(t, "{ foo -> bar; baz a; b ->");
|
|
|
|
|
expect_error_of_type(pt, APFL_ERR_UNEXPECTED_EOF);
|
|
|
|
|
destroy_parser_test(pt);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(err_unclosed_paren, t) {
|
|
|
|
|
struct parser_test *pt = new_parser_test(t, "(a b.c@d");
|
|
|
|
|
expect_error_of_type(pt, APFL_ERR_UNEXPECTED_EOF_AFTER_TOKEN);
|
|
|
|
|
destroy_parser_test(pt);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(err_assignment_missing_rhs, t) {
|
|
|
|
|
struct parser_test *pt = new_parser_test(t, "foo = bar = ;");
|
|
|
|
|
expect_error_of_type(pt, APFL_ERR_EMPTY_ASSIGNMENT);
|
|
|
|
|
// TODO: Actually kinda weird that we return the same error as in err_empty_assignment.
|
|
|
|
|
destroy_parser_test(pt);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(err_assignment_invalid_lhs, t) {
|
|
|
|
|
struct parser_test *pt = new_parser_test(t, "{foo} = bar");
|
|
|
|
|
expect_error_of_type(pt, APFL_ERR_INVALID_ASSIGNMENT_LHS);
|
|
|
|
|
destroy_parser_test(pt);
|
|
|
|
|
|
|
|
|
|
pt = new_parser_test(t, "(a b) = bar");
|
|
|
|
|
expect_error_of_type(pt, APFL_ERR_INVALID_ASSIGNMENT_LHS);
|
|
|
|
|
destroy_parser_test(pt);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(err_fragments_after_mapsto_in_empty_dict, t) {
|
|
|
|
|
struct parser_test *pt = new_parser_test(t, "[-> foo]");
|
|
|
|
|
expect_error_of_type(pt, APFL_ERR_UNEXPECTED_TOKEN);
|
|
|
|
|
destroy_parser_test(pt);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(err_fragments_after_mapsto_in_dict, t) {
|
|
|
|
|
struct parser_test *pt = new_parser_test(t, "[a -> b, (c d) -> e foo->]");
|
|
|
|
|
expect_error_of_type(pt, APFL_ERR_UNEXPECTED_TOKEN);
|
|
|
|
|
destroy_parser_test(pt);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(err_mapsto_in_list, t) {
|
|
|
|
|
struct parser_test *pt = new_parser_test(t, "[a b c -> d]");
|
|
|
|
|
expect_error_of_type(pt, APFL_ERR_UNEXPECTED_TOKEN);
|
|
|
|
|
destroy_parser_test(pt);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(err_expr_in_param, t) {
|
|
|
|
|
struct parser_test *pt = new_parser_test(t, "{ foo (bar baz) -> }");
|
|
|
|
|
expect_error_of_type(pt, APFL_ERR_UNEXPECTED_EXPRESSION);
|
|
|
|
|
destroy_parser_test(pt);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(err_statements_before_params, t) {
|
|
|
|
|
struct parser_test *pt = new_parser_test(t, "{ a = b = foo bar; 1 \n baz -> }");
|
|
|
|
|
expect_error_of_type(pt, APFL_ERR_STATEMENTS_BEFORE_PARAMETERS);
|
|
|
|
|
destroy_parser_test(pt);
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-05 20:50:50 +00:00
|
|
|
TEST(err_dict_mapsto_missing, t) {
|
|
|
|
|
struct parser_test *pt = new_parser_test(t, "[foo -> bar, baz wtf -> xyu]");
|
|
|
|
|
expect_error_of_type(pt, APFL_ERR_UNEXPECTED_TOKEN);
|
|
|
|
|
destroy_parser_test(pt);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(err_dict_mapsto_too_early, t) {
|
|
|
|
|
struct parser_test *pt = new_parser_test(t, "[foo -> bar -> baz]");
|
|
|
|
|
expect_error_of_type(pt, APFL_ERR_UNEXPECTED_TOKEN);
|
|
|
|
|
destroy_parser_test(pt);
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-07 22:08:25 +00:00
|
|
|
TEST(err_assign_multiple_expands_per_level, t) {
|
|
|
|
|
struct parser_test *pt = new_parser_test(t, "[~foo ~bar] = baz");
|
|
|
|
|
expect_error_of_type(pt, APFL_ERR_ONLY_ONE_EXPAND_ALLOWED);
|
|
|
|
|
destroy_parser_test(pt);
|
|
|
|
|
}
|
|
|
|
|
TEST(err_assign_multiple_expands_per_level_nested, t) {
|
|
|
|
|
struct parser_test *pt = new_parser_test(t, "[1 [~foo ~bar]] = baz");
|
|
|
|
|
expect_error_of_type(pt, APFL_ERR_ONLY_ONE_EXPAND_ALLOWED);
|
|
|
|
|
destroy_parser_test(pt);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(err_params_multiple_expands, t) {
|
|
|
|
|
struct parser_test *pt = new_parser_test(t, "{ ~foo ~bar -> baz }");
|
|
|
|
|
expect_error_of_type(pt, APFL_ERR_ONLY_ONE_EXPAND_ALLOWED);
|
|
|
|
|
destroy_parser_test(pt);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(err_params_multiple_expands_nested, t) {
|
|
|
|
|
struct parser_test *pt = new_parser_test(t, "{ 1 [~foo ~bar] -> baz }");
|
|
|
|
|
expect_error_of_type(pt, APFL_ERR_ONLY_ONE_EXPAND_ALLOWED);
|
|
|
|
|
destroy_parser_test(pt);
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-08 21:55:24 +00:00
|
|
|
TEST(err_unexpected_expression_in_member_assignable, t) {
|
|
|
|
|
struct parser_test *pt = new_parser_test(t, "(foo).bar = 123");
|
|
|
|
|
expect_error_of_type(pt, APFL_ERR_UNEXPECTED_EXPR_IN_MEMBER_ACCESS);
|
|
|
|
|
destroy_parser_test(pt);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(err_unexpected_constant_in_member_assignable, t) {
|
|
|
|
|
struct parser_test *pt = new_parser_test(t, "nil.bar = 456");
|
|
|
|
|
expect_error_of_type(pt, APFL_ERR_UNEXPECTED_CONSTANT_IN_MEMBER_ACCESS);
|
|
|
|
|
destroy_parser_test(pt);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(err_unexpected_assignment_member_access, t) {
|
|
|
|
|
struct parser_test *pt = new_parser_test(t, "[1 2 3]@bar = 456");
|
|
|
|
|
expect_error_of_type(pt, APFL_ERR_UNEXPECTED_TOKEN);
|
|
|
|
|
destroy_parser_test(pt);
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-16 22:42:37 +00:00
|
|
|
TESTS_BEGIN
|
|
|
|
|
ADDTEST(empty),
|
|
|
|
|
ADDTEST(hello_world),
|
2021-12-17 20:08:03 +00:00
|
|
|
ADDTEST(empty_function),
|
|
|
|
|
ADDTEST(empty_list),
|
|
|
|
|
ADDTEST(empty_dict),
|
|
|
|
|
ADDTEST(simple_dot_access),
|
|
|
|
|
ADDTEST(simple_at_access),
|
|
|
|
|
ADDTEST(parens),
|
|
|
|
|
ADDTEST(complex_dot_at_access),
|
|
|
|
|
ADDTEST(factorial),
|
|
|
|
|
ADDTEST(map),
|
2021-12-18 15:12:37 +00:00
|
|
|
ADDTEST(lists),
|
|
|
|
|
ADDTEST(stringification),
|
|
|
|
|
ADDTEST(line_continuation),
|
|
|
|
|
ADDTEST(dict),
|
|
|
|
|
ADDTEST(assignment),
|
|
|
|
|
ADDTEST(simple_function),
|
2021-12-18 16:06:17 +00:00
|
|
|
ADDTEST(complex_function),
|
2022-01-06 21:50:14 +00:00
|
|
|
ADDTEST(function_calls),
|
2022-01-08 21:55:24 +00:00
|
|
|
ADDTEST(member_assignment_with_predicate),
|
2021-12-18 23:27:34 +00:00
|
|
|
ADDTEST(err_empty_assignment),
|
|
|
|
|
ADDTEST(err_mismatching_parens),
|
|
|
|
|
ADDTEST(err_unclosed_func),
|
|
|
|
|
ADDTEST(err_unclosed_paren),
|
|
|
|
|
ADDTEST(err_assignment_missing_rhs),
|
|
|
|
|
ADDTEST(err_assignment_invalid_lhs),
|
|
|
|
|
ADDTEST(err_fragments_after_mapsto_in_empty_dict),
|
|
|
|
|
ADDTEST(err_fragments_after_mapsto_in_dict),
|
|
|
|
|
ADDTEST(err_mapsto_in_list),
|
|
|
|
|
ADDTEST(err_expr_in_param),
|
|
|
|
|
ADDTEST(err_statements_before_params),
|
2022-01-05 20:50:50 +00:00
|
|
|
ADDTEST(err_dict_mapsto_missing),
|
|
|
|
|
ADDTEST(err_dict_mapsto_too_early),
|
2022-01-07 22:08:25 +00:00
|
|
|
ADDTEST(err_assign_multiple_expands_per_level),
|
|
|
|
|
ADDTEST(err_assign_multiple_expands_per_level_nested),
|
|
|
|
|
ADDTEST(err_params_multiple_expands),
|
|
|
|
|
ADDTEST(err_params_multiple_expands_nested),
|
2022-01-08 21:55:24 +00:00
|
|
|
ADDTEST(err_unexpected_expression_in_member_assignable),
|
|
|
|
|
ADDTEST(err_unexpected_constant_in_member_assignable),
|
|
|
|
|
ADDTEST(err_unexpected_assignment_member_access),
|
2021-12-16 22:42:37 +00:00
|
|
|
TESTS_END
|