gofront 0.0.8 → 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.
Files changed (51) hide show
  1. package/README.md +88 -12
  2. package/package.json +8 -4
  3. package/src/codegen/expressions.js +520 -1717
  4. package/src/{codegen.js → codegen/index.js} +155 -184
  5. package/src/codegen/runtime.js +151 -0
  6. package/src/codegen/statements.js +305 -296
  7. package/src/codegen/stdlib/builder.js +36 -0
  8. package/src/codegen/stdlib/bytes.js +62 -0
  9. package/src/codegen/stdlib/errors.js +17 -0
  10. package/src/codegen/stdlib/fmt.js +101 -0
  11. package/src/codegen/stdlib/gom.js +152 -0
  12. package/src/codegen/stdlib/html.js +17 -0
  13. package/src/codegen/stdlib/index.js +95 -0
  14. package/src/codegen/stdlib/io.js +33 -0
  15. package/src/codegen/stdlib/maps.js +42 -0
  16. package/src/codegen/stdlib/math.js +53 -0
  17. package/src/codegen/stdlib/os.js +10 -0
  18. package/src/codegen/stdlib/path.js +31 -0
  19. package/src/codegen/stdlib/rand.js +24 -0
  20. package/src/codegen/stdlib/regexp.js +51 -0
  21. package/src/codegen/stdlib/slices.js +48 -0
  22. package/src/codegen/stdlib/sort.js +28 -0
  23. package/src/codegen/stdlib/strconv.js +30 -0
  24. package/src/codegen/stdlib/strings.js +83 -0
  25. package/src/codegen/stdlib/time.js +54 -0
  26. package/src/codegen/stdlib/unicode.js +22 -0
  27. package/src/codegen/stdlib/utf8.js +23 -0
  28. package/src/codegen/templ.js +281 -0
  29. package/src/compiler.js +102 -62
  30. package/src/dts-parser.js +355 -371
  31. package/src/index.js +22 -54
  32. package/src/lexer.js +200 -286
  33. package/src/minifier.js +305 -318
  34. package/src/parser/expressions.js +54 -36
  35. package/src/{parser.js → parser/index.js} +12 -5
  36. package/src/parser/statements.js +29 -35
  37. package/src/parser/types.js +31 -29
  38. package/src/templ-lexer.js +737 -0
  39. package/src/templ-parser.js +235 -0
  40. package/src/tokens.js +109 -0
  41. package/src/typechecker/assignability.js +258 -0
  42. package/src/typechecker/expressions.js +559 -549
  43. package/src/typechecker/index.js +555 -0
  44. package/src/typechecker/resolve.js +421 -0
  45. package/src/typechecker/statements.js +267 -280
  46. package/src/typechecker/stdlib/core.js +592 -0
  47. package/src/typechecker/stdlib/extended.js +332 -0
  48. package/src/typechecker/stdlib.js +9 -1060
  49. package/src/typechecker/termination.js +83 -0
  50. package/src/typechecker/types.js +6 -3
  51. package/src/typechecker.js +0 -1271
@@ -17,10 +17,26 @@
17
17
  // - make(map[K]V) → {}
18
18
  // - for range → for...of with .entries()
19
19
 
20
- import { expressionGenMethods } from "./codegen/expressions.js";
21
- import { buildSourceMap } from "./codegen/source-map.js";
22
- import { statementGenMethods } from "./codegen/statements.js";
23
- import { isComplex, isNumeric } from "./typechecker/types.js";
20
+ import { isComplex, isNumeric } from "../typechecker/types.js";
21
+ import { expressionGenMethods } from "./expressions.js";
22
+ import {
23
+ HELPER_APPEND,
24
+ HELPER_CDIV,
25
+ HELPER_CMUL,
26
+ HELPER_EQUAL,
27
+ HELPER_ERROR,
28
+ HELPER_ERROR_IS,
29
+ HELPER_LEN,
30
+ HELPER_PATH_CLEAN,
31
+ HELPER_S,
32
+ HELPER_SPRINTF,
33
+ HELPER_TIME_FMT,
34
+ HELPER_TIME_PARSE,
35
+ } from "./runtime.js";
36
+ import { buildSourceMap } from "./source-map.js";
37
+ import { statementGenMethods } from "./statements.js";
38
+ import { stdlibGenMethods } from "./stdlib/index.js";
39
+ import { templGenMethods } from "./templ.js";
24
40
 
25
41
  export class CodeGen {
26
42
  // jsImports: Map<importPath, string[]> — npm package imports to emit at top of file
@@ -88,43 +104,15 @@ export class CodeGen {
88
104
  this.indent--;
89
105
  }
90
106
 
91
- generate(program) {
92
- // Collect struct names first (needed by zero-value helpers and genCompositeLit)
93
- for (const d of program.decls) {
94
- if (d.kind === "TypeDecl" && d.type.kind === "StructType") {
95
- this.structNames.add(d.name);
96
- }
97
- }
98
-
99
- // Build a map: typeName → [MethodDecl, ...]
100
- const methods = new Map();
101
- for (const d of program.decls) {
102
- if (d.kind === "MethodDecl") {
103
- const name = d.recvType.name;
104
- if (!methods.has(name)) methods.set(name, []);
105
- methods.get(name).push(d);
106
- }
107
- }
108
-
109
- // Collect named non-struct types that have methods (emitted as wrapper classes)
110
- for (const d of program.decls) {
111
- if (
112
- d.kind === "TypeDecl" &&
113
- d.type.kind !== "StructType" &&
114
- d.type.kind !== "InterfaceType" &&
115
- (methods.get(d.name) ?? []).length > 0
116
- ) {
117
- this.namedWrapperNames.add(d.name);
118
- }
119
- }
120
-
121
- // Emit ESM import statements for npm packages
107
+ _emitJsImports() {
122
108
  for (const [importPath, names] of this.jsImports) {
123
109
  if (names.length === 0) continue;
124
110
  this.line(`import { ${names.join(", ")} } from '${importPath}';`);
125
111
  }
126
112
  if (this.jsImports.size > 0) this.blank();
113
+ }
127
114
 
115
+ _emitTypeDecls(program, methods) {
128
116
  for (const d of program.decls) {
129
117
  if (d.kind === "TypeDecl") {
130
118
  this._currentSrcFileIdx = d._srcFileIdx ?? 0;
@@ -132,7 +120,9 @@ export class CodeGen {
132
120
  this.blank();
133
121
  }
134
122
  }
123
+ }
135
124
 
125
+ _emitVarConstDecls(program) {
136
126
  for (const d of program.decls) {
137
127
  if (d.kind === "VarDecl") {
138
128
  this._currentSrcFileIdx = d._srcFileIdx ?? 0;
@@ -145,8 +135,28 @@ export class CodeGen {
145
135
  this.blank();
146
136
  }
147
137
  }
138
+ }
139
+
140
+ _callInitAndMain(initNames, program) {
141
+ for (const name of initNames) this.line(`${name}();`);
142
+ if (program.decls.some((d) => d.kind === "FuncDecl" && d.name === "main"))
143
+ this.line("main();");
144
+ }
145
+
146
+ generate(program) {
147
+ const methods = this._collectDecls(program);
148
+ this._emitJsImports();
149
+ this._emitTypeDecls(program, methods);
150
+ this._emitVarConstDecls(program);
151
+ const initNames = this._emitFuncDecls(program);
152
+ this._callInitAndMain(initNames, program);
153
+ this._prependHelpers();
154
+ while (this.out[0] === "") this.out.shift();
155
+ return this.out.join("\n");
156
+ }
148
157
 
149
- // Emit functions — rename duplicate init() to init$0, init$1, etc.
158
+ // Emits FuncDecl and TemplDecl nodes; returns renamed init function names.
159
+ _emitFuncDecls(program) {
150
160
  let initCount = 0;
151
161
  const initNames = [];
152
162
  for (const d of program.decls) {
@@ -161,121 +171,73 @@ export class CodeGen {
161
171
  this.genFuncDecl(d);
162
172
  }
163
173
  this.blank();
174
+ } else if (d.kind === "TemplDecl") {
175
+ this._currentSrcFileIdx = d._srcFileIdx ?? 0;
176
+ this.genTemplDecl(d);
177
+ this.blank();
164
178
  }
165
179
  }
180
+ return initNames;
181
+ }
166
182
 
167
- // Auto-call init() functions, then main()
168
- for (const name of initNames) this.line(`${name}();`);
169
- const hasMain = program.decls.some(
170
- (d) => d.kind === "FuncDecl" && d.name === "main",
171
- );
172
- if (hasMain) this.line("main();");
183
+ // Collects struct names, method map, and named wrapper names from program decls.
184
+ // Returns the method map (typeName → MethodDecl[]).
185
+ _collectStructNames(program) {
186
+ for (const d of program.decls) {
187
+ if (d.kind === "TypeDecl" && d.type.kind === "StructType")
188
+ this.structNames.add(d.name);
189
+ }
190
+ }
173
191
 
174
- // Prepend runtime helpers that were actually used
175
- const helpers = [];
176
- if (this._usesLen)
177
- helpers.push(
178
- "var __len = __len || function(a) { if (a && typeof a === 'object' && !Array.isArray(a)) return Object.keys(a).length; return a?.length ?? 0; };",
179
- );
180
- if (this._usesAppend)
181
- helpers.push(
182
- "var __append = __append || function(a, ...b) { return a ? [...a, ...b] : b; };",
183
- );
184
- if (this._usesSliceGuard)
185
- helpers.push("var __s = __s || function(a) { return a || []; };");
186
- if (this._usesEqual)
187
- helpers.push(
188
- 'var __equal = __equal || function __equal(a,b){if(a===b)return true;if(a===null||b===null)return false;if(Array.isArray(a)&&Array.isArray(b)){if(a.length!==b.length)return false;for(let i=0;i<a.length;i++)if(!__equal(a[i],b[i]))return false;return true;}if(typeof a==="object"&&typeof b==="object"){const ka=Object.keys(a),kb=Object.keys(b);if(ka.length!==kb.length)return false;for(const k of ka)if(!__equal(a[k],b[k]))return false;return true;}return false;};',
189
- );
190
- if (this._usesCmul)
191
- helpers.push(
192
- "var __cmul = __cmul || function(a, b) { return { re: a.re * b.re - a.im * b.im, im: a.re * b.im + a.im * b.re }; };",
193
- );
194
- if (this._usesCdiv)
195
- helpers.push(
196
- "var __cdiv = __cdiv || function(a, b) { const d = b.re * b.re + b.im * b.im; return { re: (a.re * b.re + a.im * b.im) / d, im: (a.im * b.re - a.re * b.im) / d }; };",
197
- );
198
- if (this._usesSprintf)
199
- helpers.push(
200
- [
201
- "var __sprintf = __sprintf || function(f,...a){let i=0;return f.replace(/%([#+\\- 0]*)([0-9]*)\\.?([0-9]*)[sdvftxXqobeEgGw%]/g,(m)=>{",
202
- "if(m==='%%')return'%';const fl=m.slice(1,-1),verb=m.slice(-1),v=a[i++];",
203
- "const pad=(s,w,z)=>{w=parseInt(w)||0;if(!w)return s;const p=(z?'0':' ').repeat(Math.max(0,w-s.length));return fl.includes('-')?s+p:p+s;};",
204
- "const [,flags,width,prec]=m.match(/^%([#+\\- 0]*)([0-9]*)\\.?([0-9]*)/)||[];",
205
- "const zero=flags?.includes('0')&&!flags?.includes('-');",
206
- "switch(verb){",
207
- "case's':return pad(String(v==null?'<nil>':v),width,false);",
208
- "case'd':return pad(String(Math.trunc(Number(v))),width,zero);",
209
- "case'v':{if(typeof v==='object'&&v!==null&&'re' in v&&'im' in v){const sign=v.im>=0?'+':'';return pad('('+v.re+sign+v.im+'i)',width,false);}return pad(String(v==null?'<nil>':v),width,false);}",
210
- "case'f':{const n=Number(v),p=prec!==''?parseInt(prec):6;return pad(n.toFixed(p),width,zero);}",
211
- "case't':return pad(String(!!v),width,false);",
212
- "case'x':return pad((Number(v)>>>0).toString(16),width,zero);",
213
- "case'X':return pad((Number(v)>>>0).toString(16).toUpperCase(),width,zero);",
214
- "case'o':return pad((Number(v)>>>0).toString(8),width,zero);",
215
- "case'b':return pad((Number(v)>>>0).toString(2),width,zero);",
216
- "case'q':return pad('\"'+String(v==null?'':v).replace(/\\\\/g,'\\\\\\\\').replace(/\"/g,'\\\\\"')+'\"',width,false);",
217
- "case'e':case'E':{const n=Number(v),p=prec!==''?parseInt(prec):6;return pad(n.toExponential(p),width,zero);}",
218
- "case'g':case'G':{const n=Number(v);return pad(prec!==''?n.toPrecision(parseInt(prec)):String(n),width,zero);}",
219
- "case'w':return pad(String(v==null?'<nil>':typeof v==='object'&&v.Error?v.Error():v),width,false);",
220
- "default:return m;}});",
221
- "};",
222
- ].join(""),
223
- );
224
- if (this._usesError)
225
- helpers.push(
226
- "var __error = __error || function(msg, cause) { return { Error() { return msg; }, toString() { return msg; }, _msg: msg, _cause: cause ?? null }; };",
227
- );
228
- if (this._usesErrorIs)
229
- helpers.push(
230
- 'var __errorIs = __errorIs || function(err, target) { while (err !== null && err !== undefined) { if (err === target) return true; if (typeof err === "object" && typeof target === "object" && err._msg !== undefined && target._msg !== undefined && err._msg === target._msg) return true; err = err?._cause ?? null; } return false; };',
231
- );
232
- if (this._usesPathClean)
233
- helpers.push(
234
- 'var __pathClean = __pathClean || function(p) { if (!p) return "."; const abs = p.startsWith("/"); const parts = p.split("/").reduce((acc, s) => { if (s === "" || s === ".") return acc; if (s === "..") { if (acc.length && acc[acc.length-1] !== "..") acc.pop(); else if (!abs) acc.push(".."); } else acc.push(s); return acc; }, []); return (abs ? "/" : "") + (parts.join("/") || "."); };',
235
- );
236
- if (this._usesTimeFmt)
237
- helpers.push(
238
- [
239
- "var __timeFmt = __timeFmt || function(d, layout) {",
240
- 'const pad = (n, w=2) => String(n).padStart(w, "0");',
241
- 'const DAYS=["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"];',
242
- 'const MONTHS=["January","February","March","April","May","June","July","August","September","October","November","December"];',
243
- "return layout.replace(/2006|06|Monday|Mon|January|Jan|01|1|02|2|15|PM|3|04|4|05|5|Z07:00|\\.000|\\.999/g, t => {",
244
- "switch(t){",
245
- 'case "2006": return d.getFullYear();',
246
- 'case "06": return String(d.getFullYear()).slice(-2);',
247
- 'case "Monday": return DAYS[d.getDay()];',
248
- 'case "Mon": return DAYS[d.getDay()].slice(0,3);',
249
- 'case "January": return MONTHS[d.getMonth()];',
250
- 'case "Jan": return MONTHS[d.getMonth()].slice(0,3);',
251
- 'case "01": return pad(d.getMonth()+1);',
252
- 'case "1": return String(d.getMonth()+1);',
253
- 'case "02": return pad(d.getDate());',
254
- 'case "2": return String(d.getDate());',
255
- 'case "15": return pad(d.getHours());',
256
- 'case "PM": return d.getHours()<12?"AM":"PM";',
257
- 'case "3": return String(d.getHours()%12||12);',
258
- 'case "04": return pad(d.getMinutes());',
259
- 'case "4": return String(d.getMinutes());',
260
- 'case "05": return pad(d.getSeconds());',
261
- 'case "5": return String(d.getSeconds());',
262
- 'case "Z07:00": { const o=-d.getTimezoneOffset(); return o===0?"Z":(o>0?"+":"-")+pad(Math.floor(Math.abs(o)/60))+":"+pad(Math.abs(o)%60); }',
263
- 'case ".000": return "."+pad(d.getMilliseconds(),3);',
264
- 'case ".999": return d.getMilliseconds()>0?"."+String(d.getMilliseconds()).replace(/0+$/,""):"";',
265
- "default: return t;}});",
266
- "};",
267
- ].join(""),
268
- );
269
- if (this._usesTimeParse)
270
- helpers.push(
271
- 'var __timeParse = __timeParse || function(layout, value) { try { const d = new Date(value); if (isNaN(d.getTime())) throw 0; return [{_d:d}, null]; } catch { return [{_d:new Date(0)}, "parsing time: cannot parse"]; } };',
272
- );
192
+ _collectMethodMap(program) {
193
+ const methods = new Map();
194
+ for (const d of program.decls) {
195
+ if (d.kind === "MethodDecl") {
196
+ const name = d.recvType.name;
197
+ if (!methods.has(name)) methods.set(name, []);
198
+ methods.get(name).push(d);
199
+ }
200
+ }
201
+ return methods;
202
+ }
273
203
 
274
- if (helpers.length > 0) this.out.unshift(...helpers, "");
204
+ _collectNamedWrappers(program, methods) {
205
+ for (const d of program.decls) {
206
+ if (
207
+ d.kind === "TypeDecl" &&
208
+ d.type.kind !== "StructType" &&
209
+ d.type.kind !== "InterfaceType" &&
210
+ (methods.get(d.name) ?? []).length > 0
211
+ ) {
212
+ this.namedWrapperNames.add(d.name);
213
+ }
214
+ }
215
+ }
275
216
 
276
- // Strip leading blank line
277
- while (this.out[0] === "") this.out.shift();
278
- return this.out.join("\n");
217
+ _collectDecls(program) {
218
+ this._collectStructNames(program);
219
+ const methods = this._collectMethodMap(program);
220
+ this._collectNamedWrappers(program, methods);
221
+ return methods;
222
+ }
223
+
224
+ _prependHelpers() {
225
+ const HELPER_MAP = [
226
+ [this._usesLen, HELPER_LEN],
227
+ [this._usesAppend, HELPER_APPEND],
228
+ [this._usesSliceGuard, HELPER_S],
229
+ [this._usesEqual, HELPER_EQUAL],
230
+ [this._usesCmul, HELPER_CMUL],
231
+ [this._usesCdiv, HELPER_CDIV],
232
+ [this._usesSprintf, HELPER_SPRINTF],
233
+ [this._usesError, HELPER_ERROR],
234
+ [this._usesErrorIs, HELPER_ERROR_IS],
235
+ [this._usesPathClean, HELPER_PATH_CLEAN],
236
+ [this._usesTimeFmt, HELPER_TIME_FMT],
237
+ [this._usesTimeParse, HELPER_TIME_PARSE],
238
+ ];
239
+ const helpers = HELPER_MAP.filter(([flag]) => flag).map(([, h]) => h);
240
+ if (helpers.length > 0) this.out.unshift(...helpers, "");
279
241
  }
280
242
 
281
243
  // Generate a single bundle from multiple programs (same-package multi-file).
@@ -341,68 +303,75 @@ export class CodeGen {
341
303
  this.line("}");
342
304
  }
343
305
 
344
- genStruct(name, structTypeAst, methodDecls) {
306
+ _collectStructFields(name, structTypeAst) {
345
307
  const fields = [];
346
308
  if (this.checker) {
347
309
  const resolved = this.checker.types.get(name)?.underlying;
348
310
  if (resolved?.kind === "struct") {
349
- for (const [fName, fType] of resolved.fields.entries()) {
311
+ for (const [fName, fType] of resolved.fields.entries())
350
312
  fields.push({ name: fName, zero: this.zeroValueForType(fType) });
351
- }
352
313
  }
353
314
  } else {
354
- // Fallback (e.g. tests without typechecker)
355
315
  for (const f of structTypeAst.fields) {
356
- if (f.embedded) continue; // Can't resolve here
316
+ if (f.embedded) continue;
357
317
  const zero = this.zeroValueForTypeNode(f.type);
358
318
  for (const n of f.names) fields.push({ name: n, zero });
359
319
  }
360
320
  }
321
+ return fields;
322
+ }
323
+
324
+ _genStructConstructor(fields) {
325
+ if (fields.length === 0) {
326
+ this.line("constructor() {}");
327
+ } else {
328
+ const params = fields.map((f) => `${f.name} = ${f.zero}`).join(", ");
329
+ this.line(`constructor({ ${params} } = {}) {`);
330
+ this.indented(() => {
331
+ for (const f of fields) this.line(`this.${f.name} = ${f.name};`);
332
+ });
333
+ this.line("}");
334
+ }
335
+ }
361
336
 
337
+ genStruct(name, structTypeAst, methodDecls) {
338
+ const fields = this._collectStructFields(name, structTypeAst);
362
339
  this.line(`class ${name} {`);
363
340
  this.indented(() => {
364
- if (fields.length === 0) {
365
- this.line("constructor() {}");
366
- } else {
367
- const params = fields.map((f) => `${f.name} = ${f.zero}`).join(", ");
368
- this.line(`constructor({ ${params} } = {}) {`);
369
- this.indented(() => {
370
- for (const f of fields) this.line(`this.${f.name} = ${f.name};`);
371
- });
372
- this.line("}");
373
- }
374
-
375
- // Methods
341
+ this._genStructConstructor(fields);
376
342
  for (const m of methodDecls) {
377
343
  this.blank();
378
344
  this.genMethod(m);
379
345
  }
380
-
381
- // Delegation stubs for promoted embedded methods
382
- if (this.checker) {
383
- const resolvedType = this.checker.types.get(name)?.underlying;
384
- if (resolvedType?.kind === "struct" && resolvedType._embeds) {
385
- const declared = new Set(methodDecls.map((m) => m.name));
386
- for (const embed of resolvedType._embeds) {
387
- const embedName = embed.kind === "named" ? embed.name : null;
388
- if (!embedName) continue;
389
- const embedBase = embed.kind === "named" ? embed.underlying : embed;
390
- if (embedBase?.kind !== "struct" || !embedBase.methods) continue;
391
- for (const [mName] of embedBase.methods.entries()) {
392
- if (!declared.has(mName)) {
393
- this.blank();
394
- this.line(
395
- `${mName}(...__a) { return ${embedName}.prototype.${mName}.call(this, ...__a); }`,
396
- );
397
- }
398
- }
399
- }
400
- }
401
- }
346
+ this._genEmbeddedMethodStubs(name, methodDecls);
402
347
  });
403
348
  this.line("}");
404
349
  }
405
350
 
351
+ _genSingleEmbedStubs(embed, declared) {
352
+ const embedName = embed.kind === "named" ? embed.name : null;
353
+ if (!embedName) return;
354
+ const embedBase = embed.kind === "named" ? embed.underlying : embed;
355
+ if (embedBase?.kind !== "struct" || !embedBase.methods) return;
356
+ for (const [mName] of embedBase.methods.entries()) {
357
+ if (!declared.has(mName)) {
358
+ this.blank();
359
+ this.line(
360
+ `${mName}(...__a) { return ${embedName}.prototype.${mName}.call(this, ...__a); }`,
361
+ );
362
+ }
363
+ }
364
+ }
365
+
366
+ _genEmbeddedMethodStubs(name, methodDecls) {
367
+ if (!this.checker) return;
368
+ const resolvedType = this.checker.types.get(name)?.underlying;
369
+ if (resolvedType?.kind !== "struct" || !resolvedType._embeds) return;
370
+ const declared = new Set(methodDecls.map((m) => m.name));
371
+ for (const embed of resolvedType._embeds)
372
+ this._genSingleEmbedStubs(embed, declared);
373
+ }
374
+
406
375
  genMethod(decl, recvField = null) {
407
376
  const params = decl.params.map((p) => p.name).join(", ");
408
377
  const asyncPrefix = decl.async ? "async " : "";
@@ -603,3 +572,5 @@ export class CodeGen {
603
572
 
604
573
  Object.assign(CodeGen.prototype, statementGenMethods);
605
574
  Object.assign(CodeGen.prototype, expressionGenMethods);
575
+ Object.assign(CodeGen.prototype, stdlibGenMethods);
576
+ Object.assign(CodeGen.prototype, templGenMethods);
@@ -0,0 +1,151 @@
1
+ // Runtime helpers emitted by the code generator when the corresponding feature is used.
2
+ // Each export is a single JS statement (var __x = __x || function(...){...};) so that
3
+ // multiple compiled files bundled together can safely repeat the declaration.
4
+
5
+ export const HELPER_LEN = `var __len = __len || function(a) {
6
+ if (a && typeof a === 'object' && !Array.isArray(a)) return Object.keys(a).length;
7
+ return a?.length ?? 0;
8
+ };`;
9
+
10
+ export const HELPER_APPEND =
11
+ "var __append = __append || function(a, ...b) { return a ? [...a, ...b] : b; };";
12
+
13
+ export const HELPER_S = "var __s = __s || function(a) { return a || []; };";
14
+
15
+ export const HELPER_EQUAL = `var __equal = __equal || function __equal(a, b) {
16
+ if (a === b) return true;
17
+ if (a === null || b === null) return false;
18
+ if (Array.isArray(a) && Array.isArray(b)) {
19
+ if (a.length !== b.length) return false;
20
+ for (let i = 0; i < a.length; i++) if (!__equal(a[i], b[i])) return false;
21
+ return true;
22
+ }
23
+ if (typeof a === "object" && typeof b === "object") {
24
+ const ka = Object.keys(a), kb = Object.keys(b);
25
+ if (ka.length !== kb.length) return false;
26
+ for (const k of ka) if (!__equal(a[k], b[k])) return false;
27
+ return true;
28
+ }
29
+ return false;
30
+ };`;
31
+
32
+ export const HELPER_CMUL = `var __cmul = __cmul || function(a, b) {
33
+ return { re: a.re * b.re - a.im * b.im, im: a.re * b.im + a.im * b.re };
34
+ };`;
35
+
36
+ export const HELPER_CDIV = `var __cdiv = __cdiv || function(a, b) {
37
+ const d = b.re * b.re + b.im * b.im;
38
+ return { re: (a.re * b.re + a.im * b.im) / d, im: (a.im * b.re - a.re * b.im) / d };
39
+ };`;
40
+
41
+ export const HELPER_SPRINTF = `var __sprintf = __sprintf || function(f, ...a) {
42
+ let i = 0;
43
+ return f.replace(/%([#+\\- 0]*)([0-9]*)\\.?([0-9]*)[sdvftxXqobeEgGw%]/g, (m) => {
44
+ if (m === "%%") return "%";
45
+ const verb = m.slice(-1);
46
+ const v = a[i++];
47
+ const [, flags, width, prec] = m.match(/^%([#+\\- 0]*)([0-9]*)\\.?([0-9]*)/) || [];
48
+ const zero = flags?.includes("0") && !flags?.includes("-");
49
+ const pad = (s, w, z) => {
50
+ w = parseInt(w) || 0;
51
+ if (!w) return s;
52
+ const p = (z ? "0" : " ").repeat(Math.max(0, w - s.length));
53
+ return flags.includes("-") ? s + p : p + s;
54
+ };
55
+ switch (verb) {
56
+ case "s": return pad(String(v == null ? "<nil>" : v), width, false);
57
+ case "d": return pad(String(Math.trunc(Number(v))), width, zero);
58
+ case "v": {
59
+ if (typeof v === "object" && v !== null && "re" in v && "im" in v) {
60
+ const sign = v.im >= 0 ? "+" : "";
61
+ return pad("(" + v.re + sign + v.im + "i)", width, false);
62
+ }
63
+ return pad(String(v == null ? "<nil>" : v), width, false);
64
+ }
65
+ case "f": { const n = Number(v), p = prec !== "" ? parseInt(prec) : 6; return pad(n.toFixed(p), width, zero); }
66
+ case "t": return pad(String(!!v), width, false);
67
+ case "x": return pad((Number(v) >>> 0).toString(16), width, zero);
68
+ case "X": return pad((Number(v) >>> 0).toString(16).toUpperCase(), width, zero);
69
+ case "o": return pad((Number(v) >>> 0).toString(8), width, zero);
70
+ case "b": return pad((Number(v) >>> 0).toString(2), width, zero);
71
+ case "q": return pad('"' + String(v == null ? "" : v).replace(/\\\\/g, "\\\\\\\\").replace(/"/g, '\\\\"') + '"', width, false);
72
+ case "e": case "E": { const n = Number(v), p = prec !== "" ? parseInt(prec) : 6; return pad(n.toExponential(p), width, zero); }
73
+ case "g": case "G": { const n = Number(v); return pad(prec !== "" ? n.toPrecision(parseInt(prec)) : String(n), width, zero); }
74
+ case "w": return pad(String(v == null ? "<nil>" : typeof v === "object" && v.Error ? v.Error() : v), width, false);
75
+ default: return m;
76
+ }
77
+ });
78
+ };`;
79
+
80
+ export const HELPER_ERROR = `var __error = __error || function(msg, cause) {
81
+ return { Error() { return msg; }, toString() { return msg; }, _msg: msg, _cause: cause ?? null };
82
+ };`;
83
+
84
+ export const HELPER_ERROR_IS = `var __errorIs = __errorIs || function(err, target) {
85
+ while (err !== null && err !== undefined) {
86
+ if (err === target) return true;
87
+ if (typeof err === "object" && typeof target === "object" &&
88
+ err._msg !== undefined && target._msg !== undefined &&
89
+ err._msg === target._msg) return true;
90
+ err = err?._cause ?? null;
91
+ }
92
+ return false;
93
+ };`;
94
+
95
+ export const HELPER_PATH_CLEAN = `var __pathClean = __pathClean || function(p) {
96
+ if (!p) return ".";
97
+ const abs = p.startsWith("/");
98
+ const parts = p.split("/").reduce((acc, s) => {
99
+ if (s === "" || s === ".") return acc;
100
+ if (s === "..") {
101
+ if (acc.length && acc[acc.length - 1] !== "..") acc.pop();
102
+ else if (!abs) acc.push("..");
103
+ } else acc.push(s);
104
+ return acc;
105
+ }, []);
106
+ return (abs ? "/" : "") + (parts.join("/") || ".");
107
+ };`;
108
+
109
+ export const HELPER_TIME_FMT = `var __timeFmt = __timeFmt || function(d, layout) {
110
+ const pad = (n, w = 2) => String(n).padStart(w, "0");
111
+ const DAYS = ["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"];
112
+ const MONTHS = ["January","February","March","April","May","June","July","August","September","October","November","December"];
113
+ return layout.replace(/2006|06|Monday|Mon|January|Jan|01|1|02|2|15|PM|3|04|4|05|5|Z07:00|\\.000|\\.999/g, t => {
114
+ switch (t) {
115
+ case "2006": return d.getFullYear();
116
+ case "06": return String(d.getFullYear()).slice(-2);
117
+ case "Monday": return DAYS[d.getDay()];
118
+ case "Mon": return DAYS[d.getDay()].slice(0, 3);
119
+ case "January": return MONTHS[d.getMonth()];
120
+ case "Jan": return MONTHS[d.getMonth()].slice(0, 3);
121
+ case "01": return pad(d.getMonth() + 1);
122
+ case "1": return String(d.getMonth() + 1);
123
+ case "02": return pad(d.getDate());
124
+ case "2": return String(d.getDate());
125
+ case "15": return pad(d.getHours());
126
+ case "PM": return d.getHours() < 12 ? "AM" : "PM";
127
+ case "3": return String(d.getHours() % 12 || 12);
128
+ case "04": return pad(d.getMinutes());
129
+ case "4": return String(d.getMinutes());
130
+ case "05": return pad(d.getSeconds());
131
+ case "5": return String(d.getSeconds());
132
+ case "Z07:00": {
133
+ const o = -d.getTimezoneOffset();
134
+ return o === 0 ? "Z" : (o > 0 ? "+" : "-") + pad(Math.floor(Math.abs(o) / 60)) + ":" + pad(Math.abs(o) % 60);
135
+ }
136
+ case ".000": return "." + pad(d.getMilliseconds(), 3);
137
+ case ".999": return d.getMilliseconds() > 0 ? "." + String(d.getMilliseconds()).replace(/0+$/, "") : "";
138
+ default: return t;
139
+ }
140
+ });
141
+ };`;
142
+
143
+ export const HELPER_TIME_PARSE = `var __timeParse = __timeParse || function(layout, value) {
144
+ try {
145
+ const d = new Date(value);
146
+ if (isNaN(d.getTime())) throw 0;
147
+ return [{ _d: d }, null];
148
+ } catch {
149
+ return [{ _d: new Date(0) }, "parsing time: cannot parse"];
150
+ }
151
+ };`;