gofront 0.0.6 → 0.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,8 +2,6 @@
2
2
 
3
3
  import { ERROR, isComplex } from "../typechecker/types.js";
4
4
 
5
- const globalError = ERROR;
6
-
7
5
  const INT_TYPE_NAMES = new Set([
8
6
  "int",
9
7
  "uint",
@@ -147,6 +145,15 @@ export const expressionGenMethods = {
147
145
  return "Number.MIN_SAFE_INTEGER";
148
146
  }
149
147
  }
148
+ // io constants
149
+ if (expr.expr.kind === "Ident" && expr.expr.name === "io") {
150
+ switch (expr.field) {
151
+ case "EOF":
152
+ return '"EOF"';
153
+ case "Discard":
154
+ return "{ WriteString(s) { return s.length; }, Write(b) { return b.length; } }";
155
+ }
156
+ }
150
157
  // os constants/vars
151
158
  if (expr.expr.kind === "Ident" && expr.expr.name === "os") {
152
159
  switch (expr.field) {
@@ -154,7 +161,7 @@ export const expressionGenMethods = {
154
161
  return "process.argv";
155
162
  }
156
163
  }
157
- // time constants (nanosecond durations)
164
+ // time constants (nanosecond durations and layout strings)
158
165
  if (expr.expr.kind === "Ident" && expr.expr.name === "time") {
159
166
  switch (expr.field) {
160
167
  case "Millisecond":
@@ -165,6 +172,68 @@ export const expressionGenMethods = {
165
172
  return "60000000000";
166
173
  case "Hour":
167
174
  return "3600000000000";
175
+ case "RFC3339":
176
+ return '"2006-01-02T15:04:05Z07:00"';
177
+ case "RFC3339Nano":
178
+ return '"2006-01-02T15:04:05.999999999Z07:00"';
179
+ case "DateOnly":
180
+ return '"2006-01-02"';
181
+ case "TimeOnly":
182
+ return '"15:04:05"';
183
+ case "DateTime":
184
+ return '"2006-01-02 15:04:05"';
185
+ case "UTC":
186
+ case "Local":
187
+ return "null";
188
+ case "January":
189
+ return "1";
190
+ case "February":
191
+ return "2";
192
+ case "March":
193
+ return "3";
194
+ case "April":
195
+ return "4";
196
+ case "May":
197
+ return "5";
198
+ case "June":
199
+ return "6";
200
+ case "July":
201
+ return "7";
202
+ case "August":
203
+ return "8";
204
+ case "September":
205
+ return "9";
206
+ case "October":
207
+ return "10";
208
+ case "November":
209
+ return "11";
210
+ case "December":
211
+ return "12";
212
+ case "Sunday":
213
+ return "0";
214
+ case "Monday":
215
+ return "1";
216
+ case "Tuesday":
217
+ return "2";
218
+ case "Wednesday":
219
+ return "3";
220
+ case "Thursday":
221
+ return "4";
222
+ case "Friday":
223
+ return "5";
224
+ case "Saturday":
225
+ return "6";
226
+ }
227
+ }
228
+ // utf8 constants
229
+ if (expr.expr.kind === "Ident" && expr.expr.name === "utf8") {
230
+ switch (expr.field) {
231
+ case "RuneError":
232
+ return "0xFFFD";
233
+ case "MaxRune":
234
+ return "0x10FFFF";
235
+ case "UTFMax":
236
+ return "4";
168
237
  }
169
238
  }
170
239
  const base = this.genExpr(expr.expr);
@@ -184,7 +253,11 @@ export const expressionGenMethods = {
184
253
  }
185
254
 
186
255
  case "IndexExpr": {
187
- const base = this.genExpr(expr.expr);
256
+ const exprType = expr.expr._type;
257
+ const wrapField = this._namedWrapperField(exprType, expr.expr);
258
+ const base = wrapField
259
+ ? `${this.genExpr(expr.expr)}.${wrapField}`
260
+ : this.genExpr(expr.expr);
188
261
  const idx = this.genExpr(expr.index);
189
262
  if (expr._mapValueType && !expr._lvalue) {
190
263
  const zero = this.zeroValueForType(expr._mapValueType);
@@ -196,7 +269,6 @@ export const expressionGenMethods = {
196
269
  return `(${base}[${idx}] ?? ${zero})`;
197
270
  }
198
271
  // String indexing returns a byte (charCodeAt), not a JS character
199
- const exprType = expr.expr._type;
200
272
  const isStr =
201
273
  (exprType?.kind === "basic" && exprType.name === "string") ||
202
274
  (exprType?.kind === "untyped" && exprType.base === "string");
@@ -212,6 +284,7 @@ export const expressionGenMethods = {
212
284
  const hi = expr.high ? this.genExpr(expr.high) : "";
213
285
  if (!lo && !hi) return `${base}.slice()`;
214
286
  if (!hi) return `${base}.slice(${lo})`;
287
+ if (!lo) return `${base}.slice(0, ${hi})`;
215
288
  return `${base}.slice(${lo}, ${hi})`;
216
289
  }
217
290
 
@@ -284,6 +357,10 @@ export const expressionGenMethods = {
284
357
  this._usesError = true;
285
358
  return `__error(${inner})`;
286
359
  }
360
+ // Named wrapper type: NodeFunc(fn) → new NodeFunc(fn)
361
+ if (target && this.namedWrapperNames.has(target)) {
362
+ return `new ${target}(${inner})`;
363
+ }
287
364
  switch (target) {
288
365
  case "string": {
289
366
  // Go string(65) → "A" (Unicode code point), not "65"
@@ -339,6 +416,16 @@ export const expressionGenMethods = {
339
416
  },
340
417
 
341
418
  genCall(expr) {
419
+ // Named type constructor call: Greeter(fn), NodeFunc(fn) → new Greeter(fn)
420
+ if (expr._isTypeConversion) {
421
+ const name = expr._conversionTargetType?.name;
422
+ const arg = this.genExpr(expr.args[0]);
423
+ if (name && this.namedWrapperNames.has(name)) {
424
+ return `new ${name}(${arg})`;
425
+ }
426
+ return arg; // non-wrapper named types: identity
427
+ }
428
+
342
429
  // Unwrap InstantiationExpr for function calls: Foo[int](42) → Foo(42)
343
430
  const funcExpr =
344
431
  expr.func.kind === "InstantiationExpr" ? expr.func.expr : expr.func;
@@ -351,6 +438,10 @@ export const expressionGenMethods = {
351
438
  if (expr._constLen != null) return String(expr._constLen);
352
439
  const arg = expr.args[0];
353
440
  const t = arg?._type;
441
+ const wrapField = this._namedWrapperField(t, arg);
442
+ if (wrapField) {
443
+ return `${this.genExpr(arg)}.${wrapField}.length`;
444
+ }
354
445
  const js = this.genExpr(arg);
355
446
  if (t?.kind === "map") return `Object.keys(${js}).length`;
356
447
  this._usesLen = true;
@@ -425,7 +516,7 @@ export const expressionGenMethods = {
425
516
  expr.func.kind === "SelectorExpr" &&
426
517
  expr.func.field === "Error" &&
427
518
  expr.func.expr._type &&
428
- (expr.func.expr._type === globalError ||
519
+ (expr.func.expr._type === ERROR ||
429
520
  expr.func.expr._type?.name === "error" ||
430
521
  (expr.func.expr._type?.kind === "named" &&
431
522
  expr.func.expr._type?.underlying?.kind === "interface"))
@@ -454,6 +545,9 @@ export const expressionGenMethods = {
454
545
  if (recvName === "regexp.Regexp") {
455
546
  return this._genRegexpMethodCall(expr.func.field, expr);
456
547
  }
548
+ if (recvName === "time.Time") {
549
+ return this._genTimeMethodCall(expr.func.field, expr);
550
+ }
457
551
  }
458
552
 
459
553
  // Mark the callee so SelectorExpr knows it's being called, not used as a value
@@ -472,6 +566,18 @@ export const expressionGenMethods = {
472
566
  },
473
567
 
474
568
  genAppend(expr) {
569
+ const retType = expr._type;
570
+ const wrapField = this._namedWrapperField(retType, expr.args[0]);
571
+ if (wrapField) {
572
+ // Named slice wrapper: append(g, x) → new G(__append(g._items, x))
573
+ const sliceJS = `${this.genExpr(expr.args[0])}.${wrapField}`;
574
+ const elems = expr.args
575
+ .slice(1)
576
+ .map((a) => (a._spread ? `...${this.genExpr(a)}` : this.genExpr(a)));
577
+ if (elems.length === 0) return this.genExpr(expr.args[0]);
578
+ this._usesAppend = true;
579
+ return `new ${retType.name}(__append(${sliceJS}, ${elems.join(", ")}))`;
580
+ }
475
581
  const slice = this.genExpr(expr.args[0]);
476
582
  const elems = expr.args
477
583
  .slice(1)
@@ -556,6 +662,34 @@ export const expressionGenMethods = {
556
662
  return `new ${typeName}({ ${this._genStructFields(expr.elems)} })`;
557
663
  }
558
664
 
665
+ // Named wrapper type: Group{a, b} → new Group([a, b])
666
+ if (typeName && this.namedWrapperNames.has(typeName)) {
667
+ const namedType = this.checker?.types.get(typeName);
668
+ const u = namedType?.underlying;
669
+ if (u?.kind === "map") {
670
+ const entries = expr.elems
671
+ .map((e) => {
672
+ if (e.kind === "KeyValueExpr") {
673
+ const k =
674
+ e.key.litKind === "STRING"
675
+ ? JSON.stringify(e.key.value)
676
+ : `[${this.genExpr(e.key)}]`;
677
+ return `${k}: ${this.genExpr(e.value)}`;
678
+ }
679
+ return this.genExpr(e);
680
+ })
681
+ .join(", ");
682
+ return `new ${typeName}({ ${entries} })`;
683
+ }
684
+ // Default: slice
685
+ const elems = expr.elems
686
+ .map((e) =>
687
+ e.kind === "KeyValueExpr" ? this.genExpr(e.value) : this.genExpr(e),
688
+ )
689
+ .join(", ");
690
+ return `new ${typeName}([${elems}])`;
691
+ }
692
+
559
693
  // Fallback: named type that's not a struct but has positional elements → treat as struct
560
694
  if (expr.elems.some((e) => e._positionalField)) {
561
695
  if (typeName) {
@@ -617,7 +751,8 @@ export const expressionGenMethods = {
617
751
  if (typeNode.kind === "Ident") return typeNode.name;
618
752
  if (typeNode.kind === "InstantiationExpr")
619
753
  return this.getTypeName(typeNode.expr);
620
- if (typeNode.kind === "SelectorExpr") return typeNode.field;
754
+ if (typeNode.kind === "SelectorExpr")
755
+ return `${this.getTypeName(typeNode.expr)}.${typeNode.field}`;
621
756
  return null;
622
757
  },
623
758
 
@@ -1002,6 +1137,36 @@ export const expressionGenMethods = {
1002
1137
  return undefined;
1003
1138
  },
1004
1139
 
1140
+ _genIo(fn, _a, expr) {
1141
+ switch (fn) {
1142
+ case "WriteString": {
1143
+ const writerArg = expr.args[0];
1144
+ const writerType = writerArg._type;
1145
+ const typeName =
1146
+ writerType?.name ??
1147
+ (writerType?.kind === "pointer" ? writerType.base?.name : null);
1148
+ const w = this.genExpr(writerArg);
1149
+ const s = this.genExpr(expr.args[1]);
1150
+ const isPtr = writerType?.kind === "pointer";
1151
+ const base = isPtr ? `${w}.value` : w;
1152
+ if (typeName === "strings.Builder") {
1153
+ return `((b,s) => { b._buf += s; return [s.length, null]; })(${base}, ${s})`;
1154
+ }
1155
+ if (typeName === "bytes.Buffer") {
1156
+ return `((b,s) => { b._buf.push(...new TextEncoder().encode(s)); return [s.length, null]; })(${base}, ${s})`;
1157
+ }
1158
+ // Generic: auto-dereference pointer, then dispatch on concrete writer type.
1159
+ // strings.Builder has { _buf: string }, bytes.Buffer has { _buf: [] }.
1160
+ 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})`;
1161
+ }
1162
+ case "ReadAll": {
1163
+ const r = this.genExpr(expr.args[0]);
1164
+ 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})`;
1165
+ }
1166
+ }
1167
+ return undefined;
1168
+ },
1169
+
1005
1170
  _genHtml(fn, a) {
1006
1171
  switch (fn) {
1007
1172
  case "EscapeString": {
@@ -1102,11 +1267,168 @@ export const expressionGenMethods = {
1102
1267
  return this._genMaps(fn, a);
1103
1268
  case "html":
1104
1269
  return this._genHtml(fn, a);
1270
+ case "io":
1271
+ return this._genIo(fn, a, expr);
1272
+ case "rand":
1273
+ return this._genRand(fn, a);
1274
+ case "utf8":
1275
+ return this._genUtf8(fn, a);
1276
+ case "path":
1277
+ return this._genPath(fn, a, expr);
1278
+ case "gom":
1279
+ return this._genGom(fn, expr);
1105
1280
  default:
1106
1281
  return undefined;
1107
1282
  }
1108
1283
  },
1109
1284
 
1285
+ _genGom(fn, expr) {
1286
+ const a = () =>
1287
+ expr.args.map((e) =>
1288
+ e._spread ? `...${this.genExpr(e)}` : this.genExpr(e),
1289
+ );
1290
+ const elementTags = {
1291
+ Div: "div",
1292
+ Section: "section",
1293
+ Article: "article",
1294
+ Aside: "aside",
1295
+ Header: "header",
1296
+ Footer: "footer",
1297
+ Main: "main",
1298
+ Nav: "nav",
1299
+ Figure: "figure",
1300
+ H1: "h1",
1301
+ H2: "h2",
1302
+ H3: "h3",
1303
+ H4: "h4",
1304
+ H5: "h5",
1305
+ H6: "h6",
1306
+ Span: "span",
1307
+ A: "a",
1308
+ Strong: "strong",
1309
+ Em: "em",
1310
+ Code: "code",
1311
+ Pre: "pre",
1312
+ Small: "small",
1313
+ Mark: "mark",
1314
+ P: "p",
1315
+ Br: "br",
1316
+ Hr: "hr",
1317
+ Ul: "ul",
1318
+ Ol: "ol",
1319
+ Li: "li",
1320
+ Dl: "dl",
1321
+ Dt: "dt",
1322
+ Dd: "dd",
1323
+ Form: "form",
1324
+ Input: "input",
1325
+ Button: "button",
1326
+ Textarea: "textarea",
1327
+ Select: "select",
1328
+ Option: "option",
1329
+ Label: "label",
1330
+ Fieldset: "fieldset",
1331
+ Legend: "legend",
1332
+ Img: "img",
1333
+ Video: "video",
1334
+ Audio: "audio",
1335
+ Canvas: "canvas",
1336
+ Table: "table",
1337
+ Thead: "thead",
1338
+ Tbody: "tbody",
1339
+ Tfoot: "tfoot",
1340
+ Tr: "tr",
1341
+ Th: "th",
1342
+ Td: "td",
1343
+ };
1344
+ if (elementTags[fn]) {
1345
+ const tag = elementTags[fn];
1346
+ const args = a();
1347
+ if (args.length === 0)
1348
+ return `(()=>({Mount(p){const e=document.createElement("${tag}");p.appendChild(e);}}))()`;
1349
+ return (
1350
+ `((...c)=>({Mount(p){const e=document.createElement("${tag}");c.forEach(n=>n?.Mount?.(e));p.appendChild(e);}}))` +
1351
+ `(${args.join(",")})`
1352
+ );
1353
+ }
1354
+ const attrHelpers = {
1355
+ For: "for",
1356
+ Name: "name",
1357
+ Value: "value",
1358
+ Target: "target",
1359
+ Rel: "rel",
1360
+ Alt: "alt",
1361
+ Title: "title",
1362
+ Lang: "lang",
1363
+ Action: "action",
1364
+ Method: "method",
1365
+ AutoComplete: "autocomplete",
1366
+ Draggable: "draggable",
1367
+ Role: "role",
1368
+ StyleAttr: "style",
1369
+ };
1370
+ if (attrHelpers[fn]) {
1371
+ const [v] = a();
1372
+ return `((v)=>({Mount(e){e.setAttribute("${attrHelpers[fn]}",v)}}))(${v})`;
1373
+ }
1374
+ if (fn === "AriaLabel") {
1375
+ const [v] = a();
1376
+ return `((v)=>({Mount(e){e.setAttribute("aria-label",v)}}))(${v})`;
1377
+ }
1378
+ const boolAttrs = {
1379
+ Disabled: "disabled",
1380
+ Checked: "checked",
1381
+ Selected: "selected",
1382
+ Readonly: "readonly",
1383
+ };
1384
+ if (boolAttrs[fn])
1385
+ return `({Mount(e){e.setAttribute("${boolAttrs[fn]}","")}})`;
1386
+
1387
+ const args = a();
1388
+ switch (fn) {
1389
+ case "El": {
1390
+ const [tag, ...children] = args;
1391
+ if (children.length === 0)
1392
+ return `((t)=>({Mount(p){const e=document.createElement(t);p.appendChild(e);}})) (${tag})`;
1393
+ return (
1394
+ `((t,...c)=>({Mount(p){const e=document.createElement(t);c.forEach(n=>n?.Mount?.(e));p.appendChild(e);}}))` +
1395
+ `(${tag},${children.join(",")})`
1396
+ );
1397
+ }
1398
+ case "Text": {
1399
+ const [s] = args;
1400
+ return `((s)=>({Mount(p){p.appendChild(document.createTextNode(s))}}))(${s})`;
1401
+ }
1402
+ case "Attr": {
1403
+ const [name, value] = args;
1404
+ return `((n,v)=>({Mount(e){e.setAttribute(n,v)}}))(${name},${value})`;
1405
+ }
1406
+ case "Class":
1407
+ return `((v)=>({Mount(e){e.className=v}}))(${args[0]})`;
1408
+ case "Type":
1409
+ return `((v)=>({Mount(e){e.type=v}}))(${args[0]})`;
1410
+ case "Href":
1411
+ return `((v)=>({Mount(e){e.href=v}}))(${args[0]})`;
1412
+ case "Src":
1413
+ return `((v)=>({Mount(e){e.src=v}}))(${args[0]})`;
1414
+ case "Placeholder":
1415
+ return `((v)=>({Mount(e){e.placeholder=v}}))(${args[0]})`;
1416
+ case "DataAttr":
1417
+ return `((k,v)=>({Mount(e){e.setAttribute("data-"+k,v)}}))(${args[0]},${args[1]})`;
1418
+ case "If":
1419
+ return `((c,n)=>c?n:{Mount(){}})(${args[0]},${args[1]})`;
1420
+ case "Map":
1421
+ return `((s,f)=>{const c=s.map(f);return{_items:c,Mount(p){c.forEach(n=>n.Mount(p))}}})(${args[0]},${args[1]})`;
1422
+ case "Style":
1423
+ return `((s)=>({Mount(p){const e=document.createElement("style");e.textContent=s;p.appendChild(e);}})) (${args[0]})`;
1424
+ case "Mount":
1425
+ return `((sel,n)=>{const e=document.querySelector(sel);e.innerHTML="";n.Mount(e)})(${args[0]},${args[1]})`;
1426
+ case "MountTo":
1427
+ return `((sel,n)=>{const e=document.querySelector(sel);n.Mount(e)})(${args[0]},${args[1]})`;
1428
+ }
1429
+ return undefined;
1430
+ },
1431
+
1110
1432
  _genFmt(fn, _a, expr) {
1111
1433
  const fmtArgs = expr.args.map((e) => this.genExpr(e)).join(", ");
1112
1434
  switch (fn) {
@@ -1176,6 +1498,31 @@ export const expressionGenMethods = {
1176
1498
  return `(${buf} += __sprintf("%v\\n", ${restJs}))`;
1177
1499
  return `(${buf} += __sprintf("%v", ${restJs}))`;
1178
1500
  }
1501
+ case "Sscan": {
1502
+ const strArg = this.genExpr(expr.args[0]);
1503
+ const restArgs = expr.args
1504
+ .slice(1)
1505
+ .map((e) => this.genExpr(e))
1506
+ .join(", ");
1507
+ return `((str, ...ptrs) => { const tokens = str.trim().split(/\\s+/).filter(Boolean); let n = 0; for (let i = 0; i < ptrs.length && i < tokens.length; i++) { const t = tokens[i]; const p = ptrs[i]; if (typeof p.value === "number") p.value = Number(t); else if (typeof p.value === "boolean") p.value = t === "true"; else p.value = t; n++; } return [n, n < ptrs.length ? "unexpected EOF" : null]; })(${strArg}, ${restArgs})`;
1508
+ }
1509
+ case "Sscanln": {
1510
+ const strArg = this.genExpr(expr.args[0]);
1511
+ const restArgs = expr.args
1512
+ .slice(1)
1513
+ .map((e) => this.genExpr(e))
1514
+ .join(", ");
1515
+ return `((str, ...ptrs) => { const line = str.split("\\n")[0]; const tokens = line.trim().split(/\\s+/).filter(Boolean); let n = 0; for (let i = 0; i < ptrs.length && i < tokens.length; i++) { const t = tokens[i]; const p = ptrs[i]; if (typeof p.value === "number") p.value = Number(t); else if (typeof p.value === "boolean") p.value = t === "true"; else p.value = t; n++; } return [n, n < ptrs.length ? "unexpected EOF" : null]; })(${strArg}, ${restArgs})`;
1516
+ }
1517
+ case "Sscanf": {
1518
+ const strArg = this.genExpr(expr.args[0]);
1519
+ const fmtArg = this.genExpr(expr.args[1]);
1520
+ const restArgs = expr.args
1521
+ .slice(2)
1522
+ .map((e) => this.genExpr(e))
1523
+ .join(", ");
1524
+ return `((str, _fmt, ...ptrs) => { const tokens = str.trim().split(/\\s+/).filter(Boolean); let n = 0; for (let i = 0; i < ptrs.length && i < tokens.length; i++) { const t = tokens[i]; const p = ptrs[i]; if (typeof p.value === "number") p.value = Number(t); else if (typeof p.value === "boolean") p.value = t === "true"; else p.value = t; n++; } return [n, n < ptrs.length ? "input does not match format" : null]; })(${strArg}, ${fmtArg}, ${restArgs})`;
1525
+ }
1179
1526
  default:
1180
1527
  return undefined;
1181
1528
  }
@@ -1224,6 +1571,56 @@ export const expressionGenMethods = {
1224
1571
  return `${args[0]}.join(${args[1]})`;
1225
1572
  case "EqualFold":
1226
1573
  return `${args[0]}.toLowerCase() === ${args[1]}.toLowerCase()`;
1574
+ case "Fields":
1575
+ return `(${args[0]}).trim() === '' ? [] : (${args[0]}).trim().split(/\\s+/)`;
1576
+ case "Cut":
1577
+ return `((s, sep) => { const i = s.indexOf(sep); return i < 0 ? [s, "", false] : [s.slice(0, i), s.slice(i + sep.length), true]; })(${args[0]}, ${args[1]})`;
1578
+ case "CutPrefix":
1579
+ return `(${args[0]}).startsWith(${args[1]}) ? [(${args[0]}).slice((${args[1]}).length), true] : [${args[0]}, false]`;
1580
+ case "CutSuffix":
1581
+ return `(${args[0]}).endsWith(${args[1]}) ? [(${args[0]}).slice(0, -(${args[1]}).length), true] : [${args[0]}, false]`;
1582
+ case "SplitN":
1583
+ return `((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; })(${args[0]}, ${args[1]}, ${args[2]})`;
1584
+ case "SplitAfter":
1585
+ 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; })(${args[0]}, ${args[1]})`;
1586
+ case "SplitAfterN":
1587
+ return `((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; })(${args[0]}, ${args[1]}, ${args[2]})`;
1588
+ case "IndexAny":
1589
+ return `((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; })(${args[0]}, ${args[1]})`;
1590
+ case "LastIndexAny":
1591
+ return `((s, chars) => { let m = -1; for (const c of chars) { const i = s.lastIndexOf(c); if (i > m) m = i; } return m; })(${args[0]}, ${args[1]})`;
1592
+ case "ContainsAny":
1593
+ return `[...(${args[1]})].some(c => (${args[0]}).includes(c))`;
1594
+ case "ContainsRune":
1595
+ return `(${args[0]}).includes(String.fromCodePoint(${args[1]}))`;
1596
+ case "IndexRune":
1597
+ return `(${args[0]}).indexOf(String.fromCodePoint(${args[1]}))`;
1598
+ case "IndexByte":
1599
+ return `(${args[0]}).indexOf(String.fromCharCode(${args[1]}))`;
1600
+ case "LastIndexByte":
1601
+ return `(${args[0]}).lastIndexOf(String.fromCharCode(${args[1]}))`;
1602
+ case "Map":
1603
+ return `[...(${args[1]})].map(c => String.fromCodePoint((${args[0]})(c.codePointAt(0)))).join("")`;
1604
+ case "Title":
1605
+ return `(${args[0]}).replace(/\\b\\w/g, c => c.toUpperCase())`;
1606
+ case "ToTitle":
1607
+ return `(${args[0]}).toUpperCase()`;
1608
+ case "TrimFunc":
1609
+ return `((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); })(${args[0]}, ${args[1]})`;
1610
+ case "TrimLeftFunc":
1611
+ return `((s, f) => { let l = 0; while (l < s.length && f(s.codePointAt(l))) l++; return s.slice(l); })(${args[0]}, ${args[1]})`;
1612
+ case "TrimRightFunc":
1613
+ return `((s, f) => { let r = s.length; while (r > 0 && f(s.codePointAt(r - 1))) r--; return s.slice(0, r); })(${args[0]}, ${args[1]})`;
1614
+ case "IndexFunc":
1615
+ return `((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; })(${args[0]}, ${args[1]})`;
1616
+ case "LastIndexFunc":
1617
+ return `((s, f) => { for (let i = s.length - 1; i >= 0; i--) { const cp = s.codePointAt(i); if (f(cp)) return i; } return -1; })(${args[0]}, ${args[1]})`;
1618
+ case "NewReader":
1619
+ return `{_src: ${args[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.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; }}`;
1620
+ case "NewReplacer": {
1621
+ const allArgs = args.join(", ");
1622
+ return `((...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; } }; })(${allArgs})`;
1623
+ }
1227
1624
  default:
1228
1625
  return undefined;
1229
1626
  }
@@ -1262,6 +1659,38 @@ export const expressionGenMethods = {
1262
1659
  return `${__bs}(${args[0]}).split(${__bs}(${args[1]})).map(p => ${__sb}(p))`;
1263
1660
  case "Join":
1264
1661
  return `${__sb}(${args[0]}.map(p => ${__bs}(p)).join(${__bs}(${args[1]})))`;
1662
+ case "ReplaceAll":
1663
+ return `((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)); })(${args[0]}, ${args[1]}, ${args[2]})`;
1664
+ case "TrimPrefix":
1665
+ return `((b, p) => p.every((v, i) => b[i] === v) ? b.slice(p.length) : b.slice())(${args[0]}, ${args[1]})`;
1666
+ case "TrimSuffix":
1667
+ return `((b, s) => s.length && s.every((v, i) => b[b.length - s.length + i] === v) ? b.slice(0, -s.length) : b.slice())(${args[0]}, ${args[1]})`;
1668
+ case "TrimLeft":
1669
+ return `((b, c) => { let i = 0; while (i < b.length && c.includes(String.fromCharCode(b[i]))) i++; return b.slice(i); })(${args[0]}, ${args[1]})`;
1670
+ case "TrimRight":
1671
+ return `((b, c) => { let i = b.length; while (i > 0 && c.includes(String.fromCharCode(b[i - 1]))) i--; return b.slice(0, i); })(${args[0]}, ${args[1]})`;
1672
+ case "TrimFunc":
1673
+ return `((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); })(${args[0]}, ${args[1]})`;
1674
+ case "IndexByte":
1675
+ return `(${args[0]}).indexOf(${args[1]})`;
1676
+ case "LastIndex":
1677
+ return `((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; })(${args[0]}, ${args[1]})`;
1678
+ case "LastIndexByte":
1679
+ return `(${args[0]}).lastIndexOf(${args[1]})`;
1680
+ case "Fields":
1681
+ return `((b) => { const s = String.fromCharCode(...b).trim(); return s === '' ? [] : s.split(/\\s+/).map(w => [...w].map(c => c.charCodeAt(0))); })(${args[0]})`;
1682
+ case "Cut":
1683
+ return `((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]; })(${args[0]}, ${args[1]})`;
1684
+ case "ContainsAny":
1685
+ return `[...(${args[1]})].some(c => (${args[0]}).includes(c.charCodeAt(0)))`;
1686
+ case "ContainsRune":
1687
+ return `(${args[0]}).includes(${args[1]})`;
1688
+ case "Map":
1689
+ return `(${args[1]}).map(v => (${args[0]})(v))`;
1690
+ case "SplitN":
1691
+ return `((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; })(${args[0]}, ${args[1]}, ${args[2]})`;
1692
+ case "NewReader":
1693
+ return `{_src: ${args[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; }}`;
1265
1694
  default:
1266
1695
  return undefined;
1267
1696
  }
@@ -1286,6 +1715,14 @@ export const expressionGenMethods = {
1286
1715
  return `(Number.isNaN(parseInt(${args[0]}, ${args[1]} || 10)) ? [0, "invalid syntax"] : [parseInt(${args[0]}, ${args[1]} || 10), null])`;
1287
1716
  case "ParseBool":
1288
1717
  return `(${args[0]} === "true" || ${args[0]} === "1" ? [true, null] : ${args[0]} === "false" || ${args[0]} === "0" ? [false, null] : [false, "invalid syntax"])`;
1718
+ case "Quote":
1719
+ return `JSON.stringify(${args[0]})`;
1720
+ case "Unquote":
1721
+ return `((s) => { try { const v = JSON.parse(s); return [v, null]; } catch(e) { return ["", "invalid syntax"]; } })(${args[0]})`;
1722
+ case "AppendInt":
1723
+ return `[...(${args[0]}), ...new TextEncoder().encode((${args[1]}).toString(${args[2]}))]`;
1724
+ case "AppendFloat":
1725
+ return `[...(${args[0]}), ...new TextEncoder().encode(${args[1]}.toFixed(${args[3]} < 0 ? 6 : ${args[3]}))]`;
1289
1726
  default:
1290
1727
  return undefined;
1291
1728
  }
@@ -1304,6 +1741,12 @@ export const expressionGenMethods = {
1304
1741
  return `${args[0]}.sort((a, b) => ${args[1]}(a, b) ? -1 : ${args[1]}(b, a) ? 1 : 0)`;
1305
1742
  case "SliceIsSorted":
1306
1743
  return `${args[0]}.every((v, i, a) => i === 0 || ${args[1]}(a[i - 1], v))`;
1744
+ case "Search":
1745
+ return `((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; })(${args[0]}, ${args[1]})`;
1746
+ case "IntsAreSorted":
1747
+ case "Float64sAreSorted":
1748
+ case "StringsAreSorted":
1749
+ return `(${args[0]}).every((v, i, a) => i === 0 || a[i - 1] <= v)`;
1307
1750
  default:
1308
1751
  return undefined;
1309
1752
  }
@@ -1352,6 +1795,30 @@ export const expressionGenMethods = {
1352
1795
  return `(${args[1]} > 0 ? ${args[0]} === Infinity : ${args[1]} < 0 ? ${args[0]} === -Infinity : !Number.isFinite(${args[0]}))`;
1353
1796
  case "NaN":
1354
1797
  return "NaN";
1798
+ case "Atan":
1799
+ return `Math.atan(${args[0]})`;
1800
+ case "Atan2":
1801
+ return `Math.atan2(${args[0]}, ${args[1]})`;
1802
+ case "Asin":
1803
+ return `Math.asin(${args[0]})`;
1804
+ case "Acos":
1805
+ return `Math.acos(${args[0]})`;
1806
+ case "Exp":
1807
+ return `Math.exp(${args[0]})`;
1808
+ case "Exp2":
1809
+ return `Math.pow(2, ${args[0]})`;
1810
+ case "Trunc":
1811
+ return `Math.trunc(${args[0]})`;
1812
+ case "Hypot":
1813
+ return `Math.hypot(${args[0]}, ${args[1]})`;
1814
+ case "Signbit":
1815
+ return `(${args[0]} < 0 || Object.is(${args[0]}, -0))`;
1816
+ case "Copysign":
1817
+ return `(Math.abs(${args[0]}) * (${args[1]} < 0 || Object.is(${args[1]}, -0) ? -1 : 1))`;
1818
+ case "Dim":
1819
+ return `Math.max(${args[0]} - ${args[1]}, 0)`;
1820
+ case "Remainder":
1821
+ return `(${args[0]} - Math.round(${args[0]} / ${args[1]}) * ${args[1]})`;
1355
1822
  default:
1356
1823
  return undefined;
1357
1824
  }
@@ -1416,15 +1883,142 @@ export const expressionGenMethods = {
1416
1883
  }
1417
1884
  },
1418
1885
 
1886
+ _genTimeMethodCall(method, expr) {
1887
+ const recv = expr.func.expr;
1888
+ const recvJs = this.genExpr(recv);
1889
+ const args = expr.args.map((a) => this.genExpr(a));
1890
+ switch (method) {
1891
+ case "Format":
1892
+ this._usesTimeFmt = true;
1893
+ return `__timeFmt(${recvJs}._d, ${args[0]})`;
1894
+ case "String":
1895
+ this._usesTimeFmt = true;
1896
+ return `__timeFmt(${recvJs}._d, "2006-01-02T15:04:05Z07:00")`;
1897
+ case "Year":
1898
+ return `${recvJs}._d.getFullYear()`;
1899
+ case "Month":
1900
+ return `${recvJs}._d.getMonth() + 1`;
1901
+ case "Day":
1902
+ return `${recvJs}._d.getDate()`;
1903
+ case "Hour":
1904
+ return `${recvJs}._d.getHours()`;
1905
+ case "Minute":
1906
+ return `${recvJs}._d.getMinutes()`;
1907
+ case "Second":
1908
+ return `${recvJs}._d.getSeconds()`;
1909
+ case "Weekday":
1910
+ return `${recvJs}._d.getDay()`;
1911
+ case "Unix":
1912
+ return `Math.floor(${recvJs}._d.getTime() / 1000)`;
1913
+ case "UnixMilli":
1914
+ return `${recvJs}._d.getTime()`;
1915
+ case "Add":
1916
+ return `{_d: new Date(${recvJs}._d.getTime() + (${args[0]}))}`;
1917
+ case "Sub":
1918
+ return `${recvJs}._d.getTime() - (${args[0]})._d.getTime()`;
1919
+ case "Before":
1920
+ return `${recvJs}._d < (${args[0]})._d`;
1921
+ case "After":
1922
+ return `${recvJs}._d > (${args[0]})._d`;
1923
+ case "Equal":
1924
+ return `${recvJs}._d.getTime() === (${args[0]})._d.getTime()`;
1925
+ default:
1926
+ return undefined;
1927
+ }
1928
+ },
1929
+
1419
1930
  _genTime(fn, a) {
1420
1931
  const args = a();
1421
1932
  switch (fn) {
1422
1933
  case "Now":
1423
- return "Date.now()";
1934
+ return "{_d: new Date()}";
1424
1935
  case "Since":
1425
- return `(Date.now() - ${args[0]})`;
1936
+ return `(Date.now() - (${args[0]})._d.getTime())`;
1426
1937
  case "Sleep":
1427
1938
  return `await new Promise(r => setTimeout(r, ${args[0]} / 1000000))`;
1939
+ case "Parse":
1940
+ this._usesTimeParse = true;
1941
+ return `__timeParse(${args[0]}, ${args[1]})`;
1942
+ case "Unix":
1943
+ return `{_d: new Date((${args[0]}) * 1000)}`;
1944
+ case "Date":
1945
+ return `{_d: new Date(${args[0]}, ${args[1]} - 1, ${args[2]}, ${args[3]}, ${args[4]}, ${args[5]})}`;
1946
+ default:
1947
+ return undefined;
1948
+ }
1949
+ },
1950
+
1951
+ _genRand(fn, a) {
1952
+ const args = a();
1953
+ switch (fn) {
1954
+ case "Intn":
1955
+ case "Int63n":
1956
+ case "Int31n":
1957
+ return `Math.floor(Math.random() * ${args[0]})`;
1958
+ case "Float64":
1959
+ case "Float32":
1960
+ return "Math.random()";
1961
+ case "Int":
1962
+ case "Int31":
1963
+ return "Math.floor(Math.random() * 2147483647)";
1964
+ case "Int63":
1965
+ return "Math.floor(Math.random() * Number.MAX_SAFE_INTEGER)";
1966
+ case "Seed":
1967
+ return "(void 0)";
1968
+ case "Shuffle":
1969
+ return `((n, f) => { for (let i = n - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); f(i, j); } })(${args[0]}, ${args[1]})`;
1970
+ case "Perm":
1971
+ return `((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; })(${args[0]})`;
1972
+ default:
1973
+ return undefined;
1974
+ }
1975
+ },
1976
+
1977
+ _genUtf8(fn, a) {
1978
+ const args = a();
1979
+ switch (fn) {
1980
+ case "RuneCountInString":
1981
+ return `[...(${args[0]})].length`;
1982
+ case "RuneLen":
1983
+ return `((r) => r < 0 ? -1 : r <= 0x7F ? 1 : r <= 0x7FF ? 2 : r <= 0xFFFF ? (r >= 0xD800 && r <= 0xDFFF ? -1 : 3) : r <= 0x10FFFF ? 4 : -1)(${args[0]})`;
1984
+ case "ValidString":
1985
+ return `/[\\uD800-\\uDBFF](?![\\uDC00-\\uDFFF])|(?<![\\uD800-\\uDBFF])[\\uDC00-\\uDFFF]/.test(${args[0]}) === false`;
1986
+ case "ValidRune":
1987
+ return `(${args[0]}) >= 0 && (${args[0]}) <= 0x10FFFF && !((${args[0]}) >= 0xD800 && (${args[0]}) <= 0xDFFF)`;
1988
+ case "DecodeRuneInString":
1989
+ return `((s) => { if (!s) return [0xFFFD, 0]; const cp = s.codePointAt(0); return [cp, cp > 0xFFFF ? 2 : 1]; })(${args[0]})`;
1990
+ case "DecodeLastRuneInString":
1991
+ return `((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]; })(${args[0]})`;
1992
+ case "FullRuneInString":
1993
+ return `(${args[0]}).length > 0`;
1994
+ default:
1995
+ return undefined;
1996
+ }
1997
+ },
1998
+
1999
+ _genPath(fn, a, expr) {
2000
+ const args = a();
2001
+ switch (fn) {
2002
+ case "Base":
2003
+ return `((p) => { if (!p) return "."; const stripped = p.replace(/\\/+$/, ""); if (!stripped) return "/"; const i = stripped.lastIndexOf("/"); return i < 0 ? stripped : stripped.slice(i + 1) || "/"; })(${args[0]})`;
2004
+ case "Dir":
2005
+ return `((p) => { const i = p.lastIndexOf("/"); if (i < 0) return "."; if (i === 0) return "/"; return p.slice(0, i); })(${args[0]})`;
2006
+ case "Ext":
2007
+ return `((p) => { const b = p.slice(p.lastIndexOf("/") + 1); const i = b.lastIndexOf("."); return i <= 0 ? "" : b.slice(i); })(${args[0]})`;
2008
+ case "Join": {
2009
+ this._usesPathClean = true;
2010
+ const allArgs = expr.args.map((e) => this.genExpr(e)).join(", ");
2011
+ return `__pathClean([${allArgs}].filter(x => x !== "").join("/"))`;
2012
+ }
2013
+ case "Clean":
2014
+ this._usesPathClean = true;
2015
+ return `__pathClean(${args[0]})`;
2016
+ case "IsAbs":
2017
+ return `(${args[0]}).startsWith("/")`;
2018
+ case "Split":
2019
+ return `((p) => { const i = p.lastIndexOf("/"); return i < 0 ? ["", p] : [p.slice(0, i + 1), p.slice(i + 1)]; })(${args[0]})`;
2020
+ case "Match":
2021
+ return `((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"]; } })(${args[0]}, ${args[1]})`;
1428
2022
  default:
1429
2023
  return undefined;
1430
2024
  }