@naanlang/naan 1.0.13 → 1.0.15

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/lib/env_node.js CHANGED
@@ -37,6 +37,7 @@ exports.NaanNodeREPL = function NaanNodeREPL(options) {
37
37
  if (typeof(options) != 'object')
38
38
  options = { };
39
39
  var repl = require('repl');
40
+ var replServer;
40
41
  var process = require('process');
41
42
  var Naan = require('./core/naanlib');
42
43
  var naanlib = new Naan.Naanlib();
@@ -69,6 +70,14 @@ exports.NaanNodeREPL = function NaanNodeREPL(options) {
69
70
  outEnable = true;
70
71
  naanlib.textLine(cmd);
71
72
  };
73
+
74
+ this.setPrompt = function setPrompt(prompt) {
75
+ if (replServer) {
76
+ replServer.setPrompt(prompt);
77
+ return (prompt);
78
+ }
79
+ return (false);
80
+ };
72
81
 
73
82
 
74
83
  //==========================================================================
@@ -85,9 +94,9 @@ exports.NaanNodeREPL = function NaanNodeREPL(options) {
85
94
 
86
95
  if (!options.replDisable)
87
96
  {
88
- var nprompt = repl.start({ prompt: 'Naan> ', eval: myEval});
97
+ replServer = repl.start({ prompt: 'Naan> ', eval: myEval});
89
98
 
90
- nprompt.on('line', function(cmd) { // process raw command line
99
+ replServer.on('line', function(cmd) { // process raw command line
91
100
  if (cmd.charAt(0) != ".") {
92
101
  textToRemoteTerm(cmd);
93
102
  naanlib.textLine(cmd + "\n");
@@ -98,7 +107,7 @@ exports.NaanNodeREPL = function NaanNodeREPL(options) {
98
107
  // Hook the ^C interrupt
99
108
  //
100
109
 
101
- nprompt._events.SIGINT = function () {
110
+ replServer._events.SIGINT = function () {
102
111
  naanlib.escape();
103
112
  };
104
113
 
@@ -106,7 +115,7 @@ exports.NaanNodeREPL = function NaanNodeREPL(options) {
106
115
  // Hook an exit command
107
116
  //
108
117
 
109
- nprompt.on('exit', function() {
118
+ replServer.on('exit', function() {
110
119
  process.exit();
111
120
  });
112
121
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@naanlang/naan",
3
- "version": "1.0.13",
3
+ "version": "1.0.15",
4
4
  "author": "Richard C. Zulch",
5
5
  "description": "Naan™ software platform",
6
6
  "main": "./lib/core/naanlib.js",
@@ -32,6 +32,10 @@
32
32
  * `number -- float or integer
33
33
  * `url -- URL
34
34
  *
35
+ * Although the currency type is only treated as a text field for now, the intent is that these will
36
+ * match the RegEx /([A-Z]{3})(-?[0-9]+(?:[,.][0-9]+)?)/ so that the currency type and amount can be
37
+ * differentiated. Please see ISO 4217 for valid currency types.
38
+ *
35
39
  * Current options are:
36
40
  * {
37
41
  * languages: [
@@ -183,15 +187,15 @@ closure openSearchIndex(prosc, indexName, ioptions, local osin) {
183
187
  //
184
188
  // date
185
189
  //
186
- date: quote(
187
- textRawMF()
188
- )
190
+ date: quote({
191
+ type: "date"
192
+ })
189
193
  //
190
194
  // boolean
191
195
  //
192
- boolean: quote(
193
- textRawMF()
194
- )
196
+ boolean: quote({
197
+ type: "boolean"
198
+ })
195
199
  //
196
200
  // currency
197
201
  //
@@ -213,15 +217,15 @@ closure openSearchIndex(prosc, indexName, ioptions, local osin) {
213
217
  //
214
218
  // integer
215
219
  //
216
- integer: quote(
217
- textRawMF()
218
- )
220
+ integer: quote({
221
+ type: "long"
222
+ })
219
223
  //
220
224
  // number
221
225
  //
222
- number: quote(
223
- textRawMF()
224
- )
226
+ number: quote({
227
+ type: "float"
228
+ })
225
229
  //
226
230
  // url
227
231
  //
@@ -303,7 +307,6 @@ closure openSearchIndex(prosc, indexName, ioptions, local osin) {
303
307
  properties: makeMapping(fields)
304
308
  }
305
309
  })
306
- // printline("OpenSearchClient.create:\n${Dialect.print(params)}")
307
310
  prosc.request(indexName, {
308
311
  method: "PUT"
309
312
  putdata: params
@@ -500,7 +503,6 @@ closure OpenSearchClient(goptions, local oscl, prosc) {
500
503
  query.multi_match.fields = ["*"].concat(soptions.boost.fields.map(function (item) {
501
504
  item.concat("^${soptions.boost.level}")
502
505
  }))
503
- printline(Dialect.print(soptions.boost))
504
506
  if dictionary(soptions.qopts)
505
507
  query.multi_match = merge(query.multi_match, soptions.qopts)
506
508
  if dictionary(soptions.filter) {
@@ -357,7 +357,7 @@ closure DynaConverter(local conv) {
357
357
  //
358
358
  // condition dictionary:
359
359
  // {
360
- // key: <keyname, e.g. partition key name> // required
360
+ // key: <keyname, e.g. partition key name or path tuple> // required
361
361
  // "=": <expression> // exclusive of other comparisons
362
362
  // ">": <expression> // exclusive of other comparisons
363
363
  // "<": <expression> // exclusive of other comparisons
@@ -365,11 +365,12 @@ closure DynaConverter(local conv) {
365
365
  // beginsWith: <string-expression> // exclusive of other comparisons
366
366
  // ">=": <expression> // with <= or alone
367
367
  // "<=": <expression> // with >= or alone
368
+ // exists: <boolean> // keyname existence matches boolean
368
369
  // }
369
370
  //
370
371
 
371
372
  conv.createKeyExpression = function createKeyExpression(params, conds
372
- local attval, rangename, range, keyname, op, expr, rangex, arg1, arg2) {
373
+ local attval, attname, rangename, range, keyname, op, expr, rangex, arg1, arg2) {
373
374
 
374
375
  // dupeop
375
376
  //
@@ -382,14 +383,24 @@ closure DynaConverter(local conv) {
382
383
  // build the expression
383
384
  //
384
385
  attval = genAttributeValueAdder(params)
386
+ attname = genAttributeNameAdder(params)
385
387
  for `(rangename, ranges) in conds {
386
388
  if !array(ranges)
387
389
  ranges = [ranges]
388
390
  for range in ranges {
389
391
  rangex = false
390
- keyname = tostring(range.key)
392
+ if tuple(range.key) {
393
+ keyname = []
394
+ expr = range.key
395
+ while expr
396
+ keyname.push(attname(pop(expr)))
397
+ keyname = keyname.join(".")
398
+ }
399
+ else
400
+ keyname = tostring(range.key)
391
401
  if !keyname
392
402
  return (list(Error("key expression requires key name")))
403
+ keyname = attname(keyname)
393
404
  for `(op, expr) in range {
394
405
  if op == "key"
395
406
  continue
@@ -417,10 +428,16 @@ closure DynaConverter(local conv) {
417
428
  arg1 = rangex.1
418
429
  if rangex.0 == "between" {
419
430
  arg2 = rangex.2
420
- expr = keyname.concat(" between ", arg1, " and ", arg2)
431
+ expr = "${keyname} between ${arg1} and ${arg2}"
421
432
  }
422
433
  else if rangex.0 == "beginsWith"
423
- expr = "begins_with(".concat(keyname, ", ", arg1, ")")
434
+ expr = "begins_with(${keyname}, ${arg1})"
435
+ else if rangex.0 == "exists" {
436
+ if arg1
437
+ expr = "attribute_exists(${keyname})"
438
+ else
439
+ expr = "attribute_not_exists(${keyname})"
440
+ }
424
441
  else
425
442
  expr = keyname.concat(space, rangex.0, space, arg1)
426
443
  if params[rangename]
@@ -674,14 +691,26 @@ closure DynaTable(dyna, tablename, local table) {
674
691
 
675
692
  // getRecord
676
693
  //
677
- // Get a record from the table with the specified hash value, and optional range value.
694
+ // Get a record from the table with the specified hash value. The optional second argument can be
695
+ // the rangeValue of the item, or an options dictionary. Dictionary options include:
696
+ // {
697
+ // rangeValue: <range-key> // record's range key
698
+ // consistentRead: <boolean> // true to use consistent read
699
+ // }
700
+ // The result is a standard result tuple `(error, record).
701
+
678
702
  //
679
- table.getRecord = closure getRecord(hashValue, rangeValue, local params, pending) {
703
+ table.getRecord = closure getRecord(hashValue, options, local params, rangeValue, pending) {
680
704
  if preflightError(this)
681
705
  return
682
706
  params = {
683
707
  TableName: tablename
684
708
  }
709
+ if dictionary(options) {
710
+ rangeValue = options.rangeValue
711
+ params.ConsistentRead = options.consistentRead
712
+ } else
713
+ rangeValue = options
685
714
  params.Key = makeDynKey(list(hashValue, rangeValue))
686
715
  pending = new(nonce)
687
716
  dyna.aws.getItem(params, function(error, data) {
@@ -856,6 +885,7 @@ closure DynaTable(dyna, tablename, local table) {
856
885
  // reverse: <boolean> // reverse order
857
886
  // project: <array> // project only specified attributes
858
887
  // paging: <exclusive start key> // for paging; updated each call
888
+ // consistentRead: <boolean> // true to use consistent read
859
889
  // limit: <number> // maximum count of results
860
890
  // }
861
891
  //
@@ -902,6 +932,8 @@ closure DynaTable(dyna, tablename, local table) {
902
932
  params.ProjectionExpression = options.project.join(", ")
903
933
  if options.paging
904
934
  params.ExclusiveStartKey = options.paging
935
+ if options.consistentRead
936
+ params.ConsistentRead = options.consistentRead
905
937
  output = []
906
938
  `(error) = dynQueryPager(`query, params, options.limit, function(data) {
907
939
  if data.Count > 0
@@ -921,12 +953,13 @@ closure DynaTable(dyna, tablename, local table) {
921
953
  //
922
954
  // Options:
923
955
  // {
924
- // index: <string> // index name in table
925
- // filter: <key-expression> // conditions for items to return
926
- // attmap: <dictionary> // map attribute names to #shortcuts
927
- // project: <array> // project only specified attributes
928
- // paging: <exclusive start key> // for paging; updated each call
929
- // limit: <number> // maximum count of results
956
+ // index: <string> // index name in table
957
+ // filter: <key-expression> // conditions for items to return
958
+ // attmap: <dictionary> // map attribute names to #shortcuts
959
+ // project: <array> // project only specified attributes
960
+ // paging: <exclusive start key> // for paging; updated each call
961
+ // consistentRead: <boolean> // true to use consistent read
962
+ // limit: <number> // maximum count of results
930
963
  // }
931
964
  //
932
965
 
@@ -953,6 +986,8 @@ closure DynaTable(dyna, tablename, local table) {
953
986
  params.ProjectionExpression = options.project.join(", ")
954
987
  if options.paging
955
988
  params.ExclusiveStartKey = options.paging
989
+ if options.consistentRead
990
+ params.ConsistentRead = options.consistentRead
956
991
  output = []
957
992
  `(error) = dynQueryPager(`scan, params, options.limit, function(data) {
958
993
  if data.Count >= 0
@@ -319,10 +319,13 @@ RegisterTestCategory("core", [
319
319
  [ "eval(`(+ 4 3));", "7" ],
320
320
 
321
321
  [ "apply(`+, `(4 3));", "7" ],
322
-
322
+ [ "apply(getproc(`+), `(4, 3));", "7" ], // ensures that call takes a builtin as function
323
+ [ "apply(Number.parseInt, list('42'));", "42" ],
324
+
323
325
  [ "call(car(`+), 4, 3);", "7" ],
324
326
  [ "call(getproc(`+), 4, 3);", "7" ], // ensures that call takes a builtin as function
325
-
327
+ [ "call(Number.parseInt, '42');", "42" ],
328
+
326
329
  [ "list();>", "false" ],
327
330
  [ "list(3, 4, 5, `(6 7));>", "(3 4 5 (6 7))" ],
328
331
 
@@ -354,12 +357,16 @@ RegisterTestCategory("core", [
354
357
  [ "print(testcore=`testcore);", "testcore|testcore" ],
355
358
  [ "print(testcore=42);", "42|42" ],
356
359
 
357
- [ "cond();", { errors: "cond( requires if expression at (1, 6) found )" } ],
360
+ [ "cond();", "false" ],
361
+ [ "cond(true);",
362
+ { errors: "if expression required at (1, 6) found true" } ],
358
363
  [ "quote(cond(if false 3 else if true 4));>", "(cond ((identity false) 3) ((identity true) 4))" ],
359
364
  [ "cond(if false 3 else if true 4);", "4" ],
360
365
  [ "cond (if 1 and\n 2, 3);", "3" ],
361
366
  [ "cond(print(false),print(\"hi\"));",
362
- { errors: "cond( requires if expression at (1, 6) found print" } ],
367
+ { errors: "if expression required at (1, 6) found print" } ],
368
+ [ "cond(if 3 4, print(5));",
369
+ { errors: "if expression required at (1, 14) found print" } ],
363
370
 
364
371
  [ "body();", "false" ],
365
372
  [ "quote(body(if false 3 else if true 4));>", "(body ((identity false) 3) ((identity true) 4))" ],
@@ -367,8 +374,10 @@ RegisterTestCategory("core", [
367
374
  [ "body(print(\"hi\"), print(\" there\"),4+3);>", "hi there|7" ],
368
375
  [ "body(print(false),print(\"hi\"));", "falsehi|\"hi\"" ],
369
376
 
370
- [ "push();>", { errors: "attempt to modify constant false" } ],
371
- [ "push(true);>", { errors: "attempt to modify constant false" } ],
377
+ [ "push();>",
378
+ { errors: "attempt to modify constant false" } ],
379
+ [ "push(true);>",
380
+ { errors: "attempt to modify constant false" } ],
372
381
  [ "push(true, 3);>", "false" ],
373
382
  [ "testcore=false;", "false" ],
374
383
  [ "push(11, testcore);>", "(11)" ],
@@ -424,7 +433,9 @@ RegisterTestCategory("core", [
424
433
  [ "temp(2, `(1 2 3));>", "(2 3)|(2 3)" ],
425
434
  [ "function (ex1, lex1) { ex1+pop(lex1), print(lex1), lex1 }(2, `(1 2 3));>", "(2 3)|(2 3)" ],
426
435
  [ "quote(function (a)\n { f(b)-\ng(c) * h(d)\n });>", "(function lambda (a) (- (f b) (* (g c) (h d))))" ],
427
- [ "quote(function (a)\n { f(b)\n-g(c) * h(d)\n });>", "(function lambda (a) (- (f b) (* (g c) (h d))))" ],
436
+ [ "quote(function (a)\n { f(b)\n-g(c) * h(d)\n });>",
437
+ { errors: "ambiguous newline (use \\ or ,) at (3, 1) found -" }],
438
+ [ "quote(function (a)\n { f(b)\\\n-g(c) * h(d)\n });>", "(function lambda (a) (- (f b) (* (g c) (h d))))" ],
428
439
 
429
440
  [ "tempo = temp.proc;>", "(function temp (ex1 lex1) (+ ex1 (pop lex1)) (print lex1) lex1)" ],
430
441
  [ "function temp(ex1) { ex1 * 2 };", "==> redefined: temp|temp" ],
@@ -589,7 +600,21 @@ RegisterTestCategory("core", [
589
600
  [ "bobby.sue(44);", "(44)(sue billy)/Dictionary{2}|(sam nevil bill)/(sue billy)|(\"sue\", \"billy\")" ],
590
601
  [ "keys(bobby);>", "(\"sue\" \"billy\")" ],
591
602
 
592
-
603
+ ["merge();", "{ }"],
604
+ ["merge({a:3});", "{ a: 3 }"],
605
+ ["merge({a:3,b:4});", "{ a: 3, b: 4 }"],
606
+ ["merge({a:3,b:4,c:5});", "{ a: 3, b: 4, c: 5 }"],
607
+ ["merge(`(a));", "{ }"],
608
+ ["merge({a:3}, `(a));", "{ }"],
609
+ ["merge({a:3,b:4}, `(a), {c:5});", "{ b: 4, c: 5 }"],
610
+ ["merge({a:3,b:4}, `(c), {c:5});", "{ a: 3, b: 4, c: 5 }"],
611
+ ["merge({a:3,b:4}, `(g), {c:5});", "{ a: 3, b: 4, c: 5 }"],
612
+ ["merge({true:a,false:b});", "{ true: a, false: a }"],
613
+ ["merge({true:a,false:b}, `(true));", "{ false: a }"],
614
+ ["oo={true:a}; oo[1.0/3]=`b; oo;", "{ true: a }|b|{ true: a, 0.3333333333333333: b }"],
615
+ ["merge(oo, list(1.0/3), {false:c});", "{ true: a, false: c }"],
616
+ ["oo;", "{ true: a, 0.3333333333333333: b }"],
617
+
593
618
  //
594
619
  // object getters
595
620
  //
@@ -796,6 +821,9 @@ RegisterTestCategory("core", [
796
821
  [ "cons(`a);>", "(a)" ],
797
822
  [ "cons(`a,`b);>", "(a . b)" ],
798
823
  [ "cons(`a,`b,`c);>", "(a b . c)" ],
824
+
825
+ [ "unintern(true) === true", "false"],
826
+
799
827
  [ "length(stacktrace());", "55" ],
800
828
 
801
829
  //
@@ -35,11 +35,11 @@ RegisterTestCategory("JSinterop", [
35
35
 
36
36
  [ "o(3);", {
37
37
  "dev": { errors: "xcall \"[object Object]\": TypeError: object.apply is not a function" }
38
- "prod": { errors: "xcall \"[object Object]\": TypeError: e[3].apply is not a function" }
38
+ "prod": { errors: "xcall \"[object Object]\": TypeError: r[3].apply is not a function" }
39
39
  }[ "prod"]],
40
40
  [ "o(Number);", {
41
41
  "dev": { errors: "xcall \"[object Object]\": TypeError: object.apply is not a function" }
42
- "prod": { errors: "xcall \"[object Object]\": TypeError: e[3].apply is not a function" }
42
+ "prod": { errors: "xcall \"[object Object]\": TypeError: r[3].apply is not a function" }
43
43
  }[ "prod"]],
44
44
  [ "Number(3);", "[Number 3]" ],
45
45
  [ "Number(3).toString();", "\"3\"" ],
@@ -63,6 +63,15 @@ RegisterTestCategory("JSinterop", [
63
63
 
64
64
  [ "xnew(js.g.Promise, function() {});", "[object Promise]" ],
65
65
 
66
+ //
67
+ // Arrays
68
+ //
69
+
70
+ ["Array(3,4,5);", "[Array 3,4,5]"],
71
+ ["Array([3]);", "[Array [3]]"],
72
+ ["Array([3]).0;", "[Array 3]"],
73
+ ["Array([3]).0.0;", "3"],
74
+
66
75
 
67
76
  //
68
77
  // child processes
@@ -71,6 +71,6 @@ RegisterTestCategory("strings", [
71
71
  [ "\"abc\".toString();", "\"abc\"" ],
72
72
  [ "xapply(String, toString, list(\"abc\"));", "\"abc\"" ],
73
73
  [ "xcall(String, toString, \"abc\");", "\"abc\"" ],
74
- [ "function(local match) { \"abc\".match(RegExp(\"abc\")) }();", "[Array abc]" ]
74
+ [ "function(local match) { \"abc\".match(RegExp(\"abc\")) }();", '[Array "abc"]' ]
75
75
 
76
76
  ]);
@@ -438,15 +438,18 @@ RegisterTestCategory("Lingo parse", [
438
438
  [ "try{nest()}finally{};",
439
439
  { exprs: "(catch ((nest)) false false)" } ],
440
440
  [ "try{nest()}catch{}finally{};",
441
- { exprs: "(catch ((nest)) (((identity true))) false)" } ],
441
+ { exprs: "(catch ((nest)) (((identity true) false)) false)" } ],
442
442
  [ "function test1() { try { nest() } finally { } };",
443
443
  { exprs: "(defproc function test1 false (catch ((nest)) false false))",
444
444
  text: "==> redefined: test1" } ],
445
445
  [ "function(){try{nest()}catch{},if(1)2};",
446
- { exprs: "(function lambda false (catch ((nest)) (((identity true))) false) ((identity 1) 2))" } ],
446
+ { exprs: "(function lambda false (catch ((nest)) (((identity true) false)) false) ((identity 1) 2))" } ],
447
447
  [ "function(x) { try {} catch { if true { 4 } } };",
448
448
  { exprs: "(function lambda (x) (catch false (((identity true) 4)) false))" } ],
449
-
449
+ [ "try { throw(green) } catch { printline(4) };",
450
+ { exprs: "(catch ((throw green)) (((identity true) (printline 4))) false)",
451
+ text: "4" } ],
452
+
450
453
  //
451
454
  // cond
452
455
  //
@@ -468,7 +471,7 @@ RegisterTestCategory("Lingo parse", [
468
471
  //
469
472
 
470
473
  [ "function() {test()[3]};>", "(function lambda false (deref (test) 3))"],
471
- [ "function() {test()\n[3]};>", "(function lambda false (deref (test) 3))"],
474
+ [ "function() {test()\\\n[3]};>", "(function lambda false (deref (test) 3))"],
472
475
  [ "function() {test(),\n[3]};>", "(function lambda false (test) Array[1])"],
473
476
  [ "function() {test(),\n[3]};", "function () {\n"
474
477
  " test(),\n" // need comma before array
@@ -497,6 +500,42 @@ RegisterTestCategory("Lingo parse", [
497
500
  " else {\n"
498
501
  " { a: 3 } }\n" // need braces around dictionary
499
502
  "}" ],
503
+ [ "function() {if 1, -2 };", "function () {\n"
504
+ " if 1\n"
505
+ " -2\n"
506
+ "}" ],
507
+ [ "function() {if 1,\n"
508
+ "-2 };", "function () {\n"
509
+ " if 1\n"
510
+ " -2\n"
511
+ "}" ],
512
+ [ "function() {if 1\n"
513
+ "-2 };",
514
+ { errors: "ambiguous newline (use \\ or ,) at (2, 1) found -" } ],
515
+ [ "function() {if 1\\\n"
516
+ "-2 3 };", "function () {\n"
517
+ " if 1 - 2\n"
518
+ " 3\n"
519
+ "}" ],
520
+ [ "function(x) {if x\n[2]\n3 };",
521
+ { errors: "ambiguous newline (use \\ or ,) at (2, 1) found [" } ],
522
+ [ "function(x) {if x,\n[2]\n3 };",
523
+ "function (x) {\n"
524
+ " if x\n"
525
+ " [2]\n"
526
+ " 3\n"
527
+ "}" ],
528
+ [ "function(x) {if x\\\n[2]\n3 };",
529
+ "function (x) {\n"
530
+ " if x[2]\n"
531
+ " 3\n"
532
+ "}" ],
533
+ [ "function(x) {if x\n(2)\n3 };",
534
+ { errors: "ambiguous newline (use \\ or ,) at (2, 1) found (" } ],
535
+ [ "function() { else };",
536
+ { errors: "stray keyword at (1, 14) found else" } ],
537
+ [ "function() { if 1 else 3 };",
538
+ { errors: "stray keyword at (1, 19) found else" } ],
500
539
 
501
540
  //
502
541
  // semantic validity
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * column positioning: // // !
8
8
  *
9
- * Copyright (c) 2018-2021 by Richard C. Zulch
9
+ * Copyright (c) 2018-2023 by Richard C. Zulch
10
10
  *
11
11
  */
12
12
 
@@ -159,15 +159,25 @@ RegisterTestCategory("Lingo", [
159
159
  // " map : [(0, 23), 0, 9, 2, 7],\n" -- maps currently omitted
160
160
  " comments : [] } }" ],
161
161
  [ "testat@jim(true);", "\"you are annotated with true!\"" ],
162
+ [ "copyann(testat, a);", "testat" ],
163
+ [ "testat@*;", '("joe", "jim", ".sourcemap")' ],
164
+ [ "copyann(testat, a, true);", "testat" ],
165
+ [ "testat@*;", '("joe")' ],
166
+ [ "copyann(3, a);", { errors: "attempt to modify constant 3" } ],
167
+ [ "copyann(a, 3);", "false" ],
162
168
 
163
169
 
164
170
  //
165
- // lists, objects & arrays
171
+ // lists, dictionaries, objects, and arrays
166
172
  //
167
173
 
168
174
  [ "divide(71, 3);", "(23 . 2)" ],
169
175
  [ "[3,4+5];", "[3, 9]" ],
170
176
  [ "{a:3,b:4+5};", "{ a: 3, b: 9 }" ],
177
+ [ "function(g) { { a: 3+g } }(5);", "{ a: 8 }" ],
178
+ [ "function(g) { { a: false || undefined } }(6);", "{ }" ],
179
+ [ "function(g) { { a: false && g || undefined } }(7);", "{ }" ],
180
+ [ "function(g) { { a: true && g || undefined } }(8);", "{ a: 8 }" ],
171
181
 
172
182
  [ "`(1, 2, {a:3,b:4}, 5);", "(1, 2, { a: 3, b: 4 }, 5)" ],
173
183
  [ "`(1, 2, {a:3,b:4}, 5);>", "(1 2 Dictionary{2} 5)" ],
@@ -287,6 +297,27 @@ RegisterTestCategory("Lingo", [
287
297
  [ "aa[1] /= 2;", "3" ],
288
298
 
289
299
 
300
+ //
301
+ // catch
302
+ //
303
+
304
+ [ "try { throw(green) } catch { printline(4) };", "4|4" ],
305
+ [ "try { throw(blue) } catch { false(),printline(5) };", "5|5" ],
306
+ [ "try { 1/0 } catch {};", "false"],
307
+ [ "try { 1/0 } catch { 0 };", "0"],
308
+ [ "try { 1/0 } catch { exception };", '(internal, "divide by zero")'],
309
+ [ "try { 1/0 } catch { if exception == 3 4, printline(5) };",
310
+ { errors: "if expression required at (1, 42) found printline"} ],
311
+ [ "try { throw(blue) } catch { if exception == blue printline(blue) };", "blue|blue"],
312
+ [ "try { throw(green) } catch { if exception == blue printline(blue) };",
313
+ { errors: "exception: green" } ],
314
+ [ "try { throw(green) } catch { if exception == blue printline(blue),"
315
+ "if exception == green printline(green) };", "green|green"],
316
+ [ "try { throw(red) } catch { if exception == blue printline(blue),"
317
+ "if exception == green printline(green) };",
318
+ { errors: "exception: red" } ],
319
+
320
+
290
321
  //
291
322
  // if else
292
323
  //