feat(parsely): Add stringUntil() parser

The counterpart of stringOf(), it reads characters until it matches its
parameter.
This commit is contained in:
Sam Atkins 2024-05-31 15:47:02 +01:00
parent 5656d9d42f
commit d46b043c5d
2 changed files with 49 additions and 1 deletions

View File

@ -1,6 +1,6 @@
import { adapt_parser, VALUE } from './parser.js';
import { Discard, FirstMatch, Optional, Repeat, Sequence } from './parsers/combinators.js';
import { Fail, Literal, None, StringOf, Symbol } from './parsers/terminals.js';
import { Fail, Literal, None, StringOf, StringUntil, Symbol } from './parsers/terminals.js';
class ParserWithAction {
#parser;
@ -89,6 +89,7 @@ export const standard_parsers = () => {
repeat: Repeat,
sequence: Sequence,
stringOf: StringOf,
stringUntil: StringUntil,
symbol: Symbol,
}
}

View File

@ -53,6 +53,53 @@ export class StringOf extends Parser {
}
}
/**
* Parses characters into a string, until it encounters the given character, unescaped.
* @param testOrCharacter End of the string. Either a character, or a function that takes a character,
* and returns whether it ends the string.
* @param escapeCharacter Character to use as the escape character. By default, is '\'.
*/
export class StringUntil extends Parser {
_create(testOrCharacter, { escapeCharacter = '\\' } = {}) {
if (typeof testOrCharacter === 'string') {
this.test = (c => c === testOrCharacter);
} else {
this.test = testOrCharacter;
}
this.escapeCharacter = escapeCharacter;
}
_parse(stream) {
const subStream = stream.fork();
let text = '';
let lastWasEscape = false;
while (true) {
let { done, value } = subStream.look();
if ( done ) break;
if ( !lastWasEscape && this.test(value) )
break;
subStream.next();
if (value === this.escapeCharacter) {
lastWasEscape = true;
continue;
}
lastWasEscape = false;
text += value;
}
if (lastWasEscape)
return INVALID;
if (text.length === 0)
return UNRECOGNIZED;
stream.join(subStream);
return { status: VALUE, $: 'stringUntil', value: text };
}
}
/**
* Parses an object defined by the symbol registry.
* @param symbolName The name of the symbol to parse.