diff --git a/src/internal/parsers/whitespace.ts b/src/internal/parsers/whitespace.ts new file mode 100644 index 0000000..24065b4 --- /dev/null +++ b/src/internal/parsers/whitespace.ts @@ -0,0 +1,16 @@ +import { Parser } from '../state' + +import { regexp } from './regexp' + +const WHITESPACE_REQUIRED_RE = /\s+/g +const WHITESPACE_OPTIONAL_RE = /\s*/g + +export function whitespace(): Parser { + return regexp(WHITESPACE_REQUIRED_RE, 'whitespace') +} + +export function whitespaceOptional(): Parser { + return regexp(WHITESPACE_OPTIONAL_RE, 'optional whitespace') +} + +export { whitespace as ws, whitespaceOptional as wsOpt } diff --git a/src/parsers.ts b/src/parsers.ts index 80cb690..aeb067c 100644 --- a/src/parsers.ts +++ b/src/parsers.ts @@ -5,3 +5,4 @@ export * from './internal/parsers/lazy' export * from './internal/parsers/nothing' export * from './internal/parsers/regexp' export * from './internal/parsers/string' +export * from './internal/parsers/whitespace' diff --git a/tests/internal/parsers/whitespace.spec.ts b/tests/internal/parsers/whitespace.spec.ts new file mode 100644 index 0000000..f423723 --- /dev/null +++ b/tests/internal/parsers/whitespace.spec.ts @@ -0,0 +1,82 @@ +import { whitespace, whitespaceOptional } from '@lib/internal/parsers/whitespace' +import { sequence } from '@lib/internal/combinators/sequence' +import { string } from '@lib/internal/parsers/string' +import { regexp } from '@lib/internal/parsers/regexp' + +import { run, result, should } from '@tests/@helpers' + +describe(whitespace, () => { + it('should succeed if given a string of spaces', () => { + const space = ' ' + + const actual = run(whitespace(), space.repeat(4)) + const expected = result('success', space.repeat(4)) + + should.matchState(actual, expected) + }) + + it('should succeed if given a string starting with spaces', () => { + const space = ' ' + + const actual = run(whitespace(), space.repeat(4) + 'const') + const expected = result('success', space.repeat(4)) + + should.matchState(actual, expected) + }) + + it('should succeed if given a mixed string with spaces', () => { + const identifier = regexp(/\w+/g, 'identifier') + const keyword = string('let') + const ws = whitespace() + + const actual = run(sequence(keyword, ws, identifier), 'let identity') + const expected = result('success', ['let', ' ', 'identity']) + + should.matchState(actual, expected) + }) + + it('should fail if given a non-matching input', () => { + const actual = run(sequence(string('let'), whitespace(), string('rec')), 'letrec') + const expected = result('failure', 'whitespace') + + should.matchState(actual, expected) + }) +}) + +describe(whitespaceOptional, () => { + it('should succeed if given a mixed string with spaces', () => { + const letKeyword = string('let') + const identifier = regexp(/\w+/g, 'identifier') + const wsOptional = whitespaceOptional() + + const parser = sequence(letKeyword, wsOptional, identifier) + + const actual = run(parser, 'let identity') + const expected = result('success', ['let', ' ', 'identity']) + + should.matchState(actual, expected) + }) + + it('should succeed if given a mixed string with optional spaces', () => { + const letKeyword = string('let') + const equalKeyword = string('=') + const identifier = regexp(/\w+/g, 'identifier') + const wsOptional = whitespaceOptional() + const wsRequired = whitespace() + + const parser = sequence( + letKeyword, + wsRequired, + identifier, + wsOptional, + equalKeyword, + wsOptional, + identifier + ) + + const actual = run(parser, 'let identity=something') + const expected = result('success', ['let', ' ', 'identity', '', '=', '', 'something']) + + should.matchState(actual, expected) + }) +}) diff --git a/tests/parsers.spec.ts b/tests/parsers.spec.ts index d03921c..f211547 100644 --- a/tests/parsers.spec.ts +++ b/tests/parsers.spec.ts @@ -17,6 +17,10 @@ it('should expose parsers', () => { 'string', 'str', 'uniString', - 'ustr' + 'ustr', + 'whitespace', + 'ws', + 'whitespaceOptional', + 'wsOpt' ) })