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.
Files changed (178) hide show
  1. package/README.md +32 -7
  2. package/README.ru.md +32 -7
  3. package/dist/ast/declarations.d.ts +20 -1
  4. package/dist/ast/expressions.d.ts +31 -1
  5. package/dist/ast/markup.d.ts +6 -1
  6. package/dist/ast/statements.d.ts +25 -1
  7. package/dist/auto-import.d.ts +11 -0
  8. package/dist/auto-import.js +81 -0
  9. package/dist/breaks.d.ts +6 -0
  10. package/dist/breaks.js +36 -0
  11. package/dist/build.d.ts +7 -0
  12. package/dist/build.js +23 -2
  13. package/dist/checker/array-literals.d.ts +6 -0
  14. package/dist/checker/array-literals.js +59 -0
  15. package/dist/checker/assignments.d.ts +7 -0
  16. package/dist/checker/assignments.js +48 -8
  17. package/dist/checker/base.d.ts +2 -1
  18. package/dist/checker/base.js +6 -12
  19. package/dist/checker/builtins.js +15 -5
  20. package/dist/checker/bytes.d.ts +2 -2
  21. package/dist/checker/bytes.js +2 -2
  22. package/dist/checker/calls.js +3 -0
  23. package/dist/checker/channel-objects.d.ts +3 -0
  24. package/dist/checker/channel-objects.js +40 -0
  25. package/dist/checker/channels.d.ts +1 -0
  26. package/dist/checker/channels.js +16 -2
  27. package/dist/checker/checker.d.ts +1 -1
  28. package/dist/checker/checker.js +19 -0
  29. package/dist/checker/class-members.d.ts +22 -0
  30. package/dist/checker/class-members.js +105 -0
  31. package/dist/checker/classes.d.ts +5 -4
  32. package/dist/checker/classes.js +41 -12
  33. package/dist/checker/closing.d.ts +21 -0
  34. package/dist/checker/closing.js +164 -0
  35. package/dist/checker/completion.d.ts +2 -2
  36. package/dist/checker/completion.js +2 -2
  37. package/dist/checker/context.d.ts +19 -0
  38. package/dist/checker/control-flow.d.ts +6 -0
  39. package/dist/checker/control-flow.js +34 -0
  40. package/dist/checker/declarations.js +1 -0
  41. package/dist/checker/decode.d.ts +8 -0
  42. package/dist/checker/decode.js +32 -0
  43. package/dist/checker/error-results.d.ts +6 -0
  44. package/dist/checker/error-results.js +31 -0
  45. package/dist/checker/expressions.d.ts +5 -1
  46. package/dist/checker/expressions.js +13 -34
  47. package/dist/checker/flow.js +8 -4
  48. package/dist/checker/helpers.d.ts +1 -1
  49. package/dist/checker/helpers.js +9 -2
  50. package/dist/checker/iterables.d.ts +3 -1
  51. package/dist/checker/iterables.js +13 -0
  52. package/dist/checker/iterators.d.ts +4 -1
  53. package/dist/checker/iterators.js +12 -4
  54. package/dist/checker/json.d.ts +5 -0
  55. package/dist/checker/json.js +92 -0
  56. package/dist/checker/loops.js +23 -3
  57. package/dist/checker/members.js +7 -0
  58. package/dist/checker/operators.d.ts +2 -2
  59. package/dist/checker/operators.js +7 -5
  60. package/dist/checker/queues.d.ts +2 -2
  61. package/dist/checker/queues.js +2 -2
  62. package/dist/checker/resolution.js +5 -3
  63. package/dist/checker/signals.d.ts +8 -0
  64. package/dist/checker/signals.js +48 -0
  65. package/dist/checker/statements.d.ts +5 -0
  66. package/dist/checker/statements.js +19 -0
  67. package/dist/checker/topics.d.ts +6 -0
  68. package/dist/checker/topics.js +17 -0
  69. package/dist/checker/transfer.d.ts +3 -1
  70. package/dist/checker/transfer.js +25 -5
  71. package/dist/checker/try.d.ts +6 -0
  72. package/dist/checker/try.js +19 -0
  73. package/dist/checker/types/constructors.d.ts +1 -1
  74. package/dist/checker/types/constructors.js +2 -2
  75. package/dist/checker/types/generics.js +1 -1
  76. package/dist/checker/types/iterables.d.ts +13 -0
  77. package/dist/checker/types/iterables.js +52 -0
  78. package/dist/checker/types/model.d.ts +25 -0
  79. package/dist/checker/types/params.js +22 -3
  80. package/dist/checker/types/printing.js +4 -3
  81. package/dist/checker/types/relations.js +19 -25
  82. package/dist/checker/types/topic.d.ts +6 -0
  83. package/dist/checker/types/topic.js +23 -0
  84. package/dist/checker/types.d.ts +2 -0
  85. package/dist/checker/types.js +2 -0
  86. package/dist/cli.js +18 -36
  87. package/dist/codegen/calls.d.ts +23 -0
  88. package/dist/codegen/calls.js +88 -7
  89. package/dist/codegen/control-statements.d.ts +19 -3
  90. package/dist/codegen/control-statements.js +105 -20
  91. package/dist/codegen/decode.d.ts +14 -0
  92. package/dist/codegen/decode.js +93 -0
  93. package/dist/codegen/emitter.d.ts +10 -41
  94. package/dist/codegen/emitter.js +9 -4
  95. package/dist/codegen/expressions.d.ts +9 -0
  96. package/dist/codegen/expressions.js +15 -24
  97. package/dist/codegen/js.d.ts +2 -2
  98. package/dist/codegen/js.js +5 -2
  99. package/dist/codegen/options.d.ts +40 -0
  100. package/dist/codegen/options.js +1 -0
  101. package/dist/codegen/runtime.d.ts +7 -5
  102. package/dist/codegen/runtime.js +28 -20
  103. package/dist/codegen/simple-statements.d.ts +10 -4
  104. package/dist/codegen/simple-statements.js +40 -15
  105. package/dist/codegen/statements.js +64 -4
  106. package/dist/codegen/try.d.ts +5 -0
  107. package/dist/codegen/try.js +40 -0
  108. package/dist/compile.d.ts +9 -0
  109. package/dist/compile.js +19 -6
  110. package/dist/completion.d.ts +4 -2
  111. package/dist/completion.js +10 -5
  112. package/dist/declarations.js +32 -6
  113. package/dist/decorator-copy.js +5 -2
  114. package/dist/goroutines.d.ts +1 -1
  115. package/dist/goroutines.js +5 -4
  116. package/dist/index.d.ts +6 -2
  117. package/dist/index.js +14 -5
  118. package/dist/lexer/base.d.ts +2 -0
  119. package/dist/lexer/base.js +6 -1
  120. package/dist/lexer/lexer.js +3 -0
  121. package/dist/lexer/literals.d.ts +5 -0
  122. package/dist/lexer/literals.js +51 -1
  123. package/dist/lexer/markup.js +3 -4
  124. package/dist/lexer/tables.js +2 -0
  125. package/dist/lexer/token.d.ts +4 -3
  126. package/dist/lexer/token.js +0 -2
  127. package/dist/loader.js +40 -13
  128. package/dist/parser/base.d.ts +4 -0
  129. package/dist/parser/base.js +2 -1
  130. package/dist/parser/classes.d.ts +2 -0
  131. package/dist/parser/classes.js +88 -23
  132. package/dist/parser/control-flow.d.ts +3 -1
  133. package/dist/parser/control-flow.js +21 -24
  134. package/dist/parser/declarations.d.ts +7 -4
  135. package/dist/parser/declarations.js +45 -6
  136. package/dist/parser/expressions.js +27 -3
  137. package/dist/parser/jumps.d.ts +11 -0
  138. package/dist/parser/jumps.js +77 -0
  139. package/dist/parser/literals.d.ts +9 -0
  140. package/dist/parser/literals.js +45 -6
  141. package/dist/parser/markup.js +1 -1
  142. package/dist/parser/simple-statements.d.ts +1 -1
  143. package/dist/parser/simple-statements.js +16 -9
  144. package/dist/parser/statements.d.ts +0 -3
  145. package/dist/parser/statements.js +22 -25
  146. package/dist/parser/syntax.d.ts +11 -1
  147. package/dist/parser/syntax.js +16 -2
  148. package/dist/parser/types.js +8 -3
  149. package/dist/project-modules.d.ts +12 -0
  150. package/dist/project-modules.js +106 -0
  151. package/dist/run-file.d.ts +6 -0
  152. package/dist/run-file.js +111 -0
  153. package/dist/runtime/sources.js +11 -5
  154. package/dist/runtime-module.d.ts +1 -0
  155. package/dist/runtime-module.js +3 -0
  156. package/dist/source-map.d.ts +17 -0
  157. package/dist/source-map.js +75 -0
  158. package/dist/ts-types.d.ts +4 -0
  159. package/dist/ts-types.js +20 -3
  160. package/dist/typescript/convert-objects.d.ts +2 -1
  161. package/dist/typescript/convert-objects.js +18 -9
  162. package/dist/vite.js +13 -5
  163. package/dist/walk-types.js +1 -0
  164. package/dist/walk.d.ts +0 -2
  165. package/dist/walk.js +11 -28
  166. package/package.json +1 -1
  167. package/src/runtime/chan.js +15 -5
  168. package/src/runtime/channel.js +28 -0
  169. package/src/runtime/classes.js +32 -0
  170. package/src/runtime/core.js +10 -1
  171. package/src/runtime/decode.js +190 -0
  172. package/src/runtime/go.js +14 -5
  173. package/src/runtime/index.js +6 -0
  174. package/src/runtime/select.js +14 -9
  175. package/src/runtime/signals.js +59 -0
  176. package/src/runtime/threads.js +32 -5
  177. package/src/runtime/topic.js +118 -0
  178. 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
- if (arg.kind === 'array')
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 `queue ${element.kind === 'union' && !element.name ? `(${text})` : text}`;
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
+ }
@@ -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';
@@ -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, mkdtempSync, readFileSync, rmSync, statSync, writeFileSync, } from 'node:fs';
3
- import { tmpdir } from 'node:os';
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, { outDir: values['out-dir'], typeCheck, markup });
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
- process.exitCode = runFile(file, typeCheck, markup);
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) {
@@ -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.