septima-lang 0.0.1 → 0.0.4

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/src/parser.ts CHANGED
@@ -22,14 +22,18 @@ export class Parser {
22
22
  const ident = this.identifier()
23
23
  this.scanner.consume('=')
24
24
  const value = this.lambda()
25
- this.scanner.consume(';')
26
-
27
25
  ret.push({ start, ident, value })
26
+
27
+ this.scanner.consumeIf(';')
28
+ if (!this.scanner.headMatches('let ')) {
29
+ return ret
30
+ }
28
31
  }
29
32
  }
30
33
 
31
34
  expression(): AstNode {
32
35
  const definitions = this.definitions()
36
+ this.scanner.consumeIf('return')
33
37
  const computation = this.lambda()
34
38
 
35
39
  if (definitions.length === 0) {
@@ -42,7 +46,7 @@ export class Parser {
42
46
  lambda(): AstNode {
43
47
  const start = this.scanner.consumeIf('fun')
44
48
  if (!start) {
45
- return this.ifExpression()
49
+ return this.arrowFunction()
46
50
  }
47
51
 
48
52
  this.scanner.consume('(')
@@ -66,6 +70,60 @@ export class Parser {
66
70
  return { tag: 'lambda', start, formalArgs: args, body }
67
71
  }
68
72
 
73
+ arrowFunction(): AstNode {
74
+ if (this.scanner.headMatches('(', ')', '=>')) {
75
+ const start = this.scanner.consume('(')
76
+ this.scanner.consume(')')
77
+ this.scanner.consume('=>')
78
+ const body = this.lambdaBody()
79
+ return { tag: 'lambda', start, formalArgs: [], body }
80
+ }
81
+ if (this.scanner.headMatches(IDENT_PATTERN, '=>')) {
82
+ const ident = this.identifier()
83
+ this.scanner.consume('=>')
84
+ const body = this.lambdaBody()
85
+ return { tag: 'lambda', start: ident.t, formalArgs: [ident], body }
86
+ }
87
+ if (this.scanner.headMatches('(', IDENT_PATTERN, ')', '=>')) {
88
+ const start = this.scanner.consume('(')
89
+ const ident = this.identifier()
90
+ this.scanner.consume(')')
91
+ this.scanner.consume('=>')
92
+ const body = this.lambdaBody()
93
+ return { tag: 'lambda', start, formalArgs: [ident], body }
94
+ }
95
+ if (this.scanner.headMatches('(', IDENT_PATTERN, ',')) {
96
+ const start = this.scanner.consume('(')
97
+ const formalArgs: Ident[] = []
98
+ while (true) {
99
+ const ident = this.identifier()
100
+ formalArgs.push(ident)
101
+
102
+ if (this.scanner.consumeIf(')')) {
103
+ break
104
+ }
105
+
106
+ this.scanner.consume(',')
107
+ }
108
+
109
+ this.scanner.consume('=>')
110
+ const body = this.lambdaBody()
111
+ return { tag: 'lambda', start, formalArgs, body }
112
+ }
113
+
114
+ return this.ifExpression()
115
+ }
116
+
117
+ private lambdaBody() {
118
+ if (this.scanner.consumeIf('{')) {
119
+ const ret = this.expression()
120
+ this.scanner.consume('}')
121
+ return ret
122
+ }
123
+
124
+ return this.expression()
125
+ }
126
+
69
127
  ifExpression(): AstNode {
70
128
  if (!this.scanner.consumeIf('if')) {
71
129
  return this.unsink()
@@ -326,6 +384,13 @@ export class Parser {
326
384
 
327
385
  const parts: ArrayLiteralPart[] = []
328
386
  while (true) {
387
+ if (this.scanner.consumeIf(',')) {
388
+ const end = this.scanner.consumeIf(']')
389
+ if (end) {
390
+ return { tag: 'arrayLiteral', start, parts, end }
391
+ }
392
+ continue
393
+ }
329
394
  if (this.scanner.consumeIf('...')) {
330
395
  parts.push({ tag: 'spread', v: this.expression() })
331
396
  } else {
@@ -333,12 +398,16 @@ export class Parser {
333
398
  parts.push({ tag: 'element', v: exp })
334
399
  }
335
400
 
336
- const end = this.scanner.consumeIf(']')
401
+ let end = this.scanner.consumeIf(']')
337
402
  if (end) {
338
403
  return { tag: 'arrayLiteral', start, parts, end }
339
404
  }
340
405
 
341
406
  this.scanner.consume(',')
407
+ end = this.scanner.consumeIf(']')
408
+ if (end) {
409
+ return { tag: 'arrayLiteral', start, parts, end }
410
+ }
342
411
  }
343
412
  }
344
413
 
@@ -370,12 +439,16 @@ export class Parser {
370
439
  parts.push({ tag: 'hardName', k, v })
371
440
  }
372
441
 
373
- const end = this.scanner.consumeIf('}')
442
+ let end = this.scanner.consumeIf('}')
374
443
  if (end) {
375
444
  return { tag: 'objectLiteral', start, parts, end }
376
445
  }
377
446
 
378
447
  this.scanner.consume(',')
448
+ end = this.scanner.consumeIf('}')
449
+ if (end) {
450
+ return { tag: 'objectLiteral', start, parts, end }
451
+ }
379
452
  }
380
453
  }
381
454
 
@@ -389,7 +462,7 @@ export class Parser {
389
462
  }
390
463
 
391
464
  private maybeIdentifier(): Ident | undefined {
392
- const t = this.scanner.consumeIf(/[a-zA-Z][0-9A-Za-z_]*/)
465
+ const t = this.scanner.consumeIf(IDENT_PATTERN)
393
466
  if (t) {
394
467
  return { tag: 'ident', t }
395
468
  }
@@ -397,3 +470,5 @@ export class Parser {
397
470
  return undefined
398
471
  }
399
472
  }
473
+
474
+ const IDENT_PATTERN = /[a-zA-Z][0-9A-Za-z_]*/
package/src/runtime.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import { AstNode, show, span } from './ast-node'
2
2
  import { extractMessage } from './extract-message'
3
3
  import { failMe } from './fail-me'
4
- import { Parser } from './parser'
5
4
  import { shouldNeverHappen } from './should-never-happen'
6
5
  import * as Stack from './stack'
7
6
  import { switchOn } from './switch-on'
@@ -51,7 +50,7 @@ export class Runtime {
51
50
  constructor(
52
51
  private readonly root: AstNode,
53
52
  private readonly verbosity: Verbosity = 'quiet',
54
- private readonly parser: Parser,
53
+ private readonly preimports: Record<string, Value> = {},
55
54
  ) {}
56
55
 
57
56
  compute() {
@@ -60,7 +59,12 @@ export class Runtime {
60
59
  const keys = Value.foreign(o => o.keys())
61
60
  const entries = Value.foreign(o => o.entries())
62
61
  const fromEntries = Value.foreign(o => o.fromEntries())
63
- const lib = new SymbolFrame('Object', { destination: Value.obj({ keys, entries, fromEntries }) }, empty)
62
+ let lib = new SymbolFrame('Object', { destination: Value.obj({ keys, entries, fromEntries }) }, empty)
63
+
64
+ for (const [importName, importValue] of Object.entries(this.preimports)) {
65
+ lib = new SymbolFrame(importName, { destination: importValue }, lib)
66
+ }
67
+
64
68
  try {
65
69
  const value = this.evalNode(this.root, lib)
66
70
  return { value }
package/src/scanner.ts CHANGED
@@ -7,10 +7,10 @@ export interface Token {
7
7
  }
8
8
 
9
9
  export class Scanner {
10
- private offset = 0
11
-
12
- constructor(private readonly sourceCode: SourceCode) {
13
- this.eatWhitespace()
10
+ constructor(private readonly sourceCode: SourceCode, private offset = 0) {
11
+ if (this.offset === 0) {
12
+ this.eatWhitespace()
13
+ }
14
14
  }
15
15
 
16
16
  get sourceRef() {
@@ -52,6 +52,18 @@ export class Scanner {
52
52
  }
53
53
  }
54
54
 
55
+ headMatches(...patterns: (RegExp | string)[]): boolean {
56
+ const alt = new Scanner(this.sourceCode, this.offset)
57
+ for (const p of patterns) {
58
+ const t = alt.consumeIf(p, true)
59
+ if (t === undefined) {
60
+ return false
61
+ }
62
+ }
63
+
64
+ return true
65
+ }
66
+
55
67
  consume(r: RegExp | string, eatWhitespace = true): Token {
56
68
  const text = this.match(r)
57
69
  if (text === undefined) {
package/src/septima.ts ADDED
@@ -0,0 +1,92 @@
1
+ import { Parser } from './parser'
2
+ import { Result, ResultSink, ResultSinkImpl } from './result'
3
+ import { Runtime, Verbosity } from './runtime'
4
+ import { Scanner } from './scanner'
5
+ import { shouldNeverHappen } from './should-never-happen'
6
+ import { SourceCode } from './source-code'
7
+ import { Value } from './value'
8
+
9
+ interface Options {
10
+ /**
11
+ * A callback function to be invoked when the Septima program evaluated to `sink`. Allows the caller to determine
12
+ * which value will be returned in that case. For instance, passing `() => undefined` will translate a `sink` value
13
+ * to `undefined`. The default behavior is to throw an error.
14
+ */
15
+ onSink?: (res: ResultSink) => unknown
16
+ }
17
+
18
+ export class Septima {
19
+ /**
20
+ * Runs a Septima program and returns the value it evaluates to. If it evaluates to `sink`, returns the value computed
21
+ * by `options.onSink()` - if present, or throws an error - otherwise.
22
+ *
23
+ * This method is the simplest way to evaluate a Septima program, and it fits many common use cases. One can also use
24
+ * `.compute()` to get additional details about the execution.
25
+ *
26
+ * @param input the source code of the Septima program
27
+ * @param options
28
+ * @returns the value that `input` evaluates to
29
+ */
30
+ static run(input: string, options?: Options): unknown {
31
+ const onSink =
32
+ options?.onSink ??
33
+ ((r: ResultSink) => {
34
+ throw new Error(r.message)
35
+ })
36
+ const res = new Septima(input).compute()
37
+ if (res.tag === 'ok') {
38
+ return res.value
39
+ }
40
+
41
+ if (res.tag === 'sink') {
42
+ return onSink(res)
43
+ }
44
+
45
+ shouldNeverHappen(res)
46
+ }
47
+
48
+ constructor(readonly input: string, private readonly preimports: Record<string, string> = {}) {}
49
+
50
+ compute(verbosity: Verbosity = 'quiet'): Result {
51
+ const lib: Record<string, Value> = {}
52
+ for (const [importName, importCode] of Object.entries(this.preimports)) {
53
+ const sourceCode = new SourceCode(importCode)
54
+ const value = this.computeImpl(sourceCode, verbosity, {})
55
+ if (value.isSink()) {
56
+ // TODO(imaman): cover!
57
+ const r = new ResultSinkImpl(value, sourceCode)
58
+ throw new Error(`preimport (${importName}) evaluated to sink: ${r.message}`)
59
+ }
60
+ lib[importName] = value
61
+ }
62
+
63
+ const sourceCode = new SourceCode(this.input)
64
+ const value = this.computeImpl(sourceCode, verbosity, lib)
65
+ if (!value.isSink()) {
66
+ return { value: value.export(), tag: 'ok' }
67
+ }
68
+ return new ResultSinkImpl(value, sourceCode)
69
+ }
70
+
71
+ private computeImpl(sourceCode: SourceCode, verbosity: Verbosity, lib: Record<string, Value>) {
72
+ const scanner = new Scanner(sourceCode)
73
+ const parser = new Parser(scanner)
74
+
75
+ const ast = parse(parser)
76
+ const runtime = new Runtime(ast, verbosity, lib)
77
+ const c = runtime.compute()
78
+
79
+ if (c.value) {
80
+ return c.value
81
+ }
82
+
83
+ const runtimeErrorMessage = `${c.errorMessage} when evaluating:\n${sourceCode.formatTrace(c.expressionTrace)}`
84
+ throw new Error(runtimeErrorMessage)
85
+ }
86
+ }
87
+
88
+ export function parse(arg: string | Parser) {
89
+ const parser = typeof arg === 'string' ? new Parser(new Scanner(new SourceCode(arg))) : arg
90
+ const ast = parser.parse()
91
+ return ast
92
+ }
@@ -1,14 +1,14 @@
1
1
  import { show } from '../src/ast-node'
2
- import * as cdl from '../src/cdl'
2
+ import { parse } from '../src/septima'
3
3
 
4
4
  describe('parser', () => {
5
5
  test('show()', () => {
6
- expect(show(cdl.parse(`5`))).toEqual('5')
7
- expect(show(cdl.parse(`fun (x) x*9`))).toEqual('fun (x) (x * 9)')
6
+ expect(show(parse(`5`))).toEqual('5')
7
+ expect(show(parse(`fun (x) x*9`))).toEqual('fun (x) (x * 9)')
8
8
  })
9
9
  test('syntax errors', () => {
10
- expect(() => cdl.parse(`a + #$%x`)).toThrowError('Unparsable input at (1:5..8) #$%x')
11
- expect(() => cdl.parse(`{#$%x: 8}`)).toThrowError('Expected an identifier at (1:2..9) #$%x: 8}')
12
- expect(() => cdl.parse(`"foo" "goo"`)).toThrowError('Loitering input at (1:7..11) "goo"')
10
+ expect(() => parse(`a + #$%x`)).toThrowError('Unparsable input at (1:5..8) #$%x')
11
+ expect(() => parse(`{#$%x: 8}`)).toThrowError('Expected an identifier at (1:2..9) #$%x: 8}')
12
+ expect(() => parse(`"foo" "goo"`)).toThrowError('Loitering input at (1:7..11) "goo"')
13
13
  })
14
14
  })
@@ -1,22 +1,22 @@
1
- import { Cdl } from '../src/cdl'
1
+ import { Septima } from '../src/septima'
2
2
 
3
3
  /**
4
- * Runs a CDL program for testing purposes. If the CDL program evaluated to `sink` an `undefined` is
4
+ * Runs a Septima program for testing purposes. If the program evaluates to `sink` an `undefined` is
5
5
  * returned.
6
- * @param input the CDL program to run
6
+ * @param input the Septima program to run
7
7
  */
8
8
  function run(input: string) {
9
- return Cdl.run(input, { onSink: () => undefined })
9
+ return Septima.run(input, { onSink: () => undefined })
10
10
  }
11
11
 
12
12
  /**
13
- * Runs a CDL program for testing purposes. The program is expected to evaluate to `sink`. Throws an exception if that
14
- * is not the case.
15
- * @param input the CDL program to run
13
+ * Runs a Septima program for testing purposes. The program is expected to evaluate to `sink`. Throws an exception if
14
+ * this expectation is not met.
15
+ * @param input the Septima program to run
16
16
  */
17
17
  function runSink(input: string) {
18
- const cdl = new Cdl(input)
19
- const res = cdl.compute()
18
+ const septima = new Septima(input)
19
+ const res = septima.compute()
20
20
 
21
21
  if (res.tag !== 'sink') {
22
22
  throw new Error(`Not a sink: ${res.value}`)
@@ -24,12 +24,16 @@ function runSink(input: string) {
24
24
  return res
25
25
  }
26
26
 
27
- describe('cdl', () => {
27
+ describe('septima', () => {
28
28
  test('basics', () => {
29
29
  expect(run(`5`)).toEqual(5)
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)
@@ -200,6 +204,10 @@ describe('cdl', () => {
200
204
  expect(run(`let x = 5; x+3`)).toEqual(8)
201
205
  expect(run(`let x = 5; let y = 20; x*y+4`)).toEqual(104)
202
206
  })
207
+ test('do not need the trailing semicolon', () => {
208
+ expect(run(`let x = 5 x+3`)).toEqual(8)
209
+ expect(run(`let x = 5 let y = 20 x*y+4`)).toEqual(104)
210
+ })
203
211
  test('fails if the variable was not defined', () => {
204
212
  expect(() => run(`let x = 5; x+y`)).toThrowError('Symbol y was not found')
205
213
  })
@@ -262,6 +270,12 @@ describe('cdl', () => {
262
270
  expect(run(`["ab", 5]`)).toEqual(['ab', 5])
263
271
  expect(run(`[]`)).toEqual([])
264
272
  })
273
+ test('allow a dangling comma', () => {
274
+ expect(run(`[,]`)).toEqual([])
275
+ expect(run(`[,,]`)).toEqual([])
276
+ expect(run(`[246,]`)).toEqual([246])
277
+ expect(run(`[246,531,]`)).toEqual([246, 531])
278
+ })
265
279
  test('individual elements of an array can be accessed via the [<index>] notation', () => {
266
280
  expect(run(`let a = ['sun', 'mon', 'tue', 'wed']; a[1]`)).toEqual('mon')
267
281
  })
@@ -282,6 +296,14 @@ describe('cdl', () => {
282
296
  expect(run(`{a: 1, b: 2}`)).toEqual({ a: 1, b: 2 })
283
297
  expect(run(`{a: "A", b: "B", c: "CCC"}`)).toEqual({ a: 'A', b: 'B', c: 'CCC' })
284
298
  })
299
+ test('allow a dangling comma', () => {
300
+ expect(run(`{a: 1,}`)).toEqual({ a: 1 })
301
+ expect(run(`{a: 1, b: 2,}`)).toEqual({ a: 1, b: 2 })
302
+ expect(run(`{a: "A", b: "B", c: "CCC",}`)).toEqual({ a: 'A', b: 'B', c: 'CCC' })
303
+ })
304
+ test('a dangling comma in an empty object is not allowed', () => {
305
+ expect(() => run(`{,}`)).toThrowError('Expected an identifier at (1:2..3) ,}')
306
+ })
285
307
  test('supports computed attributes names via the [<expression>]: <value> notation', () => {
286
308
  expect(run(`{["a" + 'b']: 'a-and-b'}`)).toEqual({ ab: 'a-and-b' })
287
309
  })
@@ -408,6 +430,33 @@ describe('cdl', () => {
408
430
  expect(run(`let triple = (fun(a) 3*a); triple(100) - triple(90)`)).toEqual(30)
409
431
  expect(run(`let triple = fun(a) 3*a; triple(100) - triple(90)`)).toEqual(30)
410
432
  })
433
+ describe('arrow function notation', () => {
434
+ test('a single formal argument does not need to be surrounded with parenthesis', () => {
435
+ expect(run(`let triple = a => 3*a; triple(100)`)).toEqual(300)
436
+ })
437
+ test('(a) => <expression>', () => {
438
+ expect(run(`let triple = (a) => 3*a; triple(100)`)).toEqual(300)
439
+ })
440
+ test('() => <expression>', () => {
441
+ expect(run(`let five = () => 5; five()`)).toEqual(5)
442
+ })
443
+ test('(a,b) => <expression>', () => {
444
+ expect(run(`let conc = (a,b) => a+b; conc('al', 'pha')`)).toEqual('alpha')
445
+ 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')
446
+ })
447
+ test('body of an arrow function can be { return <expression>}', () => {
448
+ expect(run(`let triple = a => { return 3*a }; triple(100)`)).toEqual(300)
449
+ expect(run(`let triple = (a) => { return 3*a }; triple(100)`)).toEqual(300)
450
+ expect(run(`let five = () => { return 5 }; five()`)).toEqual(5)
451
+ expect(run(`let concat = (a,b) => { return a+b }; concat('al', 'pha')`)).toEqual('alpha')
452
+ })
453
+ test('body of an arrow function can include let definitions', () => {
454
+ expect(run(`let triple = a => { let factor = 3; return factor*a }; triple(100)`)).toEqual(300)
455
+ expect(run(`let triple = (a) => { let factor = 3; return 3*a }; triple(100)`)).toEqual(300)
456
+ expect(run(`let five = () => { let two = 2; let three = 3; return three+two }; five()`)).toEqual(5)
457
+ expect(run(`let concat = (a,b) => { let u = '_'; return u+a+b+u }; concat('a', 'b')`)).toEqual('_ab_')
458
+ })
459
+ })
411
460
  test('can have no args', () => {
412
461
  expect(run(`let pi = fun() 3.14; 2*pi()`)).toEqual(6.28)
413
462
  expect(run(`(fun() 3.14)()*2`)).toEqual(6.28)
@@ -743,6 +792,28 @@ describe('cdl', () => {
743
792
  expect(run(`let count = fun (n) if (n <= 0) 0 else 1 + count(n-1); count(330)`)).toEqual(330)
744
793
  })
745
794
  })
795
+ describe('preimport', () => {
796
+ test('definitions from a preimported file can be used', () => {
797
+ const septima = new Septima(`libA.plus10(4) + libA.plus20(2)`, {
798
+ libA: `{ plus10: fun (n) n+10, plus20: fun (n) n+20}`,
799
+ })
800
+ expect(septima.compute()).toMatchObject({ value: 36 })
801
+ })
802
+ test('supports multiple preimports', () => {
803
+ const septima = new Septima(`a.calc(4) + b.calc(1)`, {
804
+ a: `{ calc: fun (n) n+10 }`,
805
+ b: `{ calc: fun (n) n+20 }`,
806
+ })
807
+ expect(septima.compute()).toMatchObject({ value: 35 })
808
+ })
809
+ })
810
+ test.todo('support file names in locations')
811
+ test.todo('string interpolation via `foo` strings')
812
+ test.todo('imports')
813
+ test.todo('arrow functions')
814
+ test.todo('optional parameters')
815
+ test.todo('optional type annotations?')
816
+ test.todo('allow redundant commas')
746
817
  test.todo('left associativity of +/-')
747
818
  test.todo('comparison of arrays')
748
819
  test.todo('comparison of lambdas?')
@@ -752,4 +823,6 @@ describe('cdl', () => {
752
823
  test.todo('quoting of a ticks inside a string')
753
824
  test.todo('number in scientific notation')
754
825
  test.todo('number methods')
826
+ test.todo('drop the fun () notation and use just arrow functions')
827
+ test.todo('proper internal representation of arrow function, in particular: show(), span()')
755
828
  })
package/dist/src/cdl.d.ts DELETED
@@ -1,33 +0,0 @@
1
- import { Parser } from './parser';
2
- import { Result, ResultSink } from './result';
3
- import { Verbosity } from './runtime';
4
- interface Options {
5
- /**
6
- * A callback function to be invoked when the CDL program evaluated to `sink`. Allows the caller to determine which
7
- * value will be returned in that case. For instance, passing `() => undefined` will translate a `sink` value to
8
- * `undefined`. The default behavior is to throw an error.
9
- */
10
- onSink?: (res: ResultSink) => unknown;
11
- }
12
- export declare class Cdl {
13
- readonly input: string;
14
- private readonly scanner;
15
- private readonly sourceCode;
16
- private readonly parser;
17
- /**
18
- * Runs a CDL program and returns the value it evaluates to. If it evaluates to `sink`, returns the value computed
19
- * by `options.onSink()` - if present, or throws an error - otherwise.
20
- *
21
- * This method is the simplest way to evaluate a CDL program. One can also use `.compute()` to get a higher degree
22
- * of details about the result.
23
- *
24
- * @param input the source code of the CDL program
25
- * @param options
26
- * @returns the value that `input` evaluates to
27
- */
28
- static run(input: string, options?: Options): unknown;
29
- constructor(input: string);
30
- compute(verbosity?: Verbosity): Result;
31
- }
32
- export declare function parse(arg: string | Parser): import("./ast-node").AstNode;
33
- export {};
package/dist/src/cdl.js DELETED
@@ -1,63 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.parse = exports.Cdl = void 0;
4
- const parser_1 = require("./parser");
5
- const result_1 = require("./result");
6
- const runtime_1 = require("./runtime");
7
- const scanner_1 = require("./scanner");
8
- const should_never_happen_1 = require("./should-never-happen");
9
- const source_code_1 = require("./source-code");
10
- class Cdl {
11
- constructor(input) {
12
- this.input = input;
13
- this.sourceCode = new source_code_1.SourceCode(this.input);
14
- this.scanner = new scanner_1.Scanner(this.sourceCode);
15
- this.parser = new parser_1.Parser(this.scanner);
16
- }
17
- /**
18
- * Runs a CDL program and returns the value it evaluates to. If it evaluates to `sink`, returns the value computed
19
- * by `options.onSink()` - if present, or throws an error - otherwise.
20
- *
21
- * This method is the simplest way to evaluate a CDL program. One can also use `.compute()` to get a higher degree
22
- * of details about the result.
23
- *
24
- * @param input the source code of the CDL program
25
- * @param options
26
- * @returns the value that `input` evaluates to
27
- */
28
- static run(input, options) {
29
- const onSink = options?.onSink ??
30
- ((r) => {
31
- throw new Error(r.message);
32
- });
33
- const res = new Cdl(input).compute();
34
- if (res.tag === 'ok') {
35
- return res.value;
36
- }
37
- if (res.tag === 'sink') {
38
- return onSink(res);
39
- }
40
- (0, should_never_happen_1.shouldNeverHappen)(res);
41
- }
42
- compute(verbosity = 'quiet') {
43
- const ast = parse(this.parser);
44
- const runtime = new runtime_1.Runtime(ast, verbosity, this.parser);
45
- const c = runtime.compute();
46
- if (c.value) {
47
- if (!c.value.isSink()) {
48
- return { value: c.value.export(), tag: 'ok' };
49
- }
50
- return new result_1.ResultSinkImpl(c.value, this.sourceCode);
51
- }
52
- const runtimeErrorMessage = `${c.errorMessage} when evaluating:\n${this.sourceCode.formatTrace(c.expressionTrace)}`;
53
- throw new Error(runtimeErrorMessage);
54
- }
55
- }
56
- exports.Cdl = Cdl;
57
- function parse(arg) {
58
- const parser = typeof arg === 'string' ? new parser_1.Parser(new scanner_1.Scanner(new source_code_1.SourceCode(arg))) : arg;
59
- const ast = parser.parse();
60
- return ast;
61
- }
62
- exports.parse = parse;
63
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2RsLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2NkbC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSxxQ0FBaUM7QUFDakMscUNBQTZEO0FBQzdELHVDQUE4QztBQUM5Qyx1Q0FBbUM7QUFDbkMsK0RBQXlEO0FBQ3pELCtDQUEwQztBQVcxQyxNQUFhLEdBQUc7SUFrQ2QsWUFBcUIsS0FBYTtRQUFiLFVBQUssR0FBTCxLQUFLLENBQVE7UUFDaEMsSUFBSSxDQUFDLFVBQVUsR0FBRyxJQUFJLHdCQUFVLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFBO1FBQzVDLElBQUksQ0FBQyxPQUFPLEdBQUcsSUFBSSxpQkFBTyxDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsQ0FBQTtRQUMzQyxJQUFJLENBQUMsTUFBTSxHQUFHLElBQUksZUFBTSxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsQ0FBQTtJQUN4QyxDQUFDO0lBakNEOzs7Ozs7Ozs7O09BVUc7SUFDSCxNQUFNLENBQUMsR0FBRyxDQUFDLEtBQWEsRUFBRSxPQUFpQjtRQUN6QyxNQUFNLE1BQU0sR0FDVixPQUFPLEVBQUUsTUFBTTtZQUNmLENBQUMsQ0FBQyxDQUFhLEVBQUUsRUFBRTtnQkFDakIsTUFBTSxJQUFJLEtBQUssQ0FBQyxDQUFDLENBQUMsT0FBTyxDQUFDLENBQUE7WUFDNUIsQ0FBQyxDQUFDLENBQUE7UUFDSixNQUFNLEdBQUcsR0FBRyxJQUFJLEdBQUcsQ0FBQyxLQUFLLENBQUMsQ0FBQyxPQUFPLEVBQUUsQ0FBQTtRQUNwQyxJQUFJLEdBQUcsQ0FBQyxHQUFHLEtBQUssSUFBSSxFQUFFO1lBQ3BCLE9BQU8sR0FBRyxDQUFDLEtBQUssQ0FBQTtTQUNqQjtRQUVELElBQUksR0FBRyxDQUFDLEdBQUcsS0FBSyxNQUFNLEVBQUU7WUFDdEIsT0FBTyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUE7U0FDbkI7UUFFRCxJQUFBLHVDQUFpQixFQUFDLEdBQUcsQ0FBQyxDQUFBO0lBQ3hCLENBQUM7SUFRRCxPQUFPLENBQUMsWUFBdUIsT0FBTztRQUNwQyxNQUFNLEdBQUcsR0FBRyxLQUFLLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFBO1FBQzlCLE1BQU0sT0FBTyxHQUFHLElBQUksaUJBQU8sQ0FBQyxHQUFHLEVBQUUsU0FBUyxFQUFFLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQTtRQUN4RCxNQUFNLENBQUMsR0FBRyxPQUFPLENBQUMsT0FBTyxFQUFFLENBQUE7UUFFM0IsSUFBSSxDQUFDLENBQUMsS0FBSyxFQUFFO1lBQ1gsSUFBSSxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsTUFBTSxFQUFFLEVBQUU7Z0JBQ3JCLE9BQU8sRUFBRSxLQUFLLEVBQUUsQ0FBQyxDQUFDLEtBQUssQ0FBQyxNQUFNLEVBQUUsRUFBRSxHQUFHLEVBQUUsSUFBSSxFQUFFLENBQUE7YUFDOUM7WUFDRCxPQUFPLElBQUksdUJBQWMsQ0FBQyxDQUFDLENBQUMsS0FBSyxFQUFFLElBQUksQ0FBQyxVQUFVLENBQUMsQ0FBQTtTQUNwRDtRQUVELE1BQU0sbUJBQW1CLEdBQUcsR0FBRyxDQUFDLENBQUMsWUFBWSxzQkFBc0IsSUFBSSxDQUFDLFVBQVUsQ0FBQyxXQUFXLENBQUMsQ0FBQyxDQUFDLGVBQWUsQ0FBQyxFQUFFLENBQUE7UUFDbkgsTUFBTSxJQUFJLEtBQUssQ0FBQyxtQkFBbUIsQ0FBQyxDQUFBO0lBQ3RDLENBQUM7Q0FDRjtBQXZERCxrQkF1REM7QUFFRCxTQUFnQixLQUFLLENBQUMsR0FBb0I7SUFDeEMsTUFBTSxNQUFNLEdBQUcsT0FBTyxHQUFHLEtBQUssUUFBUSxDQUFDLENBQUMsQ0FBQyxJQUFJLGVBQU0sQ0FBQyxJQUFJLGlCQUFPLENBQUMsSUFBSSx3QkFBVSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFBO0lBQzNGLE1BQU0sR0FBRyxHQUFHLE1BQU0sQ0FBQyxLQUFLLEVBQUUsQ0FBQTtJQUMxQixPQUFPLEdBQUcsQ0FBQTtBQUNaLENBQUM7QUFKRCxzQkFJQyJ9