diff options
| -rw-r--r-- | README.md | 2 | ||||
| -rw-r--r-- | TODO.md | 10 | ||||
| -rw-r--r-- | src/eval/index.ts | 130 | ||||
| -rw-r--r-- | src/index.ts | 34 | ||||
| -rw-r--r-- | tests/basic.spec.ts | 13 | ||||
| -rw-r--r-- | tests/eval.spec.ts | 24 |
6 files changed, 193 insertions, 20 deletions
@@ -1,6 +1,6 @@ # ListExp [ WIP ] -Regular expression-like syntax for list operations +Regular expression-like syntax for list operations. You didn't ask for this so here it is. Example - @@ -5,11 +5,17 @@ - [ ] Eval: Next item - [ ] Parse: `{2, 5}` quantifiers - [ ] Eval: `^$` start-end - - [ ] Parse, Eval: Not/Inverse - - [X] Func: filter + - [ ] Func: filter - [ ] Func: match - [ ] Func: replace + - [ ] Not/Inverse + - [ ] Named capture groups + - [ ] Non-capture groups ## Issues - [ ] . should not match for empty list +## Refactor + - [ ] Remove default null for constructors + - [ ] Create group inside PropertyMatch and send single `expr` + diff --git a/src/eval/index.ts b/src/eval/index.ts index 6656743..1050295 100644 --- a/src/eval/index.ts +++ b/src/eval/index.ts @@ -1,8 +1,136 @@ import { pipe } from 'fp-ts/function' import { takeLeftWhile } from 'fp-ts/lib/Array' +import { + chain, + getOrElseW, + isSome, + map, + none, + Option, + some, +} from 'fp-ts/lib/Option' import { Expr, ListExpr } from '../types' import { match } from '../utils' +export interface MatchGroupIndexed<T = any> { + value: T + index: number +} + +export interface MatchGroupResult { + groups: MatchGroupIndexed[] +} + +const group = <T>(value: T, index: number): MatchGroupIndexed<T> => ({ + value, + index, +}) + +// :: ListExpr -> [a] -> [{ groups: [T] }] +export const matchAll = <T>( + [startO, exprs, endO]: ListExpr, + list: T[], +): MatchGroupResult => { + const check = ( + index: number, + ls: T[], + expr: Expr, + ): Option<MatchGroupIndexed[]> => { + if (ls.length === 0) return some([]) + + const [item, ...rest] = ls + + const next = + (i: number = 1) => + (cur: Option<MatchGroupIndexed[]>) => + pipe( + check(index + i, ls.slice(i), expr), + getOrElseW(() => [] as MatchGroupIndexed[]), + nextMatch => + pipe( + cur, + map(curMatch => [...curMatch, ...nextMatch]), + ), + ) + + return pipe( + expr, + match<Option<MatchGroupIndexed<any>[]>, Expr>({ + AnyItem: _ => pipe(some([group(item, index)]), next()), + AnyNumber: _ => + pipe( + typeof item === 'number' ? some([group(item, index)]) : none, + next(), + ), + AnyString: _ => + pipe( + typeof item === 'string' ? some([group(item, index)]) : none, + next(), + ), + AnyBool: _ => + pipe( + typeof item === 'boolean' ? some([group(item, index)]) : none, + next(), + ), + Truthy: _ => pipe(!!item ? some([group(item, index)]) : none, next()), + Falsey: _ => pipe(!item ? some([group(item, index)]) : none, next()), + + Group: ({ exprs }) => { + const matches = exprs.reduce( + (acc, exp) => + pipe( + acc, + chain(m => + pipe( + check(index, ls, exp), + map(ac => [...ac, ...m]), + ), + ), + ), + some([] as MatchGroupIndexed<any>[]), + ) + // console.log(matches, exprs, '---', item) + + return next()(matches) + }, + + PropertyMatch: ({ name, exprs }) => + pipe( + Object.prototype.hasOwnProperty.call(item, name) + ? check(index, [item[name]], Expr.Group({ exprs })) + : none, + next(), + ), + + OneOrMore: ({ expr }) => { + //console.log(item) + // TODO: Nested quantified expression + const matches = pipe( + ls, + takeLeftWhile(a => isSome(check(index, [a], expr))), + ) + //console.log(matches) + return pipe( + matches.length > 0 ? some([group(matches, index)]) : none, + next(matches.length || 1), + ) + }, + + _: _ => none, + }), + ) + } + + const expr = Expr.Group({ exprs }) + + return { + groups: pipe( + check(0, list, expr), + getOrElseW(() => []), + ), + } +} + export const find = <T>([startO, exprs, endO]: ListExpr, list: T[]): any => { const check = (expr: Expr) => @@ -25,7 +153,7 @@ export const find = <T>([startO, exprs, endO]: ListExpr, list: T[]): any => { list.slice(i), takeLeftWhile(x => check(expr)(x, i, list)), ) - console.log(x) + //console.log(x) return true }, diff --git a/src/index.ts b/src/index.ts index 691d676..db6630c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,17 +1,29 @@ -import { fold, map } from 'fp-ts/lib/Either' -import { identity, pipe } from 'fp-ts/lib/function' -import { find } from './eval' +import { getOrElseW, map } from 'fp-ts/lib/Either' +import { flow, pipe } from 'fp-ts/lib/function' +import {fst} from 'fp-ts/lib/Tuple' +import * as ev from './eval' import { parser } from './parser' +import {ListExpr} from './types' const toSourceString = (r: string | RegExp): string => typeof r === 'string' ? r : r.source -export const filter = <T>(listExp: string | RegExp, list: T[]): T[] => +export const liexp: (r: string | RegExp) => ListExpr = flow( + toSourceString, + parser, + map(fst), + getOrElseW(([e, _]) => { throw new Error(e) }) +) + +export const matchAll = <T>(exp: string | RegExp, list: T[]) => + pipe( + exp, + liexp, + lxp => ev.matchAll(lxp, list), + ) + +export const filter = <T>(exp: string | RegExp, list: T[]): T[] => pipe( - listExp, - toSourceString, - parser, - map(([lxp, _]) => find(lxp, list)), - fold(([err, _]) => { - throw new Error(err) - }, identity), + exp, + liexp, + lxp => ev.find(lxp, list), ) diff --git a/tests/basic.spec.ts b/tests/basic.spec.ts index d964053..db2e9a3 100644 --- a/tests/basic.spec.ts +++ b/tests/basic.spec.ts @@ -1,4 +1,5 @@ -import { filter } from '../src' +import { jlog } from '../src/utils' +import { filter, matchAll } from '../src' describe('Basic tests', () => { it('should do it', () => { @@ -9,4 +10,14 @@ describe('Basic tests', () => { expect(filter(/\F\T/, [1, 0, '4', ''])).toEqual([]) expect(filter(/\s\T/, [1, 0, '4', ''])).toEqual(['4']) }) + + fit('should do it', () => { + // jlog(matchAll(/[age \n]+/, [ {}, { age: 1 }, { age: 2 }, { age: 0 }, '' ])) + // jlog(matchAll(/[age \n]+/, [ {}, { age: 1 }, '' ])) + // jlog(matchAll(/([age \T][age \n])+/, [ {}, { age: 1 }, { age: 2 }, { age: 0 }, '' ])) + + jlog(matchAll(/\n/, [ 'b', 1, 'a' ])) + // jlog(matchAll(/\n\T/, [1, 0, 2, '4', ''])) + // jlog(matchAll(/\n+/, [ '', 1, 2, 0, '', 5, '' ])) + }) }) diff --git a/tests/eval.spec.ts b/tests/eval.spec.ts index 8c79375..ca06033 100644 --- a/tests/eval.spec.ts +++ b/tests/eval.spec.ts @@ -2,9 +2,27 @@ import { left, right } from 'fp-ts/Either' import { none, some } from 'fp-ts/Option' import { Expr, ListExpr } from '../src/types' import { jlog } from '../src/utils' -import { find } from '../src/eval' +import { find, matchAll } from '../src/eval' describe('Eval', () => { + it('should do stuff', () => { + const ls = [-1, 1, 2, '3', 4, '5', 6, 7, ''] + + const liexp: ListExpr = [ + none, + [ + Expr.OneOrMore({ + expr: Expr.Group({ + exprs: [Expr.AnyNumber(null), Expr.Truthy(null)], + }), + }), + ], + none, + ] + + //jlog(matchAll(liexp, ls)) + }) + it('basic evaluation', () => { const list = [0, 1, '2', 3, [4], 5] @@ -66,9 +84,7 @@ describe('Eval', () => { const liexp: ListExpr = [ none, - [ - Expr.OneOrMore({ expr: Expr.AnyNumber(null) }), - ], + [Expr.OneOrMore({ expr: Expr.AnyNumber(null) })], none, ] expect(find(liexp, list)).toEqual([1, 3, 5]) |
