rooklang 0.4.1 → 0.4.2

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.
@@ -10,6 +10,12 @@ export declare function containsElement(node: ast.Node): boolean;
10
10
  export declare function hasOptionalLink(node: ast.Expression): boolean;
11
11
  export declare function endsWithJump(statements: readonly ast.Statement[]): boolean;
12
12
  export declare function startsWithObjectLiteral(node: ast.Expression): boolean;
13
+ /**
14
+ * Function literals that an expression gives as its value, not to a call: `() => count++`, the
15
+ * branches of `ok ? save : () => {}`, the fields of `{ onSave: () => {...} }`. They run later, when
16
+ * whoever gets them calls them; a callback given to a call, as to `items.map`, may run right away.
17
+ */
18
+ export declare function functionValues(node: ast.Expression): ast.Expression[];
13
19
  /** The variable at the start of `a.b.c`, or `null`. */
14
20
  export declare function rootName(node: ast.Expression): string | null;
15
21
  /** Literals, which can be put into inlined code as they are. */
@@ -104,6 +104,30 @@ export function startsWithObjectLiteral(node) {
104
104
  return false;
105
105
  }
106
106
  }
107
+ /**
108
+ * Function literals that an expression gives as its value, not to a call: `() => count++`, the
109
+ * branches of `ok ? save : () => {}`, the fields of `{ onSave: () => {...} }`. They run later, when
110
+ * whoever gets them calls them; a callback given to a call, as to `items.map`, may run right away.
111
+ */
112
+ export function functionValues(node) {
113
+ switch (node.kind) {
114
+ case 'ArrowFunction':
115
+ case 'FuncExpression':
116
+ return [node];
117
+ case 'ConditionalExpression':
118
+ return [...functionValues(node.consequent), ...functionValues(node.alternate)];
119
+ case 'BinaryExpression':
120
+ return node.operator === '??' || node.operator === '||' || node.operator === '&&'
121
+ ? [...functionValues(node.left), ...functionValues(node.right)]
122
+ : [];
123
+ case 'ArrayLiteral':
124
+ return node.elements.flatMap((element) => element.kind === 'SpreadElement' ? [] : functionValues(element));
125
+ case 'ObjectLiteral':
126
+ return node.properties.flatMap((property) => property.kind === 'Property' ? functionValues(property.value) : []);
127
+ default:
128
+ return [];
129
+ }
130
+ }
107
131
  /** The variable at the start of `a.b.c`, or `null`. */
108
132
  export function rootName(node) {
109
133
  if (node.kind === 'Identifier')
@@ -19,6 +19,8 @@ export declare abstract class UpdateEmitter extends StatementEmitter {
19
19
  * the markup, which would update it again; code that runs later (handlers) is not included.
20
20
  */
21
21
  protected rendering: number;
22
+ /** Functions that live markup gives as values: they run later, not while it is rendered. */
23
+ private readonly later;
22
24
  /**
23
25
  * After a statement that changes state of the module, the updates of the components that read
24
26
  * it; code that renders markup changes none, as with the state of a component.
@@ -41,9 +43,12 @@ export declare abstract class UpdateEmitter extends StatementEmitter {
41
43
  protected liveFunction(node: ast.Expression): string;
42
44
  /**
43
45
  * An expression that is evaluated again on updates: elements inside it are created in place,
44
- * so that each evaluation creates new ones.
46
+ * so that each evaluation creates new ones. A function that it gives as a value, as to a property
47
+ * `onAdd={() => count++}`, runs later, so what it changes updates the markup as it does in the
48
+ * value the markup was created with.
45
49
  */
46
50
  protected liveExpression(node: ast.Expression, precedence?: number): string;
51
+ protected expression(node: ast.Expression, minPrecedence: number, forceParens?: boolean): string;
47
52
  }
48
53
  /**
49
54
  * Replaces the markers of a component's sources: by the call of the update function, by the only
@@ -1,7 +1,7 @@
1
1
  import { widenLiterals } from "../checker/types.js";
2
2
  import { updatesOf } from "./reactive.js";
3
3
  import { StatementEmitter } from "./statements.js";
4
- import { startsWithObjectLiteral } from "./analysis.js";
4
+ import { functionValues, startsWithObjectLiteral } from "./analysis.js";
5
5
  import { sharedWrites } from "./shared.js";
6
6
  import { ARROW } from "./syntax.js";
7
7
  /**
@@ -21,6 +21,8 @@ export class UpdateEmitter extends StatementEmitter {
21
21
  * the markup, which would update it again; code that runs later (handlers) is not included.
22
22
  */
23
23
  rendering = 0;
24
+ /** Functions that live markup gives as values: they run later, not while it is rendered. */
25
+ later = new WeakSet();
24
26
  /**
25
27
  * After a statement that changes state of the module, the updates of the components that read
26
28
  * it; code that renders markup changes none, as with the state of a component.
@@ -157,11 +159,20 @@ export class UpdateEmitter extends StatementEmitter {
157
159
  }
158
160
  /**
159
161
  * An expression that is evaluated again on updates: elements inside it are created in place,
160
- * so that each evaluation creates new ones.
162
+ * so that each evaluation creates new ones. A function that it gives as a value, as to a property
163
+ * `onAdd={() => count++}`, runs later, so what it changes updates the markup as it does in the
164
+ * value the markup was created with.
161
165
  */
162
166
  liveExpression(node, precedence = ARROW) {
167
+ for (const value of functionValues(node))
168
+ this.later.add(value);
163
169
  return this.withRendering(this.rendering + 1, () => this.withHoisting(false, () => this.expression(node, precedence)));
164
170
  }
171
+ expression(node, minPrecedence, forceParens = false) {
172
+ return this.later.has(node)
173
+ ? this.withRendering(0, () => super.expression(node, minPrecedence, forceParens))
174
+ : super.expression(node, minPrecedence, forceParens);
175
+ }
165
176
  }
166
177
  /**
167
178
  * Replaces the markers of a component's sources: by the call of the update function, by the only
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rooklang",
3
- "version": "0.4.1",
3
+ "version": "0.4.2",
4
4
  "description": "A language with JavaScript semantics, Go-style syntax, goroutines on real threads and static types that compiles to readable JavaScript",
5
5
  "keywords": [
6
6
  "rooklang",