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.
@@ -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(input)
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('error on arg list mismatch', () => {
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(`libA.plus10(4) + libA.plus20(2)`, {
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(`a.calc(4) + b.calc(1)`, {
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
  })