2021-12-17 20:08:03 +00:00
|
|
|
#include <assert.h>
|
|
|
|
|
#include <stdarg.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
|
|
|
|
|
|
|
|
#include "test.h"
|
|
|
|
|
|
|
|
|
|
struct parser_test {
|
|
|
|
|
testctx t;
|
|
|
|
|
void *source_reader_ctx;
|
|
|
|
|
apfl_tokenizer_ptr tokenizer;
|
|
|
|
|
apfl_parser_ptr parser;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static struct parser_test *
|
|
|
|
|
new_parser_test(testctx t, const char *source)
|
|
|
|
|
{
|
|
|
|
|
struct parser_test *pt = must_alloc(t, sizeof(struct parser_test));
|
|
|
|
|
pt->t = t;
|
|
|
|
|
if ((pt->source_reader_ctx = apfl_string_source_reader_new(apfl_string_view_from(source))) == NULL) {
|
|
|
|
|
test_fatalf(t, "Failed initializing source reader");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((pt->tokenizer = apfl_tokenizer_new(apfl_string_source_reader, pt->source_reader_ctx)) == NULL) {
|
|
|
|
|
test_fatalf(t, "Failed initializing the tokenizer");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((pt->parser = apfl_parser_new(apfl_tokenizer_as_token_source(pt->tokenizer))) == NULL) {
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
apfl_expr_deinit(&expr);
|
|
|
|
|
apfl_expr_deinit(&expected);
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct apfl_string
|
|
|
|
|
new_string(struct parser_test *pt, const char *in)
|
|
|
|
|
{
|
|
|
|
|
struct apfl_string out = { .bytes = NULL, .len = 0 };
|
|
|
|
|
if (!apfl_string_copy(&out, apfl_string_view_from(in))) {
|
|
|
|
|
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)
|
|
|
|
|
{
|
|
|
|
|
void *out = must_alloc(pt->t, size);
|
|
|
|
|
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,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
|
|
#define MKLISTBUILDER(name, listtype, itemtype, items_memb, len_memb) \
|
|
|
|
|
listtype \
|
|
|
|
|
LISTBUILDER_NAME(name)(struct parser_test *pt, ...) \
|
|
|
|
|
{ \
|
|
|
|
|
listtype body; \
|
|
|
|
|
size_t cap = 0; \
|
|
|
|
|
\
|
|
|
|
|
apfl_resizable_init((void **)&body.items_memb, &body.len_memb, &cap); \
|
|
|
|
|
\
|
|
|
|
|
va_list ap; \
|
|
|
|
|
va_start(ap, pt); \
|
|
|
|
|
\
|
|
|
|
|
for (;;) { \
|
|
|
|
|
enum listbuilder_cmd cmd = va_arg(ap, enum listbuilder_cmd); \
|
|
|
|
|
if (cmd == LISTBUILDER_STOP) { \
|
|
|
|
|
break; \
|
|
|
|
|
} \
|
|
|
|
|
assert(cmd == LISTBUILDER_ADD); \
|
|
|
|
|
itemtype item = va_arg(ap, itemtype); \
|
|
|
|
|
\
|
|
|
|
|
if (!apfl_resizable_append( \
|
|
|
|
|
sizeof(itemtype), \
|
|
|
|
|
(void **)&body.items_memb, \
|
|
|
|
|
&body.len, \
|
|
|
|
|
&cap, \
|
|
|
|
|
&item, \
|
|
|
|
|
1 \
|
|
|
|
|
)) { \
|
|
|
|
|
va_end(ap); \
|
|
|
|
|
test_fatalf(pt->t, "Failed appending"); \
|
|
|
|
|
assert(false); \
|
|
|
|
|
} \
|
|
|
|
|
} \
|
|
|
|
|
\
|
|
|
|
|
va_end(ap); \
|
|
|
|
|
\
|
|
|
|
|
return body; \
|
|
|
|
|
} \
|
|
|
|
|
|
|
|
|
|
#define LIST_BEGIN(pt, builder) (LISTBUILDER_NAME(builder)(pt,
|
|
|
|
|
#define LIST_END LISTBUILDER_STOP))
|
|
|
|
|
#define LIST_ADD LISTBUILDER_ADD,
|
|
|
|
|
|
|
|
|
|
MKLISTBUILDER(list, struct apfl_expr_list, struct apfl_expr_list_item, items, len)
|
|
|
|
|
MKLISTBUILDER(body, struct apfl_expr_body, struct apfl_expr, items, len)
|
|
|
|
|
MKLISTBUILDER(dict, struct apfl_expr_dict, struct apfl_expr_dict_pair, items, len)
|
|
|
|
|
MKLISTBUILDER(complex_func, struct apfl_expr_complex_func, struct apfl_expr_subfunc, subfuncs, len)
|
|
|
|
|
MKLISTBUILDER(params, struct apfl_expr_params, struct apfl_expr_param, params, len)
|
|
|
|
|
|
|
|
|
|
#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-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,
|
|
|
|
|
.expr = BEGIN_NEW(pt, struct apfl_expr) {
|
|
|
|
|
.type = APFL_EXPR_CONSTANT,
|
2021-12-17 20:08:03 +00:00
|
|
|
.position = POS(1, 7),
|
2021-12-16 22:42:37 +00:00
|
|
|
.constant.type = APFL_EXPR_CONST_STRING,
|
|
|
|
|
.constant.string = new_string(pt, "Hello World!"),
|
|
|
|
|
} END_NEW,
|
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),
|
|
|
|
|
.simple_func = LIST_BEGIN(pt, body)
|
|
|
|
|
LIST_END,
|
|
|
|
|
});
|
|
|
|
|
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),
|
|
|
|
|
.rhs = BEGIN_NEW(pt, struct apfl_expr) {
|
|
|
|
|
.type = APFL_EXPR_CONSTANT,
|
|
|
|
|
.position = POS(1, 7),
|
|
|
|
|
.constant.type = APFL_EXPR_CONST_NUMBER,
|
|
|
|
|
.constant.number = 1,
|
|
|
|
|
} END_NEW,
|
|
|
|
|
},
|
|
|
|
|
} END_NEW,
|
|
|
|
|
.rhs = BEGIN_NEW(pt, struct apfl_expr) {
|
|
|
|
|
.type = APFL_EXPR_CONSTANT,
|
|
|
|
|
.position = POS(1, 9),
|
|
|
|
|
.constant.type = APFL_EXPR_CONST_STRING,
|
|
|
|
|
.constant.string = new_string(pt, "d"),
|
|
|
|
|
} END_NEW,
|
|
|
|
|
},
|
|
|
|
|
} 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) {
|
|
|
|
|
.lhs = (struct apfl_expr_assignable) {
|
|
|
|
|
.type = APFL_EXPR_ASSIGNABLE_VAR,
|
|
|
|
|
.var = new_string(pt, "factorial"),
|
2021-12-16 22:42:37 +00:00
|
|
|
},
|
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)
|
|
|
|
|
LIST_ADD (struct apfl_expr_param) {
|
|
|
|
|
.type = APFL_EXPR_PARAM_CONSTANT,
|
|
|
|
|
.constant = (struct apfl_expr_const) {
|
|
|
|
|
.type = APFL_EXPR_CONST_NUMBER,
|
|
|
|
|
.number = 0,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
LIST_END,
|
|
|
|
|
.body = LIST_BEGIN(pt, body)
|
|
|
|
|
LIST_ADD (struct apfl_expr) {
|
|
|
|
|
.type = APFL_EXPR_CONSTANT,
|
|
|
|
|
.position = POS(2, 10),
|
|
|
|
|
.constant = (struct apfl_expr_const) {
|
|
|
|
|
.type = APFL_EXPR_CONST_NUMBER,
|
|
|
|
|
.number = 1,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
LIST_END
|
|
|
|
|
},
|
|
|
|
|
LIST_ADD (struct apfl_expr_subfunc) {
|
|
|
|
|
.params = LIST_BEGIN(pt, params)
|
|
|
|
|
LIST_ADD (struct apfl_expr_param) {
|
|
|
|
|
.type = APFL_EXPR_PARAM_VAR,
|
|
|
|
|
.var = new_string(pt, "n"),
|
|
|
|
|
},
|
|
|
|
|
LIST_END,
|
|
|
|
|
.body = LIST_BEGIN(pt, body)
|
|
|
|
|
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")),
|
|
|
|
|
LIST_ADD list_item(false, BEGIN_NEW(pt, struct apfl_expr) {
|
|
|
|
|
.type = APFL_EXPR_CONSTANT,
|
|
|
|
|
.position = POS(3, 30),
|
|
|
|
|
.constant = (struct apfl_expr_const) {
|
|
|
|
|
.type = APFL_EXPR_CONST_NUMBER,
|
|
|
|
|
.number = 1,
|
|
|
|
|
},
|
|
|
|
|
} END_NEW),
|
|
|
|
|
LIST_END,
|
|
|
|
|
},
|
|
|
|
|
} END_NEW),
|
|
|
|
|
LIST_END,
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
} END_NEW),
|
|
|
|
|
LIST_END,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
LIST_END
|
|
|
|
|
},
|
|
|
|
|
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)
|
|
|
|
|
LIST_ADD list_item(false, BEGIN_NEW(pt, struct apfl_expr) {
|
|
|
|
|
.type = APFL_EXPR_CONSTANT,
|
|
|
|
|
.position = POS(5, 11),
|
|
|
|
|
.constant = (struct apfl_expr_const) {
|
|
|
|
|
.type = APFL_EXPR_CONST_NUMBER,
|
|
|
|
|
.number = 10,
|
|
|
|
|
},
|
|
|
|
|
} END_NEW),
|
|
|
|
|
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) {
|
|
|
|
|
.lhs = (struct apfl_expr_assignable) {
|
|
|
|
|
.type = APFL_EXPR_ASSIGNABLE_VAR,
|
|
|
|
|
.var = new_string(pt, "map"),
|
|
|
|
|
},
|
|
|
|
|
.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)
|
|
|
|
|
LIST_ADD (struct apfl_expr_param) {
|
|
|
|
|
.type = APFL_EXPR_PARAM_VAR,
|
|
|
|
|
.var = new_string(pt, "_"),
|
|
|
|
|
},
|
|
|
|
|
LIST_ADD (struct apfl_expr_param) {
|
|
|
|
|
.type = APFL_EXPR_PARAM_LIST,
|
|
|
|
|
.list = LIST_BEGIN(pt, params)
|
|
|
|
|
LIST_END,
|
|
|
|
|
},
|
|
|
|
|
LIST_END,
|
|
|
|
|
.body = LIST_BEGIN(pt, body)
|
|
|
|
|
LIST_ADD (struct apfl_expr) {
|
|
|
|
|
.type = APFL_EXPR_LIST,
|
|
|
|
|
.position = POS(2, 9),
|
|
|
|
|
.list = LIST_BEGIN(pt, list)
|
|
|
|
|
LIST_END,
|
|
|
|
|
},
|
|
|
|
|
LIST_END,
|
|
|
|
|
},
|
|
|
|
|
LIST_ADD (struct apfl_expr_subfunc) {
|
|
|
|
|
.params = LIST_BEGIN(pt, params)
|
|
|
|
|
LIST_ADD (struct apfl_expr_param) {
|
|
|
|
|
.type = APFL_EXPR_PARAM_VAR,
|
|
|
|
|
.var = new_string(pt, "f"),
|
|
|
|
|
},
|
|
|
|
|
LIST_ADD (struct apfl_expr_param) {
|
|
|
|
|
.type = APFL_EXPR_PARAM_LIST,
|
|
|
|
|
.list = LIST_BEGIN(pt, params)
|
|
|
|
|
LIST_ADD (struct apfl_expr_param) {
|
|
|
|
|
.type = APFL_EXPR_PARAM_VAR,
|
|
|
|
|
.var = new_string(pt, "x"),
|
|
|
|
|
},
|
|
|
|
|
LIST_ADD (struct apfl_expr_param) {
|
|
|
|
|
.type = APFL_EXPR_PARAM_EXPAND,
|
|
|
|
|
.expand = BEGIN_NEW(pt, struct apfl_expr_param) {
|
|
|
|
|
.type = APFL_EXPR_PARAM_VAR,
|
|
|
|
|
.var = new_string(pt, "xs"),
|
|
|
|
|
} END_NEW
|
|
|
|
|
},
|
|
|
|
|
LIST_END,
|
|
|
|
|
},
|
|
|
|
|
LIST_END,
|
|
|
|
|
.body = LIST_BEGIN(pt, body)
|
|
|
|
|
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,
|
|
|
|
|
},
|
|
|
|
|
LIST_END
|
|
|
|
|
},
|
|
|
|
|
LIST_END,
|
|
|
|
|
} END_NEW,
|
2021-12-16 22:42:37 +00:00
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
expect_eof(pt);
|
|
|
|
|
destroy_parser_test(pt);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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-16 22:42:37 +00:00
|
|
|
TESTS_END
|