Pairs are 2-tuples of values that are constructed and matched with the `::`
operator. They can also be matched with a `:` operator, the LHS is an
expression then, the pair will then only match, if the LHS matches the
result of that expression.
Pairs should be useful to do something similar what sum types / tagged
unions do in statically typed languages, e.g. you could write something
like:
some := (symbol) # Somthing that creates a unique value
filter-map := {
_ [] -> []
f [x ~xs] ->
{
some:y -> [y ~(filter-map f xs)]
nil -> filter-map f xs
} (f x)
}
filter-map {
x?even -> some :: (* x 10)
_ -> nil
} some-list
1629 lines
66 KiB
C
1629 lines
66 KiB
C
#include <assert.h>
|
|
|
|
#include "apfl.h"
|
|
#include "resizable.h"
|
|
|
|
#include "alloc.h"
|
|
#include "test.h"
|
|
|
|
struct parser_test {
|
|
testctx t;
|
|
struct apfl_allocator allocator;
|
|
struct apfl_io_string_reader_data string_reader;
|
|
struct apfl_io_reader reader;
|
|
apfl_tokenizer_ptr tokenizer;
|
|
apfl_parser_ptr parser;
|
|
};
|
|
|
|
static struct parser_test *
|
|
new_parser_test(testctx t, const char *source)
|
|
{
|
|
struct apfl_allocator allocator = test_allocator(t);
|
|
|
|
struct parser_test *pt = must_alloc(t, sizeof(struct parser_test));
|
|
pt->t = t;
|
|
pt->allocator = allocator;
|
|
|
|
pt->string_reader = apfl_io_string_reader_create(apfl_string_view_from(source));
|
|
pt->reader = apfl_io_string_reader(&pt->string_reader);
|
|
|
|
if ((pt->tokenizer = apfl_tokenizer_new(
|
|
allocator,
|
|
apfl_io_reader_as_source_reader(&pt->reader)
|
|
)) == NULL) {
|
|
test_fatalf(t, "Failed initializing the tokenizer");
|
|
}
|
|
|
|
if ((pt->parser = apfl_parser_new(allocator, 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);
|
|
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:");
|
|
assert(apfl_expr_print(expected, stderr));
|
|
test_failf(pt->t, "Have:");
|
|
assert(apfl_expr_print(expr, stderr));
|
|
}
|
|
apfl_expr_deinit(pt->allocator, &expr);
|
|
apfl_expr_deinit(pt->allocator, &expected);
|
|
break;
|
|
case APFL_PARSE_EOF:
|
|
test_fatalf(pt->t, "Extected an expression but got EOF");
|
|
break;
|
|
case APFL_PARSE_ERROR:
|
|
test_failf(pt->t, "Got an error instead of an expression");
|
|
apfl_error_print(apfl_parser_get_error(pt->parser), stderr);
|
|
test_fatal(pt->t);
|
|
break;
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
static struct apfl_string
|
|
new_string(struct parser_test *pt, const char *in)
|
|
{
|
|
struct apfl_string out = apfl_string_blank();
|
|
if (!apfl_string_copy(pt->allocator, &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 = ALLOC_BYTES(pt->allocator, size);
|
|
memcpy(out, data, size);
|
|
return out;
|
|
}
|
|
|
|
static struct apfl_expr_list_item
|
|
list_item(bool expand, struct apfl_expr *expr)
|
|
{
|
|
return (struct apfl_expr_list_item) {
|
|
.expand = expand,
|
|
.expr = expr,
|
|
};
|
|
}
|
|
|
|
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,
|
|
};
|
|
}
|
|
|
|
// 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 )))
|
|
|
|
enum listbuilder_cmd {
|
|
LISTBUILDER_ADD,
|
|
LISTBUILDER_STOP,
|
|
};
|
|
|
|
#define LISTBUILDER_NAME(n) listbuilder_##n
|
|
#define LISTBUILDER_ITEM_NAME(n) listbuilder_item_##n
|
|
#define LISTBUILDER_FILL_LIST(pt, itemtype, items, list_items, list_len, cap) \
|
|
apfl_resizable_init((void **)&list_items, &list_len, &cap); \
|
|
\
|
|
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); \
|
|
} \
|
|
} \
|
|
|
|
#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; \
|
|
} \
|
|
|
|
#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 =
|
|
|
|
MKLISTBUILDER(list, struct apfl_expr_list, struct apfl_expr_list_item, items)
|
|
MKCAPLISTBUILDER(body, struct apfl_expr_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)
|
|
|
|
#define POS(l, c) (struct apfl_position) { .line = l, .col = c }
|
|
|
|
static struct apfl_expr
|
|
var(struct parser_test *pt, size_t line, size_t 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, size_t line, size_t 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, size_t line, size_t 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, size_t line, size_t 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);
|
|
}
|
|
|
|
|
|
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(size_t line, size_t 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, size_t line, size_t col, struct apfl_expr_const constant)
|
|
{
|
|
struct apfl_expr expr = const_expr(line, col, constant);
|
|
return new_helper(pt, sizeof(struct apfl_expr), &expr);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
|
|
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,
|
|
.position = POS(1, 1),
|
|
.call = (struct apfl_expr_call) {
|
|
.callee = new_var(pt, 1, 1, "print"),
|
|
.arguments = LIST_BEGIN(pt, list)
|
|
LIST_ADD (struct apfl_expr_list_item) {
|
|
.expand = false,
|
|
.expr = new_const_expr(pt, 1, 7, string_const(pt, "Hello World!")),
|
|
},
|
|
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_pair) {
|
|
.lhs = BEGIN_NEW(pt, struct apfl_expr) {
|
|
.type = APFL_EXPR_AT,
|
|
.position = POS(1, 8),
|
|
.at = (struct apfl_expr_pair) {
|
|
.lhs = BEGIN_NEW(pt, struct apfl_expr) {
|
|
.type = APFL_EXPR_AT,
|
|
.position = POS(1, 6),
|
|
.at = (struct apfl_expr_pair) {
|
|
.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_pair) {
|
|
.lhs = new_var(pt, 1, 1, "a"),
|
|
.rhs = new_var(pt, 1, 3, "b"),
|
|
}
|
|
} END_NEW),
|
|
.rhs = new_const_expr(pt, 1, 7, num_const(1)),
|
|
},
|
|
} END_NEW,
|
|
.rhs = new_const_expr(pt, 1, 9, string_const(pt, "d")),
|
|
},
|
|
} 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_pair) {
|
|
.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 = assignable_var(pt, "factorial"),
|
|
.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 params_item(false, (struct apfl_expr_param) {
|
|
.type = APFL_EXPR_PARAM_CONSTANT,
|
|
.constant = num_const(0),
|
|
}),
|
|
LIST_END,
|
|
.body = LIST_BEGIN(pt, body)
|
|
LIST_ADD const_expr(2, 10, num_const(1)),
|
|
LIST_END
|
|
},
|
|
LIST_ADD (struct apfl_expr_subfunc) {
|
|
.params = LIST_BEGIN(pt, params)
|
|
LIST_ADD params_item(false, (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, new_const_expr(pt, 3, 30,num_const(1))),
|
|
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, new_const_expr(pt, 5, 11, num_const(10))),
|
|
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 = assignable_var(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 params_item(false, (struct apfl_expr_param) {
|
|
.type = APFL_EXPR_PARAM_BLANK,
|
|
}),
|
|
LIST_ADD params_item(false, (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 params_item(false, (struct apfl_expr_param) {
|
|
.type = APFL_EXPR_PARAM_VAR,
|
|
.var = new_string(pt, "f"),
|
|
}),
|
|
LIST_ADD params_item(false, (struct apfl_expr_param) {
|
|
.type = APFL_EXPR_PARAM_LIST,
|
|
.list = LIST_BEGIN(pt, params)
|
|
LIST_ADD params_item(false, (struct apfl_expr_param) {
|
|
.type = APFL_EXPR_PARAM_VAR,
|
|
.var = new_string(pt, "x"),
|
|
}),
|
|
LIST_ADD params_item(true, (struct apfl_expr_param) {
|
|
.type = APFL_EXPR_PARAM_VAR,
|
|
.var = new_string(pt, "xs"),
|
|
}),
|
|
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,
|
|
},
|
|
});
|
|
expect_eof(pt);
|
|
destroy_parser_test(pt);
|
|
}
|
|
|
|
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"
|
|
// 1 2 3
|
|
// 123456789012345678901234567890123456
|
|
"foo :: [ bar :: [1::_ 2] ~xs] := baz\n"
|
|
// 1
|
|
// 123456789012345
|
|
"cool:var = xxx\n"
|
|
);
|
|
|
|
expect_expr(pt, (struct apfl_expr) {
|
|
.type = APFL_EXPR_ASSIGNMENT,
|
|
.position = POS(1, 3),
|
|
.assignment = (struct apfl_expr_assignment) {
|
|
.local = false,
|
|
.lhs = assignable_var(pt, "a"),
|
|
.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) {
|
|
.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"),
|
|
},
|
|
},
|
|
},
|
|
.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) {
|
|
.lhs = new_assignable_var(pt, "bar"),
|
|
.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)
|
|
LIST_ADD assignable_list_item(false, (struct apfl_expr_assignable) {
|
|
.type = APFL_EXPR_ASSIGNABLE_LIST,
|
|
.list = LIST_BEGIN(pt, assignable_list)
|
|
LIST_ADD assignable_list_item(false, (struct apfl_expr_assignable) {
|
|
.type = APFL_EXPR_ASSIGNABLE_CONSTANT,
|
|
.constant = num_const(1),
|
|
}),
|
|
LIST_ADD assignable_list_item(false, (struct apfl_expr_assignable) {
|
|
.type = APFL_EXPR_ASSIGNABLE_CONSTANT,
|
|
.constant = num_const(2),
|
|
}),
|
|
LIST_END,
|
|
}),
|
|
LIST_ADD assignable_list_item(true, assignable_var(pt, "xs")),
|
|
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) {
|
|
.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,
|
|
},
|
|
}
|
|
},
|
|
.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_expr(pt, (struct apfl_expr) {
|
|
.type = APFL_EXPR_ASSIGNMENT,
|
|
.position = POS(4, 31),
|
|
.assignment = {
|
|
.local = true,
|
|
.lhs = {
|
|
.type = APFL_EXPR_ASSIGNABLE_PAIR,
|
|
.pair = {
|
|
.lhs = new_assignable_var(pt, "foo"),
|
|
.rhs = BEGIN_NEW(pt, struct apfl_expr_assignable) {
|
|
.type = APFL_EXPR_ASSIGNABLE_LIST,
|
|
.list = LIST_BEGIN(pt, assignable_list)
|
|
LIST_ADD assignable_list_item(false, (struct apfl_expr_assignable) {
|
|
.type = APFL_EXPR_ASSIGNABLE_PAIR,
|
|
.pair = {
|
|
.lhs = new_assignable_var(pt, "bar"),
|
|
.rhs = BEGIN_NEW(pt, struct apfl_expr_assignable) {
|
|
.type = APFL_EXPR_ASSIGNABLE_LIST,
|
|
.list = LIST_BEGIN(pt, assignable_list)
|
|
LIST_ADD assignable_list_item(false, (struct apfl_expr_assignable) {
|
|
.type = APFL_EXPR_ASSIGNABLE_PAIR,
|
|
.pair = {
|
|
.lhs = BEGIN_NEW(pt, struct apfl_expr_assignable) {
|
|
.type = APFL_EXPR_ASSIGNABLE_CONSTANT,
|
|
.constant = num_const(1)
|
|
} END_NEW,
|
|
.rhs = BEGIN_NEW(pt, struct apfl_expr_assignable) {
|
|
.type = APFL_EXPR_ASSIGNABLE_BLANK,
|
|
} END_NEW,
|
|
},
|
|
}),
|
|
LIST_ADD assignable_list_item(false, (struct apfl_expr_assignable) {
|
|
.type = APFL_EXPR_ASSIGNABLE_CONSTANT,
|
|
.constant = num_const(2),
|
|
})
|
|
LIST_END,
|
|
} END_NEW,
|
|
},
|
|
}),
|
|
LIST_ADD assignable_list_item(true, assignable_var(pt, "xs"))
|
|
LIST_END,
|
|
} END_NEW,
|
|
},
|
|
},
|
|
.rhs = BEGIN_NEW(pt, struct apfl_expr) {
|
|
.type = APFL_EXPR_VAR,
|
|
.position = POS(4, 34),
|
|
.var = new_string(pt, "baz"),
|
|
} END_NEW,
|
|
},
|
|
});
|
|
expect_expr(pt, (struct apfl_expr) {
|
|
.type = APFL_EXPR_ASSIGNMENT,
|
|
.position = POS(5, 10),
|
|
.assignment = {
|
|
.local = true,
|
|
.lhs = {
|
|
.type = APFL_EXPR_ASSIGNABLE_TAGGED,
|
|
.tagged = {
|
|
.lhs = new_var(pt, 5, 1, "cool"),
|
|
.rhs = new_assignable_var(pt, "var"),
|
|
},
|
|
},
|
|
.rhs = BEGIN_NEW(pt, struct apfl_expr) {
|
|
.type = APFL_EXPR_VAR,
|
|
.position = POS(5, 12),
|
|
.var = new_string(pt, "xxx"),
|
|
} 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),
|
|
.simple_func = LIST_BEGIN(pt, body)
|
|
LIST_ADD (struct apfl_expr) {
|
|
.type = APFL_EXPR_SIMPLE_FUNC,
|
|
.position = POS(1, 2),
|
|
.simple_func = LIST_BEGIN(pt, body)
|
|
LIST_END,
|
|
},
|
|
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),
|
|
.simple_func = LIST_BEGIN(pt, body)
|
|
LIST_ADD (struct apfl_expr) {
|
|
.type = APFL_EXPR_ASSIGNMENT,
|
|
.position = POS(4, 9),
|
|
.assignment = (struct apfl_expr_assignment) {
|
|
.local = false,
|
|
.lhs = assignable_var(pt, "baz"),
|
|
.rhs = new_const_expr(pt, 4, 11, num_const(10)),
|
|
},
|
|
},
|
|
LIST_ADD var(pt, 5, 5, "a"),
|
|
LIST_ADD var(pt, 6, 5, "b"),
|
|
LIST_END,
|
|
} END_NEW),
|
|
LIST_END,
|
|
},
|
|
},
|
|
LIST_ADD var(pt, 7, 4, "c"),
|
|
LIST_END,
|
|
});
|
|
destroy_parser_test(pt);
|
|
}
|
|
|
|
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"
|
|
"a?b :: c:d?e f:g -> h"
|
|
"}\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)
|
|
LIST_ADD params_item(false, (struct apfl_expr_param) {
|
|
.type = APFL_EXPR_PARAM_VAR,
|
|
.var = new_string(pt, "a"),
|
|
}),
|
|
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) {
|
|
.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,
|
|
},
|
|
}),
|
|
LIST_END,
|
|
.body = LIST_BEGIN(pt, body)
|
|
LIST_ADD var(pt, 1, 21, "foo"),
|
|
LIST_ADD var(pt, 1, 26, "bar"),
|
|
LIST_ADD var(pt, 2, 5, "baz"),
|
|
LIST_END,
|
|
},
|
|
LIST_ADD {
|
|
.params = LIST_BEGIN(pt, params)
|
|
LIST_ADD params_item(false, (struct apfl_expr_param) {
|
|
.type = APFL_EXPR_PARAM_CONSTANT,
|
|
.constant = num_const(1),
|
|
}),
|
|
LIST_ADD params_item(false, (struct apfl_expr_param) {
|
|
.type = APFL_EXPR_PARAM_LIST,
|
|
.list = LIST_BEGIN(pt, params)
|
|
LIST_END,
|
|
}),
|
|
LIST_END,
|
|
.body = LIST_BEGIN(pt, body)
|
|
LIST_END,
|
|
},
|
|
LIST_ADD {
|
|
.params = LIST_BEGIN(pt, params)
|
|
LIST_ADD params_item(false, (struct apfl_expr_param) {
|
|
.type = APFL_EXPR_PARAM_VAR,
|
|
.var = new_string(pt, "x"),
|
|
}),
|
|
LIST_ADD params_item(false, (struct apfl_expr_param) {
|
|
.type = APFL_EXPR_PARAM_LIST,
|
|
.list = LIST_BEGIN(pt, params)
|
|
LIST_ADD params_item(false, (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, "y"),
|
|
} END_NEW,
|
|
.rhs = new_var(pt, 3, 15, "x"),
|
|
},
|
|
}),
|
|
LIST_ADD params_item(true, (struct apfl_expr_param) {
|
|
.type = APFL_EXPR_PARAM_VAR,
|
|
.var = new_string(pt, "ys"),
|
|
}),
|
|
LIST_END
|
|
}),
|
|
LIST_END,
|
|
.body = LIST_BEGIN(pt, body)
|
|
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)
|
|
LIST_ADD params_item(false, (struct apfl_expr_param) {
|
|
.type = APFL_EXPR_PARAM_VAR,
|
|
.var = new_string(pt, "x"),
|
|
}),
|
|
LIST_END,
|
|
.body = LIST_BEGIN(pt, body)
|
|
LIST_ADD var(pt, 6, 14, "y"),
|
|
LIST_END,
|
|
}
|
|
LIST_END
|
|
} END_NEW),
|
|
LIST_END,
|
|
},
|
|
}
|
|
LIST_END,
|
|
},
|
|
LIST_ADD {
|
|
.params = LIST_BEGIN(pt, params)
|
|
LIST_ADD params_item(false, (struct apfl_expr_param) {
|
|
.type = APFL_EXPR_PARAM_PAIR,
|
|
.pair = {
|
|
.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, "a"),
|
|
} END_NEW,
|
|
.rhs = new_var(pt, 8, 3, "b"),
|
|
},
|
|
} END_NEW,
|
|
.rhs = BEGIN_NEW(pt, struct apfl_expr_param) {
|
|
.type = APFL_EXPR_PARAM_TAGGED,
|
|
.tagged = {
|
|
.lhs = new_var(pt, 8, 8, "c"),
|
|
.rhs = 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, "d"),
|
|
} END_NEW,
|
|
.rhs = new_var(pt, 8, 12, "e"),
|
|
},
|
|
} END_NEW,
|
|
},
|
|
} END_NEW,
|
|
},
|
|
}),
|
|
LIST_ADD params_item(false, (struct apfl_expr_param) {
|
|
.type = APFL_EXPR_PARAM_TAGGED,
|
|
.tagged = {
|
|
.lhs = new_var(pt, 8, 14, "f"),
|
|
.rhs = BEGIN_NEW(pt, struct apfl_expr_param) {
|
|
.type = APFL_EXPR_PARAM_VAR,
|
|
.var = new_string(pt, "g"),
|
|
} END_NEW,
|
|
},
|
|
}),
|
|
LIST_END,
|
|
.body = LIST_BEGIN(pt, body)
|
|
LIST_ADD var(pt, 8, 21, "h"),
|
|
LIST_END,
|
|
},
|
|
LIST_END,
|
|
});
|
|
expect_eof(pt);
|
|
destroy_parser_test(pt);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
TEST(build_pair, t) {
|
|
// 1
|
|
// 123456789012345
|
|
struct parser_test *pt = new_parser_test(t, "a :: (b c) :: d");
|
|
expect_expr(pt, (struct apfl_expr) {
|
|
.type = APFL_EXPR_PAIR,
|
|
.position = POS(1, 3),
|
|
.pair = {
|
|
.lhs = new_var(pt, 1, 1, "a"),
|
|
.rhs = BEGIN_NEW(pt, struct apfl_expr) {
|
|
.type = APFL_EXPR_PAIR,
|
|
.position = POS(1, 12),
|
|
.pair = {
|
|
.lhs = BEGIN_NEW(pt, struct apfl_expr) {
|
|
.type = APFL_EXPR_CALL,
|
|
.position = POS(1, 6),
|
|
.call = {
|
|
.callee = new_var(pt, 1, 7, "b"),
|
|
.arguments = LIST_BEGIN(pt, list)
|
|
LIST_ADD list_item(false, new_var(pt, 1, 9, "c")),
|
|
LIST_END,
|
|
},
|
|
} END_NEW,
|
|
.rhs = new_var(pt, 1, 15, "d"),
|
|
},
|
|
} END_NEW,
|
|
},
|
|
});
|
|
expect_eof(pt);
|
|
destroy_parser_test(pt);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
TESTS_BEGIN
|
|
ADDTEST(empty),
|
|
ADDTEST(hello_world),
|
|
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),
|
|
ADDTEST(lists),
|
|
ADDTEST(stringification),
|
|
ADDTEST(line_continuation),
|
|
ADDTEST(dict),
|
|
ADDTEST(assignment),
|
|
ADDTEST(simple_function),
|
|
ADDTEST(complex_function),
|
|
ADDTEST(function_calls),
|
|
ADDTEST(member_assignment_with_predicate),
|
|
ADDTEST(build_pair),
|
|
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),
|
|
ADDTEST(err_dict_mapsto_missing),
|
|
ADDTEST(err_dict_mapsto_too_early),
|
|
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),
|
|
ADDTEST(err_unexpected_expression_in_member_assignable),
|
|
ADDTEST(err_unexpected_constant_in_member_assignable),
|
|
ADDTEST(err_unexpected_assignment_member_access),
|
|
TESTS_END
|