septima-lang 0.1.0 → 0.2.0
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/README.md +435 -0
- package/change-log.md +57 -0
- package/dist/tests/parser.spec.d.ts +1 -0
- package/dist/tests/parser.spec.js +75 -0
- package/dist/tests/septima-compile.spec.d.ts +1 -0
- package/dist/tests/septima-compile.spec.js +118 -0
- package/dist/tests/septima.spec.d.ts +1 -0
- package/dist/tests/septima.spec.js +1090 -0
- package/dist/tests/value.spec.d.ts +1 -0
- package/dist/tests/value.spec.js +263 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +1 -1
- package/src/a.js +66 -0
- package/src/ast-node.ts +340 -0
- package/src/extract-message.ts +5 -0
- package/src/fail-me.ts +7 -0
- package/src/find-array-method.ts +124 -0
- package/src/find-string-method.ts +84 -0
- package/src/index.ts +1 -0
- package/src/location.ts +13 -0
- package/src/parser.ts +698 -0
- package/src/result.ts +54 -0
- package/src/runtime.ts +462 -0
- package/src/scanner.ts +136 -0
- package/src/septima.ts +218 -0
- package/src/should-never-happen.ts +4 -0
- package/src/source-code.ts +101 -0
- package/src/stack.ts +18 -0
- package/src/switch-on.ts +4 -0
- package/src/symbol-table.ts +9 -0
- package/src/value.ts +823 -0
- package/tests/parser.spec.ts +81 -0
- package/tests/septima-compile.spec.ts +187 -0
- package/tests/septima.spec.ts +1169 -0
- package/tests/value.spec.ts +291 -0
package/README.md
ADDED
|
@@ -0,0 +1,435 @@
|
|
|
1
|
+
# Septima: An Overview
|
|
2
|
+
|
|
3
|
+
Septima is a programming language that closely follows JavaScript, not just in syntax but also in behavior. If you're familiar with JavaScript, you'll feel right at home with Septima's objects, arrays, functions, and built-in methods. However, Septima makes some deliberate departures from JavaScript to promote cleaner, more predictable code:
|
|
4
|
+
|
|
5
|
+
- It is immutable - variables cannot be reassigned after definition
|
|
6
|
+
- Side effect free - a computation is only affected by its inputs. The only "trace" that a computation leaves is the value that it computed.
|
|
7
|
+
- All expressions, including `if...else`, return values
|
|
8
|
+
- There are no `null` values - only `undefined`
|
|
9
|
+
- There's no automatic type coercion
|
|
10
|
+
- No global scope or `var` keyword - only lexical block scoping with `let`
|
|
11
|
+
- No classes or prototypes - You can create objects using JavaScript's standard object notation (`{a: 'foo'}`).
|
|
12
|
+
|
|
13
|
+
## Why Septima?
|
|
14
|
+
|
|
15
|
+
Septima excels in scenarios where you need to safely execute user-provided code within your application, particularly in backend systems. Common use cases include:
|
|
16
|
+
|
|
17
|
+
- **Configuration as Code**: Allow users to write complex configuration logic using a familiar JavaScript-like syntax instead of being limited to static JSON or YAML files
|
|
18
|
+
- **Business Rules Engines**: Enable domain experts to define and maintain business rules in a familiar syntax without risking system stability
|
|
19
|
+
- **ETL Transformations**: Let users write data transformation logic that can safely process data without accessing external systems
|
|
20
|
+
- **Plugin Systems**: Implement extensible architectures where third-party code can be safely executed in a controlled environment
|
|
21
|
+
- **Customizable Workflows**: Allow users to define custom workflow logic while ensuring system security
|
|
22
|
+
|
|
23
|
+
Key benefits that make Septima ideal for these scenarios:
|
|
24
|
+
|
|
25
|
+
1. **Secured**:
|
|
26
|
+
|
|
27
|
+
- No file system or network access
|
|
28
|
+
- No `eval()` or dynamic code execution
|
|
29
|
+
- No access to system resources
|
|
30
|
+
|
|
31
|
+
2. **Reliable**:
|
|
32
|
+
|
|
33
|
+
- Functional programming paradigm (no loops) makes code more robust
|
|
34
|
+
- Immutability and lack of side effects make code easier to reason about
|
|
35
|
+
- No type coercion reduces unexpected behavior
|
|
36
|
+
- Modules allow you to organize and manage large Septima codebases
|
|
37
|
+
|
|
38
|
+
3. **Friendly**:
|
|
39
|
+
- JavaScript-like syntax reduces learning curve
|
|
40
|
+
- Highly compatible with JavaScript - code ports easily in both directions
|
|
41
|
+
|
|
42
|
+
Unlike alternatives such as JavaScript's `vm` module or `eval()`, Septima provides a secure environment for running user-provided code without sacrificing expressiveness or ease of use. It strikes a balance between power and safety that makes it particularly well-suited for enterprise applications where reliability and security are paramount.
|
|
43
|
+
|
|
44
|
+
## Table of Contents
|
|
45
|
+
|
|
46
|
+
- [Language Fundamentals](#language-fundamentals)
|
|
47
|
+
- [Numbers and Arithmetic](#numbers-and-arithmetic)
|
|
48
|
+
- [Booleans and Logic](#booleans-and-logic)
|
|
49
|
+
- [Control Flow and Expressions](#control-flow-and-expressions)
|
|
50
|
+
- [Variables and Immutability](#variables-and-immutability)
|
|
51
|
+
- [Arrays and Objects](#arrays-and-objects)
|
|
52
|
+
- [Conversions](#conversions)
|
|
53
|
+
- [Coding in Septima](#coding-in-septima)
|
|
54
|
+
- [Functions](#functions)
|
|
55
|
+
- [Extended Operators](#extended-operators)
|
|
56
|
+
- [Built-in Methods](#built-in-methods)
|
|
57
|
+
- [Console Output](#console-output-for-debugging)
|
|
58
|
+
- [Modules](#modules)
|
|
59
|
+
- [Error Handling](#error-handling)
|
|
60
|
+
|
|
61
|
+
## Language Fundamentals
|
|
62
|
+
|
|
63
|
+
Like JavaScript, Septima works with familiar data types like numbers, strings, and booleans. However, its treatment of these types is more strict and predictable than JavaScript's loose type handling.
|
|
64
|
+
|
|
65
|
+
### Numbers and Arithmetic
|
|
66
|
+
|
|
67
|
+
Numbers in Septima work similarly to JavaScript, but without the quirks. There's no automatic type coercion in arithmetic operations.
|
|
68
|
+
|
|
69
|
+
```javascript
|
|
70
|
+
// Similar to JavaScript
|
|
71
|
+
5 // Integer literal
|
|
72
|
+
3.14 // Floating point literal
|
|
73
|
+
8 * 2 // Multiplication
|
|
74
|
+
3 + 1 // Addition
|
|
75
|
+
|
|
76
|
+
// Different from JavaScript - no type coercion
|
|
77
|
+
'5' + 3 // Error: Cannot add string and number
|
|
78
|
+
5 + '3' // Error: Cannot add number and string
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Booleans and Logic
|
|
82
|
+
|
|
83
|
+
Boolean operations in Septima are similar to JavaScript but stricter. They only work with actual boolean values.
|
|
84
|
+
|
|
85
|
+
```javascript
|
|
86
|
+
// Similar to JavaScript
|
|
87
|
+
true || false // Logical OR
|
|
88
|
+
true && false // Logical AND
|
|
89
|
+
|
|
90
|
+
// Different from JavaScript - no truthy/falsy values
|
|
91
|
+
1 && 2 // Error: Expected boolean values
|
|
92
|
+
'' || 'default' // Error: Expected boolean values
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Control Flow and Expressions
|
|
96
|
+
|
|
97
|
+
Unlike JavaScript, all control structures in Septima are expressions that return values.
|
|
98
|
+
|
|
99
|
+
```javascript
|
|
100
|
+
// Different from JavaScript - if expressions return values
|
|
101
|
+
let result = if (4 > 3) 200 else -100 // Valid in Septima
|
|
102
|
+
|
|
103
|
+
// Different from JavaScript - no if statements without else
|
|
104
|
+
if (x > 0) doSomething() // Error in Septima - must have else
|
|
105
|
+
|
|
106
|
+
// Ternary operator works the same as JavaScript
|
|
107
|
+
let result = (4 > 3) ? 200 : -100
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Variables and Immutability
|
|
111
|
+
|
|
112
|
+
One of the biggest differences from JavaScript is Septima's immutability. Variables cannot be reassigned after definition.
|
|
113
|
+
|
|
114
|
+
```javascript
|
|
115
|
+
// Similar to JavaScript - initial definition
|
|
116
|
+
let x = 5
|
|
117
|
+
|
|
118
|
+
// Different from JavaScript - no reassignment
|
|
119
|
+
x = 6 // Error: Cannot reassign variables
|
|
120
|
+
|
|
121
|
+
// Different from JavaScript - no var or const
|
|
122
|
+
var y = 10 // Error: var is not supported
|
|
123
|
+
const z = 15 // Error: const is not supported
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Arrays and Objects
|
|
127
|
+
|
|
128
|
+
Arrays and objects in Septima are immutable by default, unlike their mutable JavaScript counterparts.
|
|
129
|
+
|
|
130
|
+
```javascript
|
|
131
|
+
// Similar to JavaScript - creation and access
|
|
132
|
+
let arr = [1, 2, 3]
|
|
133
|
+
arr[0] // Returns 1
|
|
134
|
+
|
|
135
|
+
// Different from JavaScript - no mutation methods
|
|
136
|
+
arr.push(4) // Error: Arrays are immutable
|
|
137
|
+
arr[0] = 2 // Error: Arrays are immutable
|
|
138
|
+
|
|
139
|
+
// Object behavior
|
|
140
|
+
let obj = { a: 1, b: 2 }
|
|
141
|
+
obj.a // Returns 1
|
|
142
|
+
|
|
143
|
+
// Different from JavaScript - no mutation
|
|
144
|
+
obj.c = 3 // Error: Objects are immutable
|
|
145
|
+
obj.a = 2 // Error: Objects are immutable
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Conversions
|
|
149
|
+
|
|
150
|
+
Unlike JavaScript's automatic type coercion, Septima requires explicit conversion between different types. It provides three conversion functions that closely mirror their JavaScript counterparts in behavior.
|
|
151
|
+
|
|
152
|
+
The `String` function converts any value to its string representation:
|
|
153
|
+
|
|
154
|
+
```javascript
|
|
155
|
+
String(42) // "42"
|
|
156
|
+
String(3.14) // "3.14"
|
|
157
|
+
String(true) // "true"
|
|
158
|
+
String(undefined) // "undefined"
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
For objects and arrays, `String` produces a JSON representation of its argument:
|
|
162
|
+
|
|
163
|
+
```javascript
|
|
164
|
+
String({ a: 1 }) // "{"a":1}"
|
|
165
|
+
String([1, 2]) // "[1,2]"
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
The `Boolean` function implements standard truthiness rules, converting values to `true` or `false`:
|
|
169
|
+
|
|
170
|
+
```javascript
|
|
171
|
+
Boolean(42) // true
|
|
172
|
+
Boolean(0) // false
|
|
173
|
+
Boolean('hello') // true
|
|
174
|
+
Boolean('') // false
|
|
175
|
+
Boolean(undefined) // false
|
|
176
|
+
Boolean({}) // true
|
|
177
|
+
Boolean([]) // true
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
The `Number` function converts values to numbers where possible, returning `NaN` when conversion fails:
|
|
181
|
+
|
|
182
|
+
```javascript
|
|
183
|
+
Number('42') // 42
|
|
184
|
+
Number('3.14') // 3.14
|
|
185
|
+
Number('abc') // NaN
|
|
186
|
+
Number(true) // 1
|
|
187
|
+
Number(false) // 0
|
|
188
|
+
Number(undefined) // NaN
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Unlike JavaScript, Septima requires these explicit conversions and does not perform automatic type coercion:
|
|
192
|
+
|
|
193
|
+
```javascript
|
|
194
|
+
"42" + 7 // Error: Cannot add string and number
|
|
195
|
+
7 + "42" // Error: Cannot add number and string
|
|
196
|
+
let x = if ("hello") 1 else -1 // Error: Condition must be boolean
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## Coding in Septima
|
|
200
|
+
|
|
201
|
+
### Functions
|
|
202
|
+
|
|
203
|
+
Functions in Septima are similar to JavaScript arrow functions, but with some key differences in scope and purity.
|
|
204
|
+
|
|
205
|
+
```javascript
|
|
206
|
+
// Similar to JavaScript - arrow functions
|
|
207
|
+
const double = x => x * 2
|
|
208
|
+
|
|
209
|
+
// Different from JavaScript - no function declarations
|
|
210
|
+
function double(x) {
|
|
211
|
+
return x * 2
|
|
212
|
+
} // Error: Use arrow syntax
|
|
213
|
+
|
|
214
|
+
// Different from JavaScript - pure functions
|
|
215
|
+
let counter = 0
|
|
216
|
+
let increment = () => counter++ // Error: Cannot modify external state
|
|
217
|
+
|
|
218
|
+
// Different from JavaScript - no this binding
|
|
219
|
+
let obj = {
|
|
220
|
+
name: 'test',
|
|
221
|
+
getName: function () {
|
|
222
|
+
return this.name
|
|
223
|
+
}, // Error: No this keyword
|
|
224
|
+
}
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### Extended Operators
|
|
228
|
+
|
|
229
|
+
#### Spread Operator (...)
|
|
230
|
+
|
|
231
|
+
The spread operator creates shallow copies of arrays and objects:
|
|
232
|
+
|
|
233
|
+
```javascript
|
|
234
|
+
// Objects
|
|
235
|
+
let user = { name: 'Sam', id: 123 }
|
|
236
|
+
let userWithRole = { ...user, role: 'admin' } // { name: 'Sam', id: 123, role: 'admin' }
|
|
237
|
+
|
|
238
|
+
// Arrays
|
|
239
|
+
let numbers = [1, 2, 3]
|
|
240
|
+
let moreNumbers = [...numbers, 4, 5] // [1, 2, 3, 4, 5]
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
#### Nullish Coalescing (??)
|
|
244
|
+
|
|
245
|
+
The nullish coalescing operator provides a way to handle undefined values:
|
|
246
|
+
|
|
247
|
+
```javascript
|
|
248
|
+
let config = {
|
|
249
|
+
port: undefined,
|
|
250
|
+
host: 'localhost',
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
let port = config.port ?? 8080 // Returns 8080 (fallback when undefined)
|
|
254
|
+
let host = config.host ?? 'default' // Returns 'localhost' (keeps defined value)
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
Note: Unlike JavaScript, Septima doesn't have `null`, so the nullish coalescing operator only works with `undefined`.
|
|
258
|
+
|
|
259
|
+
### Built-in Methods
|
|
260
|
+
|
|
261
|
+
Septima provides common methods for working with arrays, strings, and objects. These methods follow JavaScript conventions while maintaining Septima's no-side-effects guarantee. As such, methods such as Array.push() are not supported.
|
|
262
|
+
|
|
263
|
+
#### Modifying Arrays
|
|
264
|
+
|
|
265
|
+
Instead of mutating methods like `push()` use the spread operator to create new arrays:
|
|
266
|
+
|
|
267
|
+
```javascript
|
|
268
|
+
let nums = [1, 2, 3]
|
|
269
|
+
nums.push(4) // Error: Array.push is not supported
|
|
270
|
+
[...nums, 4] // Returns [1, 2, 3, 4]
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
#### Array and String Methods
|
|
274
|
+
|
|
275
|
+
Transform strings and arrays using familiar methods (non-exhaustive list):
|
|
276
|
+
|
|
277
|
+
```javascript
|
|
278
|
+
// String methods
|
|
279
|
+
'hello'.toUpperCase() // Returns "HELLO"
|
|
280
|
+
'hello_world'.split('_') // Returns ['hello', 'world']
|
|
281
|
+
' piano '
|
|
282
|
+
.trim() // Returns "piano"
|
|
283
|
+
|
|
284
|
+
[
|
|
285
|
+
// Array methods
|
|
286
|
+
(1, 2, 3)
|
|
287
|
+
].map(x => x * 2) // Returns [2, 4, 6]
|
|
288
|
+
[(19, 6, 8, 3, 10)].filter(x => x > 5) // Returns [19, 6, 8, 10]
|
|
289
|
+
[(1, 2, 3, 4)].reduce((a, b) => a + b, 0) // Returns 10
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
#### Object Methods
|
|
293
|
+
|
|
294
|
+
Access and transform object properties and structure:
|
|
295
|
+
|
|
296
|
+
```javascript
|
|
297
|
+
let user = {
|
|
298
|
+
name: 'Sam',
|
|
299
|
+
role: 'admin',
|
|
300
|
+
active: true,
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// Get object keys as array
|
|
304
|
+
Object.keys(user) // Returns ['name', 'role', 'active']
|
|
305
|
+
|
|
306
|
+
// Get key-value pairs as array
|
|
307
|
+
Object.entries(user) // Returns [['name', 'Sam'], ['role', 'admin'], ['active', true]]
|
|
308
|
+
|
|
309
|
+
// Create object from key-value pairs
|
|
310
|
+
let pairs = [
|
|
311
|
+
['name', 'Pat'],
|
|
312
|
+
['role', 'user'],
|
|
313
|
+
]
|
|
314
|
+
Object.fromEntries(pairs) // Returns { name: 'Pat', role: 'user' }
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
#### Array.isArray()
|
|
318
|
+
|
|
319
|
+
Checks whether the given input is an array:
|
|
320
|
+
|
|
321
|
+
```javascript
|
|
322
|
+
Array.isArray([1, 2, 3]) // Returns true
|
|
323
|
+
Array.isArray('not array') // Returns false
|
|
324
|
+
Array.isArray({ key: 'val' }) // Returns false
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
#### Cryptographic Hashing
|
|
328
|
+
|
|
329
|
+
Septima provides a secure hashing function through the `crypto.hash224()` method, which computes SHA-224 hashes of any value. The method takes a single argument of any type and returns a hexadecimal string representing the hash.
|
|
330
|
+
|
|
331
|
+
```javascript
|
|
332
|
+
// Hash a simple string
|
|
333
|
+
crypto.hash224('hello') // Returns a 56-character hex string
|
|
334
|
+
|
|
335
|
+
// Hash numbers
|
|
336
|
+
crypto.hash224(42) // Hashes the number 42
|
|
337
|
+
|
|
338
|
+
// Hash complex objects
|
|
339
|
+
crypto.hash224({ name: 'Alice', roles: ['admin', 'user'], settings: { theme: 'dark' } }) // Hashes the entire object structure
|
|
340
|
+
|
|
341
|
+
// Hashes are deterministic but unique per input
|
|
342
|
+
crypto.hash224('A') === crypto.hash224('A') // true (same input = same hash)
|
|
343
|
+
crypto.hash224('A') !== crypto.hash224('B') // true (different input = different hash)
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
Note: Septima's `crypto` object is not intended to be compatible with Node.js's `crypto` module. It provides its own simplified cryptographic utilities specifically designed for Septima's use cases.
|
|
347
|
+
|
|
348
|
+
### Console Output for Debugging
|
|
349
|
+
|
|
350
|
+
Like JavaScript, Septima provides `console.log()` for debugging and monitoring your code. However, unlike JavaScript's `console.log()` which can take multiple arguments, Septima's version accepts only a single argument. In keeping with Septima's functional nature, `console.log()` returns its argument, making it useful within expressions.
|
|
351
|
+
|
|
352
|
+
```javascript
|
|
353
|
+
// Basic logging - single argument only
|
|
354
|
+
console.log('Hello World') // Prints: Hello World
|
|
355
|
+
|
|
356
|
+
// Different from JavaScript - can't log multiple values
|
|
357
|
+
console.log('x', 42) // Error: Too many arguments
|
|
358
|
+
|
|
359
|
+
// Logging within expressions works because console.log returns its argument
|
|
360
|
+
let result = 5 * console.log(3) // Prints: 3, returns: 15
|
|
361
|
+
|
|
362
|
+
// Logging with arrays and objects
|
|
363
|
+
let arr = [1, 2, 3]
|
|
364
|
+
console.log(arr) // Prints: [1, 2, 3]
|
|
365
|
+
let obj = { a: 1, b: 2 }
|
|
366
|
+
console.log(obj) // Prints: {"a":1,"b":2}
|
|
367
|
+
|
|
368
|
+
// To log multiple values, you need to combine them first
|
|
369
|
+
console.log([x, y, z]) // Use an array
|
|
370
|
+
console.log({ x: x, y: y, z: z }) // Or an object
|
|
371
|
+
console.log('x=' + x + ', y=' + y) // Or string concatenation
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
### Modules
|
|
375
|
+
|
|
376
|
+
Septima provides a module system for organizing code across files, supporting exports and namespace imports.
|
|
377
|
+
|
|
378
|
+
#### Exports
|
|
379
|
+
|
|
380
|
+
Use the `export` keyword to expose values from a module:
|
|
381
|
+
|
|
382
|
+
```javascript
|
|
383
|
+
// utils.septima.js
|
|
384
|
+
export let capitalize = str => str.charAt(0).toUpperCase() + str.slice(1)
|
|
385
|
+
|
|
386
|
+
// config.septima.js
|
|
387
|
+
export let timeoutInSeconds = 30
|
|
388
|
+
export let retries = 3
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
Key points:
|
|
392
|
+
|
|
393
|
+
- Only top-level definitions can be exported
|
|
394
|
+
- Multiple exports per file are allowed
|
|
395
|
+
- No default exports
|
|
396
|
+
|
|
397
|
+
#### Imports
|
|
398
|
+
|
|
399
|
+
Import using the namespace pattern:
|
|
400
|
+
|
|
401
|
+
```javascript
|
|
402
|
+
import * as utils from './utils.septima.js'
|
|
403
|
+
import * as config from './config.septima.js'
|
|
404
|
+
|
|
405
|
+
// Use imported values
|
|
406
|
+
let result = utils.capitalize('hello')
|
|
407
|
+
let timeoutInMillis = config.timeoutInSeconds * 1000
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
Key points:
|
|
411
|
+
|
|
412
|
+
- Files must end with `.septima.js`
|
|
413
|
+
- Only namespace imports (`* as`) supported
|
|
414
|
+
- Imports must be at file top
|
|
415
|
+
- Paths are relative to current file
|
|
416
|
+
- No circular dependencies
|
|
417
|
+
|
|
418
|
+
### Error Handling
|
|
419
|
+
|
|
420
|
+
Unlike JavaScript's sometimes forgiving nature, Septima is strict about type checking and provides clear error messages. It doesn't do automatic type coercion or silent failures.
|
|
421
|
+
|
|
422
|
+
```javascript
|
|
423
|
+
// JavaScript allows this
|
|
424
|
+
'5' + 3 // "53"
|
|
425
|
+
|
|
426
|
+
// Septima throws an error
|
|
427
|
+
'5' + 3 // Error: Cannot add string and number
|
|
428
|
+
|
|
429
|
+
// JavaScript returns undefined
|
|
430
|
+
let obj = {}
|
|
431
|
+
obj.nonexistent.prop // TypeError: Cannot read property 'prop' of undefined
|
|
432
|
+
|
|
433
|
+
// Septima provides better error message
|
|
434
|
+
obj.nonexistent.prop // Error: Attempting to access property of undefined
|
|
435
|
+
```
|
package/change-log.md
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
### PR/153
|
|
2
|
+
|
|
3
|
+
- allow semicolons before the expression: `;;; 4.8`
|
|
4
|
+
- allow shorthand notation attributes in an object: `let n = 42; { n }`
|
|
5
|
+
|
|
6
|
+
### PR/163
|
|
7
|
+
|
|
8
|
+
- retire `sink` - it was too confusing + there are some things that undefined (in JS) can do but sink could not
|
|
9
|
+
|
|
10
|
+
### PR/164
|
|
11
|
+
|
|
12
|
+
- `undefined` is now a first class value: `let x = undefined`
|
|
13
|
+
- `{a: undefined, n: 42}` is identical to `{n: 42}`
|
|
14
|
+
- `undefined ?? 42` is `42`
|
|
15
|
+
|
|
16
|
+
### PR/165
|
|
17
|
+
|
|
18
|
+
- spreading `undefined` in an array is a no-op: `[42, ...undefined, 'poo']` is `[42, 'poo']`
|
|
19
|
+
- spreading `undefined` in an object is a no-op: `{n: 42, ...undefined, p: 'poo'}` is `{n: 42, p: 'poo'}`
|
|
20
|
+
- support `String(x)`, `Boolean(x)`, `Number(x)`
|
|
21
|
+
|
|
22
|
+
### PR/168
|
|
23
|
+
|
|
24
|
+
- support `Array.isArray(x)`
|
|
25
|
+
- support `console.log('my message')`
|
|
26
|
+
|
|
27
|
+
### PR/172
|
|
28
|
+
|
|
29
|
+
- supoort `JSON.parse(s)`
|
|
30
|
+
- supoort sorting an array: `[97, 100, 50].sort()`, `['the', 'quick', 'brown', 'fox'].sort((a, b) => a.length - b.length)`
|
|
31
|
+
- fix object comparsion
|
|
32
|
+
|
|
33
|
+
### PR/173
|
|
34
|
+
|
|
35
|
+
- support block comments `4 + /* now five */ 5`
|
|
36
|
+
- support default values for function args: `(a, b = 1000) => a + b`
|
|
37
|
+
- inspect the runtime type of a value via `constructor.name`
|
|
38
|
+
|
|
39
|
+
### PR/174
|
|
40
|
+
|
|
41
|
+
- allow a dangling comma after last formal arg of an arrow function `(a,b,) => a + b`
|
|
42
|
+
|
|
43
|
+
### PR/176
|
|
44
|
+
|
|
45
|
+
- allow computing hash values: `crypto.hash224({a: 1, b: 2})`
|
|
46
|
+
|
|
47
|
+
### PR/177
|
|
48
|
+
|
|
49
|
+
- support throwing: `throw "boom"`
|
|
50
|
+
|
|
51
|
+
### PR/182
|
|
52
|
+
|
|
53
|
+
- support template literals: `` `name is ${x}` ``
|
|
54
|
+
|
|
55
|
+
### PR/183
|
|
56
|
+
|
|
57
|
+
- support `const` keyword (behaves like `let`): `const x = 5`
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const ast_node_1 = require("../src/ast-node");
|
|
4
|
+
const parser_1 = require("../src/parser");
|
|
5
|
+
const scanner_1 = require("../src/scanner");
|
|
6
|
+
const source_code_1 = require("../src/source-code");
|
|
7
|
+
function parse(arg) {
|
|
8
|
+
const parser = new parser_1.Parser(new scanner_1.Scanner(new source_code_1.SourceCode(arg, '<test-file>')));
|
|
9
|
+
const ast = parser.parse();
|
|
10
|
+
return ast;
|
|
11
|
+
}
|
|
12
|
+
describe('parser', () => {
|
|
13
|
+
test('show()', () => {
|
|
14
|
+
expect((0, ast_node_1.show)(parse(`5`))).toEqual('5');
|
|
15
|
+
expect((0, ast_node_1.show)(parse(`fun (x) x*9`))).toEqual('fun (x) (x * 9)');
|
|
16
|
+
});
|
|
17
|
+
test('syntax errors', () => {
|
|
18
|
+
expect(() => parse(`a + #$%x`)).toThrowError('Unparsable input at (<test-file>:1:5..8) #$%x');
|
|
19
|
+
expect(() => parse(`{#$%x: 8}`)).toThrowError('Expected an identifier at (<test-file>:1:2..9) #$%x: 8}');
|
|
20
|
+
expect(() => parse(`"foo" "goo"`)).toThrowError('Loitering input at (<test-file>:1:7..11) "goo"');
|
|
21
|
+
});
|
|
22
|
+
describe('unit', () => {
|
|
23
|
+
test('show', () => {
|
|
24
|
+
expect((0, ast_node_1.show)(parse(`import * as foo from './bar';'a'`))).toEqual(`import * as foo from './bar';\n'a'`);
|
|
25
|
+
expect((0, ast_node_1.show)(parse(`let f = x => x*x; f(2)`))).toEqual(`let f = fun (x) (x * x); f(2)`);
|
|
26
|
+
expect((0, ast_node_1.show)(parse(`let f = x => x*x; let g = n => n+1`))).toEqual(`let f = fun (x) (x * x); let g = fun (n) (n + 1);`);
|
|
27
|
+
expect((0, ast_node_1.show)(parse(`export let a = 1; let b = 2; export let c = 3;`))).toEqual(`export let a = 1; let b = 2; export let c = 3;`);
|
|
28
|
+
});
|
|
29
|
+
test('const keyword', () => {
|
|
30
|
+
expect((0, ast_node_1.show)(parse(`const x = 5; x`))).toEqual(`let x = 5; x`);
|
|
31
|
+
expect((0, ast_node_1.show)(parse(`const f = x => x*x; f(2)`))).toEqual(`let f = fun (x) (x * x); f(2)`);
|
|
32
|
+
expect((0, ast_node_1.show)(parse(`const a = 1; let b = 2; const c = 3`))).toEqual(`let a = 1; let b = 2; let c = 3;`);
|
|
33
|
+
expect((0, ast_node_1.show)(parse(`export const a = 1;`))).toEqual(`export let a = 1;`);
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
describe('expression', () => {
|
|
37
|
+
test('show', () => {
|
|
38
|
+
expect((0, ast_node_1.show)(parse(`'sunday'`))).toEqual(`'sunday'`);
|
|
39
|
+
expect((0, ast_node_1.show)(parse(`true`))).toEqual(`true`);
|
|
40
|
+
expect((0, ast_node_1.show)(parse(`500`))).toEqual(`500`);
|
|
41
|
+
expect((0, ast_node_1.show)(parse(`if (3+4 > 8) "above" else "below"`))).toEqual(`if (((3 + 4) > 8)) 'above' else 'below'`);
|
|
42
|
+
expect((0, ast_node_1.show)(parse(`(3+4 > 8) ? "above" : "below"`))).toEqual(`((3 + 4) > 8) ? 'above' : 'below'`);
|
|
43
|
+
});
|
|
44
|
+
test('can have an optional throw token', () => {
|
|
45
|
+
expect((0, ast_node_1.show)(parse(`throw 'sunday'`))).toEqual(`throw 'sunday'`);
|
|
46
|
+
expect((0, ast_node_1.show)(parse(`let a = 8;throw 'sunday'`))).toEqual(`let a = 8; throw 'sunday'`);
|
|
47
|
+
expect((0, ast_node_1.show)(parse(`{a: [45 + 8*3 > 2 + (throw 'err')]}`))).toEqual(`{a: [((45 + (8 * 3)) > (2 + throw 'err'))]}`);
|
|
48
|
+
expect((0, ast_node_1.show)(parse(`if (5 > 8) 'yes' else throw 'no'`))).toEqual(`if ((5 > 8)) 'yes' else throw 'no'`);
|
|
49
|
+
expect((0, ast_node_1.show)(parse(`let f = x >= 0 ? x : throw 'negative'`))).toEqual(`let f = (x >= 0) ? x : throw 'negative';`);
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
describe('lambda', () => {
|
|
53
|
+
test('show', () => {
|
|
54
|
+
expect((0, ast_node_1.show)(parse(`(a) => a*2`))).toEqual(`fun (a) (a * 2)`);
|
|
55
|
+
expect((0, ast_node_1.show)(parse(`(a, b = {x: 1, y: ['bee', 'camel']}) => a*2 + b.x`))).toEqual(`fun (a, b = {x: 1, y: ['bee', 'camel']}) ((a * 2) + b.x)`);
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
describe('template literal', () => {
|
|
59
|
+
test('show', () => {
|
|
60
|
+
expect((0, ast_node_1.show)(parse('`hello`'))).toEqual('`hello`');
|
|
61
|
+
expect((0, ast_node_1.show)(parse('`hello ${x} world`'))).toEqual('`hello ${x} world`');
|
|
62
|
+
expect((0, ast_node_1.show)(parse('`${a}${b}`'))).toEqual('`${a}${b}`');
|
|
63
|
+
expect((0, ast_node_1.show)(parse('`start ${x + y} end`'))).toEqual('`start ${(x + y)} end`');
|
|
64
|
+
});
|
|
65
|
+
test('span', () => {
|
|
66
|
+
expect((0, ast_node_1.span)(parse('`hello`'))).toEqual({ from: { offset: 0 }, to: { offset: 6 } });
|
|
67
|
+
expect((0, ast_node_1.span)(parse('`hi ${x} bye`'))).toEqual({ from: { offset: 0 }, to: { offset: 12 } });
|
|
68
|
+
});
|
|
69
|
+
test('unterminated template literal', () => {
|
|
70
|
+
expect(() => parse('`hello')).toThrowError('Unterminated template literal');
|
|
71
|
+
expect(() => parse('`hello ${x}')).toThrowError('Unterminated template literal');
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGFyc2VyLnNwZWMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90ZXN0cy9wYXJzZXIuc3BlYy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUFBLDhDQUE0QztBQUM1QywwQ0FBc0M7QUFDdEMsNENBQXdDO0FBQ3hDLG9EQUErQztBQUUvQyxTQUFTLEtBQUssQ0FBQyxHQUFXO0lBQ3hCLE1BQU0sTUFBTSxHQUFHLElBQUksZUFBTSxDQUFDLElBQUksaUJBQU8sQ0FBQyxJQUFJLHdCQUFVLENBQUMsR0FBRyxFQUFFLGFBQWEsQ0FBQyxDQUFDLENBQUMsQ0FBQTtJQUMxRSxNQUFNLEdBQUcsR0FBRyxNQUFNLENBQUMsS0FBSyxFQUFFLENBQUE7SUFDMUIsT0FBTyxHQUFHLENBQUE7QUFDWixDQUFDO0FBRUQsUUFBUSxDQUFDLFFBQVEsRUFBRSxHQUFHLEVBQUU7SUFDdEIsSUFBSSxDQUFDLFFBQVEsRUFBRSxHQUFHLEVBQUU7UUFDbEIsTUFBTSxDQUFDLElBQUEsZUFBSSxFQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxDQUFBO1FBQ3JDLE1BQU0sQ0FBQyxJQUFBLGVBQUksRUFBQyxLQUFLLENBQUMsYUFBYSxDQUFDLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxpQkFBaUIsQ0FBQyxDQUFBO0lBQy9ELENBQUMsQ0FBQyxDQUFBO0lBQ0YsSUFBSSxDQUFDLGVBQWUsRUFBRSxHQUFHLEVBQUU7UUFDekIsTUFBTSxDQUFDLEdBQUcsRUFBRSxDQUFDLEtBQUssQ0FBQyxVQUFVLENBQUMsQ0FBQyxDQUFDLFlBQVksQ0FBQywrQ0FBK0MsQ0FBQyxDQUFBO1FBQzdGLE1BQU0sQ0FBQyxHQUFHLEVBQUUsQ0FBQyxLQUFLLENBQUMsV0FBVyxDQUFDLENBQUMsQ0FBQyxZQUFZLENBQUMseURBQXlELENBQUMsQ0FBQTtRQUN4RyxNQUFNLENBQUMsR0FBRyxFQUFFLENBQUMsS0FBSyxDQUFDLGFBQWEsQ0FBQyxDQUFDLENBQUMsWUFBWSxDQUFDLGdEQUFnRCxDQUFDLENBQUE7SUFDbkcsQ0FBQyxDQUFDLENBQUE7SUFFRixRQUFRLENBQUMsTUFBTSxFQUFFLEdBQUcsRUFBRTtRQUNwQixJQUFJLENBQUMsTUFBTSxFQUFFLEdBQUcsRUFBRTtZQUNoQixNQUFNLENBQUMsSUFBQSxlQUFJLEVBQUMsS0FBSyxDQUFDLGtDQUFrQyxDQUFDLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxvQ0FBb0MsQ0FBQyxDQUFBO1lBQ3JHLE1BQU0sQ0FBQyxJQUFBLGVBQUksRUFBQyxLQUFLLENBQUMsd0JBQXdCLENBQUMsQ0FBQyxDQUFDLENBQUMsT0FBTyxDQUFDLCtCQUErQixDQUFDLENBQUE7WUFDdEYsTUFBTSxDQUFDLElBQUEsZUFBSSxFQUFDLEtBQUssQ0FBQyxvQ0FBb0MsQ0FBQyxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQy9ELG1EQUFtRCxDQUNwRCxDQUFBO1lBQ0QsTUFBTSxDQUFDLElBQUEsZUFBSSxFQUFDLEtBQUssQ0FBQyxnREFBZ0QsQ0FBQyxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQzNFLGdEQUFnRCxDQUNqRCxDQUFBO1FBQ0gsQ0FBQyxDQUFDLENBQUE7UUFDRixJQUFJLENBQUMsZUFBZSxFQUFFLEdBQUcsRUFBRTtZQUN6QixNQUFNLENBQUMsSUFBQSxlQUFJLEVBQUMsS0FBSyxDQUFDLGdCQUFnQixDQUFDLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxjQUFjLENBQUMsQ0FBQTtZQUM3RCxNQUFNLENBQUMsSUFBQSxlQUFJLEVBQUMsS0FBSyxDQUFDLDBCQUEwQixDQUFDLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQywrQkFBK0IsQ0FBQyxDQUFBO1lBQ3hGLE1BQU0sQ0FBQyxJQUFBLGVBQUksRUFBQyxLQUFLLENBQUMscUNBQXFDLENBQUMsQ0FBQyxDQUFDLENBQUMsT0FBTyxDQUFDLGtDQUFrQyxDQUFDLENBQUE7WUFDdEcsTUFBTSxDQUFDLElBQUEsZUFBSSxFQUFDLEtBQUssQ0FBQyxxQkFBcUIsQ0FBQyxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQUMsbUJBQW1CLENBQUMsQ0FBQTtRQUN6RSxDQUFDLENBQUMsQ0FBQTtJQUNKLENBQUMsQ0FBQyxDQUFBO0lBQ0YsUUFBUSxDQUFDLFlBQVksRUFBRSxHQUFHLEVBQUU7UUFDMUIsSUFBSSxDQUFDLE1BQU0sRUFBRSxHQUFHLEVBQUU7WUFDaEIsTUFBTSxDQUFDLElBQUEsZUFBSSxFQUFDLEtBQUssQ0FBQyxVQUFVLENBQUMsQ0FBQyxDQUFDLENBQUMsT0FBTyxDQUFDLFVBQVUsQ0FBQyxDQUFBO1lBQ25ELE1BQU0sQ0FBQyxJQUFBLGVBQUksRUFBQyxLQUFLLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxNQUFNLENBQUMsQ0FBQTtZQUMzQyxNQUFNLENBQUMsSUFBQSxlQUFJLEVBQUMsS0FBSyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQUMsS0FBSyxDQUFDLENBQUE7WUFDekMsTUFBTSxDQUFDLElBQUEsZUFBSSxFQUFDLEtBQUssQ0FBQyxtQ0FBbUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQUMseUNBQXlDLENBQUMsQ0FBQTtZQUMzRyxNQUFNLENBQUMsSUFBQSxlQUFJLEVBQUMsS0FBSyxDQUFDLCtCQUErQixDQUFDLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxtQ0FBbUMsQ0FBQyxDQUFBO1FBQ25HLENBQUMsQ0FBQyxDQUFBO1FBQ0YsSUFBSSxDQUFDLGtDQUFrQyxFQUFFLEdBQUcsRUFBRTtZQUM1QyxNQUFNLENBQUMsSUFBQSxlQUFJLEVBQUMsS0FBSyxDQUFDLGdCQUFnQixDQUFDLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFBO1lBQy9ELE1BQU0sQ0FBQyxJQUFBLGVBQUksRUFBQyxLQUFLLENBQUMsMEJBQTBCLENBQUMsQ0FBQyxDQUFDLENBQUMsT0FBTyxDQUFDLDJCQUEyQixDQUFDLENBQUE7WUFDcEYsTUFBTSxDQUFDLElBQUEsZUFBSSxFQUFDLEtBQUssQ0FBQyxxQ0FBcUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQUMsNkNBQTZDLENBQUMsQ0FBQTtZQUNqSCxNQUFNLENBQUMsSUFBQSxlQUFJLEVBQUMsS0FBSyxDQUFDLGtDQUFrQyxDQUFDLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxvQ0FBb0MsQ0FBQyxDQUFBO1lBQ3JHLE1BQU0sQ0FBQyxJQUFBLGVBQUksRUFBQyxLQUFLLENBQUMsdUNBQXVDLENBQUMsQ0FBQyxDQUFDLENBQUMsT0FBTyxDQUFDLDBDQUEwQyxDQUFDLENBQUE7UUFDbEgsQ0FBQyxDQUFDLENBQUE7SUFDSixDQUFDLENBQUMsQ0FBQTtJQUNGLFFBQVEsQ0FBQyxRQUFRLEVBQUUsR0FBRyxFQUFFO1FBQ3RCLElBQUksQ0FBQyxNQUFNLEVBQUUsR0FBRyxFQUFFO1lBQ2hCLE1BQU0sQ0FBQyxJQUFBLGVBQUksRUFBQyxLQUFLLENBQUMsWUFBWSxDQUFDLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxpQkFBaUIsQ0FBQyxDQUFBO1lBQzVELE1BQU0sQ0FBQyxJQUFBLGVBQUksRUFBQyxLQUFLLENBQUMsbURBQW1ELENBQUMsQ0FBQyxDQUFDLENBQUMsT0FBTyxDQUM5RSwwREFBMEQsQ0FDM0QsQ0FBQTtRQUNILENBQUMsQ0FBQyxDQUFBO0lBQ0osQ0FBQyxDQUFDLENBQUE7SUFDRixRQUFRLENBQUMsa0JBQWtCLEVBQUUsR0FBRyxFQUFFO1FBQ2hDLElBQUksQ0FBQyxNQUFNLEVBQUUsR0FBRyxFQUFFO1lBQ2hCLE1BQU0sQ0FBQyxJQUFBLGVBQUksRUFBQyxLQUFLLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxTQUFTLENBQUMsQ0FBQTtZQUNqRCxNQUFNLENBQUMsSUFBQSxlQUFJLEVBQUMsS0FBSyxDQUFDLG9CQUFvQixDQUFDLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxvQkFBb0IsQ0FBQyxDQUFBO1lBQ3ZFLE1BQU0sQ0FBQyxJQUFBLGVBQUksRUFBQyxLQUFLLENBQUMsWUFBWSxDQUFDLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxZQUFZLENBQUMsQ0FBQTtZQUN2RCxNQUFNLENBQUMsSUFBQSxlQUFJLEVBQUMsS0FBSyxDQUFDLHNCQUFzQixDQUFDLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQyx3QkFBd0IsQ0FBQyxDQUFBO1FBQy9FLENBQUMsQ0FBQyxDQUFBO1FBQ0YsSUFBSSxDQUFDLE1BQU0sRUFBRSxHQUFHLEVBQUU7WUFDaEIsTUFBTSxDQUFDLElBQUEsZUFBSSxFQUFDLEtBQUssQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDLENBQUMsT0FBTyxDQUFDLEVBQUUsSUFBSSxFQUFFLEVBQUUsTUFBTSxFQUFFLENBQUMsRUFBRSxFQUFFLEVBQUUsRUFBRSxFQUFFLE1BQU0sRUFBRSxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUE7WUFDbEYsTUFBTSxDQUFDLElBQUEsZUFBSSxFQUFDLEtBQUssQ0FBQyxlQUFlLENBQUMsQ0FBQyxDQUFDLENBQUMsT0FBTyxDQUFDLEVBQUUsSUFBSSxFQUFFLEVBQUUsTUFBTSxFQUFFLENBQUMsRUFBRSxFQUFFLEVBQUUsRUFBRSxFQUFFLE1BQU0sRUFBRSxFQUFFLEVBQUUsRUFBRSxDQUFDLENBQUE7UUFDM0YsQ0FBQyxDQUFDLENBQUE7UUFDRixJQUFJLENBQUMsK0JBQStCLEVBQUUsR0FBRyxFQUFFO1lBQ3pDLE1BQU0sQ0FBQyxHQUFHLEVBQUUsQ0FBQyxLQUFLLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQyxZQUFZLENBQUMsK0JBQStCLENBQUMsQ0FBQTtZQUMzRSxNQUFNLENBQUMsR0FBRyxFQUFFLENBQUMsS0FBSyxDQUFDLGFBQWEsQ0FBQyxDQUFDLENBQUMsWUFBWSxDQUFDLCtCQUErQixDQUFDLENBQUE7UUFDbEYsQ0FBQyxDQUFDLENBQUE7SUFDSixDQUFDLENBQUMsQ0FBQTtBQUNKLENBQUMsQ0FBQyxDQUFBIn0=
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|