Implement pairs

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
This commit is contained in:
Laria 2023-03-22 23:54:03 +01:00
parent 8233e94ab3
commit 6056e3a450
20 changed files with 953 additions and 58 deletions

View file

@ -36,6 +36,7 @@ apfl has these types of values:
- strings - Immutable byte string
- lists - An ordered list of values
- dictionaries - An unordered mapping from arbitrary keys to values
- pairs - A pair of two values
- functions - Can be called with values as arguments and returns a value
### Comments
@ -125,6 +126,19 @@ Individual values can be accessed using the `dict@key` syntax, where the `key` c
k = "baz"
print d@k # Prints 3
### Pairs
A pair is created with the `::` operator between two values. Both the left and the right side can be arbitrary values, including other pairs.
The pair operator is right-associative, so this:
pair = a :: b :: c
results in the same pair as this:
tmp := b :: c
pair := a :: tmp
### Functions
Functions are the fundamental building block of apfl programs, as they are the only way of grouping code together and also the only way of control flow, as there are no traditional keywords like `if`, `while` and so forth (these are instead implemented as functions).
@ -265,6 +279,40 @@ A parameter can not only be a variable name, it can also be a constant value tha
factorial 5 # prints 120 (1 * 2 * 3 * 4 * 5)
Also a parameter can be a deconstructed pair:
my-pair := 1 :: 2
add-pair := {
a :: b ->
+ a b
}
add-pair my-pair # prints 3
It is often useful to deconstruct a pair and check the left value against a predefined value. If it's a constant value like a number, `::` can be used:
foo := {
42 :: x ->
+ x 1
}
However, sometimes the left value is not a constant. We could use predicates for this (see below), but we also have another syntax at hand for this:
a := {}
b := {}
foo := {
a:x -> + 10 x
b:x -> * 10 x
}
foo a :: 1 # prints 11, first subfunction was chosen
foo b :: 2 # prints 20, second subfunction was chosen
foo {} :: 3 # Will fail, no subfunction matches
In these examples, we say that the parameter x is tagged with a / b.
A parameter list can also contain up to one expansion, a variable name preceded by `~`. All remaining arguments are copied into the variable as a list.
print-many := {
@ -340,13 +388,18 @@ We've also already seen that you can also use `:=` instead of `=`, if you want t
# foo is still 2, as the variable `foo` inside the last immediately called function was a local one
The left hand side of an assignment can however be more than just variables. Like parameters of subfunctions, they can also be constants, list matches and can have predicates.
The left hand side of an assignment can however be more than just variables. Like parameters of subfunctions, they can also be constants, list matches, pairs, tagged values and can have predicates.
foo = [1 2 3 4]
[first ~middle _] = foo
# first = 1
# middle = [2 3]
pair := 1 :: 2
a :: b = pair
# a = 1
# b = 2
Also instead of assigning into variables you can assign into keys of a dictionary. By using `variable.key` instead of a variable, the `key` gets replaced by the value. You can also use the `key@value` syntax
d = [

View file

@ -113,6 +113,7 @@ functionaltest("concat")
functionaltest("join")
functionaltest("quine")
functionaltest("string-manip")
functionaltest("pairs")
install(TARGETS apfl DESTINATION lib)
install(TARGETS apfl-bin DESTINATION bin)

View file

@ -199,6 +199,8 @@ enum apfl_token_type {
APFL_TOK_NUMBER,
APFL_TOK_NAME,
APFL_TOK_STRING,
APFL_TOK_COLON,
APFL_TOK_DOUBLE_COLON,
};
struct apfl_token {
@ -275,6 +277,7 @@ enum apfl_expr_type {
APFL_EXPR_AT,
APFL_EXPR_CONSTANT,
APFL_EXPR_VAR,
APFL_EXPR_PAIR,
APFL_EXPR_BLANK,
};
@ -338,6 +341,8 @@ enum apfl_expr_param_type {
APFL_EXPR_PARAM_CONSTANT,
APFL_EXPR_PARAM_PREDICATE,
APFL_EXPR_PARAM_LIST,
APFL_EXPR_PARAM_PAIR,
APFL_EXPR_PARAM_TAGGED,
APFL_EXPR_PARAM_BLANK,
};
@ -347,6 +352,16 @@ struct apfl_expr_params {
size_t cap;
};
struct apfl_expr_param_pair {
struct apfl_expr_param *lhs;
struct apfl_expr_param *rhs;
};
struct apfl_expr_param_tagged {
struct apfl_expr *lhs;
struct apfl_expr_param *rhs;
};
struct apfl_expr_param {
enum apfl_expr_param_type type;
@ -355,6 +370,8 @@ struct apfl_expr_param {
struct apfl_expr_const constant;
struct apfl_expr_param_predicate predicate;
struct apfl_expr_params list;
struct apfl_expr_param_pair pair;
struct apfl_expr_param_tagged tagged;
};
};
@ -379,8 +396,9 @@ enum apfl_expr_assignable_type {
APFL_EXPR_ASSIGNABLE_CONSTANT,
APFL_EXPR_ASSIGNABLE_PREDICATE,
APFL_EXPR_ASSIGNABLE_LIST,
APFL_EXPR_ASSIGNABLE_PAIR,
APFL_EXPR_ASSIGNABLE_TAGGED,
APFL_EXPR_ASSIGNABLE_BLANK,
};
enum apfl_expr_assignable_var_or_member_type {
@ -416,6 +434,14 @@ struct apfl_expr_assignable_list {
struct apfl_expr_assignable_list_item *items;
size_t len;
};
struct apfl_expr_assignable_pair {
struct apfl_expr_assignable *lhs;
struct apfl_expr_assignable *rhs;
};
struct apfl_expr_assignable_tagged {
struct apfl_expr *lhs;
struct apfl_expr_assignable *rhs;
};
struct apfl_expr_assignable {
enum apfl_expr_assignable_type type;
@ -425,6 +451,8 @@ struct apfl_expr_assignable {
struct apfl_expr_const constant;
struct apfl_expr_assignable_predicate predicate;
struct apfl_expr_assignable_list list;
struct apfl_expr_assignable_pair pair;
struct apfl_expr_assignable_tagged tagged;
};
};
@ -444,11 +472,16 @@ struct apfl_expr_dot {
struct apfl_string rhs;
};
struct apfl_expr_at {
struct apfl_expr_pair {
struct apfl_expr *lhs;
struct apfl_expr *rhs;
};
struct apfl_expr_pipe {
struct apfl_expr *lhs;
struct apfl_expr_call rhs;
};
struct apfl_expr {
enum apfl_expr_type type;
@ -460,9 +493,11 @@ struct apfl_expr {
struct apfl_expr_complex_func complex_func;
struct apfl_expr_assignment assignment;
struct apfl_expr_dot dot;
struct apfl_expr_at at;
struct apfl_expr_pair at;
struct apfl_expr_pair pair;
struct apfl_expr_const constant;
struct apfl_string var;
struct apfl_expr_pipe pipe;
// blank has no further data
};
@ -484,6 +519,8 @@ void apfl_expr_call_deinit(struct apfl_allocator allocator, struct apfl_expr_cal
void apfl_expr_body_deinit(struct apfl_allocator allocator, struct apfl_expr_body *);
void apfl_expr_const_deinit(struct apfl_allocator allocator, struct apfl_expr_const *);
void apfl_expr_param_predicate_deinit(struct apfl_allocator allocator, struct apfl_expr_param_predicate *);
void apfl_expr_param_pair_deinit(struct apfl_allocator allocator, struct apfl_expr_param_pair *);
void apfl_expr_param_tagged_deinit(struct apfl_allocator allocator, struct apfl_expr_param_tagged *);
void apfl_expr_params_deinit(struct apfl_allocator allocator, struct apfl_expr_params *);
void apfl_expr_params_item_deinit(struct apfl_allocator allocator, struct apfl_expr_params_item *);
void apfl_expr_param_deinit(struct apfl_allocator allocator, struct apfl_expr_param *);
@ -492,13 +529,15 @@ void apfl_expr_complex_func_deinit(struct apfl_allocator allocator, struct apfl_
void apfl_expr_assignable_predicate_deinit(struct apfl_allocator allocator, struct apfl_expr_assignable_predicate *);
void apfl_expr_assignable_list_item_deinit(struct apfl_allocator allocator, struct apfl_expr_assignable_list_item *);
void apfl_expr_assignable_list_deinit(struct apfl_allocator allocator, struct apfl_expr_assignable_list *);
void apfl_expr_assignable_pair_deinit(struct apfl_allocator allocator, struct apfl_expr_assignable_pair *);
void apfl_expr_assignable_tagged_deinit(struct apfl_allocator allocator, struct apfl_expr_assignable_tagged *);
void apfl_expr_assignable_var_or_member_dot_deinit(struct apfl_allocator allocator, struct apfl_expr_assignable_var_or_member_dot *);
void apfl_expr_assignable_var_or_member_at_deinit(struct apfl_allocator allocator, struct apfl_expr_assignable_var_or_member_at *);
void apfl_expr_assignable_var_or_member_deinit(struct apfl_allocator allocator, struct apfl_expr_assignable_var_or_member *);
void apfl_expr_assignable_deinit(struct apfl_allocator allocator, struct apfl_expr_assignable *);
void apfl_expr_assignment_deinit(struct apfl_allocator allocator, struct apfl_expr_assignment *);
void apfl_expr_dot_deinit(struct apfl_allocator allocator, struct apfl_expr_dot *);
void apfl_expr_at_deinit(struct apfl_allocator allocator, struct apfl_expr_at *);
void apfl_expr_pair_deinit(struct apfl_allocator allocator, struct apfl_expr_pair *);
// End deinit functions
@ -513,19 +552,23 @@ struct apfl_expr_call apfl_expr_call_move(struct apfl_expr_c
struct apfl_expr_body apfl_expr_body_move(struct apfl_expr_body *);
struct apfl_expr_const apfl_expr_const_move(struct apfl_expr_const *);
struct apfl_expr_param_predicate apfl_expr_param_predicate_move(struct apfl_expr_param_predicate *);
struct apfl_expr_param_pair apfl_expr_param_pair_move(struct apfl_expr_param_pair *);
struct apfl_expr_param_tagged apfl_expr_param_tagged_move(struct apfl_expr_param_tagged *);
struct apfl_expr_params apfl_expr_params_move(struct apfl_expr_params *);
struct apfl_expr_param apfl_expr_param_move(struct apfl_expr_param *);
struct apfl_expr_subfunc apfl_expr_subfunc_move(struct apfl_expr_subfunc *);
struct apfl_expr_complex_func apfl_expr_complex_func_move(struct apfl_expr_complex_func *);
struct apfl_expr_assignable_var_or_member apfl_expr_assignable_var_or_member_move(struct apfl_expr_assignable_var_or_member *);
struct apfl_expr_assignable_predicate apfl_expr_assignable_predicate_move(struct apfl_expr_assignable_predicate *);
struct apfl_expr_assignable_pair apfl_expr_assignable_pair_move(struct apfl_expr_assignable_pair *);
struct apfl_expr_assignable_tagged apfl_expr_assignable_tagged_move(struct apfl_expr_assignable_tagged *);
struct apfl_expr_assignable_dot apfl_expr_assignable_dot_move(struct apfl_expr_assignable_dot *);
struct apfl_expr_assignable_at apfl_expr_assignable_at_move(struct apfl_expr_assignable_at *);
struct apfl_expr_assignable_list apfl_expr_assignable_list_move(struct apfl_expr_assignable_list *);
struct apfl_expr_assignable apfl_expr_assignable_move(struct apfl_expr_assignable *);
struct apfl_expr_assignment apfl_expr_assignment_move(struct apfl_expr_assignment *);
struct apfl_expr_dot apfl_expr_dot_move(struct apfl_expr_dot *);
struct apfl_expr_at apfl_expr_at_move(struct apfl_expr_at *);
struct apfl_expr_pair apfl_expr_pair_move(struct apfl_expr_pair *);
// End move functions
@ -623,6 +666,7 @@ enum apfl_value_type {
APFL_VALUE_STRING,
APFL_VALUE_LIST,
APFL_VALUE_DICT,
APFL_VALUE_PAIR,
APFL_VALUE_FUNC,
APFL_VALUE_USERDATA,
};
@ -705,6 +749,8 @@ void apfl_list_append_list(apfl_ctx, apfl_stackidx a, apfl_stackidx b);
void apfl_dict_create(apfl_ctx);
// Set a value in a dictionary. k and v will be dropped.
void apfl_dict_set(apfl_ctx, apfl_stackidx dict, apfl_stackidx k, apfl_stackidx v);
// Create a new pair out of two values from the stack. l and r will be dropped.
void apfl_push_pair(apfl_ctx, apfl_stackidx l, apfl_stackidx r);
// Get a value from a container (list or dict) and push it on the stack. container and k will be dropped.
void apfl_get_member(apfl_ctx, apfl_stackidx container, apfl_stackidx k);
// Get a value from a container (list or dict) and push it on the stack, if it exists. container and k will be dropped.

View file

@ -47,6 +47,7 @@ argument_type_for_instruction(enum instruction insn)
case INSN_DUP:
case INSN_CALL:
case INSN_MATCHER_MUST_MATCH:
case INSN_BUILD_PAIR:
return INSN_ARGS_NONE;
case INSN_NUMBER:
return INSN_ARGS_NUMBER;
@ -87,6 +88,7 @@ matcher_argument_type_for_instruction(enum matcher_instruction insn)
case MATCHER_LEAVE_LIST:
case MATCHER_CONTINUE_FROM_END:
case MATCHER_REMAINDING:
case MATCHER_UNPACK_PAIR:
return MINSN_ARGS_NONE;
case MATCHER_CHECK_CONST: // with index as values index
case MATCHER_CHECK_PRED: // with index as values index
@ -118,6 +120,7 @@ valid_matcher_instruction(enum matcher_instruction insn)
case MATCHER_CAPTURE_TO_VAR_LOCAL:
case MATCHER_CAPTURE_TO_VAR_WITH_PATH:
case MATCHER_CAPTURE_TO_VAR_LOCAL_WITH_PATH:
case MATCHER_UNPACK_PAIR:
return true;
}
return false;
@ -157,6 +160,7 @@ valid_instruction(enum instruction insn)
case INSN_FUNC_ADD_SUBFUNC:
case INSN_FUNC_ADD_SUBFUNC_ANYARGS:
case INSN_MATCHER_PUSH:
case INSN_BUILD_PAIR:
return true;
}
@ -319,6 +323,8 @@ apfl_instruction_to_string(enum instruction insn)
return "INSN_MATCHER_SET_VAL";
case INSN_MATCHER_MUST_MATCH:
return "INSN_MATCHER_MUST_MATCH";
case INSN_BUILD_PAIR:
return "INSN_BUILD_PAIR";
}
return "??";
@ -350,6 +356,8 @@ apfl_matcher_instruction_to_string(enum matcher_instruction insn)
return "MATCHER_CONTINUE_FROM_END";
case MATCHER_REMAINDING:
return "MATCHER_REMAINDING";
case MATCHER_UNPACK_PAIR:
return "MATCHER_UNPACK_PAIR";
}
return "??";

View file

@ -21,6 +21,7 @@ enum matcher_instruction {
MATCHER_LEAVE_LIST,
MATCHER_CONTINUE_FROM_END,
MATCHER_REMAINDING,
MATCHER_UNPACK_PAIR,
};
union matcher_instruction_or_arg {
@ -69,6 +70,7 @@ enum instruction {
INSN_MATCHER_PUSH, // ( -- ), arg: matcher; pushes a matcher onto the matcher stack
INSN_MATCHER_SET_VAL, // ( val -- ), arg: index
INSN_MATCHER_MUST_MATCH, // ( val -- ); pops a matcher from the matcher stack
INSN_BUILD_PAIR, // ( val val -- pair )
};
union instruction_or_arg {

View file

@ -244,7 +244,7 @@ compile_dot(struct compiler *compiler, struct apfl_expr_dot *dot, struct instruc
}
static bool
compile_at(struct compiler *compiler, struct apfl_expr_at *at, struct instruction_list *ilist)
compile_at(struct compiler *compiler, struct apfl_expr_pair *at, struct instruction_list *ilist)
{
TRY(compile_expr(compiler, at->lhs, ilist, NULL));
TRY(compile_expr(compiler, at->rhs, ilist, NULL));
@ -269,6 +269,18 @@ compile_var(struct compiler *compiler, struct apfl_string *var, struct instructi
return true;
}
static bool
compile_pair(struct compiler *compiler, struct apfl_expr_pair *pair, struct instruction_list *ilist)
{
TRY(compile_expr(compiler, pair->lhs, ilist, NULL));
TRY(compile_expr(compiler, pair->rhs, ilist, NULL));
TRY(ilist_ensure_cap(compiler, ilist, 1));
ilist_put_insn(ilist, INSN_BUILD_PAIR);
return true;
}
static bool
compile_simple_assignment(
struct compiler *compiler,
@ -575,6 +587,59 @@ DEF_COMPILE_ABSTRACT_MATCHABLE_LIST(
assignable
)
#define DEF_COMPILE_ABSTRACT_MATCHABLE_PAIR(name, compile_matchable, pairtype) \
static bool \
name( \
struct compiler *compiler, \
struct apfl_position position, \
pairtype *pair, \
struct compile_matchable_args args \
) { \
TRY(milist_ensure_cap(compiler, args.milist, 2)); \
milist_put_insn(args.milist, MATCHER_UNPACK_PAIR); \
\
TRY(compile_matchable(compiler, position, pair->lhs, args)); \
TRY(compile_matchable(compiler, position, pair->rhs, args)); \
\
return true; \
}
DEF_COMPILE_ABSTRACT_MATCHABLE_PAIR(
compile_assignable_pair,
compile_assignable,
struct apfl_expr_assignable_pair
)
#define DEF_COMPILE_ABSTRACT_MATCHABLE_TAGGED(name, compile_matchable, taggedtype) \
static bool \
name( \
struct compiler *compiler, \
struct apfl_position position, \
taggedtype *tagged, \
struct compile_matchable_args args \
) { \
size_t index = args.milist->value_count++; \
\
TRY(compile_expr(compiler, tagged->lhs, args.ilist, NULL)); \
TRY(ilist_ensure_cap(compiler, args.ilist, 2)); \
ilist_put_insn(args.ilist, INSN_MATCHER_SET_VAL); \
ilist_put_index(args.ilist, index); \
\
TRY(milist_ensure_cap(compiler, args.milist, 4)); \
milist_put_insn(args.milist, MATCHER_UNPACK_PAIR); \
milist_put_insn(args.milist, MATCHER_CHECK_CONST); \
milist_put_index(args.milist, index); \
milist_put_insn(args.milist, MATCHER_IGNORE); \
\
return compile_matchable(compiler, position, tagged->rhs, args); \
}
DEF_COMPILE_ABSTRACT_MATCHABLE_TAGGED(
compile_assignable_tagged,
compile_assignable,
struct apfl_expr_assignable_tagged
)
static bool
compile_matchable_blank(struct compiler *compiler, struct matcher_instruction_list *milist)
{
@ -599,6 +664,10 @@ compile_assignable(
return compile_assignable_predicate(compiler, position, &assignable->predicate, args);
case APFL_EXPR_ASSIGNABLE_LIST:
return compile_assignable_list(compiler, position, &assignable->list, args);
case APFL_EXPR_ASSIGNABLE_PAIR:
return compile_assignable_pair(compiler, position, &assignable->pair, args);
case APFL_EXPR_ASSIGNABLE_TAGGED:
return compile_assignable_tagged(compiler, position, &assignable->tagged, args);
case APFL_EXPR_ASSIGNABLE_BLANK:
return compile_matchable_blank(compiler, args.milist);
}
@ -756,6 +825,18 @@ DEF_COMPILE_ABSTRACT_MATCHABLE_PREDICATE(
struct apfl_expr_param_predicate
)
DEF_COMPILE_ABSTRACT_MATCHABLE_PAIR(
compile_param_pair,
compile_param,
struct apfl_expr_param_pair
)
DEF_COMPILE_ABSTRACT_MATCHABLE_TAGGED(
compile_param_tagged,
compile_param,
struct apfl_expr_param_tagged
)
static bool
compile_param_var(
struct compiler *compiler,
@ -803,6 +884,10 @@ compile_param(
return compile_param_list(compiler, position, &param->list, args);
case APFL_EXPR_PARAM_PREDICATE:
return compile_param_predicate(compiler, position, &param->predicate, args);
case APFL_EXPR_PARAM_PAIR:
return compile_param_pair(compiler, position, &param->pair, args);
case APFL_EXPR_PARAM_TAGGED:
return compile_param_tagged(compiler, position, &param->tagged, args);
case APFL_EXPR_PARAM_BLANK:
return compile_matchable_blank(compiler, args.milist);
}
@ -928,6 +1013,8 @@ compile_expr(
return compile_at(compiler, &expr->at, ilist);
case APFL_EXPR_VAR:
return compile_var(compiler, &expr->var, ilist);
case APFL_EXPR_PAIR:
return compile_pair(compiler, &expr->pair, ilist);
case APFL_EXPR_ASSIGNMENT:
return compile_assignment(compiler, &expr->assignment, expr->position, ilist);
}

View file

@ -1200,6 +1200,41 @@ apfl_dict_set(
assert(apfl_stack_drop_multi(ctx, 2, (apfl_stackidx[]){k_index, v_index}));
}
static void
must_tmproot_add_value(apfl_ctx ctx, struct apfl_value value)
{
if (!apfl_value_add_as_tmproot(&ctx->gc, value)) {
apfl_raise_alloc_error(ctx);
}
}
static void
push_pair_inner(apfl_ctx ctx, apfl_stackidx l_index, apfl_stackidx r_index)
{
struct apfl_value l = apfl_stack_must_get(ctx, l_index);
struct apfl_value r = apfl_stack_must_get(ctx, r_index);
must_tmproot_add_value(ctx, l);
must_tmproot_add_value(ctx, r);
assert(apfl_stack_drop_multi(ctx, 2, (apfl_stackidx[]){l_index, r_index}));
CREATE_GC_OBJECT_VALUE_ON_STACK(
ctx,
VALUE_PAIR,
pair,
apfl_pair_new(&ctx->gc, l, r)
)
}
void
apfl_push_pair(apfl_ctx ctx, apfl_stackidx l, apfl_stackidx r)
{
size_t tmproots = apfl_gc_tmproots_begin(&ctx->gc);
push_pair_inner(ctx, l, r);
apfl_gc_tmproots_restore(&ctx->gc, tmproots);
}
bool
apfl_get_member_if_exists(
apfl_ctx ctx,
@ -1333,6 +1368,7 @@ apfl_len(apfl_ctx ctx, apfl_stackidx index)
case VALUE_NIL:
case VALUE_BOOLEAN:
case VALUE_NUMBER:
case VALUE_PAIR:
case VALUE_FUNC:
case VALUE_CFUNC:
case VALUE_USERDATA:
@ -1387,6 +1423,7 @@ apfl_iterate_dict(apfl_ctx ctx, apfl_stackidx dict, void *opaque, bool (*it)(apf
iterate_dict_inner(ctx, dict, opaque, it);
apfl_gc_tmproots_restore(&ctx->gc, tmproots);
}
static bool
get_string_view_of_value(struct apfl_string_view *sv, struct apfl_value value)
{
@ -1399,6 +1436,7 @@ get_string_view_of_value(struct apfl_string_view *sv, struct apfl_value value)
case VALUE_USERDATA:
case VALUE_LIST:
case VALUE_DICT:
case VALUE_PAIR:
case VALUE_NATIVE_OBJECT:
return false;
case VALUE_STRING:

View file

@ -75,6 +75,8 @@ enum matcher_mode {
MATCHER_MODE_LIST_END,
MATCHER_MODE_LIST_REMAINING,
MATCHER_MODE_LIST_UNDERFLOW,
MATCHER_MODE_PAIR_L,
MATCHER_MODE_PAIR_R,
};
struct matcher_state {

View file

@ -701,6 +701,9 @@ evaluate(apfl_ctx ctx, struct func_call_stack_entry *cse)
matcher_must_match(ctx, cse);
return;
case INSN_BUILD_PAIR:
apfl_push_pair(ctx, -2, -1);
goto continue_loop;
}
assert(false);
@ -909,6 +912,18 @@ matcher_current_val_in_state(apfl_ctx ctx, struct matcher_state *state, struct a
}
*value = cur.list->items[state->upper-1];
return true;
case MATCHER_MODE_PAIR_L:
case MATCHER_MODE_PAIR_R:
if (!apfl_stack_get(ctx, &cur, -1)) {
raise_invalid_matcher_state(ctx);
}
if (cur.type != VALUE_PAIR) {
raise_invalid_matcher_state(ctx);
}
*value = state->mode == MATCHER_MODE_PAIR_L
? cur.pair->l
: cur.pair->r;
return true;
}
raise_invalid_matcher_state(ctx);
@ -949,12 +964,16 @@ again:;
state->upper--;
return true;
case MATCHER_MODE_LIST_REMAINING:
case MATCHER_MODE_PAIR_R:
if (!apfl_stack_drop(ctx, -1)) {
raise_invalid_matcher_state(ctx);
}
matcher_state_drop(ctx, cse);
goto again; // We also need to advance the previous stack entry,
// like we would do when doing a MATCHER_LEAVE_LIST
goto again; // We also need to advance the previous stack entry, as
// we're done with the currently matched item in that state
case MATCHER_MODE_PAIR_L:
state->mode = MATCHER_MODE_PAIR_R;
return true;
}
raise_invalid_matcher_state(ctx);
@ -1039,6 +1058,26 @@ matcher_remainding(apfl_ctx ctx, struct matcher_call_stack_entry *cse)
state->mode = MATCHER_MODE_LIST_REMAINING;
}
static bool
matcher_unpack_pair(apfl_ctx ctx, struct matcher_call_stack_entry *cse)
{
struct matcher_state *state = matcher_cur_state(ctx, cse);
struct apfl_value cur;
if (!matcher_current_val_in_state(ctx, state, &cur)) {
return false;
}
if (cur.type != VALUE_PAIR) {
return false;
}
apfl_stack_must_push(ctx, cur);
matcher_state_push(ctx, cse, (struct matcher_state) {
.mode = MATCHER_MODE_PAIR_L,
});
return true;
}
static bool
matcher_leave_list(apfl_ctx ctx, struct matcher_call_stack_entry *cse)
{
@ -1291,6 +1330,9 @@ evaluate_matcher(apfl_ctx ctx, struct matcher_call_stack_entry *cse)
case MATCHER_REMAINDING:
matcher_remainding(ctx, cse);
goto continue_loop;
case MATCHER_UNPACK_PAIR:
RETURN_WITHOUT_MATCH_ON_FALSE(ctx, matcher_unpack_pair(ctx, cse));
goto continue_loop;
}
assert(false);

View file

@ -35,7 +35,10 @@ apfl_expr_deinit(struct apfl_allocator allocator, struct apfl_expr *expr)
apfl_expr_dot_deinit(allocator, &expr->dot);
break;
case APFL_EXPR_AT:
apfl_expr_at_deinit(allocator, &expr->at);
apfl_expr_pair_deinit(allocator, &expr->at);
break;
case APFL_EXPR_PAIR:
apfl_expr_pair_deinit(allocator, &expr->pair);
break;
case APFL_EXPR_CONSTANT:
apfl_expr_const_deinit(allocator, &expr->constant);
@ -114,6 +117,20 @@ apfl_expr_param_predicate_deinit(struct apfl_allocator allocator, struct apfl_ex
DEINIT_GENERIC_LHS_RHS_EXPR(allocator, pred, apfl_expr_param_deinit);
}
void
apfl_expr_param_pair_deinit(struct apfl_allocator allocator, struct apfl_expr_param_pair *pair)
{
DESTROY(allocator, pair->lhs, apfl_expr_param_deinit);
DESTROY(allocator, pair->rhs, apfl_expr_param_deinit);
}
void
apfl_expr_param_tagged_deinit(struct apfl_allocator allocator, struct apfl_expr_param_tagged *tagged)
{
DESTROY(allocator, tagged->lhs, apfl_expr_deinit);
DESTROY(allocator, tagged->rhs, apfl_expr_param_deinit);
}
void
apfl_expr_params_deinit(struct apfl_allocator allocator, struct apfl_expr_params *params)
{
@ -142,6 +159,12 @@ apfl_expr_param_deinit(struct apfl_allocator allocator, struct apfl_expr_param *
case APFL_EXPR_PARAM_LIST:
apfl_expr_params_deinit(allocator, &param->list);
break;
case APFL_EXPR_PARAM_PAIR:
apfl_expr_param_pair_deinit(allocator, &param->pair);
break;
case APFL_EXPR_PARAM_TAGGED:
apfl_expr_param_tagged_deinit(allocator, &param->tagged);
break;
case APFL_EXPR_PARAM_BLANK:
// nop
break;
@ -179,6 +202,20 @@ apfl_expr_assignable_list_deinit(struct apfl_allocator allocator, struct apfl_ex
DEINIT_LIST(allocator, list->items, list->len, apfl_expr_assignable_list_item_deinit);
}
void
apfl_expr_assignable_pair_deinit(struct apfl_allocator allocator, struct apfl_expr_assignable_pair *pair)
{
DESTROY(allocator, pair->lhs, apfl_expr_assignable_deinit);
DESTROY(allocator, pair->rhs, apfl_expr_assignable_deinit);
}
void
apfl_expr_assignable_tagged_deinit(struct apfl_allocator allocator, struct apfl_expr_assignable_tagged *tagged)
{
DESTROY(allocator, tagged->lhs, apfl_expr_deinit);
DESTROY(allocator, tagged->rhs, apfl_expr_assignable_deinit);
}
void
apfl_expr_assignable_var_or_member_dot_deinit(struct apfl_allocator allocator, struct apfl_expr_assignable_var_or_member_dot *dot)
{
@ -225,6 +262,12 @@ apfl_expr_assignable_deinit(struct apfl_allocator allocator, struct apfl_expr_as
case APFL_EXPR_ASSIGNABLE_LIST:
apfl_expr_assignable_list_deinit(allocator, &a->list);
break;
case APFL_EXPR_ASSIGNABLE_PAIR:
apfl_expr_assignable_pair_deinit(allocator, &a->pair);
break;
case APFL_EXPR_ASSIGNABLE_TAGGED:
apfl_expr_assignable_tagged_deinit(allocator, &a->tagged);
break;
case APFL_EXPR_ASSIGNABLE_BLANK:
// nop
break;
@ -246,9 +289,9 @@ apfl_expr_dot_deinit(struct apfl_allocator allocator, struct apfl_expr_dot *dot)
}
void
apfl_expr_at_deinit(struct apfl_allocator allocator, struct apfl_expr_at *at)
apfl_expr_pair_deinit(struct apfl_allocator allocator, struct apfl_expr_pair *pair)
{
DEINIT_GENERIC_LHS_RHS_EXPR(allocator, at, apfl_expr_deinit);
DEINIT_GENERIC_LHS_RHS_EXPR(allocator, pair, apfl_expr_deinit);
}
// Move functions
@ -280,7 +323,10 @@ apfl_expr_move(struct apfl_expr *in)
out.dot = apfl_expr_dot_move(&in->dot);
break;
case APFL_EXPR_AT:
out.at = apfl_expr_at_move(&in->at);
out.at = apfl_expr_pair_move(&in->at);
break;
case APFL_EXPR_PAIR:
out.pair = apfl_expr_pair_move(&in->pair);
break;
case APFL_EXPR_CONSTANT:
out.constant = apfl_expr_const_move(&in->constant);
@ -395,6 +441,22 @@ apfl_expr_param_predicate_move(struct apfl_expr_param_predicate *in)
return out;
}
struct apfl_expr_param_pair
apfl_expr_param_pair_move(struct apfl_expr_param_pair *in)
{
struct apfl_expr_param_pair out;
GENERIC_LHS_RHS_PTRS_MOVE(out, in);
return out;
}
struct apfl_expr_param_tagged
apfl_expr_param_tagged_move(struct apfl_expr_param_tagged *in)
{
struct apfl_expr_param_tagged out;
GENERIC_LHS_RHS_PTRS_MOVE(out, in);
return out;
}
struct apfl_expr_params
apfl_expr_params_move(struct apfl_expr_params *in)
{
@ -420,6 +482,12 @@ apfl_expr_param_move(struct apfl_expr_param *in)
case APFL_EXPR_PARAM_LIST:
out.list = apfl_expr_params_move(&in->list);
break;
case APFL_EXPR_PARAM_PAIR:
out.pair = apfl_expr_param_pair_move(&in->pair);
break;
case APFL_EXPR_PARAM_TAGGED:
out.tagged = apfl_expr_param_tagged_move(&in->tagged);
break;
case APFL_EXPR_PARAM_BLANK:
// nop
break;
@ -473,6 +541,22 @@ apfl_expr_assignable_predicate_move(struct apfl_expr_assignable_predicate *in)
return out;
}
struct apfl_expr_assignable_pair
apfl_expr_assignable_pair_move(struct apfl_expr_assignable_pair *in)
{
struct apfl_expr_assignable_pair out;
GENERIC_LHS_RHS_PTRS_MOVE(out, in);
return out;
}
struct apfl_expr_assignable_tagged
apfl_expr_assignable_tagged_move(struct apfl_expr_assignable_tagged *in)
{
struct apfl_expr_assignable_tagged out;
GENERIC_LHS_RHS_PTRS_MOVE(out, in);
return out;
}
struct apfl_expr_assignable_list
apfl_expr_assignable_list_move(struct apfl_expr_assignable_list *in)
{
@ -498,6 +582,12 @@ apfl_expr_assignable_move(struct apfl_expr_assignable *in)
case APFL_EXPR_ASSIGNABLE_LIST:
out.list = apfl_expr_assignable_list_move(&in->list);
break;
case APFL_EXPR_ASSIGNABLE_PAIR:
out.pair = apfl_expr_assignable_pair_move(&in->pair);
break;
case APFL_EXPR_ASSIGNABLE_TAGGED:
out.tagged = apfl_expr_assignable_tagged_move(&in->tagged);
break;
case APFL_EXPR_ASSIGNABLE_BLANK:
// nop
break;
@ -523,10 +613,10 @@ apfl_expr_dot_move(struct apfl_expr_dot *in)
return out;
}
struct apfl_expr_at
apfl_expr_at_move(struct apfl_expr_at *in)
struct apfl_expr_pair
apfl_expr_pair_move(struct apfl_expr_pair *in)
{
struct apfl_expr_at out;
struct apfl_expr_pair out;
GENERIC_LHS_RHS_PTRS_MOVE(out, in);
return out;
}
@ -665,6 +755,20 @@ format_param(struct apfl_io_writer w, struct apfl_expr_param *param, unsigned in
TRY(format_simple_headline(w, "RHS", indent+1));
TRY(format_expr(w, param->predicate.rhs, indent+2));
return true;
case APFL_EXPR_PARAM_PAIR:
TRY(format_simple_headline(w, "Pair", indent));
TRY(format_simple_headline(w, "LHS", indent+1));
TRY(format_param(w, param->pair.lhs, indent+2));
TRY(format_simple_headline(w, "RHS", indent+1));
TRY(format_param(w, param->pair.rhs, indent+2));
return true;
case APFL_EXPR_PARAM_TAGGED:
TRY(format_simple_headline(w, "Tagged", indent));
TRY(format_simple_headline(w, "LHS", indent+1));
TRY(format_expr(w, param->tagged.lhs, indent+2));
TRY(format_simple_headline(w, "RHS", indent+1));
TRY(format_param(w, param->tagged.rhs, indent+2));
return true;
case APFL_EXPR_PARAM_LIST:
TRY(format_simple_headline(w, "List", indent));
for (size_t i = 0; i < param->list.len; i++) {
@ -724,6 +828,20 @@ format_assignable(struct apfl_io_writer w, struct apfl_expr_assignable assignabl
TRY(format_simple_headline(w, "RHS", indent+1));
TRY(format_expr(w, assignable.predicate.rhs, indent+2));
return true;
case APFL_EXPR_ASSIGNABLE_PAIR:
TRY(format_simple_headline(w, "Pair", indent));
TRY(format_simple_headline(w, "LHS", indent+1));
TRY(format_assignable(w, *assignable.pair.lhs, indent+2));
TRY(format_simple_headline(w, "RHS", indent+1));
TRY(format_assignable(w, *assignable.pair.rhs, indent+2));
return true;
case APFL_EXPR_ASSIGNABLE_TAGGED:
TRY(format_simple_headline(w, "Tagged", indent));
TRY(format_simple_headline(w, "LHS", indent+1));
TRY(format_expr(w, assignable.tagged.lhs, indent+2));
TRY(format_simple_headline(w, "RHS", indent+1));
TRY(format_assignable(w, *assignable.tagged.rhs, indent+2));
return true;
case APFL_EXPR_ASSIGNABLE_LIST:
TRY(format_simple_headline(w, "List", indent));
for (size_t i = 0; i < assignable.list.len; i++) {
@ -809,6 +927,13 @@ format_expr(struct apfl_io_writer w, struct apfl_expr *expr, unsigned indent)
TRY(format_simple_headline(w, "RHS", indent+1));
TRY(format_expr(w, expr->at.rhs, indent+2));
return true;
case APFL_EXPR_PAIR:
TRY(format_expr_headline(w, "Pair", expr->position, indent));
TRY(format_simple_headline(w, "LHS", indent+1));
TRY(format_expr(w, expr->pair.lhs, indent+2));
TRY(format_simple_headline(w, "RHS", indent+1));
TRY(format_expr(w, expr->pair.rhs, indent+2));
return true;
case APFL_EXPR_CONSTANT:
return format_constant_with_pos(w, expr->constant, expr->position, indent);
case APFL_EXPR_VAR:
@ -924,6 +1049,12 @@ param_eq(struct apfl_expr_param a, struct apfl_expr_param b)
case APFL_EXPR_PARAM_PREDICATE:
return param_eq(*a.predicate.lhs, *b.predicate.lhs)
&& apfl_expr_eq(*a.predicate.rhs, *b.predicate.rhs);
case APFL_EXPR_PARAM_PAIR:
return param_eq(*a.pair.lhs, *b.pair.lhs)
&& param_eq(*a.pair.rhs, *b.pair.rhs);
case APFL_EXPR_PARAM_TAGGED:
return apfl_expr_eq(*a.tagged.lhs, *b.tagged.lhs)
&& param_eq(*a.tagged.rhs, *b.tagged.rhs);
case APFL_EXPR_PARAM_LIST:
return params_eq(a.list, b.list);
case APFL_EXPR_PARAM_BLANK:
@ -971,6 +1102,12 @@ assignable_eq(struct apfl_expr_assignable a, struct apfl_expr_assignable b)
case APFL_EXPR_ASSIGNABLE_PREDICATE:
return assignable_eq(*a.predicate.lhs, *b.predicate.lhs)
&& apfl_expr_eq(*a.predicate.rhs, *b.predicate.rhs);
case APFL_EXPR_ASSIGNABLE_PAIR:
return assignable_eq(*a.pair.lhs, *b.pair.lhs)
&& assignable_eq(*a.pair.rhs, *b.pair.rhs);
case APFL_EXPR_ASSIGNABLE_TAGGED:
return apfl_expr_eq(*a.tagged.lhs, *b.tagged.lhs)
&& assignable_eq(*a.tagged.rhs, *b.tagged.rhs);
case APFL_EXPR_ASSIGNABLE_LIST:
if (a.list.len != b.list.len) {
return false;
@ -1047,6 +1184,9 @@ apfl_expr_eq(struct apfl_expr a, struct apfl_expr b)
case APFL_EXPR_AT:
return apfl_expr_eq(*a.at.lhs, *b.at.lhs)
&& apfl_expr_eq(*a.at.rhs, *b.at.rhs);
case APFL_EXPR_PAIR:
return apfl_expr_eq(*a.pair.lhs, *b.pair.lhs)
&& apfl_expr_eq(*a.pair.rhs, *b.pair.rhs);
case APFL_EXPR_CONSTANT:
return const_eq(a.constant, b.constant);
case APFL_EXPR_VAR:

View file

@ -0,0 +1,56 @@
===== script =====
foo = 'very :: 'cool
a :: b = foo
print a
print b
fn := {
'a :: x -> print (* x 10)
'b :: x -> print (+ x 42)
'c :: a :: b -> print (- a b)
}
p := 1 :: 2
fn 'b :: 10
fn 'a :: 42
fn 'c :: p
fn 'c :: 10 :: 1
symbol := {->{}}
sym-a := (symbol)
sym-b := (symbol)
sym-c := (symbol)
fn := {
sym-a: x -> print (* x 10)
sym-b: x -> print (+ x 42)
sym-c: a :: b -> print (- a b)
_ -> print "something else"
}
fn sym-b :: 10
fn sym-a :: 42
fn sym-c :: p
fn sym-c :: 10 :: 1
fn (symbol) :: 10 :: 1
a := 1 :: 2 :: 3
tmp := 2 :: 3
b := 1 :: tmp
print (== a b)
===== output =====
very
cool
52
420
-1
9
52
420
-1
9
something else
true

View file

@ -34,6 +34,7 @@ struct gc_object {
struct matcher_instruction_list matcher_instructions;
struct matcher matcher;
struct native_object native_object;
struct value_pair pair;
};
enum gc_type type;
enum gc_status status;
@ -190,6 +191,7 @@ IMPL_NEW(struct cfunction, apfl_gc_new_cfunc, GC_T
IMPL_NEW(struct matcher_instruction_list, apfl_gc_new_matcher_instructions, GC_TYPE_MATCHER_INSTRUCTIONS, matcher_instructions)
IMPL_NEW(struct matcher, apfl_gc_new_matcher, GC_TYPE_MATCHER, matcher )
IMPL_NEW(struct native_object, apfl_gc_new_native_object, GC_TYPE_NATIVE_OBJECT, native_object )
IMPL_NEW(struct value_pair, apfl_gc_new_pair, GC_TYPE_PAIR, pair )
size_t
apfl_gc_tmproots_begin(struct gc *gc)
@ -296,6 +298,9 @@ visit_children(struct gc_object *object, gc_visitor cb, void *opaque)
case GC_TYPE_MATCHER:
apfl_gc_matcher_traverse(&object->matcher, cb, opaque);
return;
case GC_TYPE_PAIR:
apfl_gc_pair_traverse(&object->pair, cb, opaque);
return;
}
assert(false);
@ -348,6 +353,7 @@ deinit_object(struct gc *gc, struct gc_object *object)
apfl_dict_deinit(&object->dict);
return;
case GC_TYPE_VAR:
case GC_TYPE_PAIR:
return;
case GC_TYPE_STRING:
apfl_string_deinit(gc->allocator, &object->string);
@ -520,6 +526,8 @@ type_to_string(enum gc_type type)
return "matcher";
case GC_TYPE_NATIVE_OBJECT:
return "native object";
case GC_TYPE_PAIR:
return "pair";
}
assert(false);

View file

@ -33,6 +33,7 @@ enum gc_type {
GC_TYPE_MATCHER_INSTRUCTIONS,
GC_TYPE_MATCHER,
GC_TYPE_NATIVE_OBJECT,
GC_TYPE_PAIR,
};
struct gc_tmproots {
@ -89,6 +90,7 @@ struct cfunction* apfl_gc_new_cfunc(struct gc *);
struct matcher_instruction_list* apfl_gc_new_matcher_instructions(struct gc *);
struct matcher* apfl_gc_new_matcher(struct gc *);
struct native_object* apfl_gc_new_native_object(struct gc *);
struct value_pair* apfl_gc_new_pair(struct gc *);
#ifdef __cplusplus
}

View file

@ -43,6 +43,8 @@ enum fragment_type {
FRAG_PREDICATE,
FRAG_EXPR,
FRAG_LIST,
FRAG_PAIR,
FRAG_TAGGED,
FRAG_BLANK,
};
@ -70,6 +72,8 @@ struct fragment {
struct fragment_dot dot;
struct fragment_lhs_rhs at;
struct fragment_lhs_rhs predicate;
struct fragment_lhs_rhs pair;
struct fragment_lhs_rhs tagged;
struct apfl_expr expr;
struct fragment_list list;
};
@ -147,6 +151,12 @@ fragment_deinit(struct apfl_allocator allocator, struct fragment *fragment)
case FRAG_PREDICATE:
deinit_fragment_lhs_rhs(allocator, &fragment->predicate);
break;
case FRAG_PAIR:
deinit_fragment_lhs_rhs(allocator, &fragment->pair);
break;
case FRAG_TAGGED:
deinit_fragment_lhs_rhs(allocator, &fragment->tagged);
break;
case FRAG_EXPR:
apfl_expr_deinit(allocator, &fragment->expr);
break;
@ -211,6 +221,12 @@ fragment_move(struct fragment *in)
case FRAG_PREDICATE:
out.predicate = fragment_lhs_rhs_move(&in->predicate);
break;
case FRAG_PAIR:
out.pair = fragment_lhs_rhs_move(&in->pair);
break;
case FRAG_TAGGED:
out.tagged = fragment_lhs_rhs_move(&in->tagged);
break;
case FRAG_EXPR:
out.expr = apfl_expr_move(&in->expr);
break;
@ -649,6 +665,19 @@ fragment_to_expr_inner(apfl_parser_ptr p, struct fragment *fragment, struct apfl
}
out->position = fragment->position;
return true;
case FRAG_PAIR:
out->type = APFL_EXPR_PAIR;
if ((out->pair.lhs = fragment_to_expr_allocated(p, fragment_move(fragment->at.lhs))) == NULL) {
return false;
}
if ((out->pair.rhs = fragment_to_expr_allocated(p, fragment_move(fragment->at.rhs))) == NULL) {
return false;
}
out->position = fragment->position;
return true;
case FRAG_TAGGED:
p->error = err_unexpected_token(APFL_TOK_COLON, fragment->position);
return false;
case FRAG_PREDICATE:
p->error = err_unexpected_token(APFL_TOK_QUESTION_MARK, fragment->position);
return false;
@ -1021,6 +1050,26 @@ static bool fragment_to_param(apfl_parser_ptr, struct fragment, struct apfl_expr
static bool fragments_to_params(apfl_parser_ptr, struct fragment_list, struct apfl_expr_params *);
static bool
fragment_to_allocated_param(
apfl_parser_ptr p,
struct fragment *in,
struct apfl_expr_param **out
) {
if ((*out = ALLOC_OBJ(p->allocator, struct apfl_expr_param)) == NULL) {
p->error = apfl_error_simple(APFL_ERR_MALLOC_FAILED);
return false;
}
if (!fragment_to_param(p, fragment_move(in), *out)) {
FREE_OBJ(p->allocator, *out);
*out = NULL;
return false;
}
return true;
}
static bool
predicate_fragment_to_param(
apfl_parser_ptr p,
@ -1031,13 +1080,7 @@ predicate_fragment_to_param(
out->predicate.lhs = NULL;
out->predicate.rhs = NULL;
if ((out->predicate.lhs = ALLOC_OBJ(p->allocator, struct apfl_expr_param)) == NULL) {
goto error;
}
if (!fragment_to_param(p, fragment_move(lhs_rhs->lhs), out->predicate.lhs)) {
FREE_OBJ(p->allocator, out->predicate.lhs);
out->predicate.lhs = NULL;
if (!fragment_to_allocated_param(p, lhs_rhs->lhs, &out->predicate.lhs)) {
goto error;
}
@ -1053,6 +1096,58 @@ error:
return false;
}
static bool
pair_fragment_to_param(
apfl_parser_ptr p,
struct fragment_lhs_rhs *lhs_rhs,
struct apfl_expr_param *out
) {
out->type = APFL_EXPR_PARAM_PAIR;
out->pair.lhs = NULL;
out->pair.rhs = NULL;
if (!fragment_to_allocated_param(p, lhs_rhs->lhs, &out->pair.lhs)) {
goto error;
}
if (!fragment_to_allocated_param(p, lhs_rhs->rhs, &out->pair.rhs)) {
goto error;
}
return true;
error:
apfl_expr_param_deinit(p->allocator, out);
return false;
}
static bool
tagged_fragment_to_param(
apfl_parser_ptr p,
struct fragment_lhs_rhs *lhs_rhs,
struct apfl_expr_param *out
) {
out->type = APFL_EXPR_PARAM_TAGGED;
out->tagged.lhs = NULL;
out->tagged.rhs = NULL;
out->tagged.lhs = fragment_to_expr_allocated(p, fragment_move(lhs_rhs->lhs));
if (out->predicate.lhs == NULL) {
goto error;
}
if (!fragment_to_allocated_param(p, lhs_rhs->rhs, &out->pair.rhs)) {
goto error;
}
return true;
error:
apfl_expr_param_deinit(p->allocator, out);
return false;
}
static bool
fragment_to_param_inner(
apfl_parser_ptr p,
@ -1079,6 +1174,10 @@ fragment_to_param_inner(
return false;
case FRAG_PREDICATE:
return predicate_fragment_to_param(p, &fragment->predicate, out);
case FRAG_PAIR:
return pair_fragment_to_param(p, &fragment->pair, out);
case FRAG_TAGGED:
return tagged_fragment_to_param(p, &fragment->tagged, out);
case FRAG_EXPR:
p->error = (struct apfl_error) {
.type = APFL_ERR_UNEXPECTED_EXPRESSION,
@ -1252,6 +1351,12 @@ static bool fragment_to_assignable_var_or_member(
case FRAG_LIST:
p->error = err_unexpected_token(APFL_TOK_LBRACKET, fragment->position);
return false;
case FRAG_PAIR:
p->error = err_unexpected_token(APFL_TOK_DOUBLE_COLON, fragment->position);
return false;
case FRAG_TAGGED:
p->error = err_unexpected_token(APFL_TOK_COLON, fragment->position);
return false;
case FRAG_BLANK:
p->error = (struct apfl_error) {
.type = APFL_ERR_UNEXPECTED_BLANK_IN_MEMBER_ACCESS,
@ -1355,6 +1460,27 @@ fragment_list_to_assignable_list(
return ok;
}
static bool
fragment_to_allocated_assignable(
apfl_parser_ptr p,
struct fragment *fragment,
struct apfl_expr_assignable **out
) {
*out = ALLOC_OBJ(p->allocator, struct apfl_expr_assignable);
if (*out == NULL) {
p->error = apfl_error_simple(APFL_ERR_MALLOC_FAILED);
return false;
}
if (!fragment_to_assignable(p, fragment_move(fragment), *out)) {
FREE_OBJ(p->allocator, *out);
*out = NULL;
return false;
}
return true;
}
static bool
fragment_to_assignable_inner(
apfl_parser_ptr p,
@ -1382,30 +1508,25 @@ fragment_to_assignable_inner(
return true;
case FRAG_PREDICATE:
out->type = APFL_EXPR_ASSIGNABLE_PREDICATE;
out->predicate.lhs = NULL;
out->predicate.rhs = NULL;
out->predicate.lhs = ALLOC_OBJ(p->allocator, struct apfl_expr_assignable);
out->predicate.rhs = ALLOC_OBJ(p->allocator, struct apfl_expr);
if (out->predicate.lhs == NULL || out->predicate.rhs == NULL) {
p->error = apfl_error_simple(APFL_ERR_MALLOC_FAILED);
goto error;
}
if (!fragment_to_assignable(
if (!fragment_to_allocated_assignable(
p,
fragment_move(fragment->at.lhs),
out->predicate.lhs
fragment->at.lhs,
&out->predicate.lhs
)) {
FREE_OBJ(p->allocator, out->predicate.rhs);
out->predicate.rhs = NULL;
goto error;
}
out->predicate.rhs = ALLOC_OBJ(p->allocator, struct apfl_expr);
if (!fragment_to_expr(
p,
fragment_move(fragment->at.rhs),
out->predicate.rhs
)) {
FREE_OBJ(p->allocator, out->predicate.rhs);
out->predicate.rhs = NULL;
goto error;
}
@ -1426,6 +1547,53 @@ fragment_to_assignable_inner(
goto error;
}
return true;
case FRAG_PAIR:
out->type = APFL_EXPR_ASSIGNABLE_PAIR;
out->pair.lhs = NULL;
out->pair.rhs = NULL;
if (!fragment_to_allocated_assignable(
p,
fragment->pair.lhs,
&out->pair.lhs
)) {
goto error;
}
if (!fragment_to_allocated_assignable(
p,
fragment->pair.rhs,
&out->pair.rhs
)) {
goto error;
}
return true;
case FRAG_TAGGED:
out->type = APFL_EXPR_ASSIGNABLE_TAGGED;
out->tagged.lhs = NULL;
out->tagged.rhs = NULL;
out->tagged.lhs = ALLOC_OBJ(p->allocator, struct apfl_expr);
if (!fragment_to_expr(
p,
fragment_move(fragment->at.lhs),
out->tagged.lhs
)) {
FREE_OBJ(p->allocator, out->tagged.lhs);
out->tagged.lhs = NULL;
goto error;
}
if (!fragment_to_allocated_assignable(
p,
fragment->at.rhs,
&out->tagged.rhs
)) {
goto error;
}
return true;
case FRAG_BLANK:
out->type = APFL_EXPR_ASSIGNABLE_BLANK;
@ -2071,6 +2239,8 @@ parse_fragment(apfl_parser_ptr p, struct fragment *fragment, bool need, enum par
break;
case APFL_TOK_AT:
case APFL_TOK_QUESTION_MARK:
case APFL_TOK_DOUBLE_COLON:
case APFL_TOK_COLON:
if (
((lhs = ALLOC_OBJ(p->allocator, struct fragment)) == NULL)
|| ((rhs = ALLOC_OBJ(p->allocator, struct fragment)) == NULL)
@ -2079,7 +2249,14 @@ parse_fragment(apfl_parser_ptr p, struct fragment *fragment, bool need, enum par
return PF_ERROR;
}
switch (parse_fragment(p, rhs, true, FFLAG_NO_POSTFIXS | FFLAG_NO_EXPAND)) {
switch (parse_fragment(
p,
rhs,
true,
p->token.type == APFL_TOK_DOUBLE_COLON || p->token.type == APFL_TOK_COLON
? FFLAG_NO_EXPAND
: FFLAG_NO_EXPAND | FFLAG_NO_POSTFIXS
)) {
case PF_OK:
break;
case PF_ERROR:
@ -2100,13 +2277,22 @@ parse_fragment(apfl_parser_ptr p, struct fragment *fragment, bool need, enum par
fragment->position = token_pos;
fragment->at.lhs = lhs;
fragment->at.rhs = rhs;
} else {
assert(token_type == APFL_TOK_QUESTION_MARK);
} else if (token_type == APFL_TOK_QUESTION_MARK) {
fragment->type = FRAG_PREDICATE;
fragment->position = token_pos;
fragment->predicate.lhs = lhs;
fragment->predicate.rhs = rhs;
} else if (token_type == APFL_TOK_DOUBLE_COLON) {
fragment->type = FRAG_PAIR;
fragment->position = token_pos;
fragment->pair.lhs = lhs;
fragment->pair.rhs = rhs;
} else {
assert(token_type == APFL_TOK_COLON);
fragment->type = FRAG_TAGGED;
fragment->position = token_pos;
fragment->tagged.lhs = lhs;
fragment->tagged.rhs = rhs;
}
break;

View file

@ -491,19 +491,19 @@ TEST(complex_dot_at_access, t) {
expect_expr(pt, (struct apfl_expr) {
.type = APFL_EXPR_AT,
.position = POS(1, 12),
.at = (struct apfl_expr_at) {
.at = (struct apfl_expr_pair) {
.lhs = BEGIN_NEW(pt, struct apfl_expr) {
.type = APFL_EXPR_AT,
.position = POS(1, 8),
.at = (struct apfl_expr_at) {
.at = (struct apfl_expr_pair) {
.lhs = BEGIN_NEW(pt, struct apfl_expr) {
.type = APFL_EXPR_AT,
.position = POS(1, 6),
.at = (struct apfl_expr_at) {
.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_at) {
.at = (struct apfl_expr_pair) {
.lhs = new_var(pt, 1, 1, "a"),
.rhs = new_var(pt, 1, 3, "b"),
}
@ -521,7 +521,7 @@ TEST(complex_dot_at_access, t) {
.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) {
.at = (struct apfl_expr_pair) {
.lhs = new_var(pt, 1, 14, "e"),
.rhs = BEGIN_NEW(pt, struct apfl_expr) {
.type = APFL_EXPR_CALL,
@ -876,6 +876,12 @@ TEST(assignment, t) {
// 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) {
@ -987,6 +993,76 @@ TEST(assignment, t) {
} 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);
@ -1057,6 +1133,7 @@ TEST(complex_function, t) {
" b\n"
" c { x -> y }\n"
" \n"
"a?b :: c:d?e f:g -> h"
"}\n"
);
expect_expr(pt, (struct apfl_expr) {
@ -1177,6 +1254,54 @@ TEST(complex_function, t) {
}
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);
@ -1303,6 +1428,38 @@ TEST(member_assignment_with_predicate, t) {
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);
@ -1448,6 +1605,7 @@ TESTS_BEGIN
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),

View file

@ -85,6 +85,10 @@ apfl_token_type_name(enum apfl_token_type type)
return "NAME";
case APFL_TOK_STRING:
return "STRING";
case APFL_TOK_COLON:
return ":";
case APFL_TOK_DOUBLE_COLON:
return "::";
}
return "(unknown token)";

View file

@ -316,20 +316,18 @@ colon(apfl_tokenizer_ptr tokenizer)
case RR_ERR:
return APFL_PARSE_ERROR;
case RR_EOF:
tokenizer->next_mode = NM_EOF;
tokenizer->error = (struct apfl_error) { .type = APFL_ERR_UNEXPECTED_EOF };
return APFL_PARSE_ERROR;
return yield_simple_token(tokenizer, APFL_TOK_COLON, pos);
}
if (byte != '=') {
tokenizer->error = (struct apfl_error) {
.type = APFL_ERR_EXPECTED_EQ_AFTER_COLON,
.position = tokenizer->position,
};
return APFL_PARSE_ERROR;
switch (byte) {
case '=':
return yield_simple_token(tokenizer, APFL_TOK_LOCAL_ASSIGN, pos);
case ':':
return yield_simple_token(tokenizer, APFL_TOK_DOUBLE_COLON, pos);
default:
unread_byte(tokenizer, pos);
return yield_simple_token(tokenizer, APFL_TOK_COLON, pos);
}
return yield_simple_token(tokenizer, APFL_TOK_LOCAL_ASSIGN, pos);
}
static enum apfl_parse_result

View file

@ -263,8 +263,10 @@ TEST(all_tokens, t) {
"'foo ;; , \\\n"
// 1234567890123456
"@ . ? ~ -> = :=\n"
// 123456
"({[]})"
// 1234567
"({[]})\n"
// 1234567
": :: :="
);
expect_text_token (tt, 1, 1, APFL_TOK_COMMENT, " test");
@ -300,6 +302,10 @@ TEST(all_tokens, t) {
expect_simple_token(tt, 6, 4, APFL_TOK_RBRACKET);
expect_simple_token(tt, 6, 5, APFL_TOK_RBRACE);
expect_simple_token(tt, 6, 6, APFL_TOK_RPAREN);
expect_simple_token(tt, 6, 7, APFL_TOK_LINEBREAK);
expect_simple_token(tt, 7, 1, APFL_TOK_COLON);
expect_simple_token(tt, 7, 3, APFL_TOK_DOUBLE_COLON);
expect_simple_token(tt, 7, 6, APFL_TOK_LOCAL_ASSIGN);
expect_eof(tt);

View file

@ -143,6 +143,13 @@ format(unsigned indent, struct apfl_io_writer w, struct apfl_value value, bool s
TRY(apfl_format_put_indent(w, indent));
TRY(apfl_io_write_string(w, "]"));
return true;
case VALUE_PAIR:
TRY(apfl_io_write_string(w, "( "));
TRY(format(indent, w, value.pair->l, false));
TRY(apfl_io_write_string(w, " :: "));
TRY(format(indent, w, value.pair->r, false));
TRY(apfl_io_write_string(w, " )"));
return true;
case VALUE_FUNC:
TRY(apfl_io_write_string(w, "{ ... }"));
if (value.func->name != NULL) {
@ -186,6 +193,8 @@ apfl_value_type_to_abstract_type(enum value_type type)
return APFL_VALUE_LIST;
case VALUE_DICT:
return APFL_VALUE_DICT;
case VALUE_PAIR:
return APFL_VALUE_PAIR;
case VALUE_FUNC:
case VALUE_CFUNC:
return APFL_VALUE_FUNC;
@ -214,6 +223,8 @@ apfl_type_name(enum apfl_value_type type)
return "list";
case APFL_VALUE_DICT:
return "dict";
case APFL_VALUE_PAIR:
return "pair";
case APFL_VALUE_FUNC:
return "function";
case APFL_VALUE_USERDATA:
@ -223,6 +234,20 @@ apfl_type_name(enum apfl_value_type type)
return "?";
}
struct value_pair *
apfl_pair_new(struct gc *gc, struct apfl_value l, struct apfl_value r)
{
struct value_pair *pair = apfl_gc_new_pair(gc);
if (pair == NULL) {
return NULL;
}
pair->l = apfl_value_set_cow_flag(l);
pair->r = apfl_value_set_cow_flag(r);
return pair;
}
struct function *
apfl_func_new(struct gc *gc, size_t cap, struct scope *scope, int line_defined, struct apfl_string *filename)
{
@ -449,6 +474,10 @@ apfl_value_eq(const struct apfl_value a, const struct apfl_value b)
return b.type == VALUE_LIST && list_eq(a.list, b.list);
case VALUE_DICT:
return b.type == VALUE_DICT && dict_eq(a.dict, b.dict);
case VALUE_PAIR:
return b.type == VALUE_PAIR
&& apfl_value_eq(a.pair->l, b.pair->l)
&& apfl_value_eq(a.pair->r, b.pair->r);
case VALUE_FUNC:
return b.type == VALUE_FUNC && a.func == b.func;
case VALUE_CFUNC:
@ -492,6 +521,7 @@ apfl_value_cmp(const struct apfl_value a, const struct apfl_value b)
return CMP(apfl_string_cmp(as_string_view(a), as_string_view(b)), 0);
case VALUE_LIST:
case VALUE_DICT:
case VALUE_PAIR:
case VALUE_USERDATA:
case VALUE_NATIVE_OBJECT:
if (b.type != a.type) {
@ -766,6 +796,15 @@ apfl_value_hash(const struct apfl_value value)
// it's rather unusual to have dictionaries as keys, this is fine
// for now, but should be improved nonetheless!
return hash;
case VALUE_PAIR: {
apfl_hash item_hash = apfl_value_hash(value.pair->l);
hash = apfl_hash_fnv1a_add(&item_hash, sizeof(apfl_hash), hash);
item_hash = apfl_value_hash(value.pair->r);
hash = apfl_hash_fnv1a_add(&item_hash, sizeof(apfl_hash), hash);
return hash;
}
case VALUE_FUNC:
return apfl_hash_fnv1a_add(&value.func, sizeof(struct function *), hash);
case VALUE_CFUNC:
@ -797,6 +836,8 @@ apfl_value_get_gc_object(struct apfl_value value)
return GC_OBJECT_FROM(value.list, GC_TYPE_LIST);
case VALUE_DICT:
return GC_OBJECT_FROM(value.dict, GC_TYPE_DICT);
case VALUE_PAIR:
return GC_OBJECT_FROM(value.pair, GC_TYPE_PAIR);
case VALUE_FUNC:
return GC_OBJECT_FROM(value.func, GC_TYPE_FUNC);
case VALUE_CFUNC:
@ -847,6 +888,13 @@ apfl_gc_dict_traverse(struct dict_header *dict, gc_visitor cb, void *opaque)
}
}
void
apfl_gc_pair_traverse(struct value_pair *pair, gc_visitor cb, void *opaque)
{
apfl_value_visit_gc_object(pair->l, cb, opaque);
apfl_value_visit_gc_object(pair->r, cb, opaque);
}
void
apfl_gc_func_traverse(struct function* function, gc_visitor cb, void *opaque)
{

View file

@ -24,6 +24,7 @@ enum value_type {
VALUE_CONST_STRING,
VALUE_LIST,
VALUE_DICT,
VALUE_PAIR,
VALUE_FUNC,
VALUE_CFUNC,
VALUE_USERDATA,
@ -80,6 +81,7 @@ struct apfl_value {
struct apfl_string_view const_string;
struct list_header *list;
struct dict_header *dict;
struct value_pair *pair;
struct function *func;
struct cfunction *cfunc;
void *userdata;
@ -87,6 +89,11 @@ struct apfl_value {
};
};
struct value_pair {
struct apfl_value l;
struct apfl_value r;
};
enum apfl_value_type apfl_value_type_to_abstract_type(enum value_type);
#define VALUE_IS_A(v, t) (apfl_value_type_to_abstract_type((v).type) == (t))
@ -145,6 +152,8 @@ bool apfl_dict_set_raw(
size_t apfl_dict_len(struct dict_header *);
void apfl_dict_deinit(struct dict_header *);
struct value_pair *apfl_pair_new(struct gc *, struct apfl_value l, struct apfl_value r);
struct function *apfl_func_new(
struct gc *,
size_t cap,
@ -169,6 +178,7 @@ void apfl_value_visit_gc_object(struct apfl_value value, gc_visitor cb, void *op
bool apfl_value_add_as_tmproot(struct gc *, struct apfl_value);
void apfl_gc_list_traverse(struct list_header *, gc_visitor, void *);
void apfl_gc_dict_traverse(struct dict_header *, gc_visitor, void *);
void apfl_gc_pair_traverse(struct value_pair *, gc_visitor, void *);
void apfl_gc_func_traverse(struct function*, gc_visitor, void *);
void apfl_gc_cfunc_traverse(struct cfunction*, gc_visitor, void *);