@naanlang/naan 1.0.6 → 1.0.8
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/LICENSE.md +3 -4
- package/README.md +20 -2
- package/bin/index.js +2 -2
- package/dist/env_web.js +91 -18
- package/dist/naan.min.js +7 -7
- package/frameworks/browser/browser.nlg +92 -3
- package/frameworks/browser/https_request.nlg +2 -2
- package/frameworks/browser/sworker.js +17 -17
- package/frameworks/browser/terminals.nlg +14 -12
- package/frameworks/browser/workers.nlg +13 -6
- package/frameworks/client/psm_client.nlg +1 -0
- package/frameworks/common/common.nlg +39 -8
- package/frameworks/node/filesystem.nlg +32 -26
- package/frameworks/node/gitter.nlg +9 -4
- package/frameworks/node/https_request.nlg +19 -14
- package/frameworks/project/build.nlg +187 -74
- package/frameworks/project/projects.nlg +42 -17
- package/frameworks/running/running.nlg +33 -11
- package/frameworks/running/taskexec.nlg +1 -1
- package/frameworks/storage/file_manager.nlg +2 -1
- package/frameworks/storage/psm.nlg +20 -4
- package/frameworks/storage/psm_dbtables.nlg +13 -3
- package/lib/browser/env_web.js +93 -20
- package/lib/browser/env_webworker.js +10 -0
- package/lib/core/naanlib.js +7 -7
- package/package.json +2 -1
- package/plugins/serviceAws/aws/aws-sdk-node.min.js +1 -1
- package/plugins/serviceAws/aws/aws-sdk.min.js +1 -1
- package/plugins/serviceAws/aws/lambda_ws_init.nlg +2 -1
- package/plugins/serviceAws/aws_dynamo.nlg +45 -26
- package/plugins/serviceAws/aws_dynextra.nlg +157 -32
- package/plugins/serviceAws/aws_lambda.nlg +4 -8
- package/plugins/serviceAws/aws_s3.nlg +74 -28
- package/plugins/serviceAws/aws_sqs.nlg +113 -0
- package/plugins/serviceAws/dbt_aws.nlg +73 -24
- package/plugins/serviceAws/psm_aws.nlg +30 -11
- package/plugins/serviceAws/serviceAws.nlg +2 -4
- package/plugins/serviceYC/generic_api.yaml +36 -0
- package/plugins/serviceYC/naanide-relay-index.js +148 -0
- package/plugins/serviceYC/serviceYC.nlg +59 -0
- package/plugins/serviceYC/yc_function.nlg +161 -0
- package/test/test_01_core.nlg +2 -2
- package/test/test_02_context.nlg +2 -2
- package/test/test_05_strings.nlg +11 -1
- package/test/test_06_lingoparse.nlg +43 -14
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* index.js
|
|
3
|
+
* naanide-relay
|
|
4
|
+
*
|
|
5
|
+
* Yandex Cloud Function for proxying API requests from NaanIDE in the browser.
|
|
6
|
+
*
|
|
7
|
+
* This function acts as a relay (mitm) proxy so that a browser may access Yandex Cloud APIs without
|
|
8
|
+
* needing a transient access token and without CORS problems. Security is provided by a pre-shared
|
|
9
|
+
* static key, configured as an environment variable in the YCF and known to the browser code.
|
|
10
|
+
*
|
|
11
|
+
* The request to the proxy should be precisely what is intended for the target API, except that the
|
|
12
|
+
* following headers should be included:
|
|
13
|
+
*
|
|
14
|
+
* x-naanide-auth: <pre-shared-key>
|
|
15
|
+
* x-naanide-ep: <target-url-with-path-and-query-variables>
|
|
16
|
+
* x-naanide-hdrs: <optional headers>
|
|
17
|
+
*
|
|
18
|
+
* The response in the browser contains some remapped headers, but is otherwise the API's response.
|
|
19
|
+
*
|
|
20
|
+
* Assuming the pre-shared key is "foo", the following gets a list of YDB databases in a folder:
|
|
21
|
+
|
|
22
|
+
curl -H "x-naanide-auth: foo" \
|
|
23
|
+
-H "x-naanide-ep: https://ydb.api.cloud.yandex.net/ydb/v1/databases?folderId=b1gjjahqm5u9tjolom9l" \
|
|
24
|
+
'https://functions.yandexcloud.net/d4e5gicpajcqdbj91jt8'
|
|
25
|
+
|
|
26
|
+
*
|
|
27
|
+
* column positioning: // // !
|
|
28
|
+
*
|
|
29
|
+
* Copyright (c) 2022 by Richard C. Zulch
|
|
30
|
+
*
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
/*
|
|
35
|
+
* index.handler
|
|
36
|
+
*
|
|
37
|
+
* Forward a request and respond with the result.
|
|
38
|
+
*
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
module.exports.handler = async function (event, context) {
|
|
42
|
+
"use strict";
|
|
43
|
+
const authorizationKey = process.env.NAANIDE_AUTH_KEY;
|
|
44
|
+
|
|
45
|
+
//
|
|
46
|
+
// OPTIONS request
|
|
47
|
+
//
|
|
48
|
+
// Allow our headers for CORS
|
|
49
|
+
//
|
|
50
|
+
|
|
51
|
+
if (event.httpMethod == "OPTIONS")
|
|
52
|
+
return {
|
|
53
|
+
statusCode: 200,
|
|
54
|
+
headers: {
|
|
55
|
+
"Access-Control-Allow-Headers": "*"
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
//
|
|
60
|
+
// Failed authentication
|
|
61
|
+
//
|
|
62
|
+
|
|
63
|
+
if (event.headers["X-Naanide-Auth"] != authorizationKey) {
|
|
64
|
+
return {
|
|
65
|
+
statusCode: 401
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
//
|
|
70
|
+
// Authenticated
|
|
71
|
+
//
|
|
72
|
+
|
|
73
|
+
let https = require("https");
|
|
74
|
+
let url = event.headers["X-Naanide-Ep"];
|
|
75
|
+
let headers = event.headers["X-Naanide-Hdrs"];
|
|
76
|
+
if (headers) {
|
|
77
|
+
try {
|
|
78
|
+
headers = JSON.parse(headers);
|
|
79
|
+
} catch (e) {
|
|
80
|
+
console.log("X-Naanide-Hdrs parse error", e);
|
|
81
|
+
headers = false;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
if (!headers)
|
|
85
|
+
headers = { };
|
|
86
|
+
if (!headers[Object.keys(headers).find(key => key.toLowerCase() === "authorization")])
|
|
87
|
+
headers.Authorization = context.token.token_type.concat(" ", context.token.access_token);
|
|
88
|
+
let options = {
|
|
89
|
+
method: event.httpMethod,
|
|
90
|
+
headers: headers
|
|
91
|
+
};
|
|
92
|
+
let chunks = [];
|
|
93
|
+
let content = false;
|
|
94
|
+
let finalResponse = {
|
|
95
|
+
statusCode: 500
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
//
|
|
99
|
+
// forward_request
|
|
100
|
+
//
|
|
101
|
+
async function forward_request() {
|
|
102
|
+
return new Promise(function(resolve, reject) {
|
|
103
|
+
let req = https.request(url, options, function(resp) {
|
|
104
|
+
finalResponse.statusCode = resp.statusCode;
|
|
105
|
+
// data event
|
|
106
|
+
resp.on("data", function (chunk) {
|
|
107
|
+
chunks.push(chunk);
|
|
108
|
+
});
|
|
109
|
+
// end event
|
|
110
|
+
resp.on("end", function () {
|
|
111
|
+
if (resp.complete)
|
|
112
|
+
{
|
|
113
|
+
let content = Buffer.concat(chunks);
|
|
114
|
+
let contentType = resp.headers["content-type"];
|
|
115
|
+
finalResponse.body = content.toString();
|
|
116
|
+
if (resp.headers["x-request-id"]) {
|
|
117
|
+
resp.headers["x-upstream-request-id"] = resp.headers["x-request-id"];
|
|
118
|
+
delete resp.headers["x-request-id"];
|
|
119
|
+
}
|
|
120
|
+
finalResponse.headers = resp.headers;
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
console.log("request terminated before full response received");
|
|
124
|
+
finalResponse.statusCode = 502;
|
|
125
|
+
}
|
|
126
|
+
resolve();
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
// error event
|
|
130
|
+
req.on("error", function (err) {
|
|
131
|
+
console.log("upstream request error:", err);
|
|
132
|
+
finalResponse.statusCode = 502;
|
|
133
|
+
reject(err);
|
|
134
|
+
});
|
|
135
|
+
console.log("event.body", event.body, event.isBase64Encoded);
|
|
136
|
+
if (event.body) {
|
|
137
|
+
if (event.isBase64Encoded)
|
|
138
|
+
req.write(decodeURIComponent(escape(atob(event.body))));
|
|
139
|
+
else
|
|
140
|
+
req.write(event.body);
|
|
141
|
+
}
|
|
142
|
+
req.end();
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
await forward_request();
|
|
147
|
+
return finalResponse;
|
|
148
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* serviceYC.nlg
|
|
3
|
+
* serviceYC
|
|
4
|
+
*
|
|
5
|
+
* ServiceYC module configuration and management for browser and NodeJS.
|
|
6
|
+
*
|
|
7
|
+
* column positioning: // // !
|
|
8
|
+
*
|
|
9
|
+
* Copyright (c) 2022 by Richard C. Zulch
|
|
10
|
+
*
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
/*
|
|
15
|
+
* YcRequest
|
|
16
|
+
*
|
|
17
|
+
* Make a request to a Yandex Cloud API using a Yandex Cloud Function acting as a relay proxy.
|
|
18
|
+
* This works like frameworks/browser/HttpsApiRequest and frameworks/node/HttpsApiRequest except
|
|
19
|
+
* that it takes a credentials argument for access to the relay. Technically this is not required
|
|
20
|
+
* when we are going from node, but I'm keeping it for consistency.
|
|
21
|
+
*
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
closure YcRequest(url, creds, options, local headers) {
|
|
25
|
+
if !options
|
|
26
|
+
options = { }
|
|
27
|
+
else
|
|
28
|
+
options = new(options)
|
|
29
|
+
headers = [
|
|
30
|
+
list("x-naanide-auth", creds.relayAuthKey)
|
|
31
|
+
list("x-naanide-ep", url)
|
|
32
|
+
]
|
|
33
|
+
if options.headers
|
|
34
|
+
headers.push(list("x-naanide-hdrs", options.headers.join("\n")))
|
|
35
|
+
options.headers = headers
|
|
36
|
+
https.HttpsApiRequest(creds.relayURL, options)
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
/*
|
|
41
|
+
* seycInit
|
|
42
|
+
*
|
|
43
|
+
* Initialize the YC module.
|
|
44
|
+
*
|
|
45
|
+
*/
|
|
46
|
+
|
|
47
|
+
function seycInit(local manifest) {
|
|
48
|
+
manifest = `(YcRequest, seycInit)
|
|
49
|
+
|
|
50
|
+
Naan.module.build(module.id, "serviceYC", function(modobj, compobj) {
|
|
51
|
+
compobj.manifest = manifest
|
|
52
|
+
require("frameworks/common").LiveImport()
|
|
53
|
+
if js.g
|
|
54
|
+
https = require("frameworks/node/https_request.nlg")
|
|
55
|
+
else
|
|
56
|
+
https = require("frameworks/browser/https_request.nlg")
|
|
57
|
+
module.exports.YcRequest = YcRequest
|
|
58
|
+
})
|
|
59
|
+
} ();
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* yc_function.nlg
|
|
3
|
+
* serviceYC
|
|
4
|
+
*
|
|
5
|
+
* Access to serverless function management services for YC.
|
|
6
|
+
*
|
|
7
|
+
* column positioning: // // !
|
|
8
|
+
*
|
|
9
|
+
* Copyright (c) 2022 by Richard C. Zulch
|
|
10
|
+
*
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
/*
|
|
15
|
+
* YCFunction
|
|
16
|
+
*
|
|
17
|
+
* YCFunction manipulation functions.
|
|
18
|
+
*
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
closure YCFunction(local ycfun) {
|
|
22
|
+
ycfun = new(object, this)
|
|
23
|
+
|
|
24
|
+
//
|
|
25
|
+
// login
|
|
26
|
+
//
|
|
27
|
+
ycfun.login = closure login(creds) {
|
|
28
|
+
ycfun.creds = creds
|
|
29
|
+
list(false, { ok: true })
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
//
|
|
33
|
+
// getFunction
|
|
34
|
+
//
|
|
35
|
+
// Get basic function info like:
|
|
36
|
+
// {
|
|
37
|
+
// id : "d4er5sq67r5j47nno6nb",
|
|
38
|
+
// folderId : "b1gjjahqm5u9tjolom9l",
|
|
39
|
+
// createdAt : "2022-08-30T00:57:18.024Z",
|
|
40
|
+
// name : "hello-world",
|
|
41
|
+
// description : "A quick first function.",
|
|
42
|
+
// logGroupId : "ckgbmh6df6dt92pphc6b",
|
|
43
|
+
// httpInvokeUrl: "https://functions.yandexcloud.net/d4er5sq67r5j47nno6nb",
|
|
44
|
+
// status : "ACTIVE"
|
|
45
|
+
// }
|
|
46
|
+
//
|
|
47
|
+
ycfun.getFunction = closure getFunction(funcID, local url, error, data) {
|
|
48
|
+
debuglog("YCFunction.getFunction", funcID)
|
|
49
|
+
url = "https://serverless-functions.api.cloud.yandex.net/functions/v1/functions/".concat(funcID)
|
|
50
|
+
`(error, data) = YcRequest(url, ycfun.creds, {
|
|
51
|
+
query: { x: 1 }
|
|
52
|
+
})
|
|
53
|
+
if error
|
|
54
|
+
debuglog("YCFunction.getFunction failed:", ErrorString(error))
|
|
55
|
+
list(error, data)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
//
|
|
59
|
+
// getVersionByTag
|
|
60
|
+
//
|
|
61
|
+
// Get info about a specific function version using its tag, e.g. $latest:
|
|
62
|
+
// {
|
|
63
|
+
// resources : { memory: "134217728" },
|
|
64
|
+
// tags : ["$latest"],
|
|
65
|
+
// id : "d4evm4lsi91hqkv78ane",
|
|
66
|
+
// functionId : "d4er5sq67r5j47nno6nb",
|
|
67
|
+
// createdAt : "2022-08-31T03:32:59.577Z",
|
|
68
|
+
// runtime : "nodejs16",
|
|
69
|
+
// entrypoint : "index.handler",
|
|
70
|
+
// executionTimeout: "3s",
|
|
71
|
+
// serviceAccountId: "aje9s3df60gofsddio0c",
|
|
72
|
+
// imageSize : "4096",
|
|
73
|
+
// status : "ACTIVE",
|
|
74
|
+
// logGroupId : "ckgbmh6df6dt92pphc6b" }
|
|
75
|
+
// }
|
|
76
|
+
//
|
|
77
|
+
ycfun.getVersionByTag = closure getVersionByTag(funcID, tag, local url, error, data) {
|
|
78
|
+
url = "https://serverless-functions.api.cloud.yandex.net/functions/v1/versions:byTag".concat(
|
|
79
|
+
EncodeQuery("?", {
|
|
80
|
+
functionId: funcID
|
|
81
|
+
tag: tag
|
|
82
|
+
}))
|
|
83
|
+
`(error, data) = YcRequest(url, ycfun.creds, {
|
|
84
|
+
query: { x: 1 }
|
|
85
|
+
})
|
|
86
|
+
if error
|
|
87
|
+
debuglog("YCFunction.getVersionByTag failed:", ErrorString(error))
|
|
88
|
+
list(error, data)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
//
|
|
92
|
+
// invokeFunction
|
|
93
|
+
//
|
|
94
|
+
// Invoke a public cloud function. It is impossible to invoke a private cloud function from
|
|
95
|
+
// browsers because the function cannot respond to a CORS authorization request using the OPTIONS
|
|
96
|
+
// method without an authorization header that cannot be added to the preflight.
|
|
97
|
+
//
|
|
98
|
+
ycfun.invokeFunction = closure invokeFunction(funcID, local url, error, data) {
|
|
99
|
+
url = "https://functions.yandexcloud.net/".concat(funcID)
|
|
100
|
+
`(error, data) = https.HttpsApiRequest(url, {
|
|
101
|
+
query: { x: 1 }
|
|
102
|
+
})
|
|
103
|
+
if error
|
|
104
|
+
debuglog("YCFunction.invokeFunction failed:", ErrorString(error))
|
|
105
|
+
list(error, data)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
//
|
|
109
|
+
// updateFunctionCode
|
|
110
|
+
//
|
|
111
|
+
//
|
|
112
|
+
ycfun.updateFunctionCode = closure updateFunctionCode(info, bucket, key, sha256,
|
|
113
|
+
local url, params, error, data) {
|
|
114
|
+
params = {
|
|
115
|
+
function_id: info.functionId
|
|
116
|
+
functionID: info.functionId
|
|
117
|
+
runtime: info.runtime
|
|
118
|
+
description: "Updated: ".concat(Date())
|
|
119
|
+
entrypoint: info.entrypoint
|
|
120
|
+
resources: info.resources
|
|
121
|
+
executionTimeout: info.executionTimeout
|
|
122
|
+
serviceAccountId: info.serviceAccountId
|
|
123
|
+
package: {
|
|
124
|
+
bucketName: bucket
|
|
125
|
+
objectName: key
|
|
126
|
+
sha256: sha256
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
if info.environment
|
|
130
|
+
params.environment = info.environment
|
|
131
|
+
url = "https://serverless-functions.api.cloud.yandex.net/functions/v1/versions"
|
|
132
|
+
`(error, data) = YcRequest(url, ycfun.creds, {
|
|
133
|
+
putdata: params
|
|
134
|
+
})
|
|
135
|
+
if error
|
|
136
|
+
debuglog("YCFunction.updateFunctionCode failed:", ErrorString(error))
|
|
137
|
+
list(error, data)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// finis
|
|
141
|
+
ycfun
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
/*
|
|
146
|
+
* ycFunctionInit
|
|
147
|
+
*
|
|
148
|
+
* Initialize the module.
|
|
149
|
+
*
|
|
150
|
+
*/
|
|
151
|
+
|
|
152
|
+
function ycFunctionInit(local manifest) {
|
|
153
|
+
manifest = `(YCFunction, ycFunctionInit)
|
|
154
|
+
|
|
155
|
+
Naan.module.build(module.id, "yc_function", function(modobj, compobj) {
|
|
156
|
+
require("./serviceYC.nlg")
|
|
157
|
+
compobj.manifest = manifest
|
|
158
|
+
modobj.exports.YCFunction = YCFunction
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
} ();
|
package/test/test_01_core.nlg
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* column positioning: // // !
|
|
8
8
|
*
|
|
9
|
-
* Copyright (c) 2017-
|
|
9
|
+
* Copyright (c) 2017-2023 by Richard C. Zulch
|
|
10
10
|
*
|
|
11
11
|
*/
|
|
12
12
|
|
|
@@ -417,7 +417,7 @@ RegisterTestCategory("core", [
|
|
|
417
417
|
"2 3 4 caught: done|cleanup|\"xo\"" ],
|
|
418
418
|
[ "try { looper() } catch { if false { print(\"caught: \"), printline(exception), \"xo\" } } finally { printline(cleanup) };",
|
|
419
419
|
{ errors: "exception: done", text: "2 3 4 cleanup" } ],
|
|
420
|
-
[ "try { looper() } catch { if false { print(\"caught: \"), printline(exception), \"xo\" } } finally { throw(3)
|
|
420
|
+
[ "try { looper() } catch { if false { print(\"caught: \"), printline(exception), \"xo\" } } finally { throw(3) };",
|
|
421
421
|
{ errors: "exception: 3", text: "2 3 4" } ],
|
|
422
422
|
[ "try { throw(blah) } catch { if true try { throw(needer) } catch { if true exception } };",
|
|
423
423
|
"needer" ],
|
package/test/test_02_context.nlg
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* column positioning: // // !
|
|
8
8
|
*
|
|
9
|
-
* Copyright (c) 2017-
|
|
9
|
+
* Copyright (c) 2017-2023 by Richard C. Zulch
|
|
10
10
|
*
|
|
11
11
|
*/
|
|
12
12
|
|
|
@@ -38,7 +38,7 @@ RegisterTestCategory("context", [
|
|
|
38
38
|
[ "function setNS(ns) { ns=reverse(ns),sudo(nsactive(reverse(ns, defNS))) };", "setNS" ],
|
|
39
39
|
[ "x;", "x" ],
|
|
40
40
|
[ "y;", "y" ],
|
|
41
|
-
[ "length(symlist(nsactive(true).0));", "
|
|
41
|
+
[ "length(symlist(nsactive(true).0));", "173"],
|
|
42
42
|
[ "testvar;", "testvar" ],
|
|
43
43
|
[ "function setTestvar(v) { testvar=v };", "setTestvar" ],
|
|
44
44
|
[ "naanvar;", "naanvar" ],
|
package/test/test_05_strings.nlg
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* column positioning: // // !
|
|
8
8
|
*
|
|
9
|
-
* Copyright (c) 2017-
|
|
9
|
+
* Copyright (c) 2017-2023 by Richard C. Zulch
|
|
10
10
|
*
|
|
11
11
|
*/
|
|
12
12
|
|
|
@@ -47,6 +47,16 @@ RegisterTestCategory("strings", [
|
|
|
47
47
|
[ "c=compress(\"b\", 123);", "b123" ],
|
|
48
48
|
|
|
49
49
|
|
|
50
|
+
//
|
|
51
|
+
// template strings
|
|
52
|
+
//
|
|
53
|
+
|
|
54
|
+
[ "'$${4+3}';", '"7"' ], // these have an extra $escape
|
|
55
|
+
[ "'$$${4+3}';", '"$${4+3}"' ],
|
|
56
|
+
[ "'a$${4+3}b';", '"a7b"' ],
|
|
57
|
+
[ "'$${}';", '' ],
|
|
58
|
+
|
|
59
|
+
|
|
50
60
|
//
|
|
51
61
|
// string native functions
|
|
52
62
|
//
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* column positioning: // // !
|
|
8
8
|
*
|
|
9
|
-
* Copyright (c) 2018-
|
|
9
|
+
* Copyright (c) 2018-2023 by Richard C. Zulch
|
|
10
10
|
*
|
|
11
11
|
*/
|
|
12
12
|
|
|
@@ -63,7 +63,7 @@ RegisterTestCategory("Lingo parse", [
|
|
|
63
63
|
[ "4&&3;", { exprs: "(&& 4 3)" } ],
|
|
64
64
|
[ "4\n&&3;", { exprs: "(&& 4 3)" } ],
|
|
65
65
|
[ "4&&\n3;", { exprs: "(&& 4 3)" } ],
|
|
66
|
-
[ "function test0(x, y) { \n
|
|
66
|
+
[ "function test0(x, y) { \n y = 3\n x \n };", { exprs: "(defproc function test0 (x y) (= y 3) x)" } ],
|
|
67
67
|
|
|
68
68
|
//
|
|
69
69
|
// Procedure types
|
|
@@ -297,8 +297,8 @@ RegisterTestCategory("Lingo parse", [
|
|
|
297
297
|
{ exprs: "(defproc function test2 (x) (loop false ((> x 3) (print \"done\")) (printline \"loop \" x) (= x (+ x 1))))",
|
|
298
298
|
text: "==> redefined: test2" } ],
|
|
299
299
|
|
|
300
|
-
[ "function test2(x) { loop { if x > 3 print(\"done\"), break
|
|
301
|
-
{ exprs: "(defproc function test2 (x) (loop false (((> x 3) (print \"done\"))) (break)
|
|
300
|
+
[ "function test2(x) { loop { if x > 3 print(\"done\"), break } };",
|
|
301
|
+
{ exprs: "(defproc function test2 (x) (loop false (((> x 3) (print \"done\"))) (break)))",
|
|
302
302
|
text: "==> redefined: test2" } ],
|
|
303
303
|
|
|
304
304
|
[ "function test2(x) { loop { if x > 3 if x > 2 { print(\"done\"), break }, printline(\"loop \", x), x = x+1 } };",
|
|
@@ -313,8 +313,8 @@ RegisterTestCategory("Lingo parse", [
|
|
|
313
313
|
{ exprs: "(defproc function test2 (x) (loop false (((> x 3) (((> x 2) (print \"done\"))) (break))) (printline \"loop \" x) (= x (+ x 1))))",
|
|
314
314
|
text: "==> redefined: test2" } ],
|
|
315
315
|
|
|
316
|
-
[ "function test2(x) { loop { if x <= 3 print(\" \") else break
|
|
317
|
-
{ exprs: "(defproc function test2 (x) (loop false (((<= x 3) (print \" \")) (break))
|
|
316
|
+
[ "function test2(x) { loop { if x <= 3 print(\" \") else break } };",
|
|
317
|
+
{ exprs: "(defproc function test2 (x) (loop false (((<= x 3) (print \" \")) (break))))",
|
|
318
318
|
text: "==> redefined: test2" } ],
|
|
319
319
|
|
|
320
320
|
[ "function test2(x) { loop { if x > 3 { if x > 2 { print(\"done\"), break }, print(\" \")} , printline(\"loop \", x), x = x+1 } };",
|
|
@@ -346,12 +346,12 @@ RegisterTestCategory("Lingo parse", [
|
|
|
346
346
|
[ "function(x) { loop { if x 2 else if y 3 } };",
|
|
347
347
|
{ exprs: "(function lambda (x) (loop false (((identity x) 2) ((identity y) 3))))" } ],
|
|
348
348
|
|
|
349
|
-
[ "function() { if 1 { 2, loop { if 3 4 }, 5 } };",
|
|
350
|
-
{ exprs: "(function lambda false ((identity 1) 2 (loop false (((identity 3) 4))) 5))" } ],
|
|
351
|
-
[ "function() { if 1 { 2, loop { if 3 4 }, 5 } };",
|
|
349
|
+
[ "function() { if 1 { f(2), loop { if 3 4 }, 5 } };",
|
|
350
|
+
{ exprs: "(function lambda false ((identity 1) (f 2) (loop false (((identity 3) 4))) 5))" } ],
|
|
351
|
+
[ "function() { if 1 { f(2), loop { if 3 4 }, 5 } };",
|
|
352
352
|
"function () {\n"
|
|
353
353
|
" if 1 {\n"
|
|
354
|
-
" 2\n"
|
|
354
|
+
" (f, 2)\n"
|
|
355
355
|
" loop\n"
|
|
356
356
|
" if 3\n"
|
|
357
357
|
" 4\n"
|
|
@@ -421,11 +421,11 @@ RegisterTestCategory("Lingo parse", [
|
|
|
421
421
|
|
|
422
422
|
[ "return;", { exprs: "(return)" } ],
|
|
423
423
|
[ "return (777);", { exprs: "(return 777)" } ],
|
|
424
|
-
[ "function test4() { loop :dumb { if 1 < 2 { 3, break :dumb }, 4 }};",
|
|
425
|
-
{ exprs: "(defproc function test4 false (loop dumb (((< 1 2) 3 (break dumb))) 4))",
|
|
424
|
+
[ "function test4() { loop :dumb { if 1 < 2 { f(3), break :dumb }, 4 }};",
|
|
425
|
+
{ exprs: "(defproc function test4 false (loop dumb (((< 1 2) (f 3) (break dumb))) 4))",
|
|
426
426
|
text: "==> redefined: test4" } ],
|
|
427
|
-
[ "function test4() { loop :smart { if 1 < 2 { 3, continue :smart }, 4 }};",
|
|
428
|
-
{ exprs: "(defproc function test4 false (loop smart (((< 1 2) 3 (continue smart))) 4))",
|
|
427
|
+
[ "function test4() { loop :smart { if 1 < 2 { f(3), continue :smart }, 4 }};",
|
|
428
|
+
{ exprs: "(defproc function test4 false (loop smart (((< 1 2) (f 3) (continue smart))) 4))",
|
|
429
429
|
text: "==> redefined: test4" } ],
|
|
430
430
|
|
|
431
431
|
//
|
|
@@ -497,4 +497,33 @@ RegisterTestCategory("Lingo parse", [
|
|
|
497
497
|
" else {\n"
|
|
498
498
|
" { a: 3 } }\n" // need braces around dictionary
|
|
499
499
|
"}" ],
|
|
500
|
+
|
|
501
|
+
//
|
|
502
|
+
// semantic validity
|
|
503
|
+
//
|
|
504
|
+
|
|
505
|
+
[ "function(x) { 3, x };",
|
|
506
|
+
{ errors: "ignored atom in body at (1, 15) found 3" } ],
|
|
507
|
+
[ "function(x x) { x * x };",
|
|
508
|
+
{ errors: "duplicate parameter at (1, 12) found x" } ],
|
|
509
|
+
[ "function(3) { 33 };",
|
|
510
|
+
{ errors: "invalid parameter at (1, 11) found 3" } ],
|
|
511
|
+
[ "global(test);",
|
|
512
|
+
{ errors: "global forbidden outside of a procedure at (1, 1)" } ],
|
|
513
|
+
[ "function(a) { b * a };",
|
|
514
|
+
{ text: "" } ],
|
|
515
|
+
[ "function(a) { global(b), b * a };",
|
|
516
|
+
{ text: "" } ],
|
|
517
|
+
[ "function&(a) { global(b), b * a };",
|
|
518
|
+
{ errors: "global forbidden in function& and let& at (1, 16)" } ],
|
|
519
|
+
[ "let&(a) { global(b), b * a };",
|
|
520
|
+
{ errors: "global forbidden in function& and let& at (1, 11)" } ],
|
|
521
|
+
[ "function(a) { global(b), a.c };",
|
|
522
|
+
{ text: "" } ],
|
|
523
|
+
[ "function(a) { global(b), c.a };",
|
|
524
|
+
{ errors: "undeclared global at (1, 26) found c" } ],
|
|
525
|
+
[ "function(a) { global(b), c * a };",
|
|
526
|
+
{ errors: "undeclared global at (1, 26) found c" } ],
|
|
500
527
|
]);
|
|
528
|
+
|
|
529
|
+
|