gofront 0.0.5 → 0.0.7
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 +104 -37
- package/package.json +7 -4
- package/src/codegen/expressions.js +589 -7
- package/src/codegen/source-map.js +13 -5
- package/src/codegen/statements.js +30 -26
- package/src/codegen.js +99 -22
- package/src/compiler.js +10 -3
- package/src/dev-server.js +83 -0
- package/src/index.js +35 -5
- package/src/parser.js +0 -10
- package/src/typechecker/expressions.js +38 -9
- package/src/typechecker/stdlib.js +764 -0
- package/src/typechecker/types.js +15 -20
- package/src/typechecker.js +34 -394
|
@@ -147,6 +147,15 @@ export const expressionGenMethods = {
|
|
|
147
147
|
return "Number.MIN_SAFE_INTEGER";
|
|
148
148
|
}
|
|
149
149
|
}
|
|
150
|
+
// io constants
|
|
151
|
+
if (expr.expr.kind === "Ident" && expr.expr.name === "io") {
|
|
152
|
+
switch (expr.field) {
|
|
153
|
+
case "EOF":
|
|
154
|
+
return '"EOF"';
|
|
155
|
+
case "Discard":
|
|
156
|
+
return "{ WriteString(s) { return s.length; }, Write(b) { return b.length; } }";
|
|
157
|
+
}
|
|
158
|
+
}
|
|
150
159
|
// os constants/vars
|
|
151
160
|
if (expr.expr.kind === "Ident" && expr.expr.name === "os") {
|
|
152
161
|
switch (expr.field) {
|
|
@@ -184,7 +193,11 @@ export const expressionGenMethods = {
|
|
|
184
193
|
}
|
|
185
194
|
|
|
186
195
|
case "IndexExpr": {
|
|
187
|
-
const
|
|
196
|
+
const exprType = expr.expr._type;
|
|
197
|
+
const wrapField = this._namedWrapperField(exprType, expr.expr);
|
|
198
|
+
const base = wrapField
|
|
199
|
+
? `${this.genExpr(expr.expr)}.${wrapField}`
|
|
200
|
+
: this.genExpr(expr.expr);
|
|
188
201
|
const idx = this.genExpr(expr.index);
|
|
189
202
|
if (expr._mapValueType && !expr._lvalue) {
|
|
190
203
|
const zero = this.zeroValueForType(expr._mapValueType);
|
|
@@ -196,7 +209,6 @@ export const expressionGenMethods = {
|
|
|
196
209
|
return `(${base}[${idx}] ?? ${zero})`;
|
|
197
210
|
}
|
|
198
211
|
// String indexing returns a byte (charCodeAt), not a JS character
|
|
199
|
-
const exprType = expr.expr._type;
|
|
200
212
|
const isStr =
|
|
201
213
|
(exprType?.kind === "basic" && exprType.name === "string") ||
|
|
202
214
|
(exprType?.kind === "untyped" && exprType.base === "string");
|
|
@@ -212,6 +224,7 @@ export const expressionGenMethods = {
|
|
|
212
224
|
const hi = expr.high ? this.genExpr(expr.high) : "";
|
|
213
225
|
if (!lo && !hi) return `${base}.slice()`;
|
|
214
226
|
if (!hi) return `${base}.slice(${lo})`;
|
|
227
|
+
if (!lo) return `${base}.slice(0, ${hi})`;
|
|
215
228
|
return `${base}.slice(${lo}, ${hi})`;
|
|
216
229
|
}
|
|
217
230
|
|
|
@@ -284,6 +297,10 @@ export const expressionGenMethods = {
|
|
|
284
297
|
this._usesError = true;
|
|
285
298
|
return `__error(${inner})`;
|
|
286
299
|
}
|
|
300
|
+
// Named wrapper type: NodeFunc(fn) → new NodeFunc(fn)
|
|
301
|
+
if (target && this.namedWrapperNames.has(target)) {
|
|
302
|
+
return `new ${target}(${inner})`;
|
|
303
|
+
}
|
|
287
304
|
switch (target) {
|
|
288
305
|
case "string": {
|
|
289
306
|
// Go string(65) → "A" (Unicode code point), not "65"
|
|
@@ -339,6 +356,16 @@ export const expressionGenMethods = {
|
|
|
339
356
|
},
|
|
340
357
|
|
|
341
358
|
genCall(expr) {
|
|
359
|
+
// Named type constructor call: Greeter(fn), NodeFunc(fn) → new Greeter(fn)
|
|
360
|
+
if (expr._isTypeConversion) {
|
|
361
|
+
const name = expr._conversionTargetType?.name;
|
|
362
|
+
const arg = this.genExpr(expr.args[0]);
|
|
363
|
+
if (name && this.namedWrapperNames.has(name)) {
|
|
364
|
+
return `new ${name}(${arg})`;
|
|
365
|
+
}
|
|
366
|
+
return arg; // non-wrapper named types: identity
|
|
367
|
+
}
|
|
368
|
+
|
|
342
369
|
// Unwrap InstantiationExpr for function calls: Foo[int](42) → Foo(42)
|
|
343
370
|
const funcExpr =
|
|
344
371
|
expr.func.kind === "InstantiationExpr" ? expr.func.expr : expr.func;
|
|
@@ -351,6 +378,10 @@ export const expressionGenMethods = {
|
|
|
351
378
|
if (expr._constLen != null) return String(expr._constLen);
|
|
352
379
|
const arg = expr.args[0];
|
|
353
380
|
const t = arg?._type;
|
|
381
|
+
const wrapField = this._namedWrapperField(t, arg);
|
|
382
|
+
if (wrapField) {
|
|
383
|
+
return `${this.genExpr(arg)}.${wrapField}.length`;
|
|
384
|
+
}
|
|
354
385
|
const js = this.genExpr(arg);
|
|
355
386
|
if (t?.kind === "map") return `Object.keys(${js}).length`;
|
|
356
387
|
this._usesLen = true;
|
|
@@ -443,6 +474,19 @@ export const expressionGenMethods = {
|
|
|
443
474
|
if (result !== undefined) return result;
|
|
444
475
|
}
|
|
445
476
|
|
|
477
|
+
// strings.Builder / bytes.Buffer / regexp.Regexp method dispatch
|
|
478
|
+
if (expr.func.kind === "SelectorExpr") {
|
|
479
|
+
const recvType = expr.func.expr._type;
|
|
480
|
+
const recvName = recvType?.name ?? recvType?.base?.name;
|
|
481
|
+
if (recvName === "strings.Builder" || recvName === "bytes.Buffer") {
|
|
482
|
+
const typeName = recvType?.name ?? recvType?.base?.name;
|
|
483
|
+
return this._genBuilderCall(typeName, expr.func.field, expr);
|
|
484
|
+
}
|
|
485
|
+
if (recvName === "regexp.Regexp") {
|
|
486
|
+
return this._genRegexpMethodCall(expr.func.field, expr);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
|
|
446
490
|
// Mark the callee so SelectorExpr knows it's being called, not used as a value
|
|
447
491
|
expr.func._callee = true;
|
|
448
492
|
const rawFn = this.genExpr(expr.func);
|
|
@@ -459,6 +503,18 @@ export const expressionGenMethods = {
|
|
|
459
503
|
},
|
|
460
504
|
|
|
461
505
|
genAppend(expr) {
|
|
506
|
+
const retType = expr._type;
|
|
507
|
+
const wrapField = this._namedWrapperField(retType, expr.args[0]);
|
|
508
|
+
if (wrapField) {
|
|
509
|
+
// Named slice wrapper: append(g, x) → new G(__append(g._items, x))
|
|
510
|
+
const sliceJS = `${this.genExpr(expr.args[0])}.${wrapField}`;
|
|
511
|
+
const elems = expr.args
|
|
512
|
+
.slice(1)
|
|
513
|
+
.map((a) => (a._spread ? `...${this.genExpr(a)}` : this.genExpr(a)));
|
|
514
|
+
if (elems.length === 0) return this.genExpr(expr.args[0]);
|
|
515
|
+
this._usesAppend = true;
|
|
516
|
+
return `new ${retType.name}(__append(${sliceJS}, ${elems.join(", ")}))`;
|
|
517
|
+
}
|
|
462
518
|
const slice = this.genExpr(expr.args[0]);
|
|
463
519
|
const elems = expr.args
|
|
464
520
|
.slice(1)
|
|
@@ -543,6 +599,34 @@ export const expressionGenMethods = {
|
|
|
543
599
|
return `new ${typeName}({ ${this._genStructFields(expr.elems)} })`;
|
|
544
600
|
}
|
|
545
601
|
|
|
602
|
+
// Named wrapper type: Group{a, b} → new Group([a, b])
|
|
603
|
+
if (typeName && this.namedWrapperNames.has(typeName)) {
|
|
604
|
+
const namedType = this.checker?.types.get(typeName);
|
|
605
|
+
const u = namedType?.underlying;
|
|
606
|
+
if (u?.kind === "map") {
|
|
607
|
+
const entries = expr.elems
|
|
608
|
+
.map((e) => {
|
|
609
|
+
if (e.kind === "KeyValueExpr") {
|
|
610
|
+
const k =
|
|
611
|
+
e.key.litKind === "STRING"
|
|
612
|
+
? JSON.stringify(e.key.value)
|
|
613
|
+
: `[${this.genExpr(e.key)}]`;
|
|
614
|
+
return `${k}: ${this.genExpr(e.value)}`;
|
|
615
|
+
}
|
|
616
|
+
return this.genExpr(e);
|
|
617
|
+
})
|
|
618
|
+
.join(", ");
|
|
619
|
+
return `new ${typeName}({ ${entries} })`;
|
|
620
|
+
}
|
|
621
|
+
// Default: slice
|
|
622
|
+
const elems = expr.elems
|
|
623
|
+
.map((e) =>
|
|
624
|
+
e.kind === "KeyValueExpr" ? this.genExpr(e.value) : this.genExpr(e),
|
|
625
|
+
)
|
|
626
|
+
.join(", ");
|
|
627
|
+
return `new ${typeName}([${elems}])`;
|
|
628
|
+
}
|
|
629
|
+
|
|
546
630
|
// Fallback: named type that's not a struct but has positional elements → treat as struct
|
|
547
631
|
if (expr.elems.some((e) => e._positionalField)) {
|
|
548
632
|
if (typeName) {
|
|
@@ -604,7 +688,8 @@ export const expressionGenMethods = {
|
|
|
604
688
|
if (typeNode.kind === "Ident") return typeNode.name;
|
|
605
689
|
if (typeNode.kind === "InstantiationExpr")
|
|
606
690
|
return this.getTypeName(typeNode.expr);
|
|
607
|
-
if (typeNode.kind === "SelectorExpr")
|
|
691
|
+
if (typeNode.kind === "SelectorExpr")
|
|
692
|
+
return `${this.getTypeName(typeNode.expr)}.${typeNode.field}`;
|
|
608
693
|
return null;
|
|
609
694
|
},
|
|
610
695
|
|
|
@@ -662,6 +747,8 @@ export const expressionGenMethods = {
|
|
|
662
747
|
case "TypeName": {
|
|
663
748
|
const basic = this._zeroForBasicName(typeNode.name);
|
|
664
749
|
if (basic !== null) return basic;
|
|
750
|
+
if (typeNode.name === "strings.Builder") return '{ _buf: "" }';
|
|
751
|
+
if (typeNode.name === "bytes.Buffer") return "{ _buf: [] }";
|
|
665
752
|
if (this.structNames.has(typeNode.name))
|
|
666
753
|
return `new ${typeNode.name}()`;
|
|
667
754
|
return "null";
|
|
@@ -706,6 +793,8 @@ export const expressionGenMethods = {
|
|
|
706
793
|
return `{ ${fields} }`;
|
|
707
794
|
}
|
|
708
795
|
case "named":
|
|
796
|
+
if (t.name === "strings.Builder") return '{ _buf: "" }';
|
|
797
|
+
if (t.name === "bytes.Buffer") return "{ _buf: [] }";
|
|
709
798
|
if (this.structNames.has(t.name)) return `new ${t.name}()`;
|
|
710
799
|
return "null";
|
|
711
800
|
default:
|
|
@@ -776,6 +865,294 @@ export const expressionGenMethods = {
|
|
|
776
865
|
return base?.kind === "struct" || base?.kind === "array";
|
|
777
866
|
},
|
|
778
867
|
|
|
868
|
+
_genBuilderCall(typeName, method, expr) {
|
|
869
|
+
// Resolve the receiver — may be a direct var or a &var (pointer)
|
|
870
|
+
const recv = expr.func.expr;
|
|
871
|
+
const isPtr = recv._type?.kind === "pointer";
|
|
872
|
+
const base = isPtr ? `${this.genExpr(recv)}.value` : this.genExpr(recv);
|
|
873
|
+
const args = expr.args.map((a) => this.genExpr(a));
|
|
874
|
+
|
|
875
|
+
if (typeName === "strings.Builder") {
|
|
876
|
+
switch (method) {
|
|
877
|
+
case "WriteString":
|
|
878
|
+
return `(${base}._buf += ${args[0]}, [${args[0]}.length, null])`;
|
|
879
|
+
case "WriteByte":
|
|
880
|
+
case "WriteRune":
|
|
881
|
+
return `(${base}._buf += String.fromCodePoint(${args[0]}))`;
|
|
882
|
+
case "Write":
|
|
883
|
+
return `(${base}._buf += String.fromCharCode(...${args[0]}))`;
|
|
884
|
+
case "String":
|
|
885
|
+
return `${base}._buf`;
|
|
886
|
+
case "Len":
|
|
887
|
+
return `${base}._buf.length`;
|
|
888
|
+
case "Reset":
|
|
889
|
+
return `(${base}._buf = "")`;
|
|
890
|
+
case "Grow":
|
|
891
|
+
return "undefined";
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
if (typeName === "bytes.Buffer") {
|
|
896
|
+
switch (method) {
|
|
897
|
+
case "WriteString":
|
|
898
|
+
return `(${base}._buf.push(...new TextEncoder().encode(${args[0]})), [${args[0]}.length, null])`;
|
|
899
|
+
case "WriteByte":
|
|
900
|
+
return `${base}._buf.push(${args[0]})`;
|
|
901
|
+
case "Write":
|
|
902
|
+
return `(${base}._buf.push(...${args[0]}), [${args[0]}.length, null])`;
|
|
903
|
+
case "String":
|
|
904
|
+
return `new TextDecoder().decode(new Uint8Array(${base}._buf))`;
|
|
905
|
+
case "Bytes":
|
|
906
|
+
return `${base}._buf.slice()`;
|
|
907
|
+
case "Len":
|
|
908
|
+
return `${base}._buf.length`;
|
|
909
|
+
case "Reset":
|
|
910
|
+
return `(${base}._buf = [])`;
|
|
911
|
+
case "Grow":
|
|
912
|
+
return "undefined";
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
return undefined;
|
|
917
|
+
},
|
|
918
|
+
|
|
919
|
+
_genRegexp(fn, a) {
|
|
920
|
+
// Converts a Go regexp pattern string (which may contain inline flags like (?i))
|
|
921
|
+
// into a JS RegExp, extracting the flags into the second constructor argument.
|
|
922
|
+
const makeRegExp = (pat) =>
|
|
923
|
+
`((p) => { const m = /^\\(\\?([gimsuy]+)\\)/.exec(p); return m ? new RegExp(p.slice(m[0].length), m[1]) : new RegExp(p); })(${pat})`;
|
|
924
|
+
switch (fn) {
|
|
925
|
+
case "MustCompile":
|
|
926
|
+
return makeRegExp(a()[0]);
|
|
927
|
+
case "Compile":
|
|
928
|
+
return `[${makeRegExp(a()[0])}, null]`;
|
|
929
|
+
case "MatchString": {
|
|
930
|
+
const [pat, s] = a();
|
|
931
|
+
return `[new RegExp(${pat}).test(${s}), null]`;
|
|
932
|
+
}
|
|
933
|
+
case "QuoteMeta":
|
|
934
|
+
return `${a()[0]}.replace(/[.*+?^\${}()|[\\]\\\\]/g, '\\\\$&')`;
|
|
935
|
+
}
|
|
936
|
+
return undefined;
|
|
937
|
+
},
|
|
938
|
+
|
|
939
|
+
_genRegexpMethodCall(method, expr) {
|
|
940
|
+
const recv = expr.func.expr;
|
|
941
|
+
// *Regexp is a plain JS RegExp — no .value boxing needed
|
|
942
|
+
const re = this.genExpr(recv);
|
|
943
|
+
const args = expr.args.map((a) => this.genExpr(a));
|
|
944
|
+
|
|
945
|
+
switch (method) {
|
|
946
|
+
case "MatchString":
|
|
947
|
+
return `${re}.test(${args[0]})`;
|
|
948
|
+
case "FindString":
|
|
949
|
+
return `(${re}.exec(${args[0]})?.[0] ?? "")`;
|
|
950
|
+
case "FindStringIndex":
|
|
951
|
+
return `((m => m ? [m.index, m.index + m[0].length] : null)(${re}.exec(${args[0]})))`;
|
|
952
|
+
case "FindAllString":
|
|
953
|
+
return `[...${args[0]}.matchAll(new RegExp(${re}.source, ${re}.flags.includes("g") ? ${re}.flags : ${re}.flags + "g"))].slice(0, ${args[1]} < 0 ? undefined : ${args[1]}).map(m => m[0])`;
|
|
954
|
+
case "FindStringSubmatch":
|
|
955
|
+
return `[...(${re}.exec(${args[0]}) ?? [])]`;
|
|
956
|
+
case "FindAllStringSubmatch":
|
|
957
|
+
return `[...${args[0]}.matchAll(new RegExp(${re}.source, ${re}.flags.includes("g") ? ${re}.flags : ${re}.flags + "g"))].slice(0, ${args[1]} < 0 ? undefined : ${args[1]}).map(m => [...m])`;
|
|
958
|
+
case "ReplaceAllString":
|
|
959
|
+
return `${args[0]}.replaceAll(new RegExp(${re}.source, ${re}.flags.includes("g") ? ${re}.flags : ${re}.flags + "g"), ${args[1]})`;
|
|
960
|
+
case "ReplaceAllLiteralString":
|
|
961
|
+
return `${args[0]}.replaceAll(new RegExp(${re}.source, ${re}.flags.includes("g") ? ${re}.flags : ${re}.flags + "g"), ${args[1]}.replace(/\\$/g, '$$$$'))`;
|
|
962
|
+
case "Split":
|
|
963
|
+
return `${args[0]}.split(${re}).slice(0, ${args[1]} < 0 ? undefined : ${args[1]})`;
|
|
964
|
+
case "String":
|
|
965
|
+
return `${re}.source`;
|
|
966
|
+
}
|
|
967
|
+
return undefined;
|
|
968
|
+
},
|
|
969
|
+
|
|
970
|
+
_genSlices(fn, a) {
|
|
971
|
+
switch (fn) {
|
|
972
|
+
case "Contains": {
|
|
973
|
+
const [s, v] = a();
|
|
974
|
+
return `${s}.includes(${v})`;
|
|
975
|
+
}
|
|
976
|
+
case "Index": {
|
|
977
|
+
const [s, v] = a();
|
|
978
|
+
return `${s}.indexOf(${v})`;
|
|
979
|
+
}
|
|
980
|
+
case "Equal": {
|
|
981
|
+
this._usesEqual = true;
|
|
982
|
+
const [a1, b1] = a();
|
|
983
|
+
return `__equal(${a1}, ${b1})`;
|
|
984
|
+
}
|
|
985
|
+
case "Compare": {
|
|
986
|
+
const [a1, b1] = a();
|
|
987
|
+
return `((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; })(${a1}, ${b1})`;
|
|
988
|
+
}
|
|
989
|
+
case "Sort": {
|
|
990
|
+
const [s] = a();
|
|
991
|
+
return `${s}.sort((a, b) => a < b ? -1 : a > b ? 1 : 0)`;
|
|
992
|
+
}
|
|
993
|
+
case "SortFunc": {
|
|
994
|
+
const [s, cmp] = a();
|
|
995
|
+
return `${s}.sort(${cmp})`;
|
|
996
|
+
}
|
|
997
|
+
case "SortStableFunc": {
|
|
998
|
+
const [s, cmp] = a();
|
|
999
|
+
return `${s}.sort(${cmp})`;
|
|
1000
|
+
}
|
|
1001
|
+
case "IsSorted": {
|
|
1002
|
+
const [s] = a();
|
|
1003
|
+
return `((s) => { for (let i = 1; i < s.length; i++) { if (s[i] < s[i-1]) return false; } return true; })(${s})`;
|
|
1004
|
+
}
|
|
1005
|
+
case "IsSortedFunc": {
|
|
1006
|
+
const [s, cmp] = a();
|
|
1007
|
+
return `((s, f) => { for (let i = 1; i < s.length; i++) { if (f(s[i], s[i-1]) < 0) return false; } return true; })(${s}, ${cmp})`;
|
|
1008
|
+
}
|
|
1009
|
+
case "Reverse": {
|
|
1010
|
+
const [s] = a();
|
|
1011
|
+
return `${s}.reverse()`;
|
|
1012
|
+
}
|
|
1013
|
+
case "Max": {
|
|
1014
|
+
const [s] = a();
|
|
1015
|
+
return `Math.max(...${s})`;
|
|
1016
|
+
}
|
|
1017
|
+
case "Min": {
|
|
1018
|
+
const [s] = a();
|
|
1019
|
+
return `Math.min(...${s})`;
|
|
1020
|
+
}
|
|
1021
|
+
case "MaxFunc": {
|
|
1022
|
+
const [s, cmp] = a();
|
|
1023
|
+
return `((s, f) => s.reduce((m, x) => f(x, m) > 0 ? x : m))(${s}, ${cmp})`;
|
|
1024
|
+
}
|
|
1025
|
+
case "MinFunc": {
|
|
1026
|
+
const [s, cmp] = a();
|
|
1027
|
+
return `((s, f) => s.reduce((m, x) => f(x, m) < 0 ? x : m))(${s}, ${cmp})`;
|
|
1028
|
+
}
|
|
1029
|
+
case "Clone": {
|
|
1030
|
+
const [s] = a();
|
|
1031
|
+
return `${s}.slice()`;
|
|
1032
|
+
}
|
|
1033
|
+
case "Compact": {
|
|
1034
|
+
const [s] = a();
|
|
1035
|
+
return `((s) => s.filter((v, i) => i === 0 || v !== s[i-1]))(${s})`;
|
|
1036
|
+
}
|
|
1037
|
+
case "CompactFunc": {
|
|
1038
|
+
const [s, eq] = a();
|
|
1039
|
+
return `((s, f) => s.filter((v, i) => i === 0 || !f(v, s[i-1])))(${s}, ${eq})`;
|
|
1040
|
+
}
|
|
1041
|
+
case "Concat": {
|
|
1042
|
+
const args = a();
|
|
1043
|
+
return `[].concat(${args.join(", ")})`;
|
|
1044
|
+
}
|
|
1045
|
+
case "Delete": {
|
|
1046
|
+
const [s, i, j] = a();
|
|
1047
|
+
return `[...${s}.slice(0, ${i}), ...${s}.slice(${j})]`;
|
|
1048
|
+
}
|
|
1049
|
+
case "DeleteFunc": {
|
|
1050
|
+
const [s, fn2] = a();
|
|
1051
|
+
return `${s}.filter((v) => !${fn2}(v))`;
|
|
1052
|
+
}
|
|
1053
|
+
case "Insert": {
|
|
1054
|
+
const args = a();
|
|
1055
|
+
const s = args[0];
|
|
1056
|
+
const i = args[1];
|
|
1057
|
+
const vs = args.slice(2);
|
|
1058
|
+
return `[...${s}.slice(0, ${i}), ${vs.join(", ")}, ...${s}.slice(${i})]`;
|
|
1059
|
+
}
|
|
1060
|
+
case "Replace": {
|
|
1061
|
+
const args = a();
|
|
1062
|
+
const s = args[0];
|
|
1063
|
+
const i = args[1];
|
|
1064
|
+
const j = args[2];
|
|
1065
|
+
const vs = args.slice(3);
|
|
1066
|
+
return `[...${s}.slice(0, ${i}), ${vs.join(", ")}, ...${s}.slice(${j})]`;
|
|
1067
|
+
}
|
|
1068
|
+
case "Grow":
|
|
1069
|
+
case "Clip": {
|
|
1070
|
+
const [s] = a();
|
|
1071
|
+
return `${s}.slice()`;
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
return undefined;
|
|
1075
|
+
},
|
|
1076
|
+
|
|
1077
|
+
_genIo(fn, _a, expr) {
|
|
1078
|
+
switch (fn) {
|
|
1079
|
+
case "WriteString": {
|
|
1080
|
+
const writerArg = expr.args[0];
|
|
1081
|
+
const writerType = writerArg._type;
|
|
1082
|
+
const typeName =
|
|
1083
|
+
writerType?.name ??
|
|
1084
|
+
(writerType?.kind === "pointer" ? writerType.base?.name : null);
|
|
1085
|
+
const w = this.genExpr(writerArg);
|
|
1086
|
+
const s = this.genExpr(expr.args[1]);
|
|
1087
|
+
const isPtr = writerType?.kind === "pointer";
|
|
1088
|
+
const base = isPtr ? `${w}.value` : w;
|
|
1089
|
+
if (typeName === "strings.Builder") {
|
|
1090
|
+
return `((b,s) => { b._buf += s; return [s.length, null]; })(${base}, ${s})`;
|
|
1091
|
+
}
|
|
1092
|
+
if (typeName === "bytes.Buffer") {
|
|
1093
|
+
return `((b,s) => { b._buf.push(...new TextEncoder().encode(s)); return [s.length, null]; })(${base}, ${s})`;
|
|
1094
|
+
}
|
|
1095
|
+
// Generic: auto-dereference pointer, then dispatch on concrete writer type.
|
|
1096
|
+
// strings.Builder has { _buf: string }, bytes.Buffer has { _buf: [] }.
|
|
1097
|
+
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})`;
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
return undefined;
|
|
1101
|
+
},
|
|
1102
|
+
|
|
1103
|
+
_genHtml(fn, a) {
|
|
1104
|
+
switch (fn) {
|
|
1105
|
+
case "EscapeString": {
|
|
1106
|
+
const [s] = a();
|
|
1107
|
+
return `${s}.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")`;
|
|
1108
|
+
}
|
|
1109
|
+
case "UnescapeString": {
|
|
1110
|
+
const [s] = a();
|
|
1111
|
+
return `${s}.replace(/'/g,"'").replace(/"/g,'"').replace(/>/g,">").replace(/</g,"<").replace(/&/g,"&")`;
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
return undefined;
|
|
1115
|
+
},
|
|
1116
|
+
|
|
1117
|
+
_genMaps(fn, a) {
|
|
1118
|
+
switch (fn) {
|
|
1119
|
+
case "Keys": {
|
|
1120
|
+
const [m] = a();
|
|
1121
|
+
return `Object.keys(${m})`;
|
|
1122
|
+
}
|
|
1123
|
+
case "Values": {
|
|
1124
|
+
const [m] = a();
|
|
1125
|
+
return `Object.values(${m})`;
|
|
1126
|
+
}
|
|
1127
|
+
case "Clone": {
|
|
1128
|
+
const [m] = a();
|
|
1129
|
+
return `({...${m}})`;
|
|
1130
|
+
}
|
|
1131
|
+
case "Copy": {
|
|
1132
|
+
const [dst, src] = a();
|
|
1133
|
+
return `Object.assign(${dst}, ${src})`;
|
|
1134
|
+
}
|
|
1135
|
+
case "Equal": {
|
|
1136
|
+
this._usesEqual = true;
|
|
1137
|
+
const [a1, b1] = a();
|
|
1138
|
+
return `__equal(${a1}, ${b1})`;
|
|
1139
|
+
}
|
|
1140
|
+
case "EqualFunc": {
|
|
1141
|
+
const [m1, m2, eq] = a();
|
|
1142
|
+
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})`;
|
|
1143
|
+
}
|
|
1144
|
+
case "Delete": {
|
|
1145
|
+
const [m, k] = a();
|
|
1146
|
+
return `(delete ${m}[${k}], undefined)`;
|
|
1147
|
+
}
|
|
1148
|
+
case "DeleteFunc": {
|
|
1149
|
+
const [m, fn2] = a();
|
|
1150
|
+
return `Object.keys(${m}).forEach(k => { if (${fn2}(k, ${m}[k])) delete ${m}[k]; })`;
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
return undefined;
|
|
1154
|
+
},
|
|
1155
|
+
|
|
779
1156
|
typeComment(typeNode) {
|
|
780
1157
|
if (!typeNode) return "unknown";
|
|
781
1158
|
switch (typeNode.kind) {
|
|
@@ -815,11 +1192,170 @@ export const expressionGenMethods = {
|
|
|
815
1192
|
return this._genErrors(fn, a);
|
|
816
1193
|
case "time":
|
|
817
1194
|
return this._genTime(fn, a);
|
|
1195
|
+
case "regexp":
|
|
1196
|
+
return this._genRegexp(fn, a);
|
|
1197
|
+
case "slices":
|
|
1198
|
+
return this._genSlices(fn, a);
|
|
1199
|
+
case "maps":
|
|
1200
|
+
return this._genMaps(fn, a);
|
|
1201
|
+
case "html":
|
|
1202
|
+
return this._genHtml(fn, a);
|
|
1203
|
+
case "io":
|
|
1204
|
+
return this._genIo(fn, a, expr);
|
|
1205
|
+
case "gom":
|
|
1206
|
+
return this._genGom(fn, expr);
|
|
818
1207
|
default:
|
|
819
1208
|
return undefined;
|
|
820
1209
|
}
|
|
821
1210
|
},
|
|
822
1211
|
|
|
1212
|
+
_genGom(fn, expr) {
|
|
1213
|
+
const a = () =>
|
|
1214
|
+
expr.args.map((e) =>
|
|
1215
|
+
e._spread ? `...${this.genExpr(e)}` : this.genExpr(e),
|
|
1216
|
+
);
|
|
1217
|
+
const elementTags = {
|
|
1218
|
+
Div: "div",
|
|
1219
|
+
Section: "section",
|
|
1220
|
+
Article: "article",
|
|
1221
|
+
Aside: "aside",
|
|
1222
|
+
Header: "header",
|
|
1223
|
+
Footer: "footer",
|
|
1224
|
+
Main: "main",
|
|
1225
|
+
Nav: "nav",
|
|
1226
|
+
Figure: "figure",
|
|
1227
|
+
H1: "h1",
|
|
1228
|
+
H2: "h2",
|
|
1229
|
+
H3: "h3",
|
|
1230
|
+
H4: "h4",
|
|
1231
|
+
H5: "h5",
|
|
1232
|
+
H6: "h6",
|
|
1233
|
+
Span: "span",
|
|
1234
|
+
A: "a",
|
|
1235
|
+
Strong: "strong",
|
|
1236
|
+
Em: "em",
|
|
1237
|
+
Code: "code",
|
|
1238
|
+
Pre: "pre",
|
|
1239
|
+
Small: "small",
|
|
1240
|
+
Mark: "mark",
|
|
1241
|
+
P: "p",
|
|
1242
|
+
Br: "br",
|
|
1243
|
+
Hr: "hr",
|
|
1244
|
+
Ul: "ul",
|
|
1245
|
+
Ol: "ol",
|
|
1246
|
+
Li: "li",
|
|
1247
|
+
Dl: "dl",
|
|
1248
|
+
Dt: "dt",
|
|
1249
|
+
Dd: "dd",
|
|
1250
|
+
Form: "form",
|
|
1251
|
+
Input: "input",
|
|
1252
|
+
Button: "button",
|
|
1253
|
+
Textarea: "textarea",
|
|
1254
|
+
Select: "select",
|
|
1255
|
+
Option: "option",
|
|
1256
|
+
Label: "label",
|
|
1257
|
+
Fieldset: "fieldset",
|
|
1258
|
+
Legend: "legend",
|
|
1259
|
+
Img: "img",
|
|
1260
|
+
Video: "video",
|
|
1261
|
+
Audio: "audio",
|
|
1262
|
+
Canvas: "canvas",
|
|
1263
|
+
Table: "table",
|
|
1264
|
+
Thead: "thead",
|
|
1265
|
+
Tbody: "tbody",
|
|
1266
|
+
Tfoot: "tfoot",
|
|
1267
|
+
Tr: "tr",
|
|
1268
|
+
Th: "th",
|
|
1269
|
+
Td: "td",
|
|
1270
|
+
};
|
|
1271
|
+
if (elementTags[fn]) {
|
|
1272
|
+
const tag = elementTags[fn];
|
|
1273
|
+
const args = a();
|
|
1274
|
+
if (args.length === 0)
|
|
1275
|
+
return `(()=>({Mount(p){const e=document.createElement("${tag}");p.appendChild(e);}}))()`;
|
|
1276
|
+
return (
|
|
1277
|
+
`((...c)=>({Mount(p){const e=document.createElement("${tag}");c.forEach(n=>n?.Mount?.(e));p.appendChild(e);}}))` +
|
|
1278
|
+
`(${args.join(",")})`
|
|
1279
|
+
);
|
|
1280
|
+
}
|
|
1281
|
+
const attrHelpers = {
|
|
1282
|
+
For: "for",
|
|
1283
|
+
Name: "name",
|
|
1284
|
+
Value: "value",
|
|
1285
|
+
Target: "target",
|
|
1286
|
+
Rel: "rel",
|
|
1287
|
+
Alt: "alt",
|
|
1288
|
+
Title: "title",
|
|
1289
|
+
Lang: "lang",
|
|
1290
|
+
Action: "action",
|
|
1291
|
+
Method: "method",
|
|
1292
|
+
AutoComplete: "autocomplete",
|
|
1293
|
+
Draggable: "draggable",
|
|
1294
|
+
Role: "role",
|
|
1295
|
+
StyleAttr: "style",
|
|
1296
|
+
};
|
|
1297
|
+
if (attrHelpers[fn]) {
|
|
1298
|
+
const [v] = a();
|
|
1299
|
+
return `((v)=>({Mount(e){e.setAttribute("${attrHelpers[fn]}",v)}}))(${v})`;
|
|
1300
|
+
}
|
|
1301
|
+
if (fn === "AriaLabel") {
|
|
1302
|
+
const [v] = a();
|
|
1303
|
+
return `((v)=>({Mount(e){e.setAttribute("aria-label",v)}}))(${v})`;
|
|
1304
|
+
}
|
|
1305
|
+
const boolAttrs = {
|
|
1306
|
+
Disabled: "disabled",
|
|
1307
|
+
Checked: "checked",
|
|
1308
|
+
Selected: "selected",
|
|
1309
|
+
Readonly: "readonly",
|
|
1310
|
+
};
|
|
1311
|
+
if (boolAttrs[fn])
|
|
1312
|
+
return `({Mount(e){e.setAttribute("${boolAttrs[fn]}","")}})`;
|
|
1313
|
+
|
|
1314
|
+
const args = a();
|
|
1315
|
+
switch (fn) {
|
|
1316
|
+
case "El": {
|
|
1317
|
+
const [tag, ...children] = args;
|
|
1318
|
+
if (children.length === 0)
|
|
1319
|
+
return `((t)=>({Mount(p){const e=document.createElement(t);p.appendChild(e);}})) (${tag})`;
|
|
1320
|
+
return (
|
|
1321
|
+
`((t,...c)=>({Mount(p){const e=document.createElement(t);c.forEach(n=>n?.Mount?.(e));p.appendChild(e);}}))` +
|
|
1322
|
+
`(${tag},${children.join(",")})`
|
|
1323
|
+
);
|
|
1324
|
+
}
|
|
1325
|
+
case "Text": {
|
|
1326
|
+
const [s] = args;
|
|
1327
|
+
return `((s)=>({Mount(p){p.appendChild(document.createTextNode(s))}}))(${s})`;
|
|
1328
|
+
}
|
|
1329
|
+
case "Attr": {
|
|
1330
|
+
const [name, value] = args;
|
|
1331
|
+
return `((n,v)=>({Mount(e){e.setAttribute(n,v)}}))(${name},${value})`;
|
|
1332
|
+
}
|
|
1333
|
+
case "Class":
|
|
1334
|
+
return `((v)=>({Mount(e){e.className=v}}))(${args[0]})`;
|
|
1335
|
+
case "Type":
|
|
1336
|
+
return `((v)=>({Mount(e){e.type=v}}))(${args[0]})`;
|
|
1337
|
+
case "Href":
|
|
1338
|
+
return `((v)=>({Mount(e){e.href=v}}))(${args[0]})`;
|
|
1339
|
+
case "Src":
|
|
1340
|
+
return `((v)=>({Mount(e){e.src=v}}))(${args[0]})`;
|
|
1341
|
+
case "Placeholder":
|
|
1342
|
+
return `((v)=>({Mount(e){e.placeholder=v}}))(${args[0]})`;
|
|
1343
|
+
case "DataAttr":
|
|
1344
|
+
return `((k,v)=>({Mount(e){e.setAttribute("data-"+k,v)}}))(${args[0]},${args[1]})`;
|
|
1345
|
+
case "If":
|
|
1346
|
+
return `((c,n)=>c?n:{Mount(){}})(${args[0]},${args[1]})`;
|
|
1347
|
+
case "Map":
|
|
1348
|
+
return `((s,f)=>{const c=s.map(f);return{_items:c,Mount(p){c.forEach(n=>n.Mount(p))}}})(${args[0]},${args[1]})`;
|
|
1349
|
+
case "Style":
|
|
1350
|
+
return `((s)=>({Mount(p){const e=document.createElement("style");e.textContent=s;p.appendChild(e);}})) (${args[0]})`;
|
|
1351
|
+
case "Mount":
|
|
1352
|
+
return `((sel,n)=>{const e=document.querySelector(sel);e.innerHTML="";n.Mount(e)})(${args[0]},${args[1]})`;
|
|
1353
|
+
case "MountTo":
|
|
1354
|
+
return `((sel,n)=>{const e=document.querySelector(sel);n.Mount(e)})(${args[0]},${args[1]})`;
|
|
1355
|
+
}
|
|
1356
|
+
return undefined;
|
|
1357
|
+
},
|
|
1358
|
+
|
|
823
1359
|
_genFmt(fn, _a, expr) {
|
|
824
1360
|
const fmtArgs = expr.args.map((e) => this.genExpr(e)).join(", ");
|
|
825
1361
|
switch (fn) {
|
|
@@ -843,6 +1379,52 @@ export const expressionGenMethods = {
|
|
|
843
1379
|
case "Println":
|
|
844
1380
|
this._usesSprintf = true;
|
|
845
1381
|
return `console.log(__sprintf(${fmtArgs}))`;
|
|
1382
|
+
case "Fprintf":
|
|
1383
|
+
case "Fprintln":
|
|
1384
|
+
case "Fprint": {
|
|
1385
|
+
this._usesSprintf = true;
|
|
1386
|
+
const writerArg = expr.args[0];
|
|
1387
|
+
const rest = expr.args.slice(1);
|
|
1388
|
+
const restJs = rest.map((e) => this.genExpr(e)).join(", ");
|
|
1389
|
+
// Resolve the writer to its underlying buffer expression
|
|
1390
|
+
const writerType = writerArg._type;
|
|
1391
|
+
const targetTypeName =
|
|
1392
|
+
writerType?.name ??
|
|
1393
|
+
(writerType?.kind === "pointer" ? writerType.base?.name : null);
|
|
1394
|
+
let buf;
|
|
1395
|
+
if (targetTypeName === "strings.Builder") {
|
|
1396
|
+
const w = this.genExpr(writerArg);
|
|
1397
|
+
buf =
|
|
1398
|
+
writerType?.kind === "pointer" ? `${w}.value._buf` : `${w}._buf`;
|
|
1399
|
+
} else if (targetTypeName === "bytes.Buffer") {
|
|
1400
|
+
const w = this.genExpr(writerArg);
|
|
1401
|
+
const base = writerType?.kind === "pointer" ? `${w}.value` : w;
|
|
1402
|
+
buf = null;
|
|
1403
|
+
if (fn === "Fprintf") {
|
|
1404
|
+
return `((__b,__s)=>{ __b._buf.push(...new TextEncoder().encode(__s)); })(${base}, __sprintf(${restJs}))`;
|
|
1405
|
+
}
|
|
1406
|
+
const valJs =
|
|
1407
|
+
fn === "Fprintln"
|
|
1408
|
+
? `__sprintf("%v\\n", ${restJs})`
|
|
1409
|
+
: `__sprintf("%v", ${restJs})`;
|
|
1410
|
+
return `((__b,__s)=>{ __b._buf.push(...new TextEncoder().encode(__s)); })(${base}, ${valJs})`;
|
|
1411
|
+
} else {
|
|
1412
|
+
// Generic io.Writer fallback — call .WriteString if available
|
|
1413
|
+
const w = this.genExpr(writerArg);
|
|
1414
|
+
const formatted =
|
|
1415
|
+
fn === "Fprintf"
|
|
1416
|
+
? `__sprintf(${restJs})`
|
|
1417
|
+
: fn === "Fprintln"
|
|
1418
|
+
? `__sprintf("%v\\n", ${restJs})`
|
|
1419
|
+
: `__sprintf("%v", ${restJs})`;
|
|
1420
|
+
return `${w}.WriteString(${formatted})`;
|
|
1421
|
+
}
|
|
1422
|
+
// strings.Builder path
|
|
1423
|
+
if (fn === "Fprintf") return `(${buf} += __sprintf(${restJs}))`;
|
|
1424
|
+
if (fn === "Fprintln")
|
|
1425
|
+
return `(${buf} += __sprintf("%v\\n", ${restJs}))`;
|
|
1426
|
+
return `(${buf} += __sprintf("%v", ${restJs}))`;
|
|
1427
|
+
}
|
|
846
1428
|
default:
|
|
847
1429
|
return undefined;
|
|
848
1430
|
}
|
|
@@ -862,7 +1444,7 @@ export const expressionGenMethods = {
|
|
|
862
1444
|
case "LastIndex":
|
|
863
1445
|
return `${args[0]}.lastIndexOf(${args[1]})`;
|
|
864
1446
|
case "Count":
|
|
865
|
-
return
|
|
1447
|
+
return `((s, sep) => sep === "" ? s.length + 1 : s.split(sep).length - 1)(${args[0]}, ${args[1]})`;
|
|
866
1448
|
case "Repeat":
|
|
867
1449
|
return `${args[0]}.repeat(${args[1]})`;
|
|
868
1450
|
case "Replace":
|
|
@@ -878,9 +1460,9 @@ export const expressionGenMethods = {
|
|
|
878
1460
|
case "Trim":
|
|
879
1461
|
return `${args[0]}.replace(new RegExp(\`^[\${${args[1]}}]+|[\${${args[1]}}]+$\`, "g"), "")`;
|
|
880
1462
|
case "TrimPrefix":
|
|
881
|
-
return `(
|
|
1463
|
+
return `((s, pre) => s.startsWith(pre) ? s.slice(pre.length) : s)(${args[0]}, ${args[1]})`;
|
|
882
1464
|
case "TrimSuffix":
|
|
883
|
-
return `(
|
|
1465
|
+
return `((s, suf) => !suf.length || !s.endsWith(suf) ? s : s.slice(0, -suf.length))(${args[0]}, ${args[1]})`;
|
|
884
1466
|
case "TrimLeft":
|
|
885
1467
|
return `${args[0]}.replace(new RegExp(\`^[\${${args[1]}}]+\`), "")`;
|
|
886
1468
|
case "TrimRight":
|
|
@@ -910,7 +1492,7 @@ export const expressionGenMethods = {
|
|
|
910
1492
|
case "Index":
|
|
911
1493
|
return `${__bs}(${args[0]}).indexOf(${__bs}(${args[1]}))`;
|
|
912
1494
|
case "Count":
|
|
913
|
-
return
|
|
1495
|
+
return `((b, sep) => sep.length === 0 ? b.length + 1 : ${__bs}(b).split(${__bs}(sep)).length - 1)(${args[0]}, ${args[1]})`;
|
|
914
1496
|
case "Repeat":
|
|
915
1497
|
return `${__sb}(${__bs}(${args[0]}).repeat(${args[1]}))`;
|
|
916
1498
|
case "Replace":
|