Skip to content

Commit

Permalink
Implement charRange and notCharRange
Browse files Browse the repository at this point in the history
  • Loading branch information
hlysine committed Aug 20, 2023
1 parent 8b7b207 commit 264c7b7
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 0 deletions.
35 changes: 35 additions & 0 deletions src/expression.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
assign,
bindAsIncomplete,
captureName,
compareCodePoint,
escapeForCharClass,
flagString,
getLiteralString,
Expand Down Expand Up @@ -324,6 +325,36 @@ class RegExpBuilder implements RegExpToken {
return this.not.charIn;
}

public get charRange(): RegExpToken['charRange'] {
function func(this: RegExpBuilder, ...args: RegExpLiteral | [start: string, end: string]) {
const continuation = (start: string) =>
function func2(this: RegExpBuilder, ...args2: RegExpLiteral): RegExpToken {
if (isLiteralArgument(args2)) {
const end = getLiteralString(args2, false);
if (compareCodePoint(start, end) > 0) throw new Error(`Invalid charRange: ${start} > ${end}`);
return this.charIn`${start === '\\' ? '\\\\' : start}-${end}`;
} else {
throw new Error('Invalid second argument for charRange');
}
};
if (isLiteralArgument(args)) {
const literal = getLiteralString(args, false);
return bindAsIncomplete(continuation(literal), this, 'charRange');
} else if (args.length === 2 && typeof args[0] === 'string' && typeof args[1] === 'string') {
const [start, end] = args;
if (compareCodePoint(start, end) > 0) throw new Error(`Invalid charRange: ${start} > ${end}`);
return this.charIn`${start === '\\' ? '\\\\' : start}-${end}`;
} else {
throw new Error('Invalid arguments for charRange');
}
}
return bindAsIncomplete(func, this, 'charRange') as RegExpToken['charRange'];
}

public get notCharRange(): RegExpToken['notCharRange'] {
return this.not.charRange;
}

public get not(): RegExpToken['not'] {
function func(this: RegExpBuilder, token: RegExpToken): RegExpToken {
if (RegExpBuilder.isRegExpBuilder(token)) {
Expand Down Expand Up @@ -1286,6 +1317,10 @@ export const charIn = r.charIn;
*/
export const notCharIn = r.notCharIn;

export const charRange = r.charRange;

export const notCharRange = r.notCharRange;

/**
* Negate a given token, causing it to match anything other than the token itself.
*
Expand Down
18 changes: 18 additions & 0 deletions src/helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -214,3 +214,21 @@ export function countTail(str: string, char: string): number {
}
return count;
}

export function compareCodePoint(char1: string, char2: string): number {
// evaluate the string to unescape it
if (char1.length > 1 && char1.startsWith('\\')) {
char1 = new Function(`return "${char1.replace('"', '\\"')}"`)();
}
if (char2.length > 1 && char2.startsWith('\\')) {
char2 = new Function(`return "${char2.replace('"', '\\"')}"`)();
}
// spread the string to check for code-point length
if ([...char1].length > 1) {
throw new Error(`The string ${char1} is not a single character.`);
}
if ([...char2].length > 1) {
throw new Error(`The string ${char2} is not a single character.`);
}
return char1.codePointAt(0)! - char2.codePointAt(0)!;
}
15 changes: 15 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,17 @@ export interface CharClassFunction {
(...args: (string | RegExpToken)[]): RegExpToken & CharClassFunction;
}

export interface CharRangeFunction {
(template: TemplateStringsArray, ...args: unknown[]): HalfCharRangeFunction & IncompleteToken;
(start: string, end: string): RegExpToken;
(start: string): HalfCharRangeFunction & IncompleteToken;
}

export interface HalfCharRangeFunction {
(template: TemplateStringsArray, ...args: unknown[]): RegExpToken;
(end: string): RegExpToken;
}

/**
* A token that requires additional parameters.
* Terminal operations are defined as `never` to prevent the token from being used without the required parameters.
Expand Down Expand Up @@ -836,6 +847,10 @@ export interface RegExpToken {
*/
get notCharIn(): CharClassFunction & IncompleteToken;

get charRange(): CharRangeFunction & IncompleteToken;

get notCharRange(): CharRangeFunction & IncompleteToken;

/**
* Negate a given token, causing it to match anything other than the token itself.
*
Expand Down

0 comments on commit 264c7b7

Please sign in to comment.