rooklang 0.0.0-stage → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (314) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +177 -2
  3. package/README.ru.md +178 -0
  4. package/dist/ast/declarations.d.ts +185 -0
  5. package/dist/ast/declarations.js +1 -0
  6. package/dist/ast/expressions.d.ts +201 -0
  7. package/dist/ast/expressions.js +1 -0
  8. package/dist/ast/markup.d.ts +141 -0
  9. package/dist/ast/markup.js +1 -0
  10. package/dist/ast/statements.d.ts +173 -0
  11. package/dist/ast/statements.js +1 -0
  12. package/dist/ast.d.ts +18 -0
  13. package/dist/ast.js +18 -0
  14. package/dist/attrs.d.ts +8 -0
  15. package/dist/attrs.js +23 -0
  16. package/dist/browser.d.ts +30 -0
  17. package/dist/browser.js +36 -0
  18. package/dist/build.d.ts +48 -0
  19. package/dist/build.js +112 -0
  20. package/dist/checker/assignments.d.ts +14 -0
  21. package/dist/checker/assignments.js +197 -0
  22. package/dist/checker/base.d.ts +124 -0
  23. package/dist/checker/base.js +180 -0
  24. package/dist/checker/bigint.d.ts +15 -0
  25. package/dist/checker/bigint.js +85 -0
  26. package/dist/checker/builtins.d.ts +9 -0
  27. package/dist/checker/builtins.js +307 -0
  28. package/dist/checker/bytes.d.ts +11 -0
  29. package/dist/checker/bytes.js +72 -0
  30. package/dist/checker/calls.d.ts +23 -0
  31. package/dist/checker/calls.js +210 -0
  32. package/dist/checker/channel.d.ts +14 -0
  33. package/dist/checker/channel.js +44 -0
  34. package/dist/checker/channels.d.ts +9 -0
  35. package/dist/checker/channels.js +95 -0
  36. package/dist/checker/checker.d.ts +4 -0
  37. package/dist/checker/checker.js +22 -0
  38. package/dist/checker/classes.d.ts +24 -0
  39. package/dist/checker/classes.js +223 -0
  40. package/dist/checker/completion-names.d.ts +11 -0
  41. package/dist/checker/completion-names.js +100 -0
  42. package/dist/checker/completion.d.ts +25 -0
  43. package/dist/checker/completion.js +222 -0
  44. package/dist/checker/component-values.d.ts +11 -0
  45. package/dist/checker/component-values.js +88 -0
  46. package/dist/checker/components.d.ts +37 -0
  47. package/dist/checker/components.js +287 -0
  48. package/dist/checker/context.d.ts +196 -0
  49. package/dist/checker/context.js +35 -0
  50. package/dist/checker/control-flow.d.ts +24 -0
  51. package/dist/checker/control-flow.js +237 -0
  52. package/dist/checker/declarations.d.ts +26 -0
  53. package/dist/checker/declarations.js +259 -0
  54. package/dist/checker/decorator-exports.d.ts +5 -0
  55. package/dist/checker/decorator-exports.js +40 -0
  56. package/dist/checker/decorator-members.d.ts +28 -0
  57. package/dist/checker/decorator-members.js +140 -0
  58. package/dist/checker/decorator-wrappers.d.ts +20 -0
  59. package/dist/checker/decorator-wrappers.js +132 -0
  60. package/dist/checker/decorators.d.ts +28 -0
  61. package/dist/checker/decorators.js +265 -0
  62. package/dist/checker/dom-svg.d.ts +7 -0
  63. package/dist/checker/dom-svg.js +19 -0
  64. package/dist/checker/dom.d.ts +22 -0
  65. package/dist/checker/dom.js +361 -0
  66. package/dist/checker/expressions.d.ts +32 -0
  67. package/dist/checker/expressions.js +244 -0
  68. package/dist/checker/flow.d.ts +18 -0
  69. package/dist/checker/flow.js +134 -0
  70. package/dist/checker/forwarding.d.ts +15 -0
  71. package/dist/checker/forwarding.js +78 -0
  72. package/dist/checker/functions.d.ts +24 -0
  73. package/dist/checker/functions.js +154 -0
  74. package/dist/checker/go-for.d.ts +12 -0
  75. package/dist/checker/go-for.js +93 -0
  76. package/dist/checker/goroutines.d.ts +19 -0
  77. package/dist/checker/goroutines.js +110 -0
  78. package/dist/checker/helpers.d.ts +35 -0
  79. package/dist/checker/helpers.js +162 -0
  80. package/dist/checker/imports.d.ts +7 -0
  81. package/dist/checker/imports.js +83 -0
  82. package/dist/checker/initializers.d.ts +22 -0
  83. package/dist/checker/initializers.js +104 -0
  84. package/dist/checker/iterables.d.ts +5 -0
  85. package/dist/checker/iterables.js +17 -0
  86. package/dist/checker/iterators.d.ts +13 -0
  87. package/dist/checker/iterators.js +95 -0
  88. package/dist/checker/loops.d.ts +10 -0
  89. package/dist/checker/loops.js +82 -0
  90. package/dist/checker/markup.d.ts +28 -0
  91. package/dist/checker/markup.js +220 -0
  92. package/dist/checker/members.d.ts +29 -0
  93. package/dist/checker/members.js +233 -0
  94. package/dist/checker/operators.d.ts +13 -0
  95. package/dist/checker/operators.js +177 -0
  96. package/dist/checker/promise.d.ts +17 -0
  97. package/dist/checker/promise.js +49 -0
  98. package/dist/checker/refs.d.ts +26 -0
  99. package/dist/checker/refs.js +136 -0
  100. package/dist/checker/resolution.d.ts +23 -0
  101. package/dist/checker/resolution.js +173 -0
  102. package/dist/checker/statements.d.ts +28 -0
  103. package/dist/checker/statements.js +212 -0
  104. package/dist/checker/styles.d.ts +14 -0
  105. package/dist/checker/styles.js +49 -0
  106. package/dist/checker/transfer.d.ts +5 -0
  107. package/dist/checker/transfer.js +83 -0
  108. package/dist/checker/types/bytes.d.ts +4 -0
  109. package/dist/checker/types/bytes.js +51 -0
  110. package/dist/checker/types/classes.d.ts +22 -0
  111. package/dist/checker/types/classes.js +93 -0
  112. package/dist/checker/types/constructors.d.ts +27 -0
  113. package/dist/checker/types/constructors.js +129 -0
  114. package/dist/checker/types/generics.d.ts +20 -0
  115. package/dist/checker/types/generics.js +75 -0
  116. package/dist/checker/types/lazy.d.ts +30 -0
  117. package/dist/checker/types/lazy.js +92 -0
  118. package/dist/checker/types/model.d.ts +149 -0
  119. package/dist/checker/types/model.js +15 -0
  120. package/dist/checker/types/params.d.ts +7 -0
  121. package/dist/checker/types/params.js +220 -0
  122. package/dist/checker/types/printing.d.ts +6 -0
  123. package/dist/checker/types/printing.js +109 -0
  124. package/dist/checker/types/relations.d.ts +16 -0
  125. package/dist/checker/types/relations.js +294 -0
  126. package/dist/checker/types.d.ts +9 -0
  127. package/dist/checker/types.js +10 -0
  128. package/dist/cli.d.ts +2 -0
  129. package/dist/cli.js +193 -0
  130. package/dist/codegen/analysis.d.ts +18 -0
  131. package/dist/codegen/analysis.js +144 -0
  132. package/dist/codegen/attributes.d.ts +40 -0
  133. package/dist/codegen/attributes.js +227 -0
  134. package/dist/codegen/calls.d.ts +22 -0
  135. package/dist/codegen/calls.js +106 -0
  136. package/dist/codegen/chan-helpers.d.ts +2 -0
  137. package/dist/codegen/chan-helpers.js +253 -0
  138. package/dist/codegen/component-helpers.d.ts +33 -0
  139. package/dist/codegen/component-helpers.js +18 -0
  140. package/dist/codegen/component-props.d.ts +42 -0
  141. package/dist/codegen/component-props.js +173 -0
  142. package/dist/codegen/component-values.d.ts +27 -0
  143. package/dist/codegen/component-values.js +146 -0
  144. package/dist/codegen/components.d.ts +53 -0
  145. package/dist/codegen/components.js +268 -0
  146. package/dist/codegen/control-flow.d.ts +25 -0
  147. package/dist/codegen/control-flow.js +152 -0
  148. package/dist/codegen/control-statements.d.ts +28 -0
  149. package/dist/codegen/control-statements.js +226 -0
  150. package/dist/codegen/creation.d.ts +31 -0
  151. package/dist/codegen/creation.js +113 -0
  152. package/dist/codegen/early-returns.d.ts +59 -0
  153. package/dist/codegen/early-returns.js +244 -0
  154. package/dist/codegen/element-helpers.d.ts +12 -0
  155. package/dist/codegen/element-helpers.js +54 -0
  156. package/dist/codegen/elements.d.ts +71 -0
  157. package/dist/codegen/elements.js +293 -0
  158. package/dist/codegen/emitter.d.ts +135 -0
  159. package/dist/codegen/emitter.js +296 -0
  160. package/dist/codegen/expressions.d.ts +49 -0
  161. package/dist/codegen/expressions.js +262 -0
  162. package/dist/codegen/functions.d.ts +28 -0
  163. package/dist/codegen/functions.js +170 -0
  164. package/dist/codegen/go-helpers.d.ts +9 -0
  165. package/dist/codegen/go-helpers.js +307 -0
  166. package/dist/codegen/helpers.d.ts +10 -0
  167. package/dist/codegen/helpers.js +356 -0
  168. package/dist/codegen/imported.d.ts +38 -0
  169. package/dist/codegen/imported.js +71 -0
  170. package/dist/codegen/js.d.ts +20 -0
  171. package/dist/codegen/js.js +113 -0
  172. package/dist/codegen/mutations.d.ts +10 -0
  173. package/dist/codegen/mutations.js +102 -0
  174. package/dist/codegen/parallel.d.ts +7 -0
  175. package/dist/codegen/parallel.js +63 -0
  176. package/dist/codegen/reactive.d.ts +95 -0
  177. package/dist/codegen/reactive.js +280 -0
  178. package/dist/codegen/refs.d.ts +62 -0
  179. package/dist/codegen/refs.js +160 -0
  180. package/dist/codegen/select-helpers.d.ts +2 -0
  181. package/dist/codegen/select-helpers.js +135 -0
  182. package/dist/codegen/simple-statements.d.ts +42 -0
  183. package/dist/codegen/simple-statements.js +253 -0
  184. package/dist/codegen/statements.d.ts +22 -0
  185. package/dist/codegen/statements.js +142 -0
  186. package/dist/codegen/styles.d.ts +54 -0
  187. package/dist/codegen/styles.js +157 -0
  188. package/dist/codegen/syntax.d.ts +18 -0
  189. package/dist/codegen/syntax.js +70 -0
  190. package/dist/codegen/thread-helpers.d.ts +2 -0
  191. package/dist/codegen/thread-helpers.js +198 -0
  192. package/dist/codegen/updates.d.ts +51 -0
  193. package/dist/codegen/updates.js +201 -0
  194. package/dist/codegen/web-components.d.ts +20 -0
  195. package/dist/codegen/web-components.js +149 -0
  196. package/dist/compile-file.d.ts +11 -0
  197. package/dist/compile-file.js +24 -0
  198. package/dist/compile.d.ts +53 -0
  199. package/dist/compile.js +187 -0
  200. package/dist/completion.d.ts +10 -0
  201. package/dist/completion.js +134 -0
  202. package/dist/config.d.ts +2 -0
  203. package/dist/config.js +44 -0
  204. package/dist/css/hash.d.ts +1 -0
  205. package/dist/css/hash.js +10 -0
  206. package/dist/css/parse.d.ts +42 -0
  207. package/dist/css/parse.js +138 -0
  208. package/dist/css/print.d.ts +9 -0
  209. package/dist/css/print.js +35 -0
  210. package/dist/css/selectors.d.ts +29 -0
  211. package/dist/css/selectors.js +168 -0
  212. package/dist/declarations.d.ts +3 -0
  213. package/dist/declarations.js +207 -0
  214. package/dist/decorator-copy.d.ts +18 -0
  215. package/dist/decorator-copy.js +158 -0
  216. package/dist/decorators.d.ts +61 -0
  217. package/dist/decorators.js +241 -0
  218. package/dist/diagnostics.d.ts +17 -0
  219. package/dist/diagnostics.js +22 -0
  220. package/dist/go-for.d.ts +26 -0
  221. package/dist/go-for.js +155 -0
  222. package/dist/goroutines.d.ts +37 -0
  223. package/dist/goroutines.js +253 -0
  224. package/dist/html-tag.d.ts +21 -0
  225. package/dist/html-tag.js +103 -0
  226. package/dist/index.d.ts +33 -0
  227. package/dist/index.js +55 -0
  228. package/dist/inline-packages.d.ts +32 -0
  229. package/dist/inline-packages.js +129 -0
  230. package/dist/iterators.d.ts +10 -0
  231. package/dist/iterators.js +33 -0
  232. package/dist/lexer/base.d.ts +24 -0
  233. package/dist/lexer/base.js +87 -0
  234. package/dist/lexer/lexer.d.ts +8 -0
  235. package/dist/lexer/lexer.js +134 -0
  236. package/dist/lexer/literals.d.ts +18 -0
  237. package/dist/lexer/literals.js +203 -0
  238. package/dist/lexer/markup.d.ts +10 -0
  239. package/dist/lexer/markup.js +93 -0
  240. package/dist/lexer/style.d.ts +17 -0
  241. package/dist/lexer/style.js +97 -0
  242. package/dist/lexer/tables.d.ts +53 -0
  243. package/dist/lexer/tables.js +101 -0
  244. package/dist/lexer/token.d.ts +42 -0
  245. package/dist/lexer/token.js +108 -0
  246. package/dist/loader.d.ts +1 -0
  247. package/dist/loader.js +50 -0
  248. package/dist/methods.d.ts +5 -0
  249. package/dist/methods.js +81 -0
  250. package/dist/modules.d.ts +3 -0
  251. package/dist/modules.js +49 -0
  252. package/dist/names.d.ts +23 -0
  253. package/dist/names.js +267 -0
  254. package/dist/parser/base.d.ts +96 -0
  255. package/dist/parser/base.js +202 -0
  256. package/dist/parser/classes.d.ts +8 -0
  257. package/dist/parser/classes.js +164 -0
  258. package/dist/parser/control-flow.d.ts +29 -0
  259. package/dist/parser/control-flow.js +232 -0
  260. package/dist/parser/declarations.d.ts +27 -0
  261. package/dist/parser/declarations.js +238 -0
  262. package/dist/parser/decorators.d.ts +30 -0
  263. package/dist/parser/decorators.js +176 -0
  264. package/dist/parser/expressions.d.ts +15 -0
  265. package/dist/parser/expressions.js +255 -0
  266. package/dist/parser/literals.d.ts +27 -0
  267. package/dist/parser/literals.js +206 -0
  268. package/dist/parser/markup.d.ts +22 -0
  269. package/dist/parser/markup.js +193 -0
  270. package/dist/parser/parser.d.ts +14 -0
  271. package/dist/parser/parser.js +22 -0
  272. package/dist/parser/simple-statements.d.ts +22 -0
  273. package/dist/parser/simple-statements.js +156 -0
  274. package/dist/parser/statements.d.ts +37 -0
  275. package/dist/parser/statements.js +267 -0
  276. package/dist/parser/syntax.d.ts +51 -0
  277. package/dist/parser/syntax.js +173 -0
  278. package/dist/parser/types.d.ts +23 -0
  279. package/dist/parser/types.js +218 -0
  280. package/dist/recursion.d.ts +25 -0
  281. package/dist/recursion.js +170 -0
  282. package/dist/refs.d.ts +49 -0
  283. package/dist/refs.js +210 -0
  284. package/dist/run.d.ts +1 -0
  285. package/dist/run.js +21 -0
  286. package/dist/source.d.ts +15 -0
  287. package/dist/source.js +34 -0
  288. package/dist/style-modules.d.ts +17 -0
  289. package/dist/style-modules.js +49 -0
  290. package/dist/styles.d.ts +36 -0
  291. package/dist/styles.js +160 -0
  292. package/dist/svg.d.ts +8 -0
  293. package/dist/svg.js +77 -0
  294. package/dist/ts-types.d.ts +11 -0
  295. package/dist/ts-types.js +133 -0
  296. package/dist/typed-arrays.d.ts +8 -0
  297. package/dist/typed-arrays.js +43 -0
  298. package/dist/typescript/convert-base.d.ts +42 -0
  299. package/dist/typescript/convert-base.js +135 -0
  300. package/dist/typescript/convert-objects.d.ts +28 -0
  301. package/dist/typescript/convert-objects.js +245 -0
  302. package/dist/typescript/convert.d.ts +14 -0
  303. package/dist/typescript/convert.js +67 -0
  304. package/dist/typescript/importer.d.ts +37 -0
  305. package/dist/typescript/importer.js +152 -0
  306. package/dist/typescript/library.d.ts +23 -0
  307. package/dist/typescript/library.js +81 -0
  308. package/dist/vite.d.ts +11 -0
  309. package/dist/vite.js +86 -0
  310. package/dist/walk-types.d.ts +4 -0
  311. package/dist/walk-types.js +56 -0
  312. package/dist/walk.d.ts +5 -0
  313. package/dist/walk.js +315 -0
  314. package/package.json +104 -4
@@ -0,0 +1,241 @@
1
+ import { renamer } from "./decorator-copy.js";
2
+ import { declaredIn, freeNames, mentionsName, topLevelNames } from "./names.js";
3
+ import { containsReturn } from "./recursion.js";
4
+ import { forEachChild } from "./walk.js";
5
+ // Applying decorators: a component with decorators becomes one component whose body runs the
6
+ // bodies of its decorators first. The checker uses it to see what the inlined code uses, the
7
+ // generator to write it.
8
+ /** The JS name of a member or a parameter of a decorator, in the components it is applied to. */
9
+ export function decoratedName(decorator, member) {
10
+ return `$$${decorator}$${member}`;
11
+ }
12
+ const argumentDeclarations = new WeakSet();
13
+ const wrappedContents = new WeakSet();
14
+ /**
15
+ * Whether a declaration of an applied component gets an argument of a decorator. When the
16
+ * argument reads the component's properties, the declaration is a `state` that the properties
17
+ * set again.
18
+ */
19
+ export function isDecoratorArgument(node) {
20
+ return node.kind === 'VariableDeclaration' && argumentDeclarations.has(node);
21
+ }
22
+ /**
23
+ * Whether markup is what a component returns to the wrapper of a decorator, as in
24
+ * `const $$visible$content = <section>...</section>`: it updates like returned markup.
25
+ */
26
+ export function isWrappedContent(node) {
27
+ return wrappedContents.has(node);
28
+ }
29
+ /** The markup that an applied component gives to the wrappers, whichever `return` it takes. */
30
+ export function wrappedMarkup(component) {
31
+ const found = [];
32
+ const visit = (node) => {
33
+ if (node.kind === 'VariableDeclaration') {
34
+ found.push(...node.values.filter((value) => wrappedContents.has(value)));
35
+ }
36
+ forEachChild(node, visit);
37
+ };
38
+ visit(component.body);
39
+ return found;
40
+ }
41
+ /** The wrapper of a decorator, when it returns a function literal with one parameter. */
42
+ export function wrapperOf(decorator) {
43
+ const last = decorator.body.body.at(-1);
44
+ const [fn] = last?.kind === 'ReturnStatement' ? last.values : [];
45
+ if (fn?.kind !== 'ArrowFunction' && fn?.kind !== 'FuncExpression')
46
+ return null;
47
+ const [param] = fn.params;
48
+ return param && fn.params.length === 1 ? { fn, param } : null;
49
+ }
50
+ /** The statements of a wrapper; an expression body is what it returns. */
51
+ export function wrapperBody(fn) {
52
+ if (fn.body.kind === 'BlockStatement')
53
+ return fn.body.body;
54
+ return [{ kind: 'ReturnStatement', values: [fn.body], start: fn.body.start, end: fn.body.end }];
55
+ }
56
+ /** The parameter of a wrapper and its declarations, which go into the component's body too. */
57
+ export function wrapperNames(wrapper) {
58
+ return [wrapper.param.name.name, ...declaredIn(wrapperBody(wrapper.fn))];
59
+ }
60
+ /**
61
+ * The names of its module that a decorator's code uses. Other modules that apply it get them
62
+ * through hidden exports: `export { log as $$visible$log }`.
63
+ */
64
+ export function moduleNamesOf(decorator, program) {
65
+ const topLevel = topLevelNames(program);
66
+ return [...freeNames(decorator)].filter((name) => topLevel.has(name)).sort();
67
+ }
68
+ /**
69
+ * The component with its decorators applied. Its body first declares the arguments of each
70
+ * decorator and runs its body, from the top decorator down, then runs the component's own body.
71
+ * The names of a decorator get its prefix, `$$visible$shown`, so they clash neither with each other
72
+ * nor with the component's names, and `get(@visible.shown)` reads that variable. The `mount()` of
73
+ * the decorators runs after the component's own, from the inside out, so it goes before every
74
+ * `return` of the component's body.
75
+ *
76
+ * A wrapper is inlined at every `return` too: `return <section />` becomes
77
+ * `const $$visible$content = <section />`, then the body of the wrapper, whose `return` is the
78
+ * component's. The wrappers apply from the bottom decorator up.
79
+ *
80
+ * `prefixes` replace the names of decorators in the prefixes: a module that inlines a component of
81
+ * another one keeps the decorators of that module apart from its own, `$$Card$visible$shown`.
82
+ */
83
+ export function decorate(component, decorators, copied, prefixes) {
84
+ if (component.decorators.length === 0)
85
+ return component;
86
+ const props = new Set(component.params.map((param) => param.name.name));
87
+ const setup = [];
88
+ const mounts = [];
89
+ const wrappers = [];
90
+ for (const use of component.decorators) {
91
+ const source = decorators.get(use.name.name);
92
+ if (!source)
93
+ continue;
94
+ const decorator = source.node;
95
+ const prefix = prefixes?.get(decorator.name.name) ?? decorator.name.name;
96
+ const names = [...decoratorNames(decorator), ...source.moduleNames];
97
+ const renames = membersOf(prefix, names);
98
+ const copy = renamer(renames, copied, { decorators: prefixes, tags: source.tags });
99
+ decorator.params.forEach((param, i) => {
100
+ // A default value is the decorator's code, an argument the component's.
101
+ const value = use.arguments[i] ?? (param.defaultValue && copy(param.defaultValue, new Set()));
102
+ const declaration = {
103
+ kind: 'VariableDeclaration',
104
+ exported: false,
105
+ isPublic: false,
106
+ keyword: value && mentionsName(value, props) ? 'state' : 'const',
107
+ names: [{ ...param.name, name: renames.get(param.name.name) }],
108
+ type: param.type,
109
+ values: value ? [value] : [],
110
+ start: param.start,
111
+ end: param.end,
112
+ };
113
+ argumentDeclarations.add(declaration);
114
+ copied?.(declaration, param);
115
+ setup.push(declaration);
116
+ });
117
+ const wrapper = wrapperOf(decorator);
118
+ const own = [];
119
+ for (const statement of decorator.body.body) {
120
+ if (wrapper && statement === decorator.body.body.at(-1))
121
+ continue;
122
+ const renamed = copy(statement, new Set());
123
+ if (renamed.kind === 'MountStatement')
124
+ own.push(renamed);
125
+ else
126
+ setup.push(renamed);
127
+ }
128
+ mounts.push(own);
129
+ if (wrapper)
130
+ wrappers.push(applyWrapper(source, wrapper, copied, prefix, prefixes));
131
+ }
132
+ const inside = mounts.reverse().flat();
133
+ const chain = wrappers.reverse();
134
+ const body = inside.length > 0 || chain.length > 0
135
+ ? mapReturns(component.body.body, (node) => [...inside, ...wrapped(node, chain)])
136
+ : component.body.body;
137
+ return {
138
+ ...component,
139
+ decorators: [],
140
+ body: { ...component.body, body: [...setup, ...body] },
141
+ };
142
+ }
143
+ /** The parameters of a decorator and the declarations of its body: the names it renames. */
144
+ export function decoratorNames(decorator) {
145
+ const names = [
146
+ ...decorator.params.map((param) => param.name.name),
147
+ ...declaredIn(decorator.body.body),
148
+ ];
149
+ return new Set(names.filter((name) => name !== '_'));
150
+ }
151
+ function membersOf(prefix, names) {
152
+ const renames = new Map();
153
+ for (const name of names) {
154
+ if (name !== '_')
155
+ renames.set(name, decoratedName(prefix, name));
156
+ }
157
+ return renames;
158
+ }
159
+ /**
160
+ * The names of the wrapper's code are renamed too: it goes into the component's body. Outside the
161
+ * wrapper they are not the decorator's, so the rest of the body is copied without them.
162
+ */
163
+ function applyWrapper(source, wrapper, copied, prefix, prefixes) {
164
+ const names = [...decoratorNames(source.node), ...source.moduleNames, ...wrapperNames(wrapper)];
165
+ const renames = membersOf(prefix, names);
166
+ const copy = renamer(renames, copied, { decorators: prefixes, tags: source.tags });
167
+ const { name } = wrapper.param;
168
+ return {
169
+ content: { ...name, name: renames.get(name.name) ?? name.name },
170
+ type: wrapper.param.type,
171
+ body: () => wrapperBody(wrapper.fn).map((statement) => copy(statement, new Set())),
172
+ };
173
+ }
174
+ /** A `return` of the component through the wrappers, the innermost first. */
175
+ function wrapped(node, chain) {
176
+ const [wrapper, ...outer] = chain;
177
+ const [value] = node.values;
178
+ if (!wrapper || !value || node.values.length !== 1)
179
+ return [node];
180
+ wrappedContents.add(value);
181
+ const content = {
182
+ kind: 'VariableDeclaration',
183
+ exported: false,
184
+ isPublic: false,
185
+ keyword: 'const',
186
+ names: [{ ...wrapper.content }],
187
+ type: wrapper.type,
188
+ values: [value],
189
+ start: node.start,
190
+ end: node.end,
191
+ };
192
+ return [content, ...mapReturns(wrapper.body(), (inner) => wrapped(inner, outer))];
193
+ }
194
+ /** The statements with each `return` replaced; those of nested functions are their own. */
195
+ function mapReturns(statements, replace) {
196
+ return statements.flatMap((statement) => statement.kind === 'ReturnStatement' ? replace(statement) : [withReplaced(statement, replace)]);
197
+ }
198
+ function withReplaced(statement, replace) {
199
+ if (!containsReturn(statement))
200
+ return statement;
201
+ const block = (node) => ({
202
+ ...node,
203
+ body: mapReturns(node.body, replace),
204
+ });
205
+ switch (statement.kind) {
206
+ case 'BlockStatement':
207
+ return block(statement);
208
+ case 'IfStatement': {
209
+ const { alternate } = statement;
210
+ return {
211
+ ...statement,
212
+ consequent: block(statement.consequent),
213
+ alternate: alternate === null
214
+ ? null
215
+ : alternate.kind === 'IfStatement'
216
+ ? withReplaced(alternate, replace)
217
+ : block(alternate),
218
+ };
219
+ }
220
+ case 'ForStatement':
221
+ case 'ForInStatement':
222
+ return { ...statement, body: block(statement.body) };
223
+ case 'SwitchStatement':
224
+ return {
225
+ ...statement,
226
+ cases: statement.cases.map((switchCase) => ({
227
+ ...switchCase,
228
+ body: mapReturns(switchCase.body, replace),
229
+ })),
230
+ };
231
+ case 'TryStatement':
232
+ return {
233
+ ...statement,
234
+ block: block(statement.block),
235
+ handler: statement.handler && { ...statement.handler, body: block(statement.handler.body) },
236
+ finalizer: statement.finalizer && block(statement.finalizer),
237
+ };
238
+ default:
239
+ return statement;
240
+ }
241
+ }
@@ -0,0 +1,17 @@
1
+ import type { SourceFile } from './source.ts';
2
+ export interface Diagnostic {
3
+ message: string;
4
+ /** Offsets into the source text. */
5
+ start: number;
6
+ end: number;
7
+ }
8
+ /**
9
+ * Formats a diagnostic in the usual compiler style:
10
+ *
11
+ * hello.rk:2:9: error: unterminated string literal
12
+ * 2 | let s = "abc
13
+ * | ^^^^
14
+ *
15
+ * Warnings do not stop compilation: `warning:` instead of `error:`.
16
+ */
17
+ export declare function formatDiagnostic(file: SourceFile, diagnostic: Diagnostic, severity?: 'error' | 'warning'): string;
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Formats a diagnostic in the usual compiler style:
3
+ *
4
+ * hello.rk:2:9: error: unterminated string literal
5
+ * 2 | let s = "abc
6
+ * | ^^^^
7
+ *
8
+ * Warnings do not stop compilation: `warning:` instead of `error:`.
9
+ */
10
+ export function formatDiagnostic(file, diagnostic, severity = 'error') {
11
+ const { line, column } = file.position(diagnostic.start);
12
+ const text = file.lineText(line);
13
+ // Keep tabs so that the caret lines up with the source line.
14
+ const indent = text.slice(0, column - 1).replace(/[^\t]/g, ' ');
15
+ const width = Math.max(1, Math.min(diagnostic.end - diagnostic.start, text.length - column + 1));
16
+ const gutter = ' '.repeat(String(line).length);
17
+ return [
18
+ `${file.name}:${line}:${column}: ${severity}: ${diagnostic.message}`,
19
+ `${line} | ${text}`,
20
+ `${gutter} | ${indent}${'^'.repeat(width)}`,
21
+ ].join('\n');
22
+ }
@@ -0,0 +1,26 @@
1
+ import type * as ast from './ast.ts';
2
+ /** Whether a node is `go for`. */
3
+ export declare function isGoFor(node: ast.Node): node is ast.ForInStatement;
4
+ /** The array that the body may write into: the iterable, if it is a name. */
5
+ export declare function arrayName(loop: ast.ForInStatement): string | null;
6
+ /** The variables of the loop, as the parameters of a function, for the names its body uses. */
7
+ export declare function loopParams(loop: ast.ForInStatement): ast.Parameter[];
8
+ /** How the body uses the array: its own elements, `items[i]`, and anything else, an error. */
9
+ export interface ArrayUses {
10
+ elements: ast.IndexExpression[];
11
+ others: ast.Identifier[];
12
+ }
13
+ /**
14
+ * The uses of the array in the body. A name declared again inside hides the array or the key; in
15
+ * a function literal of `go` the element is not the loop's, since that goroutine runs elsewhere.
16
+ */
17
+ export declare function arrayUses(loop: ast.ForInStatement): ArrayUses;
18
+ /** Whether the body writes into the array, so that the parts go back into it. */
19
+ export declare function writesArray(loop: ast.ForInStatement, uses?: ArrayUses): boolean;
20
+ /** The `go for` that a loop of a function of the worker module goes over a part of, or null. */
21
+ export declare function partOf(node: ast.ForInStatement): ast.ForInStatement | null;
22
+ /**
23
+ * The function of the worker module that runs a part of `go for`: it gets the part, the index of
24
+ * its first element in the array, and the local names that the body uses.
25
+ */
26
+ export declare function partFunction(loop: ast.ForInStatement, name: string, captured: readonly string[]): ast.FuncDeclaration;
package/dist/go-for.js ADDED
@@ -0,0 +1,155 @@
1
+ import { declaredIn, declaredNames } from "./names.js";
2
+ import { forEachChild } from "./walk.js";
3
+ // `go for i, v in items { ... }`: the array is split into parts, one per processor, each part runs
4
+ // as a goroutine, and the loop waits for all of them. A goroutine is a function of the worker
5
+ // module that loops over its part. The body reaches the array only through its own element,
6
+ // `items[i]`, so the part is all that a goroutine needs: there `items` is the part, and `items[i]`
7
+ // becomes `items[$$i]`, the index in the part. The local names that the body uses are copied to
8
+ // the workers, as for a function literal of `go` (goroutines.ts).
9
+ /** Whether a node is `go for`. */
10
+ export function isGoFor(node) {
11
+ return node.kind === 'ForInStatement' && node.go === true;
12
+ }
13
+ /** The array that the body may write into: the iterable, if it is a name. */
14
+ export function arrayName(loop) {
15
+ return loop.iterable.kind === 'Identifier' ? loop.iterable.name : null;
16
+ }
17
+ /** The variables of the loop, as the parameters of a function, for the names its body uses. */
18
+ export function loopParams(loop) {
19
+ return [loop.key, loop.value].flatMap((name) => name
20
+ ? [
21
+ {
22
+ kind: 'Parameter',
23
+ name,
24
+ type: null,
25
+ defaultValue: null,
26
+ start: name.start,
27
+ end: name.end,
28
+ },
29
+ ]
30
+ : []);
31
+ }
32
+ /**
33
+ * The uses of the array in the body. A name declared again inside hides the array or the key; in
34
+ * a function literal of `go` the element is not the loop's, since that goroutine runs elsewhere.
35
+ */
36
+ export function arrayUses(loop) {
37
+ const uses = { elements: [], others: [] };
38
+ const array = arrayName(loop);
39
+ const key = loop.key && loop.key.name !== '_' ? loop.key.name : null;
40
+ if (array === null)
41
+ return uses;
42
+ const visit = (node, keyShown) => {
43
+ const declared = scopeNames(node);
44
+ if (declared.has(array))
45
+ return;
46
+ const shown = keyShown && (key === null || !declared.has(key));
47
+ switch (node.kind) {
48
+ case 'Identifier':
49
+ if (node.name === array)
50
+ uses.others.push(node);
51
+ return;
52
+ case 'IndexExpression':
53
+ if (shown &&
54
+ !node.optional &&
55
+ node.object.kind === 'Identifier' &&
56
+ node.object.name === array &&
57
+ node.index.kind === 'Identifier' &&
58
+ node.index.name === key) {
59
+ uses.elements.push(node);
60
+ return;
61
+ }
62
+ break;
63
+ case 'MemberExpression':
64
+ visit(node.object, shown);
65
+ return;
66
+ case 'Property':
67
+ visit(node.value, shown);
68
+ return;
69
+ case 'GoExpression':
70
+ forEachChild(node, (child) => visit(child, false));
71
+ return;
72
+ default:
73
+ break;
74
+ }
75
+ forEachChild(node, (child) => visit(child, shown));
76
+ };
77
+ visit(loop.body, true);
78
+ return uses;
79
+ }
80
+ /** The names that a node declares for the code inside it. */
81
+ function scopeNames(node) {
82
+ switch (node.kind) {
83
+ case 'FuncDeclaration':
84
+ case 'FuncExpression':
85
+ case 'ArrowFunction':
86
+ case 'ClassDeclaration':
87
+ return declaredNames(node);
88
+ case 'BlockStatement':
89
+ return new Set(declaredIn(node.body));
90
+ case 'ForInStatement':
91
+ return new Set([node.value.name, ...(node.key ? [node.key.name] : [])]);
92
+ case 'ForStatement':
93
+ return new Set(node.init?.kind === 'VariableDeclaration' ? declaredIn([node.init]) : []);
94
+ case 'SelectCase':
95
+ return new Set(node.names.map((name) => name.name));
96
+ case 'CatchClause':
97
+ return new Set(node.param ? [node.param.name] : []);
98
+ default:
99
+ return new Set();
100
+ }
101
+ }
102
+ /** Whether the body writes into the array, so that the parts go back into it. */
103
+ export function writesArray(loop, uses = arrayUses(loop)) {
104
+ const elements = new Set(uses.elements);
105
+ let found = false;
106
+ const visit = (node) => {
107
+ if (node.kind === 'AssignmentStatement') {
108
+ found ||= node.targets.some((target) => elements.has(target));
109
+ }
110
+ else if (node.kind === 'IncDecStatement') {
111
+ found ||= elements.has(node.target);
112
+ }
113
+ forEachChild(node, visit);
114
+ };
115
+ visit(loop.body);
116
+ return found;
117
+ }
118
+ /** The loops in the functions of the worker module, by the `go for` they were made from. */
119
+ const parts = new WeakMap();
120
+ /** The `go for` that a loop of a function of the worker module goes over a part of, or null. */
121
+ export function partOf(node) {
122
+ return parts.get(node) ?? null;
123
+ }
124
+ /**
125
+ * The function of the worker module that runs a part of `go for`: it gets the part, the index of
126
+ * its first element in the array, and the local names that the body uses.
127
+ */
128
+ export function partFunction(loop, name, captured) {
129
+ const at = { start: loop.start, end: loop.start };
130
+ const id = (name) => ({ kind: 'Identifier', name, ...at });
131
+ const array = arrayName(loop) ?? '$$items';
132
+ const part = { ...loop, iterable: id(array) };
133
+ const params = [array, '$$from', ...captured].map((each) => ({
134
+ kind: 'Parameter',
135
+ name: id(each),
136
+ type: null,
137
+ defaultValue: null,
138
+ ...at,
139
+ }));
140
+ parts.set(part, loop);
141
+ return {
142
+ kind: 'FuncDeclaration',
143
+ exported: false,
144
+ isPublic: false,
145
+ // The body may wait, for a channel or for `go`.
146
+ async: true,
147
+ name: id(name),
148
+ typeParams: [],
149
+ params,
150
+ results: [],
151
+ body: { kind: 'BlockStatement', body: [part], start: loop.start, end: loop.end },
152
+ start: loop.start,
153
+ end: loop.end,
154
+ };
155
+ }
@@ -0,0 +1,37 @@
1
+ import type * as ast from './ast.ts';
2
+ /** What runs as a goroutine: the call of `go`, or the body of `go for`. */
3
+ export type Goroutine = ast.GoExpression | ast.ForInStatement;
4
+ /** What a goroutine reaches from the function it calls. */
5
+ export interface Reach {
6
+ /** Top-level declarations of the module, in source order. */
7
+ statements: ast.Statement[];
8
+ /** Imported names, by their import declarations. */
9
+ imports: Map<ast.ImportDeclaration, Set<string>>;
10
+ /** `let` variables of the module: a worker does not share them. */
11
+ lets: string[];
12
+ /** Components, decorators or markup, which need a document: the first one found. */
13
+ markup: string | null;
14
+ }
15
+ /** Every `go` expression and `go for` loop of the program, nested ones too. */
16
+ export declare function goroutines(program: ast.Program): Goroutine[];
17
+ /**
18
+ * The function that a goroutine calls in the worker module: a name, or the name given to a
19
+ * function literal or a `go for`; anything else is reported by the checker.
20
+ */
21
+ export declare function goTarget(program: ast.Program, node: Goroutine): string | null;
22
+ export declare function recordCaptures(node: Goroutine, names: string[]): void;
23
+ /**
24
+ * The local names that the literal of a `go` or a `go for` gets first: those the checker found.
25
+ * Code that was not checked passes the names that the code uses and the declaration around it
26
+ * declares, but not the array of `go for`, which goes in parts.
27
+ */
28
+ export declare function goCaptures(program: ast.Program, node: Goroutine): string[];
29
+ /** Parameters for the local names that a literal of `go` gets first. */
30
+ export declare function goParams(literal: ast.FuncExpression, names: string[]): ast.Parameter[];
31
+ /** What the code of the functions `names` reaches in the module, through the names it uses. */
32
+ export declare function reach(program: ast.Program, names: Iterable<string>): Reach;
33
+ /**
34
+ * The program of the worker module: the imports and the declarations that the functions `targets`
35
+ * reach, unexported, since the worker module is only loaded by the worker.
36
+ */
37
+ export declare function workerProgram(program: ast.Program, targets: readonly string[]): ast.Program;