gofront 0.0.9 → 1.0.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.
- package/README.md +6 -6
- package/package.json +7 -4
- package/src/codegen/expressions.js +520 -1717
- package/src/codegen/index.js +576 -0
- package/src/codegen/runtime.js +151 -0
- package/src/codegen/statements.js +305 -296
- package/src/codegen/stdlib/builder.js +36 -0
- package/src/codegen/stdlib/bytes.js +62 -0
- package/src/codegen/stdlib/errors.js +17 -0
- package/src/codegen/stdlib/fmt.js +101 -0
- package/src/codegen/stdlib/gom.js +152 -0
- package/src/codegen/stdlib/html.js +17 -0
- package/src/codegen/stdlib/index.js +95 -0
- package/src/codegen/stdlib/io.js +33 -0
- package/src/codegen/stdlib/maps.js +42 -0
- package/src/codegen/stdlib/math.js +53 -0
- package/src/codegen/stdlib/os.js +10 -0
- package/src/codegen/stdlib/path.js +31 -0
- package/src/codegen/stdlib/rand.js +24 -0
- package/src/codegen/stdlib/regexp.js +51 -0
- package/src/codegen/stdlib/slices.js +48 -0
- package/src/codegen/stdlib/sort.js +28 -0
- package/src/codegen/stdlib/strconv.js +30 -0
- package/src/codegen/stdlib/strings.js +83 -0
- package/src/codegen/stdlib/time.js +54 -0
- package/src/codegen/stdlib/unicode.js +22 -0
- package/src/codegen/stdlib/utf8.js +23 -0
- package/src/codegen/templ.js +281 -0
- package/src/compiler.js +80 -74
- package/src/dts-parser.js +355 -371
- package/src/index.js +13 -49
- package/src/lexer.js +186 -272
- package/src/minifier.js +305 -318
- package/src/parser/expressions.js +51 -34
- package/src/{parser.js → parser/index.js} +4 -4
- 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 +258 -0
- package/src/typechecker/expressions.js +559 -549
- package/src/typechecker/index.js +555 -0
- package/src/typechecker/resolve.js +421 -0
- package/src/typechecker/statements.js +267 -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 +83 -0
- package/src/codegen.js +0 -882
- package/src/typechecker.js +0 -1299
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// CodeGen for `strings.Builder` and `bytes.Buffer` method calls.
|
|
2
|
+
|
|
3
|
+
const BUILDER_STR = {
|
|
4
|
+
WriteString: (b, a) => `(${b}._buf += ${a[0]}, [${a[0]}.length, null])`,
|
|
5
|
+
WriteByte: (b, a) => `(${b}._buf += String.fromCodePoint(${a[0]}))`,
|
|
6
|
+
Write: (b, a) => `(${b}._buf += String.fromCharCode(...${a[0]}))`,
|
|
7
|
+
String: (b) => `${b}._buf`,
|
|
8
|
+
Reset: (b) => `(${b}._buf = "")`,
|
|
9
|
+
};
|
|
10
|
+
const BUILDER_BYTES = {
|
|
11
|
+
WriteString: (b, a) =>
|
|
12
|
+
`(${b}._buf.push(...new TextEncoder().encode(${a[0]})), [${a[0]}.length, null])`,
|
|
13
|
+
WriteByte: (b, a) => `${b}._buf.push(${a[0]})`,
|
|
14
|
+
Write: (b, a) => `(${b}._buf.push(...${a[0]}), [${a[0]}.length, null])`,
|
|
15
|
+
String: (b) => `new TextDecoder().decode(new Uint8Array(${b}._buf))`,
|
|
16
|
+
Reset: (b) => `(${b}._buf = [])`,
|
|
17
|
+
};
|
|
18
|
+
const BUILDER_COMMON = {
|
|
19
|
+
WriteRune: (b, a) => `(${b}._buf += String.fromCodePoint(${a[0]}))`,
|
|
20
|
+
Bytes: (b) => `${b}._buf.slice()`,
|
|
21
|
+
Len: (b) => `${b}._buf.length`,
|
|
22
|
+
Grow: () => "undefined",
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const builderMethods = {
|
|
26
|
+
_genBuilderCall(typeName, method, expr) {
|
|
27
|
+
const recv = expr.func.expr;
|
|
28
|
+
const isPtr = recv._type?.kind === "pointer";
|
|
29
|
+
const base = isPtr ? `${this.genExpr(recv)}.value` : this.genExpr(recv);
|
|
30
|
+
const args = expr.args.map((a) => this.genExpr(a));
|
|
31
|
+
const isStr = typeName === "strings.Builder";
|
|
32
|
+
const table = isStr ? BUILDER_STR : BUILDER_BYTES;
|
|
33
|
+
const gen = table[method] ?? BUILDER_COMMON[method];
|
|
34
|
+
return gen ? gen(base, args) : undefined;
|
|
35
|
+
},
|
|
36
|
+
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// CodeGen for Go `bytes` package.
|
|
2
|
+
|
|
3
|
+
const __bs = `(b => String.fromCharCode(...b))`;
|
|
4
|
+
const __sb = `(s => [...s].map(c => c.charCodeAt(0)))`;
|
|
5
|
+
|
|
6
|
+
const BYTES_DISPATCH = {
|
|
7
|
+
Contains: (a) => `${__bs}(${a[0]}).includes(${__bs}(${a[1]}))`,
|
|
8
|
+
HasPrefix: (a) =>
|
|
9
|
+
`((b, p) => { for (let i = 0; i < p.length; i++) if (b[i] !== p[i]) return false; return b.length >= p.length; })(${a[0]}, ${a[1]})`,
|
|
10
|
+
HasSuffix: (a) =>
|
|
11
|
+
`((b, s) => { const off = b.length - s.length; if (off < 0) return false; for (let i = 0; i < s.length; i++) if (b[off + i] !== s[i]) return false; return true; })(${a[0]}, ${a[1]})`,
|
|
12
|
+
Index: (a) => `${__bs}(${a[0]}).indexOf(${__bs}(${a[1]}))`,
|
|
13
|
+
Count: (a) =>
|
|
14
|
+
`((b, sep) => sep.length === 0 ? b.length + 1 : ${__bs}(b).split(${__bs}(sep)).length - 1)(${a[0]}, ${a[1]})`,
|
|
15
|
+
Repeat: (a) => `${__sb}(${__bs}(${a[0]}).repeat(${a[1]}))`,
|
|
16
|
+
Replace: (a) =>
|
|
17
|
+
`((b, o, n, cnt) => { let s = ${__bs}(b), os = ${__bs}(o), ns = ${__bs}(n); if (cnt < 0) return ${__sb}(s.replaceAll(os, ns)); for (let i = 0; i < cnt; i++) s = s.replace(os, ns); return ${__sb}(s); })(${a[0]}, ${a[1]}, ${a[2]}, ${a[3]})`,
|
|
18
|
+
ToUpper: (a) => `${__sb}(${__bs}(${a[0]}).toUpperCase())`,
|
|
19
|
+
ToLower: (a) => `${__sb}(${__bs}(${a[0]}).toLowerCase())`,
|
|
20
|
+
TrimSpace: (a) => `${__sb}(${__bs}(${a[0]}).trim())`,
|
|
21
|
+
Trim: (a) =>
|
|
22
|
+
`${__sb}(${__bs}(${a[0]}).replace(new RegExp(\`^[\${${a[1]}}]+|[\${${a[1]}}]+$\`, "g"), ""))`,
|
|
23
|
+
Equal: (a) =>
|
|
24
|
+
`((a, b) => { if (a.length !== b.length) return false; for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false; return true; })(${a[0]}, ${a[1]})`,
|
|
25
|
+
Split: (a) => `${__bs}(${a[0]}).split(${__bs}(${a[1]})).map(p => ${__sb}(p))`,
|
|
26
|
+
Join: (a) => `${__sb}(${a[0]}.map(p => ${__bs}(p)).join(${__bs}(${a[1]})))`,
|
|
27
|
+
ReplaceAll: (a) =>
|
|
28
|
+
`((b, o, n) => { const r = []; let i = 0; while (i <= b.length - o.length) { if (o.every((v, j) => b[i + j] === v)) { r.push(...n); i += o.length; } else r.push(b[i++]); } return r.concat(b.slice(i)); })(${a[0]}, ${a[1]}, ${a[2]})`,
|
|
29
|
+
TrimPrefix: (a) =>
|
|
30
|
+
`((b, p) => p.every((v, i) => b[i] === v) ? b.slice(p.length) : b.slice())(${a[0]}, ${a[1]})`,
|
|
31
|
+
TrimSuffix: (a) =>
|
|
32
|
+
`((b, s) => s.length && s.every((v, i) => b[b.length - s.length + i] === v) ? b.slice(0, -s.length) : b.slice())(${a[0]}, ${a[1]})`,
|
|
33
|
+
TrimLeft: (a) =>
|
|
34
|
+
`((b, c) => { let i = 0; while (i < b.length && c.includes(String.fromCharCode(b[i]))) i++; return b.slice(i); })(${a[0]}, ${a[1]})`,
|
|
35
|
+
TrimRight: (a) =>
|
|
36
|
+
`((b, c) => { let i = b.length; while (i > 0 && c.includes(String.fromCharCode(b[i - 1]))) i--; return b.slice(0, i); })(${a[0]}, ${a[1]})`,
|
|
37
|
+
TrimFunc: (a) =>
|
|
38
|
+
`((b, f) => { let l = 0, r = b.length; while (l < r && f(b[l])) l++; while (r > l && f(b[r - 1])) r--; return b.slice(l, r); })(${a[0]}, ${a[1]})`,
|
|
39
|
+
IndexByte: (a) => `(${a[0]}).indexOf(${a[1]})`,
|
|
40
|
+
LastIndex: (a) =>
|
|
41
|
+
`((b, s) => { for (let i = b.length - s.length; i >= 0; i--) if (s.every((v, j) => b[i + j] === v)) return i; return -1; })(${a[0]}, ${a[1]})`,
|
|
42
|
+
LastIndexByte: (a) => `(${a[0]}).lastIndexOf(${a[1]})`,
|
|
43
|
+
Fields: (a) =>
|
|
44
|
+
`((b) => { const s = String.fromCharCode(...b).trim(); return s === '' ? [] : s.split(/\\s+/).map(w => [...w].map(c => c.charCodeAt(0))); })(${a[0]})`,
|
|
45
|
+
Cut: (a) =>
|
|
46
|
+
`((b, sep) => { for (let i = 0; i <= b.length - sep.length; i++) if (sep.every((v, j) => b[i + j] === v)) return [b.slice(0, i), b.slice(i + sep.length), true]; return [b.slice(), [], false]; })(${a[0]}, ${a[1]})`,
|
|
47
|
+
ContainsAny: (a) =>
|
|
48
|
+
`[...(${a[1]})].some(c => (${a[0]}).includes(c.charCodeAt(0)))`,
|
|
49
|
+
ContainsRune: (a) => `(${a[0]}).includes(${a[1]})`,
|
|
50
|
+
Map: (a) => `(${a[1]}).map(v => (${a[0]})(v))`,
|
|
51
|
+
SplitN: (a) =>
|
|
52
|
+
`((b, sep, n) => { if (n === 0) return []; if (n < 0) return ((b, sep) => { const r = []; let i = 0; while (i <= b.length) { const j = b.findIndex((_, k) => k >= i && sep.every((v, l) => b[k + l] === sep[l])); if (j < 0 || j >= b.length) break; r.push(b.slice(i, j)); i = j + sep.length; } r.push(b.slice(i)); return r; })(b, sep); const r = []; let i = 0; for (let c = 1; c < n; c++) { const j = b.findIndex((_, k) => k >= i && sep.every((v, l) => b[k + l] === sep[l])); if (j < 0) break; r.push(b.slice(i, j)); i = j + sep.length; } r.push(b.slice(i)); return r; })(${a[0]}, ${a[1]}, ${a[2]})`,
|
|
53
|
+
NewReader: (a) =>
|
|
54
|
+
`{_src: ${a[0]}, _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[this._pos + i]; this._pos += n; return [n, n === 0 ? "EOF" : null]; }, Len() { return this._src.length - this._pos; }, Reset(b) { this._src = b; this._pos = 0; }}`,
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export const bytesMethods = {
|
|
58
|
+
_genBytes(fn, a) {
|
|
59
|
+
const gen = BYTES_DISPATCH[fn];
|
|
60
|
+
return gen ? gen(a()) : undefined;
|
|
61
|
+
},
|
|
62
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// CodeGen for Go `errors` package.
|
|
2
|
+
|
|
3
|
+
export const errorsMethods = {
|
|
4
|
+
_genErrors(fn, a) {
|
|
5
|
+
const args = a();
|
|
6
|
+
if (fn === "New") {
|
|
7
|
+
this._usesError = true;
|
|
8
|
+
return `__error(${args[0]})`;
|
|
9
|
+
}
|
|
10
|
+
if (fn === "Is") {
|
|
11
|
+
this._usesErrorIs = true;
|
|
12
|
+
return `__errorIs(${args[0]}, ${args[1]})`;
|
|
13
|
+
}
|
|
14
|
+
if (fn === "Unwrap") return `(${args[0]}?._cause ?? null)`;
|
|
15
|
+
return undefined;
|
|
16
|
+
},
|
|
17
|
+
};
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
// CodeGen for Go `fmt` package — installed onto CodeGen.prototype via stdlibGenMethods.
|
|
2
|
+
|
|
3
|
+
const _SSCAN_LOOP =
|
|
4
|
+
`let n = 0; for (let i = 0; i < ptrs.length && i < tokens.length; i++) { ` +
|
|
5
|
+
`const t = tokens[i]; const p = ptrs[i]; ` +
|
|
6
|
+
`if (typeof p.value === "number") p.value = Number(t); ` +
|
|
7
|
+
`else if (typeof p.value === "boolean") p.value = t === "true"; ` +
|
|
8
|
+
`else p.value = t; n++; }`;
|
|
9
|
+
|
|
10
|
+
export const fmtMethods = {
|
|
11
|
+
_genFmt(fn, _a, expr) {
|
|
12
|
+
const fmtArgs = expr.args.map((e) => this.genExpr(e)).join(", ");
|
|
13
|
+
switch (fn) {
|
|
14
|
+
case "Sprintf":
|
|
15
|
+
this._usesSprintf = true;
|
|
16
|
+
return `__sprintf(${fmtArgs})`;
|
|
17
|
+
case "Errorf": {
|
|
18
|
+
this._usesSprintf = true;
|
|
19
|
+
this._usesError = true;
|
|
20
|
+
const fmtStr = expr.args[0];
|
|
21
|
+
if (fmtStr?.kind === "BasicLit" && fmtStr.value?.includes("%w")) {
|
|
22
|
+
const lastArg = this.genExpr(expr.args[expr.args.length - 1]);
|
|
23
|
+
return `__error(__sprintf(${fmtArgs}), ${lastArg})`;
|
|
24
|
+
}
|
|
25
|
+
return `__error(__sprintf(${fmtArgs}))`;
|
|
26
|
+
}
|
|
27
|
+
case "Printf":
|
|
28
|
+
case "Print":
|
|
29
|
+
this._usesSprintf = true;
|
|
30
|
+
return `process?.stdout?.write(__sprintf(${fmtArgs}))`;
|
|
31
|
+
case "Println":
|
|
32
|
+
this._usesSprintf = true;
|
|
33
|
+
return `console.log(__sprintf(${fmtArgs}))`;
|
|
34
|
+
case "Fprintf":
|
|
35
|
+
case "Fprintln":
|
|
36
|
+
case "Fprint":
|
|
37
|
+
return this._genFmtFprint(fn, expr);
|
|
38
|
+
case "Sscan": {
|
|
39
|
+
const strArg = this.genExpr(expr.args[0]);
|
|
40
|
+
const restArgs = expr.args
|
|
41
|
+
.slice(1)
|
|
42
|
+
.map((e) => this.genExpr(e))
|
|
43
|
+
.join(", ");
|
|
44
|
+
return `((str, ...ptrs) => { const tokens = str.trim().split(/\\s+/).filter(Boolean); ${_SSCAN_LOOP} return [n, n < ptrs.length ? "unexpected EOF" : null]; })(${strArg}, ${restArgs})`;
|
|
45
|
+
}
|
|
46
|
+
case "Sscanln": {
|
|
47
|
+
const strArg = this.genExpr(expr.args[0]);
|
|
48
|
+
const restArgs = expr.args
|
|
49
|
+
.slice(1)
|
|
50
|
+
.map((e) => this.genExpr(e))
|
|
51
|
+
.join(", ");
|
|
52
|
+
return `((str, ...ptrs) => { const tokens = str.split("\\n")[0].trim().split(/\\s+/).filter(Boolean); ${_SSCAN_LOOP} return [n, n < ptrs.length ? "unexpected EOF" : null]; })(${strArg}, ${restArgs})`;
|
|
53
|
+
}
|
|
54
|
+
case "Sscanf": {
|
|
55
|
+
const strArg = this.genExpr(expr.args[0]);
|
|
56
|
+
const fmtArg = this.genExpr(expr.args[1]);
|
|
57
|
+
const restArgs = expr.args
|
|
58
|
+
.slice(2)
|
|
59
|
+
.map((e) => this.genExpr(e))
|
|
60
|
+
.join(", ");
|
|
61
|
+
return `((str, _fmt, ...ptrs) => { const tokens = str.trim().split(/\\s+/).filter(Boolean); ${_SSCAN_LOOP} return [n, n < ptrs.length ? "input does not match format" : null]; })(${strArg}, ${fmtArg}, ${restArgs})`;
|
|
62
|
+
}
|
|
63
|
+
default:
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
|
|
68
|
+
_genFmtFprint(fn, expr) {
|
|
69
|
+
this._usesSprintf = true;
|
|
70
|
+
const writerArg = expr.args[0];
|
|
71
|
+
const rest = expr.args.slice(1);
|
|
72
|
+
const restJs = rest.map((e) => this.genExpr(e)).join(", ");
|
|
73
|
+
const writerType = writerArg._type;
|
|
74
|
+
const targetTypeName =
|
|
75
|
+
writerType?.name ??
|
|
76
|
+
(writerType?.kind === "pointer" ? writerType.base?.name : null);
|
|
77
|
+
const sprintfCall = this._buildFprintSprintfCall(fn, rest.length, restJs);
|
|
78
|
+
if (targetTypeName === "strings.Builder") {
|
|
79
|
+
const w = this.genExpr(writerArg);
|
|
80
|
+
const buf =
|
|
81
|
+
writerType?.kind === "pointer" ? `${w}.value._buf` : `${w}._buf`;
|
|
82
|
+
return `(${buf} += ${sprintfCall})`;
|
|
83
|
+
}
|
|
84
|
+
if (targetTypeName === "bytes.Buffer") {
|
|
85
|
+
const w = this.genExpr(writerArg);
|
|
86
|
+
const base = writerType?.kind === "pointer" ? `${w}.value` : w;
|
|
87
|
+
return `((__b,__s)=>{ __b._buf.push(...new TextEncoder().encode(__s)); })(${base}, ${sprintfCall})`;
|
|
88
|
+
}
|
|
89
|
+
// Generic io.Writer fallback — call .WriteString if available
|
|
90
|
+
const w = this.genExpr(writerArg);
|
|
91
|
+
return `${w}.WriteString(${sprintfCall})`;
|
|
92
|
+
},
|
|
93
|
+
|
|
94
|
+
_buildFprintSprintfCall(fn, argc, restJs) {
|
|
95
|
+
if (fn === "Fprintf") return `__sprintf(${restJs})`;
|
|
96
|
+
const sep = fn === "Fprintln" ? " " : "";
|
|
97
|
+
const suffix = fn === "Fprintln" ? "\\n" : "";
|
|
98
|
+
const fmt = Array(argc).fill("%v").join(sep) + suffix;
|
|
99
|
+
return `__sprintf("${fmt}", ${restJs})`;
|
|
100
|
+
},
|
|
101
|
+
};
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
// CodeGen for the GoFront-built-in `gom` DOM-rendering package.
|
|
2
|
+
|
|
3
|
+
const GOM_ELEMENT_TAGS = {
|
|
4
|
+
Div: "div",
|
|
5
|
+
Section: "section",
|
|
6
|
+
Article: "article",
|
|
7
|
+
Aside: "aside",
|
|
8
|
+
Header: "header",
|
|
9
|
+
Footer: "footer",
|
|
10
|
+
Main: "main",
|
|
11
|
+
Nav: "nav",
|
|
12
|
+
Figure: "figure",
|
|
13
|
+
H1: "h1",
|
|
14
|
+
H2: "h2",
|
|
15
|
+
H3: "h3",
|
|
16
|
+
H4: "h4",
|
|
17
|
+
H5: "h5",
|
|
18
|
+
H6: "h6",
|
|
19
|
+
Span: "span",
|
|
20
|
+
A: "a",
|
|
21
|
+
Strong: "strong",
|
|
22
|
+
Em: "em",
|
|
23
|
+
Code: "code",
|
|
24
|
+
Pre: "pre",
|
|
25
|
+
Small: "small",
|
|
26
|
+
Mark: "mark",
|
|
27
|
+
P: "p",
|
|
28
|
+
Br: "br",
|
|
29
|
+
Hr: "hr",
|
|
30
|
+
Ul: "ul",
|
|
31
|
+
Ol: "ol",
|
|
32
|
+
Li: "li",
|
|
33
|
+
Dl: "dl",
|
|
34
|
+
Dt: "dt",
|
|
35
|
+
Dd: "dd",
|
|
36
|
+
Form: "form",
|
|
37
|
+
Input: "input",
|
|
38
|
+
Button: "button",
|
|
39
|
+
Textarea: "textarea",
|
|
40
|
+
Select: "select",
|
|
41
|
+
Option: "option",
|
|
42
|
+
Label: "label",
|
|
43
|
+
Fieldset: "fieldset",
|
|
44
|
+
Legend: "legend",
|
|
45
|
+
Img: "img",
|
|
46
|
+
Video: "video",
|
|
47
|
+
Audio: "audio",
|
|
48
|
+
Canvas: "canvas",
|
|
49
|
+
Table: "table",
|
|
50
|
+
Thead: "thead",
|
|
51
|
+
Tbody: "tbody",
|
|
52
|
+
Tfoot: "tfoot",
|
|
53
|
+
Tr: "tr",
|
|
54
|
+
Th: "th",
|
|
55
|
+
Td: "td",
|
|
56
|
+
};
|
|
57
|
+
const GOM_ATTR_HELPERS = {
|
|
58
|
+
For: "for",
|
|
59
|
+
Name: "name",
|
|
60
|
+
Value: "value",
|
|
61
|
+
Target: "target",
|
|
62
|
+
Rel: "rel",
|
|
63
|
+
Alt: "alt",
|
|
64
|
+
Title: "title",
|
|
65
|
+
Lang: "lang",
|
|
66
|
+
Action: "action",
|
|
67
|
+
Method: "method",
|
|
68
|
+
AutoComplete: "autocomplete",
|
|
69
|
+
Draggable: "draggable",
|
|
70
|
+
Role: "role",
|
|
71
|
+
StyleAttr: "style",
|
|
72
|
+
AriaLabel: "aria-label",
|
|
73
|
+
};
|
|
74
|
+
const GOM_BOOL_ATTRS = {
|
|
75
|
+
Disabled: "disabled",
|
|
76
|
+
Checked: "checked",
|
|
77
|
+
Selected: "selected",
|
|
78
|
+
Readonly: "readonly",
|
|
79
|
+
};
|
|
80
|
+
const GOM_PROP_SETTERS = {
|
|
81
|
+
Class: "className",
|
|
82
|
+
Type: "type",
|
|
83
|
+
Href: "href",
|
|
84
|
+
Src: "src",
|
|
85
|
+
Placeholder: "placeholder",
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
export const gomMethods = {
|
|
89
|
+
_genGom(fn, expr) {
|
|
90
|
+
const a = () =>
|
|
91
|
+
expr.args.map((e) =>
|
|
92
|
+
e._spread ? `...${this.genExpr(e)}` : this.genExpr(e),
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
if (GOM_ELEMENT_TAGS[fn]) {
|
|
96
|
+
const tag = GOM_ELEMENT_TAGS[fn];
|
|
97
|
+
const args = a();
|
|
98
|
+
if (args.length === 0)
|
|
99
|
+
return `(()=>({Mount(p){const e=document.createElement("${tag}");p.appendChild(e);}}))()`;
|
|
100
|
+
return (
|
|
101
|
+
`((...c)=>({Mount(p){const e=document.createElement("${tag}");c.forEach(n=>n?.Mount?.(e));p.appendChild(e);}}))` +
|
|
102
|
+
`(${args.join(",")})`
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (GOM_ATTR_HELPERS[fn]) {
|
|
107
|
+
const [v] = a();
|
|
108
|
+
return `((v)=>({Mount(e){e.setAttribute("${GOM_ATTR_HELPERS[fn]}",v)}}))(${v})`;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (GOM_BOOL_ATTRS[fn])
|
|
112
|
+
return `({Mount(e){e.setAttribute("${GOM_BOOL_ATTRS[fn]}","")}})`;
|
|
113
|
+
|
|
114
|
+
const args = a();
|
|
115
|
+
|
|
116
|
+
if (GOM_PROP_SETTERS[fn])
|
|
117
|
+
return `((v)=>({Mount(e){e.${GOM_PROP_SETTERS[fn]}=v}}))(${args[0]})`;
|
|
118
|
+
|
|
119
|
+
switch (fn) {
|
|
120
|
+
case "El": {
|
|
121
|
+
const [tag, ...children] = args;
|
|
122
|
+
if (children.length === 0)
|
|
123
|
+
return `((t)=>({Mount(p){const e=document.createElement(t);p.appendChild(e);}})) (${tag})`;
|
|
124
|
+
return (
|
|
125
|
+
`((t,...c)=>({Mount(p){const e=document.createElement(t);c.forEach(n=>n?.Mount?.(e));p.appendChild(e);}}))` +
|
|
126
|
+
`(${tag},${children.join(",")})`
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
case "Text": {
|
|
130
|
+
const [s] = args;
|
|
131
|
+
return `((s)=>({Mount(p){p.appendChild(document.createTextNode(s))}}))(${s})`;
|
|
132
|
+
}
|
|
133
|
+
case "Attr": {
|
|
134
|
+
const [name, value] = args;
|
|
135
|
+
return `((n,v)=>({Mount(e){e.setAttribute(n,v)}}))(${name},${value})`;
|
|
136
|
+
}
|
|
137
|
+
case "DataAttr":
|
|
138
|
+
return `((k,v)=>({Mount(e){e.setAttribute("data-"+k,v)}}))(${args[0]},${args[1]})`;
|
|
139
|
+
case "If":
|
|
140
|
+
return `((c,n)=>c?n:{Mount(){}})(${args[0]},${args[1]})`;
|
|
141
|
+
case "Map":
|
|
142
|
+
return `((s,f)=>{const c=s.map(f);return{_items:c,Mount(p){c.forEach(n=>n.Mount(p))}}})(${args[0]},${args[1]})`;
|
|
143
|
+
case "Style":
|
|
144
|
+
return `((s)=>({Mount(p){const e=document.createElement("style");e.textContent=s;p.appendChild(e);}})) (${args[0]})`;
|
|
145
|
+
case "Mount":
|
|
146
|
+
return `((sel,n)=>{const e=document.querySelector(sel);e.innerHTML="";n.Mount(e)})(${args[0]},${args[1]})`;
|
|
147
|
+
case "MountTo":
|
|
148
|
+
return `((sel,n)=>{const e=document.querySelector(sel);n.Mount(e)})(${args[0]},${args[1]})`;
|
|
149
|
+
}
|
|
150
|
+
return undefined;
|
|
151
|
+
},
|
|
152
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// CodeGen for Go `html` package.
|
|
2
|
+
|
|
3
|
+
export const htmlMethods = {
|
|
4
|
+
_genHtml(fn, a) {
|
|
5
|
+
switch (fn) {
|
|
6
|
+
case "EscapeString": {
|
|
7
|
+
const [s] = a();
|
|
8
|
+
return `${s}.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")`;
|
|
9
|
+
}
|
|
10
|
+
case "UnescapeString": {
|
|
11
|
+
const [s] = a();
|
|
12
|
+
return `${s}.replace(/'/g,"'").replace(/"/g,'"').replace(/>/g,">").replace(/</g,"<").replace(/&/g,"&")`;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
return undefined;
|
|
16
|
+
},
|
|
17
|
+
};
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
// CodeGen stdlib call generation — installed as a mixin on CodeGen.prototype.
|
|
2
|
+
// One file per Go package under ./stdlib/. This index merges them.
|
|
3
|
+
|
|
4
|
+
import { builderMethods } from "./builder.js";
|
|
5
|
+
import { bytesMethods } from "./bytes.js";
|
|
6
|
+
import { errorsMethods } from "./errors.js";
|
|
7
|
+
import { fmtMethods } from "./fmt.js";
|
|
8
|
+
import { gomMethods } from "./gom.js";
|
|
9
|
+
import { htmlMethods } from "./html.js";
|
|
10
|
+
import { ioMethods } from "./io.js";
|
|
11
|
+
import { mapsMethods } from "./maps.js";
|
|
12
|
+
import { mathMethods } from "./math.js";
|
|
13
|
+
import { osMethods } from "./os.js";
|
|
14
|
+
import { pathMethods } from "./path.js";
|
|
15
|
+
import { randMethods } from "./rand.js";
|
|
16
|
+
import { regexpMethods } from "./regexp.js";
|
|
17
|
+
import { slicesMethods } from "./slices.js";
|
|
18
|
+
import { sortMethods } from "./sort.js";
|
|
19
|
+
import { strconvMethods } from "./strconv.js";
|
|
20
|
+
import { stringsMethods } from "./strings.js";
|
|
21
|
+
import { timeMethods } from "./time.js";
|
|
22
|
+
import { unicodeMethods } from "./unicode.js";
|
|
23
|
+
import { utf8Methods } from "./utf8.js";
|
|
24
|
+
|
|
25
|
+
// Stdlib namespace → handler method name. Used by _genStdlibCall to dispatch
|
|
26
|
+
// dynamically without creating static call edges.
|
|
27
|
+
const STDLIB_METHOD_MAP = {
|
|
28
|
+
fmt: "_genFmt",
|
|
29
|
+
strings: "_genStrings",
|
|
30
|
+
bytes: "_genBytes",
|
|
31
|
+
strconv: "_genStrconv",
|
|
32
|
+
sort: "_genSort",
|
|
33
|
+
math: "_genMath",
|
|
34
|
+
unicode: "_genUnicode",
|
|
35
|
+
os: "_genOs",
|
|
36
|
+
errors: "_genErrors",
|
|
37
|
+
time: "_genTime",
|
|
38
|
+
regexp: "_genRegexp",
|
|
39
|
+
slices: "_genSlices",
|
|
40
|
+
maps: "_genMaps",
|
|
41
|
+
html: "_genHtml",
|
|
42
|
+
io: "_genIo",
|
|
43
|
+
rand: "_genRand",
|
|
44
|
+
utf8: "_genUtf8",
|
|
45
|
+
path: "_genPath",
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const dispatchMethods = {
|
|
49
|
+
typeComment(typeNode) {
|
|
50
|
+
if (!typeNode) return "unknown";
|
|
51
|
+
switch (typeNode.kind) {
|
|
52
|
+
case "TypeName":
|
|
53
|
+
return typeNode.name;
|
|
54
|
+
case "SliceType":
|
|
55
|
+
return `[]${this.typeComment(typeNode.elem)}`;
|
|
56
|
+
case "MapType":
|
|
57
|
+
return `map[${this.typeComment(typeNode.key)}]${this.typeComment(typeNode.value)}`;
|
|
58
|
+
default:
|
|
59
|
+
return typeNode.kind;
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
|
|
63
|
+
// Dispatch table for stdlib namespace calls (pkg.Func → inline JS).
|
|
64
|
+
// Returns the generated JS string, or undefined if not handled.
|
|
65
|
+
_genStdlibCall(ns, fn, expr) {
|
|
66
|
+
const a = () => expr.args.map((e) => this.genExpr(e));
|
|
67
|
+
if (ns === "gom") return this._genGom(fn, expr);
|
|
68
|
+
const method = STDLIB_METHOD_MAP[ns];
|
|
69
|
+
return method ? this[method](fn, a, expr) : undefined;
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
export const stdlibGenMethods = {
|
|
74
|
+
...builderMethods,
|
|
75
|
+
...bytesMethods,
|
|
76
|
+
...errorsMethods,
|
|
77
|
+
...fmtMethods,
|
|
78
|
+
...gomMethods,
|
|
79
|
+
...htmlMethods,
|
|
80
|
+
...ioMethods,
|
|
81
|
+
...mapsMethods,
|
|
82
|
+
...mathMethods,
|
|
83
|
+
...osMethods,
|
|
84
|
+
...pathMethods,
|
|
85
|
+
...randMethods,
|
|
86
|
+
...regexpMethods,
|
|
87
|
+
...slicesMethods,
|
|
88
|
+
...sortMethods,
|
|
89
|
+
...strconvMethods,
|
|
90
|
+
...stringsMethods,
|
|
91
|
+
...timeMethods,
|
|
92
|
+
...unicodeMethods,
|
|
93
|
+
...utf8Methods,
|
|
94
|
+
...dispatchMethods,
|
|
95
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// CodeGen for Go `io` package.
|
|
2
|
+
|
|
3
|
+
export const ioMethods = {
|
|
4
|
+
_genIo(fn, _a, expr) {
|
|
5
|
+
switch (fn) {
|
|
6
|
+
case "WriteString": {
|
|
7
|
+
const writerArg = expr.args[0];
|
|
8
|
+
const writerType = writerArg._type;
|
|
9
|
+
const typeName =
|
|
10
|
+
writerType?.name ??
|
|
11
|
+
(writerType?.kind === "pointer" ? writerType.base?.name : null);
|
|
12
|
+
const w = this.genExpr(writerArg);
|
|
13
|
+
const s = this.genExpr(expr.args[1]);
|
|
14
|
+
const isPtr = writerType?.kind === "pointer";
|
|
15
|
+
const base = isPtr ? `${w}.value` : w;
|
|
16
|
+
if (typeName === "strings.Builder") {
|
|
17
|
+
return `((b,s) => { b._buf += s; return [s.length, null]; })(${base}, ${s})`;
|
|
18
|
+
}
|
|
19
|
+
if (typeName === "bytes.Buffer") {
|
|
20
|
+
return `((b,s) => { b._buf.push(...new TextEncoder().encode(s)); return [s.length, null]; })(${base}, ${s})`;
|
|
21
|
+
}
|
|
22
|
+
// Generic: auto-dereference pointer, then dispatch on concrete writer type.
|
|
23
|
+
// strings.Builder has { _buf: string }, bytes.Buffer has { _buf: [] }.
|
|
24
|
+
return `((b,s) => { const w = b?.value ?? b; if (typeof w.WriteString === "function") return [w.WriteString(s), null]; if (typeof w._buf === "string") { w._buf += s; return [s.length, null]; } if (Array.isArray(w._buf)) { w._buf.push(...new TextEncoder().encode(s)); return [s.length, null]; } return [0, "io.WriteString: unsupported writer"]; })(${w}, ${s})`;
|
|
25
|
+
}
|
|
26
|
+
case "ReadAll": {
|
|
27
|
+
const r = this.genExpr(expr.args[0]);
|
|
28
|
+
return `((r) => { const w = r?.value ?? r; if (typeof w._src === "string") { const s = w._src.slice(w._pos); w._pos = w._src.length; return [[...s].map(c => c.charCodeAt(0)), null]; } if (Array.isArray(w._src)) { const b = w._src.slice(w._pos); w._pos = w._src.length; return [b, null]; } const chunks = []; const buf = new Array(4096); let err = null; while (!err) { const [n, e] = w.Read(buf); if (n > 0) chunks.push(...buf.slice(0, n)); err = e; } return [chunks, err === "EOF" ? null : err]; })(${r})`;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return undefined;
|
|
32
|
+
},
|
|
33
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// CodeGen for Go `maps` package.
|
|
2
|
+
|
|
3
|
+
export const mapsMethods = {
|
|
4
|
+
_genMaps(fn, a) {
|
|
5
|
+
if (fn === "Equal") {
|
|
6
|
+
this._usesEqual = true;
|
|
7
|
+
const [a1, b1] = a();
|
|
8
|
+
return `__equal(${a1}, ${b1})`;
|
|
9
|
+
}
|
|
10
|
+
switch (fn) {
|
|
11
|
+
case "Keys": {
|
|
12
|
+
const [m] = a();
|
|
13
|
+
return `Object.keys(${m})`;
|
|
14
|
+
}
|
|
15
|
+
case "Values": {
|
|
16
|
+
const [m] = a();
|
|
17
|
+
return `Object.values(${m})`;
|
|
18
|
+
}
|
|
19
|
+
case "Clone": {
|
|
20
|
+
const [m] = a();
|
|
21
|
+
return `({...${m}})`;
|
|
22
|
+
}
|
|
23
|
+
case "Copy": {
|
|
24
|
+
const [dst, src] = a();
|
|
25
|
+
return `Object.assign(${dst}, ${src})`;
|
|
26
|
+
}
|
|
27
|
+
case "EqualFunc": {
|
|
28
|
+
const [m1, m2, eq] = a();
|
|
29
|
+
return `((a, b, f) => { const ka = Object.keys(a); if (ka.length !== Object.keys(b).length) return false; return ka.every(k => k in b && f(a[k], b[k])); })(${m1}, ${m2}, ${eq})`;
|
|
30
|
+
}
|
|
31
|
+
case "Delete": {
|
|
32
|
+
const [m, k] = a();
|
|
33
|
+
return `(delete ${m}[${k}], undefined)`;
|
|
34
|
+
}
|
|
35
|
+
case "DeleteFunc": {
|
|
36
|
+
const [m, fn2] = a();
|
|
37
|
+
return `Object.keys(${m}).forEach(k => { if (${fn2}(k, ${m}[k])) delete ${m}[k]; })`;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return undefined;
|
|
41
|
+
},
|
|
42
|
+
};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// CodeGen for Go `math` package.
|
|
2
|
+
|
|
3
|
+
const MATH1 = {
|
|
4
|
+
Abs: "abs",
|
|
5
|
+
Floor: "floor",
|
|
6
|
+
Ceil: "ceil",
|
|
7
|
+
Round: "round",
|
|
8
|
+
Sqrt: "sqrt",
|
|
9
|
+
Cbrt: "cbrt",
|
|
10
|
+
Log: "log",
|
|
11
|
+
Log2: "log2",
|
|
12
|
+
Log10: "log10",
|
|
13
|
+
Sin: "sin",
|
|
14
|
+
Cos: "cos",
|
|
15
|
+
Tan: "tan",
|
|
16
|
+
Atan: "atan",
|
|
17
|
+
Asin: "asin",
|
|
18
|
+
Acos: "acos",
|
|
19
|
+
Exp: "exp",
|
|
20
|
+
Trunc: "trunc",
|
|
21
|
+
};
|
|
22
|
+
const MATH2 = {
|
|
23
|
+
Pow: "pow",
|
|
24
|
+
Min: "min",
|
|
25
|
+
Max: "max",
|
|
26
|
+
Atan2: "atan2",
|
|
27
|
+
Hypot: "hypot",
|
|
28
|
+
};
|
|
29
|
+
const MATH_EXTRA = {
|
|
30
|
+
Mod: ([x, y]) => `${x} % ${y}`,
|
|
31
|
+
Inf: ([x]) => `(${x} >= 0 ? Infinity : -Infinity)`,
|
|
32
|
+
IsNaN: ([x]) => `Number.isNaN(${x})`,
|
|
33
|
+
IsInf: ([x, y]) =>
|
|
34
|
+
`(${y} > 0 ? ${x} === Infinity : ${y} < 0 ? ${x} === -Infinity : !Number.isFinite(${x}))`,
|
|
35
|
+
NaN: () => "NaN",
|
|
36
|
+
Exp2: ([x]) => `Math.pow(2, ${x})`,
|
|
37
|
+
Signbit: ([x]) => `(${x} < 0 || Object.is(${x}, -0))`,
|
|
38
|
+
Copysign: ([x, y]) =>
|
|
39
|
+
`(Math.abs(${x}) * (${y} < 0 || Object.is(${y}, -0) ? -1 : 1))`,
|
|
40
|
+
Dim: ([x, y]) => `Math.max(${x} - ${y}, 0)`,
|
|
41
|
+
Remainder: ([x, y]) => `(${x} - Math.round(${x} / ${y}) * ${y})`,
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export const mathMethods = {
|
|
45
|
+
_genMath(fn, a) {
|
|
46
|
+
const args = a();
|
|
47
|
+
const [x, y] = args;
|
|
48
|
+
if (MATH1[fn]) return `Math.${MATH1[fn]}(${x})`;
|
|
49
|
+
if (MATH2[fn]) return `Math.${MATH2[fn]}(${x}, ${y})`;
|
|
50
|
+
const gen = MATH_EXTRA[fn];
|
|
51
|
+
return gen ? gen(args) : undefined;
|
|
52
|
+
},
|
|
53
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// CodeGen for Go `path` package.
|
|
2
|
+
|
|
3
|
+
const PATH_DISPATCH = {
|
|
4
|
+
Base: (a) =>
|
|
5
|
+
`((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]})`,
|
|
6
|
+
Dir: (a) =>
|
|
7
|
+
`((p) => { const i = p.lastIndexOf("/"); if (i < 0) return "."; if (i === 0) return "/"; return p.slice(0, i); })(${a[0]})`,
|
|
8
|
+
Ext: (a) =>
|
|
9
|
+
`((p) => { const b = p.slice(p.lastIndexOf("/") + 1); const i = b.lastIndexOf("."); return i <= 0 ? "" : b.slice(i); })(${a[0]})`,
|
|
10
|
+
IsAbs: (a) => `(${a[0]}).startsWith("/")`,
|
|
11
|
+
Split: (a) =>
|
|
12
|
+
`((p) => { const i = p.lastIndexOf("/"); return i < 0 ? ["", p] : [p.slice(0, i + 1), p.slice(i + 1)]; })(${a[0]})`,
|
|
13
|
+
Match: (a) =>
|
|
14
|
+
`((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]})`,
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export const pathMethods = {
|
|
18
|
+
_genPath(fn, a, expr) {
|
|
19
|
+
if (fn === "Join") {
|
|
20
|
+
this._usesPathClean = true;
|
|
21
|
+
const allArgs = expr.args.map((e) => this.genExpr(e)).join(", ");
|
|
22
|
+
return `__pathClean([${allArgs}].filter(x => x !== "").join("/"))`;
|
|
23
|
+
}
|
|
24
|
+
if (fn === "Clean") {
|
|
25
|
+
this._usesPathClean = true;
|
|
26
|
+
return `__pathClean(${a()[0]})`;
|
|
27
|
+
}
|
|
28
|
+
const gen = PATH_DISPATCH[fn];
|
|
29
|
+
return gen ? gen(a()) : undefined;
|
|
30
|
+
},
|
|
31
|
+
};
|