@malloydata/malloy-filter 0.0.237-dev250221201621

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/README.md +81 -0
  2. package/SAMPLES.md +381 -0
  3. package/SERIALIZE_SAMPLES.md +300 -0
  4. package/dist/a_simple_parser.d.ts +1 -0
  5. package/dist/a_simple_parser.js +20 -0
  6. package/dist/a_simple_parser.js.map +1 -0
  7. package/dist/a_simple_serializer.d.ts +1 -0
  8. package/dist/a_simple_serializer.js +31 -0
  9. package/dist/a_simple_serializer.js.map +1 -0
  10. package/dist/base_parser.d.ts +13 -0
  11. package/dist/base_parser.js +33 -0
  12. package/dist/base_parser.js.map +1 -0
  13. package/dist/base_serializer.d.ts +6 -0
  14. package/dist/base_serializer.js +11 -0
  15. package/dist/base_serializer.js.map +1 -0
  16. package/dist/boolean_parser.d.ts +7 -0
  17. package/dist/boolean_parser.js +59 -0
  18. package/dist/boolean_parser.js.map +1 -0
  19. package/dist/boolean_serializer.d.ts +8 -0
  20. package/dist/boolean_serializer.js +31 -0
  21. package/dist/boolean_serializer.js.map +1 -0
  22. package/dist/clause_types.d.ts +70 -0
  23. package/dist/clause_types.js +3 -0
  24. package/dist/clause_types.js.map +1 -0
  25. package/dist/date_parser.d.ts +22 -0
  26. package/dist/date_parser.js +315 -0
  27. package/dist/date_parser.js.map +1 -0
  28. package/dist/date_serializer.d.ts +10 -0
  29. package/dist/date_serializer.js +100 -0
  30. package/dist/date_serializer.js.map +1 -0
  31. package/dist/filter_parser.d.ts +12 -0
  32. package/dist/filter_parser.js +66 -0
  33. package/dist/filter_parser.js.map +1 -0
  34. package/dist/filter_serializer.d.ts +13 -0
  35. package/dist/filter_serializer.js +43 -0
  36. package/dist/filter_serializer.js.map +1 -0
  37. package/dist/filter_types.d.ts +10 -0
  38. package/dist/filter_types.js +3 -0
  39. package/dist/filter_types.js.map +1 -0
  40. package/dist/generate_samples.d.ts +1 -0
  41. package/dist/generate_samples.js +344 -0
  42. package/dist/generate_samples.js.map +1 -0
  43. package/dist/number_parser.d.ts +20 -0
  44. package/dist/number_parser.js +275 -0
  45. package/dist/number_parser.js.map +1 -0
  46. package/dist/number_serializer.d.ts +11 -0
  47. package/dist/number_serializer.js +76 -0
  48. package/dist/number_serializer.js.map +1 -0
  49. package/dist/string_parser.d.ts +18 -0
  50. package/dist/string_parser.js +198 -0
  51. package/dist/string_parser.js.map +1 -0
  52. package/dist/string_serializer.d.ts +11 -0
  53. package/dist/string_serializer.js +77 -0
  54. package/dist/string_serializer.js.map +1 -0
  55. package/dist/token_types.d.ts +7 -0
  56. package/dist/token_types.js +3 -0
  57. package/dist/token_types.js.map +1 -0
  58. package/dist/tokenizer.d.ts +52 -0
  59. package/dist/tokenizer.js +263 -0
  60. package/dist/tokenizer.js.map +1 -0
  61. package/dist/tokenizer.spec.d.ts +1 -0
  62. package/dist/tokenizer.spec.js +255 -0
  63. package/dist/tokenizer.spec.js.map +1 -0
  64. package/jest.config.js +3 -0
  65. package/package.json +21 -0
  66. package/src/DEVELOPING.md +26 -0
  67. package/src/a_simple_parser.ts +22 -0
  68. package/src/a_simple_serializer.ts +40 -0
  69. package/src/base_parser.ts +45 -0
  70. package/src/base_serializer.ts +9 -0
  71. package/src/boolean_parser.ts +60 -0
  72. package/src/boolean_serializer.ts +32 -0
  73. package/src/clause_types.ts +160 -0
  74. package/src/date_parser.ts +413 -0
  75. package/src/date_serializer.ts +114 -0
  76. package/src/filter_parser.ts +68 -0
  77. package/src/filter_serializer.ts +49 -0
  78. package/src/filter_types.ts +12 -0
  79. package/src/generate_samples.ts +387 -0
  80. package/src/number_parser.ts +308 -0
  81. package/src/number_serializer.ts +96 -0
  82. package/src/string_parser.ts +193 -0
  83. package/src/string_serializer.ts +87 -0
  84. package/src/token_types.ts +7 -0
  85. package/src/tokenizer.spec.ts +273 -0
  86. package/src/tokenizer.ts +320 -0
  87. package/tsconfig.json +14 -0
@@ -0,0 +1,40 @@
1
+ import {FilterSerializer} from './filter_serializer';
2
+ import {
3
+ NumberClause,
4
+ StringCondition,
5
+ BooleanClause,
6
+ DateClause,
7
+ } from './clause_types';
8
+
9
+ /* eslint-disable no-console */
10
+ function aSimpleSerializer() {
11
+ const strings: StringCondition[] = [{operator: '=', values: ['CAT', 'DOG']}];
12
+ let response = new FilterSerializer(strings, 'string').serialize();
13
+ console.log(...strings, '\n', response.result, '\n');
14
+
15
+ const numbers: NumberClause[] = [
16
+ {
17
+ operator: 'range',
18
+ startOperator: '>=',
19
+ startValue: -5.5,
20
+ endOperator: '<',
21
+ endValue: 10,
22
+ },
23
+ ];
24
+ response = new FilterSerializer(numbers, 'number').serialize();
25
+ console.log(...numbers, '\n', response.result, '\n');
26
+
27
+ const booleans: BooleanClause[] = [{operator: 'NULL'}, {operator: 'TRUE'}];
28
+
29
+ response = new FilterSerializer(booleans, 'boolean').serialize();
30
+ console.log(...booleans, '\n', response.result, '\n');
31
+
32
+ const dates: DateClause[] = [
33
+ {operator: 'YESTERDAY'},
34
+ {operator: 'NEXT', unit: 'TUESDAY'},
35
+ ];
36
+ response = new FilterSerializer(dates, 'date').serialize();
37
+ console.log(...dates, '\n', response.result, '\n');
38
+ }
39
+
40
+ aSimpleSerializer();
@@ -0,0 +1,45 @@
1
+ import {Token} from './token_types';
2
+ import {FilterParserResponse} from './filter_types';
3
+
4
+ export abstract class BaseParser {
5
+ protected inputString: string;
6
+ protected index: number;
7
+ protected tokens: Token[];
8
+
9
+ constructor(inputString: string) {
10
+ this.index = 0;
11
+ this.tokens = [];
12
+ this.inputString = inputString;
13
+ }
14
+
15
+ public getTokens(): Token[] {
16
+ return this.tokens;
17
+ }
18
+
19
+ public abstract parse(): FilterParserResponse;
20
+
21
+ protected getAt(index: number): Token {
22
+ return this.tokens[index];
23
+ }
24
+
25
+ protected getNext(): Token {
26
+ return this.getAt(this.index);
27
+ }
28
+
29
+ protected static matchTokenTypes(
30
+ candidates: string[],
31
+ index: number,
32
+ tokens: Token[]
33
+ ): boolean {
34
+ const maxIndex = index + candidates.length;
35
+ if (index < 0 || maxIndex > tokens.length) {
36
+ return false;
37
+ }
38
+ for (let i = 0; i < candidates.length; i++) {
39
+ if (candidates[i] !== tokens[i + index].type) {
40
+ return false;
41
+ }
42
+ }
43
+ return true;
44
+ }
45
+ }
@@ -0,0 +1,9 @@
1
+ import {Clause} from './clause_types';
2
+
3
+ export abstract class BaseSerializer {
4
+ constructor(protected clauses: Clause[]) {
5
+ this.clauses = clauses;
6
+ }
7
+
8
+ public abstract serialize(): string;
9
+ }
@@ -0,0 +1,60 @@
1
+ import {SpecialToken, Tokenizer, TokenizerParams} from './tokenizer';
2
+ import {BooleanClause, BooleanOperator} from './clause_types';
3
+ import {BaseParser} from './base_parser';
4
+ import {FilterParserResponse, FilterError} from './filter_types';
5
+
6
+ export class BooleanParser extends BaseParser {
7
+ constructor(input: string) {
8
+ super(input);
9
+ }
10
+
11
+ private tokenize(): void {
12
+ const specialSubstrings: SpecialToken[] = [{type: ',', value: ','}];
13
+ const specialWords: SpecialToken[] = [
14
+ {type: 'NULL', value: 'null', ignoreCase: true},
15
+ {type: 'NOTNULL', value: '-null', ignoreCase: true},
16
+ {type: 'TRUE', value: 'true', ignoreCase: true},
17
+ {type: 'FALSE', value: 'false', ignoreCase: true},
18
+ ];
19
+ const params: TokenizerParams = {
20
+ trimWordWhitespace: true,
21
+ splitOnWhitespace: true,
22
+ specialSubstrings,
23
+ specialWords: specialWords,
24
+ };
25
+
26
+ const tokenizer = new Tokenizer(this.inputString, params);
27
+ this.tokens = tokenizer.parse();
28
+ this.tokens = Tokenizer.convertSpecialWords(this.tokens, specialWords);
29
+ }
30
+
31
+ public parse(): FilterParserResponse {
32
+ this.index = 0;
33
+ this.tokenize();
34
+ const clauses: BooleanClause[] = [];
35
+ const errors: FilterError[] = [];
36
+ while (this.index < this.tokens.length) {
37
+ const token = this.getNext();
38
+ if (token.type === ',') {
39
+ this.index++;
40
+ } else if (
41
+ token.type === 'NULL' ||
42
+ token.type === 'TRUE' ||
43
+ token.type === 'FALSE' ||
44
+ token.type === 'NOTNULL'
45
+ ) {
46
+ const clause: BooleanClause = {operator: token.type as BooleanOperator};
47
+ clauses.push(clause);
48
+ this.index++;
49
+ } else {
50
+ errors.push({
51
+ message: 'Invalid token ' + token.value,
52
+ startIndex: token.startIndex,
53
+ endIndex: token.endIndex,
54
+ });
55
+ this.index++;
56
+ }
57
+ }
58
+ return {clauses, errors};
59
+ }
60
+ }
@@ -0,0 +1,32 @@
1
+ import {BooleanClause, Clause} from './clause_types';
2
+ import {BaseSerializer} from './base_serializer';
3
+
4
+ export class BooleanSerializer extends BaseSerializer {
5
+ constructor(clauses: Clause[]) {
6
+ super(clauses);
7
+ }
8
+
9
+ public serialize(): string {
10
+ const result = BooleanSerializer.clauseToString(this.clauses);
11
+ return result.trim().replace(/,$/, '');
12
+ }
13
+
14
+ private static booleanClauseToString(clause: BooleanClause): string {
15
+ return clause.operator === 'NOTNULL' ? '-NULL' : clause.operator;
16
+ }
17
+
18
+ private static clauseToString(clauses: Clause[]): string {
19
+ let result = '';
20
+ for (const clause of clauses) {
21
+ if ('operator' in clause) {
22
+ result += BooleanSerializer.booleanClauseToString(
23
+ clause as BooleanClause
24
+ );
25
+ } else {
26
+ throw new Error('Invalid boolean clause ' + JSON.stringify(clause));
27
+ }
28
+ result += ', ';
29
+ }
30
+ return result;
31
+ }
32
+ }
@@ -0,0 +1,160 @@
1
+ export type NumberOperator = '<=' | '>=' | '!=' | '=' | '>' | '<';
2
+
3
+ export type NumberValue = number | null;
4
+
5
+ export interface NumberCondition {
6
+ operator: NumberOperator;
7
+ values: NumberValue[];
8
+ }
9
+
10
+ export type NumberRangeOperator = '<=' | '>=' | '>' | '<';
11
+
12
+ export interface NumberRange {
13
+ operator: 'range';
14
+ startOperator: NumberRangeOperator;
15
+ startValue: NumberValue;
16
+ endOperator: NumberRangeOperator;
17
+ endValue: NumberValue;
18
+ }
19
+
20
+ export type QuoteType =
21
+ | 'SINGLE'
22
+ | 'DOUBLE'
23
+ | 'BACKTICK'
24
+ | 'TRIPLESINGLE'
25
+ | 'TRIPLEDOUBLE'
26
+ | 'ESCAPEDSINGLE'
27
+ | 'ESCAPEDDOUBLE'
28
+ | 'ESCAPEDBACKTICK';
29
+
30
+ export type StringOperator =
31
+ | 'EMPTY'
32
+ | 'NOTEMPTY'
33
+ | 'starts'
34
+ | 'ends'
35
+ | 'contains'
36
+ | 'notStarts'
37
+ | 'notEnds'
38
+ | 'notContains'
39
+ | '~'
40
+ | '='
41
+ | '!~'
42
+ | '!=';
43
+
44
+ export type StringValue = string | null;
45
+
46
+ export interface StringCondition {
47
+ operator: StringOperator;
48
+ values: StringValue[];
49
+ quotes?: QuoteType[]; // List of quote types found in the string.
50
+ }
51
+
52
+ export type BooleanOperator = 'TRUE' | 'FALSE' | 'NULL' | 'NOTNULL';
53
+
54
+ export interface BooleanClause {
55
+ operator: BooleanOperator;
56
+ }
57
+
58
+ export type DatePrefix = 'BEFORE' | 'AFTER';
59
+
60
+ export type DateTimeUnit =
61
+ | 'YEAR'
62
+ | 'QUARTER'
63
+ | 'MONTH'
64
+ | 'WEEK'
65
+ | 'DAY'
66
+ | 'HOUR'
67
+ | 'MINUTE'
68
+ | 'SECOND';
69
+
70
+ export type DateWeekday =
71
+ | 'MONDAY'
72
+ | 'TUESDAY'
73
+ | 'WEDNESDAY'
74
+ | 'THURSDAY'
75
+ | 'FRIDAY'
76
+ | 'SATURDAY'
77
+ | 'SUNDAY';
78
+
79
+ export type DateMomentNowOperator = 'NOW' | 'TODAY' | 'YESTERDAY' | 'TOMORROW';
80
+
81
+ // now, after today, before yesterday, tomorrow
82
+ export interface DateMomentNow {
83
+ prefix?: DatePrefix;
84
+ operator: DateMomentNowOperator;
85
+ }
86
+
87
+ export type DateMomentIntervalOperator = 'LAST' | 'THIS' | 'NEXT';
88
+
89
+ // LAST|UNITOFTIME, LAST|DAYOFWEEK
90
+ // THIS|UNITOFTIME
91
+ // NEXT|UNITOFTIME, NEXT|DAYOFWEEK
92
+ // before last month, after next tuesday, this month
93
+ export interface DateMomentInterval {
94
+ prefix?: DatePrefix;
95
+ operator: DateMomentIntervalOperator;
96
+ unit: DateTimeUnit | DateWeekday;
97
+ }
98
+
99
+ export type DateMomentNumberIntervalOperator =
100
+ | 'LASTN'
101
+ | 'NEXTN'
102
+ | 'AGO'
103
+ | 'FROMNOW';
104
+
105
+ // LAST|NUMBER|UNITOFTIME
106
+ // NEXT|NUMBER|UNITOFTIME
107
+ // NUMBER|UNITOFTIME|AGO
108
+ // NUMBER|UNITOFTIME|FROM|NOW
109
+ // before 3 hours ago, next 5 days, 2025 seconds ago, after 6 weeks from now
110
+ export interface DateMomentNumberInterval {
111
+ prefix?: DatePrefix;
112
+ operator: DateMomentNumberIntervalOperator;
113
+ unit: DateTimeUnit;
114
+ value: string;
115
+ }
116
+
117
+ export type DateMomentNumberUnitOperator = 'TIMEBLOCK';
118
+
119
+ // NUMBER|UNITOFTIME
120
+ // 2025 seconds, after 32 hours
121
+ export interface DateMomentNumberUnit {
122
+ prefix?: DatePrefix;
123
+ operator: DateMomentNumberUnitOperator;
124
+ unit: DateTimeUnit;
125
+ value: string;
126
+ }
127
+
128
+ export type DateMomentNumberOperator = 'DATE' | 'DATETIME';
129
+
130
+ // DATE|TIME, DATE
131
+ // after 2025, before 2025-08-04, 2025-08-04 08:11:52
132
+ export interface DateMomentNumber {
133
+ prefix?: DatePrefix;
134
+ operator: DateMomentNumberOperator;
135
+ date: string;
136
+ time?: string;
137
+ }
138
+
139
+ export type DateMoment =
140
+ | DateMomentNow
141
+ | DateMomentInterval
142
+ | DateMomentNumberInterval
143
+ | DateMomentNumberUnit
144
+ | DateMomentNumber;
145
+
146
+ export interface DateRange {
147
+ start: DateMoment;
148
+ operator: 'TO' | 'FOR';
149
+ end: DateMoment;
150
+ }
151
+
152
+ export type NumberClause = NumberCondition | NumberRange;
153
+
154
+ export type DateClause = DateMoment | DateRange;
155
+
156
+ export type Clause =
157
+ | NumberClause
158
+ | StringCondition
159
+ | BooleanClause
160
+ | DateClause;