septima-lang 0.0.3 → 0.0.6
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 +7 -2
- package/dist/src/parser.js +74 -13
- package/dist/src/runtime.d.ts +6 -2
- package/dist/src/runtime.js +61 -4
- package/dist/src/scanner.d.ts +1 -1
- package/dist/src/scanner.js +1 -1
- package/dist/src/septima.d.ts +6 -6
- package/dist/src/septima.js +26 -14
- 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 +36 -0
- package/dist/tests/septima.spec.js +60 -12
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/jest-output.json +1 -1
- package/package.json +1 -1
- package/src/ast-node.ts +52 -8
- package/src/parser.ts +82 -15
- package/src/runtime.ts +70 -4
- package/src/scanner.ts +1 -1
- package/src/septima.ts +41 -12
- package/src/symbol-table.ts +1 -0
- package/tests/parser.spec.ts +16 -0
- package/tests/septima-compute-module.spec.ts +41 -0
- package/tests/septima.spec.ts +69 -11
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, {}, 'quiet', {})
|
|
20
20
|
|
|
21
21
|
if (res.tag !== 'sink') {
|
|
22
22
|
throw new Error(`Not a sink: ${res.value}`)
|
|
@@ -84,6 +84,7 @@ describe('septima', () => {
|
|
|
84
84
|
const expected = [
|
|
85
85
|
`value type error: expected num but found "zxcvbnm" when evaluating:`,
|
|
86
86
|
` at (1:1..21) 9 * 8 * 'zxcvbnm' * 7`,
|
|
87
|
+
` at (1:1..21) 9 * 8 * 'zxcvbnm' * 7`,
|
|
87
88
|
` at (1:5..21) 8 * 'zxcvbnm' * 7`,
|
|
88
89
|
` at (1:10..21) zxcvbnm' * 7`,
|
|
89
90
|
].join('\n')
|
|
@@ -270,6 +271,12 @@ describe('septima', () => {
|
|
|
270
271
|
expect(run(`["ab", 5]`)).toEqual(['ab', 5])
|
|
271
272
|
expect(run(`[]`)).toEqual([])
|
|
272
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
|
+
})
|
|
273
280
|
test('individual elements of an array can be accessed via the [<index>] notation', () => {
|
|
274
281
|
expect(run(`let a = ['sun', 'mon', 'tue', 'wed']; a[1]`)).toEqual('mon')
|
|
275
282
|
})
|
|
@@ -424,6 +431,10 @@ describe('septima', () => {
|
|
|
424
431
|
expect(run(`let triple = (fun(a) 3*a); triple(100) - triple(90)`)).toEqual(30)
|
|
425
432
|
expect(run(`let triple = fun(a) 3*a; triple(100) - triple(90)`)).toEqual(30)
|
|
426
433
|
})
|
|
434
|
+
test('allows a dangling comma, at the call site, after the last actual argument', () => {
|
|
435
|
+
expect(run(`let triple = (fun(a) 3*a); triple(100,)`)).toEqual(300)
|
|
436
|
+
expect(run(`let mean = (fun(a,b) (a+b)/2); mean(4, 28,)`)).toEqual(16)
|
|
437
|
+
})
|
|
427
438
|
describe('arrow function notation', () => {
|
|
428
439
|
test('a single formal argument does not need to be surrounded with parenthesis', () => {
|
|
429
440
|
expect(run(`let triple = a => 3*a; triple(100)`)).toEqual(300)
|
|
@@ -455,7 +466,7 @@ describe('septima', () => {
|
|
|
455
466
|
expect(run(`let pi = fun() 3.14; 2*pi()`)).toEqual(6.28)
|
|
456
467
|
expect(run(`(fun() 3.14)()*2`)).toEqual(6.28)
|
|
457
468
|
})
|
|
458
|
-
test('
|
|
469
|
+
test('errors on arg list mismatch', () => {
|
|
459
470
|
expect(() => run(`let quadSum = fun(a,b,c,d) a+b+c+d; quadSum(4,8,2)`)).toThrowError(
|
|
460
471
|
'Arg list length mismatch: expected 4 but got 3',
|
|
461
472
|
)
|
|
@@ -515,6 +526,9 @@ describe('septima', () => {
|
|
|
515
526
|
expect(run(`let f = fun (a) if (a > 0) a else sink; 2+f(-1)+4`)).toEqual(undefined)
|
|
516
527
|
expect(run(`let f = fun (a) if (a > 0) a else sink; 2+f(3)+4`)).toEqual(9)
|
|
517
528
|
})
|
|
529
|
+
test('the "undefined" literal is an alias for "sink"', () => {
|
|
530
|
+
expect(run(`let x = sink; 5+8+9+x+20+30`)).toEqual(undefined)
|
|
531
|
+
})
|
|
518
532
|
test('an array can hold a sink without becoming a sink itself', () => {
|
|
519
533
|
expect(run(`let f = fun (a) if (a > 0) a else sink; [f(1), f(-1), f(8)]`)).toEqual([1, undefined, 8])
|
|
520
534
|
})
|
|
@@ -606,6 +620,7 @@ describe('septima', () => {
|
|
|
606
620
|
test(`captures the expression trace at runtime`, () => {
|
|
607
621
|
expect(runSink(`1000 + 2000 + 3000 + sink!`).trace).toEqual(
|
|
608
622
|
[
|
|
623
|
+
` at (1:1..26) 1000 + 2000 + 3000 + sink!`,
|
|
609
624
|
` at (1:1..26) 1000 + 2000 + 3000 + sink!`,
|
|
610
625
|
` at (1:8..26) 2000 + 3000 + sink!`,
|
|
611
626
|
` at (1:15..26) 3000 + sink!`,
|
|
@@ -613,6 +628,13 @@ describe('septima', () => {
|
|
|
613
628
|
].join('\n'),
|
|
614
629
|
)
|
|
615
630
|
})
|
|
631
|
+
test(`can also appear in code as undefined!`, () => {
|
|
632
|
+
expect(runSink(`1000 + undefined!`).trace).toEqual(
|
|
633
|
+
[` at (1:1..17) 1000 + undefined!`, ` at (1:1..17) 1000 + undefined!`, ` at (1:8..17) undefined!`].join(
|
|
634
|
+
'\n',
|
|
635
|
+
),
|
|
636
|
+
)
|
|
637
|
+
})
|
|
616
638
|
})
|
|
617
639
|
describe('sink!!', () => {
|
|
618
640
|
test(`captures the expression trace and the symbol-table at runtime`, () => {
|
|
@@ -623,9 +645,10 @@ describe('septima', () => {
|
|
|
623
645
|
x: 30,
|
|
624
646
|
y: 40,
|
|
625
647
|
})
|
|
626
|
-
expect(Object.keys(actual.symbols ?? {})).toEqual(['Object', 'a', 'f', 'x', 'y'])
|
|
648
|
+
expect(Object.keys(actual.symbols ?? {})).toEqual(['Object', 'args', 'a', 'f', 'x', 'y'])
|
|
627
649
|
expect(actual.trace).toEqual(
|
|
628
650
|
[
|
|
651
|
+
` at (1:1..58) let a = 2; let f = fun(x, y) x * y * sink!! * a; f(30, 40)`,
|
|
629
652
|
` at (1:1..58) let a = 2; let f = fun(x, y) x * y * sink!! * a; f(30, 40)`,
|
|
630
653
|
` at (1:50..58) f(30, 40)`,
|
|
631
654
|
` at (1:30..47) x * y * sink!! * a`,
|
|
@@ -640,6 +663,11 @@ describe('septima', () => {
|
|
|
640
663
|
n: -3,
|
|
641
664
|
})
|
|
642
665
|
})
|
|
666
|
+
test(`can also appear in code as undefined!!`, () => {
|
|
667
|
+
expect(runSink(`let f = fun (n) undefined!!; f(18)`).symbols).toMatchObject({
|
|
668
|
+
n: 18,
|
|
669
|
+
})
|
|
670
|
+
})
|
|
643
671
|
})
|
|
644
672
|
|
|
645
673
|
describe('array methods', () => {
|
|
@@ -788,23 +816,51 @@ describe('septima', () => {
|
|
|
788
816
|
})
|
|
789
817
|
describe('preimport', () => {
|
|
790
818
|
test('definitions from a preimported file can be used', () => {
|
|
791
|
-
const septima = new Septima(
|
|
819
|
+
const septima = new Septima()
|
|
820
|
+
|
|
821
|
+
const input = `libA.plus10(4) + libA.plus20(2)`
|
|
822
|
+
const preimports = {
|
|
792
823
|
libA: `{ plus10: fun (n) n+10, plus20: fun (n) n+20}`,
|
|
793
|
-
}
|
|
794
|
-
expect(septima.compute()).toMatchObject({ value: 36 })
|
|
824
|
+
}
|
|
825
|
+
expect(septima.compute(input, preimports, 'quiet', {})).toMatchObject({ value: 36 })
|
|
795
826
|
})
|
|
796
827
|
test('supports multiple preimports', () => {
|
|
797
|
-
const septima = new Septima(
|
|
828
|
+
const septima = new Septima()
|
|
829
|
+
|
|
830
|
+
const input = `a.calc(4) + b.calc(1)`
|
|
831
|
+
const preimports = {
|
|
798
832
|
a: `{ calc: fun (n) n+10 }`,
|
|
799
833
|
b: `{ calc: fun (n) n+20 }`,
|
|
800
|
-
}
|
|
801
|
-
expect(septima.compute()).toMatchObject({ value: 35 })
|
|
834
|
+
}
|
|
835
|
+
expect(septima.compute(input, preimports, 'quiet', {})).toMatchObject({ value: 35 })
|
|
836
|
+
})
|
|
837
|
+
})
|
|
838
|
+
describe('args', () => {
|
|
839
|
+
test('are bounded at runtime to a special variable called "args"', () => {
|
|
840
|
+
const septima = new Septima()
|
|
841
|
+
expect(
|
|
842
|
+
septima.compute(`args.a + '_' + args.color[0] + '_' + args.b + '_' + args.color[1]`, {}, 'quiet', {
|
|
843
|
+
a: 'Sunday',
|
|
844
|
+
b: 'Monday',
|
|
845
|
+
color: ['Red', 'Green'],
|
|
846
|
+
}),
|
|
847
|
+
).toMatchObject({ value: 'Sunday_Red_Monday_Green' })
|
|
848
|
+
})
|
|
849
|
+
test('are shadowed by a program-defined "args" symbol', () => {
|
|
850
|
+
const septima = new Septima()
|
|
851
|
+
expect(
|
|
852
|
+
septima.compute(`let args = {color: 'Green' }; args.color`, {}, 'quiet', {
|
|
853
|
+
color: 'Red',
|
|
854
|
+
}),
|
|
855
|
+
).toMatchObject({ value: 'Green' })
|
|
856
|
+
})
|
|
857
|
+
test('are supported also via the Septima.run() API', () => {
|
|
858
|
+
expect(Septima.run(`args.a + args.b`, undefined, { a: 100, b: 2 })).toEqual(102)
|
|
802
859
|
})
|
|
803
860
|
})
|
|
804
861
|
test.todo('support file names in locations')
|
|
805
862
|
test.todo('string interpolation via `foo` strings')
|
|
806
863
|
test.todo('imports')
|
|
807
|
-
test.todo('arrow functions')
|
|
808
864
|
test.todo('optional parameters')
|
|
809
865
|
test.todo('optional type annotations?')
|
|
810
866
|
test.todo('allow redundant commas')
|
|
@@ -819,4 +875,6 @@ describe('septima', () => {
|
|
|
819
875
|
test.todo('number methods')
|
|
820
876
|
test.todo('drop the fun () notation and use just arrow functions')
|
|
821
877
|
test.todo('proper internal representation of arrow function, in particular: show(), span()')
|
|
878
|
+
test.todo('sink sinkifies arrays and objects it is stored at')
|
|
879
|
+
test.todo('{foo}')
|
|
822
880
|
})
|