kopscript 0.4.1 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LLM.md +8 -1
- package/README.md +15 -0
- package/dist/checker.js +30 -20
- package/dist/parser.js +2 -1
- package/package.json +1 -1
package/LLM.md
CHANGED
|
@@ -281,7 +281,8 @@ Box<string> sb = new Box<string>("hi");
|
|
|
281
281
|
print(nb.Get()); // 5
|
|
282
282
|
```
|
|
283
283
|
|
|
284
|
-
One unconstrained, invariant type parameter per class/interface
|
|
284
|
+
One unconstrained, invariant type parameter per class/interface (real *or* `extern` — see
|
|
285
|
+
the `extern` section below) — the whole feature.
|
|
285
286
|
Erased at codegen (JS has no generics either; `Box<number>` and `Box<string>` both
|
|
286
287
|
compile to the exact same plain `class Box`), so there's no runtime cost and no way to
|
|
287
288
|
inspect `T` at runtime.
|
|
@@ -387,6 +388,12 @@ KopScript-declared name exactly. Extern class members use real JS member names v
|
|
|
387
388
|
(camelCase, no rename mechanism). No inheritance modeling between two `extern class`
|
|
388
389
|
declarations — each stands alone.
|
|
389
390
|
|
|
391
|
+
`extern class` can carry `<T>` (`extern class Box<T> { constructor(T v); T Value { get; } }
|
|
392
|
+
from "some-package";`) — identical rules to a real generic class (see "Generics" above:
|
|
393
|
+
one unconstrained invariant parameter, erased, no base-list generics), so a generic type
|
|
394
|
+
from another package instantiates and type-checks exactly like a local one
|
|
395
|
+
(`Box<number>`, arity/invariance errors included).
|
|
396
|
+
|
|
390
397
|
**Never write `async` on an extern function/method signature** — declare its return type
|
|
391
398
|
as `task`/`task<T>` directly (`task<string> text();`, not `async task<string> text();`).
|
|
392
399
|
`async` only means something for a *body* the checker validates (legalizing `await`
|
package/README.md
CHANGED
|
@@ -347,6 +347,21 @@ consumes `Component`/`Router` themselves via `extern class ... from "kopular/...
|
|
|
347
347
|
proving `extern` works as a real cross-*package* boundary, not just for describing DOM
|
|
348
348
|
globals within a single project.
|
|
349
349
|
|
|
350
|
+
An `extern class` can carry its own type parameter, `extern class Box<T> { ... }` —
|
|
351
|
+
exactly the same single-type-parameter rules as a real generic class (see "Generics"
|
|
352
|
+
above: invariant, unconstrained, erased, can't appear in a base list), so a generic type
|
|
353
|
+
from another package (e.g. Kopular's `FormField<T>`) can be described and instantiated
|
|
354
|
+
generically, not just per concrete type:
|
|
355
|
+
|
|
356
|
+
```ks
|
|
357
|
+
extern class Box<T> {
|
|
358
|
+
constructor(T v);
|
|
359
|
+
T Value { get; }
|
|
360
|
+
} from "some-package";
|
|
361
|
+
|
|
362
|
+
Box<number> nb = new Box<number>(5);
|
|
363
|
+
```
|
|
364
|
+
|
|
350
365
|
Now that KopScript has `async`/`await` and `task<T>` (see below), a Promise-based JS API is
|
|
351
366
|
describable too — `extern task<string> Fetch(...) from "..." as "fetch";` is legitimate,
|
|
352
367
|
and `await`ing it works exactly like awaiting any other KopScript task. What's still not cleanly
|
package/dist/checker.js
CHANGED
|
@@ -114,6 +114,9 @@ export class Checker {
|
|
|
114
114
|
for (const i of interfaceDecls)
|
|
115
115
|
if (i.typeParam)
|
|
116
116
|
this.genericTypeParams.set(i.name, i.typeParam);
|
|
117
|
+
for (const c of externClassDecls)
|
|
118
|
+
if (c.typeParam)
|
|
119
|
+
this.genericTypeParams.set(c.name, c.typeParam);
|
|
117
120
|
for (const e of enumDecls)
|
|
118
121
|
this.registerEnum(e);
|
|
119
122
|
for (const i of interfaceDecls)
|
|
@@ -169,28 +172,35 @@ export class Checker {
|
|
|
169
172
|
});
|
|
170
173
|
}
|
|
171
174
|
registerExternClass(decl) {
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
175
|
+
// Same withTypeParamInScope treatment registerClass gives a real class's
|
|
176
|
+
// signature (fields/methods/ctor params) — an extern class's own `T`
|
|
177
|
+
// needs to resolve the same way while its declared members are being
|
|
178
|
+
// resolved, e.g. `state<T> Value;` in an `extern class FormField<T>`.
|
|
179
|
+
const { fields, staticFields, methods, staticMethods, ownCtorParams } = this.withTypeParamInScope(decl.typeParam, () => {
|
|
180
|
+
const fields = new Map();
|
|
181
|
+
const staticFields = new Map();
|
|
182
|
+
for (const p of decl.properties) {
|
|
183
|
+
const info = { type: this.resolveType(p.type, decl.line, decl.col), visibility: "public", hasSetter: p.hasSetter };
|
|
184
|
+
(p.isStatic ? staticFields : fields).set(p.name, info);
|
|
185
|
+
}
|
|
186
|
+
const methods = new Map();
|
|
187
|
+
const staticMethods = new Map();
|
|
188
|
+
for (const m of decl.methods) {
|
|
189
|
+
const info = {
|
|
190
|
+
params: m.params.map((p) => this.resolveType(p.type, decl.line, decl.col)),
|
|
191
|
+
returnType: this.resolveType(m.returnType, decl.line, decl.col),
|
|
192
|
+
visibility: "public",
|
|
193
|
+
isVirtual: m.isVirtual,
|
|
194
|
+
isOverride: false,
|
|
195
|
+
};
|
|
196
|
+
(m.isStatic ? staticMethods : methods).set(m.name, info);
|
|
197
|
+
}
|
|
198
|
+
const ownCtorParams = decl.hasConstructor ? decl.ctorParams.map((p) => this.resolveType(p.type, decl.line, decl.col)) : null;
|
|
199
|
+
return { fields, staticFields, methods, staticMethods, ownCtorParams };
|
|
200
|
+
});
|
|
191
201
|
this.classes.set(decl.name, {
|
|
192
202
|
name: decl.name,
|
|
193
|
-
typeParam:
|
|
203
|
+
typeParam: decl.typeParam,
|
|
194
204
|
superclass: null,
|
|
195
205
|
interfaces: [],
|
|
196
206
|
fields,
|
package/dist/parser.js
CHANGED
|
@@ -455,6 +455,7 @@ export class Parser {
|
|
|
455
455
|
}
|
|
456
456
|
parseExternClassBody(start, isExported) {
|
|
457
457
|
const name = this.consume(TokenKind.Identifier, "Expected class name").lexeme;
|
|
458
|
+
const typeParam = this.parseOptionalTypeParam();
|
|
458
459
|
this.consume(TokenKind.LBrace, "Expected '{' before extern class body");
|
|
459
460
|
let hasConstructor = false;
|
|
460
461
|
let ctorParams = [];
|
|
@@ -519,7 +520,7 @@ export class Parser {
|
|
|
519
520
|
this.consume(TokenKind.RBrace, "Expected '}' after extern class body");
|
|
520
521
|
const { modulePath, jsName } = this.parseExternTail(name);
|
|
521
522
|
this.consume(TokenKind.Semicolon, "Expected ';' after extern class declaration");
|
|
522
|
-
return { kind: "ExternClassDecl", isExported, name, jsName, hasConstructor, ctorParams, properties, methods, modulePath, line: start.line, col: start.col };
|
|
523
|
+
return { kind: "ExternClassDecl", isExported, name, typeParam, jsName, hasConstructor, ctorParams, properties, methods, modulePath, line: start.line, col: start.col };
|
|
523
524
|
}
|
|
524
525
|
parseBlock() {
|
|
525
526
|
const start = this.consume(TokenKind.LBrace, "Expected '{'");
|
package/package.json
CHANGED