septima-lang 0.0.2 → 0.0.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.
- package/dist/src/ast-node.d.ts +18 -5
- package/dist/src/ast-node.js +27 -2
- package/dist/src/parser.d.ts +9 -2
- package/dist/src/parser.js +126 -13
- package/dist/src/runtime.d.ts +6 -4
- package/dist/src/runtime.js +62 -6
- package/dist/src/scanner.d.ts +3 -2
- package/dist/src/scanner.js +16 -4
- package/dist/src/septima.d.ts +6 -7
- package/dist/src/septima.js +44 -16
- package/dist/src/symbol-table.d.ts +1 -0
- package/dist/tests/parser.spec.js +16 -1
- package/dist/tests/septima-compute-module.spec.d.ts +1 -0
- package/dist/tests/septima-compute-module.spec.js +30 -0
- package/dist/tests/septima.spec.js +85 -3
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/jest-output.json +1 -1
- package/package.json +1 -1
- package/src/a.js +66 -0
- package/src/ast-node.ts +52 -8
- package/src/parser.ts +143 -16
- package/src/runtime.ts +71 -6
- package/src/scanner.ts +16 -4
- package/src/septima.ts +54 -18
- package/src/symbol-table.ts +1 -0
- package/tests/parser.spec.ts +16 -0
- package/tests/septima-compute-module.spec.ts +33 -0
- package/tests/septima.spec.ts +86 -2
package/tests/septima.spec.ts
CHANGED
|
@@ -15,8 +15,8 @@ function run(input: string) {
|
|
|
15
15
|
* @param input the Septima program to run
|
|
16
16
|
*/
|
|
17
17
|
function runSink(input: string) {
|
|
18
|
-
const septima = new Septima(
|
|
19
|
-
const res = septima.compute()
|
|
18
|
+
const septima = new Septima()
|
|
19
|
+
const res = septima.compute(input)
|
|
20
20
|
|
|
21
21
|
if (res.tag !== 'sink') {
|
|
22
22
|
throw new Error(`Not a sink: ${res.value}`)
|
|
@@ -30,6 +30,10 @@ describe('septima', () => {
|
|
|
30
30
|
expect(() => run(`6 789`)).toThrowError(`Loitering input at (1:3..5) 789`)
|
|
31
31
|
expect(run(`3.14`)).toEqual(3.14)
|
|
32
32
|
})
|
|
33
|
+
test('an optional return keyword can be placed before the result', () => {
|
|
34
|
+
expect(run(`return 5`)).toEqual(5)
|
|
35
|
+
expect(run(`return 3.14`)).toEqual(3.14)
|
|
36
|
+
})
|
|
33
37
|
|
|
34
38
|
test('booleans', () => {
|
|
35
39
|
expect(run(`true`)).toEqual(true)
|
|
@@ -80,6 +84,7 @@ describe('septima', () => {
|
|
|
80
84
|
const expected = [
|
|
81
85
|
`value type error: expected num but found "zxcvbnm" when evaluating:`,
|
|
82
86
|
` at (1:1..21) 9 * 8 * 'zxcvbnm' * 7`,
|
|
87
|
+
` at (1:1..21) 9 * 8 * 'zxcvbnm' * 7`,
|
|
83
88
|
` at (1:5..21) 8 * 'zxcvbnm' * 7`,
|
|
84
89
|
` at (1:10..21) zxcvbnm' * 7`,
|
|
85
90
|
].join('\n')
|
|
@@ -200,6 +205,10 @@ describe('septima', () => {
|
|
|
200
205
|
expect(run(`let x = 5; x+3`)).toEqual(8)
|
|
201
206
|
expect(run(`let x = 5; let y = 20; x*y+4`)).toEqual(104)
|
|
202
207
|
})
|
|
208
|
+
test('do not need the trailing semicolon', () => {
|
|
209
|
+
expect(run(`let x = 5 x+3`)).toEqual(8)
|
|
210
|
+
expect(run(`let x = 5 let y = 20 x*y+4`)).toEqual(104)
|
|
211
|
+
})
|
|
203
212
|
test('fails if the variable was not defined', () => {
|
|
204
213
|
expect(() => run(`let x = 5; x+y`)).toThrowError('Symbol y was not found')
|
|
205
214
|
})
|
|
@@ -262,6 +271,12 @@ describe('septima', () => {
|
|
|
262
271
|
expect(run(`["ab", 5]`)).toEqual(['ab', 5])
|
|
263
272
|
expect(run(`[]`)).toEqual([])
|
|
264
273
|
})
|
|
274
|
+
test('allow a dangling comma', () => {
|
|
275
|
+
expect(run(`[,]`)).toEqual([])
|
|
276
|
+
expect(run(`[,,]`)).toEqual([])
|
|
277
|
+
expect(run(`[246,]`)).toEqual([246])
|
|
278
|
+
expect(run(`[246,531,]`)).toEqual([246, 531])
|
|
279
|
+
})
|
|
265
280
|
test('individual elements of an array can be accessed via the [<index>] notation', () => {
|
|
266
281
|
expect(run(`let a = ['sun', 'mon', 'tue', 'wed']; a[1]`)).toEqual('mon')
|
|
267
282
|
})
|
|
@@ -282,6 +297,14 @@ describe('septima', () => {
|
|
|
282
297
|
expect(run(`{a: 1, b: 2}`)).toEqual({ a: 1, b: 2 })
|
|
283
298
|
expect(run(`{a: "A", b: "B", c: "CCC"}`)).toEqual({ a: 'A', b: 'B', c: 'CCC' })
|
|
284
299
|
})
|
|
300
|
+
test('allow a dangling comma', () => {
|
|
301
|
+
expect(run(`{a: 1,}`)).toEqual({ a: 1 })
|
|
302
|
+
expect(run(`{a: 1, b: 2,}`)).toEqual({ a: 1, b: 2 })
|
|
303
|
+
expect(run(`{a: "A", b: "B", c: "CCC",}`)).toEqual({ a: 'A', b: 'B', c: 'CCC' })
|
|
304
|
+
})
|
|
305
|
+
test('a dangling comma in an empty object is not allowed', () => {
|
|
306
|
+
expect(() => run(`{,}`)).toThrowError('Expected an identifier at (1:2..3) ,}')
|
|
307
|
+
})
|
|
285
308
|
test('supports computed attributes names via the [<expression>]: <value> notation', () => {
|
|
286
309
|
expect(run(`{["a" + 'b']: 'a-and-b'}`)).toEqual({ ab: 'a-and-b' })
|
|
287
310
|
})
|
|
@@ -408,6 +431,33 @@ describe('septima', () => {
|
|
|
408
431
|
expect(run(`let triple = (fun(a) 3*a); triple(100) - triple(90)`)).toEqual(30)
|
|
409
432
|
expect(run(`let triple = fun(a) 3*a; triple(100) - triple(90)`)).toEqual(30)
|
|
410
433
|
})
|
|
434
|
+
describe('arrow function notation', () => {
|
|
435
|
+
test('a single formal argument does not need to be surrounded with parenthesis', () => {
|
|
436
|
+
expect(run(`let triple = a => 3*a; triple(100)`)).toEqual(300)
|
|
437
|
+
})
|
|
438
|
+
test('(a) => <expression>', () => {
|
|
439
|
+
expect(run(`let triple = (a) => 3*a; triple(100)`)).toEqual(300)
|
|
440
|
+
})
|
|
441
|
+
test('() => <expression>', () => {
|
|
442
|
+
expect(run(`let five = () => 5; five()`)).toEqual(5)
|
|
443
|
+
})
|
|
444
|
+
test('(a,b) => <expression>', () => {
|
|
445
|
+
expect(run(`let conc = (a,b) => a+b; conc('al', 'pha')`)).toEqual('alpha')
|
|
446
|
+
expect(run(`let conc = (a,b,c,d,e,f) => a+b+c+d+e+f; conc('M', 'o', 'n', 'd', 'a', 'y')`)).toEqual('Monday')
|
|
447
|
+
})
|
|
448
|
+
test('body of an arrow function can be { return <expression>}', () => {
|
|
449
|
+
expect(run(`let triple = a => { return 3*a }; triple(100)`)).toEqual(300)
|
|
450
|
+
expect(run(`let triple = (a) => { return 3*a }; triple(100)`)).toEqual(300)
|
|
451
|
+
expect(run(`let five = () => { return 5 }; five()`)).toEqual(5)
|
|
452
|
+
expect(run(`let concat = (a,b) => { return a+b }; concat('al', 'pha')`)).toEqual('alpha')
|
|
453
|
+
})
|
|
454
|
+
test('body of an arrow function can include let definitions', () => {
|
|
455
|
+
expect(run(`let triple = a => { let factor = 3; return factor*a }; triple(100)`)).toEqual(300)
|
|
456
|
+
expect(run(`let triple = (a) => { let factor = 3; return 3*a }; triple(100)`)).toEqual(300)
|
|
457
|
+
expect(run(`let five = () => { let two = 2; let three = 3; return three+two }; five()`)).toEqual(5)
|
|
458
|
+
expect(run(`let concat = (a,b) => { let u = '_'; return u+a+b+u }; concat('a', 'b')`)).toEqual('_ab_')
|
|
459
|
+
})
|
|
460
|
+
})
|
|
411
461
|
test('can have no args', () => {
|
|
412
462
|
expect(run(`let pi = fun() 3.14; 2*pi()`)).toEqual(6.28)
|
|
413
463
|
expect(run(`(fun() 3.14)()*2`)).toEqual(6.28)
|
|
@@ -563,6 +613,7 @@ describe('septima', () => {
|
|
|
563
613
|
test(`captures the expression trace at runtime`, () => {
|
|
564
614
|
expect(runSink(`1000 + 2000 + 3000 + sink!`).trace).toEqual(
|
|
565
615
|
[
|
|
616
|
+
` at (1:1..26) 1000 + 2000 + 3000 + sink!`,
|
|
566
617
|
` at (1:1..26) 1000 + 2000 + 3000 + sink!`,
|
|
567
618
|
` at (1:8..26) 2000 + 3000 + sink!`,
|
|
568
619
|
` at (1:15..26) 3000 + sink!`,
|
|
@@ -583,6 +634,7 @@ describe('septima', () => {
|
|
|
583
634
|
expect(Object.keys(actual.symbols ?? {})).toEqual(['Object', 'a', 'f', 'x', 'y'])
|
|
584
635
|
expect(actual.trace).toEqual(
|
|
585
636
|
[
|
|
637
|
+
` at (1:1..58) let a = 2; let f = fun(x, y) x * y * sink!! * a; f(30, 40)`,
|
|
586
638
|
` at (1:1..58) let a = 2; let f = fun(x, y) x * y * sink!! * a; f(30, 40)`,
|
|
587
639
|
` at (1:50..58) f(30, 40)`,
|
|
588
640
|
` at (1:30..47) x * y * sink!! * a`,
|
|
@@ -743,6 +795,34 @@ describe('septima', () => {
|
|
|
743
795
|
expect(run(`let count = fun (n) if (n <= 0) 0 else 1 + count(n-1); count(330)`)).toEqual(330)
|
|
744
796
|
})
|
|
745
797
|
})
|
|
798
|
+
describe('preimport', () => {
|
|
799
|
+
test('definitions from a preimported file can be used', () => {
|
|
800
|
+
const septima = new Septima()
|
|
801
|
+
|
|
802
|
+
const input = `libA.plus10(4) + libA.plus20(2)`
|
|
803
|
+
const preimports = {
|
|
804
|
+
libA: `{ plus10: fun (n) n+10, plus20: fun (n) n+20}`,
|
|
805
|
+
}
|
|
806
|
+
expect(septima.compute(input, preimports)).toMatchObject({ value: 36 })
|
|
807
|
+
})
|
|
808
|
+
test('supports multiple preimports', () => {
|
|
809
|
+
const septima = new Septima()
|
|
810
|
+
|
|
811
|
+
const input = `a.calc(4) + b.calc(1)`
|
|
812
|
+
const preimports = {
|
|
813
|
+
a: `{ calc: fun (n) n+10 }`,
|
|
814
|
+
b: `{ calc: fun (n) n+20 }`,
|
|
815
|
+
}
|
|
816
|
+
expect(septima.compute(input, preimports)).toMatchObject({ value: 35 })
|
|
817
|
+
})
|
|
818
|
+
})
|
|
819
|
+
test.todo('support file names in locations')
|
|
820
|
+
test.todo('string interpolation via `foo` strings')
|
|
821
|
+
test.todo('imports')
|
|
822
|
+
test.todo('arrow functions')
|
|
823
|
+
test.todo('optional parameters')
|
|
824
|
+
test.todo('optional type annotations?')
|
|
825
|
+
test.todo('allow redundant commas')
|
|
746
826
|
test.todo('left associativity of +/-')
|
|
747
827
|
test.todo('comparison of arrays')
|
|
748
828
|
test.todo('comparison of lambdas?')
|
|
@@ -752,4 +832,8 @@ describe('septima', () => {
|
|
|
752
832
|
test.todo('quoting of a ticks inside a string')
|
|
753
833
|
test.todo('number in scientific notation')
|
|
754
834
|
test.todo('number methods')
|
|
835
|
+
test.todo('drop the fun () notation and use just arrow functions')
|
|
836
|
+
test.todo('proper internal representation of arrow function, in particular: show(), span()')
|
|
837
|
+
test.todo('sink sinkifies arrays and objects it is stored at')
|
|
838
|
+
test.todo('{foo}')
|
|
755
839
|
})
|