diff --git a/packages/phoenix/packages/parsely/parsers/combinators.js b/packages/phoenix/packages/parsely/parsers/combinators.js index 5078b6a5..b597e857 100644 --- a/packages/phoenix/packages/parsely/parsers/combinators.js +++ b/packages/phoenix/packages/parsely/parsers/combinators.js @@ -73,7 +73,7 @@ export class Optional extends Parser { /** * Parses a repeated sequence of values with separators between them. * @param value_parser Parser for the value - * @param separator_parser Parser for the separator + * @param separator_parser Parser for the separator, optional * @param trailing Whether to allow a trailing separator */ export class Repeat extends Parser { @@ -85,40 +85,49 @@ export class Repeat extends Parser { _parse (stream) { const results = []; - for ( ;; ) { - const subStream = stream.fork(); + const subStream = stream.fork(); - // Value - const result = this.value_parser.parse(subStream); - if ( result.status === UNRECOGNIZED ) { - break; - } - if ( result.status === INVALID ) { - return { status: INVALID, value: result }; - } + // Parse first value + const result = this.value_parser.parse(subStream); + if ( result.status === INVALID ) + return { status: INVALID, value: result }; + + if ( result.status === VALUE ) { stream.join(subStream); - if ( ! result.$discard ) results.push(result); + if (!result.$discard) results.push(result); - // Separator - if ( ! this.separator_parser ) { - continue; - } - const separatorResult = this.separator_parser.parse(subStream); - if ( separatorResult.status === UNRECOGNIZED ) { - break; - } - if ( separatorResult.status === INVALID ) { - return { status: INVALID, value: separatorResult }; - } - stream.join(subStream); - if ( ! result.$discard ) results.push(separatorResult); + // Repeatedly parse + for (;;) { + // Separator + if (!this.separator_parser) + continue; - // TODO: Detect trailing separator and reject it if trailing==false + const separatorResult = this.separator_parser.parse(subStream); + if (separatorResult.status === UNRECOGNIZED) + break; + if (separatorResult.status === INVALID) + return { status: INVALID, value: separatorResult }; + stream.join(subStream); + if (!separatorResult.$discard) results.push(separatorResult); + + // Value + const result = this.value_parser.parse(subStream); + if (result.status === UNRECOGNIZED) { + // If we failed to parse a value, we have a trailing separator + if (this.trailing === false) + return { status: INVALID, value: result }; + break; + } + if (result.status === INVALID) + return { status: INVALID, value: result }; + + stream.join(subStream); + if (!result.$discard) results.push(result); + } } - if ( results.length === 0 ) { + if ( results.length === 0 ) return UNRECOGNIZED; - } return { status: VALUE, value: results }; }