ether-code 0.7.8 → 0.7.9

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.
@@ -0,0 +1,622 @@
1
+ const { EtherParserBase, TokenType } = require('./ether-parser-base')
2
+
3
+ class EtherParserSQL extends EtherParserBase {
4
+ constructor(options = {}) {
5
+ super(options)
6
+ this.loadI18n(['i18n-sql.json'])
7
+ }
8
+
9
+ parse(source) {
10
+ this.tokenize(source)
11
+ return this.parseSQLProgram()
12
+ }
13
+
14
+ parseSQLProgram() {
15
+ const program = {
16
+ type: 'SQLProgram',
17
+ statements: []
18
+ }
19
+
20
+ this.skipNewlines()
21
+
22
+ while (!this.isAtEnd()) {
23
+ const statement = this.parseSQLStatement()
24
+ if (statement) {
25
+ program.statements.push(statement)
26
+ }
27
+ this.skipNewlines()
28
+ }
29
+
30
+ return program
31
+ }
32
+
33
+ parseSQLStatement() {
34
+ this.skipNewlines()
35
+ const token = this.current()
36
+
37
+ if (!token || token.type === TokenType.EOF) return null
38
+
39
+ const value = token.value ? token.value.toLowerCase() : ''
40
+
41
+ if (value === 'selectionner' || value === 'select' || value === 'choisir') {
42
+ return this.parseSQLSelect()
43
+ }
44
+
45
+ if (value === 'inserer' || value === 'insert' || value === 'ajouter') {
46
+ return this.parseSQLInsert()
47
+ }
48
+
49
+ if (value === 'mettre' || value === 'update' || value === 'modifier') {
50
+ return this.parseSQLUpdate()
51
+ }
52
+
53
+ if (value === 'supprimer' || value === 'delete' || value === 'effacer') {
54
+ return this.parseSQLDelete()
55
+ }
56
+
57
+ if (value === 'creer' || value === 'create') {
58
+ return this.parseSQLCreate()
59
+ }
60
+
61
+ if (value === 'detruire' || value === 'drop') {
62
+ return this.parseSQLDrop()
63
+ }
64
+
65
+ if (value === 'modifier' || value === 'alter') {
66
+ return this.parseSQLAlter()
67
+ }
68
+
69
+ this.advance()
70
+ return null
71
+ }
72
+
73
+ parseSQLSelect() {
74
+ this.advance()
75
+
76
+ const columns = []
77
+
78
+ while (!this.isAtEnd()) {
79
+ const token = this.current()
80
+
81
+ if (!token || token.type === TokenType.EOF) break
82
+
83
+ const value = token.value ? token.value.toLowerCase() : ''
84
+ if (value === 'depuis' || value === 'de' || value === 'from') break
85
+
86
+ if (token.type === TokenType.STAR) {
87
+ columns.push('*')
88
+ this.advance()
89
+ } else if (token.type === TokenType.IDENTIFIER) {
90
+ columns.push(token.value)
91
+ this.advance()
92
+ } else if (token.type === TokenType.COMMA) {
93
+ this.advance()
94
+ } else {
95
+ break
96
+ }
97
+ }
98
+
99
+ let table = null
100
+ if (this.matchValue('depuis') || this.matchValue('de') || this.matchValue('from')) {
101
+ const tableToken = this.current()
102
+ if (tableToken && tableToken.type === TokenType.IDENTIFIER) {
103
+ table = tableToken.value
104
+ this.advance()
105
+ }
106
+ }
107
+
108
+ let where = null
109
+ if (this.matchValue('ou') || this.matchValue('where')) {
110
+ where = this.parseSQLWhere()
111
+ }
112
+
113
+ let orderBy = null
114
+ if (this.matchValue('trier') || this.matchValue('ordonner') || this.matchValue('order')) {
115
+ if (this.matchValue('par') || this.matchValue('by')) {
116
+ orderBy = this.parseSQLOrderBy()
117
+ }
118
+ }
119
+
120
+ let limit = null
121
+ if (this.matchValue('limiter') || this.matchValue('limit')) {
122
+ const limitToken = this.current()
123
+ if (limitToken && (limitToken.type === TokenType.NUMBER || limitToken.type === TokenType.INTEGER)) {
124
+ limit = parseInt(limitToken.value)
125
+ this.advance()
126
+ }
127
+ }
128
+
129
+ return {
130
+ type: 'SelectStatement',
131
+ columns: columns,
132
+ table: table,
133
+ where: where,
134
+ orderBy: orderBy,
135
+ limit: limit
136
+ }
137
+ }
138
+
139
+ parseSQLWhere() {
140
+ const conditions = []
141
+
142
+ while (!this.isAtEnd()) {
143
+ const token = this.current()
144
+
145
+ if (!token || token.type === TokenType.EOF) break
146
+
147
+ const value = token.value ? token.value.toLowerCase() : ''
148
+ if (value === 'trier' || value === 'ordonner' || value === 'order' ||
149
+ value === 'limiter' || value === 'limit' || value === 'grouper') break
150
+
151
+ if (token.type === TokenType.IDENTIFIER) {
152
+ const field = token.value
153
+ this.advance()
154
+
155
+ const opToken = this.current()
156
+ let operator = '='
157
+
158
+ if (opToken) {
159
+ if (opToken.type === TokenType.EQUALS || opToken.type === TokenType.DOUBLE_EQUALS) {
160
+ operator = '='
161
+ this.advance()
162
+ } else if (opToken.type === TokenType.NOT_EQUALS) {
163
+ operator = '<>'
164
+ this.advance()
165
+ } else if (opToken.type === TokenType.LT) {
166
+ operator = '<'
167
+ this.advance()
168
+ } else if (opToken.type === TokenType.GT) {
169
+ operator = '>'
170
+ this.advance()
171
+ } else if (opToken.type === TokenType.LTE) {
172
+ operator = '<='
173
+ this.advance()
174
+ } else if (opToken.type === TokenType.GTE) {
175
+ operator = '>='
176
+ this.advance()
177
+ } else if (opToken.value && opToken.value.toLowerCase() === 'egal') {
178
+ operator = '='
179
+ this.advance()
180
+ }
181
+ }
182
+
183
+ const valueToken = this.current()
184
+ let condValue = null
185
+
186
+ if (valueToken) {
187
+ if (valueToken.type === TokenType.STRING) {
188
+ condValue = valueToken.value
189
+ } else if (valueToken.type === TokenType.NUMBER || valueToken.type === TokenType.INTEGER) {
190
+ condValue = parseFloat(valueToken.value)
191
+ } else if (valueToken.type === TokenType.IDENTIFIER) {
192
+ condValue = valueToken.value
193
+ }
194
+ this.advance()
195
+ }
196
+
197
+ conditions.push({ field, operator, value: condValue })
198
+ } else if (value === 'et' || value === 'and') {
199
+ conditions.push({ type: 'AND' })
200
+ this.advance()
201
+ } else if (value === 'ou' || value === 'or') {
202
+ conditions.push({ type: 'OR' })
203
+ this.advance()
204
+ } else {
205
+ this.advance()
206
+ }
207
+ }
208
+
209
+ return conditions
210
+ }
211
+
212
+ parseSQLOrderBy() {
213
+ const orders = []
214
+
215
+ while (!this.isAtEnd()) {
216
+ const token = this.current()
217
+
218
+ if (!token || token.type === TokenType.EOF) break
219
+
220
+ const value = token.value ? token.value.toLowerCase() : ''
221
+ if (value === 'limiter' || value === 'limit' || value === 'grouper') break
222
+
223
+ if (token.type === TokenType.IDENTIFIER) {
224
+ const field = token.value
225
+ this.advance()
226
+
227
+ let direction = 'ASC'
228
+ const dirToken = this.current()
229
+
230
+ if (dirToken) {
231
+ const dirValue = dirToken.value ? dirToken.value.toLowerCase() : ''
232
+ if (dirValue === 'desc' || dirValue === 'descendant' || dirValue === 'decroissant') {
233
+ direction = 'DESC'
234
+ this.advance()
235
+ } else if (dirValue === 'asc' || dirValue === 'ascendant' || dirValue === 'croissant') {
236
+ direction = 'ASC'
237
+ this.advance()
238
+ }
239
+ }
240
+
241
+ orders.push({ field, direction })
242
+ } else if (token.type === TokenType.COMMA) {
243
+ this.advance()
244
+ } else {
245
+ break
246
+ }
247
+ }
248
+
249
+ return orders
250
+ }
251
+
252
+ parseSQLInsert() {
253
+ this.advance()
254
+
255
+ this.matchValue('dans') || this.matchValue('into') || this.matchValue('in')
256
+
257
+ const tableToken = this.current()
258
+ let table = null
259
+ if (tableToken && tableToken.type === TokenType.IDENTIFIER) {
260
+ table = tableToken.value
261
+ this.advance()
262
+ }
263
+
264
+ const columns = []
265
+ if (this.match(TokenType.LPAREN)) {
266
+ while (!this.isAtEnd() && !this.match(TokenType.RPAREN)) {
267
+ const colToken = this.current()
268
+ if (colToken && colToken.type === TokenType.IDENTIFIER) {
269
+ columns.push(colToken.value)
270
+ this.advance()
271
+ }
272
+ this.match(TokenType.COMMA)
273
+ }
274
+ }
275
+
276
+ const values = []
277
+ if (this.matchValue('valeurs') || this.matchValue('values')) {
278
+ if (this.match(TokenType.LPAREN)) {
279
+ while (!this.isAtEnd() && !this.match(TokenType.RPAREN)) {
280
+ const valToken = this.current()
281
+ if (valToken) {
282
+ if (valToken.type === TokenType.STRING) {
283
+ values.push(valToken.value)
284
+ } else if (valToken.type === TokenType.NUMBER || valToken.type === TokenType.INTEGER) {
285
+ values.push(parseFloat(valToken.value))
286
+ } else if (valToken.type === TokenType.IDENTIFIER) {
287
+ const v = valToken.value.toLowerCase()
288
+ if (v === 'vrai' || v === 'true') values.push(true)
289
+ else if (v === 'faux' || v === 'false') values.push(false)
290
+ else if (v === 'nul' || v === 'null') values.push(null)
291
+ else values.push(valToken.value)
292
+ }
293
+ this.advance()
294
+ }
295
+ this.match(TokenType.COMMA)
296
+ }
297
+ }
298
+ }
299
+
300
+ return {
301
+ type: 'InsertStatement',
302
+ table: table,
303
+ columns: columns,
304
+ values: values
305
+ }
306
+ }
307
+
308
+ parseSQLUpdate() {
309
+ this.advance()
310
+
311
+ this.matchValue('a jour') || this.matchValue('jour')
312
+
313
+ const tableToken = this.current()
314
+ let table = null
315
+ if (tableToken && tableToken.type === TokenType.IDENTIFIER) {
316
+ table = tableToken.value
317
+ this.advance()
318
+ }
319
+
320
+ const assignments = []
321
+ if (this.matchValue('definir') || this.matchValue('set') || this.matchValue('avec')) {
322
+ while (!this.isAtEnd()) {
323
+ const colToken = this.current()
324
+
325
+ if (!colToken || colToken.type === TokenType.EOF) break
326
+
327
+ const value = colToken.value ? colToken.value.toLowerCase() : ''
328
+ if (value === 'ou' || value === 'where') break
329
+
330
+ if (colToken.type === TokenType.IDENTIFIER) {
331
+ const column = colToken.value
332
+ this.advance()
333
+
334
+ if (this.match(TokenType.EQUALS) || this.match(TokenType.COLON)) {
335
+ const valToken = this.current()
336
+ let val = null
337
+
338
+ if (valToken) {
339
+ if (valToken.type === TokenType.STRING) {
340
+ val = valToken.value
341
+ } else if (valToken.type === TokenType.NUMBER || valToken.type === TokenType.INTEGER) {
342
+ val = parseFloat(valToken.value)
343
+ } else if (valToken.type === TokenType.IDENTIFIER) {
344
+ val = valToken.value
345
+ }
346
+ this.advance()
347
+ }
348
+
349
+ assignments.push({ column, value: val })
350
+ }
351
+ }
352
+
353
+ this.match(TokenType.COMMA)
354
+ }
355
+ }
356
+
357
+ let where = null
358
+ if (this.matchValue('ou') || this.matchValue('where')) {
359
+ where = this.parseSQLWhere()
360
+ }
361
+
362
+ return {
363
+ type: 'UpdateStatement',
364
+ table: table,
365
+ assignments: assignments,
366
+ where: where
367
+ }
368
+ }
369
+
370
+ parseSQLDelete() {
371
+ this.advance()
372
+
373
+ this.matchValue('de') || this.matchValue('depuis') || this.matchValue('from')
374
+
375
+ const tableToken = this.current()
376
+ let table = null
377
+ if (tableToken && tableToken.type === TokenType.IDENTIFIER) {
378
+ table = tableToken.value
379
+ this.advance()
380
+ }
381
+
382
+ let where = null
383
+ if (this.matchValue('ou') || this.matchValue('where')) {
384
+ where = this.parseSQLWhere()
385
+ }
386
+
387
+ return {
388
+ type: 'DeleteStatement',
389
+ table: table,
390
+ where: where
391
+ }
392
+ }
393
+
394
+ parseSQLCreate() {
395
+ this.advance()
396
+
397
+ if (this.matchValue('table')) {
398
+ return this.parseSQLCreateTable()
399
+ }
400
+
401
+ if (this.matchValue('base') || this.matchValue('database')) {
402
+ this.matchValue('de')
403
+ this.matchValue('donnees')
404
+ return this.parseSQLCreateDatabase()
405
+ }
406
+
407
+ if (this.matchValue('index')) {
408
+ return this.parseSQLCreateIndex()
409
+ }
410
+
411
+ return null
412
+ }
413
+
414
+ parseSQLCreateTable() {
415
+ const nameToken = this.current()
416
+ let name = null
417
+ if (nameToken && nameToken.type === TokenType.IDENTIFIER) {
418
+ name = nameToken.value
419
+ this.advance()
420
+ }
421
+
422
+ const columns = []
423
+
424
+ this.skipNewlines()
425
+ if (this.match(TokenType.INDENT)) {
426
+ while (!this.isAtEnd()) {
427
+ const current = this.current()
428
+
429
+ if (current && current.type === TokenType.DEDENT) {
430
+ this.advance()
431
+ break
432
+ }
433
+
434
+ if (current && current.type === TokenType.NEWLINE) {
435
+ this.advance()
436
+ continue
437
+ }
438
+
439
+ const col = this.parseSQLColumnDef()
440
+ if (col) {
441
+ columns.push(col)
442
+ }
443
+ }
444
+ }
445
+
446
+ return {
447
+ type: 'CreateTableStatement',
448
+ name: name,
449
+ columns: columns
450
+ }
451
+ }
452
+
453
+ parseSQLColumnDef() {
454
+ const nameToken = this.current()
455
+ if (!nameToken || nameToken.type !== TokenType.IDENTIFIER) return null
456
+
457
+ const name = nameToken.value
458
+ this.advance()
459
+
460
+ let dataType = 'TEXT'
461
+ const typeToken = this.current()
462
+ if (typeToken && typeToken.type === TokenType.IDENTIFIER) {
463
+ const typeMap = {
464
+ 'entier': 'INTEGER',
465
+ 'texte': 'TEXT',
466
+ 'reel': 'REAL',
467
+ 'booleen': 'BOOLEAN',
468
+ 'date': 'DATE',
469
+ 'horodatage': 'TIMESTAMP'
470
+ }
471
+ dataType = typeMap[typeToken.value.toLowerCase()] || typeToken.value.toUpperCase()
472
+ this.advance()
473
+ }
474
+
475
+ const constraints = []
476
+ while (!this.isAtEnd()) {
477
+ const token = this.current()
478
+ if (!token || token.type === TokenType.NEWLINE || token.type === TokenType.COMMA || token.type === TokenType.DEDENT) break
479
+
480
+ const val = token.value ? token.value.toLowerCase() : ''
481
+
482
+ if (val === 'cle' || val === 'primaire' || val === 'primary') {
483
+ constraints.push('PRIMARY KEY')
484
+ this.advance()
485
+ this.matchValue('primaire') || this.matchValue('key')
486
+ } else if (val === 'unique') {
487
+ constraints.push('UNIQUE')
488
+ this.advance()
489
+ } else if (val === 'non' || val === 'not') {
490
+ this.advance()
491
+ if (this.matchValue('nul') || this.matchValue('null')) {
492
+ constraints.push('NOT NULL')
493
+ }
494
+ } else if (val === 'auto') {
495
+ constraints.push('AUTOINCREMENT')
496
+ this.advance()
497
+ this.matchValue('increment')
498
+ } else {
499
+ break
500
+ }
501
+ }
502
+
503
+ return {
504
+ name: name,
505
+ type: dataType,
506
+ constraints: constraints
507
+ }
508
+ }
509
+
510
+ parseSQLCreateDatabase() {
511
+ const nameToken = this.current()
512
+ let name = null
513
+ if (nameToken && nameToken.type === TokenType.IDENTIFIER) {
514
+ name = nameToken.value
515
+ this.advance()
516
+ }
517
+
518
+ return {
519
+ type: 'CreateDatabaseStatement',
520
+ name: name
521
+ }
522
+ }
523
+
524
+ parseSQLCreateIndex() {
525
+ const nameToken = this.current()
526
+ let name = null
527
+ if (nameToken && nameToken.type === TokenType.IDENTIFIER) {
528
+ name = nameToken.value
529
+ this.advance()
530
+ }
531
+
532
+ this.matchValue('sur') || this.matchValue('on')
533
+
534
+ const tableToken = this.current()
535
+ let table = null
536
+ if (tableToken && tableToken.type === TokenType.IDENTIFIER) {
537
+ table = tableToken.value
538
+ this.advance()
539
+ }
540
+
541
+ const columns = []
542
+ if (this.match(TokenType.LPAREN)) {
543
+ while (!this.isAtEnd() && !this.match(TokenType.RPAREN)) {
544
+ const colToken = this.current()
545
+ if (colToken && colToken.type === TokenType.IDENTIFIER) {
546
+ columns.push(colToken.value)
547
+ this.advance()
548
+ }
549
+ this.match(TokenType.COMMA)
550
+ }
551
+ }
552
+
553
+ return {
554
+ type: 'CreateIndexStatement',
555
+ name: name,
556
+ table: table,
557
+ columns: columns
558
+ }
559
+ }
560
+
561
+ parseSQLDrop() {
562
+ this.advance()
563
+
564
+ if (this.matchValue('table')) {
565
+ const nameToken = this.current()
566
+ let name = null
567
+ if (nameToken && nameToken.type === TokenType.IDENTIFIER) {
568
+ name = nameToken.value
569
+ this.advance()
570
+ }
571
+ return { type: 'DropTableStatement', name: name }
572
+ }
573
+
574
+ return null
575
+ }
576
+
577
+ parseSQLAlter() {
578
+ this.advance()
579
+
580
+ if (this.matchValue('table')) {
581
+ const nameToken = this.current()
582
+ let name = null
583
+ if (nameToken && nameToken.type === TokenType.IDENTIFIER) {
584
+ name = nameToken.value
585
+ this.advance()
586
+ }
587
+
588
+ const operations = []
589
+
590
+ while (!this.isAtEnd()) {
591
+ if (this.matchValue('ajouter') || this.matchValue('add')) {
592
+ this.matchValue('colonne') || this.matchValue('column')
593
+ const col = this.parseSQLColumnDef()
594
+ if (col) operations.push({ type: 'ADD', column: col })
595
+ } else if (this.matchValue('supprimer') || this.matchValue('drop')) {
596
+ this.matchValue('colonne') || this.matchValue('column')
597
+ const colToken = this.current()
598
+ if (colToken && colToken.type === TokenType.IDENTIFIER) {
599
+ operations.push({ type: 'DROP', column: colToken.value })
600
+ this.advance()
601
+ }
602
+ } else {
603
+ break
604
+ }
605
+ }
606
+
607
+ return { type: 'AlterTableStatement', name: name, operations: operations }
608
+ }
609
+
610
+ return null
611
+ }
612
+
613
+ parseStatement(lang) {
614
+ return this.parseSQLStatement()
615
+ }
616
+
617
+ parseExpression(lang) {
618
+ return null
619
+ }
620
+ }
621
+
622
+ module.exports = { EtherParserSQL }