Skip to content

Commit

Permalink
Add SOME and FURTHER as native combinators
Browse files Browse the repository at this point in the history
  • Loading branch information
hostilefork committed Jul 19, 2021
1 parent 4a17795 commit 608286c
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 0 deletions.
92 changes: 92 additions & 0 deletions src/core/functionals/c-combinator.c
Expand Up @@ -413,6 +413,98 @@ REBNATIVE(text_x_combinator)
}


//
// some-combinator: native-combinator [
//
// {Must run at least one match}
//
// return: "Result of last successful match"
// [<opt> any-value!]
// parser [action!]
// ]
//
REBNATIVE(some_combinator)
{
INCLUDE_PARAMS_OF_SOME_COMBINATOR;

REBVAL *remainder = ARG(remainder);
REBVAL *parser = ARG(parser);
REBVAL *input = ARG(input);
UNUSED(ARG(state));

if (Call_Parser_Throws(D_OUT, remainder, parser, input))
return R_THROWN;

if (IS_NULLED(D_OUT))
return nullptr; // didn't match even once, so not enough.

while (true) {
//
// Make the remainder from previous call the new input
//
Get_Var_May_Fail(input, remainder, SPECIFIED, true, false);

// Don't overwrite the last output (if it's null we want the previous
// iteration's successful output value)
//
if (Call_Parser_Throws(D_SPARE, remainder, parser, input))
return R_THROWN;

if (IS_NULLED(D_SPARE)) {
//
// There's no guarantee that a parser that fails leaves the
// remainder as-is (in fact multi-returns have historically unset
// variables to hide their previous values from acting as input).
// So we have to put the remainder back to the input we just tried
// but didn't work.
//
Set_Var_May_Fail(remainder, SPECIFIED, input, SPECIFIED, false);

return D_OUT; // return previous successful parser result
}

Move_Cell(D_OUT, D_SPARE); // update last successful result
}
}


//
// further-combinator: native-combinator [
//
// {Pass through the result only if the input was advanced by the rule}
//
// return: "parser result if it succeeded and advanced input, else NULL"
// [<opt> any-value!]
// parser [action!]
// ]
//
REBNATIVE(further_combinator)
{
INCLUDE_PARAMS_OF_FURTHER_COMBINATOR;

REBVAL *remainder = ARG(remainder);
REBVAL *input = ARG(input);
REBVAL *parser = ARG(parser);
UNUSED(ARG(state));

if (Call_Parser_Throws(D_OUT, remainder, parser, input))
return R_THROWN;

if (IS_NULLED(D_OUT))
return nullptr; // the parse rule did not match

if (GET_CELL_FLAG(D_OUT, OUT_NOTE_STALE))
fail ("Rule passed to FURTHER must synthesize a product");

Get_Var_May_Fail(D_SPARE, remainder, SPECIFIED, true, false);

if (VAL_INDEX(D_SPARE) <= VAL_INDEX(input))
return nullptr; // the rule matched but did not advance the input

return D_OUT;
}


struct Combinator_Param_State {
REBCTX *ctx;
REBFRM *frame_;
Expand Down
6 changes: 6 additions & 0 deletions src/mezz/uparse.reb
Expand Up @@ -217,6 +217,7 @@ default-combinators: make map! reduce [
return null
]

comment [
'further combinator [
{Pass through the result only if the input was advanced by the rule}
return: "parser result if it succeeded and advanced input, else NULL"
Expand All @@ -236,6 +237,8 @@ default-combinators: make map! reduce [
set remainder pos
return unmeta result'
]
] ; replaced by native
'further :further-combinator

=== LOOPING CONSTRUCT KEYWORDS ===

Expand Down Expand Up @@ -266,6 +269,7 @@ default-combinators: make map! reduce [
fail ~unreachable~
]

comment [
'some combinator [
{Must run at least one match}
return: "Result of last successful match"
Expand All @@ -286,6 +290,8 @@ default-combinators: make map! reduce [
]
fail ~unreachable~
]
] ; replaced by native
'some :some-combinator

'tally combinator [
{Iterate a rule and count the number of times it matches}
Expand Down

0 comments on commit 608286c

Please sign in to comment.