From db497bb9950648c056c079c591b239ad4932b5c9 Mon Sep 17 00:00:00 2001 From: Akshay Nair Date: Mon, 24 Jan 2022 19:19:15 +0530 Subject: feat(parser): implements min-max quantifier parser --- src/eval/index.ts | 4 +--- src/parser/index.ts | 21 ++++++++++++++++++++- src/types.ts | 3 +++ tests/parser.spec.ts | 39 +++++++++++++++++++++++++++++++++++++-- 4 files changed, 61 insertions(+), 6 deletions(-) diff --git a/src/eval/index.ts b/src/eval/index.ts index 8e162b4..ed757e7 100644 --- a/src/eval/index.ts +++ b/src/eval/index.ts @@ -1,7 +1,7 @@ import { identity, pipe } from 'fp-ts/function' import { filter, takeLeftWhile, zip, zipWith } from 'fp-ts/Array' import * as Option from 'fp-ts/Option' -import { Expr, ListExpr } from '../types' +import { index, Expr, ListExpr } from '../types' import { match } from '../utils' export interface MatchGroupIndexed { @@ -18,8 +18,6 @@ const group = (value: T, index: number): MatchGroupIndexed => ({ index, }) -type index = number - const indexed = (ls: T[]): Array<[number, T]> => ls.map((x, i) => [i, x]) const accumulateSkip = () => { diff --git a/src/parser/index.ts b/src/parser/index.ts index 2db91a4..a336b83 100644 --- a/src/parser/index.ts +++ b/src/parser/index.ts @@ -23,7 +23,7 @@ import { tuple3, whitespaces0, } from './utils' -import { Expr, ListExpr, Literal } from '../types' +import { Expr, index, ListExpr, Literal } from '../types' import * as Option from 'fp-ts/Option' import { snd } from 'fp-ts/Tuple' @@ -36,6 +36,13 @@ const anyBool = mapTo(symbol('\\b'), _ => Expr.AnyBool()) const truthy = mapTo(symbol('\\T'), _ => Expr.Truthy()) const falsey = mapTo(symbol('\\F'), _ => Expr.Falsey()) +const parseQuantifier = (s: Option.Option, def: index): index => + pipe( + s, + Option.map(s => parseInt(s, 10)), + Option.getOrElse(() => def), + ) + const wrapQuantifiers: (e: ParserResult) => ParserResult = Either.chain(([expr, input]) => pipe( @@ -44,6 +51,18 @@ const wrapQuantifiers: (e: ParserResult) => ParserResult = mapTo(symbol('*'), _ => Expr.ZeroOrMore({ expr })), mapTo(symbol('+'), _ => Expr.OneOrMore({ expr })), mapTo(symbol('?'), _ => Expr.Optional({ expr })), + mapTo( + pair( + prefixed(symbol('{'), optional(digits)), + delimited(symbol(','), optional(digits), symbol('}')), + ), + ([min, max]) => + Expr.MinMax({ + expr, + min: parseQuantifier(min, 0), + max: parseQuantifier(max, Infinity), + }), + ), ]), Either.orElse(_ => Either.right([expr, input])), ), diff --git a/src/types.ts b/src/types.ts index 473f5e1..dd40ce9 100644 --- a/src/types.ts +++ b/src/types.ts @@ -3,6 +3,8 @@ import { constructors, Union } from './utils' type _ = never +export type index = number + export type Literal = Union<{ String: string Number: number @@ -15,6 +17,7 @@ export type Expr = Union<{ End: _ Optional: { expr: Expr } OneOrMore: { expr: Expr } + MinMax: { expr: Expr, min: index, max: index } ZeroOrMore: { expr: Expr } AnyItem: _ Or: { exprs: Expr[] } diff --git a/tests/parser.spec.ts b/tests/parser.spec.ts index 6033040..13a8838 100644 --- a/tests/parser.spec.ts +++ b/tests/parser.spec.ts @@ -14,7 +14,7 @@ import { import { parser } from '../src/parser' import { none, some } from 'fp-ts/Option' import { Expr, Literal } from '../src/types' -import {jlog} from '../src/utils' +import { jlog } from '../src/utils' const wrap = (e: Expr) => right([[none, e, none], '']) const grouped = (l: Expr[]) => wrap(Expr.Group({ exprs: l })) @@ -77,6 +77,37 @@ describe('Parser', () => { '', ]), ) + + expect(parser(/ \s([age \n]{ 2, 5 }) /.source)).toEqual( + grouped([ + Expr.AnyString(), + Expr.MinMax({ + expr: Expr.PropertyMatch({ name: 'age', expr: Expr.AnyNumber() }), + min: 2, + max: 5, + }), + ]), + ) + expect(parser(/ \s([age \n]{ 2, }) /.source)).toEqual( + grouped([ + Expr.AnyString(), + Expr.MinMax({ + expr: Expr.PropertyMatch({ name: 'age', expr: Expr.AnyNumber() }), + min: 2, + max: Infinity, + }), + ]), + ) + expect(parser(/ \s([age \n]{ , 20 }) /.source)).toEqual( + grouped([ + Expr.AnyString(), + Expr.MinMax({ + expr: Expr.PropertyMatch({ name: 'age', expr: Expr.AnyNumber() }), + min: 0, + max: 20, + }), + ]), + ) }) it('should or expressions', () => { @@ -152,7 +183,11 @@ describe('Parser', () => { wrap(Expr.Literal(Literal.String('foobar'))), ) expect(parser(/ "\nwowksadj\n\t wjksdlsd'' !@#%^(&^%$) " /.source)).toEqual( - wrap(Expr.Literal(Literal.String('\\nwowksadj\\n\\t wjksdlsd\'\' !@#%^(&^%$) '))), + wrap( + Expr.Literal( + Literal.String("\\nwowksadj\\n\\t wjksdlsd'' !@#%^(&^%$) "), + ), + ), ) }) -- cgit v1.3.1