rooklang 0.0.0-stage → 0.3.1

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 +195 -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 +105 -4
@@ -0,0 +1,160 @@
1
+ import { attrsParam } from "../attrs.js";
2
+ import { mentionsName } from "../names.js";
3
+ import { componentRoot, constantAttributes, isComponentUse, ownRoot, refName, refTargets, } from "../refs.js";
4
+ import { hasClass } from "../styles.js";
5
+ import { isSvgElement, svgElements } from "../svg.js";
6
+ import { forEachChild } from "../walk.js";
7
+ import { EarlyReturnEmitter } from "./early-returns.js";
8
+ export class RefEmitter extends EarlyReturnEmitter {
9
+ /** The elements that the refs of the component being written are bound to. */
10
+ refElements = new Map();
11
+ /** Those of them that are SVG elements. */
12
+ svgTargets = new Set();
13
+ rootPreset = null;
14
+ /** Tags of components that refs point to or that are at the top of components: lazily. */
15
+ rootTags = null;
16
+ /** Writes the code of a component: its refs, and the element at its top if a ref created it. */
17
+ withRefs(component, preset, emit) {
18
+ const saved = {
19
+ refElements: this.refElements,
20
+ svgTargets: this.svgTargets,
21
+ rootPreset: this.rootPreset,
22
+ inSvg: this.inSvg,
23
+ attrsParam: this.attrsParam,
24
+ };
25
+ this.refElements = refTargets(component.body);
26
+ this.svgTargets = this.refElements.size > 0 ? svgElements(component.body) : new Set();
27
+ this.rootPreset = preset;
28
+ // The markup of a component starts outside <svg>, wherever the component is used.
29
+ this.inSvg = false;
30
+ // Its `{...attrs}` applies the attributes it was given.
31
+ this.attrsParam = attrsParam(component)?.name.name ?? null;
32
+ try {
33
+ return emit();
34
+ }
35
+ finally {
36
+ this.refElements = saved.refElements;
37
+ this.svgTargets = saved.svgTargets;
38
+ this.rootPreset = saved.rootPreset;
39
+ this.inSvg = saved.inSvg;
40
+ this.attrsParam = saved.attrsParam;
41
+ }
42
+ }
43
+ /** `ref input`: `const input = document.createElement("input");` and its constant attributes. */
44
+ refDeclaration(node) {
45
+ for (const id of node.names) {
46
+ const name = this.name(id.name);
47
+ const target = this.refElements.get(id.name);
48
+ const preset = target && this.rootPreset?.node === target ? this.rootPreset : null;
49
+ if (preset?.created) {
50
+ this.line(`const ${name} = ${preset.name};`);
51
+ continue;
52
+ }
53
+ const element = target ? this.elementOf(target) : null;
54
+ // Only without the checker: it reports a ref that points to no element.
55
+ if (!element?.tag) {
56
+ this.line(`const ${name} = null;`);
57
+ continue;
58
+ }
59
+ const tag = element.tag.name;
60
+ // The root of another component has no <svg> above it in its own markup.
61
+ const svg = element === target ? this.svgTargets.has(target) : isSvgElement(tag, false);
62
+ const scope = this.scopeOf(element);
63
+ const constants = constantAttributes(element);
64
+ // The class of the style is constant too, when the element has no class of its own.
65
+ const scopeClass = scope && !hasClass(element) ? scope : null;
66
+ let creation = { covered: new Set(), scoped: false };
67
+ if (preset) {
68
+ this.createdElements++;
69
+ this.line(`const ${name} = ${preset.name} ?? ${this.createElement(tag, svg)};`);
70
+ }
71
+ else {
72
+ creation = this.createWith(`const ${name}`, name, tag, svg, constants, null, scope, scopeClass);
73
+ }
74
+ for (const attribute of constants) {
75
+ if (!creation.covered.has(attribute))
76
+ this.attribute(name, tag, attribute, null, svg, scope);
77
+ }
78
+ if (scopeClass !== null && !creation.scoped)
79
+ this.scopeClass(name, scopeClass, svg);
80
+ }
81
+ }
82
+ /**
83
+ * The element that markup fills in instead of creating one: that of its ref, which has its
84
+ * constant attributes already, or the one a ref to the component created.
85
+ */
86
+ presetElement(node) {
87
+ const ref = refName(node);
88
+ if (ref !== null)
89
+ return { name: this.name(ref), created: true };
90
+ const preset = this.rootPreset;
91
+ return preset?.node === node && node.tag !== null ? preset : null;
92
+ }
93
+ /**
94
+ * What a use of a component gives it as the element at its top: the element of a ref on the use,
95
+ * or the one given to the component being written, when the use is at its top.
96
+ */
97
+ rootFor(use) {
98
+ const ref = refName(use);
99
+ if (ref !== null)
100
+ return { name: this.name(ref), created: true };
101
+ const preset = this.rootPreset;
102
+ return preset?.node === use ? { name: preset.name, created: preset.created } : null;
103
+ }
104
+ /** The preset of an inlined component, whose names may hide the variable with the element. */
105
+ inlineRoot(component, given) {
106
+ const own = ownRoot(component);
107
+ if (!given || typeof own === 'string')
108
+ return null;
109
+ let { name } = given;
110
+ if (!name.startsWith('$$') && mentionsName(component, new Set([name.replace(/\$$/, '')]))) {
111
+ name = `$$root${this.nextId()}`;
112
+ this.line(`const ${name} = ${given.name};`);
113
+ }
114
+ return { node: own, name, created: given.created };
115
+ }
116
+ /**
117
+ * Whether the function of a component takes `$$root`: when the element at its top is known, and
118
+ * a ref may point to it, here or in another module, or a component that does returns it.
119
+ */
120
+ takesRoot(component) {
121
+ if (typeof this.componentRootOf(component, new Set()) === 'string')
122
+ return false;
123
+ this.rootTags ??= this.findRootTags();
124
+ return component.exported || this.rootTags.has(component.name.name);
125
+ }
126
+ findRootTags() {
127
+ const tags = new Set();
128
+ const visit = (node) => {
129
+ if (node.kind === 'ElementExpression' && isComponentUse(node) && refName(node) !== null) {
130
+ tags.add(node.tag.name);
131
+ }
132
+ forEachChild(node, visit);
133
+ };
134
+ visit(this.program);
135
+ for (const component of this.components.values()) {
136
+ const own = ownRoot(component);
137
+ if (typeof own !== 'string' && isComponentUse(own))
138
+ tags.add(own.tag.name);
139
+ }
140
+ return tags;
141
+ }
142
+ /** The element that markup creates: itself, or the one at the top of the component it uses. */
143
+ elementOf(node) {
144
+ if (!isComponentUse(node))
145
+ return node;
146
+ const component = this.components.get(node.tag.name);
147
+ const root = component ? this.componentRootOf(component, new Set()) : null;
148
+ return typeof root === 'string' ? null : root;
149
+ }
150
+ componentRootOf(component, seen) {
151
+ const imported = this.importedRoots.get(component);
152
+ if (imported !== undefined)
153
+ return imported;
154
+ seen.add(component);
155
+ return componentRoot(component, (tag) => {
156
+ const other = this.components.get(tag);
157
+ return other && !seen.has(other) ? this.componentRootOf(other, seen) : undefined;
158
+ });
159
+ }
160
+ }
@@ -0,0 +1,2 @@
1
+ export type SelectHelper = 'select';
2
+ export declare const SELECT_HELPERS: Readonly<Record<SelectHelper, readonly string[]>>;
@@ -0,0 +1,135 @@
1
+ // `select`. Its cases may wait in channels of several threads; a channel that can go on with a case
2
+ // asks the thread of the select to lock it first, so that only one case goes on, as Go locks the
3
+ // channels of a select.
4
+ export const SELECT_HELPERS = {
5
+ select: [
6
+ '// `select`: the index of the case that went on, and what it received. A case is { channel } to',
7
+ '// receive or { channel, value } to send; from another thread, { channel, send, value }. One that',
8
+ '// can go on at once wins, a random one as in Go; else `default`, -1, if there is one; else the',
9
+ '// first that can go on later. A case with a null channel never goes on.',
10
+ 'async function $$select(cases, hasDefault) {',
11
+ ' const registry = $$registry();',
12
+ ' const all = cases.map((each, i) => ({',
13
+ ' i,',
14
+ ' id: typeof each.channel === "string" ? each.channel : (each.channel?.$$chan ?? null),',
15
+ ' send: each.send ?? "value" in each,',
16
+ ' value: each.value,',
17
+ ' }));',
18
+ ' const live = all.filter(({ id }) => id !== null);',
19
+ ' const owners = new Set(live.map(({ id }) => $$ownerOf(id)));',
20
+ ' // The channels of one other thread: the select runs there.',
21
+ ' if (owners.size === 1 && !owners.has(registry.key)) {',
22
+ ' const [owner] = owners;',
23
+ ' return $$ask(owner, { op: "select", cases: all.map(({ id: channel, send, value }) => ({ channel, send, value })), hasDefault });',
24
+ ' }',
25
+ ' const order = live.map(({ i }) => i).sort(() => Math.random() - 0.5);',
26
+ ' for (const i of order) {',
27
+ ' const { id, send, value } = all[i];',
28
+ ' const owner = $$ownerOf(id);',
29
+ ' const ready =',
30
+ ' owner === registry.key',
31
+ ' ? $$try(id, send, value)',
32
+ ' : await $$ask(owner, { op: "try", channel: id, send, value });',
33
+ ' if (ready) return [i, ...ready];',
34
+ ' }',
35
+ ' if (hasDefault) return [-1];',
36
+ ' return new Promise((resolve, reject) => {',
37
+ ' const sid = registry.asks++;',
38
+ ' const channels = new Map();',
39
+ ' registry.selects.set(sid, { locked: false, resolve, reject, channels });',
40
+ ' live.forEach(({ i, id, send, value }) => {',
41
+ ' const owner = $$ownerOf(id);',
42
+ ' if (!channels.has(owner)) channels.set(owner, []);',
43
+ ' channels.get(owner).push(id);',
44
+ ' if (owner === registry.key) {',
45
+ ' const entry = { select: { key: owner, sid }, case: i, value };',
46
+ ' registry.states.get(id)[send ? "senders" : "receivers"].push(entry);',
47
+ ' } else {',
48
+ ' $$post(owner, { op: "wait", sid, case: i, channel: id, send, value });',
49
+ ' }',
50
+ ' });',
51
+ ' for (const owner of channels.keys()) if (owner !== registry.key) $$pin(owner, 1);',
52
+ ' for (const id of channels.get(registry.key) ?? []) $$pump(id);',
53
+ ' });',
54
+ '}',
55
+ '',
56
+ '// What a case of a select gets at once, if it can go on: [] for a send, [value, ok] for a receive.',
57
+ '// A receiver or sender that a select of another thread waits with is left to $$pump.',
58
+ 'function $$try(id, send, value) {',
59
+ ' const state = $$registry().states.get(id);',
60
+ ' if (!state) throw $$stopped();',
61
+ ' if (state.busy) return null;',
62
+ ' if (send && state.closed) throw new Error("send on a closed channel");',
63
+ ' if (!send && state.buffer.length > 0) {',
64
+ ' const received = state.buffer.shift();',
65
+ ' $$pump(id);',
66
+ ' return [received, true];',
67
+ ' }',
68
+ ' const queue = send ? state.receivers : state.senders;',
69
+ ' while (queue.length > 0) {',
70
+ ' const [entry] = queue;',
71
+ ' if (entry.select && entry.select.key !== $$registry().key) break;',
72
+ ' const lock = entry.select ? $$lock(entry.select.sid) : "locked";',
73
+ ' if (lock === "busy") break;',
74
+ ' queue.shift();',
75
+ ' if (lock === "done") continue;',
76
+ ' if (!send) {',
77
+ ' $$finish(entry);',
78
+ ' return [entry.value, true];',
79
+ ' }',
80
+ ' $$finish(entry, [value, true]);',
81
+ ' return [];',
82
+ ' }',
83
+ ' if (send && state.buffer.length < state.size) {',
84
+ ' state.buffer.push(value);',
85
+ ' $$pump(id);',
86
+ ' return [];',
87
+ ' }',
88
+ ' return !send && state.closed ? [Array.isArray(state.zero) ? [] : state.zero, false] : null;',
89
+ '}',
90
+ '',
91
+ '// The thread of a select decides which of its cases goes on: a channel locks the select, then',
92
+ '// commits a case or unlocks it.',
93
+ 'function $$lock(sid) {',
94
+ ' const select = $$registry().selects.get(sid);',
95
+ ' if (!select) return "done";',
96
+ ' if (select.locked) return "busy";',
97
+ ' select.locked = true;',
98
+ ' return "locked";',
99
+ '}',
100
+ '',
101
+ 'function $$unlock(sid) {',
102
+ ' const select = $$registry().selects.get(sid);',
103
+ ' if (select) select.locked = false;',
104
+ '}',
105
+ '',
106
+ 'function $$commit(sid, i, result, error) {',
107
+ ' const registry = $$registry();',
108
+ ' const select = registry.selects.get(sid);',
109
+ ' if (!select) return;',
110
+ ' registry.selects.delete(sid);',
111
+ ' // The other cases leave the queues of their channels.',
112
+ ' for (const [owner, channels] of select.channels) {',
113
+ ' if (owner === registry.key) {',
114
+ ' $$unwait({ key: owner, sid }, channels);',
115
+ ' } else {',
116
+ ' $$post(owner, { op: "cancel", sid, channels });',
117
+ ' $$pin(owner, -1);',
118
+ ' }',
119
+ ' }',
120
+ ' if (error) select.reject(error);',
121
+ ' else select.resolve([i, ...(result ?? [])]);',
122
+ '}',
123
+ '',
124
+ 'function $$unwait(select, channels) {',
125
+ ' const { states } = $$registry();',
126
+ ' const other = (entry) => entry.select?.key !== select.key || entry.select.sid !== select.sid;',
127
+ ' for (const id of channels) {',
128
+ ' const state = states.get(id);',
129
+ ' if (!state) continue;',
130
+ ' state.receivers = state.receivers.filter(other);',
131
+ ' state.senders = state.senders.filter(other);',
132
+ ' }',
133
+ '}',
134
+ ],
135
+ };
@@ -0,0 +1,42 @@
1
+ import type * as ast from '../ast.ts';
2
+ import { CallEmitter } from './calls.ts';
3
+ /** Statements: lists and blocks of them, variables, assignments, `return`; the others are in later layers. */
4
+ export declare abstract class SimpleStatementEmitter extends CallEmitter {
5
+ protected abstract forInStatement(node: ast.ForInStatement): void;
6
+ protected abstract selectStatement(node: ast.SelectStatement): void;
7
+ protected abstract forStatement(node: ast.ForStatement): void;
8
+ protected abstract ifStatement(node: ast.IfStatement): string;
9
+ protected abstract switchStatement(node: ast.SwitchStatement, discriminant: ast.Expression): void;
10
+ protected abstract taglessSwitch(node: ast.SwitchStatement): void;
11
+ protected abstract tryStatement(node: ast.TryStatement): string;
12
+ protected abstract classDeclaration(node: ast.ClassDeclaration): void;
13
+ protected abstract importDeclaration(node: ast.ImportDeclaration): void;
14
+ /** Counter for the labels of switches compiled to if/else. */
15
+ protected labels: number;
16
+ /** A `defer` inside an if/loop/switch; implemented by FunctionEmitter. */
17
+ protected abstract deferStatement(node: ast.DeferStatement): void;
18
+ /** `mount() { ... }` of a component (see ComponentEmitter). */
19
+ protected abstract mountStatement(node: ast.MountStatement): void;
20
+ /** `effect { ... }` of a component (see FunctionEmitter). */
21
+ protected abstract effectStatement(node: ast.EffectStatement): void;
22
+ /** `const link = <a>...</a>`: creates the element right in the variable; see ElementEmitter. */
23
+ protected abstract elementDeclaration(declaration: string, node: ast.ElementExpression): void;
24
+ /** An element in the block of `{if ...}` / `{for ...}` in markup; see ElementEmitter. */
25
+ protected abstract elementStatement(node: ast.JsxElementStatement): void;
26
+ /** A recursive component, which becomes a function; see ComponentEmitter. */
27
+ protected abstract componentFunction(node: ast.ComponentDeclaration): void;
28
+ protected statements(list: readonly ast.Statement[]): void;
29
+ /** A block's statements in a new scope, without the braces; `then` writes more code in it. */
30
+ protected blockStatements(statements: readonly ast.Statement[], then?: () => void): void;
31
+ protected blockStatement(node: ast.BlockStatement): string;
32
+ protected statement(node: ast.Statement): void;
33
+ protected emitStatement(node: ast.Statement): void;
34
+ /** `let a, b = 1, 2` → `let a = 1, b = 2`; `const q, err = f()` → `const [q, err] = f()`. */
35
+ protected variableDeclaration(node: ast.VariableDeclaration): string;
36
+ /** Go-style zero value of a type, or `null` for types without one. */
37
+ protected zeroValue(type: ast.TypeNode, seen?: Set<string>): string | null;
38
+ /** `a = b`, `a, b = b, a` → `[a, b] = [b, a]`, `q, err = f()` → `[q, err] = f()`. */
39
+ protected assignment(node: ast.AssignmentStatement): string;
40
+ /** Several results are returned as an array: `return a, b` → `return [a, b]`. */
41
+ protected returnStatement(node: ast.ReturnStatement): string;
42
+ }
@@ -0,0 +1,253 @@
1
+ import { isIterator } from "../iterators.js";
2
+ import { holdsBigInts, TYPED_ARRAYS, TYPED_ELEMENTS } from "../typed-arrays.js";
3
+ import { declarationsOf } from "./analysis.js";
4
+ import { CallEmitter } from "./calls.js";
5
+ import { ARROW, arrayPattern, POSTFIX } from "./syntax.js";
6
+ /** Statements: lists and blocks of them, variables, assignments, `return`; the others are in later layers. */
7
+ export class SimpleStatementEmitter extends CallEmitter {
8
+ /** Counter for the labels of switches compiled to if/else. */
9
+ labels = 0;
10
+ statements(list) {
11
+ let previous;
12
+ for (const statement of list) {
13
+ // Types, decorators and inlined components exist only at compile time.
14
+ if (statement.kind === 'InterfaceDeclaration' ||
15
+ statement.kind === 'TypeAliasDeclaration' ||
16
+ statement.kind === 'DecoratorDeclaration' ||
17
+ (statement.kind === 'ComponentDeclaration' &&
18
+ !this.functionComponents.has(this.applied(statement)))) {
19
+ continue;
20
+ }
21
+ if (previous && this.blankLineBetween(previous, statement))
22
+ this.blankLine();
23
+ this.statement(statement);
24
+ previous = statement;
25
+ }
26
+ }
27
+ /** A block's statements in a new scope, without the braces; `then` writes more code in it. */
28
+ blockStatements(statements, then) {
29
+ this.withScope(declarationsOf(statements), () => {
30
+ this.statements(statements);
31
+ then?.();
32
+ });
33
+ }
34
+ blockStatement(node) {
35
+ return this.block(() => this.blockStatements(node.body));
36
+ }
37
+ statement(node) {
38
+ this.withHoisting(true, () => this.emitStatement(node));
39
+ }
40
+ emitStatement(node) {
41
+ switch (node.kind) {
42
+ case 'ImportDeclaration':
43
+ this.importDeclaration(node);
44
+ break;
45
+ case 'FuncDeclaration': {
46
+ const [params, body] = this.func(node.params, node.body);
47
+ const exported = node.exported ? 'export ' : '';
48
+ const async = node.async ? 'async ' : '';
49
+ const star = isIterator(node.params) ? '*' : '';
50
+ this.line(`${exported}${async}function${star} ${this.name(node.name.name)}(${params}) ${body}`);
51
+ break;
52
+ }
53
+ case 'VariableDeclaration': {
54
+ const [name] = node.names;
55
+ const [value] = node.values;
56
+ if (node.names.length === 1 && value?.kind === 'ElementExpression' && name?.name !== '_') {
57
+ const exported = node.exported ? 'export ' : '';
58
+ const keyword = node.keyword === 'state' || node.keyword === 'derived' ? 'let' : node.keyword;
59
+ this.elementDeclaration(`${exported}${keyword} ${this.name(name.name)}`, value);
60
+ break;
61
+ }
62
+ if (node.names.every((name) => name.name === '_') && !node.exported) {
63
+ // `const _ = f()` only evaluates the values.
64
+ for (const value of node.values)
65
+ this.line(`${this.expressionStatement(value)};`);
66
+ }
67
+ else {
68
+ this.line(`${node.exported ? 'export ' : ''}${this.variableDeclaration(node)};`);
69
+ }
70
+ break;
71
+ }
72
+ case 'ClassDeclaration':
73
+ this.classDeclaration(node);
74
+ break;
75
+ case 'InterfaceDeclaration':
76
+ case 'TypeAliasDeclaration':
77
+ case 'DecoratorDeclaration':
78
+ break;
79
+ case 'ComponentDeclaration':
80
+ this.componentFunction(this.applied(node));
81
+ break;
82
+ case 'BlockStatement':
83
+ this.line(this.blockStatement(node));
84
+ break;
85
+ case 'ExpressionStatement':
86
+ this.line(`${this.expressionStatement(node.expression)};`);
87
+ break;
88
+ case 'JsxElementStatement':
89
+ this.elementStatement(node);
90
+ break;
91
+ case 'AssignmentStatement':
92
+ this.line(`${this.assignment(node)};`);
93
+ break;
94
+ case 'IncDecStatement':
95
+ this.line(`${this.expression(node.target, POSTFIX)}${node.operator};`);
96
+ break;
97
+ case 'DeleteStatement':
98
+ this.line(`delete ${this.expression(node.target, POSTFIX)};`);
99
+ break;
100
+ case 'ReturnStatement':
101
+ this.line(this.returnStatement(node));
102
+ break;
103
+ case 'IfStatement': {
104
+ const yielded = this.yieldStatement(node);
105
+ this.line(yielded === null ? this.ifStatement(node) : `${yielded};`);
106
+ break;
107
+ }
108
+ case 'ForStatement':
109
+ this.forStatement(node);
110
+ break;
111
+ case 'ForInStatement':
112
+ this.forInStatement(node);
113
+ break;
114
+ case 'SelectStatement':
115
+ this.selectStatement(node);
116
+ break;
117
+ case 'SwitchStatement':
118
+ if (node.discriminant === null)
119
+ this.taglessSwitch(node);
120
+ else
121
+ this.switchStatement(node, node.discriminant);
122
+ break;
123
+ case 'BreakStatement': {
124
+ const label = this.fn.breakTargets.at(-1);
125
+ this.line(label ? `break ${label};` : 'break;');
126
+ break;
127
+ }
128
+ case 'ContinueStatement':
129
+ this.line('continue;');
130
+ break;
131
+ case 'ThrowStatement':
132
+ this.line(`throw ${this.expression(node.argument, 0)};`);
133
+ break;
134
+ case 'TryStatement':
135
+ this.line(this.tryStatement(node));
136
+ break;
137
+ case 'DeferStatement':
138
+ this.deferStatement(node);
139
+ break;
140
+ // Waits until the channel takes the value.
141
+ case 'SendStatement': {
142
+ const channel = this.expression(node.channel, ARROW);
143
+ this.helpers.add('chan');
144
+ this.line(`await $$send(${channel}, ${this.expression(node.value, ARROW)});`);
145
+ break;
146
+ }
147
+ case 'MountStatement':
148
+ this.mountStatement(node);
149
+ break;
150
+ case 'EffectStatement':
151
+ this.effectStatement(node);
152
+ break;
153
+ case 'StyleStatement':
154
+ // Components write their styles themselves (ComponentEmitter).
155
+ break;
156
+ }
157
+ }
158
+ /** `let a, b = 1, 2` → `let a = 1, b = 2`; `const q, err = f()` → `const [q, err] = f()`. */
159
+ variableDeclaration(node) {
160
+ // A state variable is an ordinary variable; updates are added where it changes. A derived one
161
+ // is computed again by them.
162
+ const keyword = node.keyword === 'state' || node.keyword === 'derived' ? 'let' : node.keyword;
163
+ if (node.values.length === 0) {
164
+ const zero = node.type ? this.zeroValue(node.type) : null;
165
+ const names = node.names.map((name) => this.name(name.name));
166
+ return `${keyword} ${names.map((name) => (zero === null ? name : `${name} = ${zero}`)).join(', ')}`;
167
+ }
168
+ this.markCommaOk(node.names.length, node.values);
169
+ const values = node.values.map((value) => this.expression(value, ARROW));
170
+ const skips = node.names.some((name) => name.name === '_');
171
+ if (values.length === node.names.length && !skips) {
172
+ const pairs = node.names.map((name, i) => `${this.name(name.name)} = ${values[i]}`);
173
+ return `${keyword} ${pairs.join(', ')}`;
174
+ }
175
+ const pattern = node.names.map((name) => (name.name === '_' ? '' : this.name(name.name)));
176
+ const value = values.length === 1 ? values[0] : `[${values.join(', ')}]`;
177
+ return `${keyword} ${arrayPattern(pattern)} = ${value}`;
178
+ }
179
+ /** Go-style zero value of a type, or `null` for types without one. */
180
+ zeroValue(type, seen = new Set()) {
181
+ switch (type.kind) {
182
+ case 'NullableType':
183
+ return 'null';
184
+ // `[]byte` is a Uint8Array and `[]float64` a Float64Array, unless the program declares a
185
+ // type of that name.
186
+ case 'ArrayType': {
187
+ const { element } = type;
188
+ const typed = element.kind === 'TypeReference' && !this.typeAliases.has(element.name.name)
189
+ ? TYPED_ELEMENTS.get(element.name.name)
190
+ : undefined;
191
+ return typed === undefined ? '[]' : `new ${typed}()`;
192
+ }
193
+ case 'FuncType':
194
+ case 'ComponentType':
195
+ case 'ChanType':
196
+ case 'ObjectType':
197
+ case 'UnionType':
198
+ case 'LiteralType':
199
+ return null;
200
+ case 'TypeReference': {
201
+ const name = type.name.name;
202
+ const alias = this.typeAliases.get(name);
203
+ if (alias && !seen.has(name))
204
+ return this.zeroValue(alias, seen.add(name));
205
+ if (TYPED_ARRAYS.has(name))
206
+ return `new ${name}()`;
207
+ if (TYPED_ELEMENTS.has(name))
208
+ return holdsBigInts(TYPED_ELEMENTS.get(name)) ? '0n' : '0';
209
+ switch (name) {
210
+ case 'number':
211
+ return '0';
212
+ case 'bigint':
213
+ return '0n';
214
+ case 'string':
215
+ return '""';
216
+ case 'bool':
217
+ return 'false';
218
+ case 'any':
219
+ case 'error':
220
+ return 'null';
221
+ default:
222
+ return null;
223
+ }
224
+ }
225
+ }
226
+ }
227
+ /** `a = b`, `a, b = b, a` → `[a, b] = [b, a]`, `q, err = f()` → `[q, err] = f()`. */
228
+ assignment(node) {
229
+ const isBlank = (target) => target.kind === 'Identifier' && target.name === '_';
230
+ if (node.targets.every(isBlank)) {
231
+ // `_ = f()` only evaluates the values.
232
+ if (node.values.length === 1)
233
+ return this.expressionStatement(node.values[0]);
234
+ return `[${node.values.map((value) => this.expression(value, ARROW)).join(', ')}]`;
235
+ }
236
+ this.markCommaOk(node.targets.length, node.values);
237
+ const values = node.values.map((value) => this.expression(value, ARROW));
238
+ if (node.targets.length === 1) {
239
+ return `${this.expression(node.targets[0], POSTFIX)} ${node.operator} ${values[0]}`;
240
+ }
241
+ const pattern = node.targets.map((target) => isBlank(target) ? '' : this.expression(target, POSTFIX));
242
+ const value = values.length === 1 ? values[0] : `[${values.join(', ')}]`;
243
+ return `${arrayPattern(pattern)} = ${value}`;
244
+ }
245
+ /** Several results are returned as an array: `return a, b` → `return [a, b]`. */
246
+ returnStatement(node) {
247
+ if (node.values.length === 0)
248
+ return 'return;';
249
+ if (node.values.length === 1)
250
+ return `return ${this.expression(node.values[0], 0)};`;
251
+ return `return [${node.values.map((value) => this.expression(value, ARROW)).join(', ')}];`;
252
+ }
253
+ }
@@ -0,0 +1,22 @@
1
+ import type * as ast from '../ast.ts';
2
+ import { ParallelLoopEmitter } from './parallel.ts';
3
+ /**
4
+ * Imports and classes; with the layers below, all statements and declarations. A decorator of
5
+ * another module is not a value, but its code uses names of its module: the import brings them
6
+ * under hidden names, `import { $$visible$log } from "./visible.js"`, which that module exports.
7
+ * So does an inlined component of another module; a called one brings its function, `$$Card`.
8
+ */
9
+ export declare abstract class StatementEmitter extends ParallelLoopEmitter {
10
+ /** A CSS module: its object and CSS go with the styles (StyleEmitter), not to an import. */
11
+ protected abstract styleModuleImport(node: ast.ImportDeclaration): boolean;
12
+ protected importDeclaration(node: ast.ImportDeclaration): void;
13
+ /**
14
+ * `export { log as $$visible$log }`: the names of the module that the code of exported
15
+ * decorators uses, and that of exported components when other modules inline them.
16
+ */
17
+ protected hiddenExports(): void;
18
+ protected importPath(source: ast.StringLiteral): string;
19
+ protected classDeclaration(node: ast.ClassDeclaration): void;
20
+ /** Visibility modifiers are checked by the compiler and do not exist in the output. */
21
+ protected classMember(member: ast.ClassMember): void;
22
+ }