hclang 0.16.0 → 0.17.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.
- package/esm/lib/frames/evaluation-scope.js +7 -6
- package/esm/lib/frames/frame-array.js +70 -0
- package/esm/lib/frames/frame-handle.js +32 -4
- package/esm/lib/frames/frame-typed-number.js +7 -0
- package/esm/lib/frames/frame.js +46 -0
- package/esm/lib/ops/conditionals.js +6 -1
- package/esm/lib/ops/math.js +6 -4
- package/esm/lib/ops.js +3 -3
- package/esm/lib/version.js +1 -1
- package/package.json +1 -1
|
@@ -166,15 +166,16 @@ export class EvaluationScope {
|
|
|
166
166
|
* the argument to the argument", which is what made a closure's write target
|
|
167
167
|
* and its argument the same frame and produced a self-referential binding.
|
|
168
168
|
*
|
|
169
|
-
* A handle argument is the one exception, because
|
|
170
|
-
* mutable view
|
|
169
|
+
* A mutable handle argument is the one exception, because the caller asked
|
|
170
|
+
* for a mutable view by spelling the name with the effect marker (`x_`). A
|
|
171
|
+
* plain name also arrives as a handle, but an immutable one, and it grants no
|
|
172
|
+
* write: `f x` must not change `x`.
|
|
171
173
|
*/
|
|
172
174
|
static call(argument, parameter = Frame.nil, closure, enclosing, receiverState) {
|
|
173
175
|
const local = new Frame();
|
|
174
|
-
const
|
|
175
|
-
const
|
|
176
|
-
|
|
177
|
-
: "statement";
|
|
176
|
+
const writable = argument instanceof FrameHandle && argument.isMutable();
|
|
177
|
+
const target = writable ? argument.unwrap() : local;
|
|
178
|
+
const targetRole = writable ? "handle" : "statement";
|
|
178
179
|
const lookupFrames = [local, argument];
|
|
179
180
|
if (!parameter.is.void)
|
|
180
181
|
lookupFrames.push(parameter);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Frame } from "./frame.js";
|
|
1
2
|
import { FrameExpr } from "./frame-expr.js";
|
|
2
3
|
import { FrameList } from "./frame-list.js";
|
|
3
4
|
import { FrameNote } from "./frame-note.js";
|
|
@@ -51,6 +52,58 @@ export class FrameArray extends FrameList {
|
|
|
51
52
|
}
|
|
52
53
|
return undefined;
|
|
53
54
|
}
|
|
55
|
+
/** Aggregates own identity, so `===` compares them by reference. */
|
|
56
|
+
ownsIdentity() {
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* `=` compares the complete view, the same one `&&` iterates: visible
|
|
61
|
+
* properties in declaration order, then elements by index, each by `=`.
|
|
62
|
+
* Spelling, private fields, the declared parent, and ids are not the value.
|
|
63
|
+
*/
|
|
64
|
+
equals(other) {
|
|
65
|
+
return this.sameMembers(other, (left, right) => left.equals(right));
|
|
66
|
+
}
|
|
67
|
+
/** `==` is the same walk with `==` at every node. */
|
|
68
|
+
typeEquals(other) {
|
|
69
|
+
return this.sameMembers(other, (left, right) => left.typeEquals(right));
|
|
70
|
+
}
|
|
71
|
+
sameMembers(other, same) {
|
|
72
|
+
const right = other.referent();
|
|
73
|
+
if (right === this)
|
|
74
|
+
return Frame.all;
|
|
75
|
+
if (right.constructor !== this.constructor)
|
|
76
|
+
return Frame.nil;
|
|
77
|
+
const inProgress = FrameArray.comparing.get(this) ?? new Set();
|
|
78
|
+
if (inProgress.has(right))
|
|
79
|
+
return Frame.all;
|
|
80
|
+
inProgress.add(right);
|
|
81
|
+
FrameArray.comparing.set(this, inProgress);
|
|
82
|
+
try {
|
|
83
|
+
const keys = this.visibleKeys();
|
|
84
|
+
const rightKeys = right.visibleKeys();
|
|
85
|
+
if (keys.length !== rightKeys.length ||
|
|
86
|
+
keys.some((key, index) => key !== rightKeys[index]))
|
|
87
|
+
return Frame.nil;
|
|
88
|
+
for (const key of keys) {
|
|
89
|
+
if (same(this.get_here(key), right.get_here(key)) !== Frame.all) {
|
|
90
|
+
return Frame.nil;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
const elements = this.elements();
|
|
94
|
+
const rightElements = right.elements();
|
|
95
|
+
if (elements.length !== rightElements.length)
|
|
96
|
+
return Frame.nil;
|
|
97
|
+
return elements.every((element, index) => same(element, rightElements[index]) === Frame.all)
|
|
98
|
+
? Frame.all
|
|
99
|
+
: Frame.nil;
|
|
100
|
+
}
|
|
101
|
+
finally {
|
|
102
|
+
inProgress.delete(right);
|
|
103
|
+
if (inProgress.size === 0)
|
|
104
|
+
FrameArray.comparing.delete(this);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
54
107
|
/**
|
|
55
108
|
* Aggregates are where identity matters, so an instance copy isolates them:
|
|
56
109
|
* every nested aggregate, in the data plane and the metadata plane alike, gets
|
|
@@ -82,6 +135,16 @@ export class FrameArray extends FrameList {
|
|
|
82
135
|
collects() {
|
|
83
136
|
return true;
|
|
84
137
|
}
|
|
138
|
+
/**
|
|
139
|
+
* A fresh aggregate holding the same elements. Only the outer aggregate is
|
|
140
|
+
* new: the elements are shared, so collecting reuses them rather than
|
|
141
|
+
* copying, and `===` still finds each one.
|
|
142
|
+
*/
|
|
143
|
+
collectingCopy() {
|
|
144
|
+
const result = this.copy();
|
|
145
|
+
result.data = [...this.data];
|
|
146
|
+
return result;
|
|
147
|
+
}
|
|
85
148
|
apply(argument, _parameter) {
|
|
86
149
|
if (!argument.is.void) {
|
|
87
150
|
this.data.push(argument);
|
|
@@ -127,3 +190,10 @@ Object.defineProperty(FrameArray, "SIGIL_STARTS", {
|
|
|
127
190
|
{ key: FrameArray.END_ARRAY, mode: "pop" },
|
|
128
191
|
]
|
|
129
192
|
});
|
|
193
|
+
/** Pairs under comparison, so a frame reachable from itself terminates. */
|
|
194
|
+
Object.defineProperty(FrameArray, "comparing", {
|
|
195
|
+
enumerable: true,
|
|
196
|
+
configurable: true,
|
|
197
|
+
writable: true,
|
|
198
|
+
value: new Map()
|
|
199
|
+
});
|
|
@@ -7,10 +7,11 @@ import { methodEffect } from "./effect-marker.js";
|
|
|
7
7
|
*
|
|
8
8
|
* The handle's charter is that reference and nothing more. Two rules define it:
|
|
9
9
|
*
|
|
10
|
-
* - **Transparency.** Wrapping must not change what a value prints, equals,
|
|
11
|
-
* exposes, so rendering, data, metadata,
|
|
12
|
-
* target. A handle is invisible to
|
|
13
|
-
*
|
|
10
|
+
* - **Transparency.** Wrapping must not change what a value prints, equals,
|
|
11
|
+
* exposes, or answers when applied, so rendering, data, metadata, array
|
|
12
|
+
* views, and application all delegate to the target. A handle is invisible to
|
|
13
|
+
* comparison, and assignment unwraps it, so a mutation lands on the same
|
|
14
|
+
* frame it would have without the wrapper.
|
|
14
15
|
* - **Caller-scoped lookup.** A handle created by symbol lookup carries that
|
|
15
16
|
* read's lexical context separately from the shared target. Local and
|
|
16
17
|
* declared-parent lookup remains live on the target, then falls back through
|
|
@@ -60,6 +61,20 @@ export class FrameHandle extends Frame {
|
|
|
60
61
|
unwrap() {
|
|
61
62
|
return this.target;
|
|
62
63
|
}
|
|
64
|
+
/** A handle is transparent to equality: it compares as its target. */
|
|
65
|
+
referent() {
|
|
66
|
+
return this.target.referent();
|
|
67
|
+
}
|
|
68
|
+
equals(other) {
|
|
69
|
+
return this.target.equals(other);
|
|
70
|
+
}
|
|
71
|
+
typeEquals(other) {
|
|
72
|
+
return this.target.typeEquals(other);
|
|
73
|
+
}
|
|
74
|
+
/** Whether the name that produced this handle carried the effect marker. */
|
|
75
|
+
isMutable() {
|
|
76
|
+
return this.mutable;
|
|
77
|
+
}
|
|
63
78
|
/** The lexical fallback selected for this particular read. */
|
|
64
79
|
readContextFrame() {
|
|
65
80
|
return this.readContext;
|
|
@@ -111,6 +126,19 @@ export class FrameHandle extends Frame {
|
|
|
111
126
|
called_by(context, parameter) {
|
|
112
127
|
return context.apply(this.target, parameter);
|
|
113
128
|
}
|
|
129
|
+
/** Transparency covers application: a named aggregate collects like its literal. */
|
|
130
|
+
collects() {
|
|
131
|
+
return this.target.collects();
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Applying through a name answers what applying the value would, without
|
|
135
|
+
* writing through the name. A collect lands on a fresh aggregate, so `xs 3`
|
|
136
|
+
* answers a new array and `xs` is unchanged, whether or not the name is
|
|
137
|
+
* mutable: a write goes through a mutating method or `@`, never application.
|
|
138
|
+
*/
|
|
139
|
+
apply(argument, parameter) {
|
|
140
|
+
return this.target.collectingCopy().apply(argument, parameter);
|
|
141
|
+
}
|
|
114
142
|
toString() {
|
|
115
143
|
return this.target.toString();
|
|
116
144
|
}
|
|
@@ -66,6 +66,13 @@ export class FrameTypedNumber extends FrameNumeric {
|
|
|
66
66
|
}
|
|
67
67
|
return super.lookup_here(key, origin);
|
|
68
68
|
}
|
|
69
|
+
/** `==` also asks the magnitude's rung: `3.m == 3.0.m` is nil. */
|
|
70
|
+
typeEquals(right) {
|
|
71
|
+
if (!(right instanceof FrameTypedNumber) || this.unit !== right.unit) {
|
|
72
|
+
return Frame.nil;
|
|
73
|
+
}
|
|
74
|
+
return this.magnitude.typeEquals(right.magnitude);
|
|
75
|
+
}
|
|
69
76
|
equals(right) {
|
|
70
77
|
if (!(right instanceof FrameTypedNumber) || this.unit !== right.unit) {
|
|
71
78
|
return Frame.nil;
|
package/esm/lib/frames/frame.js
CHANGED
|
@@ -179,6 +179,41 @@ export class Frame extends MetaFrame {
|
|
|
179
179
|
equals(other) {
|
|
180
180
|
return this.toString() === other.toString() ? Frame.all : Frame.nil;
|
|
181
181
|
}
|
|
182
|
+
/**
|
|
183
|
+
* typeEquals is `==`: the same value, and the same type at every node.
|
|
184
|
+
* It is strictly stronger than `equals`, so `3 == 3.0` is nil while
|
|
185
|
+
* `3 = 3.0` holds.
|
|
186
|
+
*/
|
|
187
|
+
typeEquals(other) {
|
|
188
|
+
const left = this.referent();
|
|
189
|
+
const right = other.referent();
|
|
190
|
+
return left.constructor === right.constructor
|
|
191
|
+
? left.equals(right)
|
|
192
|
+
: Frame.nil;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* identical is `===`: whether both references reach the same frame.
|
|
196
|
+
*
|
|
197
|
+
* Only a frame that owns identity can be told apart from an equal copy.
|
|
198
|
+
* Atoms and closure bodies are shared by the copy contract, so sharing them
|
|
199
|
+
* is unobservable and identity falls back to `typeEquals`, which keeps the
|
|
200
|
+
* ladder monotone: `===` implies `==` implies `=`. No id is ever exposed.
|
|
201
|
+
*/
|
|
202
|
+
identical(other) {
|
|
203
|
+
const left = this.referent();
|
|
204
|
+
const right = other.referent();
|
|
205
|
+
if (left.ownsIdentity())
|
|
206
|
+
return left === right ? Frame.all : Frame.nil;
|
|
207
|
+
return left.typeEquals(right);
|
|
208
|
+
}
|
|
209
|
+
/** The frame a reference reaches. A handle answers its target. */
|
|
210
|
+
referent() {
|
|
211
|
+
return this;
|
|
212
|
+
}
|
|
213
|
+
/** Whether a write can land on this frame, so that identity is observable. */
|
|
214
|
+
ownsIdentity() {
|
|
215
|
+
return false;
|
|
216
|
+
}
|
|
182
217
|
/** Compare only this frame's data plane, excluding metadata. */
|
|
183
218
|
dataEquals(other) {
|
|
184
219
|
return this.dataString() === other.dataString() ? Frame.all : Frame.nil;
|
|
@@ -267,6 +302,17 @@ export class Frame extends MetaFrame {
|
|
|
267
302
|
collects() {
|
|
268
303
|
return false;
|
|
269
304
|
}
|
|
305
|
+
/**
|
|
306
|
+
* The receiver a collect lands on when it must not write this frame.
|
|
307
|
+
*
|
|
308
|
+
* A literal is a fresh aggregate each time it is evaluated, so collecting
|
|
309
|
+
* into it in place is unobservable. A value reached through a name is not,
|
|
310
|
+
* so the name asks for this instead. Anything that does not collect is its
|
|
311
|
+
* own receiver.
|
|
312
|
+
*/
|
|
313
|
+
collectingCopy() {
|
|
314
|
+
return this;
|
|
315
|
+
}
|
|
270
316
|
/**
|
|
271
317
|
* The values this frame streams when it is iterated.
|
|
272
318
|
*
|
|
@@ -5,9 +5,14 @@ export const IfThen = (source, block, receiverState) => {
|
|
|
5
5
|
}
|
|
6
6
|
return Frame.nil;
|
|
7
7
|
};
|
|
8
|
+
/**
|
|
9
|
+
* `:` is "or else": a value that is not nil answers itself, and only nil calls
|
|
10
|
+
* the fallback. Composed after `?`, that makes `c ? t : e` answer `t` or `e`,
|
|
11
|
+
* because the taken branch's value reaches `:` as its source.
|
|
12
|
+
*/
|
|
8
13
|
export const IfElse = (source, block, receiverState) => {
|
|
9
14
|
if (source === Frame.nil) {
|
|
10
15
|
return block.call(Frame.nil, Frame.nil, receiverState);
|
|
11
16
|
}
|
|
12
|
-
return
|
|
17
|
+
return source;
|
|
13
18
|
};
|
package/esm/lib/ops/math.js
CHANGED
|
@@ -13,11 +13,13 @@ export const Power = (source, block) => numeric(source, block, (left, right) =>
|
|
|
13
13
|
export const Equals = (source, block) => {
|
|
14
14
|
return source.equals(block);
|
|
15
15
|
};
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
/** `==`: the same value and the same type at every node. */
|
|
17
|
+
export const TypeEquals = (source, block) => {
|
|
18
|
+
return source.typeEquals(block);
|
|
18
19
|
};
|
|
19
|
-
|
|
20
|
-
|
|
20
|
+
/** `===`: the same frame. Atoms, which own no identity, fall back to `==`. */
|
|
21
|
+
export const Identical = (source, block) => {
|
|
22
|
+
return source.identical(block);
|
|
21
23
|
};
|
|
22
24
|
export const GreaterThan = (source, block) => numeric(source, block, (left, right) => left.greaterThan(right));
|
|
23
25
|
export const GreaterThanOrEqual = (source, block) => numeric(source, block, (left, right) => left.greaterThanOrEqual(right));
|
package/esm/lib/ops.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { IfElse, IfThen } from "./ops/conditionals.js";
|
|
2
2
|
import { FrameOps } from "./ops/frame-ops.js";
|
|
3
3
|
import { MapElements, MapMembers, ReduceElements, ReduceMembers, } from "./ops/iterators.js";
|
|
4
|
-
import { Add,
|
|
4
|
+
import { Add, Divide, Equals, GreaterThan, GreaterThanOrEqual, Identical, LessThan, LessThanOrEqual, Modulo, Multiply, Power, Subtract, TypeEquals, } from "./ops/math.js";
|
|
5
5
|
import { BindType, HasType } from "./ops/type-operations.js";
|
|
6
6
|
export { FrameCurry } from "./ops/frame-curry.js";
|
|
7
7
|
export const Ops = new FrameOps({
|
|
@@ -18,8 +18,8 @@ export const Ops = new FrameOps({
|
|
|
18
18
|
"%%": Modulo,
|
|
19
19
|
"**": Power,
|
|
20
20
|
"=": Equals,
|
|
21
|
-
"==":
|
|
22
|
-
"===":
|
|
21
|
+
"==": TypeEquals,
|
|
22
|
+
"===": Identical,
|
|
23
23
|
">": GreaterThan,
|
|
24
24
|
">>": GreaterThan,
|
|
25
25
|
">=": GreaterThanOrEqual,
|
package/esm/lib/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const VERSION = "0.
|
|
1
|
+
export const VERSION = "0.17.1";
|