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.
@@ -3,20 +3,16 @@ import { parseToAst, parseConstraint } from '../src/visitor.js'
3
3
  import type {
4
4
  ObjectDeclaration,
5
5
  ImportDeclaration,
6
- ExportDeclaration,
7
6
  GenerateDeclaration,
8
- PackageDeclaration,
9
7
  RealizeDeclaration,
10
8
  IncludeDeclaration,
11
- ExponentialPattern,
12
- EnumObject,
13
9
  Constraint,
14
10
  } from '../src/ast.js'
15
11
 
16
12
  describe('SpexParserVisitor', () => {
17
13
  describe('lexing errors', () => {
18
14
  it('should throw on unexpected characters', () => {
19
- expect(() => parseToAst('package module spex-parser as Main;')).toThrow(
15
+ expect(() => parseToAst('create Foo as spex-parser;')).toThrow(
20
16
  'Lexing errors: unexpected character: ->-<'
21
17
  )
22
18
  })
@@ -115,6 +111,7 @@ describe('SpexParserVisitor', () => {
115
111
  kind: 'SubObject',
116
112
  base: { kind: 'NamedObject', name: 'Number' },
117
113
  constraint: {
114
+ type: 'NaturalLanguage',
118
115
  raw: 'value is positive',
119
116
  parts: [{ kind: 'ConstraintText', text: 'value is positive' }],
120
117
  },
@@ -204,6 +201,7 @@ describe('SpexParserVisitor', () => {
204
201
  kind: 'SubObject',
205
202
  base: { kind: 'NamedObject', name: 'Number' },
206
203
  constraint: {
204
+ type: 'NaturalLanguage',
207
205
  raw: 'value is positive',
208
206
  parts: [{ kind: 'ConstraintText', text: 'value is positive' }],
209
207
  },
@@ -212,6 +210,7 @@ describe('SpexParserVisitor', () => {
212
210
  kind: 'SubObject',
213
211
  base: { kind: 'NamedObject', name: 'Number' },
214
212
  constraint: {
213
+ type: 'NaturalLanguage',
215
214
  raw: 'value is positive',
216
215
  parts: [{ kind: 'ConstraintText', text: 'value is positive' }],
217
216
  },
@@ -231,6 +230,7 @@ describe('SpexParserVisitor', () => {
231
230
  kind: 'SubObject',
232
231
  base: { kind: 'NamedObject', name: 'Number' },
233
232
  constraint: {
233
+ type: 'NaturalLanguage',
234
234
  raw: 'isPositive',
235
235
  parts: [{ kind: 'ConstraintText', text: 'isPositive' }],
236
236
  },
@@ -254,6 +254,7 @@ describe('SpexParserVisitor', () => {
254
254
  right: { kind: 'NamedObject', name: 'TypeScript' },
255
255
  },
256
256
  constraint: {
257
+ type: 'NaturalLanguage',
257
258
  raw: 'is an express app',
258
259
  parts: [{ kind: 'ConstraintText', text: 'is an express app' }],
259
260
  },
@@ -272,6 +273,7 @@ describe('SpexParserVisitor', () => {
272
273
  kind: 'SubObject',
273
274
  base: { kind: 'NamedObject', name: 'Number' },
274
275
  constraint: {
276
+ type: 'NaturalLanguage',
275
277
  raw: 'the number is positive',
276
278
  parts: [{ kind: 'ConstraintText', text: 'the number is positive' }],
277
279
  },
@@ -297,9 +299,10 @@ describe('SpexParserVisitor', () => {
297
299
  },
298
300
  },
299
301
  constraint: {
302
+ type: 'NaturalLanguage',
300
303
  raw: '@n is positive',
301
304
  parts: [
302
- { kind: 'ConstraintReference', name: 'n' },
305
+ { kind: 'ReferenceDirective', name: 'n' },
303
306
  { kind: 'ConstraintText', text: ' is positive' },
304
307
  ],
305
308
  },
@@ -329,6 +332,7 @@ describe('SpexParserVisitor', () => {
329
332
  base: { kind: 'NamedObject', name: 'Bool' },
330
333
  },
331
334
  constraint: {
335
+ type: 'NaturalLanguage',
332
336
  raw: 'logs the given input',
333
337
  parts: [{ kind: 'ConstraintText', text: 'logs the given input' }],
334
338
  },
@@ -350,11 +354,13 @@ describe('SpexParserVisitor', () => {
350
354
  kind: 'SubObject',
351
355
  base: { kind: 'NamedObject', name: 'Number' },
352
356
  constraint: {
357
+ type: 'NaturalLanguage',
353
358
  raw: 'value is positive',
354
359
  parts: [{ kind: 'ConstraintText', text: 'value is positive' }],
355
360
  },
356
361
  },
357
362
  constraint: {
363
+ type: 'NaturalLanguage',
358
364
  raw: 'value is odd',
359
365
  parts: [{ kind: 'ConstraintText', text: 'value is odd' }],
360
366
  },
@@ -446,6 +452,20 @@ describe('SpexParserVisitor', () => {
446
452
  })
447
453
  })
448
454
 
455
+ it('should convert basic object artifact to AST', () => {
456
+ const testCase = 'create MyObject as artifact;'
457
+ const ast = parseToAst(testCase)
458
+ const decl = ast.declarations[0] as ObjectDeclaration
459
+ expect(decl).toEqual({
460
+ kind: 'ObjectDeclaration',
461
+ name: 'MyObject',
462
+ object: {
463
+ kind: 'NamedObject',
464
+ name: 'artifact',
465
+ },
466
+ })
467
+ })
468
+
449
469
  it('should convert array type declaration to AST', () => {
450
470
  const testCase = 'create MyArray as string[];'
451
471
  const ast = parseToAst(testCase)
@@ -559,18 +579,6 @@ describe('SpexParserVisitor', () => {
559
579
  })
560
580
  })
561
581
 
562
- describe('export declaration', () => {
563
- it('should convert export declaration to AST', () => {
564
- const testCase = 'export EmailAddress;'
565
- const ast = parseToAst(testCase)
566
- const decl = ast.declarations[0] as ExportDeclaration
567
- expect(decl).toEqual({
568
- kind: 'ExportDeclaration',
569
- name: 'EmailAddress',
570
- })
571
- })
572
- })
573
-
574
582
  describe('generate declaration', () => {
575
583
  it('should convert generate declaration to AST', () => {
576
584
  const testCase = 'generate Main;'
@@ -579,76 +587,18 @@ describe('SpexParserVisitor', () => {
579
587
  expect(decl).toEqual({
580
588
  kind: 'GenerateDeclaration',
581
589
  name: 'Main',
582
- })
583
- })
584
- })
585
-
586
- describe('enum object', () => {
587
- it('should convert enum object declaration to AST', () => {
588
- const testCase = "create myEnum as enum ('v1', 'v2');"
589
- const ast = parseToAst(testCase)
590
- const decl = ast.declarations[0] as ObjectDeclaration
591
- expect(decl).toEqual({
592
- kind: 'ObjectDeclaration',
593
- name: 'myEnum',
594
- object: {
595
- kind: 'EnumObject',
596
- values: ['v1', 'v2'],
597
- },
598
- })
599
- })
600
-
601
- it('should convert single-value enum object to AST', () => {
602
- const testCase = "create Status as enum ('ACTIVE');"
603
- const ast = parseToAst(testCase)
604
- const decl = ast.declarations[0] as ObjectDeclaration
605
- expect(decl).toEqual({
606
- kind: 'ObjectDeclaration',
607
- name: 'Status',
608
- object: {
609
- kind: 'EnumObject',
610
- values: ['ACTIVE'],
611
- },
612
- })
613
- })
614
-
615
- it('should convert enum object with mixed quote values to AST', () => {
616
- const testCase = "create myEnum as enum (\"v1\", 'v2');"
617
- const ast = parseToAst(testCase)
618
- const decl = ast.declarations[0] as ObjectDeclaration
619
- expect(decl).toEqual({
620
- kind: 'ObjectDeclaration',
621
- name: 'myEnum',
622
- object: {
623
- kind: 'EnumObject',
624
- values: ['v1', 'v2'],
625
- },
590
+ environment: { kind: 'NamedObject', name: 'environment' },
626
591
  })
627
592
  })
628
593
 
629
- it('should convert enum object with escaped values to AST', () => {
630
- const testCase = "create myEnum as enum ('it\\'s', \"a \\\"b\\\"\", 'a\\\\b');"
594
+ it('should convert generate declaration with an environment to AST', () => {
595
+ const testCase = 'generate Main in Python;'
631
596
  const ast = parseToAst(testCase)
632
- const decl = ast.declarations[0] as ObjectDeclaration
597
+ const decl = ast.declarations[0] as GenerateDeclaration
633
598
  expect(decl).toEqual({
634
- kind: 'ObjectDeclaration',
635
- name: 'myEnum',
636
- object: {
637
- kind: 'EnumObject',
638
- values: ["it's", 'a "b"', 'a\\b'],
639
- },
640
- })
641
- })
642
-
643
- it('should convert enum object inside a product object to AST', () => {
644
- const testCase = "create Config as (kind: enum ('a', 'b'));"
645
- const ast = parseToAst(testCase)
646
- const decl = ast.declarations[0] as ObjectDeclaration
647
- expect(decl.object).toEqual({
648
- kind: 'ProductObject',
649
- fields: {
650
- kind: { kind: 'EnumObject', values: ['a', 'b'] },
651
- },
599
+ kind: 'GenerateDeclaration',
600
+ name: 'Main',
601
+ environment: { kind: 'NamedObject', name: 'Python' },
652
602
  })
653
603
  })
654
604
  })
@@ -1082,7 +1032,7 @@ describe('SpexParserVisitor', () => {
1082
1032
  expect(result).toEqual<Constraint>({
1083
1033
  raw: '@n is positive',
1084
1034
  parts: [
1085
- { kind: 'ConstraintReference', name: 'n' },
1035
+ { kind: 'ReferenceDirective', name: 'n' },
1086
1036
  { kind: 'ConstraintText', text: ' is positive' },
1087
1037
  ],
1088
1038
  })
@@ -1094,7 +1044,7 @@ describe('SpexParserVisitor', () => {
1094
1044
  raw: 'use @path for storage',
1095
1045
  parts: [
1096
1046
  { kind: 'ConstraintText', text: 'use ' },
1097
- { kind: 'ConstraintReference', name: 'path' },
1047
+ { kind: 'ReferenceDirective', name: 'path' },
1098
1048
  { kind: 'ConstraintText', text: ' for storage' },
1099
1049
  ],
1100
1050
  })
@@ -1106,9 +1056,9 @@ describe('SpexParserVisitor', () => {
1106
1056
  raw: 'return @z.real^2 + @z.imag^2',
1107
1057
  parts: [
1108
1058
  { kind: 'ConstraintText', text: 'return ' },
1109
- { kind: 'ConstraintReference', name: 'z.real' },
1059
+ { kind: 'ReferenceDirective', name: 'z.real' },
1110
1060
  { kind: 'ConstraintText', text: '^2 + ' },
1111
- { kind: 'ConstraintReference', name: 'z.imag' },
1061
+ { kind: 'ReferenceDirective', name: 'z.imag' },
1112
1062
  { kind: 'ConstraintText', text: '^2' },
1113
1063
  ],
1114
1064
  })
@@ -1120,9 +1070,9 @@ describe('SpexParserVisitor', () => {
1120
1070
  raw: 'call @LoadTodos using @path',
1121
1071
  parts: [
1122
1072
  { kind: 'ConstraintText', text: 'call ' },
1123
- { kind: 'ConstraintReference', name: 'LoadTodos' },
1073
+ { kind: 'ReferenceDirective', name: 'LoadTodos' },
1124
1074
  { kind: 'ConstraintText', text: ' using ' },
1125
- { kind: 'ConstraintReference', name: 'path' },
1075
+ { kind: 'ReferenceDirective', name: 'path' },
1126
1076
  ],
1127
1077
  })
1128
1078
  })
@@ -1132,7 +1082,7 @@ describe('SpexParserVisitor', () => {
1132
1082
  expect(result).toEqual<Constraint>({
1133
1083
  raw: '@validate the input',
1134
1084
  parts: [
1135
- { kind: 'ConstraintReference', name: 'validate' },
1085
+ { kind: 'ReferenceDirective', name: 'validate' },
1136
1086
  { kind: 'ConstraintText', text: ' the input' },
1137
1087
  ],
1138
1088
  })
@@ -1151,7 +1101,7 @@ describe('SpexParserVisitor', () => {
1151
1101
  expect(result).toEqual<Constraint>({
1152
1102
  raw: '@todo_item is valid',
1153
1103
  parts: [
1154
- { kind: 'ConstraintReference', name: 'todo_item' },
1104
+ { kind: 'ReferenceDirective', name: 'todo_item' },
1155
1105
  { kind: 'ConstraintText', text: ' is valid' },
1156
1106
  ],
1157
1107
  })
@@ -1180,6 +1130,7 @@ describe('SpexParserVisitor', () => {
1180
1130
  kind: 'SubObject',
1181
1131
  base: { kind: 'NamedObject', name: 'string' },
1182
1132
  constraint: {
1133
+ type: 'NaturalLanguage',
1183
1134
  raw: 'are valid -- like emails',
1184
1135
  parts: [{ kind: 'ConstraintText', text: 'are valid -- like emails' }],
1185
1136
  },
@@ -1194,10 +1145,11 @@ describe('SpexParserVisitor', () => {
1194
1145
  kind: 'SubObject',
1195
1146
  base: { kind: 'NamedObject', name: 'string' },
1196
1147
  constraint: {
1148
+ type: 'NaturalLanguage',
1197
1149
  raw: 'match /* strict */ @pattern',
1198
1150
  parts: [
1199
1151
  { kind: 'ConstraintText', text: 'match /* strict */ ' },
1200
- { kind: 'ConstraintReference', name: 'pattern' },
1152
+ { kind: 'ReferenceDirective', name: 'pattern' },
1201
1153
  ],
1202
1154
  },
1203
1155
  })
@@ -1213,6 +1165,7 @@ describe('SpexParserVisitor', () => {
1213
1165
  kind: 'SubObject',
1214
1166
  base: { kind: 'NamedObject', name: 'string' },
1215
1167
  constraint: {
1168
+ type: 'NaturalLanguage',
1216
1169
  raw: 'end with }',
1217
1170
  parts: [{ kind: 'ConstraintText', text: 'end with }' }],
1218
1171
  },
@@ -1227,6 +1180,7 @@ describe('SpexParserVisitor', () => {
1227
1180
  kind: 'SubObject',
1228
1181
  base: { kind: 'NamedObject', name: 'string' },
1229
1182
  constraint: {
1183
+ type: 'NaturalLanguage',
1230
1184
  raw: 'match {a}',
1231
1185
  parts: [{ kind: 'ConstraintText', text: 'match {a}' }],
1232
1186
  },
@@ -1241,6 +1195,7 @@ describe('SpexParserVisitor', () => {
1241
1195
  kind: 'SubObject',
1242
1196
  base: { kind: 'NamedObject', name: 'string' },
1243
1197
  constraint: {
1198
+ type: 'NaturalLanguage',
1244
1199
  raw: 'paths use \\',
1245
1200
  parts: [{ kind: 'ConstraintText', text: 'paths use \\' }],
1246
1201
  },
@@ -1255,10 +1210,11 @@ describe('SpexParserVisitor', () => {
1255
1210
  kind: 'SubObject',
1256
1211
  base: { kind: 'NamedObject', name: 'string' },
1257
1212
  constraint: {
1213
+ type: 'NaturalLanguage',
1258
1214
  raw: 'call @foo with }',
1259
1215
  parts: [
1260
1216
  { kind: 'ConstraintText', text: 'call ' },
1261
- { kind: 'ConstraintReference', name: 'foo' },
1217
+ { kind: 'ReferenceDirective', name: 'foo' },
1262
1218
  { kind: 'ConstraintText', text: ' with }' },
1263
1219
  ],
1264
1220
  },
@@ -1266,101 +1222,6 @@ describe('SpexParserVisitor', () => {
1266
1222
  })
1267
1223
  })
1268
1224
 
1269
- describe('package declaration', () => {
1270
- it('should convert package executable declaration to AST', () => {
1271
- const testCase = 'package executable myapp as Main in Python;'
1272
- const ast = parseToAst(testCase)
1273
- const decl = ast.declarations[0] as PackageDeclaration
1274
- expect(decl).toEqual({
1275
- kind: 'PackageDeclaration',
1276
- packageType: 'EXECUTABLE',
1277
- name: 'myapp',
1278
- objectName: { kind: 'NamedObject', name: 'Main' },
1279
- environment: { kind: 'NamedObject', name: 'Python' },
1280
- })
1281
- })
1282
-
1283
- it('should convert package module declaration to AST', () => {
1284
- const testCase = 'package module mylib as utils in Node;'
1285
- const ast = parseToAst(testCase)
1286
- const decl = ast.declarations[0] as PackageDeclaration
1287
- expect(decl).toEqual({
1288
- kind: 'PackageDeclaration',
1289
- packageType: 'MODULE',
1290
- name: 'mylib',
1291
- objectName: { kind: 'NamedObject', name: 'utils' },
1292
- environment: { kind: 'NamedObject', name: 'Node' },
1293
- })
1294
- })
1295
-
1296
- it('should convert package executable with complex object to AST', () => {
1297
- const testCase = 'package executable cli as (path: string) -> unit in Python;'
1298
- const ast = parseToAst(testCase)
1299
- const decl = ast.declarations[0] as PackageDeclaration
1300
- expect(decl).toEqual({
1301
- kind: 'PackageDeclaration',
1302
- packageType: 'EXECUTABLE',
1303
- name: 'cli',
1304
- objectName: {
1305
- kind: 'ExponentialObject',
1306
- exponent: {
1307
- kind: 'ProductObject',
1308
- fields: { path: { kind: 'NamedObject', name: 'string' } },
1309
- },
1310
- base: { kind: 'NamedObject', name: 'unit' },
1311
- },
1312
- environment: { kind: 'NamedObject', name: 'Python' },
1313
- })
1314
- })
1315
-
1316
- it('should convert package executable with dotted name to AST', () => {
1317
- const testCase = 'package executable myapp as app.Main in Python;'
1318
- const ast = parseToAst(testCase)
1319
- const decl = ast.declarations[0] as PackageDeclaration
1320
- expect(decl).toEqual({
1321
- kind: 'PackageDeclaration',
1322
- packageType: 'EXECUTABLE',
1323
- name: 'myapp',
1324
- objectName: { kind: 'NamedObject', name: 'app.Main' },
1325
- environment: { kind: 'NamedObject', name: 'Python' },
1326
- })
1327
- })
1328
-
1329
- it('should convert package executable with array type to AST', () => {
1330
- const testCase = 'package module mylib as string[] in Python;'
1331
- const ast = parseToAst(testCase)
1332
- const decl = ast.declarations[0] as PackageDeclaration
1333
- expect(decl).toEqual({
1334
- kind: 'PackageDeclaration',
1335
- packageType: 'MODULE',
1336
- name: 'mylib',
1337
- objectName: { kind: 'ArrayObject', base: { kind: 'NamedObject', name: 'string' } },
1338
- environment: { kind: 'NamedObject', name: 'Python' },
1339
- })
1340
- })
1341
-
1342
- it('should convert mixed declarations with package to AST', () => {
1343
- const testCase =
1344
- 'create Main as Number;\npackage executable myapp as Main in Python;'
1345
- const ast = parseToAst(testCase)
1346
- expect(ast.declarations).toHaveLength(2)
1347
- const createDecl = ast.declarations[0] as ObjectDeclaration
1348
- expect(createDecl).toEqual({
1349
- kind: 'ObjectDeclaration',
1350
- name: 'Main',
1351
- object: { kind: 'NamedObject', name: 'Number' },
1352
- })
1353
- const packageDecl = ast.declarations[1] as PackageDeclaration
1354
- expect(packageDecl).toEqual({
1355
- kind: 'PackageDeclaration',
1356
- packageType: 'EXECUTABLE',
1357
- name: 'myapp',
1358
- objectName: { kind: 'NamedObject', name: 'Main' },
1359
- environment: { kind: 'NamedObject', name: 'Python' },
1360
- })
1361
- })
1362
- })
1363
-
1364
1225
  describe('realize declaration', () => {
1365
1226
  it('should convert a realize declaration to AST', () => {
1366
1227
  const testCase = 'realize Shape as Circle in environment;'
@@ -1479,103 +1340,76 @@ describe('SpexParserVisitor', () => {
1479
1340
  })
1480
1341
  })
1481
1342
 
1482
- describe('lambda object', () => {
1483
- it('should convert lambda with product base to AST', () => {
1484
- const testCase = 'create sum as lambda (a: number, b: number) -> number ```python\nreturn a + b\n```;'
1343
+ describe('subobject with structured and code constraints', () => {
1344
+ it('should convert subobject with a structured constraint to AST', () => {
1345
+ const testCase = 'create double as from number -> number select ```\nreturn n * 2\n```;'
1485
1346
  const ast = parseToAst(testCase)
1486
1347
  const decl = ast.declarations[0] as ObjectDeclaration
1487
- const lambda = decl.object as ExponentialPattern
1488
- expect(lambda).toEqual({
1489
- kind: 'ExponentialPattern',
1490
- base: { kind: 'NamedObject', name: 'number' },
1491
- exponent: {
1492
- kind: 'ProductObject',
1493
- fields: {
1494
- a: { kind: 'NamedObject', name: 'number' },
1495
- b: { kind: 'NamedObject', name: 'number' },
1496
- },
1348
+ expect(decl.object).toEqual({
1349
+ kind: 'SubObject',
1350
+ base: {
1351
+ kind: 'ExponentialObject',
1352
+ exponent: { kind: 'NamedObject', name: 'number' },
1353
+ base: { kind: 'NamedObject', name: 'number' },
1354
+ },
1355
+ constraint: {
1356
+ type: 'Structured',
1357
+ raw: 'return n * 2',
1358
+ parts: [{ kind: 'ConstraintText', text: 'return n * 2' }],
1497
1359
  },
1498
- language: 'python',
1499
- body: 'return a + b',
1500
- patterns: [],
1501
- })
1502
- })
1503
-
1504
- it('should convert lambda with named base to AST', () => {
1505
- const testCase = 'create double as lambda number -> number ```python\nreturn @n * 2\n```;'
1506
- const ast = parseToAst(testCase)
1507
- const decl = ast.declarations[0] as ObjectDeclaration
1508
- const lambda = decl.object as ExponentialPattern
1509
- expect(lambda).toEqual({
1510
- kind: 'ExponentialPattern',
1511
- base: { kind: 'NamedObject', name: 'number' },
1512
- exponent: { kind: 'NamedObject', name: 'number' },
1513
- language: 'python',
1514
- body: 'return @n * 2',
1515
- patterns: [],
1516
1360
  })
1517
1361
  })
1518
1362
 
1519
- it('should extract pattern blocks from lambda body', () => {
1520
- const testCase =
1521
- 'create transform as lambda (x: number) -> number ```python\nif @x > 0:\n @{return sin(@x)}\nelse:\n @{return cos(@x)}\n```;'
1363
+ it('should convert subobject with a code constraint to AST', () => {
1364
+ const testCase = 'create double as from number -> number select ```python\nreturn @n * 2\n```;'
1522
1365
  const ast = parseToAst(testCase)
1523
1366
  const decl = ast.declarations[0] as ObjectDeclaration
1524
- const lambda = decl.object as ExponentialPattern
1525
- expect(lambda.patterns).toHaveLength(2)
1526
- expect(lambda.patterns[0]).toEqual({
1527
- raw: 'return sin(@x)',
1528
- parts: [
1529
- { kind: 'ConstraintText', text: 'return sin(' },
1530
- { kind: 'ConstraintReference', name: 'x' },
1531
- { kind: 'ConstraintText', text: ')' },
1532
- ],
1533
- start: expect.any(Number),
1534
- end: expect.any(Number),
1535
- })
1536
- expect(lambda.patterns[1]).toEqual({
1537
- raw: 'return cos(@x)',
1538
- parts: [
1539
- { kind: 'ConstraintText', text: 'return cos(' },
1540
- { kind: 'ConstraintReference', name: 'x' },
1541
- { kind: 'ConstraintText', text: ')' },
1542
- ],
1543
- start: expect.any(Number),
1544
- end: expect.any(Number),
1367
+ expect(decl.object).toEqual({
1368
+ kind: 'SubObject',
1369
+ base: {
1370
+ kind: 'ExponentialObject',
1371
+ exponent: { kind: 'NamedObject', name: 'number' },
1372
+ base: { kind: 'NamedObject', name: 'number' },
1373
+ },
1374
+ constraint: {
1375
+ type: 'Code',
1376
+ language: 'python',
1377
+ body: 'return @n * 2',
1378
+ parts: [
1379
+ { kind: 'ConstraintText', text: 'return ' },
1380
+ { kind: 'ReferenceDirective', name: 'n' },
1381
+ { kind: 'ConstraintText', text: ' * 2' },
1382
+ ],
1383
+ },
1545
1384
  })
1546
1385
  })
1547
1386
 
1548
- it('should track correct positions for multiple pattern blocks', () => {
1549
- const testCase =
1550
- 'create transform as lambda (x: number) -> number ```python\nif @x > 0:\n @{return sin(@x)}\nelse:\n @{return cos(@x)}\n```;'
1551
- const ast = parseToAst(testCase)
1552
- const decl = ast.declarations[0] as ObjectDeclaration
1553
- const lambda = decl.object as ExponentialPattern
1554
- expect(lambda.patterns[0].start).toBeLessThan(lambda.patterns[0].end)
1555
- expect(lambda.patterns[1].start).toBeGreaterThan(lambda.patterns[0].end)
1556
- expect(lambda.patterns[1].start).toBeLessThan(lambda.patterns[1].end)
1557
- })
1558
-
1559
- it('should convert lambda in product field to AST', () => {
1387
+ it('should convert a code constraint in a product field to AST', () => {
1560
1388
  const testCase =
1561
- 'create Config as (handler: lambda (x: string) -> string ```typescript\nreturn x.toUpperCase();\n```, port: number);'
1389
+ 'create Config as (handler: from (x: string) -> string select ```typescript\nreturn x.toUpperCase();\n```, port: number);'
1562
1390
  const ast = parseToAst(testCase)
1563
1391
  const decl = ast.declarations[0] as ObjectDeclaration
1564
1392
  expect(decl.object).toEqual({
1565
1393
  kind: 'ProductObject',
1566
1394
  fields: {
1567
1395
  handler: {
1568
- kind: 'ExponentialPattern',
1569
- base: { kind: 'NamedObject', name: 'string' },
1570
- exponent: {
1571
- kind: 'ProductObject',
1572
- fields: {
1573
- x: { kind: 'NamedObject', name: 'string' },
1396
+ kind: 'SubObject',
1397
+ base: {
1398
+ kind: 'ExponentialObject',
1399
+ exponent: {
1400
+ kind: 'ProductObject',
1401
+ fields: {
1402
+ x: { kind: 'NamedObject', name: 'string' },
1403
+ },
1574
1404
  },
1405
+ base: { kind: 'NamedObject', name: 'string' },
1406
+ },
1407
+ constraint: {
1408
+ type: 'Code',
1409
+ language: 'typescript',
1410
+ body: 'return x.toUpperCase();',
1411
+ parts: [{ kind: 'ConstraintText', text: 'return x.toUpperCase();' }],
1575
1412
  },
1576
- language: 'typescript',
1577
- body: 'return x.toUpperCase();',
1578
- patterns: [],
1579
1413
  },
1580
1414
  port: { kind: 'NamedObject', name: 'number' },
1581
1415
  },