-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlabelGrammar.ne
119 lines (108 loc) · 5.99 KB
/
labelGrammar.ne
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
@{%
import { Prisma } from '@prisma/client';
import { addDays, addMonths, addYears } from 'date-fns';
import moo from 'moo';
type DateTimeFilter = Prisma.DateTimeFilter;
type TrackWhereInput = Prisma.TrackWhereInput;
const addFuncs = {
d: addDays,
m: addMonths,
y: addYears,
};
type Unit = 'd' | 'm' | 'y';
// Generate a comparison query between an absolute date in a given unit
// makeAbsoluteComparison('=', new Date(2020, 1, 1), 'd') means "dates on the same day as 1/1/2020"
// makeAbsoluteComparison('=', new Date(2020, 1, 1), 'y') means "dates in the same year as 1/1/2020"
// makeAbsoluteComparison('>', new Date(2020, 1, 1), 'y') means "dates after 1/1/2020"
// makeAbsoluteComparison('<=', new Date(2020, 1, 1), 'y') means "dates before or on 1/1/2020"
function makeAbsoluteComparison(operator: string, date: Date, unit: Unit): DateTimeFilter {
// Determine the next day/month/year based on the given unit
const addFunc = addFuncs[unit];
const next = addFunc(date, 1);
if (operator === '=') {
// The model equals the date if it falls between the date and the next day/month/year
return { gte: date, lt: next };
} else if (operator === '<') {
return { lt: date };
} else if (operator === '<=') {
return { lt: next };
} else if (operator === '>') {
return { gte: next };
} else if (operator === '>=') {
return { gte: date };
} else {
throw new Error('Invalid operator');
}
}
// Generate a comparison query a certain distance before now
// makeRelativeComparison('=', 3, 'd') means "dates between 2 and 4 days before now"
// makeRelativeComparison('=', 3, 'm') means "dates between 2 and 4 months before now"
// makeRelativeComparison('>', 3, 'd') means "dates more than 3 days before now"
// makeRelativeComparison('<=', 3, 'y') means "dates 3 or fewer years before now"
function makeRelativeComparison(operator: string, amount: number, unit: Unit): DateTimeFilter {
const addFunc = addFuncs[unit];
const now = new Date();
if (operator === '=') {
return { gt: addFunc(now, -amount), lt: addFunc(now, amount) };
} else if (operator === '<') {
return { gt: addFunc(now, -amount) };
} else if (operator === '<=') {
return { gte: addFunc(now, -amount) };
} else if (operator === '>') {
return { lt: addFunc(now, -amount) };
} else if (operator === '>=') {
return { lte: addFunc(now, -amount) };
} else {
throw new Error('Invalid operator');
}
}
const lexer = moo.compile({
ws: / +/,
number: { match: /[1-9]\d*/, value: (v: string) => parseInt(v, 10) },
quotedString: { match: /\".+?\"/, value: (v: string) => v.slice(1, -1) },
dateUnit: ['d', 'm', 'y'],
cleanKw: 'clean',
explicitKw: 'explicit',
unlabeledKw: 'unlabeled',
addedKw: 'added',
releasedKw: 'released',
nameKw: 'name:',
labelKw: 'label:',
albumKw: 'album:',
artistKw: 'artist:',
dash: '-',
comparison: ['<=', '>=', '<', '>', '='],
not: '!',
and: '&&',
or: '||',
lparen: '(',
rparen: ')',
});
%}
@preprocessor typescript
@lexer lexer
main -> binary {% id %}
relativeDate -> %number %dateUnit {% ([amount, unit]: [{ value: number }, { value: Unit }]) => ({ amount: amount.value, unit: unit.value }) %}
absoluteDate -> %number {% ([year]: [{ value: number }]) => ({ unit: 'y', date: new Date(year.value, 0, 1) }) %}
| %number %dash %number %dash %number {% ([month, _a, day, _b, year]: [{ value: number }, unknown, { value: number }, unknown, { value: number }]) => ({ unit: 'd', date: new Date(year.value, month.value - 1, day.value) }) %}
added -> %addedKw %comparison absoluteDate {% ([_, operator, date]: [unknown, { value: string }, { date: Date, unit: Unit }]): TrackWhereInput => ({ dateAdded: makeAbsoluteComparison(operator.value, date.date, date.unit) }) %}
| %addedKw %comparison relativeDate {% ([_, operator, date]: [unknown, { value: string }, { amount: number, unit: Unit }]): TrackWhereInput => ({ dateAdded: makeRelativeComparison(operator.value, date.amount, date.unit)}) %}
released -> %releasedKw %comparison absoluteDate {% ([_, operator, date]: [unknown, { value: string }, { date: Date, unit: Unit }]): TrackWhereInput => ({ spotifyTrack: { album: { dateReleased: makeAbsoluteComparison(operator.value, date.date, date.unit) } } }) %}
| %releasedKw %comparison relativeDate {% ([_, operator, date]: [unknown, { value: string }, { amount: number, unit: Unit }]): TrackWhereInput => ({ spotifyTrack: { album: { dateReleased: makeRelativeComparison(operator.value, date.amount, date.unit) } } }) %}
value -> %cleanKw {% (_): TrackWhereInput => ({ spotifyTrack: { explicit: false } }) %}
| %explicitKw {% (_): TrackWhereInput => ({ spotifyTrack: { explicit: true } }) %}
| %unlabeledKw {% (_): TrackWhereInput => ({ trackLabels: { none: {} } }) %}
| %nameKw %quotedString {% ([_, name]: [unknown, { value: string }]): TrackWhereInput => ({ spotifyTrack: { name: { contains: name.value, mode: "insensitive" } } }) %}
| %labelKw %quotedString {% ([_, name]: [unknown, { value: string }]): TrackWhereInput => ({ trackLabels: { some: { label: { name: name.value } } } }) %}
| %albumKw %quotedString {% ([_, name]: [unknown, { value: string }]): TrackWhereInput => ({ spotifyTrack: { album: { name: name.value } }}) %}
| %artistKw %quotedString {% ([_, name]: [unknown, { value: string }]): TrackWhereInput => ({ spotifyTrack: { artists: { some: { name: name.value } } } }) %}
| added {% id %}
| released {% id %}
parentheses -> %lparen binary %rparen {% ([_, inner]) => inner %}
| value {% id %}
unary -> %not parentheses {% ([ _, rhs ]: [unknown, TrackWhereInput]): TrackWhereInput => ({ NOT: rhs }) %}
| parentheses {% id %}
binary -> binary %ws %and %ws unary {% ([lhs, _a, _b, _c, rhs]: [TrackWhereInput, unknown, unknown, unknown, TrackWhereInput]): TrackWhereInput => ({ AND: [lhs, rhs] }) %}
| binary %ws %or %ws unary {% ([lhs, _a, _b, _c, rhs]: [TrackWhereInput, unknown, unknown, unknown, TrackWhereInput]): TrackWhereInput => ({ OR: [lhs, rhs] }) %}
| unary {% id %}
ws -> %ws