it('can parse a char sequence many times', () => { const zeroOrMoreParser = many(pstring('marco')); let parsing = zeroOrMoreParser.run('marcomarcociao'); expect(parsing.isSuccess).to.be.true; expect(parsing.toString()) .to.be.eql('Validation.Success([[[m,a,r,c,o],[m,a,r,c,o]],row=0;col=10;rest=ciao])'); });
it('allows to exclude parentheses', () => { const insideParens = pchar('(') .discardFirst(many(anyOf(lowercases))) .discardSecond(pchar(')')); expect(insideParens.run('(marco)').toString()) .to.be.eql('Validation.Success([[m,a,r,c,o],row=1;col=0;rest=])'); expect(insideParens.run('()').toString()) .to.be.eql('Validation.Success([[],row=1;col=0;rest=])'); });
it('can parse a char exactly n times and return an array (or fail)', () => { const exactlyThree = many(pchar('m'), 3); let parsing = exactlyThree.run(text('mmmarco')); expect(parsing.isSuccess).to.be.true; expect(parsing.toString()).to.be.eql('Validation.Success([[m,m,m],row=0;col=3;rest=arco])'); parsing = exactlyThree.run(text('mmmmarco')); expect(parsing.isFailure).to.be.true; expect(parsing.toString()).to.be.eql('Validation.Failure([many pchar_m times=3,times param wanted 3; got 4,row=0;col=0;rest=mmmmarco])'); });
it('can parse whitespaces!!', () => { const whitesParser = many(anyOf(whites)); const twoWords = sequenceP([pstring('ciao'), whitesParser, pstring('mamma')]); let parsing = twoWords.run('ciaomammaX'); expect(parsing.toString()) .to.be.eql('Validation.Success([[[c,i,a,o],[],[m,a,m,m,a]],row=0;col=9;rest=X])'); parsing = twoWords.run('ciao mammaX'); expect(parsing.toString()) .to.be.eql('Validation.Success([[[c,i,a,o],[ ],[m,a,m,m,a]],row=0;col=10;rest=X])'); parsing = twoWords.run('ciao mammaX'); expect(parsing.toString()) .to.be.eql('Validation.Success([[[c,i,a,o],[ , , ],[m,a,m,m,a]],row=0;col=12;rest=X])'); parsing = twoWords.run('ciao \t mammaX'); expect(parsing.toString()) .to.be.eql('Validation.Success([[[c,i,a,o],[ ,\t, ],[m,a,m,m,a]],row=0;col=12;rest=X])'); });
it('...also when inside a lists', () => { const substringsWithCommas = many(many1(anyOf(lowercases)).discardSecond(pchar(','))); const listElements = between(pchar('['), substringsWithCommas, pchar(']')); expect(listElements.run('[a,b,cd,marco,]1').toString()) .to.be.eql('Validation.Success([[[a],[b],[c,d],[m,a,r,c,o]],row=0;col=15;rest=1])'); });
it('cherry-picking elements separated by separators', () => { const substringsWithCommas = many(many1(anyOf(lowercases)).discardSecond(pchar(','))); expect(substringsWithCommas.run('a,b,cd,1').toString()) .to.be.eql('Validation.Success([[[a],[b],[c,d]],row=0;col=7;rest=1])'); });
it('can parse a char many times and return an array', () => { const zeroOrMoreParser = many(pchar('m')); let parsing = zeroOrMoreParser.run(text('mmmarco')); expect(parsing.isSuccess).to.be.true; expect(parsing.toString()).to.be.eql('Validation.Success([[m,m,m],row=0;col=3;rest=arco])'); });
it('can parse a char zero times', () => { const zeroOrMoreParser = many(pchar('m')); let parsing = zeroOrMoreParser.run(text('arco')); expect(parsing.isSuccess).to.be.true; expect(parsing.toString()).to.be.eql('Validation.Success([[],row=0;col=0;rest=arco])'); });
const numberP1 = manyChars1(digitP).fmap(parseFloat).setLabel('manyChars1(digitP).fmap(parseFloat)'); console.log('\n05_recursive_grammar.js'); logToScreen('42', numberP1); // parse("42", number) |> IO.inspect // # >> {:ok, 42} logToScreen('1834798542234', numberP1); // # We also need to ignore whitespaces around a number, to do that we will use // # the `surrounded_by` combinator, since the whitespaces are optionals we also // # use the `maybe` combinator // number = while("0123456789", at_least: 1) // |> surrounded_by(bls?) // |> bind(&String.to_integer/1) const numberP2 = between(opt(many(whiteP)), numberP1, opt(many(whiteP))); logToScreen(' 42 ', numberP2); // parse(" 42 ", number) |> IO.inspect // # >> {:ok, 42} // # An expression is a number, an operation between expressions and an expression // # between parentheses. An expression is defined in terms of itself so it's a // # recursive definition. For a recursive definition we need a recursive parser // // # We need to be able to talk about a parser before having completed its // # definition, for this we use the `recursive` combinator // // // def box(%Parser{} = p), do: p // def box(%Regex{} = r), do: re(r)
sepBy1, lowercaseP, uppercaseP, letterP, digitP, whiteP, tapP, logP, pword, } from 'parsers'; // word = while(letters, at_least: 1) const wordP = many1(letterP).fmap(arra => arra.join('')); // separator = lex(",") |> skip const separatorP = between(many(whiteP), pchar(','), many(whiteP)); // terminator = lex("!") |> one_or_more |> skip const terminatorP = many1(pchar('!')); // greetings = sequence_of([word, separator, word, terminator]) const greetingsP = wordP.discardSecond(separatorP).andThen(wordP).discardSecond(terminatorP); console.log('\n01_hello_world.js'); console.log('Using wordP.discardSecond(separatorP).andThen(wordP).discardSecond(terminatorP);'); logToScreen('Hello,World!'); // # >> {:ok, ["Hello", "World"]} logToScreen('Hello, World!'); // # >> {:ok, ["Hello", "World"]}