Skip to content

Commit 8cad1fa

Browse files
authored
feat(sdk): regex.match (#4039)
An implementation to have regex introduced into Winglang Motivation [first step to Add Regex type to String.replace() #3644 ] Solution I have implemented regex match function by using the match in Javascript. It takes in the userString and regexPattern string (example below) and returns a true if they match ; false otherwise. ``` let userString = "peach"; let regexPattern = "p[a-z]+h"; let matches = userString.match(regexPattern); assert(matches == true); ``` It returns a boolean value of true or false. ## Checklist - [ ] Title matches [Winglang's style guide](https://www.winglang.io/contributing/start-here/pull_requests#how-are-pull-request-titles-formatted) - [ ] Description explains motivation and solution - [ ] Tests added (always) - [ ] Docs updated (only required for features) - [ ] Added `pr/e2e-full` label if this feature requires end-to-end testing *By submitting this pull request, I confirm that my contribution is made under the terms of the [Wing Cloud Contribution License](https://github.com/winglang/wing/blob/main/CONTRIBUTION_LICENSE.md)*.
1 parent 33165cf commit 8cad1fa

File tree

7 files changed

+336
-1
lines changed

7 files changed

+336
-1
lines changed
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
bring regex;
2+
3+
let matches1 = regex.match("p[a-z]+ch" , "peach");
4+
let matches2 = regex.match("[0-9]+" , "0923");
5+
let matches3 = regex.match("[0-9]+" , "0a923");
6+
let matches4 = regex.match("^([a-zA-Z0-9_.-]+)@[a-z]+.[a-z]+\$" , "[email protected]");
7+
8+
let matches5 = regex.match("p([a-z]+)ch" , "leach");
9+
let matches6 = regex.match("^([a-zA-Z0-9_.-]+)@[a-z]+.[a-z]+" , "@[email protected]");
10+
11+
let matches7 = regex.match("^Mary" , "Mary had a little lamb");
12+
let matches8 = regex.match("lamb\$" , "Mary had a little lamb");
13+
14+
let matches9 = regex.match("lamb\$" , "Mary had a little hamb");
15+
let matches10 = regex.match("^([a-zA-Z0-9_.-]+)@[a-z]+.[a-z]+\$" , "[email protected]");
16+
17+
assert(matches1 == true );
18+
assert(matches2 == true );
19+
assert(matches3 == true );
20+
assert(matches4 == true );
21+
22+
assert(matches5 == false);
23+
assert(matches6 == false);
24+
25+
assert(matches7 == true );
26+
assert(matches8 == true );
27+
28+
assert(matches9 == false);
29+
assert(matches10 == false);
30+
31+
test "inflight match" {
32+
33+
let matches1 = regex.match("p[a-z]+ch" , "peach");
34+
let matches2 = regex.match("[0-9]+" , "0923");
35+
let matches3 = regex.match("[0-9]+" , "0a923");
36+
let matches4 = regex.match("^([a-zA-Z0-9_.-]+)@[a-z]+.[a-z]+\$" , "[email protected]");
37+
38+
let matches5 = regex.match("p([a-z]+)ch" , "leach");
39+
let matches6 = regex.match("^([a-zA-Z0-9_.-]+)@[a-z]+.[a-z]+" , "@[email protected]");
40+
41+
let matches7 = regex.match("^Mary" , "Mary had a little lamb");
42+
let matches8 = regex.match("lamb\$" , "Mary had a little lamb");
43+
44+
let matches9 = regex.match("lamb\$" , "Mary had a little hamb");
45+
let matches10 = regex.match("^([a-zA-Z0-9_.-]+)@[a-z]+.[a-z]+\$" , "[email protected]");
46+
47+
48+
assert(matches1 == true );
49+
assert(matches2 == true );
50+
assert(matches3 == true );
51+
assert(matches4 == true );
52+
53+
assert(matches5 == false);
54+
assert(matches6 == false);
55+
56+
assert(matches7 == true );
57+
assert(matches8 == true );
58+
59+
assert(matches9 == false);
60+
assert(matches10 == false);
61+
62+
}

libs/wingc/src/lib.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,14 +71,16 @@ const WINGSDK_HTTP_MODULE: &'static str = "http";
7171
const WINGSDK_MATH_MODULE: &'static str = "math";
7272
const WINGSDK_AWS_MODULE: &'static str = "aws";
7373
const WINGSDK_EX_MODULE: &'static str = "ex";
74+
const WINGSDK_REGEX_MODULE: &'static str = "regex";
7475

75-
const WINGSDK_BRINGABLE_MODULES: [&'static str; 6] = [
76+
const WINGSDK_BRINGABLE_MODULES: [&'static str; 7] = [
7677
WINGSDK_CLOUD_MODULE,
7778
WINGSDK_UTIL_MODULE,
7879
WINGSDK_HTTP_MODULE,
7980
WINGSDK_MATH_MODULE,
8081
WINGSDK_AWS_MODULE,
8182
WINGSDK_EX_MODULE,
83+
WINGSDK_REGEX_MODULE,
8284
];
8385

8486
const WINGSDK_DURATION: &'static str = "std.Duration";

libs/wingsdk/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ export * as core from "./core";
44
export * as ex from "./ex";
55
export * as http from "./http";
66
export * as math from "./math";
7+
export * as regex from "./regex";
78
export * as aws from "./shared-aws";
89
export * as std from "./std";
910
export * as testing from "./testing";

libs/wingsdk/src/regex/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from "./regex";

libs/wingsdk/src/regex/regex.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { InflightClient } from "../core";
2+
3+
/**
4+
* Regex utilities and functions
5+
*/
6+
export class Util {
7+
/**
8+
* Check if a regex pattern is matched by a given string
9+
* @param pattern - regex pattern
10+
* @param text - given input string
11+
* @returns true if it matches the pattern, false otherwise
12+
*/
13+
public static match(pattern: string, text: string): boolean {
14+
const regex = new RegExp(pattern);
15+
if (text.match(regex) === null) {
16+
return false;
17+
}
18+
return true;
19+
}
20+
21+
/**
22+
* @internal
23+
*/
24+
public static _toInflightType(): string {
25+
return InflightClient.forType(__filename, this.name);
26+
}
27+
private constructor() {}
28+
}
Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
# [match.w](../../../../../../examples/tests/sdk_tests/regex/match.w) | compile | tf-aws
2+
3+
## inflight.$Closure1-1.js
4+
```js
5+
module.exports = function({ $regex_Util }) {
6+
class $Closure1 {
7+
constructor({ }) {
8+
const $obj = (...args) => this.handle(...args);
9+
Object.setPrototypeOf($obj, this);
10+
return $obj;
11+
}
12+
async handle() {
13+
const matches1 = (await $regex_Util.match("p[a-z]+ch","peach"));
14+
const matches2 = (await $regex_Util.match("[0-9]+","0923"));
15+
const matches3 = (await $regex_Util.match("[0-9]+","0a923"));
16+
const matches4 = (await $regex_Util.match("^([a-zA-Z0-9_.-]+)@[a-z]+.[a-z]+\$","[email protected]"));
17+
const matches5 = (await $regex_Util.match("p([a-z]+)ch","leach"));
18+
const matches6 = (await $regex_Util.match("^([a-zA-Z0-9_.-]+)@[a-z]+.[a-z]+","@[email protected]"));
19+
const matches7 = (await $regex_Util.match("^Mary","Mary had a little lamb"));
20+
const matches8 = (await $regex_Util.match("lamb\$","Mary had a little lamb"));
21+
const matches9 = (await $regex_Util.match("lamb\$","Mary had a little hamb"));
22+
const matches10 = (await $regex_Util.match("^([a-zA-Z0-9_.-]+)@[a-z]+.[a-z]+\$","[email protected]"));
23+
{((cond) => {if (!cond) throw new Error("assertion failed: matches1 == true ")})((((a,b) => { try { return require('assert').deepStrictEqual(a,b) === undefined; } catch { return false; } })(matches1,true)))};
24+
{((cond) => {if (!cond) throw new Error("assertion failed: matches2 == true ")})((((a,b) => { try { return require('assert').deepStrictEqual(a,b) === undefined; } catch { return false; } })(matches2,true)))};
25+
{((cond) => {if (!cond) throw new Error("assertion failed: matches3 == true ")})((((a,b) => { try { return require('assert').deepStrictEqual(a,b) === undefined; } catch { return false; } })(matches3,true)))};
26+
{((cond) => {if (!cond) throw new Error("assertion failed: matches4 == true ")})((((a,b) => { try { return require('assert').deepStrictEqual(a,b) === undefined; } catch { return false; } })(matches4,true)))};
27+
{((cond) => {if (!cond) throw new Error("assertion failed: matches5 == false")})((((a,b) => { try { return require('assert').deepStrictEqual(a,b) === undefined; } catch { return false; } })(matches5,false)))};
28+
{((cond) => {if (!cond) throw new Error("assertion failed: matches6 == false")})((((a,b) => { try { return require('assert').deepStrictEqual(a,b) === undefined; } catch { return false; } })(matches6,false)))};
29+
{((cond) => {if (!cond) throw new Error("assertion failed: matches7 == true ")})((((a,b) => { try { return require('assert').deepStrictEqual(a,b) === undefined; } catch { return false; } })(matches7,true)))};
30+
{((cond) => {if (!cond) throw new Error("assertion failed: matches8 == true ")})((((a,b) => { try { return require('assert').deepStrictEqual(a,b) === undefined; } catch { return false; } })(matches8,true)))};
31+
{((cond) => {if (!cond) throw new Error("assertion failed: matches9 == false")})((((a,b) => { try { return require('assert').deepStrictEqual(a,b) === undefined; } catch { return false; } })(matches9,false)))};
32+
{((cond) => {if (!cond) throw new Error("assertion failed: matches10 == false")})((((a,b) => { try { return require('assert').deepStrictEqual(a,b) === undefined; } catch { return false; } })(matches10,false)))};
33+
}
34+
}
35+
return $Closure1;
36+
}
37+
38+
```
39+
40+
## main.tf.json
41+
```json
42+
{
43+
"//": {
44+
"metadata": {
45+
"backend": "local",
46+
"stackName": "root",
47+
"version": "0.17.0"
48+
},
49+
"outputs": {
50+
"root": {
51+
"Default": {
52+
"cloud.TestRunner": {
53+
"TestFunctionArns": "WING_TEST_RUNNER_FUNCTION_ARNS"
54+
}
55+
}
56+
}
57+
}
58+
},
59+
"output": {
60+
"WING_TEST_RUNNER_FUNCTION_ARNS": {
61+
"value": "[[\"root/Default/Default/test:inflight match\",\"${aws_lambda_function.testinflightmatch_Handler_91AFEF4E.arn}\"]]"
62+
}
63+
},
64+
"provider": {
65+
"aws": [
66+
{}
67+
]
68+
},
69+
"resource": {
70+
"aws_iam_role": {
71+
"testinflightmatch_Handler_IamRole_DA813D38": {
72+
"//": {
73+
"metadata": {
74+
"path": "root/Default/Default/test:inflight match/Handler/IamRole",
75+
"uniqueId": "testinflightmatch_Handler_IamRole_DA813D38"
76+
}
77+
},
78+
"assume_role_policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Action\":\"sts:AssumeRole\",\"Principal\":{\"Service\":\"lambda.amazonaws.com\"},\"Effect\":\"Allow\"}]}"
79+
}
80+
},
81+
"aws_iam_role_policy": {
82+
"testinflightmatch_Handler_IamRolePolicy_B7F9EB14": {
83+
"//": {
84+
"metadata": {
85+
"path": "root/Default/Default/test:inflight match/Handler/IamRolePolicy",
86+
"uniqueId": "testinflightmatch_Handler_IamRolePolicy_B7F9EB14"
87+
}
88+
},
89+
"policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Action\":\"none:null\",\"Resource\":\"*\"}]}",
90+
"role": "${aws_iam_role.testinflightmatch_Handler_IamRole_DA813D38.name}"
91+
}
92+
},
93+
"aws_iam_role_policy_attachment": {
94+
"testinflightmatch_Handler_IamRolePolicyAttachment_B8D01B6A": {
95+
"//": {
96+
"metadata": {
97+
"path": "root/Default/Default/test:inflight match/Handler/IamRolePolicyAttachment",
98+
"uniqueId": "testinflightmatch_Handler_IamRolePolicyAttachment_B8D01B6A"
99+
}
100+
},
101+
"policy_arn": "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole",
102+
"role": "${aws_iam_role.testinflightmatch_Handler_IamRole_DA813D38.name}"
103+
}
104+
},
105+
"aws_lambda_function": {
106+
"testinflightmatch_Handler_91AFEF4E": {
107+
"//": {
108+
"metadata": {
109+
"path": "root/Default/Default/test:inflight match/Handler/Default",
110+
"uniqueId": "testinflightmatch_Handler_91AFEF4E"
111+
}
112+
},
113+
"architectures": [
114+
"arm64"
115+
],
116+
"environment": {
117+
"variables": {
118+
"WING_FUNCTION_NAME": "Handler-c8aa61ca",
119+
"WING_TARGET": "tf-aws"
120+
}
121+
},
122+
"function_name": "Handler-c8aa61ca",
123+
"handler": "index.handler",
124+
"publish": true,
125+
"role": "${aws_iam_role.testinflightmatch_Handler_IamRole_DA813D38.arn}",
126+
"runtime": "nodejs18.x",
127+
"s3_bucket": "${aws_s3_bucket.Code.bucket}",
128+
"s3_key": "${aws_s3_object.testinflightmatch_Handler_S3Object_2184865C.key}",
129+
"timeout": 30,
130+
"vpc_config": {
131+
"security_group_ids": [],
132+
"subnet_ids": []
133+
}
134+
}
135+
},
136+
"aws_s3_bucket": {
137+
"Code": {
138+
"//": {
139+
"metadata": {
140+
"path": "root/Default/Code",
141+
"uniqueId": "Code"
142+
}
143+
},
144+
"bucket_prefix": "code-c84a50b1-"
145+
}
146+
},
147+
"aws_s3_object": {
148+
"testinflightmatch_Handler_S3Object_2184865C": {
149+
"//": {
150+
"metadata": {
151+
"path": "root/Default/Default/test:inflight match/Handler/S3Object",
152+
"uniqueId": "testinflightmatch_Handler_S3Object_2184865C"
153+
}
154+
},
155+
"bucket": "${aws_s3_bucket.Code.bucket}",
156+
"key": "<ASSET_KEY>",
157+
"source": "<ASSET_SOURCE>"
158+
}
159+
}
160+
}
161+
}
162+
```
163+
164+
## preflight.js
165+
```js
166+
const $stdlib = require('@winglang/sdk');
167+
const $plugins = ((s) => !s ? [] : s.split(';'))(process.env.WING_PLUGIN_PATHS);
168+
const $outdir = process.env.WING_SYNTH_DIR ?? ".";
169+
const $wing_is_test = process.env.WING_IS_TEST === "true";
170+
const std = $stdlib.std;
171+
const regex = $stdlib.regex;
172+
class $Root extends $stdlib.std.Resource {
173+
constructor(scope, id) {
174+
super(scope, id);
175+
class $Closure1 extends $stdlib.std.Resource {
176+
constructor(scope, id, ) {
177+
super(scope, id);
178+
(std.Node.of(this)).hidden = true;
179+
}
180+
static _toInflightType(context) {
181+
return `
182+
require("./inflight.$Closure1-1.js")({
183+
$regex_Util: ${context._lift(regex.Util)},
184+
})
185+
`;
186+
}
187+
_toInflight() {
188+
return `
189+
(await (async () => {
190+
const $Closure1Client = ${$Closure1._toInflightType(this)};
191+
const client = new $Closure1Client({
192+
});
193+
if (client.$inflight_init) { await client.$inflight_init(); }
194+
return client;
195+
})())
196+
`;
197+
}
198+
_getInflightOps() {
199+
return ["handle", "$inflight_init"];
200+
}
201+
}
202+
const matches1 = (regex.Util.match("p[a-z]+ch","peach"));
203+
const matches2 = (regex.Util.match("[0-9]+","0923"));
204+
const matches3 = (regex.Util.match("[0-9]+","0a923"));
205+
const matches4 = (regex.Util.match("^([a-zA-Z0-9_.-]+)@[a-z]+.[a-z]+\$","[email protected]"));
206+
const matches5 = (regex.Util.match("p([a-z]+)ch","leach"));
207+
const matches6 = (regex.Util.match("^([a-zA-Z0-9_.-]+)@[a-z]+.[a-z]+","@[email protected]"));
208+
const matches7 = (regex.Util.match("^Mary","Mary had a little lamb"));
209+
const matches8 = (regex.Util.match("lamb\$","Mary had a little lamb"));
210+
const matches9 = (regex.Util.match("lamb\$","Mary had a little hamb"));
211+
const matches10 = (regex.Util.match("^([a-zA-Z0-9_.-]+)@[a-z]+.[a-z]+\$","[email protected]"));
212+
{((cond) => {if (!cond) throw new Error("assertion failed: matches1 == true ")})((((a,b) => { try { return require('assert').deepStrictEqual(a,b) === undefined; } catch { return false; } })(matches1,true)))};
213+
{((cond) => {if (!cond) throw new Error("assertion failed: matches2 == true ")})((((a,b) => { try { return require('assert').deepStrictEqual(a,b) === undefined; } catch { return false; } })(matches2,true)))};
214+
{((cond) => {if (!cond) throw new Error("assertion failed: matches3 == true ")})((((a,b) => { try { return require('assert').deepStrictEqual(a,b) === undefined; } catch { return false; } })(matches3,true)))};
215+
{((cond) => {if (!cond) throw new Error("assertion failed: matches4 == true ")})((((a,b) => { try { return require('assert').deepStrictEqual(a,b) === undefined; } catch { return false; } })(matches4,true)))};
216+
{((cond) => {if (!cond) throw new Error("assertion failed: matches5 == false")})((((a,b) => { try { return require('assert').deepStrictEqual(a,b) === undefined; } catch { return false; } })(matches5,false)))};
217+
{((cond) => {if (!cond) throw new Error("assertion failed: matches6 == false")})((((a,b) => { try { return require('assert').deepStrictEqual(a,b) === undefined; } catch { return false; } })(matches6,false)))};
218+
{((cond) => {if (!cond) throw new Error("assertion failed: matches7 == true ")})((((a,b) => { try { return require('assert').deepStrictEqual(a,b) === undefined; } catch { return false; } })(matches7,true)))};
219+
{((cond) => {if (!cond) throw new Error("assertion failed: matches8 == true ")})((((a,b) => { try { return require('assert').deepStrictEqual(a,b) === undefined; } catch { return false; } })(matches8,true)))};
220+
{((cond) => {if (!cond) throw new Error("assertion failed: matches9 == false")})((((a,b) => { try { return require('assert').deepStrictEqual(a,b) === undefined; } catch { return false; } })(matches9,false)))};
221+
{((cond) => {if (!cond) throw new Error("assertion failed: matches10 == false")})((((a,b) => { try { return require('assert').deepStrictEqual(a,b) === undefined; } catch { return false; } })(matches10,false)))};
222+
this.node.root.new("@winglang/sdk.std.Test",std.Test,this,"test:inflight match",new $Closure1(this,"$Closure1"));
223+
}
224+
}
225+
const $App = $stdlib.core.App.for(process.env.WING_TARGET);
226+
new $App({ outdir: $outdir, name: "match", rootConstruct: $Root, plugins: $plugins, isTestEnvironment: $wing_is_test, entrypointDir: process.env['WING_SOURCE_DIR'], rootId: process.env['WING_ROOT_ID'] }).synth();
227+
228+
```
229+
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# [match.w](../../../../../../examples/tests/sdk_tests/regex/match.w) | test | sim
2+
3+
## stdout.log
4+
```log
5+
pass ─ match.wsim » root/env0/test:inflight match
6+
7+
8+
Tests 1 passed (1)
9+
Test Files 1 passed (1)
10+
Duration <DURATION>
11+
```
12+

0 commit comments

Comments
 (0)