aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAkshay Nair <phenax5@gmail.com>2022-01-10 21:29:42 +0530
committerAkshay Nair <phenax5@gmail.com>2022-01-10 21:29:42 +0530
commit3581e398a2fe2f38bee7e6d13cbf609221f61672 (patch)
tree95715115303ecb9620cbf37530e89a37c0ff5c4b
parentaa006086f69c827d780e6e0e69947ca7ee6e2c70 (diff)
downloadelxr-3581e398a2fe2f38bee7e6d13cbf609221f61672.tar.gz
elxr-3581e398a2fe2f38bee7e6d13cbf609221f61672.zip
feat: adds matchAll function (incomplete)
-rw-r--r--README.md2
-rw-r--r--TODO.md10
-rw-r--r--src/eval/index.ts130
-rw-r--r--src/index.ts34
-rw-r--r--tests/basic.spec.ts13
-rw-r--r--tests/eval.spec.ts24
6 files changed, 193 insertions, 20 deletions
diff --git a/README.md b/README.md
index f595163..bc3b297 100644
--- a/README.md
+++ b/README.md
@@ -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 -
diff --git a/TODO.md b/TODO.md
index e1523ba..5760fde 100644
--- a/TODO.md
+++ b/TODO.md
@@ -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])