rooklang 0.3.2 → 0.3.4
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 +32 -7
- package/README.ru.md +32 -7
- package/dist/ast/declarations.d.ts +20 -1
- package/dist/ast/expressions.d.ts +31 -1
- package/dist/ast/markup.d.ts +6 -1
- package/dist/ast/statements.d.ts +25 -1
- package/dist/auto-import.d.ts +11 -0
- package/dist/auto-import.js +81 -0
- package/dist/breaks.d.ts +6 -0
- package/dist/breaks.js +36 -0
- package/dist/build.d.ts +7 -0
- package/dist/build.js +23 -2
- package/dist/checker/array-literals.d.ts +6 -0
- package/dist/checker/array-literals.js +59 -0
- package/dist/checker/assignments.d.ts +7 -0
- package/dist/checker/assignments.js +48 -8
- package/dist/checker/base.d.ts +2 -1
- package/dist/checker/base.js +6 -12
- package/dist/checker/builtins.js +15 -5
- package/dist/checker/bytes.d.ts +2 -2
- package/dist/checker/bytes.js +2 -2
- package/dist/checker/calls.js +3 -0
- package/dist/checker/channel-objects.d.ts +3 -0
- package/dist/checker/channel-objects.js +40 -0
- package/dist/checker/channels.d.ts +1 -0
- package/dist/checker/channels.js +16 -2
- package/dist/checker/checker.d.ts +1 -1
- package/dist/checker/checker.js +19 -0
- package/dist/checker/class-members.d.ts +22 -0
- package/dist/checker/class-members.js +105 -0
- package/dist/checker/classes.d.ts +5 -4
- package/dist/checker/classes.js +41 -12
- package/dist/checker/closing.d.ts +21 -0
- package/dist/checker/closing.js +164 -0
- package/dist/checker/completion.d.ts +2 -2
- package/dist/checker/completion.js +2 -2
- package/dist/checker/context.d.ts +19 -0
- package/dist/checker/control-flow.d.ts +6 -0
- package/dist/checker/control-flow.js +34 -0
- package/dist/checker/declarations.js +1 -0
- package/dist/checker/decode.d.ts +8 -0
- package/dist/checker/decode.js +32 -0
- package/dist/checker/error-results.d.ts +6 -0
- package/dist/checker/error-results.js +31 -0
- package/dist/checker/expressions.d.ts +5 -1
- package/dist/checker/expressions.js +13 -34
- package/dist/checker/flow.js +8 -4
- package/dist/checker/helpers.d.ts +1 -1
- package/dist/checker/helpers.js +9 -2
- package/dist/checker/iterables.d.ts +3 -1
- package/dist/checker/iterables.js +13 -0
- package/dist/checker/iterators.d.ts +4 -1
- package/dist/checker/iterators.js +12 -4
- package/dist/checker/json.d.ts +5 -0
- package/dist/checker/json.js +92 -0
- package/dist/checker/loops.js +23 -3
- package/dist/checker/members.js +7 -0
- package/dist/checker/operators.d.ts +2 -2
- package/dist/checker/operators.js +7 -5
- package/dist/checker/queues.d.ts +2 -2
- package/dist/checker/queues.js +2 -2
- package/dist/checker/resolution.js +5 -3
- package/dist/checker/signals.d.ts +8 -0
- package/dist/checker/signals.js +48 -0
- package/dist/checker/statements.d.ts +5 -0
- package/dist/checker/statements.js +19 -0
- package/dist/checker/topics.d.ts +6 -0
- package/dist/checker/topics.js +17 -0
- package/dist/checker/transfer.d.ts +3 -1
- package/dist/checker/transfer.js +25 -5
- package/dist/checker/try.d.ts +6 -0
- package/dist/checker/try.js +19 -0
- package/dist/checker/types/constructors.d.ts +1 -1
- package/dist/checker/types/constructors.js +2 -2
- package/dist/checker/types/generics.js +1 -1
- package/dist/checker/types/iterables.d.ts +13 -0
- package/dist/checker/types/iterables.js +52 -0
- package/dist/checker/types/model.d.ts +25 -0
- package/dist/checker/types/params.js +22 -3
- package/dist/checker/types/printing.js +4 -3
- package/dist/checker/types/relations.js +19 -25
- package/dist/checker/types/topic.d.ts +6 -0
- package/dist/checker/types/topic.js +23 -0
- package/dist/checker/types.d.ts +2 -0
- package/dist/checker/types.js +2 -0
- package/dist/cli.js +18 -36
- package/dist/codegen/calls.d.ts +23 -0
- package/dist/codegen/calls.js +88 -7
- package/dist/codegen/control-statements.d.ts +19 -3
- package/dist/codegen/control-statements.js +105 -20
- package/dist/codegen/decode.d.ts +14 -0
- package/dist/codegen/decode.js +93 -0
- package/dist/codegen/emitter.d.ts +10 -41
- package/dist/codegen/emitter.js +9 -4
- package/dist/codegen/expressions.d.ts +9 -0
- package/dist/codegen/expressions.js +15 -24
- package/dist/codegen/js.d.ts +2 -2
- package/dist/codegen/js.js +5 -2
- package/dist/codegen/options.d.ts +40 -0
- package/dist/codegen/options.js +1 -0
- package/dist/codegen/runtime.d.ts +7 -5
- package/dist/codegen/runtime.js +28 -20
- package/dist/codegen/simple-statements.d.ts +10 -4
- package/dist/codegen/simple-statements.js +40 -15
- package/dist/codegen/statements.js +64 -4
- package/dist/codegen/try.d.ts +5 -0
- package/dist/codegen/try.js +40 -0
- package/dist/compile.d.ts +9 -0
- package/dist/compile.js +19 -6
- package/dist/completion.d.ts +4 -2
- package/dist/completion.js +10 -5
- package/dist/declarations.js +32 -6
- package/dist/decorator-copy.js +5 -2
- package/dist/goroutines.d.ts +1 -1
- package/dist/goroutines.js +5 -4
- package/dist/index.d.ts +6 -2
- package/dist/index.js +14 -5
- package/dist/lexer/base.d.ts +2 -0
- package/dist/lexer/base.js +6 -1
- package/dist/lexer/lexer.js +3 -0
- package/dist/lexer/literals.d.ts +5 -0
- package/dist/lexer/literals.js +51 -1
- package/dist/lexer/markup.js +3 -4
- package/dist/lexer/tables.js +2 -0
- package/dist/lexer/token.d.ts +4 -3
- package/dist/lexer/token.js +0 -2
- package/dist/loader.js +40 -13
- package/dist/parser/base.d.ts +4 -0
- package/dist/parser/base.js +2 -1
- package/dist/parser/classes.d.ts +2 -0
- package/dist/parser/classes.js +88 -23
- package/dist/parser/control-flow.d.ts +3 -1
- package/dist/parser/control-flow.js +21 -24
- package/dist/parser/declarations.d.ts +7 -4
- package/dist/parser/declarations.js +45 -6
- package/dist/parser/expressions.js +27 -3
- package/dist/parser/jumps.d.ts +11 -0
- package/dist/parser/jumps.js +77 -0
- package/dist/parser/literals.d.ts +9 -0
- package/dist/parser/literals.js +45 -6
- package/dist/parser/markup.js +1 -1
- package/dist/parser/simple-statements.d.ts +1 -1
- package/dist/parser/simple-statements.js +16 -9
- package/dist/parser/statements.d.ts +0 -3
- package/dist/parser/statements.js +22 -25
- package/dist/parser/syntax.d.ts +11 -1
- package/dist/parser/syntax.js +16 -2
- package/dist/parser/types.js +8 -3
- package/dist/project-modules.d.ts +12 -0
- package/dist/project-modules.js +106 -0
- package/dist/run-file.d.ts +6 -0
- package/dist/run-file.js +111 -0
- package/dist/runtime/sources.js +11 -5
- package/dist/runtime-module.d.ts +1 -0
- package/dist/runtime-module.js +3 -0
- package/dist/source-map.d.ts +17 -0
- package/dist/source-map.js +75 -0
- package/dist/ts-types.d.ts +4 -0
- package/dist/ts-types.js +20 -3
- package/dist/typescript/convert-objects.d.ts +2 -1
- package/dist/typescript/convert-objects.js +18 -9
- package/dist/vite.js +13 -5
- package/dist/walk-types.js +1 -0
- package/dist/walk.d.ts +0 -2
- package/dist/walk.js +11 -28
- package/package.json +1 -1
- package/src/runtime/chan.js +15 -5
- package/src/runtime/channel.js +28 -0
- package/src/runtime/classes.js +32 -0
- package/src/runtime/core.js +10 -1
- package/src/runtime/decode.js +190 -0
- package/src/runtime/go.js +14 -5
- package/src/runtime/index.js +6 -0
- package/src/runtime/select.js +14 -9
- package/src/runtime/signals.js +59 -0
- package/src/runtime/threads.js +32 -5
- package/src/runtime/topic.js +118 -0
- package/src/runtime/try.js +45 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { DecodeChecker } from "./decode.js";
|
|
2
|
+
import { isErrorType } from "./error-results.js";
|
|
3
|
+
import { ERROR } from "./types.js";
|
|
4
|
+
// `try expr`: an exception in the expression, or a rejected Promise that an `await` in it waits
|
|
5
|
+
// for, becomes an error, as if the expression returned it (codegen/try.ts): `try JSON.parse(text)`
|
|
6
|
+
// gives `(any, error)`, `try save()` of a function without results gives the error alone, and of
|
|
7
|
+
// `(User, error)` the exception is one more way to get that error.
|
|
8
|
+
export class TryChecker extends DecodeChecker {
|
|
9
|
+
checkTryExpression(node) {
|
|
10
|
+
const type = this.checkExpression(node.argument, null);
|
|
11
|
+
if (type.kind === 'void' || isErrorType(type))
|
|
12
|
+
return ERROR;
|
|
13
|
+
if (type.kind !== 'tuple')
|
|
14
|
+
return { kind: 'tuple', types: [type, ERROR] };
|
|
15
|
+
return isErrorType(type.types.at(-1))
|
|
16
|
+
? type
|
|
17
|
+
: { kind: 'tuple', types: [...type.types, ERROR] };
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type ArrayType, type ClassInfo, type FunctionType, type LiteralType, type Member, type Type, type TypeParam } from './model.ts';
|
|
2
|
-
export declare function arrayOf(element: Type): ArrayType;
|
|
2
|
+
export declare function arrayOf(element: Type, positions?: Type[]): ArrayType;
|
|
3
3
|
export declare function nullable(type: Type): Type;
|
|
4
4
|
export declare function literal(value: string | number | boolean): LiteralType;
|
|
5
5
|
/** The type that a literal belongs to: `"all"` → `string`. */
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { BOOL, NEVER, NULL, NUMBER, STRING, } from "./model.js";
|
|
2
2
|
import { typesEqual } from "./relations.js";
|
|
3
3
|
// Making types: arrays, nullable types, literals, unions, functions, members, classes.
|
|
4
|
-
export function arrayOf(element) {
|
|
5
|
-
return { kind: 'array', element };
|
|
4
|
+
export function arrayOf(element, positions) {
|
|
5
|
+
return positions ? { kind: 'array', element, positions } : { kind: 'array', element };
|
|
6
6
|
}
|
|
7
7
|
export function nullable(type) {
|
|
8
8
|
switch (type.kind) {
|
|
@@ -43,7 +43,7 @@ export function instantiate(template, args) {
|
|
|
43
43
|
}
|
|
44
44
|
// The indexes and the values of a loop, substituted when first used: the template of a
|
|
45
45
|
// `.d.ts` may get them after its instances.
|
|
46
|
-
for (const key of ['index', 'numberIndex', 'iterates']) {
|
|
46
|
+
for (const key of ['index', 'numberIndex', 'iterates', 'asyncIterates']) {
|
|
47
47
|
let substituted;
|
|
48
48
|
Object.defineProperty(instance, key, {
|
|
49
49
|
enumerable: true,
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type ObjectType, type Type } from './model.ts';
|
|
2
|
+
/** The values that `for` gives of a type it goes over at once, or null. */
|
|
3
|
+
export declare function iteratedBy(type: Type): Type | null;
|
|
4
|
+
/**
|
|
5
|
+
* Whether `source` fits `Iterable<T>` or another interface that `for` goes over: its values must
|
|
6
|
+
* fit. An array or a string, which have no members here, fits one whose members may be left out;
|
|
7
|
+
* an object goes on to the check of its members (null).
|
|
8
|
+
*/
|
|
9
|
+
export declare function iterableAssignable(source: Type, target: ObjectType, assuming: [Type, Type][]): boolean | null;
|
|
10
|
+
/** `{ [string]: T }` accepts an object whose own index and fields are all `T`. */
|
|
11
|
+
export declare function indexAssignable(source: Type, index: Type, assuming: [Type, Type][]): boolean;
|
|
12
|
+
/** `ArrayLike<T>` from a `.d.ts` accepts an array, a string or a typed array of `T`. */
|
|
13
|
+
export declare function numberIndexAssignable(source: Type, index: Type, assuming: [Type, Type][]): boolean;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { isNullable } from "./constructors.js";
|
|
2
|
+
import { memberNames } from "./lazy.js";
|
|
3
|
+
import { STRING } from "./model.js";
|
|
4
|
+
import { assignable } from "./relations.js";
|
|
5
|
+
// What types from a `.d.ts` that are gone over or indexed accept: `Iterable<T>` takes what `for`
|
|
6
|
+
// goes over with values that fit, an array or a Set; `{ [string]: T }` an object of fields of `T`;
|
|
7
|
+
// `ArrayLike<T>` an array, a string or a typed array.
|
|
8
|
+
/** The values that `for` gives of a type it goes over at once, or null. */
|
|
9
|
+
export function iteratedBy(type) {
|
|
10
|
+
if (type.kind === 'array')
|
|
11
|
+
return type.element;
|
|
12
|
+
if (type.kind === 'string')
|
|
13
|
+
return STRING;
|
|
14
|
+
return type.kind === 'object' ? (type.iterates ?? null) : null;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Whether `source` fits `Iterable<T>` or another interface that `for` goes over: its values must
|
|
18
|
+
* fit. An array or a string, which have no members here, fits one whose members may be left out;
|
|
19
|
+
* an object goes on to the check of its members (null).
|
|
20
|
+
*/
|
|
21
|
+
export function iterableAssignable(source, target, assuming) {
|
|
22
|
+
const values = iteratedBy(source);
|
|
23
|
+
if (values === null || !assignable(values, target.iterates, assuming))
|
|
24
|
+
return values === null ? null : false;
|
|
25
|
+
if (source.kind === 'object')
|
|
26
|
+
return null;
|
|
27
|
+
return [...memberNames(target.members)].every((name) => {
|
|
28
|
+
const member = target.members.get(name);
|
|
29
|
+
return !member || (!member.method && isNullable(member.type));
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
/** `{ [string]: T }` accepts an object whose own index and fields are all `T`. */
|
|
33
|
+
export function indexAssignable(source, index, assuming) {
|
|
34
|
+
if (source.kind !== 'object')
|
|
35
|
+
return false;
|
|
36
|
+
if (source.index && !assignable(source.index, index, assuming))
|
|
37
|
+
return false;
|
|
38
|
+
return [...memberNames(source.members)].every((name) => {
|
|
39
|
+
const member = source.members.get(name);
|
|
40
|
+
return !member || member.method || assignable(member.type, index, assuming);
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
/** `ArrayLike<T>` from a `.d.ts` accepts an array, a string or a typed array of `T`. */
|
|
44
|
+
export function numberIndexAssignable(source, index, assuming) {
|
|
45
|
+
if (source.kind === 'array')
|
|
46
|
+
return assignable(source.element, index, assuming);
|
|
47
|
+
if (source.kind === 'string')
|
|
48
|
+
return assignable(source, index, assuming);
|
|
49
|
+
return (source.kind === 'object' &&
|
|
50
|
+
source.numberIndex !== undefined &&
|
|
51
|
+
assignable(source.numberIndex, index, assuming));
|
|
52
|
+
}
|
|
@@ -22,6 +22,11 @@ export declare const UNKNOWN: SimpleType;
|
|
|
22
22
|
export interface ArrayType {
|
|
23
23
|
kind: 'array';
|
|
24
24
|
element: Type;
|
|
25
|
+
/**
|
|
26
|
+
* The types by position of a tuple of a `.d.ts`, `[K, V]`, or of an array literal: only for
|
|
27
|
+
* inferring type parameters, since the array may change. `element` is the union of them.
|
|
28
|
+
*/
|
|
29
|
+
positions?: Type[];
|
|
25
30
|
}
|
|
26
31
|
/** `?T`; the inner type itself is never nullable. */
|
|
27
32
|
export interface NullableType {
|
|
@@ -66,6 +71,15 @@ export interface Member {
|
|
|
66
71
|
visibility: Visibility;
|
|
67
72
|
/** The class that declares the member, for visibility checks. */
|
|
68
73
|
owner: ClassInfo | null;
|
|
74
|
+
/** `readonly id string`: only its value and the constructor of its class assign it. */
|
|
75
|
+
readonly?: true;
|
|
76
|
+
/**
|
|
77
|
+
* A getter, a setter or both, as in `get area() number`: the member is a value of its type, which
|
|
78
|
+
* a getter alone cannot be assigned and a setter alone cannot be read.
|
|
79
|
+
*/
|
|
80
|
+
accessor?: 'get' | 'set' | 'both';
|
|
81
|
+
/** `abstract area() number`: a subclass that is not abstract implements it. */
|
|
82
|
+
abstract?: true;
|
|
69
83
|
}
|
|
70
84
|
/** Interfaces, object type literals and the types of object literals. */
|
|
71
85
|
export interface ObjectType {
|
|
@@ -82,6 +96,8 @@ export interface ObjectType {
|
|
|
82
96
|
numberIndex?: Type;
|
|
83
97
|
/** What `for v in x` gives: the values of `[Symbol.iterator]()` in a `.d.ts`. */
|
|
84
98
|
iterates?: Type;
|
|
99
|
+
/** What `for v in x` waits for: the values of `[Symbol.asyncIterator]()`, as of a Node stream. */
|
|
100
|
+
asyncIterates?: Type;
|
|
85
101
|
/** A generic interface, `interface Box<T>`: its members use these parameters. */
|
|
86
102
|
typeParams?: TypeParam[];
|
|
87
103
|
/** `Box<number>`: the generic interface and the type arguments it was made from. */
|
|
@@ -105,6 +121,15 @@ export interface ClassInfo {
|
|
|
105
121
|
/** Type arguments for a generic base class: the defaults of a class from a `.d.ts`. */
|
|
106
122
|
superArgs?: Type[];
|
|
107
123
|
ctorVisibility: Visibility;
|
|
124
|
+
/** `abstract class Shape`: `new` makes only instances of its subclasses. */
|
|
125
|
+
abstract?: boolean;
|
|
126
|
+
/**
|
|
127
|
+
* `data class Point`: its fields are data, and its instances go to other threads with their
|
|
128
|
+
* class. False for the other classes of Rook; not set for those of a `.d.ts`.
|
|
129
|
+
*/
|
|
130
|
+
data?: boolean;
|
|
131
|
+
/** What `for v in x` waits for: the values of `[Symbol.asyncIterator]()` in a `.d.ts`. */
|
|
132
|
+
asyncIterates?: Type;
|
|
108
133
|
instance: ClassType;
|
|
109
134
|
value: ClassValueType;
|
|
110
135
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { instanceMembers } from "./classes.js";
|
|
2
2
|
import { arrayOf, func, nonNull, nullable, union } from "./constructors.js";
|
|
3
3
|
import { instantiate } from "./generics.js";
|
|
4
|
+
import { iteratedBy } from "./iterables.js";
|
|
4
5
|
import { memberNames } from "./lazy.js";
|
|
5
6
|
import { VOID, } from "./model.js";
|
|
6
7
|
import { typesEqual } from "./relations.js";
|
|
@@ -45,7 +46,7 @@ export function substitute(type, bindings, fallback) {
|
|
|
45
46
|
case 'param':
|
|
46
47
|
return bindings.get(type) ?? fallback ?? type;
|
|
47
48
|
case 'array':
|
|
48
|
-
return arrayOf(substitute(type.element, bindings, fallback));
|
|
49
|
+
return arrayOf(substitute(type.element, bindings, fallback), type.positions?.map((each) => substitute(each, bindings, fallback)));
|
|
49
50
|
case 'nullable':
|
|
50
51
|
return nullable(substitute(type.type, bindings, fallback));
|
|
51
52
|
case 'union':
|
|
@@ -127,6 +128,8 @@ function substituteObject(type, bindings, fallback) {
|
|
|
127
128
|
return { ...type, members, call: type.call && sub(type.call) };
|
|
128
129
|
}
|
|
129
130
|
const MAX_STRUCTURAL_DEPTH = 3;
|
|
131
|
+
/** The names of the templates of channel types (checker/channel.ts). */
|
|
132
|
+
const CHANNELS = new Set(['chan', 'chan<-', '<-chan']);
|
|
130
133
|
/** Infers type parameters in `param` from the argument type `arg`. */
|
|
131
134
|
export function inferTypeParams(param, arg, bindings, depth = 0) {
|
|
132
135
|
if (!containsTypeParam(param))
|
|
@@ -139,8 +142,15 @@ export function inferTypeParams(param, arg, bindings, depth = 0) {
|
|
|
139
142
|
}
|
|
140
143
|
break;
|
|
141
144
|
case 'array':
|
|
142
|
-
|
|
145
|
+
// `[K, V]` from `["a", 1]`: each by its position; else the union of K and V would take all.
|
|
146
|
+
if (arg.kind === 'array' &&
|
|
147
|
+
param.positions?.length === arg.positions?.length &&
|
|
148
|
+
arg.positions) {
|
|
149
|
+
param.positions.forEach((each, i) => infer(each, arg.positions[i]));
|
|
150
|
+
}
|
|
151
|
+
else if (arg.kind === 'array') {
|
|
143
152
|
infer(param.element, arg.element);
|
|
153
|
+
}
|
|
144
154
|
break;
|
|
145
155
|
case 'nullable':
|
|
146
156
|
infer(param.type, nonNull(arg));
|
|
@@ -171,12 +181,21 @@ export function inferTypeParams(param, arg, bindings, depth = 0) {
|
|
|
171
181
|
}
|
|
172
182
|
break;
|
|
173
183
|
case 'object': {
|
|
184
|
+
// A channel of one direction from any channel: `chan<- T` from a `chan number`.
|
|
174
185
|
if (param.instanceOf &&
|
|
175
186
|
arg.kind === 'object' &&
|
|
176
|
-
arg.instanceOf?.template === param.instanceOf.template
|
|
187
|
+
(arg.instanceOf?.template === param.instanceOf.template ||
|
|
188
|
+
(CHANNELS.has(param.instanceOf.template.name ?? '') &&
|
|
189
|
+
CHANNELS.has(arg.instanceOf?.template.name ?? '')))) {
|
|
177
190
|
param.instanceOf.args.forEach((each, i) => infer(each, arg.instanceOf.args[i]));
|
|
178
191
|
break;
|
|
179
192
|
}
|
|
193
|
+
// `Iterable<T>` from `[]number` or a `Set<number>`: T is what `for` gives of it.
|
|
194
|
+
const values = param.iterates && iteratedBy(arg);
|
|
195
|
+
if (values) {
|
|
196
|
+
infer(param.iterates, values);
|
|
197
|
+
break;
|
|
198
|
+
}
|
|
180
199
|
// Structurally: `{ value T }` from `{ value number }`. Types refer to themselves, and the
|
|
181
200
|
// methods of generic interfaces give ever new instances (`then` of a promise gives a
|
|
182
201
|
// promise), so only a few levels deep, as in TypeScript.
|
|
@@ -2,6 +2,7 @@ import { TYPED_ARRAYS, typedArrayName } from "../../typed-arrays.js";
|
|
|
2
2
|
import { ERROR_CLASS } from "./classes.js";
|
|
3
3
|
import { nullable } from "./constructors.js";
|
|
4
4
|
import { isQueue, queueElement } from "./queue.js";
|
|
5
|
+
import { isTopic, topicElement } from "./topic.js";
|
|
5
6
|
// Types as text, for messages.
|
|
6
7
|
/** How deep object types without a name are printed with their members. */
|
|
7
8
|
const MAX_OBJECT_DEPTH = 3;
|
|
@@ -60,10 +61,10 @@ export function typeToString(type) {
|
|
|
60
61
|
return `${type.name} ${typeToString(type.instanceOf.args[0])}`;
|
|
61
62
|
}
|
|
62
63
|
// `queue (string | number)`: the parentheses that the type is written with.
|
|
63
|
-
if (isQueue(type)) {
|
|
64
|
-
const element = queueElement(type);
|
|
64
|
+
if (isQueue(type) || isTopic(type)) {
|
|
65
|
+
const element = isQueue(type) ? queueElement(type) : topicElement(type);
|
|
65
66
|
const text = typeToString(element);
|
|
66
|
-
return
|
|
67
|
+
return `${type.name} ${element.kind === 'union' && !element.name ? `(${text})` : text}`;
|
|
67
68
|
}
|
|
68
69
|
if (type.instanceOf && type.name !== null) {
|
|
69
70
|
return `${type.name}<${type.instanceOf.args.map(typeToString).join(', ')}>`;
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { instanceMembers, isSubclass, publicMembers } from "./classes.js";
|
|
2
|
-
import { memberNames } from "./lazy.js";
|
|
3
2
|
import { isNullable, literalBase, nonNull, union } from "./constructors.js";
|
|
4
3
|
import { UNKNOWN, VOID, } from "./model.js";
|
|
4
|
+
import { indexAssignable, iterableAssignable, numberIndexAssignable } from "./iterables.js";
|
|
5
5
|
import { typedArrayOf, typeToString } from "./printing.js";
|
|
6
6
|
import { isQueue, queueElement } from "./queue.js";
|
|
7
|
+
import { isTopic, topicElement } from "./topic.js";
|
|
7
8
|
// Relations between types: assignability, comparison, equality, common types.
|
|
8
9
|
/** Whether a value of type `source` can be used where `target` is expected. */
|
|
9
10
|
export function isAssignable(source, target) {
|
|
@@ -80,6 +81,15 @@ export function assignable(source, target, assuming) {
|
|
|
80
81
|
if (isQueue(target)) {
|
|
81
82
|
return isQueue(source) && assignable(queueElement(source), queueElement(target), nested);
|
|
82
83
|
}
|
|
84
|
+
// A topic both sends and receives, so its values are the same both ways, as a channel's;
|
|
85
|
+
// `topic any` is any topic, for `close(t)`.
|
|
86
|
+
if (isTopic(target)) {
|
|
87
|
+
const element = topicElement(target);
|
|
88
|
+
return (isTopic(source) &&
|
|
89
|
+
(element.kind === 'any' ||
|
|
90
|
+
(assignable(topicElement(source), element, nested) &&
|
|
91
|
+
assignable(element, topicElement(source), nested))));
|
|
92
|
+
}
|
|
83
93
|
// A channel both takes and gives values, so `chan number` is not a `chan ?number`, as in Go;
|
|
84
94
|
// `chan any` is any channel, for `close(ch)`. A `chan T` is also a channel of one
|
|
85
95
|
// direction, `chan<- T` or `<-chan T`, but not the other way round.
|
|
@@ -106,6 +116,10 @@ export function assignable(source, target, assuming) {
|
|
|
106
116
|
if (source.kind === 'function')
|
|
107
117
|
return target.members.size === 0;
|
|
108
118
|
}
|
|
119
|
+
// `Iterable<T>`: what `for` gives of the value fits.
|
|
120
|
+
const iterable = target.iterates && iterableAssignable(source, target, nested);
|
|
121
|
+
if (iterable !== undefined && iterable !== null)
|
|
122
|
+
return iterable;
|
|
109
123
|
if (target.index && !indexAssignable(source, target.index, nested))
|
|
110
124
|
return false;
|
|
111
125
|
if (target.numberIndex && !numberIndexAssignable(source, target.numberIndex, nested)) {
|
|
@@ -115,33 +129,12 @@ export function assignable(source, target, assuming) {
|
|
|
115
129
|
}
|
|
116
130
|
}
|
|
117
131
|
}
|
|
118
|
-
/** `{ [string]: T }` accepts an object whose own index and fields are all `T`. */
|
|
119
132
|
/** The templates of channel types by name (channel.ts): no other type can have these names. */
|
|
120
133
|
const CHAN_NAMES = new Map([
|
|
121
134
|
['chan', 'both'],
|
|
122
135
|
['chan<-', 'send'],
|
|
123
136
|
['<-chan', 'receive'],
|
|
124
137
|
]);
|
|
125
|
-
function indexAssignable(source, index, assuming) {
|
|
126
|
-
if (source.kind !== 'object')
|
|
127
|
-
return false;
|
|
128
|
-
if (source.index && !assignable(source.index, index, assuming))
|
|
129
|
-
return false;
|
|
130
|
-
return [...memberNames(source.members)].every((name) => {
|
|
131
|
-
const member = source.members.get(name);
|
|
132
|
-
return !member || member.method || assignable(member.type, index, assuming);
|
|
133
|
-
});
|
|
134
|
-
}
|
|
135
|
-
/** `ArrayLike<T>` from a `.d.ts` accepts an array, a string or a typed array of `T`. */
|
|
136
|
-
function numberIndexAssignable(source, index, assuming) {
|
|
137
|
-
if (source.kind === 'array')
|
|
138
|
-
return assignable(source.element, index, assuming);
|
|
139
|
-
if (source.kind === 'string')
|
|
140
|
-
return assignable(source, index, assuming);
|
|
141
|
-
return (source.kind === 'object' &&
|
|
142
|
-
source.numberIndex !== undefined &&
|
|
143
|
-
assignable(source.numberIndex, index, assuming));
|
|
144
|
-
}
|
|
145
138
|
function functionAssignable(source, target, assuming) {
|
|
146
139
|
// An iterator is a JS generator: it does not take the callback that a function of its shape does.
|
|
147
140
|
if (Boolean(source.iterator) !== Boolean(target.iterator))
|
|
@@ -211,7 +204,7 @@ export function explainMismatch(source, target) {
|
|
|
211
204
|
if (source.kind === 'class' && target.kind === 'class' && source.info === target.info) {
|
|
212
205
|
return null;
|
|
213
206
|
}
|
|
214
|
-
if (isQueue(target))
|
|
207
|
+
if (isQueue(target) || isTopic(target))
|
|
215
208
|
return null;
|
|
216
209
|
const members = target.kind === 'object'
|
|
217
210
|
? target.members
|
|
@@ -296,9 +289,10 @@ export function hasZeroValue(type) {
|
|
|
296
289
|
case 'any':
|
|
297
290
|
case 'unknown':
|
|
298
291
|
return true;
|
|
299
|
-
// An empty queue, as `[]` is the zero value of an array.
|
|
292
|
+
// An empty queue, as `[]` is the zero value of an array; Channel.EMPTY and Topic.EMPTY, which
|
|
293
|
+
// are closed.
|
|
300
294
|
case 'object':
|
|
301
|
-
return isQueue(type);
|
|
295
|
+
return isQueue(type) || isTopic(type) || CHAN_NAMES.has(type.instanceOf?.template.name ?? '');
|
|
302
296
|
default:
|
|
303
297
|
return false;
|
|
304
298
|
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ObjectType, Type } from './model.ts';
|
|
2
|
+
export declare function topicOf(element: Type): ObjectType;
|
|
3
|
+
/** Whether a type is `topic T`. */
|
|
4
|
+
export declare function isTopic(type: Type): boolean;
|
|
5
|
+
/** The type of the values of a topic type. */
|
|
6
|
+
export declare function topicElement(topic: Type): Type;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { instantiate } from "./generics.js";
|
|
2
|
+
// `topic T`, made by `topic<T>(name)`: values that go to the other tabs of a site over a
|
|
3
|
+
// BroadcastChannel. A topic type is an instance of a generic object without members, as a channel
|
|
4
|
+
// type is, written and printed by its own syntax; only a topic of the same values is assignable to
|
|
5
|
+
// it (relations.ts), since a topic both sends and receives them.
|
|
6
|
+
const TOPIC = {
|
|
7
|
+
kind: 'object',
|
|
8
|
+
name: 'topic',
|
|
9
|
+
members: new Map(),
|
|
10
|
+
call: null,
|
|
11
|
+
typeParams: [{ kind: 'param', name: 'T' }],
|
|
12
|
+
};
|
|
13
|
+
export function topicOf(element) {
|
|
14
|
+
return instantiate(TOPIC, [element]);
|
|
15
|
+
}
|
|
16
|
+
/** Whether a type is `topic T`. */
|
|
17
|
+
export function isTopic(type) {
|
|
18
|
+
return type.kind === 'object' && type.instanceOf?.template === TOPIC;
|
|
19
|
+
}
|
|
20
|
+
/** The type of the values of a topic type. */
|
|
21
|
+
export function topicElement(topic) {
|
|
22
|
+
return topic.instanceOf.args[0];
|
|
23
|
+
}
|
package/dist/checker/types.d.ts
CHANGED
|
@@ -4,7 +4,9 @@ export * from './types/lazy.ts';
|
|
|
4
4
|
export * from './types/generics.ts';
|
|
5
5
|
export * from './types/classes.ts';
|
|
6
6
|
export * from './types/relations.ts';
|
|
7
|
+
export * from './types/iterables.ts';
|
|
7
8
|
export * from './types/params.ts';
|
|
8
9
|
export * from './types/printing.ts';
|
|
9
10
|
export * from './types/bytes.ts';
|
|
10
11
|
export * from './types/queue.ts';
|
|
12
|
+
export * from './types/topic.ts';
|
package/dist/checker/types.js
CHANGED
|
@@ -5,7 +5,9 @@ export * from "./types/lazy.js";
|
|
|
5
5
|
export * from "./types/generics.js";
|
|
6
6
|
export * from "./types/classes.js";
|
|
7
7
|
export * from "./types/relations.js";
|
|
8
|
+
export * from "./types/iterables.js";
|
|
8
9
|
export * from "./types/params.js";
|
|
9
10
|
export * from "./types/printing.js";
|
|
10
11
|
export * from "./types/bytes.js";
|
|
11
12
|
export * from "./types/queue.js";
|
|
13
|
+
export * from "./types/topic.js";
|
package/dist/cli.js
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { existsSync, mkdirSync,
|
|
3
|
-
import {
|
|
4
|
-
import { spawnSync } from 'node:child_process';
|
|
5
|
-
import { dirname, join } from 'node:path';
|
|
6
|
-
import { fileURLToPath } from 'node:url';
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { dirname } from 'node:path';
|
|
7
4
|
import { parseArgs } from 'node:util';
|
|
8
5
|
import { build, displayPath } from "./build.js";
|
|
9
6
|
import { CompileError, compileFile } from "./compile-file.js";
|
|
@@ -11,6 +8,7 @@ import { markupFor } from "./config.js";
|
|
|
11
8
|
import { formatDiagnostic } from "./diagnostics.js";
|
|
12
9
|
import { tokenize } from "./lexer/lexer.js";
|
|
13
10
|
import { parse } from "./parser/parser.js";
|
|
11
|
+
import { runFile } from "./run-file.js";
|
|
14
12
|
import { SourceFile } from "./source.js";
|
|
15
13
|
const { version } = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8'));
|
|
16
14
|
const HELP = `Rook ${version}
|
|
@@ -25,6 +23,8 @@ Options:
|
|
|
25
23
|
--out-dir <dir> build: write the .js files to <dir>, keeping the folder structure
|
|
26
24
|
(by default they go next to the .rk files)
|
|
27
25
|
--stdout build: print the JS of a single file instead of writing files
|
|
26
|
+
--no-source-map build: do not write the source maps, app.js.map
|
|
27
|
+
--watch run: run again when a file of the program changes
|
|
28
28
|
--no-check Skip type checking
|
|
29
29
|
--markup Turn markup and components on; by default they are on where the
|
|
30
30
|
package.json has "rook": { "markup": true }
|
|
@@ -41,6 +41,8 @@ function main() {
|
|
|
41
41
|
'out-dir': { type: 'string' },
|
|
42
42
|
stdout: { type: 'boolean' },
|
|
43
43
|
'no-check': { type: 'boolean' },
|
|
44
|
+
'no-source-map': { type: 'boolean' },
|
|
45
|
+
watch: { type: 'boolean' },
|
|
44
46
|
markup: { type: 'boolean' },
|
|
45
47
|
'no-markup': { type: 'boolean' },
|
|
46
48
|
help: { type: 'boolean', short: 'h' },
|
|
@@ -81,11 +83,16 @@ function main() {
|
|
|
81
83
|
process.stdout.write(compileFile(file, { rewriteImports: true, typeCheck, markup }).code);
|
|
82
84
|
}
|
|
83
85
|
else {
|
|
84
|
-
buildFiles(files, {
|
|
86
|
+
buildFiles(files, {
|
|
87
|
+
outDir: values['out-dir'],
|
|
88
|
+
typeCheck,
|
|
89
|
+
markup,
|
|
90
|
+
sourceMap: !values['no-source-map'],
|
|
91
|
+
});
|
|
85
92
|
}
|
|
86
93
|
break;
|
|
87
94
|
case 'run':
|
|
88
|
-
|
|
95
|
+
runFile(file, { typeCheck, markup, watch: values.watch === true });
|
|
89
96
|
break;
|
|
90
97
|
case 'tokens':
|
|
91
98
|
printTokens(new SourceFile(file, readFileSync(file, 'utf8')));
|
|
@@ -113,44 +120,19 @@ function buildFiles(entries, options) {
|
|
|
113
120
|
const messages = errors.flatMap(({ file, diagnostics }) => diagnostics.map((diagnostic) => formatDiagnostic(file, diagnostic)));
|
|
114
121
|
throw new CompileError(messages.join('\n\n'));
|
|
115
122
|
}
|
|
116
|
-
for (const { source, output, code, declarations, worker } of outputs) {
|
|
117
|
-
const extra = `${declarations ? ' + .d.ts' : ''}${worker ? ' + .worker.js' : ''}`;
|
|
123
|
+
for (const { source, output, code, declarations, worker, maps = [] } of outputs) {
|
|
124
|
+
const extra = `${declarations ? ' + .d.ts' : ''}${worker ? ' + .worker.js' : ''}${maps.length > 0 ? ' + .js.map' : ''}`;
|
|
118
125
|
mkdirSync(dirname(output), { recursive: true });
|
|
119
126
|
writeFileSync(output, code);
|
|
120
127
|
if (declarations)
|
|
121
128
|
writeFileSync(declarations.output, declarations.code);
|
|
122
129
|
if (worker)
|
|
123
130
|
writeFileSync(worker.output, worker.code);
|
|
131
|
+
for (const map of maps)
|
|
132
|
+
writeFileSync(map.output, map.code);
|
|
124
133
|
console.log(`${displayPath(source)} → ${displayPath(output)}${extra}`);
|
|
125
134
|
}
|
|
126
135
|
}
|
|
127
|
-
/**
|
|
128
|
-
* Runs a program in a process of its own that imports loader.ts first: so do the worker threads
|
|
129
|
-
* of its goroutines, which do not share the hooks of the thread that registers them. They take
|
|
130
|
-
* the compiled code from a directory of the run, removed after it.
|
|
131
|
-
*/
|
|
132
|
-
function runFile(file, typeCheck, markup) {
|
|
133
|
-
// The build rewrites the extensions of imports, not of these URLs: in the package they are .js.
|
|
134
|
-
const extension = import.meta.url.endsWith('.ts') ? '.ts' : '.js';
|
|
135
|
-
const loader = new URL(`./loader${extension}`, import.meta.url).href;
|
|
136
|
-
const run = fileURLToPath(new URL(`./run${extension}`, import.meta.url));
|
|
137
|
-
const cache = mkdtempSync(join(tmpdir(), 'rook-run-'));
|
|
138
|
-
try {
|
|
139
|
-
const { status, signal } = spawnSync(process.execPath, ['--import', loader, run, file], {
|
|
140
|
-
stdio: 'inherit',
|
|
141
|
-
env: {
|
|
142
|
-
...process.env,
|
|
143
|
-
ROOK_RUN_CACHE: cache,
|
|
144
|
-
...(typeCheck ? {} : { ROOK_NO_CHECK: '1' }),
|
|
145
|
-
...(markup === undefined ? {} : { ROOK_MARKUP: markup ? '1' : '0' }),
|
|
146
|
-
},
|
|
147
|
-
});
|
|
148
|
-
return status ?? (signal ? 1 : 0);
|
|
149
|
-
}
|
|
150
|
-
finally {
|
|
151
|
-
rmSync(cache, { recursive: true, force: true });
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
136
|
function printTokens(file) {
|
|
155
137
|
const { tokens, diagnostics } = tokenize(file.text);
|
|
156
138
|
for (const token of tokens) {
|
package/dist/codegen/calls.d.ts
CHANGED
|
@@ -2,6 +2,12 @@ import type * as ast from '../ast.ts';
|
|
|
2
2
|
import { ExpressionEmitter } from './expressions.ts';
|
|
3
3
|
export declare abstract class CallEmitter extends ExpressionEmitter {
|
|
4
4
|
protected call(node: ast.CallExpression): string;
|
|
5
|
+
/**
|
|
6
|
+
* `go f(x)`: the worker module has the function under the same name, a literal under the name
|
|
7
|
+
* given to it, after the local names it uses; the arguments are copied there. In the worker
|
|
8
|
+
* module a literal runs in the same worker, with copies of those names as its first parameters.
|
|
9
|
+
*/
|
|
10
|
+
protected go(node: ast.GoExpression): string;
|
|
5
11
|
/** `yield v` for `yield(v)` in an iterator, `yield [k, v]` for `yield(k, v)`; else `null`. */
|
|
6
12
|
private yielded;
|
|
7
13
|
protected expressionStatement(node: ast.Expression): string;
|
|
@@ -23,6 +29,23 @@ export declare abstract class CallEmitter extends ExpressionEmitter {
|
|
|
23
29
|
protected chan(node: ast.ChanExpression): string;
|
|
24
30
|
/** `queue<number>(items)`: the queue copies the values, as `[...items]` would. */
|
|
25
31
|
protected queue(node: ast.QueueExpression): string;
|
|
32
|
+
/** Whether the checker found a topic here, or a `?topic T` in a case of `select`. */
|
|
33
|
+
protected isTopicValue(node: ast.Expression): boolean;
|
|
34
|
+
/**
|
|
35
|
+
* The channel that `<-x`, `for v in x` and a case of `select` receive from: a topic listens from
|
|
36
|
+
* the first of them on, into a channel of its own (src/runtime/topic.js).
|
|
37
|
+
*/
|
|
38
|
+
protected receivedFrom(node: ast.Expression): string;
|
|
39
|
+
/**
|
|
40
|
+
* The zero value that `<-x` gives if `x` is `Channel.EMPTY` or `Topic.EMPTY`, which are the same
|
|
41
|
+
* for all types; any other channel keeps its own. `null` when it is null.
|
|
42
|
+
*/
|
|
43
|
+
protected receivedZero(node: ast.Expression): string;
|
|
44
|
+
protected receive(node: ast.Expression, withOk: boolean): string;
|
|
45
|
+
/** `ch <- v` waits until the channel takes the value; a topic sends and goes on. */
|
|
46
|
+
protected send(node: ast.SendStatement): string;
|
|
47
|
+
/** `topic<T>(name)`: a topic keeps the zero value that a closed one gives, as a channel does. */
|
|
48
|
+
protected topic(node: ast.TopicExpression): string;
|
|
26
49
|
/**
|
|
27
50
|
* `chan(stream, size)`: the channel keeps the zero value of its values, as `chan<T>(size)` does,
|
|
28
51
|
* from the type of the stream that the checker found; without it, null.
|