gofront 0.0.9 → 1.0.1
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/README.md +5 -5
- package/package.json +7 -4
- package/src/cli-core.js +65 -0
- package/src/codegen/expressions.js +523 -1717
- package/src/codegen/index.js +576 -0
- package/src/codegen/runtime.js +151 -0
- package/src/codegen/statements.js +308 -296
- package/src/codegen/stdlib/builder.js +39 -0
- package/src/codegen/stdlib/bytes.js +65 -0
- package/src/codegen/stdlib/errors.js +20 -0
- package/src/codegen/stdlib/fmt.js +104 -0
- package/src/codegen/stdlib/gom.js +155 -0
- package/src/codegen/stdlib/html.js +20 -0
- package/src/codegen/stdlib/index.js +98 -0
- package/src/codegen/stdlib/io.js +36 -0
- package/src/codegen/stdlib/maps.js +45 -0
- package/src/codegen/stdlib/math.js +56 -0
- package/src/codegen/stdlib/os.js +13 -0
- package/src/codegen/stdlib/path.js +34 -0
- package/src/codegen/stdlib/rand.js +27 -0
- package/src/codegen/stdlib/regexp.js +54 -0
- package/src/codegen/stdlib/slices.js +51 -0
- package/src/codegen/stdlib/sort.js +31 -0
- package/src/codegen/stdlib/strconv.js +33 -0
- package/src/codegen/stdlib/strings.js +86 -0
- package/src/codegen/stdlib/time.js +57 -0
- package/src/codegen/stdlib/unicode.js +25 -0
- package/src/codegen/stdlib/utf8.js +26 -0
- package/src/codegen/templ.js +284 -0
- package/src/compiler.js +142 -77
- package/src/dts-parser.js +355 -371
- package/src/index.js +32 -162
- package/src/lexer.js +186 -272
- package/src/minifier.js +305 -318
- package/src/{parser.js → parser/declarations.js} +18 -175
- package/src/parser/expressions.js +51 -34
- package/src/parser/index.js +161 -0
- package/src/parser/statements.js +24 -34
- package/src/parser/types.js +31 -29
- package/src/templ-parser.js +2 -2
- package/src/tokens.js +109 -0
- package/src/typechecker/assignability.js +261 -0
- package/src/typechecker/expressions.js +568 -549
- package/src/typechecker/index.js +556 -0
- package/src/typechecker/resolve.js +428 -0
- package/src/typechecker/statements.js +270 -280
- package/src/typechecker/stdlib/core.js +592 -0
- package/src/typechecker/stdlib/extended.js +332 -0
- package/src/typechecker/stdlib.js +9 -1060
- package/src/typechecker/termination.js +86 -0
- package/src/typechecker/types.js +1 -0
- package/src/codegen.js +0 -882
- package/src/typechecker.js +0 -1299
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// CodeGen for Go `path` package.
|
|
2
|
+
|
|
3
|
+
/** @typedef {import('../index.js').CodeGen} CodeGen */
|
|
4
|
+
|
|
5
|
+
const PATH_DISPATCH = {
|
|
6
|
+
Base: (a) =>
|
|
7
|
+
`((p) => { if (!p) return "."; const stripped = p.replace(/\\/+$/, ""); if (!stripped) return "/"; const i = stripped.lastIndexOf("/"); return i < 0 ? stripped : stripped.slice(i + 1) || "/"; })(${a[0]})`,
|
|
8
|
+
Dir: (a) =>
|
|
9
|
+
`((p) => { const i = p.lastIndexOf("/"); if (i < 0) return "."; if (i === 0) return "/"; return p.slice(0, i); })(${a[0]})`,
|
|
10
|
+
Ext: (a) =>
|
|
11
|
+
`((p) => { const b = p.slice(p.lastIndexOf("/") + 1); const i = b.lastIndexOf("."); return i <= 0 ? "" : b.slice(i); })(${a[0]})`,
|
|
12
|
+
IsAbs: (a) => `(${a[0]}).startsWith("/")`,
|
|
13
|
+
Split: (a) =>
|
|
14
|
+
`((p) => { const i = p.lastIndexOf("/"); return i < 0 ? ["", p] : [p.slice(0, i + 1), p.slice(i + 1)]; })(${a[0]})`,
|
|
15
|
+
Match: (a) =>
|
|
16
|
+
`((pat, name) => { try { const sp=/[.+^$()|[\\]\\\\]/g; const re = new RegExp("^" + pat.replace(sp, "\\\\$&").replace(/\\*/g, "[^/]*").replace(/\\?/g, "[^/]") + "$"); return [re.test(name), null]; } catch(e) { return [false, "syntax error in pattern"]; } })(${a[0]}, ${a[1]})`,
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/** @type {ThisType<CodeGen>} */
|
|
20
|
+
export const pathMethods = {
|
|
21
|
+
_genPath(fn, a, expr) {
|
|
22
|
+
if (fn === "Join") {
|
|
23
|
+
this._usesPathClean = true;
|
|
24
|
+
const allArgs = expr.args.map((e) => this.genExpr(e)).join(", ");
|
|
25
|
+
return `__pathClean([${allArgs}].filter(x => x !== "").join("/"))`;
|
|
26
|
+
}
|
|
27
|
+
if (fn === "Clean") {
|
|
28
|
+
this._usesPathClean = true;
|
|
29
|
+
return `__pathClean(${a()[0]})`;
|
|
30
|
+
}
|
|
31
|
+
const gen = PATH_DISPATCH[fn];
|
|
32
|
+
return gen ? gen(a()) : undefined;
|
|
33
|
+
},
|
|
34
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// CodeGen for Go `math/rand` package.
|
|
2
|
+
|
|
3
|
+
/** @typedef {import('../index.js').CodeGen} CodeGen */
|
|
4
|
+
|
|
5
|
+
const RAND_DISPATCH = {
|
|
6
|
+
Intn: (a) => `Math.floor(Math.random() * ${a[0]})`,
|
|
7
|
+
Int63n: (a) => `Math.floor(Math.random() * ${a[0]})`,
|
|
8
|
+
Int31n: (a) => `Math.floor(Math.random() * ${a[0]})`,
|
|
9
|
+
Float64: () => "Math.random()",
|
|
10
|
+
Float32: () => "Math.random()",
|
|
11
|
+
Int: () => "Math.floor(Math.random() * 2147483647)",
|
|
12
|
+
Int31: () => "Math.floor(Math.random() * 2147483647)",
|
|
13
|
+
Int63: () => "Math.floor(Math.random() * Number.MAX_SAFE_INTEGER)",
|
|
14
|
+
Seed: () => "(void 0)",
|
|
15
|
+
Shuffle: (a) =>
|
|
16
|
+
`((n, f) => { for (let i = n - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); f(i, j); } })(${a[0]}, ${a[1]})`,
|
|
17
|
+
Perm: (a) =>
|
|
18
|
+
`((n) => { const a = Array.from({length: n}, (_, i) => i); for (let i = n - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [a[i], a[j]] = [a[j], a[i]]; } return a; })(${a[0]})`,
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/** @type {ThisType<CodeGen>} */
|
|
22
|
+
export const randMethods = {
|
|
23
|
+
_genRand(fn, a) {
|
|
24
|
+
const gen = RAND_DISPATCH[fn];
|
|
25
|
+
return gen ? gen(a()) : undefined;
|
|
26
|
+
},
|
|
27
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// CodeGen for Go `regexp` package and `*Regexp` methods.
|
|
2
|
+
|
|
3
|
+
/** @typedef {import('../index.js').CodeGen} CodeGen */
|
|
4
|
+
|
|
5
|
+
const REGEXP_METHOD = {
|
|
6
|
+
MatchString: (re, a) => `${re}.test(${a[0]})`,
|
|
7
|
+
FindString: (re, a) => `(${re}.exec(${a[0]})?.[0] ?? "")`,
|
|
8
|
+
FindStringIndex: (re, a) =>
|
|
9
|
+
`((m => m ? [m.index, m.index + m[0].length] : null)(${re}.exec(${a[0]})))`,
|
|
10
|
+
FindAllString: (re, a) =>
|
|
11
|
+
`[...${a[0]}.matchAll(new RegExp(${re}.source, ${re}.flags.includes("g") ? ${re}.flags : ${re}.flags + "g"))].slice(0, ${a[1]} < 0 ? undefined : ${a[1]}).map(m => m[0])`,
|
|
12
|
+
FindStringSubmatch: (re, a) => `[...(${re}.exec(${a[0]}) ?? [])]`,
|
|
13
|
+
FindAllStringSubmatch: (re, a) =>
|
|
14
|
+
`[...${a[0]}.matchAll(new RegExp(${re}.source, ${re}.flags.includes("g") ? ${re}.flags : ${re}.flags + "g"))].slice(0, ${a[1]} < 0 ? undefined : ${a[1]}).map(m => [...m])`,
|
|
15
|
+
ReplaceAllString: (re, a) =>
|
|
16
|
+
`${a[0]}.replaceAll(new RegExp(${re}.source, ${re}.flags.includes("g") ? ${re}.flags : ${re}.flags + "g"), ${a[1]})`,
|
|
17
|
+
ReplaceAllLiteralString: (re, a) =>
|
|
18
|
+
`${a[0]}.replaceAll(new RegExp(${re}.source, ${re}.flags.includes("g") ? ${re}.flags : ${re}.flags + "g"), ${a[1]}.replace(/\\$/g, '$$$$'))`,
|
|
19
|
+
Split: (re, a) =>
|
|
20
|
+
`${a[0]}.split(${re}).slice(0, ${a[1]} < 0 ? undefined : ${a[1]})`,
|
|
21
|
+
String: (re) => `${re}.source`,
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/** @type {ThisType<CodeGen>} */
|
|
25
|
+
export const regexpMethods = {
|
|
26
|
+
_genRegexp(fn, a) {
|
|
27
|
+
// Converts a Go regexp pattern string (which may contain inline flags like (?i))
|
|
28
|
+
// into a JS RegExp, extracting the flags into the second constructor argument.
|
|
29
|
+
const makeRegExp = (pat) =>
|
|
30
|
+
`((p) => { const m = /^\\(\\?([gimsuy]+)\\)/.exec(p); return m ? new RegExp(p.slice(m[0].length), m[1]) : new RegExp(p); })(${pat})`;
|
|
31
|
+
switch (fn) {
|
|
32
|
+
case "MustCompile":
|
|
33
|
+
return makeRegExp(a()[0]);
|
|
34
|
+
case "Compile":
|
|
35
|
+
return `[${makeRegExp(a()[0])}, null]`;
|
|
36
|
+
case "MatchString": {
|
|
37
|
+
const [pat, s] = a();
|
|
38
|
+
return `[new RegExp(${pat}).test(${s}), null]`;
|
|
39
|
+
}
|
|
40
|
+
case "QuoteMeta":
|
|
41
|
+
return `${a()[0]}.replace(/[.*+?^\${}()|[\\]\\\\]/g, '\\\\$&')`;
|
|
42
|
+
}
|
|
43
|
+
return undefined;
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
_genRegexpMethodCall(method, expr) {
|
|
47
|
+
const recv = expr.func.expr;
|
|
48
|
+
// *Regexp is a plain JS RegExp — no .value boxing needed
|
|
49
|
+
const re = this.genExpr(recv);
|
|
50
|
+
const args = expr.args.map((a) => this.genExpr(a));
|
|
51
|
+
const gen = REGEXP_METHOD[method];
|
|
52
|
+
return gen ? gen(re, args) : undefined;
|
|
53
|
+
},
|
|
54
|
+
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// CodeGen for Go `slices` package.
|
|
2
|
+
|
|
3
|
+
/** @typedef {import('../index.js').CodeGen} CodeGen */
|
|
4
|
+
|
|
5
|
+
const SLICES_DISPATCH = {
|
|
6
|
+
Contains: (a) => `${a[0]}.includes(${a[1]})`,
|
|
7
|
+
Index: (a) => `${a[0]}.indexOf(${a[1]})`,
|
|
8
|
+
Compare: (a) =>
|
|
9
|
+
`((a, b) => { for (let i = 0; i < a.length && i < b.length; i++) { if (a[i] < b[i]) return -1; if (a[i] > b[i]) return 1; } return a.length - b.length; })(${a[0]}, ${a[1]})`,
|
|
10
|
+
Sort: (a) => `${a[0]}.sort((a, b) => a < b ? -1 : a > b ? 1 : 0)`,
|
|
11
|
+
SortFunc: (a) => `${a[0]}.sort(${a[1]})`,
|
|
12
|
+
SortStableFunc: (a) => `${a[0]}.sort(${a[1]})`,
|
|
13
|
+
IsSorted: (a) =>
|
|
14
|
+
`((s) => { for (let i = 1; i < s.length; i++) { if (s[i] < s[i-1]) return false; } return true; })(${a[0]})`,
|
|
15
|
+
IsSortedFunc: (a) =>
|
|
16
|
+
`((s, f) => { for (let i = 1; i < s.length; i++) { if (f(s[i], s[i-1]) < 0) return false; } return true; })(${a[0]}, ${a[1]})`,
|
|
17
|
+
Reverse: (a) => `${a[0]}.reverse()`,
|
|
18
|
+
Max: (a) => `Math.max(...${a[0]})`,
|
|
19
|
+
Min: (a) => `Math.min(...${a[0]})`,
|
|
20
|
+
MaxFunc: (a) =>
|
|
21
|
+
`((s, f) => s.reduce((m, x) => f(x, m) > 0 ? x : m))(${a[0]}, ${a[1]})`,
|
|
22
|
+
MinFunc: (a) =>
|
|
23
|
+
`((s, f) => s.reduce((m, x) => f(x, m) < 0 ? x : m))(${a[0]}, ${a[1]})`,
|
|
24
|
+
Clone: (a) => `${a[0]}.slice()`,
|
|
25
|
+
Compact: (a) =>
|
|
26
|
+
`((s) => s.filter((v, i) => i === 0 || v !== s[i-1]))(${a[0]})`,
|
|
27
|
+
CompactFunc: (a) =>
|
|
28
|
+
`((s, f) => s.filter((v, i) => i === 0 || !f(v, s[i-1])))(${a[0]}, ${a[1]})`,
|
|
29
|
+
Concat: (a) => `[].concat(${a.join(", ")})`,
|
|
30
|
+
Delete: (a) => `[...${a[0]}.slice(0, ${a[1]}), ...${a[0]}.slice(${a[2]})]`,
|
|
31
|
+
DeleteFunc: (a) => `${a[0]}.filter((v) => !${a[1]}(v))`,
|
|
32
|
+
Insert: (a) =>
|
|
33
|
+
`[...${a[0]}.slice(0, ${a[1]}), ${a.slice(2).join(", ")}, ...${a[0]}.slice(${a[1]})]`,
|
|
34
|
+
Replace: (a) =>
|
|
35
|
+
`[...${a[0]}.slice(0, ${a[1]}), ${a.slice(3).join(", ")}, ...${a[0]}.slice(${a[2]})]`,
|
|
36
|
+
Grow: (a) => `${a[0]}.slice()`,
|
|
37
|
+
Clip: (a) => `${a[0]}.slice()`,
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
/** @type {ThisType<CodeGen>} */
|
|
41
|
+
export const slicesMethods = {
|
|
42
|
+
_genSlices(fn, a) {
|
|
43
|
+
if (fn === "Equal") {
|
|
44
|
+
this._usesEqual = true;
|
|
45
|
+
const [a1, b1] = a();
|
|
46
|
+
return `__equal(${a1}, ${b1})`;
|
|
47
|
+
}
|
|
48
|
+
const gen = SLICES_DISPATCH[fn];
|
|
49
|
+
return gen ? gen(a()) : undefined;
|
|
50
|
+
},
|
|
51
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// CodeGen for Go `sort` package.
|
|
2
|
+
|
|
3
|
+
/** @typedef {import('../index.js').CodeGen} CodeGen */
|
|
4
|
+
|
|
5
|
+
const SORT_DISPATCH = {
|
|
6
|
+
Ints: (a) => `${a[0]}.sort((a, b) => a - b)`,
|
|
7
|
+
Float64s: (a) => `${a[0]}.sort((a, b) => a - b)`,
|
|
8
|
+
Strings: (a) => `${a[0]}.sort()`,
|
|
9
|
+
Slice: (a) =>
|
|
10
|
+
`${a[0]}.sort((a, b) => ${a[1]}(a, b) ? -1 : ${a[1]}(b, a) ? 1 : 0)`,
|
|
11
|
+
SliceStable: (a) =>
|
|
12
|
+
`${a[0]}.sort((a, b) => ${a[1]}(a, b) ? -1 : ${a[1]}(b, a) ? 1 : 0)`,
|
|
13
|
+
SliceIsSorted: (a) =>
|
|
14
|
+
`${a[0]}.every((v, i, a) => i === 0 || ${a[1]}(a[i - 1], v))`,
|
|
15
|
+
Search: (a) =>
|
|
16
|
+
`((n, f) => { let lo = 0, hi = n; while (lo < hi) { const mid = (lo + hi) >>> 1; if (f(mid)) hi = mid; else lo = mid + 1; } return lo; })(${a[0]}, ${a[1]})`,
|
|
17
|
+
IntsAreSorted: (a) =>
|
|
18
|
+
`(${a[0]}).every((v, i, a) => i === 0 || a[i - 1] <= v)`,
|
|
19
|
+
Float64sAreSorted: (a) =>
|
|
20
|
+
`(${a[0]}).every((v, i, a) => i === 0 || a[i - 1] <= v)`,
|
|
21
|
+
StringsAreSorted: (a) =>
|
|
22
|
+
`(${a[0]}).every((v, i, a) => i === 0 || a[i - 1] <= v)`,
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/** @type {ThisType<CodeGen>} */
|
|
26
|
+
export const sortMethods = {
|
|
27
|
+
_genSort(fn, a) {
|
|
28
|
+
const gen = SORT_DISPATCH[fn];
|
|
29
|
+
return gen ? gen(a()) : undefined;
|
|
30
|
+
},
|
|
31
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// CodeGen for Go `strconv` package.
|
|
2
|
+
|
|
3
|
+
/** @typedef {import('../index.js').CodeGen} CodeGen */
|
|
4
|
+
|
|
5
|
+
const STRCONV_DISPATCH = {
|
|
6
|
+
Itoa: (a) => `String(${a[0]})`,
|
|
7
|
+
Atoi: (a) =>
|
|
8
|
+
`(Number.isNaN(Number(${a[0]})) ? [0, "invalid syntax"] : [Number(${a[0]}) | 0, null])`,
|
|
9
|
+
FormatBool: (a) => `String(${a[0]})`,
|
|
10
|
+
FormatInt: (a) => `(${a[0]}).toString(${a[1]})`,
|
|
11
|
+
FormatFloat: (a) => `String(${a[0]})`,
|
|
12
|
+
ParseFloat: (a) =>
|
|
13
|
+
`(Number.isNaN(Number(${a[0]})) ? [0, "invalid syntax"] : [Number(${a[0]}), null])`,
|
|
14
|
+
ParseInt: (a) =>
|
|
15
|
+
`(Number.isNaN(parseInt(${a[0]}, ${a[1]} || 10)) ? [0, "invalid syntax"] : [parseInt(${a[0]}, ${a[1]} || 10), null])`,
|
|
16
|
+
ParseBool: (a) =>
|
|
17
|
+
`(${a[0]} === "true" || ${a[0]} === "1" ? [true, null] : ${a[0]} === "false" || ${a[0]} === "0" ? [false, null] : [false, "invalid syntax"])`,
|
|
18
|
+
Quote: (a) => `JSON.stringify(${a[0]})`,
|
|
19
|
+
Unquote: (a) =>
|
|
20
|
+
`((s) => { try { const v = JSON.parse(s); return [v, null]; } catch(e) { return ["", "invalid syntax"]; } })(${a[0]})`,
|
|
21
|
+
AppendInt: (a) =>
|
|
22
|
+
`[...(${a[0]}), ...new TextEncoder().encode((${a[1]}).toString(${a[2]}))]`,
|
|
23
|
+
AppendFloat: (a) =>
|
|
24
|
+
`[...(${a[0]}), ...new TextEncoder().encode(${a[1]}.toFixed(${a[3]} < 0 ? 6 : ${a[3]}))]`,
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/** @type {ThisType<CodeGen>} */
|
|
28
|
+
export const strconvMethods = {
|
|
29
|
+
_genStrconv(fn, a) {
|
|
30
|
+
const gen = STRCONV_DISPATCH[fn];
|
|
31
|
+
return gen ? gen(a()) : undefined;
|
|
32
|
+
},
|
|
33
|
+
};
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
// CodeGen for Go `strings` package.
|
|
2
|
+
|
|
3
|
+
/** @typedef {import('../index.js').CodeGen} CodeGen */
|
|
4
|
+
|
|
5
|
+
const STRINGS_M1 = {
|
|
6
|
+
ToUpper: "toUpperCase",
|
|
7
|
+
ToLower: "toLowerCase",
|
|
8
|
+
TrimSpace: "trim",
|
|
9
|
+
ToTitle: "toUpperCase",
|
|
10
|
+
};
|
|
11
|
+
const STRINGS_M2 = {
|
|
12
|
+
Contains: "includes",
|
|
13
|
+
HasPrefix: "startsWith",
|
|
14
|
+
HasSuffix: "endsWith",
|
|
15
|
+
Index: "indexOf",
|
|
16
|
+
LastIndex: "lastIndexOf",
|
|
17
|
+
Repeat: "repeat",
|
|
18
|
+
Split: "split",
|
|
19
|
+
Join: "join",
|
|
20
|
+
};
|
|
21
|
+
const STRINGS_DISPATCH = {
|
|
22
|
+
Count: ([s, s2]) =>
|
|
23
|
+
`((s, sep) => sep === "" ? s.length + 1 : s.split(sep).length - 1)(${s}, ${s2})`,
|
|
24
|
+
Replace: ([s, s2, s3]) => `${s}.replace(${s2}, ${s3})`,
|
|
25
|
+
ReplaceAll: ([s, s2, s3]) => `${s}.replaceAll(${s2}, ${s3})`,
|
|
26
|
+
Trim: ([s, s2]) =>
|
|
27
|
+
`${s}.replace(new RegExp(\`^[\${${s2}}]+|[\${${s2}}]+$\`, "g"), "")`,
|
|
28
|
+
TrimPrefix: ([s, s2]) =>
|
|
29
|
+
`((s, pre) => s.startsWith(pre) ? s.slice(pre.length) : s)(${s}, ${s2})`,
|
|
30
|
+
TrimSuffix: ([s, s2]) =>
|
|
31
|
+
`((s, suf) => !suf.length || !s.endsWith(suf) ? s : s.slice(0, -suf.length))(${s}, ${s2})`,
|
|
32
|
+
TrimLeft: ([s, s2]) => `${s}.replace(new RegExp(\`^[\${${s2}}]+\`), "")`,
|
|
33
|
+
TrimRight: ([s, s2]) => `${s}.replace(new RegExp(\`[\${${s2}}]+$\`), "")`,
|
|
34
|
+
EqualFold: ([s, s2]) => `${s}.toLowerCase() === ${s2}.toLowerCase()`,
|
|
35
|
+
Fields: ([s]) => `(${s}).trim() === '' ? [] : (${s}).trim().split(/\\s+/)`,
|
|
36
|
+
Cut: ([s, s2]) =>
|
|
37
|
+
`((s, sep) => { const i = s.indexOf(sep); return i < 0 ? [s, "", false] : [s.slice(0, i), s.slice(i + sep.length), true]; })(${s}, ${s2})`,
|
|
38
|
+
CutPrefix: ([s, s2]) =>
|
|
39
|
+
`(${s}).startsWith(${s2}) ? [(${s}).slice((${s2}).length), true] : [${s}, false]`,
|
|
40
|
+
CutSuffix: ([s, s2]) =>
|
|
41
|
+
`(${s}).endsWith(${s2}) ? [(${s}).slice(0, -(${s2}).length), true] : [${s}, false]`,
|
|
42
|
+
SplitN: ([s, s2, s3]) =>
|
|
43
|
+
`((s, sep, n) => { if (n === 0) return []; if (n < 0) return s.split(sep); const r = []; let cur = s; for (let i = 1; i < n && cur.length; i++) { const j = cur.indexOf(sep); if (j < 0) break; r.push(cur.slice(0, j)); cur = cur.slice(j + sep.length); } r.push(cur); return r; })(${s}, ${s2}, ${s3})`,
|
|
44
|
+
SplitAfter: ([s, s2]) =>
|
|
45
|
+
`((s, sep) => { if (sep === "") return [...s]; const r = []; let cur = s; while (cur.length) { const j = cur.indexOf(sep); if (j < 0) { r.push(cur); break; } r.push(cur.slice(0, j + sep.length)); cur = cur.slice(j + sep.length); } return r; })(${s}, ${s2})`,
|
|
46
|
+
SplitAfterN: ([s, s2, s3]) =>
|
|
47
|
+
`((s, sep, n) => { if (n === 0) return []; if (n < 0 || sep === "") return ((s, sep) => { if (sep === "") return [...s]; const r = []; let cur = s; while (cur.length) { const j = cur.indexOf(sep); if (j < 0) { r.push(cur); break; } r.push(cur.slice(0, j + sep.length)); cur = cur.slice(j + sep.length); } return r; })(s, sep); const r = []; let cur = s; for (let i = 1; i < n && cur.length; i++) { const j = cur.indexOf(sep); if (j < 0) break; r.push(cur.slice(0, j + sep.length)); cur = cur.slice(j + sep.length); } r.push(cur); return r; })(${s}, ${s2}, ${s3})`,
|
|
48
|
+
IndexAny: ([s, s2]) =>
|
|
49
|
+
`((s, chars) => { let m = -1; for (const c of chars) { const i = s.indexOf(c); if (i >= 0 && (m < 0 || i < m)) m = i; } return m; })(${s}, ${s2})`,
|
|
50
|
+
LastIndexAny: ([s, s2]) =>
|
|
51
|
+
`((s, chars) => { let m = -1; for (const c of chars) { const i = s.lastIndexOf(c); if (i > m) m = i; } return m; })(${s}, ${s2})`,
|
|
52
|
+
ContainsAny: ([s, s2]) => `[...(${s2})].some(c => (${s}).includes(c))`,
|
|
53
|
+
ContainsRune: ([s, s2]) => `(${s}).includes(String.fromCodePoint(${s2}))`,
|
|
54
|
+
IndexRune: ([s, s2]) => `(${s}).indexOf(String.fromCodePoint(${s2}))`,
|
|
55
|
+
IndexByte: ([s, s2]) => `(${s}).indexOf(String.fromCharCode(${s2}))`,
|
|
56
|
+
LastIndexByte: ([s, s2]) => `(${s}).lastIndexOf(String.fromCharCode(${s2}))`,
|
|
57
|
+
Map: ([s, s2]) =>
|
|
58
|
+
`[...(${s2})].map(c => String.fromCodePoint((${s})(c.codePointAt(0)))).join("")`,
|
|
59
|
+
Title: ([s]) => `(${s}).replace(/\\b\\w/g, c => c.toUpperCase())`,
|
|
60
|
+
TrimFunc: ([s, s2]) =>
|
|
61
|
+
`((s, f) => { let l = 0, r = s.length; while (l < r && f(s.codePointAt(l))) l++; while (r > l && f(s.codePointAt(r - 1))) r--; return s.slice(l, r); })(${s}, ${s2})`,
|
|
62
|
+
TrimLeftFunc: ([s, s2]) =>
|
|
63
|
+
`((s, f) => { let l = 0; while (l < s.length && f(s.codePointAt(l))) l++; return s.slice(l); })(${s}, ${s2})`,
|
|
64
|
+
TrimRightFunc: ([s, s2]) =>
|
|
65
|
+
`((s, f) => { let r = s.length; while (r > 0 && f(s.codePointAt(r - 1))) r--; return s.slice(0, r); })(${s}, ${s2})`,
|
|
66
|
+
IndexFunc: ([s, s2]) =>
|
|
67
|
+
`((s, f) => { for (let i = 0; i < s.length; i++) { const cp = s.codePointAt(i); if (f(cp)) return i; if (cp > 0xFFFF) i++; } return -1; })(${s}, ${s2})`,
|
|
68
|
+
LastIndexFunc: ([s, s2]) =>
|
|
69
|
+
`((s, f) => { for (let i = s.length - 1; i >= 0; i--) { const cp = s.codePointAt(i); if (f(cp)) return i; } return -1; })(${s}, ${s2})`,
|
|
70
|
+
NewReader: ([s]) =>
|
|
71
|
+
`{_src: ${s}, _pos: 0, Read(p) { const n = Math.min(p.length, this._src.length - this._pos); for (let i = 0; i < n; i++) p[i] = this._src.charCodeAt(this._pos + i); this._pos += n; return [n, n === 0 ? "EOF" : null]; }, Len() { return this._src.length - this._pos; }, Reset(s) { this._src = s; this._pos = 0; }}`,
|
|
72
|
+
NewReplacer: (args) =>
|
|
73
|
+
`((...pairs) => { const p = []; for (let i = 0; i < pairs.length; i += 2) p.push([pairs[i], pairs[i+1]]); return { _p: p, Replace(s) { let r = s; for (const [o, n] of this._p) r = r.split(o).join(n); return r; } }; })(${args.join(", ")})`,
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
/** @type {ThisType<CodeGen>} */
|
|
77
|
+
export const stringsMethods = {
|
|
78
|
+
_genStrings(fn, a) {
|
|
79
|
+
const args = a();
|
|
80
|
+
const [s, s2] = args;
|
|
81
|
+
if (STRINGS_M1[fn]) return `${s}.${STRINGS_M1[fn]}()`;
|
|
82
|
+
if (STRINGS_M2[fn]) return `${s}.${STRINGS_M2[fn]}(${s2})`;
|
|
83
|
+
const gen = STRINGS_DISPATCH[fn];
|
|
84
|
+
return gen ? gen(args) : undefined;
|
|
85
|
+
},
|
|
86
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// CodeGen for Go `time` package — `time.Now`, `time.Since`, etc., plus `*Time` methods.
|
|
2
|
+
|
|
3
|
+
/** @typedef {import('../index.js').CodeGen} CodeGen */
|
|
4
|
+
|
|
5
|
+
const TIME_METHOD_DISPATCH = {
|
|
6
|
+
Year: (r) => `${r}._d.getFullYear()`,
|
|
7
|
+
Month: (r) => `${r}._d.getMonth() + 1`,
|
|
8
|
+
Day: (r) => `${r}._d.getDate()`,
|
|
9
|
+
Hour: (r) => `${r}._d.getHours()`,
|
|
10
|
+
Minute: (r) => `${r}._d.getMinutes()`,
|
|
11
|
+
Second: (r) => `${r}._d.getSeconds()`,
|
|
12
|
+
Weekday: (r) => `${r}._d.getDay()`,
|
|
13
|
+
Unix: (r) => `Math.floor(${r}._d.getTime() / 1000)`,
|
|
14
|
+
UnixMilli: (r) => `${r}._d.getTime()`,
|
|
15
|
+
Add: (r, a) => `{_d: new Date(${r}._d.getTime() + (${a[0]}))}`,
|
|
16
|
+
Sub: (r, a) => `${r}._d.getTime() - (${a[0]})._d.getTime()`,
|
|
17
|
+
Before: (r, a) => `${r}._d < (${a[0]})._d`,
|
|
18
|
+
After: (r, a) => `${r}._d > (${a[0]})._d`,
|
|
19
|
+
Equal: (r, a) => `${r}._d.getTime() === (${a[0]})._d.getTime()`,
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const TIME_DISPATCH = {
|
|
23
|
+
Now: () => "{_d: new Date()}",
|
|
24
|
+
Since: (a) => `(Date.now() - (${a[0]})._d.getTime())`,
|
|
25
|
+
Sleep: (a) => `await new Promise(r => setTimeout(r, ${a[0]} / 1000000))`,
|
|
26
|
+
Unix: (a) => `{_d: new Date((${a[0]}) * 1000)}`,
|
|
27
|
+
Date: (a) =>
|
|
28
|
+
`{_d: new Date(${a[0]}, ${a[1]} - 1, ${a[2]}, ${a[3]}, ${a[4]}, ${a[5]})}`,
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/** @type {ThisType<CodeGen>} */
|
|
32
|
+
export const timeMethods = {
|
|
33
|
+
_genTimeMethodCall(method, expr) {
|
|
34
|
+
const recv = expr.func.expr;
|
|
35
|
+
const recvJs = this.genExpr(recv);
|
|
36
|
+
const args = expr.args.map((a) => this.genExpr(a));
|
|
37
|
+
if (method === "Format") {
|
|
38
|
+
this._usesTimeFmt = true;
|
|
39
|
+
return `__timeFmt(${recvJs}._d, ${args[0]})`;
|
|
40
|
+
}
|
|
41
|
+
if (method === "String") {
|
|
42
|
+
this._usesTimeFmt = true;
|
|
43
|
+
return `__timeFmt(${recvJs}._d, "2006-01-02T15:04:05Z07:00")`;
|
|
44
|
+
}
|
|
45
|
+
const gen = TIME_METHOD_DISPATCH[method];
|
|
46
|
+
return gen ? gen(recvJs, args) : undefined;
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
_genTime(fn, a) {
|
|
50
|
+
if (fn === "Parse") {
|
|
51
|
+
this._usesTimeParse = true;
|
|
52
|
+
return `__timeParse(${a()[0]}, ${a()[1]})`;
|
|
53
|
+
}
|
|
54
|
+
const gen = TIME_DISPATCH[fn];
|
|
55
|
+
return gen ? gen(a()) : undefined;
|
|
56
|
+
},
|
|
57
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// CodeGen for Go `unicode` package.
|
|
2
|
+
|
|
3
|
+
/** @typedef {import('../index.js').CodeGen} CodeGen */
|
|
4
|
+
|
|
5
|
+
/** @type {ThisType<CodeGen>} */
|
|
6
|
+
export const unicodeMethods = {
|
|
7
|
+
_genUnicode(fn, a) {
|
|
8
|
+
const [arg] = a();
|
|
9
|
+
const cp = `String.fromCodePoint(${arg})`;
|
|
10
|
+
const UNICODE_TEST = {
|
|
11
|
+
IsLetter: `/\\p{L}/u.test(${cp})`,
|
|
12
|
+
IsDigit: `/\\p{Nd}/u.test(${cp})`,
|
|
13
|
+
IsSpace: `/\\s/.test(${cp})`,
|
|
14
|
+
IsUpper: `((__c) => __c === __c.toUpperCase() && /\\p{L}/u.test(__c))(${cp})`,
|
|
15
|
+
IsLower: `((__c) => __c === __c.toLowerCase() && /\\p{L}/u.test(__c))(${cp})`,
|
|
16
|
+
IsPunct: `/\\p{P}/u.test(${cp})`,
|
|
17
|
+
IsControl: `/\\p{Cc}/u.test(${cp})`,
|
|
18
|
+
IsPrint: `!/\\p{Cc}/u.test(${cp})`,
|
|
19
|
+
IsGraphic: `!/\\p{Cc}/u.test(${cp})`,
|
|
20
|
+
ToUpper: `${cp}.toUpperCase().codePointAt(0)`,
|
|
21
|
+
ToLower: `${cp}.toLowerCase().codePointAt(0)`,
|
|
22
|
+
};
|
|
23
|
+
return UNICODE_TEST[fn] ?? undefined;
|
|
24
|
+
},
|
|
25
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// CodeGen for Go `unicode/utf8` package.
|
|
2
|
+
|
|
3
|
+
/** @typedef {import('../index.js').CodeGen} CodeGen */
|
|
4
|
+
|
|
5
|
+
const UTF8_DISPATCH = {
|
|
6
|
+
RuneCountInString: (a) => `[...(${a[0]})].length`,
|
|
7
|
+
RuneLen: (a) =>
|
|
8
|
+
`((r) => r < 0 ? -1 : r <= 0x7F ? 1 : r <= 0x7FF ? 2 : r <= 0xFFFF ? (r >= 0xD800 && r <= 0xDFFF ? -1 : 3) : r <= 0x10FFFF ? 4 : -1)(${a[0]})`,
|
|
9
|
+
ValidString: (a) =>
|
|
10
|
+
`/[\\uD800-\\uDBFF](?![\\uDC00-\\uDFFF])|(?<![\\uD800-\\uDBFF])[\\uDC00-\\uDFFF]/.test(${a[0]}) === false`,
|
|
11
|
+
ValidRune: (a) =>
|
|
12
|
+
`(${a[0]}) >= 0 && (${a[0]}) <= 0x10FFFF && !((${a[0]}) >= 0xD800 && (${a[0]}) <= 0xDFFF)`,
|
|
13
|
+
DecodeRuneInString: (a) =>
|
|
14
|
+
`((s) => { if (!s) return [0xFFFD, 0]; const cp = s.codePointAt(0); return [cp, cp > 0xFFFF ? 2 : 1]; })(${a[0]})`,
|
|
15
|
+
DecodeLastRuneInString: (a) =>
|
|
16
|
+
`((s) => { if (!s) return [0xFFFD, 0]; const i = s.length > 1 && s.charCodeAt(s.length - 1) >= 0xDC00 && s.charCodeAt(s.length - 1) <= 0xDFFF ? s.length - 2 : s.length - 1; const cp = s.codePointAt(i); return [cp, cp > 0xFFFF ? 2 : 1]; })(${a[0]})`,
|
|
17
|
+
FullRuneInString: (a) => `(${a[0]}).length > 0`,
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/** @type {ThisType<CodeGen>} */
|
|
21
|
+
export const utf8Methods = {
|
|
22
|
+
_genUtf8(fn, a) {
|
|
23
|
+
const gen = UTF8_DISPATCH[fn];
|
|
24
|
+
return gen ? gen(a()) : undefined;
|
|
25
|
+
},
|
|
26
|
+
};
|