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.
@@ -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)
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
  })