ether-code 0.4.7 → 0.4.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/cli/ether.js CHANGED
@@ -6,7 +6,7 @@ const http = require('http')
6
6
  const { EtherCompiler } = require('./compiler')
7
7
  const { Watcher } = require('./watcher')
8
8
 
9
- const VERSION = '0.4.7'
9
+ const VERSION = '0.4.9'
10
10
 
11
11
  const COLORS = {
12
12
  reset: '\x1b[0m',
package/ether-parser.js CHANGED
@@ -118,9 +118,9 @@ class EtherParser {
118
118
  'absolu': 'abs',
119
119
  'maximum': 'max',
120
120
  'minimum': 'min',
121
- 'date': 'Date',
121
+
122
122
  'maintenant': 'now',
123
- 'carte': 'Map',
123
+
124
124
  'ensemble': 'Set',
125
125
  'symbole': 'Symbol',
126
126
  'objet': 'Object',
@@ -297,9 +297,36 @@ class EtherParser {
297
297
  const token = this.current()
298
298
  if (!token || token.value === null || token.value === undefined) return null
299
299
  const tokenVal = String(token.value).toLowerCase()
300
- if (tokenVal === value.toLowerCase()) {
300
+ const valueLower = value.toLowerCase()
301
+
302
+ if (tokenVal === valueLower) {
301
303
  return this.advance()
302
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
+
303
330
  return null
304
331
  }
305
332
 
@@ -314,6 +341,27 @@ class EtherParser {
314
341
  detectTargetLanguage(source) {
315
342
  const lower = source.toLowerCase()
316
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
+
317
365
  if (/^\s*(document|page|<!doctype|<html|tete|corps|entete|navigation|section|article)/m.test(lower)) {
318
366
  return 'html'
319
367
  }
@@ -1250,7 +1298,7 @@ class EtherParser {
1250
1298
  'cesure': 'wbr',
1251
1299
  'noscript': 'noscript',
1252
1300
  'sans script': 'noscript',
1253
- 'carte': 'map',
1301
+
1254
1302
  'zone': 'area'
1255
1303
  }
1256
1304
 
@@ -1506,6 +1554,47 @@ class EtherParser {
1506
1554
  }
1507
1555
  }
1508
1556
 
1557
+ parseFunctionExpression(lang) {
1558
+ this.advance()
1559
+
1560
+ let name = null
1561
+ const nameToken = this.current()
1562
+ if (nameToken && nameToken.type === TokenType.IDENTIFIER && !this.check(TokenType.LPAREN)) {
1563
+ const nextToken = this.peek(1)
1564
+ if (nextToken && nextToken.type === TokenType.LPAREN) {
1565
+ name = nameToken.value
1566
+ this.advance()
1567
+ }
1568
+ }
1569
+
1570
+ const params = []
1571
+ if (this.match(TokenType.LPAREN)) {
1572
+ while (!this.isAtEnd() && !this.match(TokenType.RPAREN)) {
1573
+ const param = this.parseParameter(lang)
1574
+ if (param) {
1575
+ params.push(param)
1576
+ }
1577
+ this.match(TokenType.COMMA)
1578
+ }
1579
+ }
1580
+
1581
+ let returnType = null
1582
+ if (this.match(TokenType.COLON) || this.match(TokenType.ARROW)) {
1583
+ returnType = this.parseType(lang)
1584
+ }
1585
+
1586
+ this.skipNewlines()
1587
+ const body = this.parseBlock(lang)
1588
+
1589
+ return {
1590
+ type: 'FunctionExpression',
1591
+ name: name,
1592
+ params: params,
1593
+ returnType: returnType,
1594
+ body: body
1595
+ }
1596
+ }
1597
+
1509
1598
  parseParameter(lang) {
1510
1599
  const nameToken = this.current()
1511
1600
  if (!nameToken || nameToken.type !== TokenType.IDENTIFIER) {
@@ -1590,7 +1679,7 @@ class EtherParser {
1590
1679
  'jamais': 'never',
1591
1680
  'inconnu': 'unknown',
1592
1681
  'symbole': 'symbol',
1593
- 'date': 'Date',
1682
+
1594
1683
  'promesse': 'Promise',
1595
1684
  'fonction': 'Function'
1596
1685
  }
@@ -1640,8 +1729,10 @@ class EtherParser {
1640
1729
  }
1641
1730
  }
1642
1731
 
1643
- parseConditional(lang) {
1644
- const keyword = this.advance()
1732
+ parseConditional(lang, isElseIf = false) {
1733
+ if (!isElseIf) {
1734
+ this.advance()
1735
+ }
1645
1736
  const condition = this.parseExpression(lang)
1646
1737
 
1647
1738
  this.skipNewlines()
@@ -1652,7 +1743,7 @@ class EtherParser {
1652
1743
 
1653
1744
  if (this.matchValue('sinon')) {
1654
1745
  if (this.matchValue('si')) {
1655
- alternate = this.parseConditional(lang)
1746
+ alternate = this.parseConditional(lang, true)
1656
1747
  } else {
1657
1748
  this.skipNewlines()
1658
1749
  alternate = this.parseBlock(lang)
@@ -1844,17 +1935,42 @@ class EtherParser {
1844
1935
  parseBlock(lang) {
1845
1936
  const body = []
1846
1937
 
1847
- if (this.match(TokenType.INDENT)) {
1938
+ const hasBrace = this.match(TokenType.LBRACE)
1939
+ this.skipNewlines()
1940
+
1941
+ if (hasBrace) {
1942
+ this.match(TokenType.INDENT)
1943
+ }
1944
+
1945
+ if (hasBrace || this.match(TokenType.INDENT)) {
1848
1946
  while (!this.isAtEnd()) {
1947
+ this.skipNewlines()
1948
+
1849
1949
  const current = this.current()
1850
1950
 
1851
1951
  if (current && current.type === TokenType.DEDENT) {
1852
1952
  this.advance()
1953
+ if (hasBrace) {
1954
+ this.skipNewlines()
1955
+ this.match(TokenType.RBRACE)
1956
+ }
1853
1957
  break
1854
1958
  }
1855
1959
 
1960
+ if (hasBrace && current && current.type === TokenType.RBRACE) {
1961
+ this.advance()
1962
+ break
1963
+ }
1964
+
1965
+ if (current && (current.type === TokenType.INDENT || current.type === TokenType.NEWLINE)) {
1966
+ this.advance()
1967
+ continue
1968
+ }
1969
+
1856
1970
  const statement = this.parseStatement(lang)
1857
- if (statement) {
1971
+ if (statement && statement.expression?.value !== null) {
1972
+ body.push(statement)
1973
+ } else if (statement && statement.type !== 'ExpressionStatement') {
1858
1974
  body.push(statement)
1859
1975
  }
1860
1976
 
@@ -1924,17 +2040,16 @@ class EtherParser {
1924
2040
  let left = this.parseComparison(lang)
1925
2041
 
1926
2042
  while (true) {
1927
- const compoundOp = this.tryMatchCompoundOperator()
1928
- if (compoundOp === '===' || compoundOp === '!==') {
2043
+ if (this.matchValue('strictement egal') || this.matchValue('strictement égal')) {
1929
2044
  const right = this.parseComparison(lang)
1930
- left = { type: 'BinaryExpression', operator: compoundOp, left, right }
1931
- continue
1932
- }
1933
-
1934
- if (this.match(TokenType.DOUBLE_EQUALS) || this.matchValue('egal') || this.matchValue('egale')) {
2045
+ left = { type: 'BinaryExpression', operator: '===', left, right }
2046
+ } else if (this.matchValue('strictement different') || this.matchValue('strictement différent')) {
2047
+ const right = this.parseComparison(lang)
2048
+ left = { type: 'BinaryExpression', operator: '!==', left, right }
2049
+ } else if (this.match(TokenType.DOUBLE_EQUALS) || this.matchValue('egal') || this.matchValue('egale') || this.matchValue('égal')) {
1935
2050
  const right = this.parseComparison(lang)
1936
2051
  left = { type: 'BinaryExpression', operator: '===', left, right }
1937
- } else if (this.match(TokenType.NOT_EQUALS) || this.matchValue('different')) {
2052
+ } else if (this.match(TokenType.NOT_EQUALS) || this.matchValue('different') || this.matchValue('différent')) {
1938
2053
  const right = this.parseComparison(lang)
1939
2054
  left = { type: 'BinaryExpression', operator: '!==', left, right }
1940
2055
  } else {
@@ -1949,25 +2064,18 @@ class EtherParser {
1949
2064
  let left = this.parseAdditive(lang)
1950
2065
 
1951
2066
  while (true) {
1952
- const compoundOp = this.tryMatchCompoundOperator()
1953
- if (compoundOp === '>=' || compoundOp === '<=') {
2067
+ if (this.matchValue('superieur ou egal') || this.matchValue('supérieur ou égal') || this.match(TokenType.GTE)) {
1954
2068
  const right = this.parseAdditive(lang)
1955
- left = { type: 'BinaryExpression', operator: compoundOp, left, right }
1956
- continue
1957
- }
1958
-
1959
- if (this.match(TokenType.LT) || this.matchValue('inferieur')) {
2069
+ left = { type: 'BinaryExpression', operator: '>=', left, right }
2070
+ } else if (this.matchValue('inferieur ou egal') || this.matchValue('inférieur ou égal') || this.match(TokenType.LTE)) {
2071
+ const right = this.parseAdditive(lang)
2072
+ left = { type: 'BinaryExpression', operator: '<=', left, right }
2073
+ } else if (this.match(TokenType.LT) || this.matchValue('inferieur') || this.matchValue('inférieur')) {
1960
2074
  const right = this.parseAdditive(lang)
1961
2075
  left = { type: 'BinaryExpression', operator: '<', left, right }
1962
- } else if (this.match(TokenType.GT) || this.matchValue('superieur')) {
2076
+ } else if (this.match(TokenType.GT) || this.matchValue('superieur') || this.matchValue('supérieur')) {
1963
2077
  const right = this.parseAdditive(lang)
1964
2078
  left = { type: 'BinaryExpression', operator: '>', left, right }
1965
- } else if (this.match(TokenType.LTE)) {
1966
- const right = this.parseAdditive(lang)
1967
- left = { type: 'BinaryExpression', operator: '<=', left, right }
1968
- } else if (this.match(TokenType.GTE)) {
1969
- const right = this.parseAdditive(lang)
1970
- left = { type: 'BinaryExpression', operator: '>=', left, right }
1971
2079
  } else {
1972
2080
  break
1973
2081
  }
@@ -2026,6 +2134,14 @@ class EtherParser {
2026
2134
  return { type: 'UnaryExpression', operator: '-', operand }
2027
2135
  }
2028
2136
 
2137
+ if (this.matchValue('nouveau') || this.matchValue('new')) {
2138
+ const callee = this.parseCall(lang)
2139
+ if (callee.type === 'CallExpression') {
2140
+ return { type: 'NewExpression', callee: callee.callee, arguments: callee.arguments }
2141
+ }
2142
+ return { type: 'NewExpression', callee, arguments: [] }
2143
+ }
2144
+
2029
2145
  return this.parseCall(lang)
2030
2146
  }
2031
2147
 
@@ -2078,6 +2194,10 @@ class EtherParser {
2078
2194
  if (token.type === TokenType.IDENTIFIER) {
2079
2195
  const value = token.value.toLowerCase()
2080
2196
 
2197
+ if (value === 'fonction' || value === 'function') {
2198
+ return this.parseFunctionExpression(lang)
2199
+ }
2200
+
2081
2201
  if (value === 'vrai' || value === 'true') {
2082
2202
  this.advance()
2083
2203
  return { type: 'Literal', value: true }
@@ -2141,26 +2261,32 @@ class EtherParser {
2141
2261
  this.match(TokenType.COMMA)
2142
2262
  }
2143
2263
 
2144
- return { type: 'ArrayLiteral', elements }
2264
+ return { type: 'ArrayExpression', elements }
2145
2265
  }
2146
2266
 
2147
2267
  parseObjectLiteral(lang) {
2148
2268
  this.expect(TokenType.LBRACE)
2149
2269
  const properties = []
2150
2270
 
2151
- while (!this.isAtEnd() && !this.match(TokenType.RBRACE)) {
2271
+ this.skipWhitespace()
2272
+
2273
+ while (!this.isAtEnd() && !this.check(TokenType.RBRACE)) {
2274
+ this.skipWhitespace()
2275
+
2152
2276
  const keyToken = this.current()
2153
- if (!keyToken) break
2277
+ if (!keyToken || keyToken.type === TokenType.RBRACE) break
2154
2278
 
2155
2279
  let key
2156
2280
  if (keyToken.type === TokenType.STRING) {
2157
- key = keyToken.value.replace(/^["']|["']$/g, '')
2281
+ const keyValue = keyToken.value.replace(/^["']|["']$/g, '')
2282
+ key = { type: 'Literal', value: keyValue }
2283
+ this.advance()
2158
2284
  } else if (keyToken.type === TokenType.IDENTIFIER) {
2159
- key = keyToken.value
2285
+ key = { type: 'Identifier', name: keyToken.value }
2286
+ this.advance()
2160
2287
  } else {
2161
2288
  break
2162
2289
  }
2163
- this.advance()
2164
2290
 
2165
2291
  this.match(TokenType.COLON) || this.match(TokenType.EQUALS)
2166
2292
 
@@ -2168,9 +2294,21 @@ class EtherParser {
2168
2294
  properties.push({ key, value })
2169
2295
 
2170
2296
  this.match(TokenType.COMMA)
2297
+ this.skipWhitespace()
2171
2298
  }
2172
2299
 
2173
- return { type: 'ObjectLiteral', properties }
2300
+ this.match(TokenType.RBRACE)
2301
+
2302
+ return { type: 'ObjectExpression', properties }
2303
+ }
2304
+
2305
+ skipWhitespace() {
2306
+ while (this.match(TokenType.NEWLINE) || this.match(TokenType.INDENT) || this.match(TokenType.DEDENT)) {}
2307
+ }
2308
+
2309
+ check(type) {
2310
+ const token = this.current()
2311
+ return token && token.type === type
2174
2312
  }
2175
2313
 
2176
2314
  parseSQLProgram() {
@@ -5,6 +5,9 @@ class JSGenerator {
5
5
  this.i18n = null
6
6
  this.indent = 0
7
7
  this.output = ''
8
+ this.keywordMap = {}
9
+ this.methodMap = {}
10
+ this.builtinMap = {}
8
11
 
9
12
  if (i18nPath) {
10
13
  this.loadI18n(i18nPath)
@@ -18,10 +21,6 @@ class JSGenerator {
18
21
  }
19
22
 
20
23
  buildMaps() {
21
- this.keywordMap = {}
22
- this.methodMap = {}
23
- this.builtinMap = {}
24
-
25
24
  if (!this.i18n) return
26
25
 
27
26
  const addToMap = (map, section) => {
@@ -326,6 +325,8 @@ class JSGenerator {
326
325
  break
327
326
  case 'ArrowFunctionExpression':
328
327
  return this.generateArrowFunction(node)
328
+ case 'FunctionExpression':
329
+ return this.generateFunctionExpression(node)
329
330
  case 'CallExpression':
330
331
  return this.generateCallExpression(node)
331
332
  case 'MemberExpression':
@@ -407,6 +408,28 @@ class JSGenerator {
407
408
  this.writeLine('')
408
409
  }
409
410
 
411
+ generateFunctionExpression(node) {
412
+ const async = node.async ? 'async ' : ''
413
+ const generator = node.generator ? '*' : ''
414
+ const name = node.name ? this.translate(node.name) : ''
415
+ const params = (node.params || []).map(p => this.generateParameter(p)).join(', ')
416
+
417
+ const bodyStatements = node.body?.body || []
418
+ const bodyCode = bodyStatements.map(stmt => {
419
+ const savedOutput = this.output
420
+ const savedIndent = this.indent
421
+ this.output = ''
422
+ this.indent = 1
423
+ this.generateNode(stmt)
424
+ const stmtCode = this.output.trim()
425
+ this.output = savedOutput
426
+ this.indent = savedIndent
427
+ return stmtCode
428
+ }).join('\n ')
429
+
430
+ return `${async}function${generator}${name ? ' ' + name : ''}(${params}) {\n ${bodyCode}\n }`
431
+ }
432
+
410
433
  generateParameter(node) {
411
434
  if (!node) return ''
412
435
  if (typeof node === 'string') return node
@@ -466,17 +489,22 @@ class JSGenerator {
466
489
  this.writeLine('')
467
490
  }
468
491
 
469
- generateIfStatement(node) {
470
- const test = this.generateNode(node.test)
471
- this.writeLine(`if (${test}) {`)
492
+ generateIfStatement(node, isElseIf = false) {
493
+ const test = this.generateNode(node.test || node.condition)
494
+
495
+ if (isElseIf) {
496
+ this.writeLine(`} else if (${test}) {`)
497
+ } else {
498
+ this.writeLine(`if (${test}) {`)
499
+ }
500
+
472
501
  this.indent++
473
502
  this.generateNode(node.consequent)
474
503
  this.indent--
475
504
 
476
505
  if (node.alternate) {
477
506
  if (node.alternate.type === 'IfStatement') {
478
- this.output = this.output.trimEnd() + ' else '
479
- this.generateIfStatement(node.alternate)
507
+ this.generateIfStatement(node.alternate, true)
480
508
  } else {
481
509
  this.writeLine('} else {')
482
510
  this.indent++
package/i18n/i18n-js.json CHANGED
@@ -158,14 +158,6 @@
158
158
  "zh": "正则表达式",
159
159
  "ja": "正規表現"
160
160
  },
161
- "carte": {
162
- "fr": "carte",
163
- "en": "map",
164
- "es": "mapa",
165
- "ru": "карта",
166
- "zh": "映射",
167
- "ja": "マップ"
168
- },
169
161
  "ensemble": {
170
162
  "fr": "ensemble",
171
163
  "en": "set",
@@ -2787,4 +2779,4 @@
2787
2779
  "ja": "レングス"
2788
2780
  }
2789
2781
  }
2790
- }
2782
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ether-code",
3
- "version": "0.4.7",
3
+ "version": "0.4.9",
4
4
  "description": "Ether - Le langage intentionnel",
5
5
  "main": "cli/compiler.js",
6
6
  "bin": {