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:
parent
8233e94ab3
commit
6056e3a450
20 changed files with 953 additions and 58 deletions
55
README.md
55
README.md
|
|
@ -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 = [
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
56
src/apfl.h
56
src/apfl.h
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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 "??";
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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, ¶m->list, args);
|
||||
case APFL_EXPR_PARAM_PREDICATE:
|
||||
return compile_param_predicate(compiler, position, ¶m->predicate, args);
|
||||
case APFL_EXPR_PARAM_PAIR:
|
||||
return compile_param_pair(compiler, position, ¶m->pair, args);
|
||||
case APFL_EXPR_PARAM_TAGGED:
|
||||
return compile_param_tagged(compiler, position, ¶m->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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
46
src/eval.c
46
src/eval.c
|
|
@ -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);
|
||||
|
|
|
|||
154
src/expr.c
154
src/expr.c
|
|
@ -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, ¶m->list);
|
||||
break;
|
||||
case APFL_EXPR_PARAM_PAIR:
|
||||
apfl_expr_param_pair_deinit(allocator, ¶m->pair);
|
||||
break;
|
||||
case APFL_EXPR_PARAM_TAGGED:
|
||||
apfl_expr_param_tagged_deinit(allocator, ¶m->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:
|
||||
|
|
|
|||
56
src/functional-tests/pairs.at
Normal file
56
src/functional-tests/pairs.at
Normal 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
|
||||
8
src/gc.c
8
src/gc.c
|
|
@ -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);
|
||||
|
|
|
|||
2
src/gc.h
2
src/gc.h
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
234
src/parser.c
234
src/parser.c
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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)";
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
48
src/value.c
48
src/value.c
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
10
src/value.h
10
src/value.h
|
|
@ -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 *);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue