Skip to content

Commit

Permalink
Implement function composition operator @< and @>
Browse files Browse the repository at this point in the history
  • Loading branch information
mhermier committed Mar 13, 2023
1 parent b97c0a3 commit 0d79e6f
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 2 deletions.
37 changes: 35 additions & 2 deletions src/vm/wren_compiler.c
Original file line number Diff line number Diff line change
Expand Up @@ -1743,6 +1743,7 @@ typedef enum
PREC_LOWEST,
PREC_ASSIGNMENT, // =
PREC_FN_COMPOSITION_CALL, // |< |>
PREC_FN_COMPOSITION, // @< @>
PREC_CONDITIONAL, // ?:
PREC_LOGICAL_OR, // ||
PREC_LOGICAL_AND, // &&
Expand Down Expand Up @@ -2590,6 +2591,38 @@ static void forwardFnCompositionCall(Compiler* compiler, bool canAssign)
return fnCompositionCall(compiler, canAssign, true);
}

static void fnComposition(Compiler* compiler, bool canAssign, bool isForward)
{
loadCoreVariable(compiler, "ComposedFn");

// Prepare the stack to call the helper class
emitOp(compiler, CODE_SWAP);

GrammarRule* rule = getRule(compiler->parser->previous.type);

// An infix operator cannot end an expression.
ignoreNewlines(compiler);

// Compile the right-hand side.
parsePrecedence(compiler, (Precedence)(rule->precedence + isForward));

if (isForward)
{
emitOp(compiler, CODE_SWAP);
}
callMethod(compiler, 2, "new(_,_)", 8);
}

static void backwardFnComposition(Compiler* compiler, bool canAssign)
{
fnComposition(compiler, canAssign, false);
}

static void forwardFnComposition(Compiler* compiler, bool canAssign)
{
fnComposition(compiler, canAssign, true);
}

static void and_(Compiler* compiler, bool canAssign)
{
ignoreNewlines(compiler);
Expand Down Expand Up @@ -2812,8 +2845,8 @@ GrammarRule rules[] =
/* TOKEN_LEFT_BRACE */ PREFIX(map),
/* TOKEN_RIGHT_BRACE */ UNUSED,
/* TOKEN_AT */ UNUSED,
/* TOKEN_ATLT */ UNUSED,
/* TOKEN_ATGT */ UNUSED,
/* TOKEN_ATLT */ INFIX(PREC_FN_COMPOSITION, backwardFnComposition),
/* TOKEN_ATGT */ INFIX(PREC_FN_COMPOSITION, forwardFnComposition),
/* TOKEN_COLON */ UNUSED,
/* TOKEN_DOT */ INFIX(PREC_CALL, call),
/* TOKEN_DOTDOT */ INFIX_OPERATOR(PREC_RANGE, ".."),
Expand Down
9 changes: 9 additions & 0 deletions src/vm/wren_core.wren
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@ class Fn {}
class Null {}
class Num {}

class ComposedFn {
construct new(lhs, rhs) {
_lhs = lhs
_rhs = rhs
}

call(value) { _lhs.call(_rhs.call(value)) }
}

class Sequence {
all(f) {
var result = true
Expand Down
9 changes: 9 additions & 0 deletions src/vm/wren_core.wren.inc
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,15 @@ static const char* coreModuleSource =
"class Null {}\n"
"class Num {}\n"
"\n"
"class ComposedFn {\n"
" construct new(lhs, rhs) {\n"
" _lhs = lhs\n"
" _rhs = rhs\n"
" }\n"
"\n"
" call(value) { _lhs.call(_rhs.call(value)) }\n"
"}\n"
"\n"
"class Sequence {\n"
" all(f) {\n"
" var result = true\n"
Expand Down
12 changes: 12 additions & 0 deletions test/language/functional_operator/backward_composition.wren
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
var add1 = Fn.new {|x| x + 1}
var double = Fn.new {|x| x * 2}

System.print((double @< add1).call(1)) // expect: 4

// Check operator priority
System.print(1 |> double @< add1) // expect: 4
System.print(double @< add1 |< 1) // expect: 4

// Swallow a trailing newline.
System.print(double @<
add1 |< 1) // expect: 4
12 changes: 12 additions & 0 deletions test/language/functional_operator/forward_composition.wren
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
var add1 = Fn.new {|x| x + 1}
var double = Fn.new {|x| x * 2}

System.print((double @> add1).call(1)) // expect: 3

// Check operator priority
System.print(1 |> double @> add1) // expect: 3
System.print(double @> add1 |< 1) // expect: 3

// Swallow a trailing newline.
System.print(double @>
add1 |< 1) // expect: 3

0 comments on commit 0d79e6f

Please sign in to comment.