Skip to content

Commit

Permalink
Added exacty parser per discussions in francisrstokes#46
Browse files Browse the repository at this point in the history
  • Loading branch information
jeffrose committed Aug 14, 2020
1 parent 67d57fe commit 6c1fea3
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 1 deletion.
36 changes: 36 additions & 0 deletions Readme.md
Expand Up @@ -41,6 +41,7 @@ Arcsecond is a zero-dependency, Fantasy Land compliant JavaScript [Parser Combin
- [lookAhead](#lookAhead)
- [sepBy](#sepBy)
- [sepBy1](#sepBy1)
- [exactly](#exactly)
- [many](#many)
- [many1](#many1)
- [between](#between)
Expand Down Expand Up @@ -1002,6 +1003,41 @@ newParser.run('1,2,3')
// }
```
#### exactly
`exactly :: (Integer, Parser e s a) -> Parser e s [a]`
`exactly` takes a positive number and parser, and returns a new parser which matches that parser the given number of times.
**Example**
```JavaScript
const newParser = exactly (4, letter)

newParser.run('abcdef')
// -> {
// isError: false,
// result: [ "a", "b", "c", "d" ],
// index: 4,
// data: null
// }

newParser.run('abc')
// -> {
// isError: true,
// error: 'ParseError (position 0): Expecting 4 letter, but got end of input.',
// index: 0,
// data: null
// }

newParser.run('12345')
// -> {
// isError: true,
// error: 'ParseError (position 0): Expecting 4 letter, got '1'',
// index: 0,
// data: null
// }
```
#### many
`many :: Parser e s a -> Parser e s [a]`
Expand Down
26 changes: 26 additions & 0 deletions index.mjs
Expand Up @@ -9,6 +9,7 @@ const reDigits = /^[0-9]+/;
const reLetter = /[a-zA-Z]/;
const reLetters = /^[a-zA-Z]+/;
const reWhitespaces = /^\s+/;
const reErrorExpectation = /ParseError.+Expecting/;

// createParserState :: x -> s -> ParserState e a s
const createParserState = (target, data = null) => ({
Expand Down Expand Up @@ -291,6 +292,31 @@ export const coroutine = function coroutine(g) {
});
};

// exactly :: (Integer, Parser e s a) -> Parser e s [a]
export const exactly = function exactly(n, parser) {
if (typeof n !== 'number' || n <= 0) {
throw new TypeError (`exactly must be called with a number > 0, but got ${n}`);
}
return new Parser(function exactly$state(state) {
if (state.isError) return state;

const results = [];
let nextState = state;

for (let i = 0; i < n; i++) {
const out = parser.p(nextState);
if(out.isError) {
return out;
} else {
nextState = out;
results.push(nextState.result);
}
}

return updateResult(nextState, results);
}).errorMap((_, index) => `ParseError (position ${index}): Expecting ${n}${_.replace(reErrorExpectation, '')}`);
}

// many :: Parser e s a -> Parser e s [a]
export const many = function many(parser) {
return new Parser(function many$state(state) {
Expand Down
12 changes: 11 additions & 1 deletion test/main.test.js
Expand Up @@ -5,6 +5,7 @@ const {
str,
digit,
fail,
exactly,
many,
many1,
digits,
Expand Down Expand Up @@ -44,7 +45,7 @@ const {
mapData,
endOfInput,
withData
} = require('../index')
} = require('../index');

const f = x => ({ f: x });
const g = x => ({ g: x });
Expand Down Expand Up @@ -267,6 +268,15 @@ testMany(
]
);

testMany(
'exactly', [
expectedSuccessTest(exactly (3, char('*')), '***'.split(''), '***'),
expectedSuccessTest(exactly (4, digit), '1234'.split(''), '1234abc'),
expectedFailTest(exactly (4, digit), 'abc'),
expectedThrowTest(() => exactly ('a', digit), '123abc', `exactly must be called with a number > 0, but got a`)
]
);

testMany(
'many', [
expectedSuccessTest(many (digit), '1234'.split(''), '1234abc'),
Expand Down

0 comments on commit 6c1fea3

Please sign in to comment.