@naanlang/naan 1.4.3 → 1.5.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.
Files changed (50) hide show
  1. package/LICENSE.md +2 -2
  2. package/README.md +4 -4
  3. package/bin/index.js +22 -3
  4. package/dist/env_web.js +2 -2
  5. package/dist/naan.min.js +6 -6
  6. package/frameworks/browser/browser.nlg +2 -2
  7. package/frameworks/browser/https_request.nlg +39 -10
  8. package/frameworks/browser/sworker.js +23 -23
  9. package/frameworks/browser/terminals.nlg +33 -24
  10. package/frameworks/client/apiclient.nlg +31 -18
  11. package/frameworks/client/client.nlg +22 -2
  12. package/frameworks/client/psm_client.nlg +12 -3
  13. package/frameworks/node/filesystem.nlg +9 -7
  14. package/frameworks/node/gitter.nlg +9 -7
  15. package/frameworks/node/http_request.nlg +103 -0
  16. package/frameworks/node/https_request.nlg +42 -9
  17. package/frameworks/node/node.nlg +1 -0
  18. package/frameworks/node/shell.nlg +4 -4
  19. package/frameworks/node/worker.nlg +1 -0
  20. package/frameworks/project/projects.nlg +2 -1
  21. package/frameworks/running/executors.nlg +26 -7
  22. package/frameworks/running/taskexec.nlg +48 -14
  23. package/frameworks/storage/dbt_pouch.nlg +26 -13
  24. package/frameworks/storage/psm.nlg +39 -5
  25. package/frameworks/storage/psm_dbtables.nlg +10 -2
  26. package/lib/browser/env_web.js +9 -9
  27. package/lib/browser/env_webworker.js +1 -2
  28. package/lib/core/naanlib.js +6 -6
  29. package/lib/env_node.js +9 -3
  30. package/lib/node_repl_init.nlg +2 -2
  31. package/package.json +1 -1
  32. package/plugins/nodeLib/node_util.nlg +14 -7
  33. package/plugins/openSearch/os_dynamo.nlg +5 -5
  34. package/plugins/serviceAws/aws_cloudwatchlogs.nlg +2 -1
  35. package/plugins/serviceAws/aws_dynamo.nlg +26 -8
  36. package/plugins/serviceAws/aws_s3.nlg +3 -3
  37. package/plugins/serviceAws/aws_sqs.nlg +3 -1
  38. package/plugins/serviceAws/aws_utils.nlg +3 -1
  39. package/plugins/serviceAws/aws_vm.nlg +198 -0
  40. package/plugins/serviceAws/dbt_aws.nlg +22 -20
  41. package/plugins/serviceAws/psm_aws.nlg +2 -1
  42. package/plugins/serviceGC/gc_api.nlg +1 -0
  43. package/plugins/serviceNexara/serviceNexara.nlg +32 -0
  44. package/plugins/serviceNexara/speechrec.nlg +160 -0
  45. package/plugins/serviceYC/serviceYC.nlg +2 -3
  46. package/plugins/serviceYC/yc_speechrec.nlg +4 -3
  47. package/plugins/serviceYC/yc_vm.nlg +44 -54
  48. package/test/test_01_core.nlg +42 -33
  49. package/test/test_03_jsinterop.nlg +35 -6
  50. package/plugins/serviceGC/russian_trusted_root_ca_pem.crt +0 -33
@@ -0,0 +1,32 @@
1
+ /*
2
+ * serviceNexara.nlg
3
+ * serviceNexara
4
+ *
5
+ * Nexara speech recognition.
6
+ *
7
+ * column positioning: // // !
8
+ *
9
+ * Copyright (c) 2025 by Richard C. Zulch
10
+ *
11
+ */
12
+
13
+
14
+ /*
15
+ * senexInit
16
+ *
17
+ * Initialize the module.
18
+ *
19
+ */
20
+
21
+ function senexInit(local manifest) {
22
+ manifest = `(senexInit)
23
+ Naan.module.build(module.id, "serviceNexara", function(modobj, compobj) {
24
+ compobj.manifest = manifest
25
+ require("naanlib:frameworks/common").LiveImport()
26
+ nodeLib = require("naanlib:plugins/nodeLib/node_util.nlg")
27
+ if js.g
28
+ https = require("naanlib:frameworks/node/https_request.nlg")
29
+ else
30
+ https = require("naanlib:frameworks/browser/https_request.nlg")
31
+ })
32
+ } ();
@@ -0,0 +1,160 @@
1
+ /*
2
+ * speechrec.nlg
3
+ * Nexara
4
+ *
5
+ * Access to Nexara speech to text recognition.
6
+ *
7
+ * column positioning: // // !
8
+ *
9
+ * Copyright (c) 2025 by Richard C. Zulch
10
+ *
11
+ */
12
+
13
+
14
+ /*
15
+ * NexaraSpeechRec
16
+ *
17
+ * Speech recognition object.
18
+ *
19
+ */
20
+
21
+ closure NexaraSpeechRec(local nxspr) {
22
+ global(https, nodeLib)
23
+ nxspr = new(object, this)
24
+ nxspr.recogURL = "https://api.nexara.ru/api/v1/audio/transcriptions"
25
+
26
+ // login
27
+ //
28
+ // creds:
29
+ // {
30
+ // apiKey: <string> // e.g. nx-xxxxx
31
+ // }
32
+ //
33
+ nxspr.login = closure login(creds) {
34
+ nxspr.creds = creds
35
+ list(false, { ok: true })
36
+ }
37
+
38
+ // recog
39
+ //
40
+ // Recognize text from the specified audio.
41
+ //
42
+ // Available options are:
43
+ // {
44
+ // language: <string> // optional: e.g. "ru-RU", "en-US"
45
+ // model: <string> // optional: e.g. "whisper-1"
46
+ // debug: <boolean> // extra debug logging for https request
47
+ // }
48
+ //
49
+ // This returns an Operation object containing the ID of the asynchronous recognition operation:
50
+ // {
51
+ // done: true // recognition complete
52
+ // createdAt: <date> // when operation started
53
+ // modifiedAt: <date> // last modified
54
+ // response: {
55
+ // "@type": <string> // gRPC type
56
+ // chunks: <array> // array of chunks
57
+ // }
58
+ // }
59
+ //
60
+ nxspr.recog = closure recog(audioBlob, options, local start, params, form, error, data) {
61
+ start = Date()
62
+ params = {
63
+ language: options.language.substring(0,2) || undefined
64
+ model: options.model || "whisper-1"
65
+ response_format: "verbose_json",
66
+ timestamp_granularities: ["word", "segment"]
67
+ file: audioBlob
68
+ }
69
+ form = nodeLib.MultipartBody(params)
70
+ `(error, data) = https.HttpsApiRequest(nxspr.recogURL, {
71
+ method: "POST"
72
+ putdata: form.body
73
+ headers: [
74
+ list("Authorization", "Bearer ".concat(nxspr.creds.apiKey))
75
+ list("Content-Type", form.headers."Content-Type")
76
+ list("Content-Length", form.headers."Content-Length")
77
+ ]
78
+ debug: options.debug || undefined
79
+ })
80
+ if error
81
+ list(Error("NexaraSpeechRec.recog failed:", error))
82
+ else if !(data.segments.length > 0) {
83
+ debuglog("NexaraSpeechRec.recog: no valid segments returned.")
84
+ printline("Nexara response:", Dialect.print(new(data)))
85
+ list(Error("NexaraSpeechRec.recog empty result"))
86
+ } else {
87
+ if options.dump
88
+ printline(Dialect.print(new(data)))
89
+ list(false, {
90
+ done: true
91
+ createdAt: start
92
+ modifiedAt: Date()
93
+ response: nexaraToYCSK(data)
94
+ })
95
+ }
96
+ }
97
+
98
+ // nexaraToYCSK
99
+ //
100
+ // Convert a Nexara response to a Yandex SpeechKit structure. Hey, it was first.
101
+ //
102
+ function nexaraToYCSK(resp,
103
+ local output, wordx, segment, chunk, word, wordet) {
104
+ output = {
105
+ \@type: "type.googleapis.com/yandex.cloud.ai.stt.v2.LongRunningRecognitionResponse"
106
+ chunks: []
107
+ language: resp.language
108
+ }
109
+ wordx = 0
110
+ for segment in resp.segments {
111
+ if segment.text == ""
112
+ continue // empty segment
113
+ chunk = {
114
+ channelTag: "1"
115
+ alternatives: [
116
+ {
117
+ words: []
118
+ text: segment.text
119
+ }
120
+ ]
121
+ }
122
+ for word in segment.text.split(" ") {
123
+ wordet = resp.words[wordx++]
124
+ if wordet.word != word {
125
+ debuglog("nexaraToYCSK: expected ${word} but got ${wordet.word}")
126
+ return (false)
127
+ }
128
+ chunk.alternatives.0.words.push({
129
+ word: wordet.word
130
+ startTime: wordet.start.toFixed(3).concat("s")
131
+ endTime: wordet.end.toFixed(3).concat("s")
132
+ })
133
+ }
134
+ output.chunks.push(chunk)
135
+ }
136
+ output
137
+ }
138
+
139
+ // finis
140
+ nxspr
141
+ }
142
+
143
+
144
+ /*
145
+ * nexaraInit
146
+ *
147
+ * Initialize the module.
148
+ *
149
+ */
150
+
151
+ function nexaraInit(local manifest) {
152
+ manifest = `(NexaraSpeechRec, nexaraInit)
153
+
154
+ Naan.module.build(module.id, "speechrec", function(modobj, compobj) {
155
+ require("./serviceNexara.nlg")
156
+ compobj.manifest = manifest
157
+ modobj.exports.NexaraSpeechRec = NexaraSpeechRec
158
+ })
159
+
160
+ } ();
@@ -50,9 +50,8 @@ function seycInit(local manifest) {
50
50
  Naan.module.build(module.id, "serviceYC", function(modobj, compobj) {
51
51
  compobj.manifest = manifest
52
52
  require("naanlib:frameworks/common").LiveImport()
53
- if js.g {
54
- nodeHttp = js.r("http")
55
- https = require("naanlib:frameworks/node/https_request.nlg") }
53
+ if js.g
54
+ https = require("naanlib:frameworks/node/https_request.nlg")
56
55
  else
57
56
  https = require("naanlib:frameworks/browser/https_request.nlg")
58
57
  module.exports.YcRequest = YcRequest
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * column positioning: // // !
8
8
  *
9
- * Copyright (c) 2024 by Richard C. Zulch
9
+ * Copyright (c) 2024-2025 by Richard C. Zulch
10
10
  *
11
11
  */
12
12
 
@@ -18,7 +18,8 @@
18
18
  *
19
19
  */
20
20
 
21
- closure YCSpeechRec(options, local ycspr) {
21
+ closure YCSpeechRec(local ycspr) {
22
+ global(https)
22
23
  ycspr = new(object, this)
23
24
  ycspr.recogURL = "https://transcribe.api.cloud.yandex.net/speech/stt/v2/longRunningRecognize"
24
25
  ycspr.checkURL = "https://operation.api.cloud.yandex.net/operations/"
@@ -55,7 +56,7 @@ closure YCSpeechRec(options, local ycspr) {
55
56
  // }
56
57
  // }
57
58
  //
58
- ycspr.startRecog = closure startRecog(audioURI, options, local error, data) {
59
+ ycspr.startRecog = closure startRecog(audioURI, options, local params, error, data) {
59
60
  params = {
60
61
  config: {
61
62
  specification: {
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * column positioning: // // !
8
8
  *
9
- * Copyright (c) 2024 by Richard C. Zulch
9
+ * Copyright (c) 2024-2025 by Richard C. Zulch
10
10
  *
11
11
  */
12
12
 
@@ -22,61 +22,13 @@ vmState = false;
22
22
  */
23
23
 
24
24
  closure VmControl(local ycvm) {
25
- global(js, https, nodeHttp, vmState)
25
+ global(js, https, http, vmState)
26
26
  if vmState.ycvm
27
27
  return (vmState.ycvm) // singleton
28
28
  ycvm = new(object, this)
29
29
  vmState = new(nonce) // don't save with state
30
30
  vmState.ycvm = ycvm
31
31
 
32
- // httpLocalRequest
33
- //
34
- // Make a simple GET request for the Docker's 169.254.169.254 metadata interface. This is not
35
- // supported by the standard NaaN framework HttpsApiRequest because it is HTTP and returns JSON
36
- // without bothering to set a contentType advising this.
37
- //
38
-
39
- closure httpLocalRequest(url, params, local pending, chunks, request) {
40
- pending = new(nonce)
41
- chunks = []
42
- request = nodeHttp.request(url, params, closure (response) {
43
- response.on("data", function (chunk) {
44
- chunks.push(chunk)
45
- })
46
- response.on("end", function (local content, error) {
47
- content = js.g.Buffer.concat(chunks)
48
- if response.statusCode < 200 || response.statusCode >= 299 { // 299 is "cancelled"
49
- debuglog("httpRequest status:", url, response.statusCode, "message:", content)
50
- pending.signal(list(Error("httpRequest statusCode:", url, response.statusCode, {
51
- status: response.statusCode
52
- }), false, { status: response.statusCode }))
53
- return
54
- }
55
- if response.complete {
56
- try {
57
- content = js.g.JSON.parse(content.toString())
58
- } catch {
59
- error = Error("httpRequest decode:", url, exception)
60
- }
61
- }
62
- else
63
- error = Error("httpRequest terminated prematurely:", url, response.statusCode)
64
- pending.signal(list(error, content, {
65
- headers: response.headers
66
- status: response.statusCode
67
- contentType: response.headers["content-type"]
68
- }))
69
- })
70
- })
71
- request.on("error", function hre(error) {
72
- debuglog("httpRequest error:", url, "error:", error)
73
- error = Error("httpRequest failed:", url, error)
74
- pending.signal(list(error))
75
- })
76
- request.end()
77
- pending.wait()
78
- }
79
-
80
32
  // getIdentityDoc
81
33
  //
82
34
  // Ask the VM for its identity document. This information does not change.
@@ -89,10 +41,11 @@ closure VmControl(local ycvm) {
89
41
  closure getIdentityDoc(local error, idDoc) {
90
42
  if ycvm.idDoc
91
43
  return (list(false, ycvm.idDoc))
92
- `(error, idDoc) = httpLocalRequest("http://169.254.169.254/computeMetadata/v1/instance/vendor/identity/document", {
44
+ `(error, idDoc) = http.HttpRequest("http://169.254.169.254/computeMetadata/v1/instance/vendor/identity/document", {
93
45
  headers: {
94
46
  "Metadata-Flavor": "Google"
95
47
  }
48
+ json: true
96
49
  })
97
50
  if error
98
51
  list(error)
@@ -116,10 +69,11 @@ closure VmControl(local ycvm) {
116
69
  now = milliseconds()
117
70
  if ycvm.authDoc && ycvm.authDocExpire > milliseconds()
118
71
  return (list(false, ycvm.authDoc)) // use cached credential
119
- `(error, authDoc) = httpLocalRequest("http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/token", {
72
+ `(error, authDoc) = http.HttpRequest("http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/token", {
120
73
  headers: {
121
74
  "Metadata-Flavor": "Google"
122
75
  }
76
+ json: true
123
77
  })
124
78
  if authDoc {
125
79
  expireTime = now + (authDoc.expires_in - 3600) * 1000 // give us 1 hour of margin
@@ -135,7 +89,7 @@ closure VmControl(local ycvm) {
135
89
  else
136
90
  list(false, authDoc)
137
91
  }
138
-
92
+
139
93
  // authHeader
140
94
  //
141
95
  // Return authorization header for the VM to be used in API requests to YC, as a result tuple.
@@ -148,7 +102,42 @@ closure VmControl(local ycvm) {
148
102
  else
149
103
  list(false, list(`Authorization, "${authDoc.token_type} ${authDoc.access_token}"))
150
104
  }
151
-
105
+
106
+ // getHostname
107
+ //
108
+ // Ask the VM for its FQDN.
109
+ //
110
+ // curl -H Metadata-Flavor:Google 169.254.169.254/computeMetadata/v1/instance/hostname
111
+ // tutorbe-1.ru-central1.internal
112
+ //
113
+ ycvm.getHostname = closure getHostname(local error, hostname) {
114
+ `(error, hostname) = http.HttpRequest("http://169.254.169.254/computeMetadata/v1/instance/hostname", {
115
+ headers: [ list("Metadata-Flavor", "Google") ]
116
+ })
117
+ if error
118
+ list(error)
119
+ else
120
+ list(false, hostname)
121
+ }
122
+
123
+ // getUserData
124
+ //
125
+ // Ask the VM for its user-data configuration.
126
+ //
127
+ // curl -H Metadata-Flavor:Google 169.254.169.254/computeMetadata/v1/instance/hostname
128
+ // #cloud-config
129
+ // <...yaml...>
130
+ //
131
+ ycvm.getUserData = closure getUserData(local error, userData) {
132
+ `(error, userData) = http.HttpRequest("http://169.254.169.254/latest/user-data", {
133
+ headers: [ list("Metadata-Flavor", "Google") ]
134
+ })
135
+ if error
136
+ list(error)
137
+ else
138
+ list(false, userData)
139
+ }
140
+
152
141
  // shutdownVM
153
142
  //
154
143
  // Tell YC to shutdown our VM.
@@ -202,6 +191,7 @@ function ycvmInit(local manifest) {
202
191
  Naan.module.build(module.id, "yc_vm", function(modobj, compobj) {
203
192
  require("./serviceYC.nlg")
204
193
  compobj.manifest = manifest
194
+ http = require("naanlib:frameworks/node/http_request.nlg")
205
195
  module.exports.VmControl = VmControl
206
196
  })
207
197
  } ();
@@ -558,42 +558,11 @@ RegisterTestCategory("core", [
558
558
  [ "function testret x { loop { loop { return(77) } } };", "testret" ],
559
559
  [ "testret();", "77" ],
560
560
 
561
-
562
561
  //
563
- // objects
562
+ // dictionaries
564
563
  //
565
-
566
- [ "new();", "false" ],
567
- [ "testo=new(object);", "Object{0}" ],
568
- [ "testo.a=3;", "3" ],
569
- [ "testo;", "Object{1}" ],
570
- [ "testo.b=4;", "4" ],
571
- [ "b=a;", "a" ],
572
- [ "testo.b;", "4" ],
573
- [ "testo;", "Object{2}" ],
574
- [ "totuple(testo);>", "((\"a\" . 3) (\"b\" . 4))" ],
575
- [ "testo1=new(testo);", "Object{2}" ],
576
- [ "totuple(testo1);>", "((\"a\" . 3) (\"b\" . 4))" ],
577
- [ "testo1.b=5;", "5" ],
578
- [ "testo.b;", "4" ],
579
- [ "testo.\"b\";", "4" ],
580
- [ "xget(testo,\"b\");", "4" ],
581
- [ "xget(testo,b);", "3" ], // b has value `a, and test.a is 3
582
- [ "testo.f = function(x) { x+1 };>", "(function lambda (x) (+ x 1))" ],
583
- [ "testo.f(5);", "6" ],
584
- [ "apply(deref(testo, \"f\"), list(6));", "7" ],
585
-
586
- [ "testo.length = 5;", "5" ],
587
- [ "testo.length;", "5" ],
588
- [ "length(testo);", "4" ],
589
- [ "xget(testo, length);", "5" ],
590
- [ "xkeys(testo);>", "(\"a\" \"b\" \"f\" \"length\")" ],
591
- [ "totuple(testo);>", "((\"a\" . 3) (\"b\" . 4) (\"f\" function lambda (x) (+ x 1)) (\"length\" . 5))" ],
592
564
 
593
- [ "testo1.4=c;", "c" ],
594
- [ "testo1.4;", "c" ],
595
-
596
- [ "this = true;", { errors: "can't modify NaanCore::this from Test-core" } ],
565
+ [ "b=a;", "a" ], // testing eval here
597
566
 
598
567
  [ "joe={};", "{ }" ],
599
568
  [ "joe.sam=function(x) { print(x, \"|\", keys(joe)) };>", "(function lambda (x) (print x \"|\" (keys joe)))" ],
@@ -637,6 +606,46 @@ RegisterTestCategory("core", [
637
606
  ["merge(oo, list(1.0/3), {false:c});", "{ true: a, false: c }"],
638
607
  ["oo;", "{ true: a, 0.3333333333333333: b }"],
639
608
 
609
+ ["joe={a:3}; {b:joe} == {b:joe}", "{ a: 3 }|true"],
610
+ ["joe === joe", "true"],
611
+ ["{b:joe} === {b:joe}", "false"],
612
+
613
+
614
+ //
615
+ // objects
616
+ //
617
+
618
+ [ "new();", "false" ],
619
+ [ "testo=new(object);", "Object{0}" ],
620
+ [ "testo.a=3;", "3" ],
621
+ [ "testo;", "Object{1}" ],
622
+ [ "testo.b=4;", "4" ],
623
+ [ "testo.b;", "4" ],
624
+ [ "testo;", "Object{2}" ],
625
+ [ "totuple(testo);>", "((\"a\" . 3) (\"b\" . 4))" ],
626
+ [ "testo1=new(testo);", "Object{2}" ],
627
+ [ "totuple(testo1);>", "((\"a\" . 3) (\"b\" . 4))" ],
628
+ [ "testo1.b=5;", "5" ],
629
+ [ "testo.b;", "4" ],
630
+ [ "testo.\"b\";", "4" ],
631
+ [ "xget(testo,\"b\");", "4" ],
632
+ [ "xget(testo,b);", "3" ], // b has value `a, and test.a is 3
633
+ [ "testo.f = function(x) { x+1 };>", "(function lambda (x) (+ x 1))" ],
634
+ [ "testo.f(5);", "6" ],
635
+ [ "apply(deref(testo, \"f\"), list(6));", "7" ],
636
+
637
+ [ "testo.length = 5;", "5" ],
638
+ [ "testo.length;", "5" ],
639
+ [ "length(testo);", "4" ],
640
+ [ "xget(testo, length);", "5" ],
641
+ [ "xkeys(testo);>", "(\"a\" \"b\" \"f\" \"length\")" ],
642
+ [ "totuple(testo);>", "((\"a\" . 3) (\"b\" . 4) (\"f\" function lambda (x) (+ x 1)) (\"length\" . 5))" ],
643
+
644
+ [ "testo1.4=c;", "c" ],
645
+ [ "testo1.4;", "c" ],
646
+
647
+ [ "this = true;", { errors: "can't modify NaanCore::this from Test-core" } ],
648
+
640
649
  //
641
650
  // object getters/setters
642
651
  //
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * column positioning: // // !
8
8
  *
9
- * Copyright (c) 2020-2022 by Richard C. Zulch
9
+ * Copyright (c) 2020-2025 by Richard C. Zulch
10
10
  *
11
11
  */
12
12
 
@@ -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: r[3].apply is not a function" }
38
+ "prod": { errors: "xcall \"[object Object]\": TypeError: e[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: r[3].apply is not a function" }
42
+ "prod": { errors: "xcall \"[object Object]\": TypeError: e[3].apply is not a function" }
43
43
  }[ "prod"]],
44
44
  [ "Number(3);", "[Number 3]" ],
45
45
  [ "Number(3).toString();", "\"3\"" ],
@@ -135,8 +135,8 @@ RegisterTestCategory("JSinterop", [
135
135
  " print(\"in-\", x, \"-\", nthl.0)\n"
136
136
  " } }" ],
137
137
 
138
- [ "Array(0,1,2,3,4,5,6,7,8,9,10).filter(function&(nth) { stacktrap(nth,function&() { print(\"in-\",nth, \" \") }) });",
139
- { text: "in-0 in-1 in-nth in-nth in-nth in-nth in-nth in-nth in-nth in-nth in-nth" } ],
138
+ [ "Array(0,1,2,3,4,5,6,7,8,9,10).filter(closure(nth) { stacktrap(nth,function() { print('in-', nth, space) }) });",
139
+ { text: "in-0 in-1 in-2 in-3 in-4 in-5 in-6 in-7 in-8 in-9 in-10" } ],
140
140
 
141
141
  [ "loop { stacktrap(0,function&(x) { print(\"loop-\",x) }), break };", "loop-Dictionary{0}|{ }" ],
142
142
  [ "loop { stacktrap(1,function&(x) { print(\"loop-\",x) }), break };", "loop-Dictionary{0}|{ }" ],
@@ -158,6 +158,35 @@ RegisterTestCategory("JSinterop", [
158
158
  " } }" ],
159
159
 
160
160
  [ "function&(top,local nth){ nth=0, loop { print(nth), closure(nth) { stacktrap(nth,function&(x) { print(\" loop-\",x,\"-\",nth) }) }(nth), if nth >= top break else nth = nth+1 }, }(9);",
161
- "0 loop-Dictionary{0}-01 loop-Dictionary{0}-123456789 loop-true-2 loop-true-3 loop-true-4 loop-true-5 loop-true-6 loop-true-7 loop-true-8 loop-true-9|true" ]
161
+ "0 loop-Dictionary{0}-01 loop-Dictionary{0}-123456789 loop-true-2 loop-true-3 loop-true-4 loop-true-5 loop-true-6 loop-true-7 loop-true-8 loop-true-9|true" ],
162
+
163
+ //
164
+ // Callbacks
165
+ //
166
+
167
+ [ "function mycb() { mynon.wait(), 15 };", "mycb"],
168
+ [ "oocb=xnew()", "[object Object]" ],
169
+ [ "oocb.mycb=mycb.proc;>", "(function mycb false (`. mynon wait false) 15)" ],
170
+ [ "oocb.mycb();", "15" ],
171
+ [ "mynon = new(nonce);", "Nonce{3}" ],
172
+ [ "oocb.mycb();", "true" ],
173
+
174
+ [ "@\\.suspend = 7", "7" ],
175
+ [ "function mycb() { mynon.wait(), 16 };", "==> redefined: mycb|mycb"],
176
+ [ "oocb.mycb=mycb.proc;>", "(function mycb false (`. mynon wait false) 16)" ],
177
+ [ "oocb.mycb();", "7" ],
178
+
179
+ //
180
+ // JavaScript compiled functions
181
+ //
182
+
183
+ [ "putproc(`arbyarb, 'return(_lib.pl(narg,nthf));');", "[Compiled arbyarb]" ],
184
+ [ "arbyarb(3,5,2,4);", "3524|4" ],
185
+
186
+ [ "putproc(`bobbuiltin, 'builtin: return(_lib.j2n(_lib.n2j(cx)+_lib.n2j(cy)+_lib.n2j(cz)));');", "[Compiled bobbuiltin]" ],
187
+ [ "bobbuiltin(1,2,3,4);", "6" ],
188
+
189
+ [ "putproc(`joemacro, 'macro: _lib.idle();return(_lib.j2n(_lib.n2j(args[1])(_lib.n2j(args[2][1]))));');", "[Compiled joemacro]" ],
190
+ [ "joemacro(function(x){x+1}, 8);", "9" ]
162
191
 
163
192
  ]);
@@ -1,33 +0,0 @@
1
- -----BEGIN CERTIFICATE-----
2
- MIIFwjCCA6qgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwcDELMAkGA1UEBhMCUlUx
3
- PzA9BgNVBAoMNlRoZSBNaW5pc3RyeSBvZiBEaWdpdGFsIERldmVsb3BtZW50IGFu
4
- ZCBDb21tdW5pY2F0aW9uczEgMB4GA1UEAwwXUnVzc2lhbiBUcnVzdGVkIFJvb3Qg
5
- Q0EwHhcNMjIwMzAxMjEwNDE1WhcNMzIwMjI3MjEwNDE1WjBwMQswCQYDVQQGEwJS
6
- VTE/MD0GA1UECgw2VGhlIE1pbmlzdHJ5IG9mIERpZ2l0YWwgRGV2ZWxvcG1lbnQg
7
- YW5kIENvbW11bmljYXRpb25zMSAwHgYDVQQDDBdSdXNzaWFuIFRydXN0ZWQgUm9v
8
- dCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMfFOZ8pUAL3+r2n
9
- qqE0Zp52selXsKGFYoG0GM5bwz1bSFtCt+AZQMhkWQheI3poZAToYJu69pHLKS6Q
10
- XBiwBC1cvzYmUYKMYZC7jE5YhEU2bSL0mX7NaMxMDmH2/NwuOVRj8OImVa5s1F4U
11
- zn4Kv3PFlDBjjSjXKVY9kmjUBsXQrIHeaqmUIsPIlNWUnimXS0I0abExqkbdrXbX
12
- YwCOXhOO2pDUx3ckmJlCMUGacUTnylyQW2VsJIyIGA8V0xzdaeUXg0VZ6ZmNUr5Y
13
- Ber/EAOLPb8NYpsAhJe2mXjMB/J9HNsoFMBFJ0lLOT/+dQvjbdRZoOT8eqJpWnVD
14
- U+QL/qEZnz57N88OWM3rabJkRNdU/Z7x5SFIM9FrqtN8xewsiBWBI0K6XFuOBOTD
15
- 4V08o4TzJ8+Ccq5XlCUW2L48pZNCYuBDfBh7FxkB7qDgGDiaftEkZZfApRg2E+M9
16
- G8wkNKTPLDc4wH0FDTijhgxR3Y4PiS1HL2Zhw7bD3CbslmEGgfnnZojNkJtcLeBH
17
- BLa52/dSwNU4WWLubaYSiAmA9IUMX1/RpfpxOxd4Ykmhz97oFbUaDJFipIggx5sX
18
- ePAlkTdWnv+RWBxlJwMQ25oEHmRguNYf4Zr/Rxr9cS93Y+mdXIZaBEE0KS2iLRqa
19
- OiWBki9IMQU4phqPOBAaG7A+eP8PAgMBAAGjZjBkMB0GA1UdDgQWBBTh0YHlzlpf
20
- BKrS6badZrHF+qwshzAfBgNVHSMEGDAWgBTh0YHlzlpfBKrS6badZrHF+qwshzAS
21
- BgNVHRMBAf8ECDAGAQH/AgEEMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsF
22
- AAOCAgEAALIY1wkilt/urfEVM5vKzr6utOeDWCUczmWX/RX4ljpRdgF+5fAIS4vH
23
- tmXkqpSCOVeWUrJV9QvZn6L227ZwuE15cWi8DCDal3Ue90WgAJJZMfTshN4OI8cq
24
- W9E4EG9wglbEtMnObHlms8F3CHmrw3k6KmUkWGoa+/ENmcVl68u/cMRl1JbW2bM+
25
- /3A+SAg2c6iPDlehczKx2oa95QW0SkPPWGuNA/CE8CpyANIhu9XFrj3RQ3EqeRcS
26
- AQQod1RNuHpfETLU/A2gMmvn/w/sx7TB3W5BPs6rprOA37tutPq9u6FTZOcG1Oqj
27
- C/B7yTqgI7rbyvox7DEXoX7rIiEqyNNUguTk/u3SZ4VXE2kmxdmSh3TQvybfbnXV
28
- 4JbCZVaqiZraqc7oZMnRoWrXRG3ztbnbes/9qhRGI7PqXqeKJBztxRTEVj8ONs1d
29
- WN5szTwaPIvhkhO3CO5ErU2rVdUr89wKpNXbBODFKRtgxUT70YpmJ46VVaqdAhOZ
30
- D9EUUn4YaeLaS8AjSF/h7UkjOibNc4qVDiPP+rkehFWM66PVnP1Msh93tc+taIfC
31
- EYVMxjh8zNbFuoc7fzvvrFILLe7ifvEIUqSVIC/AzplM/Jxw7buXFeGP1qVCBEHq
32
- 391d/9RAfaZ12zkwFsl+IKwE/OZxW8AHa9i1p4GO0YSNuczzEm4=
33
- -----END CERTIFICATE-----