spex-parser 0.6.2 → 0.7.0
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/README.md +428 -226
- package/dist/ast.d.ts +22 -36
- package/dist/ast.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/lexer.d.ts +3 -8
- package/dist/lexer.d.ts.map +1 -1
- package/dist/lexer.js +21 -46
- package/dist/lexer.js.map +1 -1
- package/dist/parser.d.ts +0 -4
- package/dist/parser.d.ts.map +1 -1
- package/dist/parser.js +7 -49
- package/dist/parser.js.map +1 -1
- package/dist/visitor.d.ts +1 -5
- package/dist/visitor.d.ts.map +1 -1
- package/dist/visitor.js +48 -83
- package/dist/visitor.js.map +1 -1
- package/package.json +1 -1
- package/src/ast.ts +38 -44
- package/src/index.ts +3 -7
- package/src/lexer.ts +21 -44
- package/src/parser.ts +7 -58
- package/src/visitor.ts +50 -89
- package/tests/constants.test.ts +0 -4
- package/tests/e2e.test.ts +9 -9
- package/tests/lexer.test.ts +10 -31
- package/tests/parser.test.ts +31 -181
- package/tests/props/express_todo_web.spex +1 -2
- package/tests/props/express_web_env.spex +0 -2
- package/tests/props/flask_todo_web.spex +1 -2
- package/tests/props/flask_web_env.spex +0 -2
- package/tests/props/python_cli_env.spex +0 -2
- package/tests/props/python_todo_cli.spex +1 -2
- package/tests/props/todo.spex +0 -17
- package/tests/props/typescript_cli_env.spex +0 -2
- package/tests/props/typescript_todo_cli.spex +1 -2
- package/tests/visitor.test.ts +99 -265
package/tests/parser.test.ts
CHANGED
|
@@ -96,6 +96,19 @@ describe('SpexParser', () => {
|
|
|
96
96
|
expect(parser.errors).toHaveLength(0)
|
|
97
97
|
})
|
|
98
98
|
|
|
99
|
+
it('should parse subobject declaration with a structured constraint', () => {
|
|
100
|
+
const testCase = 'create double as from number -> number select ```\nreturn n * 2\n```;'
|
|
101
|
+
const { parser } = parseInput(testCase)
|
|
102
|
+
expect(parser.errors).toHaveLength(0)
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
it('should parse subobject declaration with a code constraint', () => {
|
|
106
|
+
const testCase =
|
|
107
|
+
'create double as from number -> number select ```python\nreturn @n * 2\n```;'
|
|
108
|
+
const { parser } = parseInput(testCase)
|
|
109
|
+
expect(parser.errors).toHaveLength(0)
|
|
110
|
+
})
|
|
111
|
+
|
|
99
112
|
it('should parse subobject declaration with a set operation base', () => {
|
|
100
113
|
const testCase =
|
|
101
114
|
'create MySubobject as from Web intersect TypeScript select { is an express app };'
|
|
@@ -164,6 +177,12 @@ describe('SpexParser', () => {
|
|
|
164
177
|
expect(parser.errors).toHaveLength(0)
|
|
165
178
|
})
|
|
166
179
|
|
|
180
|
+
it('should parse basic object artifact', () => {
|
|
181
|
+
const testCase = 'create MyObject as artifact;'
|
|
182
|
+
const { parser } = parseInput(testCase)
|
|
183
|
+
expect(parser.errors).toHaveLength(0)
|
|
184
|
+
})
|
|
185
|
+
|
|
167
186
|
it('should parse basic objects in product fields', () => {
|
|
168
187
|
const testCase = 'create Config as (name: string, count: number, active: bool);'
|
|
169
188
|
const { parser } = parseInput(testCase)
|
|
@@ -205,6 +224,12 @@ describe('SpexParser', () => {
|
|
|
205
224
|
const { parser } = parseInput(testCase)
|
|
206
225
|
expect(parser.errors).not.toHaveLength(0)
|
|
207
226
|
})
|
|
227
|
+
|
|
228
|
+
it('should not allow overriding basic object artifact', () => {
|
|
229
|
+
const testCase = 'create artifact as Number;'
|
|
230
|
+
const { parser } = parseInput(testCase)
|
|
231
|
+
expect(parser.errors).not.toHaveLength(0)
|
|
232
|
+
})
|
|
208
233
|
})
|
|
209
234
|
|
|
210
235
|
describe('import declaration', () => {
|
|
@@ -227,88 +252,30 @@ describe('SpexParser', () => {
|
|
|
227
252
|
})
|
|
228
253
|
})
|
|
229
254
|
|
|
230
|
-
describe('export declaration', () => {
|
|
231
|
-
it('should parse export declaration', () => {
|
|
232
|
-
const testCase = 'export EmailAddress;'
|
|
233
|
-
const { parser } = parseInput(testCase)
|
|
234
|
-
expect(parser.errors).toHaveLength(0)
|
|
235
|
-
})
|
|
236
|
-
})
|
|
237
|
-
|
|
238
255
|
describe('generate declaration', () => {
|
|
239
256
|
it('should parse generate declaration', () => {
|
|
240
257
|
const testCase = 'generate Main;'
|
|
241
258
|
const { parser } = parseInput(testCase)
|
|
242
259
|
expect(parser.errors).toHaveLength(0)
|
|
243
260
|
})
|
|
244
|
-
})
|
|
245
261
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
const testCase = "create myEnum as enum ('v1', 'v2');"
|
|
262
|
+
it('should parse generate declaration with an environment', () => {
|
|
263
|
+
const testCase = 'generate Main in Python;'
|
|
249
264
|
const { parser } = parseInput(testCase)
|
|
250
265
|
expect(parser.errors).toHaveLength(0)
|
|
251
266
|
})
|
|
252
267
|
|
|
253
|
-
it('should parse
|
|
254
|
-
const testCase =
|
|
268
|
+
it('should parse generate declaration with a dotted environment', () => {
|
|
269
|
+
const testCase = 'generate Main in dev.Linux;'
|
|
255
270
|
const { parser } = parseInput(testCase)
|
|
256
271
|
expect(parser.errors).toHaveLength(0)
|
|
257
272
|
})
|
|
258
273
|
|
|
259
|
-
it('should parse
|
|
260
|
-
const testCase =
|
|
274
|
+
it('should parse generate declaration with an environment subobject', () => {
|
|
275
|
+
const testCase = 'generate Main in from environment select { python };'
|
|
261
276
|
const { parser } = parseInput(testCase)
|
|
262
277
|
expect(parser.errors).toHaveLength(0)
|
|
263
278
|
})
|
|
264
|
-
|
|
265
|
-
it('should parse enum object with double quoted values', () => {
|
|
266
|
-
const testCase = 'create myEnum as enum ("v1", "v2");'
|
|
267
|
-
const { parser } = parseInput(testCase)
|
|
268
|
-
expect(parser.errors).toHaveLength(0)
|
|
269
|
-
})
|
|
270
|
-
|
|
271
|
-
it('should parse enum object with mixed quote values', () => {
|
|
272
|
-
const testCase = "create myEnum as enum (\"v1\", 'v2');"
|
|
273
|
-
const { parser } = parseInput(testCase)
|
|
274
|
-
expect(parser.errors).toHaveLength(0)
|
|
275
|
-
})
|
|
276
|
-
|
|
277
|
-
it('should parse enum object with escaped values', () => {
|
|
278
|
-
const testCase = "create myEnum as enum ('it\\'s');"
|
|
279
|
-
const { parser } = parseInput(testCase)
|
|
280
|
-
expect(parser.errors).toHaveLength(0)
|
|
281
|
-
})
|
|
282
|
-
|
|
283
|
-
it('should parse enum object inside a product object', () => {
|
|
284
|
-
const testCase = "create Config as (kind: enum ('a', 'b'));"
|
|
285
|
-
const { parser } = parseInput(testCase)
|
|
286
|
-
expect(parser.errors).toHaveLength(0)
|
|
287
|
-
})
|
|
288
|
-
|
|
289
|
-
it('should parse array of enum object', () => {
|
|
290
|
-
const testCase = "create myEnum as enum ('a', 'b')[];"
|
|
291
|
-
const { parser } = parseInput(testCase)
|
|
292
|
-
expect(parser.errors).toHaveLength(0)
|
|
293
|
-
})
|
|
294
|
-
|
|
295
|
-
it('should not parse enum object without values', () => {
|
|
296
|
-
const testCase = 'create myEnum as enum ();'
|
|
297
|
-
const { parser } = parseInput(testCase)
|
|
298
|
-
expect(parser.errors).not.toHaveLength(0)
|
|
299
|
-
})
|
|
300
|
-
|
|
301
|
-
it('should not parse enum object with a trailing comma', () => {
|
|
302
|
-
const testCase = "create myEnum as enum ('v1',);"
|
|
303
|
-
const { parser } = parseInput(testCase)
|
|
304
|
-
expect(parser.errors).not.toHaveLength(0)
|
|
305
|
-
})
|
|
306
|
-
|
|
307
|
-
it('should not parse enum object without parentheses', () => {
|
|
308
|
-
const testCase = 'create myEnum as enum;'
|
|
309
|
-
const { parser } = parseInput(testCase)
|
|
310
|
-
expect(parser.errors).not.toHaveLength(0)
|
|
311
|
-
})
|
|
312
279
|
})
|
|
313
280
|
|
|
314
281
|
describe('literal object', () => {
|
|
@@ -515,76 +482,6 @@ describe('SpexParser', () => {
|
|
|
515
482
|
})
|
|
516
483
|
})
|
|
517
484
|
|
|
518
|
-
describe('package declaration', () => {
|
|
519
|
-
it('should parse package executable declaration', () => {
|
|
520
|
-
const testCase = 'package executable myapp as Main in Python;'
|
|
521
|
-
const { parser } = parseInput(testCase)
|
|
522
|
-
expect(parser.errors).toHaveLength(0)
|
|
523
|
-
})
|
|
524
|
-
|
|
525
|
-
it('should parse package module declaration', () => {
|
|
526
|
-
const testCase = 'package module mylib as utils in Node;'
|
|
527
|
-
const { parser } = parseInput(testCase)
|
|
528
|
-
expect(parser.errors).toHaveLength(0)
|
|
529
|
-
})
|
|
530
|
-
|
|
531
|
-
it('should parse package executable with exponential object', () => {
|
|
532
|
-
const testCase = 'package executable cli as (path: string) -> unit in Python;'
|
|
533
|
-
const { parser } = parseInput(testCase)
|
|
534
|
-
expect(parser.errors).toHaveLength(0)
|
|
535
|
-
})
|
|
536
|
-
|
|
537
|
-
it('should parse package executable with dotted name', () => {
|
|
538
|
-
const testCase = 'package executable myapp as app.Main in Python;'
|
|
539
|
-
const { parser } = parseInput(testCase)
|
|
540
|
-
expect(parser.errors).toHaveLength(0)
|
|
541
|
-
})
|
|
542
|
-
|
|
543
|
-
it('should parse package module with array type', () => {
|
|
544
|
-
const testCase = 'package module mylib as string[] in Python;'
|
|
545
|
-
const { parser } = parseInput(testCase)
|
|
546
|
-
expect(parser.errors).toHaveLength(0)
|
|
547
|
-
})
|
|
548
|
-
|
|
549
|
-
it('should parse package executable with product type', () => {
|
|
550
|
-
const testCase = 'package module mylib as (name: string, count: number) in Python;'
|
|
551
|
-
const { parser } = parseInput(testCase)
|
|
552
|
-
expect(parser.errors).toHaveLength(0)
|
|
553
|
-
})
|
|
554
|
-
|
|
555
|
-
it('should parse package executable with subobject', () => {
|
|
556
|
-
const testCase =
|
|
557
|
-
'package executable myapp as from string select { is a valid command } in Python;'
|
|
558
|
-
const { parser } = parseInput(testCase)
|
|
559
|
-
expect(parser.errors).toHaveLength(0)
|
|
560
|
-
})
|
|
561
|
-
|
|
562
|
-
it('should parse package declaration case-insensitively', () => {
|
|
563
|
-
const testCase = 'PACKAGE EXECUTABLE myapp AS Main IN Python;'
|
|
564
|
-
const { parser } = parseInput(testCase)
|
|
565
|
-
expect(parser.errors).toHaveLength(0)
|
|
566
|
-
})
|
|
567
|
-
|
|
568
|
-
it('should parse mixed package and generate declarations', () => {
|
|
569
|
-
const testCase = 'package executable myapp as Main in Python;\ngenerate Main;'
|
|
570
|
-
const { parser } = parseInput(testCase)
|
|
571
|
-
expect(parser.errors).toHaveLength(0)
|
|
572
|
-
})
|
|
573
|
-
|
|
574
|
-
it('should parse package declaration with complex nested type', () => {
|
|
575
|
-
const testCase =
|
|
576
|
-
'package module mylib as from (x: number, y: number) -> number select { computes distance } in Node;'
|
|
577
|
-
const { parser } = parseInput(testCase)
|
|
578
|
-
expect(parser.errors).toHaveLength(0)
|
|
579
|
-
})
|
|
580
|
-
|
|
581
|
-
it('should parse package with complex environment object', () => {
|
|
582
|
-
const testCase = 'package executable myapp as Main in from environment select { python };'
|
|
583
|
-
const { parser } = parseInput(testCase)
|
|
584
|
-
expect(parser.errors).toHaveLength(0)
|
|
585
|
-
})
|
|
586
|
-
})
|
|
587
|
-
|
|
588
485
|
describe('comments', () => {
|
|
589
486
|
it('should parse declarations separated by single-line comments', () => {
|
|
590
487
|
const testCase = `
|
|
@@ -641,7 +538,6 @@ describe('SpexParser', () => {
|
|
|
641
538
|
const testCase = `
|
|
642
539
|
create Todo as (id: string, title: string, completed: bool);
|
|
643
540
|
create EmailAddress as from string select { are email addresses };
|
|
644
|
-
export EmailAddress;
|
|
645
541
|
generate Main;
|
|
646
542
|
`
|
|
647
543
|
const { parser } = parseInput(testCase)
|
|
@@ -737,50 +633,4 @@ describe('SpexParser', () => {
|
|
|
737
633
|
expect(parser.errors).not.toHaveLength(0)
|
|
738
634
|
})
|
|
739
635
|
})
|
|
740
|
-
|
|
741
|
-
describe('lambda object', () => {
|
|
742
|
-
it('should parse lambda with product base', () => {
|
|
743
|
-
const testCase = 'create sum as lambda (a: number, b: number) -> number ```python\nreturn a + b\n```;'
|
|
744
|
-
const { parser } = parseInput(testCase)
|
|
745
|
-
expect(parser.errors).toHaveLength(0)
|
|
746
|
-
})
|
|
747
|
-
|
|
748
|
-
it('should parse lambda with named base', () => {
|
|
749
|
-
const testCase = 'create double as lambda number -> number ```python\nreturn @n * 2\n```;'
|
|
750
|
-
const { parser } = parseInput(testCase)
|
|
751
|
-
expect(parser.errors).toHaveLength(0)
|
|
752
|
-
})
|
|
753
|
-
|
|
754
|
-
it('should parse lambda case-insensitively', () => {
|
|
755
|
-
const testCase = 'create sum as LAMBDA (a: number) -> number ```python\nreturn @a\n```;'
|
|
756
|
-
const { parser } = parseInput(testCase)
|
|
757
|
-
expect(parser.errors).toHaveLength(0)
|
|
758
|
-
})
|
|
759
|
-
|
|
760
|
-
it('should parse lambda with pattern blocks', () => {
|
|
761
|
-
const testCase =
|
|
762
|
-
'create transform as lambda (x: number) -> number ```python\nif @x > 0:\n @{return sin(@x)}\nelse:\n @{return cos(@x)}\n```;'
|
|
763
|
-
const { parser } = parseInput(testCase)
|
|
764
|
-
expect(parser.errors).toHaveLength(0)
|
|
765
|
-
})
|
|
766
|
-
|
|
767
|
-
it('should parse lambda in product field', () => {
|
|
768
|
-
const testCase =
|
|
769
|
-
'create Config as (handler: lambda (x: string) -> string ```typescript\nreturn x.toUpperCase();\n```, port: number);'
|
|
770
|
-
const { parser } = parseInput(testCase)
|
|
771
|
-
expect(parser.errors).toHaveLength(0)
|
|
772
|
-
})
|
|
773
|
-
|
|
774
|
-
it('should not parse lambda without language', () => {
|
|
775
|
-
const testCase = 'create sum as lambda (a: number) -> number ```\nreturn @a\n```;'
|
|
776
|
-
const { parser } = parseInput(testCase)
|
|
777
|
-
expect(parser.errors).not.toHaveLength(0)
|
|
778
|
-
})
|
|
779
|
-
|
|
780
|
-
it('should not parse lambda without code block', () => {
|
|
781
|
-
const testCase = 'create sum as lambda (a: number) -> number;'
|
|
782
|
-
const { parser } = parseInput(testCase)
|
|
783
|
-
expect(parser.errors).not.toHaveLength(0)
|
|
784
|
-
})
|
|
785
|
-
})
|
|
786
636
|
})
|
package/tests/props/todo.spex
CHANGED
|
@@ -106,20 +106,3 @@ select {
|
|
|
106
106
|
language: TypeScript,
|
|
107
107
|
runtime: Node.js 20
|
|
108
108
|
};
|
|
109
|
-
|
|
110
|
-
/* Exports for the concrete realizations. */
|
|
111
|
-
export TodoId;
|
|
112
|
-
export TodoTitle;
|
|
113
|
-
export Todo;
|
|
114
|
-
export HttpRequest;
|
|
115
|
-
export HttpResponse;
|
|
116
|
-
export AddTodo;
|
|
117
|
-
export ListTodos;
|
|
118
|
-
export CompleteTodo;
|
|
119
|
-
export TodoApp;
|
|
120
|
-
export TodoCli;
|
|
121
|
-
export TodoWeb;
|
|
122
|
-
export Cli;
|
|
123
|
-
export Web;
|
|
124
|
-
export Python;
|
|
125
|
-
export TypeScript;
|