gofront 0.0.7 → 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.
- package/README.md +26 -16
- package/package.json +8 -4
- package/src/codegen/expressions.js +351 -6
- package/src/codegen/source-map.js +8 -5
- package/src/codegen.js +51 -3
- package/src/compiler.js +2 -1
- package/src/dev-server.js +1 -0
- package/src/index.js +1 -1
- package/src/resolver.js +29 -0
- package/src/typechecker/stdlib.js +305 -2
- package/src/typechecker/types.js +0 -15
package/README.md
CHANGED
|
@@ -101,8 +101,8 @@ Walks the typed AST and emits clean, readable JavaScript. No intermediate repres
|
|
|
101
101
|
— the codegen writes directly to an output buffer with indentation tracking.
|
|
102
102
|
|
|
103
103
|
Runtime helpers (`__len`, `__append`, `__s`, `__sprintf`, `__cmul`, `__cdiv`, `__error`,
|
|
104
|
-
`__errorIs`) are tree-shaken: only emitted
|
|
105
|
-
maps are supported via VLQ-encoded mappings.
|
|
104
|
+
`__errorIs`, `__timeFmt`, `__timeParse`, `__pathClean`) are tree-shaken: only emitted
|
|
105
|
+
when actually used. Optional inline source maps are supported via VLQ-encoded mappings.
|
|
106
106
|
|
|
107
107
|
---
|
|
108
108
|
|
|
@@ -464,21 +464,24 @@ signatures into GoFront's internal type representation.
|
|
|
464
464
|
|
|
465
465
|
| Package | Functions |
|
|
466
466
|
|---|---|
|
|
467
|
-
| `fmt` | `Sprintf`, `Printf`, `Println`, `Print`, `Errorf`, `Fprintf`, `Fprintln`, `Fprint` — format verbs: `%v`, `%d`, `%s`, `%t`, `%x`, `%o`, `%b`, `%q`, `%e`, `%g`, `%w`, width/precision |
|
|
468
|
-
| `strings` | `Contains`, `HasPrefix`, `HasSuffix`, `Index`, `LastIndex`, `Count`, `Repeat`, `Replace`, `ReplaceAll`, `ToUpper`, `ToLower`, `TrimSpace`, `Trim`, `TrimPrefix`, `TrimSuffix`, `TrimLeft`, `TrimRight`, `Split`, `Join`, `EqualFold
|
|
469
|
-
| `bytes` | `Contains`, `HasPrefix`, `HasSuffix`, `Index`, `Join`, `Split`, `Replace`, `ToUpper`, `ToLower`, `TrimSpace`, `Equal`, `Count`, `Repeat
|
|
470
|
-
| `strconv` | `Itoa`, `Atoi`, `FormatBool`, `FormatInt`, `FormatFloat`, `ParseFloat`, `ParseInt`, `ParseBool` |
|
|
471
|
-
| `sort` | `Ints`, `Float64s`, `Strings`, `Slice`, `SliceStable`, `SliceIsSorted` |
|
|
472
|
-
| `math` | `Abs`, `Floor`, `Ceil`, `Round`, `Sqrt`, `Cbrt`, `Pow`, `Log`, `Log2`, `Log10`,
|
|
467
|
+
| `fmt` | `Sprintf`, `Printf`, `Println`, `Print`, `Errorf`, `Fprintf`, `Fprintln`, `Fprint` — format verbs: `%v`, `%d`, `%s`, `%t`, `%x`, `%o`, `%b`, `%q`, `%e`, `%g`, `%w`, width/precision; **scanning**: `Sscan`, `Sscanln`, `Sscanf` |
|
|
468
|
+
| `strings` | `Contains`, `HasPrefix`, `HasSuffix`, `Index`, `LastIndex`, `Count`, `Repeat`, `Replace`, `ReplaceAll`, `ToUpper`, `ToLower`, `TrimSpace`, `Trim`, `TrimPrefix`, `TrimSuffix`, `TrimLeft`, `TrimRight`, `Split`, `Join`, `EqualFold`, `Fields`, `Cut`, `CutPrefix`, `CutSuffix`, `SplitN`, `SplitAfter`, `SplitAfterN`, `IndexAny`, `LastIndexAny`, `ContainsAny`, `ContainsRune`, `IndexRune`, `IndexByte`, `LastIndexByte`, `Map`, `Title`, `ToTitle`, `TrimFunc`, `TrimLeftFunc`, `TrimRightFunc`, `IndexFunc`, `LastIndexFunc`, `NewReplacer`; **`Builder`** type; **`NewReader`** → reader shim |
|
|
469
|
+
| `bytes` | `Contains`, `HasPrefix`, `HasSuffix`, `Index`, `Join`, `Split`, `Replace`, `ToUpper`, `ToLower`, `TrimSpace`, `Equal`, `Count`, `Repeat`, `ReplaceAll`, `TrimPrefix`, `TrimSuffix`, `TrimLeft`, `TrimRight`, `TrimFunc`, `IndexByte`, `LastIndex`, `LastIndexByte`, `Fields`, `Cut`, `ContainsAny`, `ContainsRune`, `Map`, `SplitN`; **`Buffer`** type; **`NewReader`** → reader shim |
|
|
470
|
+
| `strconv` | `Itoa`, `Atoi`, `FormatBool`, `FormatInt`, `FormatFloat`, `ParseFloat`, `ParseInt`, `ParseBool`, `Quote`, `Unquote`, `AppendInt`, `AppendFloat` |
|
|
471
|
+
| `sort` | `Ints`, `Float64s`, `Strings`, `Slice`, `SliceStable`, `SliceIsSorted`, `Search`, `IntsAreSorted`, `Float64sAreSorted`, `StringsAreSorted` |
|
|
472
|
+
| `math` | `Abs`, `Floor`, `Ceil`, `Round`, `Sqrt`, `Cbrt`, `Pow`, `Log`, `Log2`, `Log10`, `Sin`, `Cos`, `Tan`, `Atan`, `Atan2`, `Asin`, `Acos`, `Exp`, `Exp2`, `Trunc`, `Hypot`, `Signbit`, `Copysign`, `Dim`, `Remainder`, `Min`, `Max`, `Mod`, `Inf`, `IsNaN`, `IsInf`, `NaN` + `Pi`, `E`, `MaxFloat64`, `SmallestNonzeroFloat64`, `MaxInt`, `MinInt` |
|
|
473
|
+
| `math/rand` | `Intn`, `Float64`, `Float32`, `Int`, `Int63`, `Int63n`, `Int31`, `Int31n`, `Seed` (no-op), `Shuffle`, `Perm` |
|
|
473
474
|
| `errors` | `New`, `Is`, `Unwrap` — custom error types via interface satisfaction |
|
|
474
|
-
| `time` | `Now`, `Since`, `Sleep`
|
|
475
|
+
| `time` | `Now` → `time.Time`, `Since`, `Sleep`, `Parse`, `Unix`, `Date`; **`time.Time`** methods: `Format`, `String`, `Year`, `Month`, `Day`, `Hour`, `Minute`, `Second`, `Weekday`, `Unix`, `UnixMilli`, `Add`, `Sub`, `Before`, `After`, `Equal`; layout constants: `RFC3339`, `RFC3339Nano`, `DateOnly`, `TimeOnly`, `DateTime`; duration constants: `Millisecond`, `Second`, `Minute`, `Hour`; month/weekday constants |
|
|
475
476
|
| `html` | `EscapeString`, `UnescapeString` |
|
|
476
477
|
| `maps` | `Keys`, `Values`, `Clone`, `Copy`, `Equal`, `EqualFunc`, `Delete`, `DeleteFunc` |
|
|
477
478
|
| `slices` | `Contains`, `Index`, `Equal`, `Compare`, `Sort`, `SortFunc`, `SortStableFunc`, `IsSorted`, `IsSortedFunc`, `Reverse`, `Max`, `Min`, `MaxFunc`, `MinFunc`, `Clone`, `Compact`, `CompactFunc`, `Concat`, `Delete`, `DeleteFunc`, `Insert`, `Replace`, `Grow`, `Clip` |
|
|
478
479
|
| `regexp` | `MustCompile`, `Compile`, `MatchString`, `QuoteMeta`; **`*Regexp`** methods: `MatchString`, `FindString`, `FindStringIndex`, `FindAllString`, `FindStringSubmatch`, `FindAllStringSubmatch`, `ReplaceAllString`, `ReplaceAllLiteralString`, `Split`, `String`. Inline flags (`(?i)`, `(?m)`, `(?s)`) are extracted automatically into the JS `RegExp` constructor. |
|
|
479
480
|
| `unicode` | `IsLetter`, `IsDigit`, `IsSpace`, `IsUpper`, `IsLower`, `IsPunct`, `IsControl`, `IsPrint`, `IsGraphic`, `ToUpper`, `ToLower` |
|
|
481
|
+
| `unicode/utf8` | `RuneCountInString`, `RuneLen`, `ValidString`, `ValidRune`, `DecodeRuneInString`, `DecodeLastRuneInString`, `FullRuneInString`; constants `RuneError`, `MaxRune`, `UTFMax` |
|
|
482
|
+
| `path` | `Base`, `Dir`, `Ext`, `Join`, `Clean`, `IsAbs`, `Split`, `Match`; `"path/filepath"` is an alias |
|
|
480
483
|
| `os` | `Exit`, `Args`, `Getenv` |
|
|
481
|
-
| `io` | `Writer`, `Reader`, `ReadWriter`, `Closer` interface types
|
|
484
|
+
| `io` | `Writer`, `Reader`, `ReadWriter`, `Closer` interface types; `ReadAll`, `EOF`, `Discard`, `WriteString` |
|
|
482
485
|
| `gom` | Browser-native declarative DOM component library. **Types**: `Node` (interface), `NodeFunc`, `Group`. **Core**: `El(tag, children...)`, `Text(s)`, `Mount(sel, node)`, `MountTo(sel, node)`. **Attributes**: `Attr`, `Class`, `Href`, `Type`, `Src`, `Placeholder`, `DataAttr`, `Style`, `For`, `Name`, `Value`, `Target`, `Rel`, `Alt`, `Title`, `Draggable`, `Role`, `AriaLabel`, `StyleAttr`; boolean: `Disabled`, `Checked`, `Selected`, `Readonly`. **Logic**: `If(cond, node)`, `Map(slice, fn)`. **Elements**: full HTML element set (`Div`, `Span`, `Button`, `Input`, `Ul`, `Li`, `Table`, `Form`, `Img`, `A`, `H1`–`H6`, …) |
|
|
483
486
|
|
|
484
487
|
### Packages & imports
|
|
@@ -558,11 +561,18 @@ Design documents for planned features are organised by release under `docs/v*/`
|
|
|
558
561
|
## Tests
|
|
559
562
|
|
|
560
563
|
```sh
|
|
561
|
-
npm test
|
|
564
|
+
npm run test:unit # unit tests only (~1076 tests, no browser required)
|
|
565
|
+
npm run test:e2e # E2E tests (Playwright, headless Chromium)
|
|
566
|
+
npm test # both
|
|
562
567
|
```
|
|
563
568
|
|
|
564
|
-
|
|
565
|
-
`.d.ts`, npm resolver, multi-file compilation, embedded structs, string
|
|
566
|
-
iteration order, integer overflow semantics, unused variable detection,
|
|
567
|
-
detection, semantic difference verification, stdlib shim packages,
|
|
568
|
-
|
|
569
|
+
**Unit tests** (1076) cover language features, type errors, edge cases, DOM (jsdom),
|
|
570
|
+
external `.d.ts`, npm resolver, multi-file compilation, embedded structs, string
|
|
571
|
+
formatting, map iteration order, integer overflow semantics, unused variable detection,
|
|
572
|
+
unused import detection, semantic difference verification, stdlib shim packages, and
|
|
573
|
+
generics.
|
|
574
|
+
|
|
575
|
+
**E2E tests** (74, Playwright) run all three example apps in a real browser and verify
|
|
576
|
+
CRUD, filtering, priority mode, persistence (reload), drag-and-drop reordering, and sync
|
|
577
|
+
status. Per-app suites check app-specific behaviour: scoped styles, stats bar, loading
|
|
578
|
+
placeholder, and `gom.If` conditional rendering.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gofront",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.8",
|
|
4
4
|
"author": "Luuk van Venrooij",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -34,14 +34,18 @@
|
|
|
34
34
|
"build:simple": "node src/index.js example/simple/src -o example/simple/app.js",
|
|
35
35
|
"build:reactive": "node src/index.js example/reactive/src -o example/reactive/app.js",
|
|
36
36
|
"build:gom": "node src/index.js example/gom/src -o example/gom/app.js",
|
|
37
|
-
"test": "
|
|
37
|
+
"test": "npm run test:unit && npm run test:e2e",
|
|
38
|
+
"test:unit": "node test/unit/run.js",
|
|
39
|
+
"test:e2e": "playwright test",
|
|
38
40
|
"format": "biome format --write .",
|
|
39
|
-
"check": "biome check .",
|
|
41
|
+
"check": "biome check . && node src/index.js example/simple/src --check && node src/index.js example/reactive/src --check && node src/index.js example/gom/src --check",
|
|
40
42
|
"prepare": "husky"
|
|
41
43
|
},
|
|
42
44
|
"devDependencies": {
|
|
43
45
|
"@biomejs/biome": "^2.4.12",
|
|
46
|
+
"@playwright/test": "^1.59.1",
|
|
44
47
|
"husky": "^9.1.7",
|
|
45
|
-
"jsdom": "^29.0.2"
|
|
48
|
+
"jsdom": "^29.0.2",
|
|
49
|
+
"serve": "^14.2.6"
|
|
46
50
|
}
|
|
47
51
|
}
|
|
@@ -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",
|
|
@@ -163,7 +161,7 @@ export const expressionGenMethods = {
|
|
|
163
161
|
return "process.argv";
|
|
164
162
|
}
|
|
165
163
|
}
|
|
166
|
-
// time constants (nanosecond durations)
|
|
164
|
+
// time constants (nanosecond durations and layout strings)
|
|
167
165
|
if (expr.expr.kind === "Ident" && expr.expr.name === "time") {
|
|
168
166
|
switch (expr.field) {
|
|
169
167
|
case "Millisecond":
|
|
@@ -174,6 +172,68 @@ export const expressionGenMethods = {
|
|
|
174
172
|
return "60000000000";
|
|
175
173
|
case "Hour":
|
|
176
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";
|
|
177
237
|
}
|
|
178
238
|
}
|
|
179
239
|
const base = this.genExpr(expr.expr);
|
|
@@ -456,7 +516,7 @@ export const expressionGenMethods = {
|
|
|
456
516
|
expr.func.kind === "SelectorExpr" &&
|
|
457
517
|
expr.func.field === "Error" &&
|
|
458
518
|
expr.func.expr._type &&
|
|
459
|
-
(expr.func.expr._type ===
|
|
519
|
+
(expr.func.expr._type === ERROR ||
|
|
460
520
|
expr.func.expr._type?.name === "error" ||
|
|
461
521
|
(expr.func.expr._type?.kind === "named" &&
|
|
462
522
|
expr.func.expr._type?.underlying?.kind === "interface"))
|
|
@@ -485,6 +545,9 @@ export const expressionGenMethods = {
|
|
|
485
545
|
if (recvName === "regexp.Regexp") {
|
|
486
546
|
return this._genRegexpMethodCall(expr.func.field, expr);
|
|
487
547
|
}
|
|
548
|
+
if (recvName === "time.Time") {
|
|
549
|
+
return this._genTimeMethodCall(expr.func.field, expr);
|
|
550
|
+
}
|
|
488
551
|
}
|
|
489
552
|
|
|
490
553
|
// Mark the callee so SelectorExpr knows it's being called, not used as a value
|
|
@@ -1096,6 +1159,10 @@ export const expressionGenMethods = {
|
|
|
1096
1159
|
// strings.Builder has { _buf: string }, bytes.Buffer has { _buf: [] }.
|
|
1097
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})`;
|
|
1098
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
|
+
}
|
|
1099
1166
|
}
|
|
1100
1167
|
return undefined;
|
|
1101
1168
|
},
|
|
@@ -1202,6 +1269,12 @@ export const expressionGenMethods = {
|
|
|
1202
1269
|
return this._genHtml(fn, a);
|
|
1203
1270
|
case "io":
|
|
1204
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);
|
|
1205
1278
|
case "gom":
|
|
1206
1279
|
return this._genGom(fn, expr);
|
|
1207
1280
|
default:
|
|
@@ -1425,6 +1498,31 @@ export const expressionGenMethods = {
|
|
|
1425
1498
|
return `(${buf} += __sprintf("%v\\n", ${restJs}))`;
|
|
1426
1499
|
return `(${buf} += __sprintf("%v", ${restJs}))`;
|
|
1427
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
|
+
}
|
|
1428
1526
|
default:
|
|
1429
1527
|
return undefined;
|
|
1430
1528
|
}
|
|
@@ -1473,6 +1571,56 @@ export const expressionGenMethods = {
|
|
|
1473
1571
|
return `${args[0]}.join(${args[1]})`;
|
|
1474
1572
|
case "EqualFold":
|
|
1475
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
|
+
}
|
|
1476
1624
|
default:
|
|
1477
1625
|
return undefined;
|
|
1478
1626
|
}
|
|
@@ -1511,6 +1659,38 @@ export const expressionGenMethods = {
|
|
|
1511
1659
|
return `${__bs}(${args[0]}).split(${__bs}(${args[1]})).map(p => ${__sb}(p))`;
|
|
1512
1660
|
case "Join":
|
|
1513
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; }}`;
|
|
1514
1694
|
default:
|
|
1515
1695
|
return undefined;
|
|
1516
1696
|
}
|
|
@@ -1535,6 +1715,14 @@ export const expressionGenMethods = {
|
|
|
1535
1715
|
return `(Number.isNaN(parseInt(${args[0]}, ${args[1]} || 10)) ? [0, "invalid syntax"] : [parseInt(${args[0]}, ${args[1]} || 10), null])`;
|
|
1536
1716
|
case "ParseBool":
|
|
1537
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]}))]`;
|
|
1538
1726
|
default:
|
|
1539
1727
|
return undefined;
|
|
1540
1728
|
}
|
|
@@ -1553,6 +1741,12 @@ export const expressionGenMethods = {
|
|
|
1553
1741
|
return `${args[0]}.sort((a, b) => ${args[1]}(a, b) ? -1 : ${args[1]}(b, a) ? 1 : 0)`;
|
|
1554
1742
|
case "SliceIsSorted":
|
|
1555
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)`;
|
|
1556
1750
|
default:
|
|
1557
1751
|
return undefined;
|
|
1558
1752
|
}
|
|
@@ -1601,6 +1795,30 @@ export const expressionGenMethods = {
|
|
|
1601
1795
|
return `(${args[1]} > 0 ? ${args[0]} === Infinity : ${args[1]} < 0 ? ${args[0]} === -Infinity : !Number.isFinite(${args[0]}))`;
|
|
1602
1796
|
case "NaN":
|
|
1603
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]})`;
|
|
1604
1822
|
default:
|
|
1605
1823
|
return undefined;
|
|
1606
1824
|
}
|
|
@@ -1665,15 +1883,142 @@ export const expressionGenMethods = {
|
|
|
1665
1883
|
}
|
|
1666
1884
|
},
|
|
1667
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
|
+
|
|
1668
1930
|
_genTime(fn, a) {
|
|
1669
1931
|
const args = a();
|
|
1670
1932
|
switch (fn) {
|
|
1671
1933
|
case "Now":
|
|
1672
|
-
return "Date
|
|
1934
|
+
return "{_d: new Date()}";
|
|
1673
1935
|
case "Since":
|
|
1674
|
-
return `(Date.now() - ${args[0]})`;
|
|
1936
|
+
return `(Date.now() - (${args[0]})._d.getTime())`;
|
|
1675
1937
|
case "Sleep":
|
|
1676
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]})`;
|
|
1677
2022
|
default:
|
|
1678
2023
|
return undefined;
|
|
1679
2024
|
}
|
|
@@ -16,9 +16,10 @@ function vlqEncode(value) {
|
|
|
16
16
|
return result;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
export function buildSourceMap(sources, mappings) {
|
|
20
|
-
// sources:
|
|
21
|
-
// mappings:
|
|
19
|
+
export function buildSourceMap(sources, mappings, sourcesContent) {
|
|
20
|
+
// sources: string[]
|
|
21
|
+
// mappings: Array<{ genLine: number, srcLine: number, srcFileIdx?: number }> (0-based)
|
|
22
|
+
// sourcesContent: string[] | undefined — original file contents, embedded for DevTools
|
|
22
23
|
// Emits one segment per generated line, column 0 → source line (delta-encoded).
|
|
23
24
|
const lines = [];
|
|
24
25
|
let prevSrcLine = 0;
|
|
@@ -44,10 +45,12 @@ export function buildSourceMap(sources, mappings) {
|
|
|
44
45
|
lines.push(""); // no mapping for this generated line
|
|
45
46
|
}
|
|
46
47
|
}
|
|
47
|
-
|
|
48
|
+
const map = {
|
|
48
49
|
version: 3,
|
|
49
50
|
sources,
|
|
50
51
|
names: [],
|
|
51
52
|
mappings: lines.join(";"),
|
|
52
|
-
}
|
|
53
|
+
};
|
|
54
|
+
if (sourcesContent) map.sourcesContent = sourcesContent;
|
|
55
|
+
return JSON.stringify(map);
|
|
53
56
|
}
|
package/src/codegen.js
CHANGED
|
@@ -52,6 +52,9 @@ export class CodeGen {
|
|
|
52
52
|
this._usesCdiv = false;
|
|
53
53
|
this._usesError = false;
|
|
54
54
|
this._usesErrorIs = false;
|
|
55
|
+
this._usesPathClean = false;
|
|
56
|
+
this._usesTimeFmt = false;
|
|
57
|
+
this._usesTimeParse = false;
|
|
55
58
|
// Iterator (range-over-func) context
|
|
56
59
|
this._inIteratorBody = false;
|
|
57
60
|
this._iterDepth = 0;
|
|
@@ -226,6 +229,47 @@ export class CodeGen {
|
|
|
226
229
|
helpers.push(
|
|
227
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; };',
|
|
228
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
|
+
);
|
|
229
273
|
|
|
230
274
|
if (helpers.length > 0) this.out.unshift(...helpers, "");
|
|
231
275
|
|
|
@@ -248,9 +292,10 @@ export class CodeGen {
|
|
|
248
292
|
|
|
249
293
|
// Returns a source map JSON string for the last generate() call.
|
|
250
294
|
// sources: string[] of source filenames (relative to the output file).
|
|
251
|
-
|
|
295
|
+
// sourcesContent: string[] of original file contents (embedded for DevTools breakpoints).
|
|
296
|
+
getSourceMap(sources, sourcesContent) {
|
|
252
297
|
const srcArray = Array.isArray(sources) ? sources : [sources];
|
|
253
|
-
return buildSourceMap(srcArray, this._srcMappings);
|
|
298
|
+
return buildSourceMap(srcArray, this._srcMappings, sourcesContent);
|
|
254
299
|
}
|
|
255
300
|
|
|
256
301
|
// ── Type declarations ────────────────────────────────────────
|
|
@@ -534,7 +579,10 @@ export class CodeGen {
|
|
|
534
579
|
}
|
|
535
580
|
} else {
|
|
536
581
|
const zero = spec.type ? this.zeroValueForTypeNode(spec.type) : "null";
|
|
537
|
-
for (const name of spec.names)
|
|
582
|
+
for (const name of spec.names) {
|
|
583
|
+
const val = this._boxedVars.has(name) ? `{ value: ${zero} }` : zero;
|
|
584
|
+
this.line(`let ${name} = ${val};`);
|
|
585
|
+
}
|
|
538
586
|
}
|
|
539
587
|
}
|
|
540
588
|
}
|
package/src/compiler.js
CHANGED
|
@@ -170,7 +170,8 @@ export function compileFiles(files, options = {}) {
|
|
|
170
170
|
if (options.sourceMap) {
|
|
171
171
|
const outputDir = options.outputDir ?? fromDir;
|
|
172
172
|
const sourceFiles = files.map((f) => relative(outputDir, f));
|
|
173
|
-
const
|
|
173
|
+
const sourcesContent = files.map((f) => readFileSync(f, "utf8"));
|
|
174
|
+
const map = codegen.getSourceMap(sourceFiles, sourcesContent);
|
|
174
175
|
const b64 = Buffer.from(map).toString("base64");
|
|
175
176
|
js += `\n//# sourceMappingURL=data:application/json;base64,${b64}`;
|
|
176
177
|
}
|
package/src/dev-server.js
CHANGED
|
@@ -10,6 +10,7 @@ const MIME = {
|
|
|
10
10
|
".js": "application/javascript; charset=utf-8",
|
|
11
11
|
".css": "text/css; charset=utf-8",
|
|
12
12
|
".json": "application/json; charset=utf-8",
|
|
13
|
+
".go": "text/plain; charset=utf-8",
|
|
13
14
|
".svg": "image/svg+xml",
|
|
14
15
|
".png": "image/png",
|
|
15
16
|
".jpg": "image/jpeg",
|
package/src/index.js
CHANGED
|
@@ -232,7 +232,7 @@ function runCompile() {
|
|
|
232
232
|
if (sourceMap) {
|
|
233
233
|
const outputDir = outputFile ? dirname(resolve(outputFile)) : resolve(".");
|
|
234
234
|
const srcPath = relative(outputDir, inputPath);
|
|
235
|
-
const map = cg.getSourceMap(srcPath);
|
|
235
|
+
const map = cg.getSourceMap(srcPath, [source]);
|
|
236
236
|
const b64 = Buffer.from(map).toString("base64");
|
|
237
237
|
js += `\n//# sourceMappingURL=data:application/json;base64,${b64}`;
|
|
238
238
|
}
|
package/src/resolver.js
CHANGED
|
@@ -25,11 +25,39 @@ function findNodeModules(startDir) {
|
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
// Built-in stdlib package paths that are always available as globals — no resolution needed.
|
|
29
|
+
const BUILTIN_PACKAGES = new Set([
|
|
30
|
+
"fmt",
|
|
31
|
+
"strings",
|
|
32
|
+
"bytes",
|
|
33
|
+
"strconv",
|
|
34
|
+
"sort",
|
|
35
|
+
"math",
|
|
36
|
+
"math/rand",
|
|
37
|
+
"errors",
|
|
38
|
+
"time",
|
|
39
|
+
"unicode",
|
|
40
|
+
"unicode/utf8",
|
|
41
|
+
"os",
|
|
42
|
+
"slices",
|
|
43
|
+
"html",
|
|
44
|
+
"io",
|
|
45
|
+
"gom",
|
|
46
|
+
"maps",
|
|
47
|
+
"regexp",
|
|
48
|
+
"path",
|
|
49
|
+
"path/filepath",
|
|
50
|
+
]);
|
|
51
|
+
|
|
28
52
|
// Returns true for local relative paths (start with ./ or ../)
|
|
29
53
|
export function isLocalPath(importPath) {
|
|
30
54
|
return importPath.startsWith("./") || importPath.startsWith("../");
|
|
31
55
|
}
|
|
32
56
|
|
|
57
|
+
export function isBuiltinPackage(importPath) {
|
|
58
|
+
return BUILTIN_PACKAGES.has(importPath);
|
|
59
|
+
}
|
|
60
|
+
|
|
33
61
|
// Resolve a local relative import path to an absolute directory of .go files.
|
|
34
62
|
// Returns the absolute dir path, or null if it doesn't contain any .go files.
|
|
35
63
|
export function resolveGwDir(importPath, fromFile) {
|
|
@@ -85,6 +113,7 @@ export function resolveAll(imports, fromFile, parseDts) {
|
|
|
85
113
|
for (const { path } of imp.imports) {
|
|
86
114
|
if (path.startsWith("js:")) continue; // handled separately
|
|
87
115
|
if (isLocalPath(path)) continue; // local .go packages — handled by compiler
|
|
116
|
+
if (isBuiltinPackage(path)) continue; // built-in stdlib namespaces — always available
|
|
88
117
|
|
|
89
118
|
if (resolved.has(path)) continue;
|
|
90
119
|
|
|
@@ -8,6 +8,9 @@
|
|
|
8
8
|
|
|
9
9
|
import { ANY, BOOL, ERROR, FLOAT64, INT, STRING, VOID } from "./types.js";
|
|
10
10
|
|
|
11
|
+
// time.Time named type — used by the time package
|
|
12
|
+
const TIME_T = { kind: "named", name: "time.Time", underlying: ANY };
|
|
13
|
+
|
|
11
14
|
export function setupGlobals(globals, types) {
|
|
12
15
|
// Browser globals — typed as 'any' so any access/call is permitted
|
|
13
16
|
const browserGlobals = [
|
|
@@ -113,6 +116,24 @@ export function setupGlobals(globals, types) {
|
|
|
113
116
|
returns: [INT, ERROR],
|
|
114
117
|
variadic: true,
|
|
115
118
|
},
|
|
119
|
+
Sscan: {
|
|
120
|
+
kind: "func",
|
|
121
|
+
params: [STRING, ANY],
|
|
122
|
+
returns: [INT, ERROR],
|
|
123
|
+
variadic: true,
|
|
124
|
+
},
|
|
125
|
+
Sscanln: {
|
|
126
|
+
kind: "func",
|
|
127
|
+
params: [STRING, ANY],
|
|
128
|
+
returns: [INT, ERROR],
|
|
129
|
+
variadic: true,
|
|
130
|
+
},
|
|
131
|
+
Sscanf: {
|
|
132
|
+
kind: "func",
|
|
133
|
+
params: [STRING, STRING, ANY],
|
|
134
|
+
returns: [INT, ERROR],
|
|
135
|
+
variadic: true,
|
|
136
|
+
},
|
|
116
137
|
},
|
|
117
138
|
});
|
|
118
139
|
|
|
@@ -164,6 +185,71 @@ export function setupGlobals(globals, types) {
|
|
|
164
185
|
returns: [STRING],
|
|
165
186
|
},
|
|
166
187
|
EqualFold: strFn2(STRING, BOOL),
|
|
188
|
+
Fields: strFn1({ kind: "slice", elem: STRING }),
|
|
189
|
+
Cut: {
|
|
190
|
+
kind: "func",
|
|
191
|
+
params: [STRING, STRING],
|
|
192
|
+
returns: [STRING, STRING, BOOL],
|
|
193
|
+
},
|
|
194
|
+
CutPrefix: {
|
|
195
|
+
kind: "func",
|
|
196
|
+
params: [STRING, STRING],
|
|
197
|
+
returns: [STRING, BOOL],
|
|
198
|
+
},
|
|
199
|
+
CutSuffix: {
|
|
200
|
+
kind: "func",
|
|
201
|
+
params: [STRING, STRING],
|
|
202
|
+
returns: [STRING, BOOL],
|
|
203
|
+
},
|
|
204
|
+
SplitN: strFn3(STRING, INT, { kind: "slice", elem: STRING }),
|
|
205
|
+
SplitAfter: strFn2(STRING, { kind: "slice", elem: STRING }),
|
|
206
|
+
SplitAfterN: strFn3(STRING, INT, { kind: "slice", elem: STRING }),
|
|
207
|
+
IndexAny: strFn2(STRING, INT),
|
|
208
|
+
LastIndexAny: strFn2(STRING, INT),
|
|
209
|
+
ContainsAny: strFn2(STRING, BOOL),
|
|
210
|
+
ContainsRune: strFn2(INT, BOOL),
|
|
211
|
+
IndexRune: strFn2(INT, INT),
|
|
212
|
+
IndexByte: strFn2(INT, INT),
|
|
213
|
+
LastIndexByte: strFn2(INT, INT),
|
|
214
|
+
Map: {
|
|
215
|
+
kind: "func",
|
|
216
|
+
params: [ANY, STRING],
|
|
217
|
+
returns: [STRING],
|
|
218
|
+
},
|
|
219
|
+
Title: strFn1(STRING),
|
|
220
|
+
ToTitle: strFn1(STRING),
|
|
221
|
+
TrimFunc: {
|
|
222
|
+
kind: "func",
|
|
223
|
+
params: [STRING, ANY],
|
|
224
|
+
returns: [STRING],
|
|
225
|
+
},
|
|
226
|
+
TrimLeftFunc: {
|
|
227
|
+
kind: "func",
|
|
228
|
+
params: [STRING, ANY],
|
|
229
|
+
returns: [STRING],
|
|
230
|
+
},
|
|
231
|
+
TrimRightFunc: {
|
|
232
|
+
kind: "func",
|
|
233
|
+
params: [STRING, ANY],
|
|
234
|
+
returns: [STRING],
|
|
235
|
+
},
|
|
236
|
+
IndexFunc: {
|
|
237
|
+
kind: "func",
|
|
238
|
+
params: [STRING, ANY],
|
|
239
|
+
returns: [INT],
|
|
240
|
+
},
|
|
241
|
+
LastIndexFunc: {
|
|
242
|
+
kind: "func",
|
|
243
|
+
params: [STRING, ANY],
|
|
244
|
+
returns: [INT],
|
|
245
|
+
},
|
|
246
|
+
NewReplacer: {
|
|
247
|
+
kind: "func",
|
|
248
|
+
params: [STRING],
|
|
249
|
+
returns: [ANY],
|
|
250
|
+
variadic: true,
|
|
251
|
+
},
|
|
252
|
+
NewReader: { kind: "func", params: [STRING], returns: [ANY] },
|
|
167
253
|
},
|
|
168
254
|
});
|
|
169
255
|
|
|
@@ -205,6 +291,46 @@ export function setupGlobals(globals, types) {
|
|
|
205
291
|
params: [{ kind: "slice", elem: BYTE_SLICE }, BYTE_SLICE],
|
|
206
292
|
returns: [BYTE_SLICE],
|
|
207
293
|
},
|
|
294
|
+
ReplaceAll: {
|
|
295
|
+
kind: "func",
|
|
296
|
+
params: [BYTE_SLICE, BYTE_SLICE, BYTE_SLICE],
|
|
297
|
+
returns: [BYTE_SLICE],
|
|
298
|
+
},
|
|
299
|
+
TrimPrefix: byFn2(BYTE_SLICE, BYTE_SLICE),
|
|
300
|
+
TrimSuffix: byFn2(BYTE_SLICE, BYTE_SLICE),
|
|
301
|
+
TrimLeft: byFn2(STRING, BYTE_SLICE),
|
|
302
|
+
TrimRight: byFn2(STRING, BYTE_SLICE),
|
|
303
|
+
TrimFunc: {
|
|
304
|
+
kind: "func",
|
|
305
|
+
params: [BYTE_SLICE, ANY],
|
|
306
|
+
returns: [BYTE_SLICE],
|
|
307
|
+
},
|
|
308
|
+
IndexByte: byFn2(INT, INT),
|
|
309
|
+
LastIndex: byFn2(BYTE_SLICE, INT),
|
|
310
|
+
LastIndexByte: byFn2(INT, INT),
|
|
311
|
+
Fields: byFn1({ kind: "slice", elem: BYTE_SLICE }),
|
|
312
|
+
Cut: {
|
|
313
|
+
kind: "func",
|
|
314
|
+
params: [BYTE_SLICE, BYTE_SLICE],
|
|
315
|
+
returns: [BYTE_SLICE, BYTE_SLICE, BOOL],
|
|
316
|
+
},
|
|
317
|
+
ContainsAny: byFn2(STRING, BOOL),
|
|
318
|
+
ContainsRune: byFn2(INT, BOOL),
|
|
319
|
+
Map: {
|
|
320
|
+
kind: "func",
|
|
321
|
+
params: [ANY, BYTE_SLICE],
|
|
322
|
+
returns: [BYTE_SLICE],
|
|
323
|
+
},
|
|
324
|
+
SplitN: {
|
|
325
|
+
kind: "func",
|
|
326
|
+
params: [BYTE_SLICE, BYTE_SLICE, INT],
|
|
327
|
+
returns: [{ kind: "slice", elem: BYTE_SLICE }],
|
|
328
|
+
},
|
|
329
|
+
NewReader: {
|
|
330
|
+
kind: "func",
|
|
331
|
+
params: [BYTE_SLICE],
|
|
332
|
+
returns: [ANY],
|
|
333
|
+
},
|
|
208
334
|
},
|
|
209
335
|
});
|
|
210
336
|
|
|
@@ -241,6 +367,22 @@ export function setupGlobals(globals, types) {
|
|
|
241
367
|
params: [STRING],
|
|
242
368
|
returns: [BOOL, ERROR],
|
|
243
369
|
},
|
|
370
|
+
Quote: { kind: "func", params: [STRING], returns: [STRING] },
|
|
371
|
+
Unquote: {
|
|
372
|
+
kind: "func",
|
|
373
|
+
params: [STRING],
|
|
374
|
+
returns: [STRING, ERROR],
|
|
375
|
+
},
|
|
376
|
+
AppendInt: {
|
|
377
|
+
kind: "func",
|
|
378
|
+
params: [{ kind: "slice", elem: INT }, INT, INT],
|
|
379
|
+
returns: [{ kind: "slice", elem: INT }],
|
|
380
|
+
},
|
|
381
|
+
AppendFloat: {
|
|
382
|
+
kind: "func",
|
|
383
|
+
params: [{ kind: "slice", elem: INT }, FLOAT64, INT, INT, INT],
|
|
384
|
+
returns: [{ kind: "slice", elem: INT }],
|
|
385
|
+
},
|
|
244
386
|
},
|
|
245
387
|
});
|
|
246
388
|
|
|
@@ -279,6 +421,26 @@ export function setupGlobals(globals, types) {
|
|
|
279
421
|
params: [ANY, ANY],
|
|
280
422
|
returns: [BOOL],
|
|
281
423
|
},
|
|
424
|
+
Search: {
|
|
425
|
+
kind: "func",
|
|
426
|
+
params: [INT, ANY],
|
|
427
|
+
returns: [INT],
|
|
428
|
+
},
|
|
429
|
+
IntsAreSorted: {
|
|
430
|
+
kind: "func",
|
|
431
|
+
params: [{ kind: "slice", elem: INT }],
|
|
432
|
+
returns: [BOOL],
|
|
433
|
+
},
|
|
434
|
+
Float64sAreSorted: {
|
|
435
|
+
kind: "func",
|
|
436
|
+
params: [{ kind: "slice", elem: FLOAT64 }],
|
|
437
|
+
returns: [BOOL],
|
|
438
|
+
},
|
|
439
|
+
StringsAreSorted: {
|
|
440
|
+
kind: "func",
|
|
441
|
+
params: [{ kind: "slice", elem: STRING }],
|
|
442
|
+
returns: [BOOL],
|
|
443
|
+
},
|
|
282
444
|
},
|
|
283
445
|
});
|
|
284
446
|
|
|
@@ -327,6 +489,26 @@ export function setupGlobals(globals, types) {
|
|
|
327
489
|
returns: [BOOL],
|
|
328
490
|
},
|
|
329
491
|
NaN: { kind: "func", params: [], returns: [FLOAT64] },
|
|
492
|
+
Atan: { kind: "func", params: [FLOAT64], returns: [FLOAT64] },
|
|
493
|
+
Atan2: { kind: "func", params: [FLOAT64, FLOAT64], returns: [FLOAT64] },
|
|
494
|
+
Asin: { kind: "func", params: [FLOAT64], returns: [FLOAT64] },
|
|
495
|
+
Acos: { kind: "func", params: [FLOAT64], returns: [FLOAT64] },
|
|
496
|
+
Exp: { kind: "func", params: [FLOAT64], returns: [FLOAT64] },
|
|
497
|
+
Exp2: { kind: "func", params: [FLOAT64], returns: [FLOAT64] },
|
|
498
|
+
Trunc: { kind: "func", params: [FLOAT64], returns: [FLOAT64] },
|
|
499
|
+
Hypot: { kind: "func", params: [FLOAT64, FLOAT64], returns: [FLOAT64] },
|
|
500
|
+
Signbit: { kind: "func", params: [FLOAT64], returns: [BOOL] },
|
|
501
|
+
Copysign: {
|
|
502
|
+
kind: "func",
|
|
503
|
+
params: [FLOAT64, FLOAT64],
|
|
504
|
+
returns: [FLOAT64],
|
|
505
|
+
},
|
|
506
|
+
Dim: { kind: "func", params: [FLOAT64, FLOAT64], returns: [FLOAT64] },
|
|
507
|
+
Remainder: {
|
|
508
|
+
kind: "func",
|
|
509
|
+
params: [FLOAT64, FLOAT64],
|
|
510
|
+
returns: [FLOAT64],
|
|
511
|
+
},
|
|
330
512
|
// Constants
|
|
331
513
|
Pi: FLOAT64,
|
|
332
514
|
E: FLOAT64,
|
|
@@ -353,15 +535,57 @@ export function setupGlobals(globals, types) {
|
|
|
353
535
|
kind: "namespace",
|
|
354
536
|
name: "time",
|
|
355
537
|
members: {
|
|
356
|
-
Now: { kind: "func", params: [], returns: [
|
|
357
|
-
Since: { kind: "func", params: [
|
|
538
|
+
Now: { kind: "func", params: [], returns: [TIME_T] },
|
|
539
|
+
Since: { kind: "func", params: [TIME_T], returns: [INT] },
|
|
358
540
|
Sleep: { kind: "func", params: [ANY], returns: [VOID], async: true },
|
|
541
|
+
Parse: {
|
|
542
|
+
kind: "func",
|
|
543
|
+
params: [STRING, STRING],
|
|
544
|
+
returns: [TIME_T, ERROR],
|
|
545
|
+
},
|
|
546
|
+
Unix: { kind: "func", params: [INT, INT], returns: [TIME_T] },
|
|
547
|
+
Date: {
|
|
548
|
+
kind: "func",
|
|
549
|
+
params: [INT, INT, INT, INT, INT, INT, INT, ANY],
|
|
550
|
+
returns: [TIME_T],
|
|
551
|
+
},
|
|
359
552
|
Millisecond: INT,
|
|
360
553
|
Second: INT,
|
|
361
554
|
Minute: INT,
|
|
362
555
|
Hour: INT,
|
|
556
|
+
// Layout constants
|
|
557
|
+
RFC3339: STRING,
|
|
558
|
+
RFC3339Nano: STRING,
|
|
559
|
+
DateOnly: STRING,
|
|
560
|
+
TimeOnly: STRING,
|
|
561
|
+
DateTime: STRING,
|
|
562
|
+
// Location constants
|
|
563
|
+
UTC: ANY,
|
|
564
|
+
Local: ANY,
|
|
565
|
+
// Month constants
|
|
566
|
+
January: INT,
|
|
567
|
+
February: INT,
|
|
568
|
+
March: INT,
|
|
569
|
+
April: INT,
|
|
570
|
+
May: INT,
|
|
571
|
+
June: INT,
|
|
572
|
+
July: INT,
|
|
573
|
+
August: INT,
|
|
574
|
+
September: INT,
|
|
575
|
+
October: INT,
|
|
576
|
+
November: INT,
|
|
577
|
+
December: INT,
|
|
578
|
+
// Weekday constants
|
|
579
|
+
Sunday: INT,
|
|
580
|
+
Monday: INT,
|
|
581
|
+
Tuesday: INT,
|
|
582
|
+
Wednesday: INT,
|
|
583
|
+
Thursday: INT,
|
|
584
|
+
Friday: INT,
|
|
585
|
+
Saturday: INT,
|
|
363
586
|
},
|
|
364
587
|
});
|
|
588
|
+
types.set("time.Time", TIME_T);
|
|
365
589
|
|
|
366
590
|
// unicode package
|
|
367
591
|
const runeToStr = { kind: "func", params: [INT], returns: [BOOL] };
|
|
@@ -455,22 +679,30 @@ export function setupGlobals(globals, types) {
|
|
|
455
679
|
// io package — Writer interface + WriteString
|
|
456
680
|
// io.Writer is typed as ANY so GoFront's built-in writer types (strings.Builder,
|
|
457
681
|
// bytes.Buffer) are accepted without explicit interface-satisfaction registration.
|
|
682
|
+
const BYTE_SLICE_IO = { kind: "slice", elem: INT };
|
|
458
683
|
globals.define("io", {
|
|
459
684
|
kind: "namespace",
|
|
460
685
|
name: "io",
|
|
461
686
|
members: {
|
|
462
687
|
Writer: ANY,
|
|
688
|
+
Reader: ANY,
|
|
463
689
|
EOF: ERROR,
|
|
464
690
|
WriteString: {
|
|
465
691
|
kind: "func",
|
|
466
692
|
params: [ANY, STRING],
|
|
467
693
|
returns: [INT, ERROR],
|
|
468
694
|
},
|
|
695
|
+
ReadAll: {
|
|
696
|
+
kind: "func",
|
|
697
|
+
params: [ANY],
|
|
698
|
+
returns: [BYTE_SLICE_IO, ERROR],
|
|
699
|
+
},
|
|
469
700
|
Discard: ANY,
|
|
470
701
|
},
|
|
471
702
|
});
|
|
472
703
|
// Register io.Writer as a type alias so it resolves in parameter/field annotations.
|
|
473
704
|
types.set("io.Writer", ANY);
|
|
705
|
+
types.set("io.Reader", ANY);
|
|
474
706
|
|
|
475
707
|
// gom package — browser-native component built-in
|
|
476
708
|
const GOM_NODE_T = {
|
|
@@ -742,6 +974,77 @@ export function setupGlobals(globals, types) {
|
|
|
742
974
|
},
|
|
743
975
|
});
|
|
744
976
|
|
|
977
|
+
// math/rand package — import "math/rand" → local name "rand"
|
|
978
|
+
globals.define("rand", {
|
|
979
|
+
kind: "namespace",
|
|
980
|
+
name: "rand",
|
|
981
|
+
members: {
|
|
982
|
+
Intn: { kind: "func", params: [INT], returns: [INT] },
|
|
983
|
+
Float64: { kind: "func", params: [], returns: [FLOAT64] },
|
|
984
|
+
Float32: { kind: "func", params: [], returns: [FLOAT64] },
|
|
985
|
+
Int: { kind: "func", params: [], returns: [INT] },
|
|
986
|
+
Int63: { kind: "func", params: [], returns: [INT] },
|
|
987
|
+
Int63n: { kind: "func", params: [INT], returns: [INT] },
|
|
988
|
+
Int31: { kind: "func", params: [], returns: [INT] },
|
|
989
|
+
Int31n: { kind: "func", params: [INT], returns: [INT] },
|
|
990
|
+
Seed: { kind: "func", params: [INT], returns: [VOID] },
|
|
991
|
+
Shuffle: { kind: "func", params: [INT, ANY], returns: [VOID] },
|
|
992
|
+
Perm: {
|
|
993
|
+
kind: "func",
|
|
994
|
+
params: [INT],
|
|
995
|
+
returns: [{ kind: "slice", elem: INT }],
|
|
996
|
+
},
|
|
997
|
+
},
|
|
998
|
+
});
|
|
999
|
+
|
|
1000
|
+
// unicode/utf8 package — import "unicode/utf8" → local name "utf8"
|
|
1001
|
+
globals.define("utf8", {
|
|
1002
|
+
kind: "namespace",
|
|
1003
|
+
name: "utf8",
|
|
1004
|
+
members: {
|
|
1005
|
+
RuneCountInString: { kind: "func", params: [STRING], returns: [INT] },
|
|
1006
|
+
RuneLen: { kind: "func", params: [INT], returns: [INT] },
|
|
1007
|
+
ValidString: { kind: "func", params: [STRING], returns: [BOOL] },
|
|
1008
|
+
ValidRune: { kind: "func", params: [INT], returns: [BOOL] },
|
|
1009
|
+
DecodeRuneInString: {
|
|
1010
|
+
kind: "func",
|
|
1011
|
+
params: [STRING],
|
|
1012
|
+
returns: [INT, INT],
|
|
1013
|
+
},
|
|
1014
|
+
DecodeLastRuneInString: {
|
|
1015
|
+
kind: "func",
|
|
1016
|
+
params: [STRING],
|
|
1017
|
+
returns: [INT, INT],
|
|
1018
|
+
},
|
|
1019
|
+
FullRuneInString: { kind: "func", params: [STRING], returns: [BOOL] },
|
|
1020
|
+
// Constants
|
|
1021
|
+
RuneError: INT,
|
|
1022
|
+
MaxRune: INT,
|
|
1023
|
+
UTFMax: INT,
|
|
1024
|
+
},
|
|
1025
|
+
});
|
|
1026
|
+
|
|
1027
|
+
// path package — import "path" or "path/filepath" → local name "path"
|
|
1028
|
+
globals.define("path", {
|
|
1029
|
+
kind: "namespace",
|
|
1030
|
+
name: "path",
|
|
1031
|
+
members: {
|
|
1032
|
+
Base: { kind: "func", params: [STRING], returns: [STRING] },
|
|
1033
|
+
Dir: { kind: "func", params: [STRING], returns: [STRING] },
|
|
1034
|
+
Ext: { kind: "func", params: [STRING], returns: [STRING] },
|
|
1035
|
+
Join: {
|
|
1036
|
+
kind: "func",
|
|
1037
|
+
params: [STRING],
|
|
1038
|
+
returns: [STRING],
|
|
1039
|
+
variadic: true,
|
|
1040
|
+
},
|
|
1041
|
+
Clean: { kind: "func", params: [STRING], returns: [STRING] },
|
|
1042
|
+
IsAbs: { kind: "func", params: [STRING], returns: [BOOL] },
|
|
1043
|
+
Split: { kind: "func", params: [STRING], returns: [STRING, STRING] },
|
|
1044
|
+
Match: { kind: "func", params: [STRING, STRING], returns: [BOOL, ERROR] },
|
|
1045
|
+
},
|
|
1046
|
+
});
|
|
1047
|
+
|
|
745
1048
|
// Built-in functions
|
|
746
1049
|
globals.define("append", { kind: "builtin", name: "append" });
|
|
747
1050
|
globals.define("len", { kind: "builtin", name: "len" });
|
package/src/typechecker/types.js
CHANGED
|
@@ -93,14 +93,6 @@ export const BASIC_TYPES = {
|
|
|
93
93
|
|
|
94
94
|
export const COMPARABLE = { kind: "basic", name: "comparable" };
|
|
95
95
|
|
|
96
|
-
export function isTypeParam(t) {
|
|
97
|
-
return t?.kind === "typeParam";
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
export function isGeneric(t) {
|
|
101
|
-
return t?.kind === "generic";
|
|
102
|
-
}
|
|
103
|
-
|
|
104
96
|
function makeBasicPredicate(...names) {
|
|
105
97
|
const set = new Set(names);
|
|
106
98
|
const pred = (t) => {
|
|
@@ -137,13 +129,6 @@ export function isNil(t) {
|
|
|
137
129
|
export function isVoid(t) {
|
|
138
130
|
return t?.kind === "basic" && t.name === "void";
|
|
139
131
|
}
|
|
140
|
-
export function isError(t) {
|
|
141
|
-
if (!t) return false;
|
|
142
|
-
if (t === ERROR) return true;
|
|
143
|
-
if (t.kind === "interface" && t.name === "error") return true;
|
|
144
|
-
if (t.kind === "named" && isError(t.underlying)) return true;
|
|
145
|
-
return false;
|
|
146
|
-
}
|
|
147
132
|
export function isPointer(t) {
|
|
148
133
|
if (!t) return false;
|
|
149
134
|
if (t.kind === "pointer") return true;
|