parent
e58d56a656
commit
7dfb1635c2
@ -1,7 +1,7 @@
|
|||||||
import * as CM from './'
|
import * as CM from './'
|
||||||
|
|
||||||
export const pattern =
|
export const pattern =
|
||||||
'foo !"\\\\ \\"" name:|(wonderwoman batman) hasCape? age:32'
|
'foo !"\\\\ \\"" name:|(wonderwoman batman) hasCape? age:32 chi*go'
|
||||||
|
|
||||||
export const ast = new CM.And([
|
export const ast = new CM.And([
|
||||||
new CM.String('foo'),
|
new CM.String('foo'),
|
||||||
@ -12,4 +12,5 @@ export const ast = new CM.And([
|
|||||||
),
|
),
|
||||||
new CM.TruthyProperty('hasCape'),
|
new CM.TruthyProperty('hasCape'),
|
||||||
new CM.Property('age', new CM.Number(32)),
|
new CM.Property('age', new CM.Number(32)),
|
||||||
|
new CM.GlobPattern('chi*go'),
|
||||||
])
|
])
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { isPlainObject, some } from 'lodash'
|
import { escapeRegExp, isPlainObject, some } from 'lodash'
|
||||||
|
|
||||||
// ===================================================================
|
// ===================================================================
|
||||||
|
|
||||||
@ -178,6 +178,48 @@ const formatString = value =>
|
|||||||
: `"${value.replace(/\\|"/g, escapeChar)}"`
|
: `"${value.replace(/\\|"/g, escapeChar)}"`
|
||||||
: `"${value}"`
|
: `"${value}"`
|
||||||
|
|
||||||
|
export class GlobPattern extends Node {
|
||||||
|
constructor (value) {
|
||||||
|
// fallback to string node if no wildcard
|
||||||
|
if (value.indexOf('*') === -1) {
|
||||||
|
return new StringNode(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
super()
|
||||||
|
|
||||||
|
this.value = value
|
||||||
|
|
||||||
|
// should not be enumerable for the tests
|
||||||
|
Object.defineProperty(this, 'match', {
|
||||||
|
value: this.match.bind(
|
||||||
|
this,
|
||||||
|
new RegExp(
|
||||||
|
value
|
||||||
|
.split('*')
|
||||||
|
.map(escapeRegExp)
|
||||||
|
.join('.*'),
|
||||||
|
'i'
|
||||||
|
)
|
||||||
|
),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
match (re, value) {
|
||||||
|
if (typeof value === 'string') {
|
||||||
|
return re.test(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(value) || isPlainObject(value)) {
|
||||||
|
return some(value, this.match)
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
toString () {
|
||||||
|
return this.value
|
||||||
|
}
|
||||||
|
}
|
||||||
export class StringNode extends Node {
|
export class StringNode extends Node {
|
||||||
constructor (value) {
|
constructor (value) {
|
||||||
super()
|
super()
|
||||||
@ -389,6 +431,17 @@ const parser = P.grammar({
|
|||||||
P.seq(r.ws, r.term.repeat(), P.eof).map(
|
P.seq(r.ws, r.term.repeat(), P.eof).map(
|
||||||
([, terms]) => (terms.length === 0 ? new Null() : new And(terms))
|
([, terms]) => (terms.length === 0 ? new Null() : new And(terms))
|
||||||
),
|
),
|
||||||
|
globPattern: new P((input, pos, end) => {
|
||||||
|
let value = ''
|
||||||
|
let c
|
||||||
|
while (pos < end && ((c = input[pos]) === '*' || c in RAW_STRING_CHARS)) {
|
||||||
|
++pos
|
||||||
|
value += c
|
||||||
|
}
|
||||||
|
return value.length === 0
|
||||||
|
? new Failure(pos, 'a raw string')
|
||||||
|
: new Success(pos, value)
|
||||||
|
}),
|
||||||
quotedString: new P((input, pos, end) => {
|
quotedString: new P((input, pos, end) => {
|
||||||
if (input[pos] !== '"') {
|
if (input[pos] !== '"') {
|
||||||
return new Failure(pos, '"')
|
return new Failure(pos, '"')
|
||||||
@ -406,6 +459,7 @@ const parser = P.grammar({
|
|||||||
|
|
||||||
return new Success(pos, value.join(''))
|
return new Success(pos, value.join(''))
|
||||||
}),
|
}),
|
||||||
|
property: r => P.alt(r.quotedString, r.rawString),
|
||||||
rawString: new P((input, pos, end) => {
|
rawString: new P((input, pos, end) => {
|
||||||
let value = ''
|
let value = ''
|
||||||
let c
|
let c
|
||||||
@ -417,7 +471,6 @@ const parser = P.grammar({
|
|||||||
? new Failure(pos, 'a raw string')
|
? new Failure(pos, 'a raw string')
|
||||||
: new Success(pos, value)
|
: new Success(pos, value)
|
||||||
}),
|
}),
|
||||||
string: r => P.alt(r.quotedString, r.rawString),
|
|
||||||
term: r =>
|
term: r =>
|
||||||
P.alt(
|
P.alt(
|
||||||
P.seq(P.text('('), r.ws, r.term.repeat(1), P.text(')')).map(
|
P.seq(P.text('('), r.ws, r.term.repeat(1), P.text(')')).map(
|
||||||
@ -439,20 +492,22 @@ const parser = P.grammar({
|
|||||||
}
|
}
|
||||||
return new Comparison(op, val)
|
return new Comparison(op, val)
|
||||||
}),
|
}),
|
||||||
P.seq(r.string, r.ws, P.text(':'), r.ws, r.term).map(
|
P.seq(r.property, r.ws, P.text(':'), r.ws, r.term).map(
|
||||||
_ => new Property(_[0], _[4])
|
_ => new Property(_[0], _[4])
|
||||||
),
|
),
|
||||||
P.seq(r.string, P.text('?')).map(_ => new TruthyProperty(_[0])),
|
P.seq(r.property, P.text('?')).map(_ => new TruthyProperty(_[0])),
|
||||||
P.alt(
|
r.value
|
||||||
r.quotedString.map(_ => new StringNode(_)),
|
|
||||||
r.rawString.map(str => {
|
|
||||||
const asNum = +str
|
|
||||||
return Number.isNaN(asNum)
|
|
||||||
? new StringNode(str)
|
|
||||||
: new NumberNode(asNum)
|
|
||||||
})
|
|
||||||
)
|
|
||||||
).skip(r.ws),
|
).skip(r.ws),
|
||||||
|
value: r =>
|
||||||
|
P.alt(
|
||||||
|
r.quotedString.map(_ => new StringNode(_)),
|
||||||
|
r.globPattern.map(str => {
|
||||||
|
const asNum = +str
|
||||||
|
return Number.isNaN(asNum)
|
||||||
|
? new GlobPattern(str)
|
||||||
|
: new NumberNode(asNum)
|
||||||
|
})
|
||||||
|
),
|
||||||
ws: P.regex(/\s*/),
|
ws: P.regex(/\s*/),
|
||||||
}).default
|
}).default
|
||||||
export const parse = parser.parse.bind(parser)
|
export const parse = parser.parse.bind(parser)
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
import { ast, pattern } from './index.fixtures'
|
import { ast, pattern } from './index.fixtures'
|
||||||
import {
|
import {
|
||||||
getPropertyClausesStrings,
|
getPropertyClausesStrings,
|
||||||
|
GlobPattern,
|
||||||
Null,
|
Null,
|
||||||
NumberNode,
|
NumberNode,
|
||||||
parse,
|
parse,
|
||||||
@ -41,6 +42,12 @@ describe('parse', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('GlobPattern', () => {
|
||||||
|
it('matches a glob pattern recursively', () => {
|
||||||
|
expect(new GlobPattern('b*r').match({ foo: 'bar' })).toBe(true)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe('Number', () => {
|
describe('Number', () => {
|
||||||
it('match a number recursively', () => {
|
it('match a number recursively', () => {
|
||||||
expect(new NumberNode(3).match([{ foo: 3 }])).toBe(true)
|
expect(new NumberNode(3).match([{ foo: 3 }])).toBe(true)
|
||||||
@ -82,5 +89,5 @@ describe('setPropertyClause', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('toString', () => {
|
it('toString', () => {
|
||||||
expect(pattern).toBe(ast.toString())
|
expect(ast.toString()).toBe(pattern)
|
||||||
})
|
})
|
||||||
|
Loading…
Reference in New Issue
Block a user