ether-code 0.7.6 → 0.7.7

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.
package/ether-parser.js DELETED
@@ -1,4335 +0,0 @@
1
- const fs = require('fs')
2
- const { EtherLexer, Token, TokenType } = require('./lexer/ether-lexer')
3
-
4
- class EtherParser {
5
- constructor(options = {}) {
6
- this.tokens = []
7
- this.pos = 0
8
- this.currentIndent = 0
9
- this.i18n = {}
10
- this.reverseMaps = {}
11
- this.targetLang = options.targetLang || 'auto'
12
-
13
- this.loadAllI18n(options.i18nDir || './i18n')
14
- this.initCompoundTerms()
15
- }
16
-
17
- initCompoundTerms() {
18
- this.compoundOperators = {
19
- 'strictement egal': '===',
20
- 'strictement égal': '===',
21
- 'strictement different': '!==',
22
- 'strictement différent': '!==',
23
- 'superieur ou egal': '>=',
24
- 'supérieur ou égal': '>=',
25
- 'inferieur ou egal': '<=',
26
- 'inférieur ou égal': '<=',
27
- 'coalescence nulle': '??',
28
- 'chainage optionnel': '?.',
29
- 'chaînage optionnel': '?.'
30
- }
31
-
32
- this.compoundKeywords = {
33
- 'sinon si': 'else if',
34
- 'tant que': 'while',
35
- 'pour chaque': 'forEach',
36
- 'fonction asynchrone': 'async function',
37
- 'fonction async': 'async function',
38
- 'fonction generatrice': 'function*',
39
- 'fonction génératrice': 'function*',
40
- 'variable globale': 'var'
41
- }
42
-
43
- this.jsTranslations = {
44
- 'variable': 'let',
45
- 'constante': 'const',
46
- 'fonction': 'function',
47
- 'retourner': 'return',
48
- 'si': 'if',
49
- 'sinon': 'else',
50
- 'pour': 'for',
51
- 'de': 'of',
52
- 'dans': 'in',
53
- 'selon': 'switch',
54
- 'cas': 'case',
55
- 'defaut': 'default',
56
- 'sortir': 'break',
57
- 'continuer': 'continue',
58
- 'essayer': 'try',
59
- 'attraper': 'catch',
60
- 'finalement': 'finally',
61
- 'lancer': 'throw',
62
- 'nouveau': 'new',
63
- 'supprimer': 'delete',
64
- 'vrai': 'true',
65
- 'faux': 'false',
66
- 'nul': 'null',
67
- 'indefini': 'undefined',
68
- 'ceci': 'this',
69
- 'classe': 'class',
70
- 'etendre': 'extends',
71
- 'super': 'super',
72
- 'constructeur': 'constructor',
73
- 'statique': 'static',
74
- 'obtenir': 'get',
75
- 'definir': 'set',
76
- 'asynchrone': 'async',
77
- 'attendre': 'await',
78
- 'importer': 'import',
79
- 'exporter': 'export',
80
- 'depuis': 'from',
81
- 'comme': 'as',
82
- 'addition': '+',
83
- 'soustraction': '-',
84
- 'multiplication': '*',
85
- 'division': '/',
86
- 'modulo': '%',
87
- 'puissance': '**',
88
- 'increment': '++',
89
- 'decrement': '--',
90
- 'egal': '==',
91
- 'different': '!=',
92
- 'superieur': '>',
93
- 'inferieur': '<',
94
- 'et': '&&',
95
- 'ou': '||',
96
- 'non': '!',
97
- 'document': 'document',
98
- 'fenetre': 'window',
99
- 'console': 'console',
100
- 'journal': 'console.log',
101
- 'erreur': 'console.error',
102
- 'avertir': 'console.warn',
103
- 'info': 'console.info',
104
- 'promesse': 'Promise',
105
- 'resoudre': 'resolve',
106
- 'rejeter': 'reject',
107
- 'alors': 'then',
108
- 'tout': 'all',
109
- 'course': 'race',
110
- 'json': 'JSON',
111
- 'serialiser': 'stringify',
112
- 'analyser': 'parse',
113
- 'math': 'Math',
114
- 'aleatoire': 'random',
115
- 'arrondir': 'round',
116
- 'plancher': 'floor',
117
- 'plafond': 'ceil',
118
- 'absolu': 'abs',
119
- 'maximum': 'max',
120
- 'minimum': 'min',
121
-
122
- 'maintenant': 'now',
123
-
124
- 'ensemble': 'Set',
125
- 'symbole': 'Symbol',
126
- 'objet': 'Object',
127
- 'selecteur': 'querySelector',
128
- 'sur': 'addEventListener',
129
- 'longueur': 'length',
130
- 'taille': 'size',
131
- 'recuperer': 'fetch',
132
- 'transformer': 'map',
133
- 'filtrer': 'filter',
134
- 'reduire': 'reduce',
135
- 'trouver': 'find',
136
- 'certains': 'some',
137
- 'tous': 'every',
138
- 'inclut': 'includes',
139
- 'joindre': 'join',
140
- 'inverser': 'reverse',
141
- 'trier': 'sort',
142
- 'decouper': 'slice',
143
- 'concatener': 'concat',
144
- 'ajouter': 'push',
145
- 'retirer': 'pop',
146
- 'diviser': 'split',
147
- 'remplacer': 'replace',
148
- 'rogner': 'trim',
149
- 'repeter': 'repeat'
150
- }
151
- }
152
-
153
- normalizeAccents(str) {
154
- return str.toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g, '')
155
- }
156
-
157
- tryMatchCompoundOperator() {
158
- const current = this.current()
159
- if (!current || current.type !== TokenType.IDENTIFIER) return null
160
-
161
- const val1 = this.normalizeAccents(String(current.value))
162
- const next = this.peek(1)
163
- const next2 = this.peek(2)
164
-
165
- if (next && next.type === TokenType.IDENTIFIER) {
166
- const val2 = this.normalizeAccents(String(next.value))
167
-
168
- if (next2 && next2.type === TokenType.IDENTIFIER) {
169
- const val3 = this.normalizeAccents(String(next2.value))
170
- const compound3 = `${val1} ${val2} ${val3}`
171
- if (this.compoundOperators[compound3]) {
172
- this.advance()
173
- this.advance()
174
- this.advance()
175
- return this.compoundOperators[compound3]
176
- }
177
- }
178
-
179
- const compound2 = `${val1} ${val2}`
180
- if (this.compoundOperators[compound2]) {
181
- this.advance()
182
- this.advance()
183
- return this.compoundOperators[compound2]
184
- }
185
- }
186
-
187
- return null
188
- }
189
-
190
- tryMatchCompoundKeyword() {
191
- const current = this.current()
192
- if (!current || current.type !== TokenType.IDENTIFIER) return null
193
-
194
- const val1 = this.normalizeAccents(String(current.value))
195
- const next = this.peek(1)
196
-
197
- if (next && next.type === TokenType.IDENTIFIER) {
198
- const val2 = this.normalizeAccents(String(next.value))
199
- const compound2 = `${val1} ${val2}`
200
- if (this.compoundKeywords[compound2]) {
201
- this.advance()
202
- this.advance()
203
- return this.compoundKeywords[compound2]
204
- }
205
- }
206
-
207
- return null
208
- }
209
-
210
- translateJSIdentifier(word) {
211
- if (!word) return word
212
- const lower = this.normalizeAccents(String(word))
213
- return this.jsTranslations[lower] || word
214
- }
215
-
216
- loadAllI18n(dir) {
217
- const files = [
218
- 'i18n-css.json', 'i18n-html.json', 'i18n-js.json',
219
- 'i18n-php.json', 'i18n-python.json', 'i18n-sql.json',
220
- 'i18n-ruby.json', 'i18n-node.json', 'i18n-react.json',
221
- 'i18n-ts.json', 'i18n-graphql.json'
222
- ]
223
-
224
- for (const file of files) {
225
- const lang = file.replace('i18n-', '').replace('.json', '')
226
- const path = `${dir}/${file}`
227
-
228
- try {
229
- if (fs.existsSync(path)) {
230
- this.i18n[lang] = JSON.parse(fs.readFileSync(path, 'utf-8'))
231
- this.buildReverseMap(lang)
232
- }
233
- } catch (e) {
234
- }
235
- }
236
- }
237
-
238
- buildReverseMap(lang) {
239
- this.reverseMaps[lang] = {}
240
- const data = this.i18n[lang]
241
-
242
- const processSection = (section, targetKey = lang) => {
243
- if (!section || typeof section !== 'object') return
244
-
245
- for (const [key, translations] of Object.entries(section)) {
246
- if (translations && typeof translations === 'object') {
247
- if (translations.fr) {
248
- const frTerms = Array.isArray(translations.fr) ? translations.fr : [translations.fr]
249
- const target = translations[targetKey] || translations.css || translations.html || translations.js || translations.value
250
-
251
- for (const term of frTerms) {
252
- if (typeof term === 'string') {
253
- this.reverseMaps[lang][term.toLowerCase()] = target
254
- }
255
- }
256
- }
257
- }
258
- }
259
- }
260
-
261
- for (const [sectionName, sectionData] of Object.entries(data)) {
262
- processSection(sectionData)
263
- }
264
- }
265
-
266
- peek(offset = 0) {
267
- const idx = this.pos + offset
268
- return idx < this.tokens.length ? this.tokens[idx] : null
269
- }
270
-
271
- current() {
272
- return this.tokens[this.pos] || null
273
- }
274
-
275
- advance() {
276
- const token = this.tokens[this.pos]
277
- this.pos++
278
- return token
279
- }
280
-
281
- expect(type) {
282
- const token = this.current()
283
- if (!token || token.type !== type) {
284
- throw new Error(`Attendu ${type}, reçu ${token ? token.type : 'EOF'}`)
285
- }
286
- return this.advance()
287
- }
288
-
289
- match(type) {
290
- if (this.current() && this.current().type === type) {
291
- return this.advance()
292
- }
293
- return null
294
- }
295
-
296
- matchValue(value) {
297
- const token = this.current()
298
- if (!token || token.value === null || token.value === undefined) return null
299
- const tokenVal = String(token.value).toLowerCase()
300
- const valueLower = value.toLowerCase()
301
-
302
- if (tokenVal === valueLower) {
303
- return this.advance()
304
- }
305
-
306
- if (valueLower.includes(' ')) {
307
- const parts = valueLower.split(' ')
308
- if (tokenVal === parts[0]) {
309
- const savedPos = this.pos
310
- const tokens = [this.advance()]
311
- let matched = true
312
-
313
- for (let i = 1; i < parts.length && matched; i++) {
314
- const nextToken = this.current()
315
- if (nextToken && String(nextToken.value).toLowerCase() === parts[i]) {
316
- tokens.push(this.advance())
317
- } else {
318
- matched = false
319
- }
320
- }
321
-
322
- if (matched) {
323
- return tokens[tokens.length - 1]
324
- } else {
325
- this.pos = savedPos
326
- }
327
- }
328
- }
329
-
330
- return null
331
- }
332
-
333
- isAtEnd() {
334
- return this.pos >= this.tokens.length || (this.current() && this.current().type === TokenType.EOF)
335
- }
336
-
337
- skipNewlines() {
338
- while (this.match(TokenType.NEWLINE)) {}
339
- }
340
-
341
- detectTargetLanguage(source) {
342
- const lower = source.toLowerCase()
343
-
344
- const targetMatch = source.match(/\/\/\s*cible\s*:\s*(\w+)/i)
345
- if (targetMatch) {
346
- const target = targetMatch[1].toLowerCase()
347
- const langMap = {
348
- 'js': 'js', 'javascript': 'js',
349
- 'css': 'css', 'style': 'css',
350
- 'html': 'html', 'page': 'html',
351
- 'ts': 'ts', 'typescript': 'ts',
352
- 'react': 'react', 'jsx': 'react',
353
- 'php': 'php',
354
- 'python': 'python', 'py': 'python',
355
- 'ruby': 'ruby', 'rb': 'ruby',
356
- 'sql': 'sql',
357
- 'node': 'node', 'nodejs': 'node',
358
- 'graphql': 'graphql', 'gql': 'graphql'
359
- }
360
- if (langMap[target]) {
361
- return langMap[target]
362
- }
363
- }
364
-
365
- if (/^\s*(document|page|<!doctype|<html|tete|corps|entete|navigation|section|article)/m.test(lower)) {
366
- return 'html'
367
- }
368
-
369
- if (/^\s*(\.|#|@media|@keyframes|corps|html|body|fond|marge|remplissage|largeur|hauteur)/m.test(lower)) {
370
- if (!/fonction|si\s|pour\s|tant que/.test(lower)) {
371
- return 'css'
372
- }
373
- }
374
-
375
- if (/^\s*(composant|etat|effet|memo|contexte|utiliser\s+(etat|effet|memo)|retourner\s*\(?\s*<)/m.test(lower)) {
376
- return 'react'
377
- }
378
-
379
- if (/<[a-z]+|jsx|tsx/i.test(lower) && /fonction|composant/.test(lower)) {
380
- return 'react'
381
- }
382
-
383
- if (/^\s*(creer table|inserer|selectionner|mettre a jour|supprimer|table|base de donnees)/m.test(lower)) {
384
- return 'sql'
385
- }
386
-
387
- if (/^\s*(type|interface|enum|generique|<[A-Z]\w*>)/m.test(lower)) {
388
- return 'ts'
389
- }
390
-
391
- if (/^\s*(schema|type\s+\w+\s*{|query|mutation|subscription|input\s+\w+)/m.test(lower)) {
392
- return 'graphql'
393
- }
394
-
395
- if (/^\s*(<\?php|\$\w+|echo|inclure|require)/m.test(lower)) {
396
- return 'php'
397
- }
398
-
399
- if (/^\s*(def\s+\w+|classe\s+\w+|importer\s|de\s+\w+\s+importer)/m.test(lower) && !/fin\s*$|faire\s*$/m.test(lower)) {
400
- return 'python'
401
- }
402
-
403
- if (/^\s*(classe\s+\w+|def\s+\w+|fin\s*$|faire\s*$|module\s+\w+)/m.test(lower)) {
404
- return 'ruby'
405
- }
406
-
407
- if (/^\s*(const\s+\w+\s*=\s*require|module\.exports|exporter\s+defaut)/m.test(lower)) {
408
- return 'node'
409
- }
410
-
411
- if (/fonction|variable|constante|si\s|pour\s|tant que|retourner/.test(lower)) {
412
- return 'js'
413
- }
414
-
415
- return 'js'
416
- }
417
-
418
- parse(source, targetLang = null) {
419
- const lexer = new EtherLexer(source)
420
- this.tokens = lexer.tokenize()
421
- this.pos = 0
422
-
423
- const detectedLang = targetLang || this.targetLang
424
- const lang = detectedLang === 'auto' ? this.detectTargetLanguage(source) : detectedLang
425
-
426
- switch (lang) {
427
- case 'css':
428
- return this.parseCSS()
429
- case 'html':
430
- return this.parseHTML()
431
- case 'js':
432
- case 'javascript':
433
- return this.parseJS()
434
- case 'ts':
435
- case 'typescript':
436
- return this.parseTS()
437
- case 'react':
438
- case 'jsx':
439
- return this.parseReact()
440
- case 'php':
441
- return this.parsePHP()
442
- case 'python':
443
- case 'py':
444
- return this.parsePython()
445
- case 'ruby':
446
- case 'rb':
447
- return this.parseRuby()
448
- case 'sql':
449
- return this.parseSQL()
450
- case 'node':
451
- case 'nodejs':
452
- return this.parseNode()
453
- case 'graphql':
454
- case 'gql':
455
- return this.parseGraphQL()
456
- default:
457
- return this.parseJS()
458
- }
459
- }
460
-
461
- parseCSS() {
462
- const stylesheet = {
463
- type: 'StyleSheet',
464
- rules: []
465
- }
466
-
467
- this.skipNewlines()
468
-
469
- while (!this.isAtEnd()) {
470
- const startPos = this.pos
471
- const rule = this.parseCSSRule()
472
- if (rule) {
473
- stylesheet.rules.push(rule)
474
- }
475
- this.skipNewlines()
476
- if (this.pos === startPos && !this.isAtEnd()) {
477
- this.advance()
478
- }
479
- }
480
-
481
- return stylesheet
482
- }
483
-
484
- parseCSSRule() {
485
- this.skipNewlines()
486
- const token = this.current()
487
- if (!token || token.type === TokenType.EOF) return null
488
-
489
- if (token.type === TokenType.AT) {
490
- return this.parseCSSAtRule()
491
- }
492
-
493
- const selector = this.parseCSSSelector()
494
- if (!selector) return null
495
-
496
- this.skipNewlines()
497
- this.match(TokenType.INDENT)
498
-
499
- const declarations = []
500
- const nestedRules = []
501
-
502
- while (!this.isAtEnd()) {
503
- const current = this.current()
504
-
505
- if (current && current.type === TokenType.DEDENT) {
506
- this.advance()
507
- break
508
- }
509
-
510
- if (current && current.type === TokenType.NEWLINE) {
511
- this.advance()
512
- continue
513
- }
514
-
515
- if (!current || current.type === TokenType.EOF) break
516
-
517
- if (current.type === TokenType.IDENTIFIER) {
518
- const value = current.value.toLowerCase()
519
-
520
- if (value.startsWith('au ') || value === 'au survol' || value === 'au clic' ||
521
- value === 'au focus' || value === 'actif' || value === 'visite' ||
522
- value.startsWith('premier') || value.startsWith('dernier') ||
523
- value.startsWith('avant') || value.startsWith('apres')) {
524
- const pseudoRule = this.parseCSSPseudoBlock(selector)
525
- if (pseudoRule) nestedRules.push(pseudoRule)
526
- continue
527
- }
528
- }
529
-
530
- const decl = this.parseCSSDeclaration()
531
- if (decl) {
532
- declarations.push(decl)
533
- } else {
534
- this.advance()
535
- }
536
- }
537
-
538
- return {
539
- type: 'Rule',
540
- selector: selector,
541
- declarations: declarations,
542
- nestedRules: nestedRules
543
- }
544
- }
545
-
546
- parseCSSSelector() {
547
- const parts = []
548
-
549
- while (!this.isAtEnd()) {
550
- const token = this.current()
551
-
552
- if (!token || token.type === TokenType.NEWLINE || token.type === TokenType.INDENT ||
553
- token.type === TokenType.EOF) {
554
- break
555
- }
556
-
557
- if (token.type === TokenType.DOUBLE_COLON) {
558
- this.advance()
559
- const pseudoName = this.current()
560
- if (pseudoName && pseudoName.type === TokenType.IDENTIFIER) {
561
- parts.push('::' + pseudoName.value)
562
- this.advance()
563
- }
564
- } else if (token.type === TokenType.COLON) {
565
- this.advance()
566
- const next = this.current()
567
- if (next && next.type === TokenType.COLON) {
568
- this.advance()
569
- const pseudoName = this.current()
570
- if (pseudoName && pseudoName.type === TokenType.IDENTIFIER) {
571
- parts.push('::' + pseudoName.value)
572
- this.advance()
573
- }
574
- } else if (next && next.type === TokenType.IDENTIFIER) {
575
- parts.push(':' + next.value)
576
- this.advance()
577
- } else {
578
- break
579
- }
580
- } else if (token.type === TokenType.DOT) {
581
- this.advance()
582
- const name = this.current()
583
- if (name && name.type === TokenType.IDENTIFIER) {
584
- parts.push('.' + name.value)
585
- this.advance()
586
- }
587
- } else if (token.type === TokenType.HASH) {
588
- this.advance()
589
- const name = this.current()
590
- if (name && name.type === TokenType.IDENTIFIER) {
591
- parts.push('#' + name.value)
592
- this.advance()
593
- }
594
- } else if (token.type === TokenType.IDENTIFIER) {
595
- parts.push(this.translateCSSSelector(token.value))
596
- this.advance()
597
- } else if (token.type === TokenType.STAR) {
598
- parts.push('*')
599
- this.advance()
600
- } else if (token.type === TokenType.COMMA) {
601
- parts.push(',')
602
- this.advance()
603
- } else if (token.type === TokenType.GT) {
604
- parts.push('>')
605
- this.advance()
606
- } else if (token.type === TokenType.PLUS) {
607
- parts.push('+')
608
- this.advance()
609
- } else if (token.type === TokenType.TILDE) {
610
- parts.push('~')
611
- this.advance()
612
- } else if (token.type === TokenType.LBRACKET) {
613
- let attrSelector = '['
614
- this.advance()
615
- while (!this.isAtEnd()) {
616
- const attrToken = this.current()
617
- if (!attrToken || attrToken.type === TokenType.RBRACKET) {
618
- attrSelector += ']'
619
- this.advance()
620
- break
621
- }
622
- attrSelector += attrToken.value
623
- this.advance()
624
- }
625
- parts.push(attrSelector)
626
- } else if (token.type === TokenType.EQUALS) {
627
- break
628
- } else {
629
- break
630
- }
631
- }
632
-
633
- return parts.join(' ').replace(/\s+([,>+~])\s+/g, ' $1 ').replace(/\s+(::?)/g, '$1').trim()
634
- }
635
-
636
- translateCSSSelector(selector) {
637
- const map = this.reverseMaps.css || {}
638
- const lower = selector.toLowerCase()
639
-
640
- const selectorTranslations = {
641
- 'corps': 'body',
642
- 'html': 'html',
643
- 'document': 'html',
644
- 'tete': 'head',
645
- 'titre': 'title',
646
- 'titre1': 'h1',
647
- 'titre2': 'h2',
648
- 'titre3': 'h3',
649
- 'titre4': 'h4',
650
- 'titre5': 'h5',
651
- 'titre6': 'h6',
652
- 'paragraphe': 'p',
653
- 'lien': 'a',
654
- 'ressource': 'link',
655
- 'lien externe': 'link',
656
- 'image': 'img',
657
- 'bouton': 'button',
658
- 'formulaire': 'form',
659
- 'champ': 'input',
660
- 'liste': 'ul',
661
- 'liste ordonnee': 'ol',
662
- 'element liste': 'li',
663
- 'tableau': 'table',
664
- 'ligne': 'tr',
665
- 'cellule': 'td',
666
- 'entete': 'header',
667
- 'piedpage': 'footer',
668
- 'navigation': 'nav',
669
- 'section': 'section',
670
- 'article': 'article',
671
- 'cote': 'aside',
672
- 'principal': 'main',
673
- 'division': 'div',
674
- 'portee': 'span'
675
- }
676
-
677
- return selectorTranslations[lower] || map[lower] || selector
678
- }
679
-
680
- parseCSSDeclaration() {
681
- this.skipNewlines()
682
- const token = this.current()
683
- if (!token || token.type !== TokenType.IDENTIFIER) return null
684
-
685
- const compoundProperties = {
686
- 'marge': ['dedans', 'autour', 'haut', 'bas', 'gauche', 'droite'],
687
- 'marge dedans': ['haut', 'bas', 'gauche', 'droite'],
688
- 'marge autour': ['haut', 'bas', 'gauche', 'droite'],
689
- 'remplissage': ['haut', 'bas', 'gauche', 'droite'],
690
- 'taille': ['police'],
691
- 'poids': ['police'],
692
- 'style': ['police', 'liste'],
693
- 'hauteur': ['ligne', 'min', 'max', 'minimum', 'maximum'],
694
- 'largeur': ['min', 'max', 'minimum', 'maximum'],
695
- 'alignement': ['texte'],
696
- 'decoration': ['texte'],
697
- 'transformation': ['texte'],
698
- 'ombre': ['texte', 'boite', 'boîte'],
699
- 'espacement': ['lettres'],
700
- 'bordure': ['haut', 'bas', 'gauche', 'droite', 'couleur', 'style', 'largeur', 'arrondi'],
701
- 'couleur': ['bordure', 'fond', 'texte', 'remplissage'],
702
- 'arrondi': ['haut', 'bas'],
703
- 'arrondi haut': ['gauche', 'droite'],
704
- 'arrondi bas': ['gauche', 'droite'],
705
- 'fond': ['couleur', 'image', 'position', 'taille', 'repetition', 'attachement'],
706
- 'direction': ['flex'],
707
- 'enveloppe': ['flex'],
708
- 'justifier': ['contenu'],
709
- 'aligner': ['elements', 'éléments', 'contenu'],
710
- 'colonnes': ['grille'],
711
- 'lignes': ['grille'],
712
- 'espace': ['ligne', 'colonne', 'blanc'],
713
- 'débordement': ['x', 'y'],
714
- 'debordement': ['x', 'y'],
715
- 'événements': ['pointeur'],
716
- 'evenements': ['pointeur'],
717
- 'modèle': ['boite', 'boîte'],
718
- 'modele': ['boite', 'boîte'],
719
- 'filtre': ['fond'],
720
- 'decoupe': ['fond'],
721
- 'découpe': ['fond'],
722
- 'origine': ['transformation', 'perspective'],
723
- 'liste': ['style', 'type', 'position', 'image'],
724
- }
725
-
726
- let property = token.value
727
- this.advance()
728
-
729
- let nextToken = this.current()
730
- while (nextToken && nextToken.type === TokenType.IDENTIFIER) {
731
- const propLower = property.toLowerCase()
732
- if (compoundProperties[propLower] && compoundProperties[propLower].includes(nextToken.value.toLowerCase())) {
733
- property = property + ' ' + nextToken.value
734
- this.advance()
735
- nextToken = this.current()
736
- } else {
737
- break
738
- }
739
- }
740
-
741
- let hasColon = this.match(TokenType.COLON)
742
- let hasEquals = !hasColon && this.match(TokenType.EQUALS)
743
-
744
- if (!hasColon && !hasEquals) {
745
- return null
746
- }
747
-
748
- const valueParts = []
749
- let lastType = null
750
-
751
- while (!this.isAtEnd()) {
752
- const current = this.current()
753
-
754
- if (!current || current.type === TokenType.NEWLINE ||
755
- current.type === TokenType.DEDENT || current.type === TokenType.EOF) {
756
- break
757
- }
758
-
759
- let needsSpace = valueParts.length > 0
760
-
761
- if (current.type === TokenType.LPAREN || current.type === TokenType.COMMA ||
762
- current.type === TokenType.RPAREN || current.type === TokenType.PERCENT ||
763
- lastType === TokenType.LPAREN || lastType === TokenType.MINUS ||
764
- lastType === TokenType.HASH || lastType === TokenType.COLON) {
765
- needsSpace = false
766
- }
767
-
768
- if (lastType === TokenType.NUMBER || lastType === TokenType.INTEGER || lastType === TokenType.FLOAT) {
769
- if (current.type === TokenType.IDENTIFIER) {
770
- const units = ['px', 'em', 'rem', '%', 'vw', 'vh', 'vmin', 'vmax', 'ch', 'ex', 'cm', 'mm', 'in', 'pt', 'pc', 'deg', 'rad', 'turn', 'ms', 's', 'fr', 'cqw', 'cqh']
771
- if (units.includes(current.value.toLowerCase())) {
772
- needsSpace = false
773
- }
774
- }
775
- }
776
-
777
- if (current.type === TokenType.IDENTIFIER) {
778
- if (needsSpace) valueParts.push(' ')
779
- valueParts.push(current.value)
780
- } else if (current.type === TokenType.NUMBER || current.type === TokenType.INTEGER ||
781
- current.type === TokenType.FLOAT) {
782
- if (needsSpace && lastType !== TokenType.HASH) valueParts.push(' ')
783
- valueParts.push(current.value)
784
- } else if (current.type === TokenType.STRING) {
785
- if (needsSpace) valueParts.push(' ')
786
- valueParts.push('"' + current.value + '"')
787
- } else if (current.type === TokenType.HEX) {
788
- if (needsSpace) valueParts.push(' ')
789
- valueParts.push(current.value)
790
- } else if (current.type === TokenType.HASH) {
791
- if (needsSpace) valueParts.push(' ')
792
- valueParts.push('#')
793
- } else if (current.type === TokenType.COMMA) {
794
- valueParts.push(',')
795
- } else if (current.type === TokenType.LPAREN) {
796
- valueParts.push('(')
797
- } else if (current.type === TokenType.RPAREN) {
798
- valueParts.push(')')
799
- } else if (current.type === TokenType.PERCENT) {
800
- valueParts.push('%')
801
- } else if (current.type === TokenType.SLASH) {
802
- if (needsSpace) valueParts.push(' ')
803
- valueParts.push('/')
804
- } else if (current.type === TokenType.MINUS) {
805
- if (current.value === '--') {
806
- valueParts.push('--')
807
- } else if (lastType === TokenType.LPAREN) {
808
- valueParts.push(current.value)
809
- } else {
810
- valueParts.push(' ' + current.value)
811
- }
812
- } else if (current.type === TokenType.PLUS) {
813
- valueParts.push(' +')
814
- } else if (current.type === TokenType.COLON) {
815
- valueParts.push(':')
816
- } else if (current.type === TokenType.DOT) {
817
- valueParts.push('.')
818
- } else {
819
- break
820
- }
821
-
822
- lastType = current.type
823
- this.advance()
824
- }
825
-
826
- return {
827
- type: 'Declaration',
828
- property: property,
829
- value: valueParts.join('')
830
- .replace(/,\s*/g, ', ')
831
- .replace(/\s+/g, ' ')
832
- .trim()
833
- }
834
- }
835
-
836
- parseCSSPseudoBlock(parentSelector) {
837
- const pseudoToken = this.current()
838
- const pseudoClass = this.translatePseudoClass(pseudoToken.value)
839
- this.advance()
840
-
841
- this.skipNewlines()
842
- this.match(TokenType.INDENT)
843
-
844
- const declarations = []
845
-
846
- while (!this.isAtEnd()) {
847
- const current = this.current()
848
-
849
- if (current && current.type === TokenType.DEDENT) {
850
- this.advance()
851
- break
852
- }
853
-
854
- if (current && current.type === TokenType.NEWLINE) {
855
- this.advance()
856
- continue
857
- }
858
-
859
- if (!current || current.type === TokenType.EOF) break
860
-
861
- const decl = this.parseCSSDeclaration()
862
- if (decl) {
863
- declarations.push(decl)
864
- } else {
865
- this.advance()
866
- }
867
- }
868
-
869
- return {
870
- type: 'Rule',
871
- selector: `${parentSelector}${pseudoClass}`,
872
- declarations: declarations
873
- }
874
- }
875
-
876
- translatePseudoClass(value) {
877
- const map = {
878
- 'au survol': ':hover',
879
- 'au clic': ':active',
880
- 'actif': ':active',
881
- 'au focus': ':focus',
882
- 'focus': ':focus',
883
- 'visite': ':visited',
884
- 'premier enfant': ':first-child',
885
- 'dernier enfant': ':last-child',
886
- 'premier de type': ':first-of-type',
887
- 'dernier de type': ':last-of-type',
888
- 'enfant unique': ':only-child',
889
- 'unique de type': ':only-of-type',
890
- 'vide': ':empty',
891
- 'cible': ':target',
892
- 'coche': ':checked',
893
- 'desactive': ':disabled',
894
- 'active': ':enabled',
895
- 'requis': ':required',
896
- 'optionnel': ':optional',
897
- 'valide': ':valid',
898
- 'invalide': ':invalid',
899
- 'lecture seule': ':read-only',
900
- 'lecture ecriture': ':read-write',
901
- 'avant': '::before',
902
- 'apres': '::after',
903
- 'premiere lettre': '::first-letter',
904
- 'premiere ligne': '::first-line',
905
- 'selection': '::selection',
906
- 'placeholder': '::placeholder',
907
- 'marqueur': '::marker'
908
- }
909
-
910
- const lower = value.toLowerCase()
911
- return map[lower] || ':' + lower.replace(/\s+/g, '-')
912
- }
913
-
914
- parseCSSAtRule() {
915
- this.advance()
916
- const nameToken = this.current()
917
-
918
- if (!nameToken) return null
919
-
920
- const name = nameToken.value.toLowerCase()
921
- this.advance()
922
-
923
- if (name === 'media' || name === 'requete media' || name === 'ecran' || name === 'si') {
924
- return this.parseCSSMediaQuery()
925
- }
926
-
927
- if (name === 'keyframes' || name === 'images cles' || name === 'animation') {
928
- return this.parseCSSKeyframes()
929
- }
930
-
931
- if (name === 'import' || name === 'importer') {
932
- return this.parseCSSImport()
933
- }
934
-
935
- if (name === 'font-face' || name === 'police') {
936
- return this.parseCSSFontFace()
937
- }
938
-
939
- return null
940
- }
941
-
942
- parseCSSMediaQuery() {
943
- const queryParts = []
944
-
945
- while (!this.isAtEnd()) {
946
- const token = this.current()
947
-
948
- if (!token || token.type === TokenType.NEWLINE || token.type === TokenType.INDENT) {
949
- break
950
- }
951
-
952
- queryParts.push(token.value)
953
- this.advance()
954
- }
955
-
956
- this.skipNewlines()
957
- this.match(TokenType.INDENT)
958
-
959
- const rules = []
960
-
961
- while (!this.isAtEnd()) {
962
- const current = this.current()
963
-
964
- if (current && current.type === TokenType.DEDENT) {
965
- this.advance()
966
- break
967
- }
968
-
969
- const rule = this.parseCSSRule()
970
- if (rule) {
971
- rules.push(rule)
972
- }
973
-
974
- this.skipNewlines()
975
- }
976
-
977
- return {
978
- type: 'MediaQuery',
979
- query: this.translateMediaQuery(queryParts.join(' ')),
980
- rules: rules
981
- }
982
- }
983
-
984
- translateMediaQuery(query) {
985
- let result = query
986
-
987
- result = result.replace(/ecran\s+tres\s+petit/gi, 'screen and (max-width: 480px)')
988
- result = result.replace(/ecran\s+petit/gi, 'screen and (max-width: 768px)')
989
- result = result.replace(/ecran\s+moyen/gi, 'screen and (max-width: 1024px)')
990
- result = result.replace(/ecran\s+large/gi, 'screen and (min-width: 1025px) and (max-width: 1280px)')
991
- result = result.replace(/ecran\s+tres\s+large/gi, 'screen and (min-width: 1281px)')
992
-
993
- result = result
994
- .replace(/ecran/gi, 'screen')
995
- .replace(/imprimante/gi, 'print')
996
- .replace(/tous/gi, 'all')
997
- .replace(/largeur\s+min/gi, 'min-width')
998
- .replace(/largeur\s+max/gi, 'max-width')
999
- .replace(/hauteur\s+min/gi, 'min-height')
1000
- .replace(/hauteur\s+max/gi, 'max-height')
1001
- .replace(/orientation/gi, 'orientation')
1002
- .replace(/paysage/gi, 'landscape')
1003
- .replace(/portrait/gi, 'portrait')
1004
- .replace(/\bet\b/gi, 'and')
1005
- .replace(/\bou\b/gi, 'or')
1006
- .replace(/\bpas\b/gi, 'not')
1007
-
1008
- return result
1009
- }
1010
-
1011
- parseCSSKeyframes() {
1012
- const nameToken = this.current()
1013
- const name = nameToken ? nameToken.value : 'animation'
1014
- if (nameToken) this.advance()
1015
-
1016
- this.skipNewlines()
1017
- this.match(TokenType.INDENT)
1018
-
1019
- const keyframes = []
1020
-
1021
- while (!this.isAtEnd()) {
1022
- const current = this.current()
1023
-
1024
- if (current && current.type === TokenType.DEDENT) {
1025
- this.advance()
1026
- break
1027
- }
1028
-
1029
- if (current && current.type === TokenType.NEWLINE) {
1030
- this.advance()
1031
- continue
1032
- }
1033
-
1034
- const keyframe = this.parseCSSKeyframe()
1035
- if (keyframe) {
1036
- keyframes.push(keyframe)
1037
- }
1038
- }
1039
-
1040
- return {
1041
- type: 'Keyframes',
1042
- name: name,
1043
- keyframes: keyframes
1044
- }
1045
- }
1046
-
1047
- parseCSSKeyframe() {
1048
- const token = this.current()
1049
- if (!token) return null
1050
-
1051
- let selector = token.value
1052
-
1053
- const selectorMap = {
1054
- 'debut': 'from',
1055
- 'fin': 'to',
1056
- 'de': 'from',
1057
- 'a': 'to',
1058
- 'vers': 'to'
1059
- }
1060
-
1061
- selector = selectorMap[selector.toLowerCase()] || selector
1062
- this.advance()
1063
-
1064
- this.skipNewlines()
1065
- this.match(TokenType.INDENT)
1066
-
1067
- const declarations = []
1068
-
1069
- while (!this.isAtEnd()) {
1070
- const current = this.current()
1071
-
1072
- if (current && current.type === TokenType.DEDENT) {
1073
- this.advance()
1074
- break
1075
- }
1076
-
1077
- if (current && current.type === TokenType.NEWLINE) {
1078
- this.advance()
1079
- continue
1080
- }
1081
-
1082
- const decl = this.parseCSSDeclaration()
1083
- if (decl) {
1084
- declarations.push(decl)
1085
- }
1086
- }
1087
-
1088
- return {
1089
- type: 'Keyframe',
1090
- selector: selector,
1091
- declarations: declarations
1092
- }
1093
- }
1094
-
1095
- parseCSSImport() {
1096
- const urlToken = this.current()
1097
- const url = urlToken ? urlToken.value.replace(/['"]/g, '') : ''
1098
- if (urlToken) this.advance()
1099
-
1100
- return {
1101
- type: 'Import',
1102
- url: url
1103
- }
1104
- }
1105
-
1106
- parseCSSFontFace() {
1107
- this.skipNewlines()
1108
- this.match(TokenType.INDENT)
1109
-
1110
- const declarations = []
1111
-
1112
- while (!this.isAtEnd()) {
1113
- const current = this.current()
1114
-
1115
- if (current && current.type === TokenType.DEDENT) {
1116
- this.advance()
1117
- break
1118
- }
1119
-
1120
- if (current && current.type === TokenType.NEWLINE) {
1121
- this.advance()
1122
- continue
1123
- }
1124
-
1125
- const decl = this.parseCSSDeclaration()
1126
- if (decl) {
1127
- declarations.push(decl)
1128
- }
1129
- }
1130
-
1131
- return {
1132
- type: 'FontFace',
1133
- declarations: declarations
1134
- }
1135
- }
1136
-
1137
- parseHTML() {
1138
- const document = {
1139
- type: 'Document',
1140
- children: []
1141
- }
1142
-
1143
- this.skipNewlines()
1144
-
1145
- while (!this.isAtEnd()) {
1146
- const node = this.parseHTMLNode()
1147
- if (node) {
1148
- document.children.push(node)
1149
- }
1150
- this.skipNewlines()
1151
- }
1152
-
1153
- return document
1154
- }
1155
-
1156
- parseHTMLNode() {
1157
- this.skipNewlines()
1158
- const token = this.current()
1159
-
1160
- if (!token || token.type === TokenType.EOF) return null
1161
-
1162
- if (token.type === TokenType.IDENTIFIER) {
1163
- return this.parseHTMLElement()
1164
- }
1165
-
1166
- if (token.type === TokenType.STRING) {
1167
- this.advance()
1168
- return {
1169
- type: 'Text',
1170
- content: token.value.replace(/^["']|["']$/g, '')
1171
- }
1172
- }
1173
-
1174
- this.advance()
1175
- return null
1176
- }
1177
-
1178
- parseHTMLElement() {
1179
- const tagToken = this.current()
1180
- let tagName = this.normalizeAccents(tagToken.value)
1181
- const originalTagName = tagName
1182
- this.advance()
1183
-
1184
- const includeKeywords = ['inclure', 'requiert', 'include', 'require', 'importer']
1185
- if (includeKeywords.includes(tagName)) {
1186
- let includePath = null
1187
-
1188
- if (this.current() && this.current().type === TokenType.COLON) {
1189
- this.advance()
1190
- }
1191
-
1192
- const pathToken = this.current()
1193
- if (pathToken && pathToken.type === TokenType.STRING) {
1194
- includePath = pathToken.value.replace(/^["']|["']$/g, '')
1195
- this.advance()
1196
- } else if (pathToken && pathToken.type === TokenType.IDENTIFIER) {
1197
- let pathParts = [pathToken.value]
1198
- this.advance()
1199
- while (this.current() && (this.current().type === TokenType.SLASH || this.current().type === TokenType.DOT || this.current().type === TokenType.IDENTIFIER)) {
1200
- if (this.current().type === TokenType.SLASH) {
1201
- pathParts.push('/')
1202
- } else if (this.current().type === TokenType.DOT) {
1203
- pathParts.push('.')
1204
- } else {
1205
- pathParts.push(this.current().value)
1206
- }
1207
- this.advance()
1208
- }
1209
- includePath = pathParts.join('')
1210
- }
1211
-
1212
- this.skipNewlines()
1213
-
1214
- return {
1215
- type: 'Include',
1216
- path: includePath,
1217
- resolved: false
1218
- }
1219
- }
1220
-
1221
- const nextToken = this.current()
1222
- if ((tagName === 'titre' || tagName === 'heading') && nextToken && (nextToken.type === TokenType.NUMBER || nextToken.type === TokenType.INTEGER)) {
1223
- const num = parseInt(nextToken.value)
1224
- if (num >= 1 && num <= 6) {
1225
- tagName = `titre${num}`
1226
- this.advance()
1227
- }
1228
- }
1229
-
1230
- if ((tagName === 'liste' || tagName === 'list') && nextToken) {
1231
- if (nextToken.type === TokenType.IDENTIFIER) {
1232
- const nextVal = this.normalizeAccents(nextToken.value)
1233
- if (nextVal === 'ordonnee' || nextVal === 'ordered') {
1234
- tagName = 'liste ordonnee'
1235
- this.advance()
1236
- } else if (nextVal === 'non') {
1237
- this.advance()
1238
- const thirdToken = this.current()
1239
- if (thirdToken && thirdToken.type === TokenType.IDENTIFIER && this.normalizeAccents(thirdToken.value) === 'ordonnee') {
1240
- tagName = 'liste non ordonnee'
1241
- this.advance()
1242
- }
1243
- }
1244
- }
1245
- }
1246
-
1247
- if ((tagName === 'element' || tagName === 'item') && nextToken && nextToken.type === TokenType.IDENTIFIER) {
1248
- if (this.normalizeAccents(nextToken.value) === 'liste' || this.normalizeAccents(nextToken.value) === 'list') {
1249
- tagName = 'element liste'
1250
- this.advance()
1251
- }
1252
- }
1253
-
1254
- if ((tagName === 'zone' || tagName === 'area') && nextToken && nextToken.type === TokenType.IDENTIFIER) {
1255
- if (this.normalizeAccents(nextToken.value) === 'texte' || this.normalizeAccents(nextToken.value) === 'text') {
1256
- tagName = 'zone texte'
1257
- this.advance()
1258
- }
1259
- }
1260
-
1261
- if ((tagName === 'retour' || tagName === 'saut') && nextToken && nextToken.type === TokenType.IDENTIFIER) {
1262
- if (this.normalizeAccents(nextToken.value) === 'ligne' || this.normalizeAccents(nextToken.value) === 'line') {
1263
- tagName = 'retour ligne'
1264
- this.advance()
1265
- }
1266
- }
1267
-
1268
- if (tagName === 'ligne' && nextToken && nextToken.type === TokenType.IDENTIFIER) {
1269
- if (this.normalizeAccents(nextToken.value) === 'horizontale') {
1270
- tagName = 'ligne horizontale'
1271
- this.advance()
1272
- }
1273
- }
1274
-
1275
- if ((tagName === 'cellule' || tagName === 'cell') && nextToken && nextToken.type === TokenType.IDENTIFIER) {
1276
- if (this.normalizeAccents(nextToken.value) === 'entete' || this.normalizeAccents(nextToken.value) === 'header') {
1277
- tagName = 'entete cellule'
1278
- this.advance()
1279
- }
1280
- }
1281
-
1282
- if (tagName === 'groupe' && nextToken && nextToken.type === TokenType.IDENTIFIER) {
1283
- const next = this.normalizeAccents(nextToken.value)
1284
- if (next === 'titres') {
1285
- tagName = 'groupe titres'
1286
- this.advance()
1287
- } else if (next === 'options') {
1288
- tagName = 'groupe options'
1289
- this.advance()
1290
- } else if (next === 'colonnes') {
1291
- tagName = 'groupe colonnes'
1292
- this.advance()
1293
- }
1294
- }
1295
-
1296
- if (tagName === 'entete' && nextToken && nextToken.type === TokenType.IDENTIFIER) {
1297
- const next = this.normalizeAccents(nextToken.value)
1298
- if (next === 'tableau' || next === 'table') {
1299
- tagName = 'entete tableau'
1300
- this.advance()
1301
- }
1302
- }
1303
-
1304
- if (tagName === 'corps' && nextToken && nextToken.type === TokenType.IDENTIFIER) {
1305
- const next = this.normalizeAccents(nextToken.value)
1306
- if (next === 'tableau' || next === 'table') {
1307
- tagName = 'corps tableau'
1308
- this.advance()
1309
- }
1310
- }
1311
-
1312
- if (tagName === 'pied' && nextToken && nextToken.type === TokenType.IDENTIFIER) {
1313
- const next = this.normalizeAccents(nextToken.value)
1314
- if (next === 'tableau' || next === 'table') {
1315
- tagName = 'pied tableau'
1316
- this.advance()
1317
- }
1318
- }
1319
-
1320
- if (tagName === 'legende' && nextToken && nextToken.type === TokenType.IDENTIFIER) {
1321
- const next = this.normalizeAccents(nextToken.value)
1322
- if (next === 'tableau' || next === 'table') {
1323
- tagName = 'legende tableau'
1324
- this.advance()
1325
- } else if (next === 'figure') {
1326
- tagName = 'legende figure'
1327
- this.advance()
1328
- }
1329
- }
1330
-
1331
- if (tagName === 'ensemble' && nextToken && nextToken.type === TokenType.IDENTIFIER) {
1332
- const next = this.normalizeAccents(nextToken.value)
1333
- if (next === 'champs' || next === 'fields') {
1334
- tagName = 'ensemble champs'
1335
- this.advance()
1336
- }
1337
- }
1338
-
1339
- if (tagName === 'liste' && nextToken && nextToken.type === TokenType.IDENTIFIER) {
1340
- const next = this.normalizeAccents(nextToken.value)
1341
- if (next === 'donnees' || next === 'data') {
1342
- tagName = 'liste donnees'
1343
- this.advance()
1344
- } else if (next === 'description') {
1345
- tagName = 'liste description'
1346
- this.advance()
1347
- }
1348
- }
1349
-
1350
- if (tagName === 'image' && nextToken && nextToken.type === TokenType.IDENTIFIER) {
1351
- const next = this.normalizeAccents(nextToken.value)
1352
- if (next === 'reactive' || next === 'responsive') {
1353
- tagName = 'image reactive'
1354
- this.advance()
1355
- }
1356
- }
1357
-
1358
- if (tagName === 'cadre' && nextToken && nextToken.type === TokenType.IDENTIFIER) {
1359
- const next = this.normalizeAccents(nextToken.value)
1360
- if (next === 'en') {
1361
- this.advance()
1362
- const thirdToken = this.current()
1363
- if (thirdToken && thirdToken.type === TokenType.IDENTIFIER && thirdToken.value.toLowerCase() === 'ligne') {
1364
- tagName = 'cadre en ligne'
1365
- this.advance()
1366
- }
1367
- }
1368
- }
1369
-
1370
- if (tagName === 'citation' && nextToken && nextToken.type === TokenType.IDENTIFIER) {
1371
- const next = this.normalizeAccents(nextToken.value)
1372
- if (next === 'bloc' || next === 'block') {
1373
- tagName = 'citation bloc'
1374
- this.advance()
1375
- }
1376
- }
1377
-
1378
- if (tagName === 'reference' && nextToken && nextToken.type === TokenType.IDENTIFIER) {
1379
- const next = this.normalizeAccents(nextToken.value)
1380
- if (next === 'citation') {
1381
- tagName = 'reference citation'
1382
- this.advance()
1383
- }
1384
- }
1385
-
1386
- if (tagName === 'sans' && nextToken && nextToken.type === TokenType.IDENTIFIER) {
1387
- const next = this.normalizeAccents(nextToken.value)
1388
- if (next === 'script') {
1389
- tagName = 'sans script'
1390
- this.advance()
1391
- }
1392
- }
1393
-
1394
- if (tagName === 'isolation' && nextToken && nextToken.type === TokenType.IDENTIFIER) {
1395
- const next = this.normalizeAccents(nextToken.value)
1396
- if (next === 'bidi') {
1397
- tagName = 'isolation bidi'
1398
- this.advance()
1399
- }
1400
- }
1401
-
1402
- if (tagName === 'remplacement' && nextToken && nextToken.type === TokenType.IDENTIFIER) {
1403
- const next = this.normalizeAccents(nextToken.value)
1404
- if (next === 'bidi') {
1405
- tagName = 'remplacement bidi'
1406
- this.advance()
1407
- }
1408
- }
1409
-
1410
- if (tagName === 'annotation' && nextToken && nextToken.type === TokenType.IDENTIFIER) {
1411
- const next = this.normalizeAccents(nextToken.value)
1412
- if (next === 'ruby' || next === 'rubis') {
1413
- tagName = 'annotation ruby'
1414
- this.advance()
1415
- }
1416
- }
1417
-
1418
- if ((tagName === 'parentheses' || tagName === 'parenthèses') && nextToken && nextToken.type === TokenType.IDENTIFIER) {
1419
- const next = this.normalizeAccents(nextToken.value)
1420
- if (next === 'ruby' || next === 'rubis') {
1421
- tagName = 'parentheses ruby'
1422
- this.advance()
1423
- }
1424
- }
1425
-
1426
- if (tagName === 'carte' && nextToken && nextToken.type === TokenType.IDENTIFIER) {
1427
- const next = this.normalizeAccents(nextToken.value)
1428
- if (next === 'image') {
1429
- tagName = 'carte image'
1430
- this.advance()
1431
- }
1432
- }
1433
-
1434
- if ((tagName === 'texte' || tagName === 'text') && nextToken && nextToken.type === TokenType.IDENTIFIER) {
1435
- const next = this.normalizeAccents(nextToken.value)
1436
- if (next === 'alternatif' || next === 'alt') {
1437
- tagName = 'texte alternatif'
1438
- this.advance()
1439
- }
1440
- }
1441
-
1442
- if (tagName === 'entree' && nextToken && nextToken.type === TokenType.IDENTIFIER) {
1443
- const next = this.normalizeAccents(nextToken.value)
1444
- if (next === 'clavier') {
1445
- tagName = 'entree clavier'
1446
- this.advance()
1447
- }
1448
- }
1449
-
1450
- if (tagName === 'sortie' && nextToken && nextToken.type === TokenType.IDENTIFIER) {
1451
- const next = this.normalizeAccents(nextToken.value)
1452
- if (next === 'exemple') {
1453
- tagName = 'sortie exemple'
1454
- this.advance()
1455
- }
1456
- }
1457
-
1458
- if (tagName === 'definition' && nextToken && nextToken.type === TokenType.IDENTIFIER) {
1459
- const next = this.normalizeAccents(nextToken.value)
1460
- if (next === 'description') {
1461
- tagName = 'definition description'
1462
- this.advance()
1463
- }
1464
- }
1465
-
1466
- const tag = this.translateHTMLTag(tagName)
1467
-
1468
- const attributes = {}
1469
- let inlineText = null
1470
-
1471
- const compoundAttrs = {
1472
- 'texte': ['alternatif'],
1473
- 'longueur': ['max', 'min'],
1474
- 'valeur': ['max', 'min'],
1475
- 'zone': ['depot'],
1476
- 'mode': ['saisie'],
1477
- 'verification': ['orthographe'],
1478
- 'ordre': ['tabulation'],
1479
- 'action': ['formulaire'],
1480
- 'methode': ['formulaire'],
1481
- 'encodage': ['formulaire'],
1482
- 'validation': ['formulaire', 'auto'],
1483
- 'cible': ['formulaire', 'popover'],
1484
- 'lecture': ['seule', 'auto'],
1485
- 'nouvelle': ['fenetre'],
1486
- 'a': ['popup'],
1487
- 'jeu': ['caracteres', 'caractères'],
1488
- 'equivalent': ['http'],
1489
- 'équivalent': ['http'],
1490
- 'fusion': ['colonnes', 'lignes'],
1491
- 'plein': ['ecran', 'écran'],
1492
- 'bac': ['sable'],
1493
- 'origine': ['croisee', 'croisée'],
1494
- 'sources': ['reactives', 'réactives']
1495
- }
1496
-
1497
- while (!this.isAtEnd()) {
1498
- const current = this.current()
1499
-
1500
- if (!current || current.type === TokenType.NEWLINE ||
1501
- current.type === TokenType.INDENT || current.type === TokenType.EOF) {
1502
- break
1503
- }
1504
-
1505
- if (current.type === TokenType.STRING) {
1506
- inlineText = current.value.replace(/^["']|["']$/g, '')
1507
- this.advance()
1508
- break
1509
- }
1510
-
1511
- if (current.type === TokenType.IDENTIFIER) {
1512
- let attrName = current.value.toLowerCase()
1513
- this.advance()
1514
-
1515
- if (compoundAttrs[attrName]) {
1516
- const nextTok = this.current()
1517
- if (nextTok && nextTok.type === TokenType.IDENTIFIER) {
1518
- const nextVal = nextTok.value.toLowerCase()
1519
- if (compoundAttrs[attrName].includes(nextVal)) {
1520
- attrName = attrName + ' ' + nextVal
1521
- this.advance()
1522
- }
1523
- }
1524
- }
1525
-
1526
- const nextToken = this.current()
1527
-
1528
- if (nextToken && (nextToken.type === TokenType.EQUALS || nextToken.type === TokenType.COLON)) {
1529
- this.advance()
1530
- const valueToken = this.current()
1531
- if (valueToken) {
1532
- if (valueToken.type === TokenType.STRING) {
1533
- attributes[attrName] = valueToken.value.replace(/^["']|["']$/g, '')
1534
- } else if (valueToken.type === TokenType.IDENTIFIER ||
1535
- valueToken.type === TokenType.NUMBER || valueToken.type === TokenType.INTEGER) {
1536
- attributes[attrName] = valueToken.value
1537
- }
1538
- this.advance()
1539
- }
1540
-
1541
- if (this.current() && this.current().type === TokenType.COMMA) {
1542
- this.advance()
1543
- }
1544
- } else {
1545
- attributes[attrName] = true
1546
-
1547
- if (this.current() && this.current().type === TokenType.COMMA) {
1548
- this.advance()
1549
- }
1550
- }
1551
- } else if (current.type === TokenType.COMMA) {
1552
- this.advance()
1553
- } else {
1554
- break
1555
- }
1556
- }
1557
-
1558
- this.skipNewlines()
1559
-
1560
- const children = []
1561
-
1562
- if (inlineText) {
1563
- children.push({
1564
- type: 'Text',
1565
- content: inlineText
1566
- })
1567
- }
1568
-
1569
- const rawContentTags = ['script', 'style']
1570
- const isRawContent = rawContentTags.includes(tag)
1571
- const isScript = tag === 'script'
1572
-
1573
- if (this.match(TokenType.INDENT)) {
1574
- if (isRawContent) {
1575
- let rawText = []
1576
- while (!this.isAtEnd()) {
1577
- const current = this.current()
1578
-
1579
- if (current && current.type === TokenType.DEDENT) {
1580
- this.advance()
1581
- break
1582
- }
1583
-
1584
- if (current && current.type === TokenType.NEWLINE) {
1585
- rawText.push('\n')
1586
- this.advance()
1587
- continue
1588
- }
1589
-
1590
- if (current && current.type === TokenType.INDENT) {
1591
- this.advance()
1592
- continue
1593
- }
1594
-
1595
- if (current && current.type === TokenType.STRING) {
1596
- if (isScript) {
1597
- rawText.push("'" + current.value.replace(/'/g, "\\'") + "'")
1598
- } else {
1599
- rawText.push(current.value)
1600
- }
1601
- this.advance()
1602
- continue
1603
- }
1604
-
1605
- if (current && current.type === TokenType.LPAREN) {
1606
- rawText.push('(')
1607
- this.advance()
1608
- continue
1609
- }
1610
-
1611
- if (current && current.type === TokenType.RPAREN) {
1612
- rawText.push(')')
1613
- this.advance()
1614
- continue
1615
- }
1616
-
1617
- if (current && current.type === TokenType.LBRACKET) {
1618
- rawText.push('[')
1619
- this.advance()
1620
- continue
1621
- }
1622
-
1623
- if (current && current.type === TokenType.RBRACKET) {
1624
- rawText.push(']')
1625
- this.advance()
1626
- continue
1627
- }
1628
-
1629
- if (current && current.type === TokenType.LBRACE) {
1630
- rawText.push('{')
1631
- this.advance()
1632
- continue
1633
- }
1634
-
1635
- if (current && current.type === TokenType.RBRACE) {
1636
- rawText.push('}')
1637
- this.advance()
1638
- continue
1639
- }
1640
-
1641
- if (current && current.type === TokenType.DOT) {
1642
- rawText.push('.')
1643
- this.advance()
1644
- continue
1645
- }
1646
-
1647
- if (current && current.type === TokenType.COMMA) {
1648
- rawText.push(',')
1649
- this.advance()
1650
- continue
1651
- }
1652
-
1653
- if (current && current.type === TokenType.COLON) {
1654
- rawText.push(':')
1655
- this.advance()
1656
- continue
1657
- }
1658
-
1659
- if (current && current.type === TokenType.EQUALS) {
1660
- rawText.push('=')
1661
- this.advance()
1662
- continue
1663
- }
1664
-
1665
- if (current && current.type === TokenType.DOUBLE_EQUALS) {
1666
- rawText.push('==')
1667
- this.advance()
1668
- continue
1669
- }
1670
-
1671
- if (current && current.type === TokenType.NOT_EQUALS) {
1672
- rawText.push('!=')
1673
- this.advance()
1674
- continue
1675
- }
1676
-
1677
- if (current && current.type === TokenType.BANG) {
1678
- rawText.push('!')
1679
- this.advance()
1680
- continue
1681
- }
1682
-
1683
- if (current && current.type === TokenType.PLUS) {
1684
- rawText.push(current.value)
1685
- this.advance()
1686
- continue
1687
- }
1688
-
1689
- if (current && current.type === TokenType.MINUS) {
1690
- rawText.push(current.value)
1691
- this.advance()
1692
- continue
1693
- }
1694
-
1695
- if (current && current.type === TokenType.STAR) {
1696
- rawText.push(current.value)
1697
- this.advance()
1698
- continue
1699
- }
1700
-
1701
- if (current && current.type === TokenType.SLASH) {
1702
- rawText.push('/')
1703
- this.advance()
1704
- continue
1705
- }
1706
-
1707
- if (current && current.type === TokenType.PERCENT) {
1708
- rawText.push('%')
1709
- this.advance()
1710
- continue
1711
- }
1712
-
1713
- if (current && current.type === TokenType.AMPERSAND) {
1714
- rawText.push('&')
1715
- this.advance()
1716
- continue
1717
- }
1718
-
1719
- if (current && current.type === TokenType.DOUBLE_AMPERSAND) {
1720
- rawText.push('&&')
1721
- this.advance()
1722
- continue
1723
- }
1724
-
1725
- if (current && current.type === TokenType.PIPE) {
1726
- rawText.push('|')
1727
- this.advance()
1728
- continue
1729
- }
1730
-
1731
- if (current && current.type === TokenType.DOUBLE_PIPE) {
1732
- rawText.push('||')
1733
- this.advance()
1734
- continue
1735
- }
1736
-
1737
- if (current && current.type === TokenType.LT) {
1738
- rawText.push('<')
1739
- this.advance()
1740
- continue
1741
- }
1742
-
1743
- if (current && current.type === TokenType.GT) {
1744
- rawText.push('>')
1745
- this.advance()
1746
- continue
1747
- }
1748
-
1749
- if (current && current.type === TokenType.LTE) {
1750
- rawText.push('<=')
1751
- this.advance()
1752
- continue
1753
- }
1754
-
1755
- if (current && current.type === TokenType.GTE) {
1756
- rawText.push('>=')
1757
- this.advance()
1758
- continue
1759
- }
1760
-
1761
- if (current && current.type === TokenType.QUESTION) {
1762
- rawText.push('?')
1763
- this.advance()
1764
- continue
1765
- }
1766
-
1767
- if (current && current.type === TokenType.FAT_ARROW) {
1768
- rawText.push('=>')
1769
- this.advance()
1770
- continue
1771
- }
1772
-
1773
- if (current && current.type === TokenType.ARROW) {
1774
- rawText.push('->')
1775
- this.advance()
1776
- continue
1777
- }
1778
-
1779
- if (current && current.type === TokenType.SEMICOLON) {
1780
- rawText.push(';')
1781
- this.advance()
1782
- continue
1783
- }
1784
-
1785
- if (current) {
1786
- rawText.push(String(current.value))
1787
- this.advance()
1788
- }
1789
- }
1790
-
1791
- const textContent = rawText.join('').trim()
1792
- if (textContent) {
1793
- children.push({
1794
- type: 'Text',
1795
- content: textContent
1796
- })
1797
- }
1798
- } else {
1799
- while (!this.isAtEnd()) {
1800
- const current = this.current()
1801
-
1802
- if (current && current.type === TokenType.DEDENT) {
1803
- this.advance()
1804
- break
1805
- }
1806
-
1807
- const child = this.parseHTMLNode()
1808
- if (child) {
1809
- children.push(child)
1810
- }
1811
-
1812
- this.skipNewlines()
1813
- }
1814
- }
1815
- }
1816
-
1817
- if (originalTagName === 'document') {
1818
- return {
1819
- type: 'Doctype',
1820
- children: children
1821
- }
1822
- }
1823
-
1824
- return {
1825
- type: 'Element',
1826
- tag: tag,
1827
- attributes: attributes,
1828
- children: children
1829
- }
1830
- }
1831
-
1832
- translateHTMLTag(tag) {
1833
- const map = this.reverseMaps.html || {}
1834
- const lower = this.normalizeAccents(tag)
1835
- const withSpaces = lower.replace(/-/g, ' ')
1836
-
1837
- const translations = {
1838
- 'document': 'html',
1839
- 'page': 'html',
1840
- 'tete': 'head',
1841
- 'corps': 'body',
1842
- 'entete': 'header',
1843
- 'piedpage': 'footer',
1844
- 'pied': 'footer',
1845
- 'navigation': 'nav',
1846
- 'nav': 'nav',
1847
- 'principal': 'main',
1848
- 'section': 'section',
1849
- 'article': 'article',
1850
- 'cote': 'aside',
1851
- 'aside': 'aside',
1852
- 'titre': 'title',
1853
- 'titre1': 'h1',
1854
- 'titre2': 'h2',
1855
- 'titre3': 'h3',
1856
- 'titre4': 'h4',
1857
- 'titre5': 'h5',
1858
- 'titre6': 'h6',
1859
- 'titre 1': 'h1',
1860
- 'titre 2': 'h2',
1861
- 'titre 3': 'h3',
1862
- 'titre 4': 'h4',
1863
- 'titre 5': 'h5',
1864
- 'titre 6': 'h6',
1865
- 'heading 1': 'h1',
1866
- 'heading 2': 'h2',
1867
- 'heading 3': 'h3',
1868
- 'heading 4': 'h4',
1869
- 'heading 5': 'h5',
1870
- 'heading 6': 'h6',
1871
- 'groupe titres': 'hgroup',
1872
- 'heading group': 'hgroup',
1873
- 'sous titre': 'h2',
1874
- 'paragraphe': 'p',
1875
- 'texte': 'span',
1876
- 'division': 'div',
1877
- 'div': 'div',
1878
- 'bloc': 'div',
1879
- 'span': 'span',
1880
- 'lien': 'a',
1881
- 'ressource': 'link',
1882
- 'lien externe': 'link',
1883
- 'image': 'img',
1884
- 'liste': 'ul',
1885
- 'liste ordonnee': 'ol',
1886
- 'liste non ordonnee': 'ul',
1887
- 'element': 'li',
1888
- 'element liste': 'li',
1889
- 'tableau': 'table',
1890
- 'ligne': 'tr',
1891
- 'cellule': 'td',
1892
- 'entete cellule': 'th',
1893
- 'formulaire': 'form',
1894
- 'champ': 'input',
1895
- 'entree': 'input',
1896
- 'input': 'input',
1897
- 'zone texte': 'textarea',
1898
- 'bouton': 'button',
1899
- 'selection': 'select',
1900
- 'option': 'option',
1901
- 'etiquette': 'label',
1902
- 'gras': 'strong',
1903
- 'fort': 'strong',
1904
- 'italique': 'em',
1905
- 'emphase': 'em',
1906
- 'souligne': 'u',
1907
- 'barre': 's',
1908
- 'code': 'code',
1909
- 'pre': 'pre',
1910
- 'preformate': 'pre',
1911
- 'citation': 'blockquote',
1912
- 'retour ligne': 'br',
1913
- 'saut ligne': 'br',
1914
- 'br': 'br',
1915
- 'ligne horizontale': 'hr',
1916
- 'hr': 'hr',
1917
- 'video': 'video',
1918
- 'audio': 'audio',
1919
- 'source': 'source',
1920
- 'cadre': 'iframe',
1921
- 'canvas': 'canvas',
1922
- 'toile': 'canvas',
1923
- 'figure': 'figure',
1924
- 'legende': 'legend',
1925
- 'legende figure': 'figcaption',
1926
- 'legende tableau': 'caption',
1927
- 'legende ensemble': 'legend',
1928
- 'figure caption': 'figcaption',
1929
- 'table caption': 'caption',
1930
- 'entete tableau': 'thead',
1931
- 'table header': 'thead',
1932
- 'corps tableau': 'tbody',
1933
- 'table body': 'tbody',
1934
- 'pied tableau': 'tfoot',
1935
- 'table footer': 'tfoot',
1936
- 'ensemble champs': 'fieldset',
1937
- 'fieldset': 'fieldset',
1938
- 'liste donnees': 'datalist',
1939
- 'datalist': 'datalist',
1940
- 'liste description': 'dl',
1941
- 'description list': 'dl',
1942
- 'definition description': 'dd',
1943
- 'image reactive': 'picture',
1944
- 'picture': 'picture',
1945
- 'cadre en ligne': 'iframe',
1946
- 'inline frame': 'iframe',
1947
- 'iframe': 'iframe',
1948
- 'citation bloc': 'blockquote',
1949
- 'blockquote': 'blockquote',
1950
- 'reference citation': 'cite',
1951
- 'groupe options': 'optgroup',
1952
- 'option group': 'optgroup',
1953
- 'groupe colonnes': 'colgroup',
1954
- 'column group': 'colgroup',
1955
- 'colonne': 'col',
1956
- 'column': 'col',
1957
- 'entree clavier': 'kbd',
1958
- 'keyboard input': 'kbd',
1959
- 'sortie exemple': 'samp',
1960
- 'sample output': 'samp',
1961
- 'texte alternatif': 'alt',
1962
- 'resultat': 'output',
1963
- 'result': 'output',
1964
- 'piste': 'track',
1965
- 'track': 'track',
1966
- 'insere': 'ins',
1967
- 'inserted': 'ins',
1968
- 'supprime': 'del',
1969
- 'deleted': 'del',
1970
- 'donnee': 'data',
1971
- 'retour ligne possible': 'wbr',
1972
- 'word break': 'wbr',
1973
- 'carte image': 'map',
1974
- 'image map': 'map',
1975
- 'script': 'script',
1976
- 'details': 'details',
1977
- 'resume': 'summary',
1978
- 'dialogue': 'dialog',
1979
- 'modele': 'template',
1980
- 'emplacement': 'slot',
1981
- 'temps': 'time',
1982
- 'marque': 'mark',
1983
- 'metre': 'meter',
1984
- 'progression': 'progress',
1985
- 'sortie': 'output',
1986
- 'donnees': 'data',
1987
- 'clavier': 'kbd',
1988
- 'exemple': 'samp',
1989
- 'variable': 'var',
1990
- 'abreviation': 'abbr',
1991
- 'indice': 'sub',
1992
- 'exposant': 'sup',
1993
- 'petit': 'small',
1994
- 'cite': 'cite',
1995
- 'definition': 'dfn',
1996
- 'adresse': 'address',
1997
- 'rubis': 'ruby',
1998
- 'texte rubis': 'rt',
1999
- 'parenthese rubis': 'rp',
2000
- 'bidirectionnel': 'bdi',
2001
- 'direction': 'bdo',
2002
- 'cesure': 'wbr',
2003
- 'noscript': 'noscript',
2004
- 'sans script': 'noscript',
2005
- 'isolation bidi': 'bdi',
2006
- 'remplacement bidi': 'bdo',
2007
- 'annotation ruby': 'rt',
2008
- 'annotation rubis': 'rt',
2009
- 'parentheses ruby': 'rp',
2010
- 'parenthèses ruby': 'rp',
2011
- 'parentheses rubis': 'rp',
2012
- 'terme': 'dt',
2013
- 'menu': 'menu',
2014
- 'recherche': 'search',
2015
- 'objet': 'object',
2016
- 'integrer': 'embed',
2017
- 'svg': 'svg',
2018
- 'math': 'math',
2019
-
2020
- 'zone': 'area'
2021
- }
2022
-
2023
- const result = translations[lower] || translations[withSpaces] || map[lower] || map[withSpaces] || tag
2024
- return result.replace(/^<!DOCTYPE\s+/i, '').replace(/^<|>$/g, '').trim() || 'div'
2025
- }
2026
-
2027
- parseJS() {
2028
- return this.parseProgram('js')
2029
- }
2030
-
2031
- parseTS() {
2032
- return this.parseProgram('ts')
2033
- }
2034
-
2035
- parseReact() {
2036
- return this.parseProgram('react')
2037
- }
2038
-
2039
- parsePHP() {
2040
- return this.parseProgram('php')
2041
- }
2042
-
2043
- parsePython() {
2044
- return this.parseProgram('python')
2045
- }
2046
-
2047
- parseRuby() {
2048
- return this.parseProgram('ruby')
2049
- }
2050
-
2051
- parseNode() {
2052
- return this.parseProgram('node')
2053
- }
2054
-
2055
- parseSQL() {
2056
- return this.parseSQLProgram()
2057
- }
2058
-
2059
- parseGraphQL() {
2060
- return this.parseGraphQLProgram()
2061
- }
2062
-
2063
- parseProgram(lang) {
2064
- const program = {
2065
- type: 'Program',
2066
- lang: lang,
2067
- body: []
2068
- }
2069
-
2070
- this.skipNewlines()
2071
-
2072
- while (!this.isAtEnd()) {
2073
- const statement = this.parseStatement(lang)
2074
- if (statement) {
2075
- program.body.push(statement)
2076
- }
2077
- this.skipNewlines()
2078
- }
2079
-
2080
- return program
2081
- }
2082
-
2083
- parseStatement(lang) {
2084
- this.skipNewlines()
2085
- const token = this.current()
2086
-
2087
- if (!token || token.type === TokenType.EOF) return null
2088
-
2089
- const value = token.value != null ? String(token.value).toLowerCase() : ''
2090
-
2091
- if (this.isVariableDeclaration(value)) {
2092
- return this.parseVariableDeclaration(lang)
2093
- }
2094
-
2095
- if (this.isFunctionDeclaration(value)) {
2096
- return this.parseFunctionDeclaration(lang)
2097
- }
2098
-
2099
- if (this.isClassDeclaration(value)) {
2100
- return this.parseClassDeclaration(lang)
2101
- }
2102
-
2103
- if (this.isConditional(value)) {
2104
- return this.parseConditional(lang)
2105
- }
2106
-
2107
- if (this.isLoop(value)) {
2108
- return this.parseLoop(lang)
2109
- }
2110
-
2111
- if (this.isReturn(value)) {
2112
- return this.parseReturn(lang)
2113
- }
2114
-
2115
- if (this.isImport(value)) {
2116
- return this.parseImport(lang)
2117
- }
2118
-
2119
- if (this.isExport(value)) {
2120
- return this.parseExport(lang)
2121
- }
2122
-
2123
- const expr = this.parseExpression(lang)
2124
- if (expr) {
2125
- return {
2126
- type: 'ExpressionStatement',
2127
- expression: expr
2128
- }
2129
- }
2130
- return null
2131
- }
2132
-
2133
- isVariableDeclaration(value) {
2134
- const keywords = ['variable', 'var', 'constante', 'const', 'soit', 'let', 'definir']
2135
- return keywords.includes(value)
2136
- }
2137
-
2138
- isFunctionDeclaration(value) {
2139
- const keywords = ['fonction', 'func', 'fn', 'methode', 'def', 'procedure']
2140
- return keywords.includes(value)
2141
- }
2142
-
2143
- isClassDeclaration(value) {
2144
- const keywords = ['classe', 'class', 'type', 'interface', 'struct', 'structure']
2145
- return keywords.includes(value)
2146
- }
2147
-
2148
- isConditional(value) {
2149
- const keywords = ['si', 'if', 'sinon', 'else', 'sinon si', 'elif', 'selon', 'switch', 'cas', 'case']
2150
- return keywords.includes(value)
2151
- }
2152
-
2153
- isLoop(value) {
2154
- const keywords = ['pour', 'for', 'tant que', 'while', 'faire', 'do', 'repeter', 'boucle', 'loop', 'chaque', 'each', 'pour chaque', 'foreach']
2155
- return keywords.includes(value)
2156
- }
2157
-
2158
- isReturn(value) {
2159
- const keywords = ['retourner', 'return', 'renvoyer', 'rendre']
2160
- return keywords.includes(value)
2161
- }
2162
-
2163
- isImport(value) {
2164
- const keywords = ['importer', 'import', 'depuis', 'from', 'require', 'inclure', 'include', 'utiliser', 'use']
2165
- return keywords.includes(value)
2166
- }
2167
-
2168
- isExport(value) {
2169
- const keywords = ['exporter', 'export', 'publier', 'public']
2170
- return keywords.includes(value)
2171
- }
2172
-
2173
- parseVariableDeclaration(lang) {
2174
- const kindToken = this.advance()
2175
- const kind = this.translateVariableKind(kindToken.value, lang)
2176
-
2177
- const nameToken = this.current()
2178
- if (!nameToken || nameToken.type !== TokenType.IDENTIFIER) {
2179
- return null
2180
- }
2181
- const name = nameToken.value
2182
- this.advance()
2183
-
2184
- let typeAnnotation = null
2185
- if (this.match(TokenType.COLON)) {
2186
- typeAnnotation = this.parseType(lang)
2187
- }
2188
-
2189
- let init = null
2190
- if (this.match(TokenType.EQUALS)) {
2191
- init = this.parseExpression(lang)
2192
- }
2193
-
2194
- return {
2195
- type: 'VariableDeclaration',
2196
- kind: kind,
2197
- name: name,
2198
- typeAnnotation: typeAnnotation,
2199
- init: init
2200
- }
2201
- }
2202
-
2203
- translateVariableKind(value, lang) {
2204
- const lower = value.toLowerCase()
2205
-
2206
- if (lower === 'constante' || lower === 'const') {
2207
- return 'const'
2208
- }
2209
- if (lower === 'variable' || lower === 'var') {
2210
- if (lang === 'js' || lang === 'ts' || lang === 'react') {
2211
- return 'let'
2212
- }
2213
- return 'var'
2214
- }
2215
- if (lower === 'soit' || lower === 'let') {
2216
- return 'let'
2217
- }
2218
-
2219
- return 'let'
2220
- }
2221
-
2222
- parseFunctionDeclaration(lang) {
2223
- this.advance()
2224
-
2225
- let isAsync = false
2226
- let isGenerator = false
2227
-
2228
- const modifierToken = this.current()
2229
- if (modifierToken && modifierToken.type === TokenType.IDENTIFIER) {
2230
- const modVal = this.normalizeAccents(modifierToken.value.toLowerCase())
2231
- if (modVal === 'asynchrone' || modVal === 'async') {
2232
- isAsync = true
2233
- this.advance()
2234
- } else if (modVal === 'generatrice' || modVal === 'generator' || modVal === 'generateur') {
2235
- isGenerator = true
2236
- this.advance()
2237
- }
2238
- }
2239
-
2240
- const nameToken = this.current()
2241
- if (!nameToken || nameToken.type !== TokenType.IDENTIFIER) {
2242
- return null
2243
- }
2244
- const name = nameToken.value
2245
- this.advance()
2246
-
2247
- const params = []
2248
- if (this.match(TokenType.LPAREN)) {
2249
- while (!this.isAtEnd() && !this.match(TokenType.RPAREN)) {
2250
- while (this.match(TokenType.NEWLINE) || this.match(TokenType.INDENT) || this.match(TokenType.DEDENT)) {}
2251
- if (this.check(TokenType.RPAREN)) break
2252
- const param = this.parseParameter(lang)
2253
- if (param) {
2254
- params.push(param)
2255
- }
2256
- this.match(TokenType.COMMA)
2257
- while (this.match(TokenType.NEWLINE) || this.match(TokenType.INDENT) || this.match(TokenType.DEDENT)) {}
2258
- }
2259
- } else {
2260
- while (!this.isAtEnd()) {
2261
- const token = this.current()
2262
- if (!token || token.type === TokenType.NEWLINE || token.type === TokenType.COLON || token.type === TokenType.ARROW) {
2263
- break
2264
- }
2265
- if (token.type === TokenType.IDENTIFIER) {
2266
- const param = this.parseParameter(lang)
2267
- if (param) {
2268
- params.push(param)
2269
- }
2270
- } else {
2271
- break
2272
- }
2273
- this.match(TokenType.COMMA)
2274
- }
2275
- }
2276
-
2277
- let returnType = null
2278
- if (this.match(TokenType.COLON) || this.match(TokenType.ARROW)) {
2279
- returnType = this.parseType(lang)
2280
- }
2281
-
2282
- this.skipNewlines()
2283
- const body = this.parseBlock(lang)
2284
-
2285
- return {
2286
- type: 'FunctionDeclaration',
2287
- name: name,
2288
- params: params,
2289
- returnType: returnType,
2290
- body: body,
2291
- async: isAsync,
2292
- generator: isGenerator
2293
- }
2294
- }
2295
-
2296
- parseFunctionExpression(lang) {
2297
- this.advance()
2298
-
2299
- let name = null
2300
- const nameToken = this.current()
2301
- if (nameToken && nameToken.type === TokenType.IDENTIFIER && !this.check(TokenType.LPAREN)) {
2302
- const nextToken = this.peek(1)
2303
- if (nextToken && nextToken.type === TokenType.LPAREN) {
2304
- name = nameToken.value
2305
- this.advance()
2306
- }
2307
- }
2308
-
2309
- const params = []
2310
- if (this.match(TokenType.LPAREN)) {
2311
- while (!this.isAtEnd() && !this.match(TokenType.RPAREN)) {
2312
- while (this.match(TokenType.NEWLINE) || this.match(TokenType.INDENT) || this.match(TokenType.DEDENT)) {}
2313
- if (this.check(TokenType.RPAREN)) break
2314
- const param = this.parseParameter(lang)
2315
- if (param) {
2316
- params.push(param)
2317
- }
2318
- this.match(TokenType.COMMA)
2319
- while (this.match(TokenType.NEWLINE) || this.match(TokenType.INDENT) || this.match(TokenType.DEDENT)) {}
2320
- }
2321
- }
2322
-
2323
- let returnType = null
2324
- if (this.match(TokenType.COLON) || this.match(TokenType.ARROW)) {
2325
- returnType = this.parseType(lang)
2326
- }
2327
-
2328
- this.skipNewlines()
2329
- const body = this.parseBlock(lang)
2330
-
2331
- return {
2332
- type: 'FunctionExpression',
2333
- name: name,
2334
- params: params,
2335
- returnType: returnType,
2336
- body: body
2337
- }
2338
- }
2339
-
2340
- parseParameter(lang) {
2341
- const nameToken = this.current()
2342
- if (!nameToken || nameToken.type !== TokenType.IDENTIFIER) {
2343
- return null
2344
- }
2345
- const name = nameToken.value
2346
- this.advance()
2347
-
2348
- let typeAnnotation = null
2349
- if (this.match(TokenType.COLON)) {
2350
- typeAnnotation = this.parseType(lang)
2351
- }
2352
-
2353
- let defaultValue = null
2354
- if (this.match(TokenType.EQUALS)) {
2355
- defaultValue = this.parseExpression(lang)
2356
- }
2357
-
2358
- return {
2359
- type: 'Parameter',
2360
- name: name,
2361
- typeAnnotation: typeAnnotation,
2362
- default: defaultValue
2363
- }
2364
- }
2365
-
2366
- parseType(lang) {
2367
- const token = this.current()
2368
- if (!token) return null
2369
-
2370
- const typeName = this.translateType(token.value, lang)
2371
- this.advance()
2372
-
2373
- let generic = null
2374
- if (this.match(TokenType.LT)) {
2375
- generic = []
2376
- while (!this.isAtEnd() && !this.match(TokenType.GT)) {
2377
- const innerType = this.parseType(lang)
2378
- if (innerType) {
2379
- generic.push(innerType)
2380
- }
2381
- this.match(TokenType.COMMA)
2382
- }
2383
- }
2384
-
2385
- let isArray = false
2386
- if (this.match(TokenType.LBRACKET)) {
2387
- this.match(TokenType.RBRACKET)
2388
- isArray = true
2389
- }
2390
-
2391
- return {
2392
- type: 'TypeAnnotation',
2393
- name: typeName,
2394
- generic: generic,
2395
- isArray: isArray
2396
- }
2397
- }
2398
-
2399
- translateType(value, lang) {
2400
- const lower = value.toLowerCase()
2401
-
2402
- const types = {
2403
- 'chaine': 'string',
2404
- 'texte': 'string',
2405
- 'nombre': 'number',
2406
- 'entier': lang === 'ts' ? 'number' : 'int',
2407
- 'decimal': lang === 'ts' ? 'number' : 'float',
2408
- 'flottant': lang === 'ts' ? 'number' : 'float',
2409
- 'booleen': 'boolean',
2410
- 'bool': 'boolean',
2411
- 'vrai faux': 'boolean',
2412
- 'tableau': 'Array',
2413
- 'liste': lang === 'python' ? 'list' : 'Array',
2414
- 'objet': 'object',
2415
- 'dictionnaire': lang === 'python' ? 'dict' : 'object',
2416
- 'nul': 'null',
2417
- 'rien': 'void',
2418
- 'vide': 'void',
2419
- 'indefini': 'undefined',
2420
- 'quelconque': 'any',
2421
- 'jamais': 'never',
2422
- 'inconnu': 'unknown',
2423
- 'symbole': 'symbol',
2424
-
2425
- 'promesse': 'Promise',
2426
- 'fonction': 'Function'
2427
- }
2428
-
2429
- return types[lower] || value
2430
- }
2431
-
2432
- parseClassDeclaration(lang) {
2433
- this.advance()
2434
-
2435
- const nameToken = this.current()
2436
- if (!nameToken || nameToken.type !== TokenType.IDENTIFIER) {
2437
- return null
2438
- }
2439
- const name = nameToken.value
2440
- this.advance()
2441
-
2442
- let extends_ = null
2443
- if (this.matchValue('etend') || this.matchValue('herite') || this.matchValue('extends')) {
2444
- const parentToken = this.current()
2445
- if (parentToken) {
2446
- extends_ = parentToken.value
2447
- this.advance()
2448
- }
2449
- }
2450
-
2451
- let implements_ = []
2452
- if (this.matchValue('implemente') || this.matchValue('implements')) {
2453
- while (!this.isAtEnd()) {
2454
- const implToken = this.current()
2455
- if (!implToken || implToken.type !== TokenType.IDENTIFIER) break
2456
- implements_.push(implToken.value)
2457
- this.advance()
2458
- if (!this.match(TokenType.COMMA)) break
2459
- }
2460
- }
2461
-
2462
- this.skipNewlines()
2463
- const body = this.parseBlock(lang)
2464
-
2465
- return {
2466
- type: 'ClassDeclaration',
2467
- name: name,
2468
- extends: extends_,
2469
- implements: implements_,
2470
- body: body
2471
- }
2472
- }
2473
-
2474
- parseConditional(lang, isElseIf = false) {
2475
- if (!isElseIf) {
2476
- this.advance()
2477
- }
2478
- const condition = this.parseExpression(lang)
2479
-
2480
- this.skipNewlines()
2481
- const consequent = this.parseBlock(lang)
2482
-
2483
- let alternate = null
2484
- this.skipNewlines()
2485
-
2486
- if (this.matchValue('sinon')) {
2487
- if (this.matchValue('si')) {
2488
- alternate = this.parseConditional(lang, true)
2489
- } else {
2490
- this.skipNewlines()
2491
- alternate = this.parseBlock(lang)
2492
- }
2493
- }
2494
-
2495
- return {
2496
- type: 'IfStatement',
2497
- condition: condition,
2498
- consequent: consequent,
2499
- alternate: alternate
2500
- }
2501
- }
2502
-
2503
- parseLoop(lang) {
2504
- const keyword = this.advance()
2505
- const keywordValue = keyword.value.toLowerCase()
2506
-
2507
- if (keywordValue === 'pour' || keywordValue === 'for' || keywordValue === 'pour chaque' || keywordValue === 'chaque') {
2508
- return this.parseForLoop(lang)
2509
- }
2510
-
2511
- if (keywordValue === 'tant que' || keywordValue === 'while') {
2512
- return this.parseWhileLoop(lang)
2513
- }
2514
-
2515
- if (keywordValue === 'faire' || keywordValue === 'do') {
2516
- return this.parseDoWhileLoop(lang)
2517
- }
2518
-
2519
- return this.parseWhileLoop(lang)
2520
- }
2521
-
2522
- parseForLoop(lang) {
2523
- let variable = null
2524
- let iterable = null
2525
- let start = null
2526
- let end = null
2527
- let step = null
2528
-
2529
- const varToken = this.current()
2530
- if (varToken && varToken.type === TokenType.IDENTIFIER) {
2531
- variable = varToken.value
2532
- this.advance()
2533
- }
2534
-
2535
- if (this.matchValue('dans') || this.matchValue('de') || this.matchValue('in')) {
2536
- iterable = this.parseExpression(lang)
2537
- } else if (this.matchValue('de') || this.matchValue('from')) {
2538
- start = this.parseExpression(lang)
2539
-
2540
- if (this.matchValue('a') || this.matchValue('jusque') || this.matchValue('to')) {
2541
- end = this.parseExpression(lang)
2542
- }
2543
-
2544
- if (this.matchValue('par') || this.matchValue('step')) {
2545
- step = this.parseExpression(lang)
2546
- }
2547
- }
2548
-
2549
- this.skipNewlines()
2550
- const body = this.parseBlock(lang)
2551
-
2552
- return {
2553
- type: 'ForStatement',
2554
- variable: variable,
2555
- iterable: iterable,
2556
- start: start,
2557
- end: end,
2558
- step: step,
2559
- body: body
2560
- }
2561
- }
2562
-
2563
- parseWhileLoop(lang) {
2564
- const condition = this.parseExpression(lang)
2565
-
2566
- this.skipNewlines()
2567
- const body = this.parseBlock(lang)
2568
-
2569
- return {
2570
- type: 'WhileStatement',
2571
- condition: condition,
2572
- body: body
2573
- }
2574
- }
2575
-
2576
- parseDoWhileLoop(lang) {
2577
- this.skipNewlines()
2578
- const body = this.parseBlock(lang)
2579
-
2580
- this.skipNewlines()
2581
- if (this.matchValue('tant que') || this.matchValue('while')) {
2582
- const condition = this.parseExpression(lang)
2583
-
2584
- return {
2585
- type: 'DoWhileStatement',
2586
- condition: condition,
2587
- body: body
2588
- }
2589
- }
2590
-
2591
- return {
2592
- type: 'DoWhileStatement',
2593
- condition: { type: 'Literal', value: true },
2594
- body: body
2595
- }
2596
- }
2597
-
2598
- parseReturn(lang) {
2599
- this.advance()
2600
-
2601
- const value = this.parseExpression(lang)
2602
-
2603
- return {
2604
- type: 'ReturnStatement',
2605
- value: value
2606
- }
2607
- }
2608
-
2609
- parseImport(lang) {
2610
- const importToken = this.current()
2611
- const importKeyword = importToken ? importToken.value.toLowerCase() : ''
2612
- this.advance()
2613
-
2614
- const includeKeywords = ['inclure', 'requiert', 'include', 'require']
2615
-
2616
- if (this.current() && this.current().type === TokenType.COLON) {
2617
- this.advance()
2618
- }
2619
-
2620
- const nextToken = this.current()
2621
- if (includeKeywords.includes(importKeyword) && nextToken && nextToken.type === TokenType.STRING) {
2622
- const includePath = nextToken.value.replace(/^["']|["']$/g, '')
2623
- this.advance()
2624
-
2625
- if (includePath.endsWith('.eth') || includePath.endsWith('.ether') ||
2626
- !includePath.includes('.') || includePath.match(/\.(html|php|js|css)$/i)) {
2627
- return {
2628
- type: 'Include',
2629
- path: includePath,
2630
- resolved: false
2631
- }
2632
- }
2633
- }
2634
-
2635
- const imports = []
2636
- let source = null
2637
- let defaultImport = null
2638
-
2639
- const token = this.current()
2640
-
2641
- if (token && token.type === TokenType.LBRACE) {
2642
- this.advance()
2643
- while (!this.isAtEnd() && !this.match(TokenType.RBRACE)) {
2644
- const nameToken = this.current()
2645
- if (nameToken && nameToken.type === TokenType.IDENTIFIER) {
2646
- let imported = nameToken.value
2647
- let local = imported
2648
- this.advance()
2649
-
2650
- if (this.matchValue('comme') || this.matchValue('as')) {
2651
- const aliasToken = this.current()
2652
- if (aliasToken && aliasToken.type === TokenType.IDENTIFIER) {
2653
- local = aliasToken.value
2654
- this.advance()
2655
- }
2656
- }
2657
-
2658
- imports.push({ imported, local })
2659
- }
2660
- this.match(TokenType.COMMA)
2661
- }
2662
- } else if (token && token.type === TokenType.IDENTIFIER) {
2663
- defaultImport = token.value
2664
- this.advance()
2665
- } else if (token && token.type === TokenType.STRING) {
2666
- source = token.value.replace(/^["']|["']$/g, '')
2667
- this.advance()
2668
-
2669
- if (includeKeywords.includes(importKeyword)) {
2670
- if (source.endsWith('.eth') || source.endsWith('.ether') ||
2671
- !source.includes('.') || source.match(/\.(html|php|js|css)$/i)) {
2672
- return {
2673
- type: 'Include',
2674
- path: source,
2675
- resolved: false
2676
- }
2677
- }
2678
- }
2679
- }
2680
-
2681
- if (this.matchValue('depuis') || this.matchValue('de') || this.matchValue('from')) {
2682
- const sourceToken = this.current()
2683
- if (sourceToken && sourceToken.type === TokenType.STRING) {
2684
- source = sourceToken.value.replace(/^["']|["']$/g, '')
2685
- this.advance()
2686
- }
2687
- }
2688
-
2689
- return {
2690
- type: 'ImportDeclaration',
2691
- imports: imports,
2692
- defaultImport: defaultImport,
2693
- source: source
2694
- }
2695
- }
2696
-
2697
- parseExport(lang) {
2698
- this.advance()
2699
-
2700
- let isDefault = false
2701
- if (this.matchValue('defaut') || this.matchValue('default')) {
2702
- isDefault = true
2703
- }
2704
-
2705
- const declaration = this.parseStatement(lang)
2706
-
2707
- return {
2708
- type: 'ExportDeclaration',
2709
- isDefault: isDefault,
2710
- declaration: declaration
2711
- }
2712
- }
2713
-
2714
- parseBlock(lang) {
2715
- const body = []
2716
-
2717
- const hasBrace = this.match(TokenType.LBRACE)
2718
- this.skipNewlines()
2719
-
2720
- if (hasBrace) {
2721
- this.match(TokenType.INDENT)
2722
- }
2723
-
2724
- if (hasBrace || this.match(TokenType.INDENT)) {
2725
- while (!this.isAtEnd()) {
2726
- this.skipNewlines()
2727
-
2728
- const current = this.current()
2729
-
2730
- if (current && current.type === TokenType.DEDENT) {
2731
- this.advance()
2732
- if (hasBrace) {
2733
- this.skipNewlines()
2734
- this.match(TokenType.RBRACE)
2735
- }
2736
- break
2737
- }
2738
-
2739
- if (hasBrace && current && current.type === TokenType.RBRACE) {
2740
- this.advance()
2741
- break
2742
- }
2743
-
2744
- if (current && current.type === TokenType.COMMA) {
2745
- break
2746
- }
2747
-
2748
- if (current && current.type === TokenType.RPAREN) {
2749
- break
2750
- }
2751
-
2752
- if (current && (current.type === TokenType.INDENT || current.type === TokenType.NEWLINE)) {
2753
- this.advance()
2754
- continue
2755
- }
2756
-
2757
- const statement = this.parseStatement(lang)
2758
- if (statement && statement.expression?.value !== null) {
2759
- body.push(statement)
2760
- } else if (statement && statement.type !== 'ExpressionStatement') {
2761
- body.push(statement)
2762
- }
2763
-
2764
- this.skipNewlines()
2765
- }
2766
- }
2767
-
2768
- return {
2769
- type: 'BlockStatement',
2770
- body: body
2771
- }
2772
- }
2773
-
2774
- parseExpression(lang) {
2775
- return this.parseAssignment(lang)
2776
- }
2777
-
2778
- parseAssignment(lang) {
2779
- const left = this.parseLogicalOr(lang)
2780
-
2781
- if (this.match(TokenType.EQUALS)) {
2782
- const right = this.parseAssignment(lang)
2783
- return {
2784
- type: 'AssignmentExpression',
2785
- operator: '=',
2786
- left: left,
2787
- right: right
2788
- }
2789
- }
2790
-
2791
- return left
2792
- }
2793
-
2794
- parseLogicalOr(lang) {
2795
- let left = this.parseLogicalAnd(lang)
2796
-
2797
- while (this.match(TokenType.DOUBLE_PIPE) || this.matchValue('ou')) {
2798
- const right = this.parseLogicalAnd(lang)
2799
- left = {
2800
- type: 'BinaryExpression',
2801
- operator: '||',
2802
- left: left,
2803
- right: right
2804
- }
2805
- }
2806
-
2807
- return left
2808
- }
2809
-
2810
- parseLogicalAnd(lang) {
2811
- let left = this.parseEquality(lang)
2812
-
2813
- while (this.match(TokenType.DOUBLE_AMPERSAND) || this.matchValue('et')) {
2814
- const right = this.parseEquality(lang)
2815
- left = {
2816
- type: 'BinaryExpression',
2817
- operator: '&&',
2818
- left: left,
2819
- right: right
2820
- }
2821
- }
2822
-
2823
- return left
2824
- }
2825
-
2826
- parseEquality(lang) {
2827
- let left = this.parseComparison(lang)
2828
-
2829
- while (true) {
2830
- if (this.matchValue('strictement egal') || this.matchValue('strictement égal')) {
2831
- const right = this.parseComparison(lang)
2832
- left = { type: 'BinaryExpression', operator: '===', left, right }
2833
- } else if (this.matchValue('strictement different') || this.matchValue('strictement différent')) {
2834
- const right = this.parseComparison(lang)
2835
- left = { type: 'BinaryExpression', operator: '!==', left, right }
2836
- } else if (this.match(TokenType.DOUBLE_EQUALS) || this.matchValue('egal') || this.matchValue('egale') || this.matchValue('égal')) {
2837
- const right = this.parseComparison(lang)
2838
- left = { type: 'BinaryExpression', operator: '===', left, right }
2839
- } else if (this.match(TokenType.NOT_EQUALS) || this.matchValue('different') || this.matchValue('différent')) {
2840
- const right = this.parseComparison(lang)
2841
- left = { type: 'BinaryExpression', operator: '!==', left, right }
2842
- } else {
2843
- break
2844
- }
2845
- }
2846
-
2847
- return left
2848
- }
2849
-
2850
- parseComparison(lang) {
2851
- let left = this.parseAdditive(lang)
2852
-
2853
- while (true) {
2854
- if (this.matchValue('superieur ou egal') || this.matchValue('supérieur ou égal') || this.match(TokenType.GTE)) {
2855
- const right = this.parseAdditive(lang)
2856
- left = { type: 'BinaryExpression', operator: '>=', left, right }
2857
- } else if (this.matchValue('inferieur ou egal') || this.matchValue('inférieur ou égal') || this.match(TokenType.LTE)) {
2858
- const right = this.parseAdditive(lang)
2859
- left = { type: 'BinaryExpression', operator: '<=', left, right }
2860
- } else if (this.match(TokenType.LT) || this.matchValue('inferieur') || this.matchValue('inférieur')) {
2861
- const right = this.parseAdditive(lang)
2862
- left = { type: 'BinaryExpression', operator: '<', left, right }
2863
- } else if (this.match(TokenType.GT) || this.matchValue('superieur') || this.matchValue('supérieur')) {
2864
- const right = this.parseAdditive(lang)
2865
- left = { type: 'BinaryExpression', operator: '>', left, right }
2866
- } else if (this.matchValue('instance de') || this.matchValue('instanceof')) {
2867
- const right = this.parseAdditive(lang)
2868
- left = { type: 'BinaryExpression', operator: 'instanceof', left, right }
2869
- } else {
2870
- break
2871
- }
2872
- }
2873
-
2874
- return left
2875
- }
2876
-
2877
- parseAdditive(lang) {
2878
- let left = this.parseMultiplicative(lang)
2879
-
2880
- while (true) {
2881
- if (this.match(TokenType.PLUS) || this.matchValue('plus') || this.matchValue('addition')) {
2882
- const right = this.parseMultiplicative(lang)
2883
- left = { type: 'BinaryExpression', operator: '+', left, right }
2884
- } else if (this.match(TokenType.MINUS) || this.matchValue('moins') || this.matchValue('soustraction')) {
2885
- const right = this.parseMultiplicative(lang)
2886
- left = { type: 'BinaryExpression', operator: '-', left, right }
2887
- } else {
2888
- break
2889
- }
2890
- }
2891
-
2892
- return left
2893
- }
2894
-
2895
- parseMultiplicative(lang) {
2896
- let left = this.parseUnary(lang)
2897
-
2898
- while (true) {
2899
- if (this.match(TokenType.STAR) || this.matchValue('fois') || this.matchValue('multiplie') || this.matchValue('multiplication')) {
2900
- const right = this.parseUnary(lang)
2901
- left = { type: 'BinaryExpression', operator: '*', left, right }
2902
- } else if (this.match(TokenType.SLASH) || this.matchValue('divise') || this.matchValue('division')) {
2903
- const right = this.parseUnary(lang)
2904
- left = { type: 'BinaryExpression', operator: '/', left, right }
2905
- } else if (this.match(TokenType.PERCENT) || this.matchValue('modulo')) {
2906
- const right = this.parseUnary(lang)
2907
- left = { type: 'BinaryExpression', operator: '%', left, right }
2908
- } else if (this.match(TokenType.DOUBLE_STAR) || this.matchValue('puissance')) {
2909
- const right = this.parseUnary(lang)
2910
- left = { type: 'BinaryExpression', operator: '**', left, right }
2911
- } else {
2912
- break
2913
- }
2914
- }
2915
-
2916
- return left
2917
- }
2918
-
2919
- parseUnary(lang) {
2920
- if (this.match(TokenType.BANG) || this.matchValue('non') || this.matchValue('pas')) {
2921
- const operand = this.parseUnary(lang)
2922
- return { type: 'UnaryExpression', operator: '!', operand }
2923
- }
2924
-
2925
- if (this.match(TokenType.MINUS)) {
2926
- const operand = this.parseUnary(lang)
2927
- return { type: 'UnaryExpression', operator: '-', operand }
2928
- }
2929
-
2930
- if (this.matchValue('type de') || this.matchValue('typeof')) {
2931
- const operand = this.parseUnary(lang)
2932
- return { type: 'UnaryExpression', operator: 'typeof', operand }
2933
- }
2934
-
2935
- if (this.matchValue('nouveau') || this.matchValue('new') || this.matchValue('nouvelle')) {
2936
- const callee = this.parseCall(lang)
2937
- if (callee.type === 'CallExpression') {
2938
- return { type: 'NewExpression', callee: callee.callee, arguments: callee.arguments }
2939
- }
2940
- return { type: 'NewExpression', callee, arguments: [] }
2941
- }
2942
-
2943
- return this.parseCall(lang)
2944
- }
2945
-
2946
- parseCall(lang) {
2947
- let expr = this.parsePrimary(lang)
2948
-
2949
- while (true) {
2950
- if (this.match(TokenType.LPAREN)) {
2951
- const args = []
2952
- while (!this.isAtEnd() && !this.match(TokenType.RPAREN)) {
2953
- while (this.match(TokenType.NEWLINE) || this.match(TokenType.INDENT) || this.match(TokenType.DEDENT)) {}
2954
- if (this.check(TokenType.RPAREN)) break
2955
- args.push(this.parseExpression(lang))
2956
- this.match(TokenType.COMMA)
2957
- while (this.match(TokenType.NEWLINE) || this.match(TokenType.INDENT) || this.match(TokenType.DEDENT)) {}
2958
- }
2959
- expr = { type: 'CallExpression', callee: expr, arguments: args }
2960
- } else if (this.match(TokenType.DOT)) {
2961
- const property = this.current()
2962
- if (property && property.type === TokenType.IDENTIFIER) {
2963
- this.advance()
2964
- expr = { type: 'MemberExpression', object: expr, property: { type: 'Identifier', name: property.value } }
2965
- }
2966
- } else if (this.match(TokenType.LBRACKET)) {
2967
- const index = this.parseExpression(lang)
2968
- this.expect(TokenType.RBRACKET)
2969
- expr = { type: 'IndexExpression', object: expr, index }
2970
- } else {
2971
- break
2972
- }
2973
- }
2974
-
2975
- return expr
2976
- }
2977
-
2978
- parsePrimary(lang) {
2979
- const token = this.current()
2980
-
2981
- if (!token) {
2982
- return { type: 'Literal', value: null }
2983
- }
2984
-
2985
- if (token.type === TokenType.NUMBER || token.type === TokenType.INTEGER || token.type === TokenType.FLOAT) {
2986
- this.advance()
2987
- return { type: 'Literal', value: parseFloat(token.value) }
2988
- }
2989
-
2990
- if (token.type === TokenType.STRING) {
2991
- this.advance()
2992
- return { type: 'Literal', value: token.value.replace(/^["']|["']$/g, '') }
2993
- }
2994
-
2995
- if (token.type === TokenType.IDENTIFIER) {
2996
- const value = token.value.toLowerCase()
2997
-
2998
- if (value === 'fonction' || value === 'function') {
2999
- return this.parseFunctionExpression(lang)
3000
- }
3001
-
3002
- if (value === 'vrai' || value === 'true') {
3003
- this.advance()
3004
- return { type: 'Literal', value: true }
3005
- }
3006
-
3007
- if (value === 'faux' || value === 'false') {
3008
- this.advance()
3009
- return { type: 'Literal', value: false }
3010
- }
3011
-
3012
- if (value === 'nul' || value === 'null' || value === 'rien') {
3013
- this.advance()
3014
- return { type: 'Literal', value: null }
3015
- }
3016
-
3017
- if (value === 'indefini' || value === 'undefined') {
3018
- this.advance()
3019
- return { type: 'Literal', value: undefined }
3020
- }
3021
-
3022
- const translated = this.translateJSIdentifier(token.value)
3023
- this.advance()
3024
-
3025
- if (translated && translated.includes('.')) {
3026
- const parts = translated.split('.')
3027
- let expr = { type: 'Identifier', name: parts[0] }
3028
- for (let i = 1; i < parts.length; i++) {
3029
- expr = { type: 'MemberExpression', object: expr, property: { type: 'Identifier', name: parts[i] } }
3030
- }
3031
- return expr
3032
- }
3033
-
3034
- return { type: 'Identifier', name: translated || token.value }
3035
- }
3036
-
3037
- if (token.type === TokenType.LBRACKET) {
3038
- return this.parseArrayLiteral(lang)
3039
- }
3040
-
3041
- if (token.type === TokenType.LBRACE) {
3042
- return this.parseObjectLiteral(lang)
3043
- }
3044
-
3045
- if (token.type === TokenType.LPAREN) {
3046
- this.advance()
3047
- const expr = this.parseExpression(lang)
3048
- this.expect(TokenType.RPAREN)
3049
- expr.parenthesized = true
3050
- return expr
3051
- }
3052
-
3053
- this.advance()
3054
- return { type: 'Literal', value: null }
3055
- }
3056
-
3057
- parseArrayLiteral(lang) {
3058
- this.expect(TokenType.LBRACKET)
3059
- const elements = []
3060
-
3061
- while (!this.isAtEnd() && !this.match(TokenType.RBRACKET)) {
3062
- elements.push(this.parseExpression(lang))
3063
- this.match(TokenType.COMMA)
3064
- }
3065
-
3066
- return { type: 'ArrayExpression', elements }
3067
- }
3068
-
3069
- parseObjectLiteral(lang) {
3070
- this.expect(TokenType.LBRACE)
3071
- const properties = []
3072
-
3073
- this.skipWhitespace()
3074
-
3075
- while (!this.isAtEnd() && !this.check(TokenType.RBRACE)) {
3076
- this.skipWhitespace()
3077
-
3078
- const keyToken = this.current()
3079
- if (!keyToken || keyToken.type === TokenType.RBRACE) break
3080
-
3081
- let key
3082
- if (keyToken.type === TokenType.STRING) {
3083
- const keyValue = keyToken.value.replace(/^["']|["']$/g, '')
3084
- key = { type: 'Literal', value: keyValue }
3085
- this.advance()
3086
- } else if (keyToken.type === TokenType.IDENTIFIER) {
3087
- key = { type: 'Identifier', name: keyToken.value }
3088
- this.advance()
3089
- } else {
3090
- break
3091
- }
3092
-
3093
- this.match(TokenType.COLON) || this.match(TokenType.EQUALS)
3094
-
3095
- const value = this.parseExpression(lang)
3096
- properties.push({ key, value })
3097
-
3098
- this.match(TokenType.COMMA)
3099
- this.skipWhitespace()
3100
- }
3101
-
3102
- this.match(TokenType.RBRACE)
3103
-
3104
- return { type: 'ObjectExpression', properties }
3105
- }
3106
-
3107
- skipWhitespace() {
3108
- while (this.match(TokenType.NEWLINE) || this.match(TokenType.INDENT) || this.match(TokenType.DEDENT)) {}
3109
- }
3110
-
3111
- check(type) {
3112
- const token = this.current()
3113
- return token && token.type === type
3114
- }
3115
-
3116
- parseSQLProgram() {
3117
- const program = {
3118
- type: 'SQLProgram',
3119
- statements: []
3120
- }
3121
-
3122
- this.skipNewlines()
3123
-
3124
- while (!this.isAtEnd()) {
3125
- const statement = this.parseSQLStatement()
3126
- if (statement) {
3127
- program.statements.push(statement)
3128
- }
3129
- this.skipNewlines()
3130
- }
3131
-
3132
- return program
3133
- }
3134
-
3135
- parseSQLStatement() {
3136
- this.skipNewlines()
3137
- const token = this.current()
3138
-
3139
- if (!token || token.type === TokenType.EOF) return null
3140
-
3141
- const value = token.value ? token.value.toLowerCase() : ''
3142
-
3143
- if (value === 'selectionner' || value === 'select' || value === 'choisir') {
3144
- return this.parseSQLSelect()
3145
- }
3146
-
3147
- if (value === 'inserer' || value === 'insert' || value === 'ajouter') {
3148
- return this.parseSQLInsert()
3149
- }
3150
-
3151
- if (value === 'mettre' || value === 'update' || value === 'modifier') {
3152
- return this.parseSQLUpdate()
3153
- }
3154
-
3155
- if (value === 'supprimer' || value === 'delete' || value === 'effacer') {
3156
- return this.parseSQLDelete()
3157
- }
3158
-
3159
- if (value === 'creer' || value === 'create') {
3160
- return this.parseSQLCreate()
3161
- }
3162
-
3163
- if (value === 'detruire' || value === 'drop') {
3164
- return this.parseSQLDrop()
3165
- }
3166
-
3167
- if (value === 'modifier' || value === 'alter') {
3168
- return this.parseSQLAlter()
3169
- }
3170
-
3171
- this.advance()
3172
- return null
3173
- }
3174
-
3175
- parseSQLSelect() {
3176
- this.advance()
3177
-
3178
- const columns = []
3179
-
3180
- while (!this.isAtEnd()) {
3181
- const token = this.current()
3182
-
3183
- if (!token || token.type === TokenType.EOF) break
3184
-
3185
- const value = token.value ? token.value.toLowerCase() : ''
3186
- if (value === 'depuis' || value === 'de' || value === 'from') break
3187
-
3188
- if (token.type === TokenType.STAR) {
3189
- columns.push('*')
3190
- this.advance()
3191
- } else if (token.type === TokenType.IDENTIFIER) {
3192
- columns.push(token.value)
3193
- this.advance()
3194
- } else if (token.type === TokenType.COMMA) {
3195
- this.advance()
3196
- } else {
3197
- break
3198
- }
3199
- }
3200
-
3201
- let table = null
3202
- if (this.matchValue('depuis') || this.matchValue('de') || this.matchValue('from')) {
3203
- const tableToken = this.current()
3204
- if (tableToken && tableToken.type === TokenType.IDENTIFIER) {
3205
- table = tableToken.value
3206
- this.advance()
3207
- }
3208
- }
3209
-
3210
- let where = null
3211
- if (this.matchValue('ou') || this.matchValue('where')) {
3212
- where = this.parseSQLWhere()
3213
- }
3214
-
3215
- let orderBy = null
3216
- if (this.matchValue('trier') || this.matchValue('ordonner') || this.matchValue('order')) {
3217
- if (this.matchValue('par') || this.matchValue('by')) {
3218
- orderBy = this.parseSQLOrderBy()
3219
- }
3220
- }
3221
-
3222
- let limit = null
3223
- if (this.matchValue('limiter') || this.matchValue('limit')) {
3224
- const limitToken = this.current()
3225
- if (limitToken && (limitToken.type === TokenType.NUMBER || limitToken.type === TokenType.INTEGER)) {
3226
- limit = parseInt(limitToken.value)
3227
- this.advance()
3228
- }
3229
- }
3230
-
3231
- return {
3232
- type: 'SelectStatement',
3233
- columns: columns,
3234
- table: table,
3235
- where: where,
3236
- orderBy: orderBy,
3237
- limit: limit
3238
- }
3239
- }
3240
-
3241
- parseSQLWhere() {
3242
- const conditions = []
3243
-
3244
- while (!this.isAtEnd()) {
3245
- const token = this.current()
3246
-
3247
- if (!token || token.type === TokenType.EOF) break
3248
-
3249
- const value = token.value ? token.value.toLowerCase() : ''
3250
- if (value === 'trier' || value === 'ordonner' || value === 'order' ||
3251
- value === 'limiter' || value === 'limit' || value === 'grouper') break
3252
-
3253
- if (token.type === TokenType.IDENTIFIER) {
3254
- const field = token.value
3255
- this.advance()
3256
-
3257
- const opToken = this.current()
3258
- let operator = '='
3259
-
3260
- if (opToken) {
3261
- if (opToken.type === TokenType.EQUALS || opToken.type === TokenType.DOUBLE_EQUALS) {
3262
- operator = '='
3263
- this.advance()
3264
- } else if (opToken.type === TokenType.NOT_EQUALS) {
3265
- operator = '<>'
3266
- this.advance()
3267
- } else if (opToken.type === TokenType.LT) {
3268
- operator = '<'
3269
- this.advance()
3270
- } else if (opToken.type === TokenType.GT) {
3271
- operator = '>'
3272
- this.advance()
3273
- } else if (opToken.type === TokenType.LTE) {
3274
- operator = '<='
3275
- this.advance()
3276
- } else if (opToken.type === TokenType.GTE) {
3277
- operator = '>='
3278
- this.advance()
3279
- } else if (opToken.value && opToken.value.toLowerCase() === 'egal') {
3280
- operator = '='
3281
- this.advance()
3282
- }
3283
- }
3284
-
3285
- const valueToken = this.current()
3286
- let condValue = null
3287
-
3288
- if (valueToken) {
3289
- if (valueToken.type === TokenType.STRING) {
3290
- condValue = valueToken.value
3291
- } else if (valueToken.type === TokenType.NUMBER || valueToken.type === TokenType.INTEGER) {
3292
- condValue = parseFloat(valueToken.value)
3293
- } else if (valueToken.type === TokenType.IDENTIFIER) {
3294
- condValue = valueToken.value
3295
- }
3296
- this.advance()
3297
- }
3298
-
3299
- conditions.push({ field, operator, value: condValue })
3300
- } else if (value === 'et' || value === 'and') {
3301
- conditions.push({ type: 'AND' })
3302
- this.advance()
3303
- } else if (value === 'ou' || value === 'or') {
3304
- conditions.push({ type: 'OR' })
3305
- this.advance()
3306
- } else {
3307
- this.advance()
3308
- }
3309
- }
3310
-
3311
- return conditions
3312
- }
3313
-
3314
- parseSQLOrderBy() {
3315
- const orders = []
3316
-
3317
- while (!this.isAtEnd()) {
3318
- const token = this.current()
3319
-
3320
- if (!token || token.type === TokenType.EOF) break
3321
-
3322
- const value = token.value ? token.value.toLowerCase() : ''
3323
- if (value === 'limiter' || value === 'limit' || value === 'grouper') break
3324
-
3325
- if (token.type === TokenType.IDENTIFIER) {
3326
- const field = token.value
3327
- this.advance()
3328
-
3329
- let direction = 'ASC'
3330
- const dirToken = this.current()
3331
-
3332
- if (dirToken) {
3333
- const dirValue = dirToken.value ? dirToken.value.toLowerCase() : ''
3334
- if (dirValue === 'desc' || dirValue === 'descendant' || dirValue === 'decroissant') {
3335
- direction = 'DESC'
3336
- this.advance()
3337
- } else if (dirValue === 'asc' || dirValue === 'ascendant' || dirValue === 'croissant') {
3338
- direction = 'ASC'
3339
- this.advance()
3340
- }
3341
- }
3342
-
3343
- orders.push({ field, direction })
3344
- } else if (token.type === TokenType.COMMA) {
3345
- this.advance()
3346
- } else {
3347
- break
3348
- }
3349
- }
3350
-
3351
- return orders
3352
- }
3353
-
3354
- parseSQLInsert() {
3355
- this.advance()
3356
-
3357
- if (this.matchValue('dans') || this.matchValue('into') || this.matchValue('in')) {
3358
- }
3359
-
3360
- const tableToken = this.current()
3361
- let table = null
3362
- if (tableToken && tableToken.type === TokenType.IDENTIFIER) {
3363
- table = tableToken.value
3364
- this.advance()
3365
- }
3366
-
3367
- const columns = []
3368
- if (this.match(TokenType.LPAREN)) {
3369
- while (!this.isAtEnd() && !this.match(TokenType.RPAREN)) {
3370
- const colToken = this.current()
3371
- if (colToken && colToken.type === TokenType.IDENTIFIER) {
3372
- columns.push(colToken.value)
3373
- this.advance()
3374
- }
3375
- this.match(TokenType.COMMA)
3376
- }
3377
- }
3378
-
3379
- const values = []
3380
- if (this.matchValue('valeurs') || this.matchValue('values')) {
3381
- if (this.match(TokenType.LPAREN)) {
3382
- while (!this.isAtEnd() && !this.match(TokenType.RPAREN)) {
3383
- const valToken = this.current()
3384
- if (valToken) {
3385
- if (valToken.type === TokenType.STRING) {
3386
- values.push(valToken.value)
3387
- } else if (valToken.type === TokenType.NUMBER || valToken.type === TokenType.INTEGER) {
3388
- values.push(parseFloat(valToken.value))
3389
- } else if (valToken.type === TokenType.IDENTIFIER) {
3390
- const v = valToken.value.toLowerCase()
3391
- if (v === 'vrai' || v === 'true') values.push(true)
3392
- else if (v === 'faux' || v === 'false') values.push(false)
3393
- else if (v === 'nul' || v === 'null') values.push(null)
3394
- else values.push(valToken.value)
3395
- }
3396
- this.advance()
3397
- }
3398
- this.match(TokenType.COMMA)
3399
- }
3400
- }
3401
- }
3402
-
3403
- return {
3404
- type: 'InsertStatement',
3405
- table: table,
3406
- columns: columns,
3407
- values: values
3408
- }
3409
- }
3410
-
3411
- parseSQLUpdate() {
3412
- this.advance()
3413
-
3414
- if (this.matchValue('a jour') || this.matchValue('jour')) {
3415
- }
3416
-
3417
- const tableToken = this.current()
3418
- let table = null
3419
- if (tableToken && tableToken.type === TokenType.IDENTIFIER) {
3420
- table = tableToken.value
3421
- this.advance()
3422
- }
3423
-
3424
- const assignments = []
3425
- if (this.matchValue('definir') || this.matchValue('set') || this.matchValue('avec')) {
3426
- while (!this.isAtEnd()) {
3427
- const colToken = this.current()
3428
-
3429
- if (!colToken || colToken.type === TokenType.EOF) break
3430
-
3431
- const value = colToken.value ? colToken.value.toLowerCase() : ''
3432
- if (value === 'ou' || value === 'where') break
3433
-
3434
- if (colToken.type === TokenType.IDENTIFIER) {
3435
- const column = colToken.value
3436
- this.advance()
3437
-
3438
- if (this.match(TokenType.EQUALS) || this.match(TokenType.COLON)) {
3439
- const valToken = this.current()
3440
- let val = null
3441
-
3442
- if (valToken) {
3443
- if (valToken.type === TokenType.STRING) {
3444
- val = valToken.value
3445
- } else if (valToken.type === TokenType.NUMBER || valToken.type === TokenType.INTEGER) {
3446
- val = parseFloat(valToken.value)
3447
- } else if (valToken.type === TokenType.IDENTIFIER) {
3448
- val = valToken.value
3449
- }
3450
- this.advance()
3451
- }
3452
-
3453
- assignments.push({ column, value: val })
3454
- }
3455
- }
3456
-
3457
- this.match(TokenType.COMMA)
3458
- }
3459
- }
3460
-
3461
- let where = null
3462
- if (this.matchValue('ou') || this.matchValue('where')) {
3463
- where = this.parseSQLWhere()
3464
- }
3465
-
3466
- return {
3467
- type: 'UpdateStatement',
3468
- table: table,
3469
- assignments: assignments,
3470
- where: where
3471
- }
3472
- }
3473
-
3474
- parseSQLDelete() {
3475
- this.advance()
3476
-
3477
- if (this.matchValue('de') || this.matchValue('depuis') || this.matchValue('from')) {
3478
- }
3479
-
3480
- const tableToken = this.current()
3481
- let table = null
3482
- if (tableToken && tableToken.type === TokenType.IDENTIFIER) {
3483
- table = tableToken.value
3484
- this.advance()
3485
- }
3486
-
3487
- let where = null
3488
- if (this.matchValue('ou') || this.matchValue('where')) {
3489
- where = this.parseSQLWhere()
3490
- }
3491
-
3492
- return {
3493
- type: 'DeleteStatement',
3494
- table: table,
3495
- where: where
3496
- }
3497
- }
3498
-
3499
- parseSQLCreate() {
3500
- this.advance()
3501
-
3502
- if (this.matchValue('table')) {
3503
- return this.parseSQLCreateTable()
3504
- }
3505
-
3506
- if (this.matchValue('base') || this.matchValue('database')) {
3507
- this.matchValue('de')
3508
- this.matchValue('donnees')
3509
- return this.parseSQLCreateDatabase()
3510
- }
3511
-
3512
- if (this.matchValue('index')) {
3513
- return this.parseSQLCreateIndex()
3514
- }
3515
-
3516
- return null
3517
- }
3518
-
3519
- parseSQLCreateTable() {
3520
- const nameToken = this.current()
3521
- let name = null
3522
- if (nameToken && nameToken.type === TokenType.IDENTIFIER) {
3523
- name = nameToken.value
3524
- this.advance()
3525
- }
3526
-
3527
- const columns = []
3528
-
3529
- if (this.match(TokenType.LPAREN)) {
3530
- while (!this.isAtEnd() && !this.match(TokenType.RPAREN)) {
3531
- const col = this.parseSQLColumnDef()
3532
- if (col) {
3533
- columns.push(col)
3534
- }
3535
- this.match(TokenType.COMMA)
3536
- }
3537
- } else {
3538
- this.skipNewlines()
3539
- this.match(TokenType.INDENT)
3540
-
3541
- while (!this.isAtEnd()) {
3542
- const current = this.current()
3543
-
3544
- if (current && current.type === TokenType.DEDENT) {
3545
- this.advance()
3546
- break
3547
- }
3548
-
3549
- if (current && current.type === TokenType.NEWLINE) {
3550
- this.advance()
3551
- continue
3552
- }
3553
-
3554
- const col = this.parseSQLColumnDef()
3555
- if (col) {
3556
- columns.push(col)
3557
- }
3558
- }
3559
- }
3560
-
3561
- return {
3562
- type: 'CreateTableStatement',
3563
- name: name,
3564
- columns: columns
3565
- }
3566
- }
3567
-
3568
- parseSQLColumnDef() {
3569
- const nameToken = this.current()
3570
- if (!nameToken || nameToken.type !== TokenType.IDENTIFIER) {
3571
- return null
3572
- }
3573
- const name = nameToken.value
3574
- this.advance()
3575
-
3576
- this.match(TokenType.COLON)
3577
-
3578
- const typeToken = this.current()
3579
- let dataType = 'TEXT'
3580
- if (typeToken && typeToken.type === TokenType.IDENTIFIER) {
3581
- dataType = this.translateSQLType(typeToken.value)
3582
- this.advance()
3583
- }
3584
-
3585
- let size = null
3586
- if (this.match(TokenType.LPAREN)) {
3587
- const sizeToken = this.current()
3588
- if (sizeToken && (sizeToken.type === TokenType.NUMBER || sizeToken.type === TokenType.INTEGER)) {
3589
- size = parseInt(sizeToken.value)
3590
- this.advance()
3591
- }
3592
- this.match(TokenType.RPAREN)
3593
- }
3594
-
3595
- const constraints = []
3596
-
3597
- while (!this.isAtEnd()) {
3598
- const token = this.current()
3599
-
3600
- if (!token || token.type === TokenType.COMMA || token.type === TokenType.RPAREN ||
3601
- token.type === TokenType.NEWLINE || token.type === TokenType.DEDENT) {
3602
- break
3603
- }
3604
-
3605
- const value = token.value ? token.value.toLowerCase() : ''
3606
-
3607
- if (value === 'cle' || value === 'key') {
3608
- this.advance()
3609
- if (this.matchValue('primaire') || this.matchValue('primary')) {
3610
- constraints.push('PRIMARY KEY')
3611
- }
3612
- } else if (value === 'primaire' || value === 'primary') {
3613
- this.advance()
3614
- constraints.push('PRIMARY KEY')
3615
- } else if (value === 'unique') {
3616
- this.advance()
3617
- constraints.push('UNIQUE')
3618
- } else if (value === 'non' || value === 'not') {
3619
- this.advance()
3620
- if (this.matchValue('nul') || this.matchValue('null')) {
3621
- constraints.push('NOT NULL')
3622
- }
3623
- } else if (value === 'auto' || value === 'autoincrement') {
3624
- this.advance()
3625
- if (this.matchValue('increment')) {
3626
- }
3627
- constraints.push('AUTO_INCREMENT')
3628
- } else if (value === 'defaut' || value === 'default') {
3629
- this.advance()
3630
- const defToken = this.current()
3631
- if (defToken) {
3632
- constraints.push(`DEFAULT ${defToken.value}`)
3633
- this.advance()
3634
- }
3635
- } else if (value === 'reference' || value === 'references') {
3636
- this.advance()
3637
- const refTable = this.current()
3638
- if (refTable && refTable.type === TokenType.IDENTIFIER) {
3639
- let ref = `REFERENCES ${refTable.value}`
3640
- this.advance()
3641
- if (this.match(TokenType.LPAREN)) {
3642
- const refCol = this.current()
3643
- if (refCol) {
3644
- ref += `(${refCol.value})`
3645
- this.advance()
3646
- }
3647
- this.match(TokenType.RPAREN)
3648
- }
3649
- constraints.push(ref)
3650
- }
3651
- } else {
3652
- this.advance()
3653
- }
3654
- }
3655
-
3656
- return {
3657
- name: name,
3658
- type: dataType,
3659
- size: size,
3660
- constraints: constraints
3661
- }
3662
- }
3663
-
3664
- translateSQLType(value) {
3665
- const lower = value.toLowerCase()
3666
-
3667
- const types = {
3668
- 'entier': 'INT',
3669
- 'nombre': 'INT',
3670
- 'texte': 'TEXT',
3671
- 'chaine': 'VARCHAR',
3672
- 'caracteres': 'VARCHAR',
3673
- 'decimal': 'DECIMAL',
3674
- 'flottant': 'FLOAT',
3675
- 'double': 'DOUBLE',
3676
- 'booleen': 'BOOLEAN',
3677
- 'date': 'DATE',
3678
- 'heure': 'TIME',
3679
- 'horodatage': 'TIMESTAMP',
3680
- 'dateheure': 'DATETIME',
3681
- 'blob': 'BLOB',
3682
- 'binaire': 'BINARY',
3683
- 'json': 'JSON',
3684
- 'uuid': 'UUID',
3685
- 'petit entier': 'SMALLINT',
3686
- 'grand entier': 'BIGINT',
3687
- 'tres petit entier': 'TINYINT'
3688
- }
3689
-
3690
- return types[lower] || value.toUpperCase()
3691
- }
3692
-
3693
- parseSQLCreateDatabase() {
3694
- const nameToken = this.current()
3695
- let name = null
3696
- if (nameToken && nameToken.type === TokenType.IDENTIFIER) {
3697
- name = nameToken.value
3698
- this.advance()
3699
- }
3700
-
3701
- return {
3702
- type: 'CreateDatabaseStatement',
3703
- name: name
3704
- }
3705
- }
3706
-
3707
- parseSQLCreateIndex() {
3708
- let unique = false
3709
- if (this.matchValue('unique')) {
3710
- unique = true
3711
- }
3712
-
3713
- const nameToken = this.current()
3714
- let name = null
3715
- if (nameToken && nameToken.type === TokenType.IDENTIFIER) {
3716
- name = nameToken.value
3717
- this.advance()
3718
- }
3719
-
3720
- if (this.matchValue('sur') || this.matchValue('on')) {
3721
- }
3722
-
3723
- const tableToken = this.current()
3724
- let table = null
3725
- if (tableToken && tableToken.type === TokenType.IDENTIFIER) {
3726
- table = tableToken.value
3727
- this.advance()
3728
- }
3729
-
3730
- const columns = []
3731
- if (this.match(TokenType.LPAREN)) {
3732
- while (!this.isAtEnd() && !this.match(TokenType.RPAREN)) {
3733
- const colToken = this.current()
3734
- if (colToken && colToken.type === TokenType.IDENTIFIER) {
3735
- columns.push(colToken.value)
3736
- this.advance()
3737
- }
3738
- this.match(TokenType.COMMA)
3739
- }
3740
- }
3741
-
3742
- return {
3743
- type: 'CreateIndexStatement',
3744
- name: name,
3745
- table: table,
3746
- columns: columns,
3747
- unique: unique
3748
- }
3749
- }
3750
-
3751
- parseSQLDrop() {
3752
- this.advance()
3753
-
3754
- if (this.matchValue('table')) {
3755
- const nameToken = this.current()
3756
- let name = null
3757
- if (nameToken && nameToken.type === TokenType.IDENTIFIER) {
3758
- name = nameToken.value
3759
- this.advance()
3760
- }
3761
- return { type: 'DropTableStatement', name }
3762
- }
3763
-
3764
- if (this.matchValue('base') || this.matchValue('database')) {
3765
- this.matchValue('de')
3766
- this.matchValue('donnees')
3767
- const nameToken = this.current()
3768
- let name = null
3769
- if (nameToken && nameToken.type === TokenType.IDENTIFIER) {
3770
- name = nameToken.value
3771
- this.advance()
3772
- }
3773
- return { type: 'DropDatabaseStatement', name }
3774
- }
3775
-
3776
- if (this.matchValue('index')) {
3777
- const nameToken = this.current()
3778
- let name = null
3779
- if (nameToken && nameToken.type === TokenType.IDENTIFIER) {
3780
- name = nameToken.value
3781
- this.advance()
3782
- }
3783
- return { type: 'DropIndexStatement', name }
3784
- }
3785
-
3786
- return null
3787
- }
3788
-
3789
- parseSQLAlter() {
3790
- this.advance()
3791
-
3792
- if (this.matchValue('table')) {
3793
- const nameToken = this.current()
3794
- let name = null
3795
- if (nameToken && nameToken.type === TokenType.IDENTIFIER) {
3796
- name = nameToken.value
3797
- this.advance()
3798
- }
3799
-
3800
- const actions = []
3801
-
3802
- if (this.matchValue('ajouter') || this.matchValue('add')) {
3803
- if (this.matchValue('colonne') || this.matchValue('column')) {
3804
- }
3805
- const col = this.parseSQLColumnDef()
3806
- if (col) {
3807
- actions.push({ type: 'ADD', column: col })
3808
- }
3809
- }
3810
-
3811
- if (this.matchValue('supprimer') || this.matchValue('drop')) {
3812
- if (this.matchValue('colonne') || this.matchValue('column')) {
3813
- }
3814
- const colToken = this.current()
3815
- if (colToken && colToken.type === TokenType.IDENTIFIER) {
3816
- actions.push({ type: 'DROP', column: colToken.value })
3817
- this.advance()
3818
- }
3819
- }
3820
-
3821
- if (this.matchValue('modifier') || this.matchValue('modify')) {
3822
- if (this.matchValue('colonne') || this.matchValue('column')) {
3823
- }
3824
- const col = this.parseSQLColumnDef()
3825
- if (col) {
3826
- actions.push({ type: 'MODIFY', column: col })
3827
- }
3828
- }
3829
-
3830
- return {
3831
- type: 'AlterTableStatement',
3832
- name: name,
3833
- actions: actions
3834
- }
3835
- }
3836
-
3837
- return null
3838
- }
3839
-
3840
- parseGraphQLProgram() {
3841
- const program = {
3842
- type: 'GraphQLProgram',
3843
- definitions: []
3844
- }
3845
-
3846
- this.skipNewlines()
3847
-
3848
- while (!this.isAtEnd()) {
3849
- const def = this.parseGraphQLDefinition()
3850
- if (def) {
3851
- program.definitions.push(def)
3852
- }
3853
- this.skipNewlines()
3854
- }
3855
-
3856
- return program
3857
- }
3858
-
3859
- parseGraphQLDefinition() {
3860
- this.skipNewlines()
3861
- const token = this.current()
3862
-
3863
- if (!token || token.type === TokenType.EOF) return null
3864
-
3865
- const value = token.value ? token.value.toLowerCase() : ''
3866
-
3867
- if (value === 'type' || value === 'modele') {
3868
- return this.parseGraphQLType()
3869
- }
3870
-
3871
- if (value === 'input' || value === 'entree') {
3872
- return this.parseGraphQLInput()
3873
- }
3874
-
3875
- if (value === 'enum' || value === 'enumeration') {
3876
- return this.parseGraphQLEnum()
3877
- }
3878
-
3879
- if (value === 'interface') {
3880
- return this.parseGraphQLInterface()
3881
- }
3882
-
3883
- if (value === 'query' || value === 'requete') {
3884
- return this.parseGraphQLQuery()
3885
- }
3886
-
3887
- if (value === 'mutation') {
3888
- return this.parseGraphQLMutation()
3889
- }
3890
-
3891
- if (value === 'subscription' || value === 'abonnement') {
3892
- return this.parseGraphQLSubscription()
3893
- }
3894
-
3895
- if (value === 'schema') {
3896
- return this.parseGraphQLSchema()
3897
- }
3898
-
3899
- this.advance()
3900
- return null
3901
- }
3902
-
3903
- parseGraphQLType() {
3904
- this.advance()
3905
-
3906
- const nameToken = this.current()
3907
- let name = null
3908
- if (nameToken && nameToken.type === TokenType.IDENTIFIER) {
3909
- name = nameToken.value
3910
- this.advance()
3911
- }
3912
-
3913
- let implements_ = []
3914
- if (this.matchValue('implemente') || this.matchValue('implements')) {
3915
- while (!this.isAtEnd()) {
3916
- const implToken = this.current()
3917
- if (!implToken || implToken.type !== TokenType.IDENTIFIER) break
3918
-
3919
- const v = implToken.value.toLowerCase()
3920
- if (v === '{' || this.peek()?.type === TokenType.LBRACE) break
3921
-
3922
- implements_.push(implToken.value)
3923
- this.advance()
3924
-
3925
- if (!this.match(TokenType.AMPERSAND) && !this.match(TokenType.COMMA)) break
3926
- }
3927
- }
3928
-
3929
- this.skipNewlines()
3930
- const fields = this.parseGraphQLFields()
3931
-
3932
- return {
3933
- type: 'TypeDefinition',
3934
- name: name,
3935
- implements: implements_,
3936
- fields: fields
3937
- }
3938
- }
3939
-
3940
- parseGraphQLInput() {
3941
- this.advance()
3942
-
3943
- const nameToken = this.current()
3944
- let name = null
3945
- if (nameToken && nameToken.type === TokenType.IDENTIFIER) {
3946
- name = nameToken.value
3947
- this.advance()
3948
- }
3949
-
3950
- this.skipNewlines()
3951
- const fields = this.parseGraphQLFields()
3952
-
3953
- return {
3954
- type: 'InputDefinition',
3955
- name: name,
3956
- fields: fields
3957
- }
3958
- }
3959
-
3960
- parseGraphQLEnum() {
3961
- this.advance()
3962
-
3963
- const nameToken = this.current()
3964
- let name = null
3965
- if (nameToken && nameToken.type === TokenType.IDENTIFIER) {
3966
- name = nameToken.value
3967
- this.advance()
3968
- }
3969
-
3970
- const values = []
3971
-
3972
- if (this.match(TokenType.LBRACE)) {
3973
- while (!this.isAtEnd() && !this.match(TokenType.RBRACE)) {
3974
- this.skipNewlines()
3975
- const valToken = this.current()
3976
- if (valToken && valToken.type === TokenType.IDENTIFIER) {
3977
- values.push(valToken.value)
3978
- this.advance()
3979
- }
3980
- this.skipNewlines()
3981
- }
3982
- } else {
3983
- this.skipNewlines()
3984
- this.match(TokenType.INDENT)
3985
-
3986
- while (!this.isAtEnd()) {
3987
- const current = this.current()
3988
-
3989
- if (current && current.type === TokenType.DEDENT) {
3990
- this.advance()
3991
- break
3992
- }
3993
-
3994
- if (current && current.type === TokenType.NEWLINE) {
3995
- this.advance()
3996
- continue
3997
- }
3998
-
3999
- if (current && current.type === TokenType.IDENTIFIER) {
4000
- values.push(current.value)
4001
- this.advance()
4002
- }
4003
- }
4004
- }
4005
-
4006
- return {
4007
- type: 'EnumDefinition',
4008
- name: name,
4009
- values: values
4010
- }
4011
- }
4012
-
4013
- parseGraphQLInterface() {
4014
- this.advance()
4015
-
4016
- const nameToken = this.current()
4017
- let name = null
4018
- if (nameToken && nameToken.type === TokenType.IDENTIFIER) {
4019
- name = nameToken.value
4020
- this.advance()
4021
- }
4022
-
4023
- this.skipNewlines()
4024
- const fields = this.parseGraphQLFields()
4025
-
4026
- return {
4027
- type: 'InterfaceDefinition',
4028
- name: name,
4029
- fields: fields
4030
- }
4031
- }
4032
-
4033
- parseGraphQLFields() {
4034
- const fields = []
4035
-
4036
- if (this.match(TokenType.LBRACE)) {
4037
- while (!this.isAtEnd() && !this.match(TokenType.RBRACE)) {
4038
- this.skipNewlines()
4039
- const field = this.parseGraphQLField()
4040
- if (field) {
4041
- fields.push(field)
4042
- }
4043
- this.skipNewlines()
4044
- }
4045
- } else {
4046
- this.skipNewlines()
4047
- this.match(TokenType.INDENT)
4048
-
4049
- while (!this.isAtEnd()) {
4050
- const current = this.current()
4051
-
4052
- if (current && current.type === TokenType.DEDENT) {
4053
- this.advance()
4054
- break
4055
- }
4056
-
4057
- if (current && current.type === TokenType.NEWLINE) {
4058
- this.advance()
4059
- continue
4060
- }
4061
-
4062
- const field = this.parseGraphQLField()
4063
- if (field) {
4064
- fields.push(field)
4065
- }
4066
- }
4067
- }
4068
-
4069
- return fields
4070
- }
4071
-
4072
- parseGraphQLField() {
4073
- const nameToken = this.current()
4074
- if (!nameToken || nameToken.type !== TokenType.IDENTIFIER) {
4075
- return null
4076
- }
4077
- const name = nameToken.value
4078
- this.advance()
4079
-
4080
- const args = []
4081
- if (this.match(TokenType.LPAREN)) {
4082
- while (!this.isAtEnd() && !this.match(TokenType.RPAREN)) {
4083
- const arg = this.parseGraphQLArg()
4084
- if (arg) {
4085
- args.push(arg)
4086
- }
4087
- this.match(TokenType.COMMA)
4088
- }
4089
- }
4090
-
4091
- this.match(TokenType.COLON)
4092
-
4093
- const fieldType = this.parseGraphQLTypeRef()
4094
-
4095
- const directives = []
4096
- while (this.match(TokenType.AT)) {
4097
- const dirToken = this.current()
4098
- if (dirToken && dirToken.type === TokenType.IDENTIFIER) {
4099
- directives.push(dirToken.value)
4100
- this.advance()
4101
- }
4102
- }
4103
-
4104
- return {
4105
- name: name,
4106
- args: args,
4107
- type: fieldType,
4108
- directives: directives
4109
- }
4110
- }
4111
-
4112
- parseGraphQLArg() {
4113
- const nameToken = this.current()
4114
- if (!nameToken || nameToken.type !== TokenType.IDENTIFIER) {
4115
- return null
4116
- }
4117
- const name = nameToken.value
4118
- this.advance()
4119
-
4120
- this.match(TokenType.COLON)
4121
-
4122
- const argType = this.parseGraphQLTypeRef()
4123
-
4124
- let defaultValue = null
4125
- if (this.match(TokenType.EQUALS)) {
4126
- const valToken = this.current()
4127
- if (valToken) {
4128
- if (valToken.type === TokenType.STRING) {
4129
- defaultValue = valToken.value
4130
- } else if (valToken.type === TokenType.NUMBER || valToken.type === TokenType.INTEGER) {
4131
- defaultValue = parseFloat(valToken.value)
4132
- } else if (valToken.type === TokenType.IDENTIFIER) {
4133
- defaultValue = valToken.value
4134
- }
4135
- this.advance()
4136
- }
4137
- }
4138
-
4139
- return {
4140
- name: name,
4141
- type: argType,
4142
- default: defaultValue
4143
- }
4144
- }
4145
-
4146
- parseGraphQLTypeRef() {
4147
- let isArray = false
4148
-
4149
- if (this.match(TokenType.LBRACKET)) {
4150
- isArray = true
4151
- }
4152
-
4153
- const typeToken = this.current()
4154
- let typeName = 'String'
4155
- if (typeToken && typeToken.type === TokenType.IDENTIFIER) {
4156
- typeName = this.translateGraphQLType(typeToken.value)
4157
- this.advance()
4158
- }
4159
-
4160
- let isRequired = false
4161
- if (this.match(TokenType.BANG)) {
4162
- isRequired = true
4163
- }
4164
-
4165
- if (isArray) {
4166
- this.match(TokenType.RBRACKET)
4167
- if (this.match(TokenType.BANG)) {
4168
- isRequired = true
4169
- }
4170
- }
4171
-
4172
- return {
4173
- name: typeName,
4174
- isArray: isArray,
4175
- isRequired: isRequired
4176
- }
4177
- }
4178
-
4179
- translateGraphQLType(value) {
4180
- const lower = value.toLowerCase()
4181
-
4182
- const types = {
4183
- 'chaine': 'String',
4184
- 'texte': 'String',
4185
- 'entier': 'Int',
4186
- 'nombre': 'Int',
4187
- 'flottant': 'Float',
4188
- 'decimal': 'Float',
4189
- 'booleen': 'Boolean',
4190
- 'identifiant': 'ID',
4191
- 'id': 'ID'
4192
- }
4193
-
4194
- return types[lower] || value
4195
- }
4196
-
4197
- parseGraphQLQuery() {
4198
- this.advance()
4199
-
4200
- const nameToken = this.current()
4201
- let name = null
4202
- if (nameToken && nameToken.type === TokenType.IDENTIFIER) {
4203
- name = nameToken.value
4204
- this.advance()
4205
- }
4206
-
4207
- const args = []
4208
- if (this.match(TokenType.LPAREN)) {
4209
- while (!this.isAtEnd() && !this.match(TokenType.RPAREN)) {
4210
- const arg = this.parseGraphQLArg()
4211
- if (arg) {
4212
- args.push(arg)
4213
- }
4214
- this.match(TokenType.COMMA)
4215
- }
4216
- }
4217
-
4218
- this.match(TokenType.COLON)
4219
- const returnType = this.parseGraphQLTypeRef()
4220
-
4221
- return {
4222
- type: 'QueryDefinition',
4223
- name: name,
4224
- args: args,
4225
- returnType: returnType
4226
- }
4227
- }
4228
-
4229
- parseGraphQLMutation() {
4230
- this.advance()
4231
-
4232
- const nameToken = this.current()
4233
- let name = null
4234
- if (nameToken && nameToken.type === TokenType.IDENTIFIER) {
4235
- name = nameToken.value
4236
- this.advance()
4237
- }
4238
-
4239
- const args = []
4240
- if (this.match(TokenType.LPAREN)) {
4241
- while (!this.isAtEnd() && !this.match(TokenType.RPAREN)) {
4242
- const arg = this.parseGraphQLArg()
4243
- if (arg) {
4244
- args.push(arg)
4245
- }
4246
- this.match(TokenType.COMMA)
4247
- }
4248
- }
4249
-
4250
- this.match(TokenType.COLON)
4251
- const returnType = this.parseGraphQLTypeRef()
4252
-
4253
- return {
4254
- type: 'MutationDefinition',
4255
- name: name,
4256
- args: args,
4257
- returnType: returnType
4258
- }
4259
- }
4260
-
4261
- parseGraphQLSubscription() {
4262
- this.advance()
4263
-
4264
- const nameToken = this.current()
4265
- let name = null
4266
- if (nameToken && nameToken.type === TokenType.IDENTIFIER) {
4267
- name = nameToken.value
4268
- this.advance()
4269
- }
4270
-
4271
- const args = []
4272
- if (this.match(TokenType.LPAREN)) {
4273
- while (!this.isAtEnd() && !this.match(TokenType.RPAREN)) {
4274
- const arg = this.parseGraphQLArg()
4275
- if (arg) {
4276
- args.push(arg)
4277
- }
4278
- this.match(TokenType.COMMA)
4279
- }
4280
- }
4281
-
4282
- this.match(TokenType.COLON)
4283
- const returnType = this.parseGraphQLTypeRef()
4284
-
4285
- return {
4286
- type: 'SubscriptionDefinition',
4287
- name: name,
4288
- args: args,
4289
- returnType: returnType
4290
- }
4291
- }
4292
-
4293
- parseGraphQLSchema() {
4294
- this.advance()
4295
-
4296
- const operations = {}
4297
-
4298
- if (this.match(TokenType.LBRACE)) {
4299
- while (!this.isAtEnd() && !this.match(TokenType.RBRACE)) {
4300
- this.skipNewlines()
4301
- const opToken = this.current()
4302
-
4303
- if (opToken && opToken.type === TokenType.IDENTIFIER) {
4304
- const op = opToken.value.toLowerCase()
4305
- this.advance()
4306
-
4307
- this.match(TokenType.COLON)
4308
-
4309
- const typeToken = this.current()
4310
- if (typeToken && typeToken.type === TokenType.IDENTIFIER) {
4311
- if (op === 'query' || op === 'requete') {
4312
- operations.query = typeToken.value
4313
- } else if (op === 'mutation') {
4314
- operations.mutation = typeToken.value
4315
- } else if (op === 'subscription' || op === 'abonnement') {
4316
- operations.subscription = typeToken.value
4317
- }
4318
- this.advance()
4319
- }
4320
- }
4321
-
4322
- this.skipNewlines()
4323
- }
4324
- }
4325
-
4326
- return {
4327
- type: 'SchemaDefinition',
4328
- operations: operations
4329
- }
4330
- }
4331
- }
4332
-
4333
- module.exports = {
4334
- EtherParser
4335
- }