functionalscript 0.2.3 → 0.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/README.md +1 -1
  3. package/crypto/README.md +3 -0
  4. package/{prime_field → crypto/prime_field}/module.f.mjs +2 -2
  5. package/{secp → crypto/secp}/module.f.mjs +2 -2
  6. package/{sha2 → crypto/sha2}/module.f.mjs +4 -4
  7. package/{sha2 → crypto/sha2}/test.f.mjs +2 -2
  8. package/djs/module.f.mjs +4 -1
  9. package/djs/parser/module.f.mjs +122 -45
  10. package/djs/parser/test.f.mjs +30 -4
  11. package/djs/tokenizer/module.f.mjs +6 -2
  12. package/djs/tokenizer/test.f.mjs +34 -0
  13. package/html/module.f.mjs +18 -33
  14. package/html/test.f.mjs +6 -6
  15. package/issues/05-publish.md +17 -22
  16. package/issues/README.md +10 -2
  17. package/js/tokenizer/module.f.mjs +75 -26
  18. package/js/tokenizer/test.f.mjs +42 -0
  19. package/json/parser/test.f.mjs +6 -0
  20. package/jsr.json +5 -4
  21. package/nanvm-lib/src/extension.rs +1 -1
  22. package/nanvm-lib/src/nanenum.rs +22 -20
  23. package/out/{prime_field → crypto/prime_field}/module.f.d.mts +1 -1
  24. package/out/{prime_field → crypto/prime_field}/module.f.mjs +2 -2
  25. package/out/{secp → crypto/secp}/module.f.d.mts +1 -1
  26. package/out/{secp → crypto/secp}/module.f.mjs +2 -2
  27. package/out/{sha2 → crypto/sha2}/module.f.d.mts +3 -3
  28. package/out/{sha2 → crypto/sha2}/module.f.mjs +4 -4
  29. package/out/{sha2 → crypto/sha2}/test.f.mjs +2 -2
  30. package/out/djs/module.f.d.mts +1 -1
  31. package/out/djs/module.f.mjs +5 -1
  32. package/out/djs/parser/module.f.d.mts +1 -1
  33. package/out/djs/parser/module.f.mjs +105 -89
  34. package/out/djs/parser/test.f.d.mts +1 -0
  35. package/out/djs/parser/test.f.mjs +38 -4
  36. package/out/djs/tokenizer/module.f.d.mts +2 -2
  37. package/out/djs/tokenizer/module.f.mjs +6 -2
  38. package/out/djs/tokenizer/test.f.d.mts +1 -0
  39. package/out/djs/tokenizer/test.f.mjs +50 -0
  40. package/out/html/module.f.d.mts +8 -6
  41. package/out/html/module.f.mjs +17 -31
  42. package/out/html/test.f.mjs +5 -5
  43. package/out/js/tokenizer/module.f.d.mts +15 -3
  44. package/out/js/tokenizer/module.f.mjs +65 -22
  45. package/out/js/tokenizer/test.f.d.mts +1 -0
  46. package/out/js/tokenizer/test.f.mjs +62 -0
  47. package/out/json/parser/test.f.mjs +8 -0
  48. package/package.json +1 -1
  49. /package/{prime_field → crypto/prime_field}/test.f.mjs +0 -0
  50. /package/{secp → crypto/secp}/test.f.mjs +0 -0
  51. /package/out/{prime_field → crypto/prime_field}/test.f.d.mts +0 -0
  52. /package/out/{prime_field → crypto/prime_field}/test.f.mjs +0 -0
  53. /package/out/{secp → crypto/secp}/test.f.d.mts +0 -0
  54. /package/out/{secp → crypto/secp}/test.f.mjs +0 -0
  55. /package/out/{sha2 → crypto/sha2}/test.f.d.mts +0 -0
package/CHANGELOG.md CHANGED
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## Unreleased
9
9
 
10
+ ## 0.2.5
11
+
12
+ - new [crypto/] directory [PR #327](https://github.com/functionalscript/functionalscript/pull/327).
13
+ - simplified HTML [PR #327](https://github.com/functionalscript/functionalscript/pull/327).
14
+ - djs: add undefined and comments [PR #325](https://github.com/functionalscript/functionalscript/pull/325).
15
+
10
16
  ## 0.2.3
11
17
 
12
18
  - BitVec and documentation update [PR #322](https://github.com/functionalscript/functionalscript/pull/322).
package/README.md CHANGED
@@ -18,7 +18,7 @@ Learn more about
18
18
  - [Purely Functional Programming in JavaScript](https://blog.bitsrc.io/purely-functional-programming-in-javascript-91114b1b2dff?sk=5f7132e56902f38fcf4c6164bfa681ed),
19
19
  - [FunctionalScript and I/O](https://medium.com/@sergeyshandar/functionalscript-5cf817345376?sk=30b32189a81d1a2dad16c2244f32328d).
20
20
 
21
- FunctionalScript is distributed under [AGPL-3.0](https://www.gnu.org/licenses/agpl-3.0.en.html#license-text). Let us know if you need another license by sending an [email](mailto:sergey.oss@proton.me).
21
+ This repository is a [monorepo](https://en.wikipedia.org/wiki/Monorepo) and distributted under under [AGPL-3.0](https://www.gnu.org/licenses/agpl-3.0.en.html#license-text). Let us know if you need another license by sending an [email](mailto:sergey.oss@proton.me).
22
22
 
23
23
  ## Vision
24
24
 
@@ -0,0 +1,3 @@
1
+ # Cryptography
2
+
3
+ The directory contains algorithms and utilities for various cryptographic methods.
@@ -1,6 +1,6 @@
1
1
  // @ts-self-types="./module.f.d.mts"
2
- import * as Operator from '../types/function/operator/module.f.mjs'
3
- import * as bi from '../types/bigint/module.f.mjs'
2
+ import * as Operator from '../../types/function/operator/module.f.mjs'
3
+ import * as bi from '../../types/bigint/module.f.mjs'
4
4
  const { scalar_mul } = bi
5
5
 
6
6
  /** @typedef {Operator.Reduce<bigint>} Reduce */
@@ -1,7 +1,7 @@
1
1
  // @ts-self-types="./module.f.d.mts"
2
- import * as Operator from '../types/function/operator/module.f.mjs'
2
+ import * as Operator from '../../types/function/operator/module.f.mjs'
3
3
  import * as pf from '../prime_field/module.f.mjs'
4
- import * as bi from '../types/bigint/module.f.mjs'
4
+ import * as bi from '../../types/bigint/module.f.mjs'
5
5
  const { scalar_mul } = bi
6
6
  const { prime_field, sqrt } = pf
7
7
 
@@ -1,5 +1,5 @@
1
1
  // @ts-self-types="./module.f.d.mts"
2
- import * as arrayT from '../types/array/module.f.mjs'
2
+ import * as array from '../../types/array/module.f.mjs'
3
3
 
4
4
  /**
5
5
  * @typedef {{
@@ -8,9 +8,9 @@ import * as arrayT from '../types/array/module.f.mjs'
8
8
  * }} HashInput
9
9
  */
10
10
 
11
- /** @typedef {arrayT.Array8<number>} Hash8 */
11
+ /** @typedef {array.Array8<number>} Hash8 */
12
12
 
13
- /** @typedef {arrayT.Array16<number>} Array16 */
13
+ /** @typedef {array.Array16<number>} Array16 */
14
14
 
15
15
  /** @type {(input: number) => (pos: number) => number} */
16
16
  const appendOneWithZeros = input => pos => (input >> pos << pos) | (1 << pos)
@@ -74,7 +74,7 @@ const smallSigma0 = smallSigma(7)(18)(3)
74
74
 
75
75
  const smallSigma1 = smallSigma(17)(19)(10)
76
76
 
77
- /** @type {(a: arrayT.Array4<number>) => number} */
77
+ /** @type {(a: array.Array4<number>) => number} */
78
78
  const wi = ([a0, a1, a2, a3]) => (smallSigma1(a0) + a1 + smallSigma0(a2) + a3) | 0
79
79
 
80
80
  /** @type {(w: Array16) => Array16} */
@@ -1,6 +1,6 @@
1
1
  import * as _ from './module.f.mjs'
2
- import * as json from '../json/module.f.mjs'
3
- import * as o from '../types/object/module.f.mjs'
2
+ import * as json from '../../json/module.f.mjs'
3
+ import * as o from '../../types/object/module.f.mjs'
4
4
  const { sort } = o
5
5
 
6
6
  /** @type {(a: number) => number} */
package/djs/module.f.mjs CHANGED
@@ -21,10 +21,12 @@ const { objectWrap, arrayWrap, stringSerialize, numberSerialize, nullSerialize,
21
21
 
22
22
  /** @typedef {readonly Unknown[]} Array */
23
23
 
24
- /** @typedef {Object|boolean|string|number|null|Array|bigint} Unknown */
24
+ /** @typedef {Object|boolean|string|number|null|Array|bigint|undefined} Unknown */
25
25
 
26
26
  const colon = [':']
27
27
 
28
+ const undefinedSerialize = ['undefined']
29
+
28
30
  /** @typedef {O.Entry<Unknown>} Entry*/
29
31
 
30
32
  /** @typedef {(list.List<Entry>)} Entries */
@@ -55,6 +57,7 @@ export const serialize = sort => {
55
57
  case 'bigint': { return [bigintSerialize(value)] }
56
58
  default: {
57
59
  if (value === null) { return nullSerialize }
60
+ if (value === undefined) { return undefinedSerialize }
58
61
  if (value instanceof Array) { return arraySerialize(value) }
59
62
  return objectSerialize(value)
60
63
  }
@@ -12,7 +12,7 @@ const { fromMap } = o
12
12
 
13
13
  /** @typedef {[readonly string[], readonly DjsConst[]] } DjsModule */
14
14
 
15
- /** @typedef {boolean|string|number|null|bigint|DjsModuleRef|DjsArray|DjsObject} DjsConst */
15
+ /** @typedef {boolean|string|number|null|bigint|undefined|DjsModuleRef|DjsArray|DjsObject} DjsConst */
16
16
 
17
17
  /** @typedef {['aref' | 'cref', number]} DjsModuleRef */
18
18
 
@@ -113,7 +113,9 @@ const parseInitialOp = token => state => {
113
113
  switch (token.kind)
114
114
  {
115
115
  case 'ws':
116
- case 'nl': return state
116
+ case 'nl':
117
+ case '//':
118
+ case '/*': return state
117
119
  case 'id': {
118
120
  switch (token.value) {
119
121
  case 'import': return { ... state, state: 'import' }
@@ -128,7 +130,9 @@ const parseInitialOp = token => state => {
128
130
  /** @type {(token: tokenizerT.DjsToken) => (state: NewLineRequiredState) => ParserState}} */
129
131
  const parseNewLineRequiredOp = token => state => {
130
132
  switch (token.kind) {
131
- case 'ws': return state
133
+ case 'ws':
134
+ case '//':
135
+ case '/*': return state
132
136
  case 'nl': return { ... state, state: '' }
133
137
  default: return { state: 'error', message: 'unexpected token' }
134
138
  }
@@ -138,7 +142,9 @@ const parseNewLineRequiredOp = token => state => {
138
142
  const parseExportOp = token => state => {
139
143
  switch (token.kind) {
140
144
  case 'ws':
141
- case 'nl': return state
145
+ case 'nl':
146
+ case '//':
147
+ case '/*': return state
142
148
  case 'id': {
143
149
  if (token.value === 'default') return { ... state, state: 'exportValue', valueState: '', top: null, stack: null }
144
150
  }
@@ -146,11 +152,24 @@ const parseExportOp = token => state => {
146
152
  return { state: 'error', message: 'unexpected token' }
147
153
  }
148
154
 
155
+ /** @type {(token: tokenizerT.DjsToken) => (state: ResultState) => ParserState}} */
156
+ const parseResultOp = token => state => {
157
+ switch (token.kind) {
158
+ case 'ws':
159
+ case 'nl':
160
+ case '//':
161
+ case '/*': return state
162
+ default: return { state: 'error', message: 'unexpected token' }
163
+ }
164
+ }
165
+
149
166
  /** @type {(token: tokenizerT.DjsToken) => (state: ConstState) => ParserState}} */
150
167
  const parseConstOp = token => state => {
151
168
  switch (token.kind) {
152
169
  case 'ws':
153
- case 'nl': return state
170
+ case 'nl':
171
+ case '//':
172
+ case '/*': return state
154
173
  case 'id': {
155
174
  if (map.at(token.value)(state.module.refs) !== null)
156
175
  return { state: 'error', message: 'duplicate id' }
@@ -167,7 +186,9 @@ const parseConstOp = token => state => {
167
186
  const parseConstNameOp = token => state => {
168
187
  switch (token.kind) {
169
188
  case 'ws':
170
- case 'nl': return state
189
+ case 'nl':
190
+ case '//':
191
+ case '/*': return state
171
192
  case '=': return { ... state, state: 'constValue', valueState: '', top: null, stack: null }
172
193
  default: return { state: 'error', message: 'unexpected token' }
173
194
  }
@@ -177,7 +198,9 @@ const parseConstNameOp = token => state => {
177
198
  const parseImportOp = token => state => {
178
199
  switch (token.kind) {
179
200
  case 'ws':
180
- case 'nl': return state
201
+ case 'nl':
202
+ case '//':
203
+ case '/*': return state
181
204
  case 'id': {
182
205
  if (map.at(token.value)(state.module.refs) !== null)
183
206
  return { state: 'error', message: 'duplicate id' }
@@ -194,7 +217,9 @@ const parseImportOp = token => state => {
194
217
  const parseImportNameOp = token => state => {
195
218
  switch (token.kind) {
196
219
  case 'ws':
197
- case 'nl': return state
220
+ case 'nl':
221
+ case '//':
222
+ case '/*': return state
198
223
  case 'id': {
199
224
  if (token.value === 'from') return { ... state, state: 'import+from' }
200
225
  }
@@ -206,7 +231,9 @@ const parseImportNameOp = token => state => {
206
231
  const parseImportFromOp = token => state => {
207
232
  switch (token.kind) {
208
233
  case 'ws':
209
- case 'nl': return state
234
+ case 'nl':
235
+ case '//':
236
+ case '/*': return state
210
237
  case 'string': {
211
238
  const modules = list.concat(state.module.modules)([token.value])
212
239
  return { ... state, state: 'nl', module: { ...state.module, modules: modules } }
@@ -295,6 +322,7 @@ const tokenToValue = token => {
295
322
  case 'number': return parseFloat(token.value)
296
323
  case 'string': return token.value
297
324
  case 'bigint': return token.value
325
+ case 'undefined': return undefined
298
326
  default: return null
299
327
  }
300
328
  }
@@ -307,7 +335,8 @@ const isValueToken = token => {
307
335
  case 'true':
308
336
  case 'number':
309
337
  case 'string':
310
- case 'bigint': return true
338
+ case 'bigint':
339
+ case 'undefined': return true
311
340
  default: return false
312
341
  }
313
342
  }
@@ -315,70 +344,118 @@ const isValueToken = token => {
315
344
  /** @type {(token: tokenizerT.DjsToken) => (state: ParseValueState) => ParserState}} */
316
345
  const parseValueOp = token => state => {
317
346
  if (isValueToken(token)) { return pushValue(state)(tokenToValue(token)) }
318
- if (token.kind === 'id') { return pushRef(state)(token.value) }
319
- if (token.kind === '[') { return startArray(state) }
320
- if (token.kind === '{') { return startObject(state) }
321
- if (token.kind === 'ws') { return state }
322
- return { state: 'error', message: 'unexpected token' }
347
+ switch (token.kind)
348
+ {
349
+ case 'id': return pushRef(state)(token.value)
350
+ case '[': return startArray(state)
351
+ case '{': return startObject(state)
352
+ case 'ws':
353
+ case 'nl':
354
+ case '//':
355
+ case '/*': return state
356
+ default: return { state: 'error', message: 'unexpected token' }
357
+ }
323
358
  }
324
359
 
325
360
  /** @type {(token: tokenizerT.DjsToken) => (state: ParseValueState) => ParserState}} */
326
361
  const parseArrayStartOp = token => state => {
327
362
  if (isValueToken(token)) { return pushValue(state)(tokenToValue(token)) }
328
- if (token.kind === 'id') { return pushRef(state)(token.value) }
329
- if (token.kind === '[') { return startArray(state) }
330
- if (token.kind === ']') { return endArray(state) }
331
- if (token.kind === '{') { return startObject(state) }
332
- if (token.kind === 'ws') { return state }
333
- return { state: 'error', message: 'unexpected token' }
363
+ switch (token.kind)
364
+ {
365
+ case 'id': return pushRef(state)(token.value)
366
+ case '[': return startArray(state)
367
+ case ']': return endArray(state)
368
+ case '{': return startObject(state)
369
+ case 'ws':
370
+ case 'nl':
371
+ case '//':
372
+ case '/*': return state
373
+ default: return { state: 'error', message: 'unexpected token' }
374
+ }
334
375
  }
335
376
 
336
377
  /** @type {(token: tokenizerT.DjsToken) => (state: ParseValueState) => ParserState}} */
337
378
  const parseArrayValueOp = token => state => {
338
- if (token.kind === ']') { return endArray(state) }
339
- if (token.kind === ',') { return { ... state, valueState: '[,', top: state.top, stack: state.stack } }
340
- if (token.kind === 'ws') { return state }
341
- return { state: 'error', message: 'unexpected token' }
379
+ switch (token.kind)
380
+ {
381
+ case ']': return endArray(state)
382
+ case ',': return { ... state, valueState: '[,', top: state.top, stack: state.stack }
383
+ case 'ws':
384
+ case 'nl':
385
+ case '//':
386
+ case '/*': return state
387
+ default: return { state: 'error', message: 'unexpected token' }
388
+ }
342
389
  }
343
390
 
344
391
  /** @type {(token: tokenizerT.DjsToken) => (state: ParseValueState) => ParserState}} */
345
392
  const parseObjectStartOp = token => state => {
346
- if (token.kind === 'string') { return pushKey(state)(token.value) }
347
- if (token.kind === '}') { return endObject(state) }
348
- if (token.kind === 'ws') { return state }
349
- return { state: 'error', message: 'unexpected token' }
393
+ switch (token.kind)
394
+ {
395
+ case 'string': return pushKey(state)(token.value)
396
+ case '}': return endObject(state)
397
+ case 'ws':
398
+ case 'nl':
399
+ case '//':
400
+ case '/*': return state
401
+ default: return { state: 'error', message: 'unexpected token' }
402
+ }
350
403
  }
351
404
 
352
405
  /** @type {(token: tokenizerT.DjsToken) => (state: ParseValueState) => ParserState}} */
353
406
  const parseObjectKeyOp = token => state => {
354
- if (token.kind === ':') { return { ... state, valueState: '{:', top: state.top, stack: state.stack } }
355
- if (token.kind === 'ws') { return state }
356
- return { state: 'error', message: 'unexpected token' }
407
+ switch (token.kind)
408
+ {
409
+ case ':': return { ... state, valueState: '{:', top: state.top, stack: state.stack }
410
+ case 'ws':
411
+ case 'nl':
412
+ case '//':
413
+ case '/*': return state
414
+ default: return { state: 'error', message: 'unexpected token' }
415
+ }
357
416
  }
358
417
 
359
418
  /** @type {(token: tokenizerT.DjsToken) => (state: ParseValueState) => ParserState}} */
360
419
  const parseObjectColonOp = token => state => {
361
420
  if (isValueToken(token)) { return pushValue(state)(tokenToValue(token)) }
362
- if (token.kind === 'id') { return pushRef(state)(token.value) }
363
- if (token.kind === '[') { return startArray(state) }
364
- if (token.kind === '{') { return startObject(state) }
365
- if (token.kind === 'ws') { return state }
366
- return { state: 'error', message: 'unexpected token' }
421
+ switch (token.kind)
422
+ {
423
+ case 'id': return pushRef(state)(token.value)
424
+ case '[': return startArray(state)
425
+ case '{': return startObject(state)
426
+ case 'ws':
427
+ case 'nl':
428
+ case '//':
429
+ case '/*': return state
430
+ default: return { state: 'error', message: 'unexpected token' }
431
+ }
367
432
  }
368
433
 
369
434
  /** @type {(token: tokenizerT.DjsToken) => (state: ParseValueState) => ParserState}} */
370
435
  const parseObjectNextOp = token => state => {
371
- if (token.kind === '}') { return endObject(state) }
372
- if (token.kind === ',') { return { ... state, valueState: '{,', top: state.top, stack: state.stack } }
373
- if (token.kind === 'ws') { return state }
374
- return { state: 'error', message: 'unexpected token' }
436
+ switch (token.kind)
437
+ {
438
+ case '}': return endObject(state)
439
+ case ',': return { ... state, valueState: '{,', top: state.top, stack: state.stack }
440
+ case 'ws':
441
+ case 'nl':
442
+ case '//':
443
+ case '/*': return state
444
+ default: return { state: 'error', message: 'unexpected token' }
445
+ }
375
446
  }
376
447
 
377
448
  /** @type {(token: tokenizerT.DjsToken) => (state: ParseValueState) => ParserState}} */
378
449
  const parseObjectCommaOp = token => state => {
379
- if (token.kind === 'string') { return pushKey(state)(token.value) }
380
- if (token.kind === 'ws') { return state }
381
- return { state: 'error', message: 'unexpected token' }
450
+ switch (token.kind)
451
+ {
452
+ case 'string': return pushKey(state)(token.value)
453
+ case 'ws':
454
+ case 'nl':
455
+ case '//':
456
+ case '/*': return state
457
+ default: return { state: 'error', message: 'unexpected token' }
458
+ }
382
459
  }
383
460
 
384
461
  /** @type {Operator.Fold<tokenizerT.DjsToken, ParserState>} */
@@ -392,7 +469,7 @@ const foldOp = token => state => {
392
469
  case 'const': return parseConstOp(token)(state)
393
470
  case 'const+name': return parseConstNameOp(token)(state)
394
471
  case 'export': return parseExportOp(token)(state)
395
- case 'result': return { state: 'error', message: 'unexpected token' }
472
+ case 'result': return parseResultOp(token)(state)
396
473
  case 'error': return { state: 'error', message: state.message }
397
474
  case 'constValue':
398
475
  case 'exportValue':
@@ -32,6 +32,12 @@ export default {
32
32
  const result = stringify(obj)
33
33
  if (result !== '["ok",[[],[false]]]') { throw result }
34
34
  },
35
+ () => {
36
+ const tokenList = tokenizeString('export default undefined')
37
+ const obj = parser.parse(tokenList)
38
+ const result = stringify(obj)
39
+ if (result !== '["ok",[[],[undefined]]]') { throw result }
40
+ },
35
41
  () => {
36
42
  const tokenList = tokenizeString('export default 0.1')
37
43
  const obj = parser.parse(tokenList)
@@ -87,10 +93,10 @@ export default {
87
93
  if (result !== '["ok",[[],[["array",[{}]]]]]') { throw result }
88
94
  },
89
95
  () => {
90
- const tokenList = tokenizeString('export default {"a":true,"b":false,"c":null}')
96
+ const tokenList = tokenizeString('export default {"a":true,"b":false,"c":null,"d":undefined}')
91
97
  const obj = parser.parse(tokenList)
92
98
  const result = stringify(obj)
93
- if (result !== '["ok",[[],[{"a":true,"b":false,"c":null}]]]') { throw result }
99
+ if (result !== '["ok",[[],[{"a":true,"b":false,"c":null,"d":undefined}]]]') { throw result }
94
100
  },
95
101
  () => {
96
102
  const tokenList = tokenizeString('export default {"a":{"b":{"c":["d"]}}}')
@@ -259,13 +265,25 @@ export default {
259
265
  ],
260
266
  validWhiteSpaces:[
261
267
  () => {
262
- const tokenList = tokenizeString('export default [ 0 , 1 , 2 ]')
268
+ const tokenList = tokenizeString(' export default [ 0 , 1 , 2 ] ')
263
269
  const obj = parser.parse(tokenList)
264
270
  const result = stringify(obj)
265
271
  if (result !== '["ok",[[],[["array",[0,1,2]]]]]') { throw result }
266
272
  },
267
273
  () => {
268
- const tokenList = tokenizeString('export default { "a" : 0 , "b" : 1 }')
274
+ const tokenList = tokenizeString(' export default { "a" : 0 , "b" : 1 } ')
275
+ const obj = parser.parse(tokenList)
276
+ const result = stringify(obj)
277
+ if (result !== '["ok",[[],[{"a":0,"b":1}]]]') { throw result }
278
+ },
279
+ () => {
280
+ const tokenList = tokenizeString('\nexport\ndefault\n[\n0\n,\n1\n,\n2\n]\n')
281
+ const obj = parser.parse(tokenList)
282
+ const result = stringify(obj)
283
+ if (result !== '["ok",[[],[["array",[0,1,2]]]]]') { throw result }
284
+ },
285
+ () => {
286
+ const tokenList = tokenizeString('\rexport\rdefault\r{\r"a"\r:\r0\r,\r"b"\r:\r1\r}\r')
269
287
  const obj = parser.parse(tokenList)
270
288
  const result = stringify(obj)
271
289
  if (result !== '["ok",[[],[{"a":0,"b":1}]]]') { throw result }
@@ -409,4 +427,12 @@ export default {
409
427
  if (result !== '["error","duplicate id"]') { throw result }
410
428
  },
411
429
  ],
430
+ comments: [
431
+ () => {
432
+ const tokenList = tokenizeString('export //comment \n default /* comment */ null //comment')
433
+ const obj = parser.parse(tokenList)
434
+ const result = stringify(obj)
435
+ if (result !== '["ok",[[],[null]]]') { throw result }
436
+ },
437
+ ]
412
438
  }
@@ -9,7 +9,7 @@ import * as jsTokenizer from '../../js/tokenizer/module.f.mjs'
9
9
 
10
10
  /**
11
11
  * @typedef {|
12
- * {readonly kind: 'true' | 'false' | 'null'} |
12
+ * {readonly kind: 'true' | 'false' | 'null' | 'undefined'} |
13
13
  * {readonly kind: '{' | '}' | ':' | ',' | '[' | ']' | '.' | '=' } |
14
14
  * jsTokenizer.StringToken |
15
15
  * jsTokenizer.NumberToken |
@@ -17,7 +17,8 @@ import * as jsTokenizer from '../../js/tokenizer/module.f.mjs'
17
17
  * jsTokenizer.IdToken |
18
18
  * jsTokenizer.BigIntToken |
19
19
  * jsTokenizer.WhitespaceToken |
20
- * jsTokenizer.NewLineToken
20
+ * jsTokenizer.NewLineToken |
21
+ * jsTokenizer.CommentToken
21
22
  * } DjsToken
22
23
  */
23
24
 
@@ -55,6 +56,9 @@ const mapToken = input =>
55
56
  case 'number':
56
57
  case 'ws':
57
58
  case 'nl':
59
+ case 'undefined':
60
+ case '//':
61
+ case '/*':
58
62
  case 'error': return [input]
59
63
  default: return jsTokenizer.isKeywordToken(input) ? [{ kind: 'id', value: input.kind }] : [{ kind: 'error', message: 'invalid token' }]
60
64
  }
@@ -321,9 +321,43 @@ export default {
321
321
  const result = stringify(tokenizeString('null'))
322
322
  if (result !== '[{"kind":"null"}]') { throw result }
323
323
  },
324
+ () => {
325
+ const result = stringify(tokenizeString('undefined'))
326
+ if (result !== '[{"kind":"undefined"}]') { throw result }
327
+ },
324
328
  () => {
325
329
  const result = stringify(tokenizeString('[null]'))
326
330
  if (result !== '[{"kind":"["},{"kind":"null"},{"kind":"]"}]') { throw result }
327
331
  },
332
+ ],
333
+ comments: [
334
+ () => {
335
+ const result = stringify(tokenizeString('//singleline comment'))
336
+ if (result !== '[{"kind":"//","value":"singleline comment"}]') { throw result }
337
+ },
338
+ () => {
339
+ const result = stringify(tokenizeString('true//singleline comment\nfalse'))
340
+ if (result !== '[{"kind":"true"},{"kind":"//","value":"singleline comment"},{"kind":"nl"},{"kind":"false"}]') { throw result }
341
+ },
342
+ () => {
343
+ const result = stringify(tokenizeString('/* multiline comment */'))
344
+ if (result !== '[{"kind":"/*","value":" multiline comment "}]') { throw result }
345
+ },
346
+ () => {
347
+ const result = stringify(tokenizeString('/* multiline comment *'))
348
+ if (result !== '[{"kind":"error","message":"*/ expected"}]') { throw result }
349
+ },
350
+ () => {
351
+ const result = stringify(tokenizeString('/* multiline comment '))
352
+ if (result !== '[{"kind":"error","message":"*/ expected"}]') { throw result }
353
+ },
354
+ () => {
355
+ const result = stringify(tokenizeString('/* multiline comment \n * **/'))
356
+ if (result !== '[{"kind":"/*","value":" multiline comment \\n * *"},{"kind":"nl"}]') { throw result }
357
+ },
358
+ () => {
359
+ const result = stringify(tokenizeString('/* multiline comment *\n * **/'))
360
+ if (result !== '[{"kind":"/*","value":" multiline comment *\\n * *"},{"kind":"nl"}]') { throw result }
361
+ },
328
362
  ]
329
363
  }
package/html/module.f.mjs CHANGED
@@ -14,7 +14,7 @@ const { entries } = Object
14
14
  /** @typedef {string} Tag */
15
15
 
16
16
  // https://developer.mozilla.org/en-US/docs/Glossary/Void_element
17
- const voidTagList = [
17
+ const voidTagList = /** @type {const} */([
18
18
  'area',
19
19
  'base',
20
20
  'br',
@@ -29,20 +29,20 @@ const voidTagList = [
29
29
  'source',
30
30
  'track',
31
31
  'wbr',
32
- ]
32
+ ])
33
33
 
34
- /** @type {(tag: string) => boolean} */
35
- const isVoid = tag => voidTagList.includes(tag)
34
+ /** @typedef {typeof voidTagList} VoidTagList */
36
35
 
37
- /** @typedef {readonly[Tag]} Element1*/
36
+ /** @typedef {keyof voidTagList} VoidTag */
38
37
 
39
- /** @typedef {readonly[Tag, Attributes]} Element2A */
38
+ /** @type {(tag: string) => boolean} */
39
+ const isVoid = tag => voidTagList.includes(/** @type {any} */(tag))
40
40
 
41
- /** @typedef {readonly[Tag, readonly Node[]]} Element2N */
41
+ /** @typedef {readonly[Tag, ...Node[]]} Element1*/
42
42
 
43
- /** @typedef {readonly[Tag, Attributes, Nodes]} Element3*/
43
+ /** @typedef {readonly[Tag, Attributes, ...Node[]]} Element2 */
44
44
 
45
- /** @typedef {Element1 | Element2A | Element2N | Element3} Element */
45
+ /** @typedef {Element1 | Element2 } Element */
46
46
 
47
47
  /**
48
48
  * @typedef {{
@@ -50,7 +50,7 @@ const isVoid = tag => voidTagList.includes(tag)
50
50
  * }} Attributes
51
51
  */
52
52
 
53
- /** @typedef {list.List<Node>} Nodes */
53
+ // /** @typedef {list.List<Node>} Nodes */
54
54
 
55
55
  /** @typedef {Element | string} Node */
56
56
 
@@ -82,34 +82,19 @@ const attribute = ([name, value]) => flat([[' ', name, '="'], escape(value), ['"
82
82
  /** @type {(a: Attributes) => list.List<string>} */
83
83
  const attributes = compose(entries)(flatMap(attribute))
84
84
 
85
- const open = (/** @type {Element2A} */[tag, a]) => flat([[`<`, tag], attributes(a), [`>`]])
86
-
87
- const close = (/** @type {string}*/tag) => ['</', tag, '>']
85
+ /** @type {(t: string) => (a: Attributes) => list.List<string>} */
86
+ const open = t => a => flat([[`<`, t], attributes(a), [`>`]])
88
87
 
89
- /** @type {(_: Element3) => list.List<string>} */
90
- const element3 = ([tag, a, ns]) =>
91
- flat([open([tag, a]), nodes(ns), close(tag)])
92
-
93
- /** @type {(_: Element2A) => list.List<string>} */
94
- const element2a = e => {
95
- const [tag] = e
96
- return flat([open(e), isVoid(tag) ? [] : close(tag)])
88
+ /** @type {(t: string) => (an: readonly[Attributes, readonly Node[]]) => list.List<string>} */
89
+ const element3 = t => ([a, n]) => {
90
+ const o = flat([[`<`, t], attributes(a), [`>`]])
91
+ return isVoid(t) ? o : flat([o, nodes(n), ['</', t, '>']])
97
92
  }
98
93
 
99
94
  /** @type {(element: Element) => list.List<string>} */
100
95
  export const element = e => {
101
- switch (e.length) {
102
- case 1: { return element2a([e[0], {}]) }
103
- case 2: {
104
- const [tag, a] = e
105
- return a instanceof Array ?
106
- element3([tag, {}, a]) :
107
- element2a([tag, a])
108
- }
109
- default: {
110
- return element3(e)
111
- }
112
- }
96
+ const [t, a, ...n] = e
97
+ return element3(t)(a === undefined ? [{}, []]: typeof a === 'object' && !(a instanceof Array) ? [a, n] : [{}, [a, ...n]])
113
98
  }
114
99
 
115
100
  export const html = compose(element)(listConcat(['<!DOCTYPE html>']))