Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions doc/parsec/range.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Parser Combinator: str

`range('min','max')` consumes a token, if the `text`'s length is 1 and the codepoint of `text` is between that of `min` (included) and `max` (included). It fails if it doesn't match the conditions.

For example, for passing TypeScript's export statement:

```typescript
3.142
```

We could write

```typescript
seq(range('1', '9'), str('.'), str(`142`))
```
11 changes: 11 additions & 0 deletions packages/ts-parsec/src/Parsers/ParserInterface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,3 +88,14 @@ export function unableToConsumeToken<TKind>(token: Token<TKind> | undefined): Pa
message: `Unable to consume token: ${token === undefined ? '<END-OF-FILE>' : token.text}`
};
}

export function rangeInvalid<TKind>(
min : string,
max: string,
token: Token<TKind> | undefined): ParseError {
return {
kind: 'Error',
pos: token === undefined ? undefined : token.pos,
message: `the range for \`range\` is not valid: min:${min}, max:${max} `
};
}
37 changes: 36 additions & 1 deletion packages/ts-parsec/src/Parsers/TokenParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// Licensed under the MIT license.

import { Token } from '../Lexer';
import { FailedParser, FailedParserOutput, Parser, ParserOutput, unableToConsumeToken } from './ParserInterface';
import { FailedParser, FailedParserOutput, Parser, ParserOutput, rangeInvalid, unableToConsumeToken } from './ParserInterface';

export function nil<T>(): Parser<T, undefined> {
return {
Expand Down Expand Up @@ -73,6 +73,41 @@ export function str<T>(toMatch: string): Parser<T, Token<T>> {
};
}

export function range<T>(min: string, max: string): Parser<T, Token<T>> {
return {
parse(token: Token<T> | undefined): ParserOutput<T, Token<T>> {
const rangeUnsuitable = (max.length !== 1)
|| (min.length !== 1)
|| (min.charCodeAt(0) > max.charCodeAt(0));
if (rangeUnsuitable) {
return {
successful: false,
error: rangeInvalid(min, max, token)
};
}
if (token === undefined
|| token.text.length !== 1
|| token.text.charCodeAt(0) < min.charCodeAt(0)
|| token.text.charCodeAt(0) > max.charCodeAt(0)
) {
return {
successful: false,
error: unableToConsumeToken(token)
};
}
return {
candidates: [{
firstToken: token,
nextToken: token.next,
result: token
}],
successful: true,
error: undefined
};
}
};
}

export function tok<T>(toMatch: T): Parser<T, Token<T>> {
return {
parse(token: Token<T> | undefined): ParserOutput<T, Token<T>> {
Expand Down
21 changes: 20 additions & 1 deletion packages/tspc-test/src/TestParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import * as assert from 'assert';
import * as parsec from 'typescript-parsec';
import { buildLexer, Token } from 'typescript-parsec';
import { alt, alt_sc, apply, errd, kleft, kmid, kright, opt, opt_sc, rep, rep_n, rep_sc, repr, seq, str, tok } from 'typescript-parsec';
import { alt, alt_sc, apply, errd, kleft, kmid, kright, opt, opt_sc, range, rep, rep_n, rep_sc, repr, seq, str, tok } from 'typescript-parsec';

function notUndefined<T>(t: T | undefined): T {
assert.notStrictEqual(t, undefined);
Expand Down Expand Up @@ -65,6 +65,25 @@ test(`Parser: tok`, () => {
}
});

test(`Parser: range`, () => {
const firstToken = notUndefined(lexer.parse(`3,142`));
{
const result = succeeded(range('1', '9').parse(firstToken));
assert.strictEqual(result.length, 1);
assert.strictEqual(result[0].result.text, '3');
assert.strictEqual(result[0].firstToken, firstToken);
assert.strictEqual(result[0].nextToken, firstToken.next);
}
{
const result = range('9', '1').parse(firstToken);
assert.strictEqual(result.successful, false);
}
{
const result = range('a', 'z').parse(firstToken);
assert.strictEqual(result.successful, false);
}
});

test(`Parser: alt`, () => {
{
const firstToken = notUndefined(lexer.parse(`123,456`));
Expand Down