@axpecter/lync 2.3.2 → 3.0.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 (135) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +302 -299
  3. package/Types.luau +215 -0
  4. package/api/Define.luau +154 -0
  5. package/api/Lifecycle.luau +119 -0
  6. package/api/Packet.luau +108 -0
  7. package/api/Query.luau +149 -0
  8. package/api/Set.luau +189 -0
  9. package/channel/Blocks.luau +124 -0
  10. package/channel/Group.luau +118 -0
  11. package/channel/Link.luau +135 -0
  12. package/channel/Outbox.luau +261 -0
  13. package/channel/Recipients.luau +129 -0
  14. package/channel/Transport.luau +138 -0
  15. package/codec/Base.luau +236 -0
  16. package/codec/Composites.luau +128 -0
  17. package/codec/Datatypes.luau +162 -0
  18. package/codec/Legality.luau +61 -0
  19. package/codec/Scalars.luau +72 -0
  20. package/codec/Text.luau +89 -0
  21. package/codec/init.luau +39 -0
  22. package/index.d.ts +285 -0
  23. package/init.luau +91 -0
  24. package/kernel/Bits.luau +16 -0
  25. package/kernel/Catalog.luau +121 -0
  26. package/kernel/Heap.luau +80 -0
  27. package/kernel/Listeners.luau +95 -0
  28. package/kernel/Log.luau +197 -0
  29. package/kernel/Tally.luau +128 -0
  30. package/kernel/Threads.luau +44 -0
  31. package/kernel/Wide.luau +267 -0
  32. package/package.json +15 -32
  33. package/runtime/Boundary.luau +180 -0
  34. package/runtime/Dispatch.luau +91 -0
  35. package/runtime/Handles.luau +114 -0
  36. package/runtime/Handshake.luau +90 -0
  37. package/runtime/Pending.luau +196 -0
  38. package/runtime/Router.luau +379 -0
  39. package/runtime/Session.luau +731 -0
  40. package/shape/Compile.luau +340 -0
  41. package/shape/Folds.luau +332 -0
  42. package/shape/Frames.luau +170 -0
  43. package/shape/Grid.luau +169 -0
  44. package/shape/Ir.luau +540 -0
  45. package/shape/Joins.luau +125 -0
  46. package/shape/Legality.luau +273 -0
  47. package/shape/Lower.luau +419 -0
  48. package/shape/Order.luau +64 -0
  49. package/shape/Regions.luau +113 -0
  50. package/shape/Rotation.luau +472 -0
  51. package/shape/Serial.luau +258 -0
  52. package/shape/Words.luau +89 -0
  53. package/shape/emit/Block.luau +453 -0
  54. package/shape/emit/Column.luau +338 -0
  55. package/shape/emit/Leaf.luau +462 -0
  56. package/shape/emit/Word.luau +234 -0
  57. package/shape/emit/init.luau +222 -0
  58. package/shape/init.luau +78 -0
  59. package/state/Apply.luau +330 -0
  60. package/state/Audience.luau +126 -0
  61. package/state/Catchup.luau +218 -0
  62. package/state/Coalesce.luau +43 -0
  63. package/state/Fieldset.luau +177 -0
  64. package/state/Newest.luau +339 -0
  65. package/state/Plan.luau +441 -0
  66. package/state/Sections.luau +468 -0
  67. package/state/Store.luau +188 -0
  68. package/state/Sync.luau +762 -0
  69. package/wire/Bytes.luau +118 -0
  70. package/wire/Clock.luau +37 -0
  71. package/wire/Protocol.luau +110 -0
  72. package/wire/Sequence.luau +40 -0
  73. package/wire/Varint.luau +124 -0
  74. package/src/Types.luau +0 -180
  75. package/src/api/Group.luau +0 -156
  76. package/src/api/Packet.luau +0 -319
  77. package/src/api/Query.luau +0 -338
  78. package/src/api/Scope.luau +0 -90
  79. package/src/api/Signal.luau +0 -188
  80. package/src/codec/Base.luau +0 -136
  81. package/src/codec/composite/Array.luau +0 -456
  82. package/src/codec/composite/Map.luau +0 -469
  83. package/src/codec/composite/Optional.luau +0 -54
  84. package/src/codec/composite/Shared.luau +0 -291
  85. package/src/codec/composite/Struct.luau +0 -494
  86. package/src/codec/composite/Tagged.luau +0 -103
  87. package/src/codec/composite/Tuple.luau +0 -143
  88. package/src/codec/datatype/Buffer.luau +0 -66
  89. package/src/codec/datatype/CFrame.luau +0 -109
  90. package/src/codec/datatype/Color.luau +0 -32
  91. package/src/codec/datatype/Instance.luau +0 -52
  92. package/src/codec/datatype/IntVector.luau +0 -39
  93. package/src/codec/datatype/NumberRange.luau +0 -39
  94. package/src/codec/datatype/Ray.luau +0 -36
  95. package/src/codec/datatype/Rect.luau +0 -31
  96. package/src/codec/datatype/Region.luau +0 -62
  97. package/src/codec/datatype/Sequence.luau +0 -184
  98. package/src/codec/datatype/String.luau +0 -94
  99. package/src/codec/datatype/UDim.luau +0 -46
  100. package/src/codec/datatype/Vector.luau +0 -130
  101. package/src/codec/meta/Auto.luau +0 -318
  102. package/src/codec/meta/Bitfield.luau +0 -147
  103. package/src/codec/meta/Custom.luau +0 -33
  104. package/src/codec/meta/DeltaScalar.luau +0 -390
  105. package/src/codec/meta/Enum.luau +0 -50
  106. package/src/codec/meta/Float.luau +0 -38
  107. package/src/codec/meta/Nothing.luau +0 -13
  108. package/src/codec/meta/Unknown.luau +0 -41
  109. package/src/codec/primitive/Bool.luau +0 -26
  110. package/src/codec/primitive/Float16.luau +0 -99
  111. package/src/codec/primitive/Int.luau +0 -59
  112. package/src/codec/primitive/Number.luau +0 -24
  113. package/src/codec/primitive/Signed.luau +0 -32
  114. package/src/codec/primitive/Varint.luau +0 -165
  115. package/src/codec/primitive/Zint.luau +0 -99
  116. package/src/index.d.ts +0 -423
  117. package/src/init.luau +0 -362
  118. package/src/internal/Baseline.luau +0 -57
  119. package/src/internal/Channel.luau +0 -390
  120. package/src/internal/Middleware.luau +0 -100
  121. package/src/internal/Pool.luau +0 -65
  122. package/src/internal/Registry.luau +0 -119
  123. package/src/internal/Transport.luau +0 -28
  124. package/src/transport/Bridge.luau +0 -110
  125. package/src/transport/Client.luau +0 -97
  126. package/src/transport/Gate.luau +0 -488
  127. package/src/transport/Reader.luau +0 -331
  128. package/src/transport/Server.luau +0 -211
  129. package/src/util/Array.luau +0 -18
  130. package/src/util/Buffer.luau +0 -90
  131. package/src/util/Constants.luau +0 -30
  132. package/src/util/Log.luau +0 -68
  133. package/src/util/Player.luau +0 -14
  134. package/src/util/Quantize.luau +0 -84
  135. package/src/util/Quat.luau +0 -124
package/shape/Ir.luau ADDED
@@ -0,0 +1,540 @@
1
+ --!strict
2
+ --!optimize 2
3
+ -- The closed set of things the emitter knows how to write, and the one place each is built.
4
+
5
+ local Base = require (script.Parent.Parent.codec.Base)
6
+ local Bits = require (script.Parent.Parent.kernel.Bits)
7
+ local Bytes = require (script.Parent.Parent.wire.Bytes)
8
+ local Grid = require (script.Parent.Grid)
9
+ local Protocol = require (script.Parent.Parent.wire.Protocol)
10
+ local Types = require (script.Parent.Parent.Types)
11
+ local Words = require (script.Parent.Words)
12
+
13
+ local Ir = {}
14
+
15
+ -- Constants -----------------------------------------------------------
16
+
17
+ const BYTE = Protocol.BYTE_BITS
18
+
19
+ -- Shared by every group holding no dissolved record, since one frozen empty table is enough.
20
+ const NO_NESTS = table.freeze ({} :: { Nest })
21
+
22
+ -- Types ---------------------------------------------------------------
23
+
24
+ export type Op =
25
+ "word"
26
+ | "wide"
27
+ | "f32"
28
+ | "f64"
29
+ | "vlq"
30
+ | "vli"
31
+ | "bytes"
32
+ | "run"
33
+ | "list"
34
+ | "pairs"
35
+ | "group"
36
+ | "apart"
37
+ | "opt"
38
+ | "branch"
39
+ | "slot"
40
+ | "xform"
41
+
42
+ --[[
43
+ What a rejection at this place names, as the two indices the serializer interned it under.
44
+ Indices and not text, since a decode composing prose would pay for it on a path a hostile
45
+ client sets the rate of, and the boundary that catches the raise reads them back.
46
+ ]]
47
+ export type Tag = {
48
+ read pathId: number,
49
+ read expectId: number,
50
+ read checks: { Base.Validate }?,
51
+ }
52
+
53
+ -- The pair every op exists to become, which is the whole of what an op is for.
54
+ export type Write = (b: buffer, pos: number, value: any) -> number
55
+ export type Read = (b: buffer, pos: number, ctx: Types.ValidateContext) -> (number, any)
56
+
57
+ -- The instances that ride beside a frame, and the map holding one reference to one slot in them.
58
+ export type Sidecar = {
59
+ refs: { Instance },
60
+ slotOf: { [Instance]: number },
61
+ bears: boolean,
62
+ }
63
+
64
+ --[[
65
+ A record that dissolved into the one holding it, as the table its fields are read out of and
66
+ written back into.
67
+ ]]
68
+ export type Nest = {
69
+ read key: string,
70
+ read under: number?,
71
+ read allow: { [string]: boolean },
72
+ read tag: Tag,
73
+ }
74
+
75
+ -- The inclusive bounds a count runs between, a ranged int of no bits at all where the two meet.
76
+ export type Span = {
77
+ read min: number,
78
+ read max: number,
79
+ }
80
+
81
+ --[[
82
+ Where a run of leaves comes from, and what it goes back to. A join of one leaf whose grid is the
83
+ whole mapping carries no pair, which is every ordinary numeric field. Everything else carries
84
+ one, since its leaves come out of a single computation that asking one at a time would run once
85
+ per component kept.
86
+ ]]
87
+ export type Join = {
88
+ read at: number,
89
+ read count: number,
90
+ read key: string?,
91
+ read nest: number?,
92
+ read tag: Tag,
93
+ read pull: ((value: any, into: { number }, at: number) -> boolean)?,
94
+ read push: ((from: { number }, at: number) -> any)?,
95
+ }
96
+
97
+ --[[
98
+ A bit region and the windows it was cut into. The one node whose size is not a whole number of
99
+ bytes, which is why it is the one that says how many bits it is.
100
+ ]]
101
+ export type Word = {
102
+ read op: "word",
103
+ read fixed: boolean,
104
+ read maxBytes: number,
105
+ read tag: Tag,
106
+ read bits: number,
107
+ read plan: { Words.Word },
108
+ read leaves: { Grid.Grid },
109
+ read joins: { Join },
110
+ }
111
+
112
+ export type Wide = {
113
+ read op: "wide",
114
+ read fixed: boolean,
115
+ read maxBytes: number,
116
+ read tag: Tag,
117
+ read grid: Grid.Grid,
118
+ }
119
+
120
+ export type Float = {
121
+ read op: "f32" | "f64",
122
+ read fixed: boolean,
123
+ read maxBytes: number,
124
+ read tag: Tag,
125
+ }
126
+
127
+ export type Varint = {
128
+ read op: "vlq" | "vli",
129
+ read fixed: boolean,
130
+ read maxBytes: number,
131
+ read tag: Tag,
132
+ }
133
+
134
+ export type Bytes = {
135
+ read op: "bytes",
136
+ read fixed: boolean,
137
+ read maxBytes: number,
138
+ read tag: Tag,
139
+ read count: Span,
140
+ read alphabet: string?,
141
+ read form: "string" | "buffer",
142
+ }
143
+
144
+ export type Run = {
145
+ read op: "run",
146
+ read fixed: boolean,
147
+ read maxBytes: number,
148
+ read tag: Tag,
149
+ read count: Span,
150
+ read elem: Grid.Grid,
151
+ }
152
+
153
+ export type List = {
154
+ read op: "list",
155
+ read fixed: boolean,
156
+ read maxBytes: number,
157
+ read tag: Tag,
158
+ read count: Span,
159
+ read elem: Shape,
160
+ }
161
+
162
+ -- A count and then that many pairs, each a key and then the value under it.
163
+ export type Pairs = {
164
+ read op: "pairs",
165
+ read fixed: boolean,
166
+ read maxBytes: number,
167
+ read tag: Tag,
168
+ read count: Span,
169
+ read key: Shape,
170
+ read value: Shape,
171
+ }
172
+
173
+ --[[
174
+ One thing after the region, and the field it is read off. Absent where the value handed to the
175
+ group is the part's own, which is a definition whose whole payload is one variable thing, and at
176
+ most one part of a group is keyless for the same reason at most one join is.
177
+ ]]
178
+ export type Part = {
179
+ read key: string?,
180
+ read nest: number?,
181
+ read of: Shape,
182
+ }
183
+
184
+ export type Group = {
185
+ read op: "group",
186
+ read fixed: boolean,
187
+ read maxBytes: number,
188
+ read tag: Tag,
189
+ read allow: { [string]: boolean }?,
190
+ read word: Word?,
191
+ read parts: { Part },
192
+ read nests: { Nest },
193
+ }
194
+
195
+ export type Opt = {
196
+ read op: "opt",
197
+ read fixed: boolean,
198
+ read maxBytes: number,
199
+ read tag: Tag,
200
+ read bit: number,
201
+ read inner: Shape,
202
+ }
203
+
204
+ --[[
205
+ A tag and the arms it chooses between. The field is the one the tag rides in, because a variant
206
+ is a record and its name is a field of it rather than something beside it, and the names are in
207
+ the order the arms are, which is canonical.
208
+ ]]
209
+ export type Branch = {
210
+ read op: "branch",
211
+ read fixed: boolean,
212
+ read maxBytes: number,
213
+ read tag: Tag,
214
+ read field: string,
215
+ read names: { string },
216
+ read tagWidth: number,
217
+ read arms: { Shape },
218
+ }
219
+
220
+ export type Slot = {
221
+ read op: "slot",
222
+ read fixed: boolean,
223
+ read maxBytes: number,
224
+ read tag: Tag,
225
+ read class: string?,
226
+ }
227
+
228
+ export type Xform = {
229
+ read op: "xform",
230
+ read fixed: boolean,
231
+ read maxBytes: number,
232
+ read tag: Tag,
233
+ read inner: Shape,
234
+ read lift: (wire: any) -> any,
235
+ read lower: (domain: any) -> any,
236
+ }
237
+
238
+ --[[
239
+ Every node there is. Closed, so the emitter's dispatch is exhaustive by the checker, and one
240
+ concrete record per op, so reading a field the op does not have is a compile error rather than a
241
+ nil at whatever rate a frame arrives.
242
+ ]]
243
+ export type Shape =
244
+ Word
245
+ | Wide
246
+ | Float
247
+ | Varint
248
+ | Bytes
249
+ | Run
250
+ | List
251
+ | Pairs
252
+ | Group
253
+ | Apart
254
+ | Opt
255
+ | Branch
256
+ | Slot
257
+ | Xform
258
+
259
+ -- Private -------------------------------------------------------------
260
+
261
+ --[[
262
+ The bits a count is written as, which is none where the bounds meet. A caller who declared one
263
+ length spends nothing saying it again, and the decoder has the number without reading one.
264
+ ]]
265
+ local function counted (of: Span): number return Bits.width (of.max - of.min) end
266
+
267
+ -- Public --------------------------------------------------------------
268
+
269
+ -- A bit region, its windows, and who fills them.
270
+ function Ir.word (tag: Tag, plan: { Words.Word }, leaves: { Grid.Grid }, joins: { Join }): Word
271
+ local bits = 0
272
+ for _, grid in leaves do
273
+ bits += grid.width
274
+ end
275
+
276
+ local bytes = 0
277
+ for _, one in plan do
278
+ bytes += one.bytes
279
+ end
280
+
281
+ return table.freeze ({
282
+ op = "word" :: "word",
283
+ fixed = true,
284
+ maxBytes = bytes,
285
+ tag = tag,
286
+ bits = bits,
287
+ plan = table.freeze (plan),
288
+ leaves = table.freeze (leaves),
289
+ joins = table.freeze (joins),
290
+ })
291
+ end
292
+
293
+ --[[
294
+ One fixed field too wide for the run accumulator to pack, written as whole bytes of its own. The
295
+ grid's width is the value's and the node's is what it costs, and past twenty-four bits those are
296
+ two different numbers.
297
+ ]]
298
+ function Ir.wide (tag: Tag, grid: Grid.Grid): Wide
299
+ return table.freeze ({
300
+ op = "wide" :: "wide",
301
+ fixed = true,
302
+ maxBytes = Bytes.whole (grid.width),
303
+ tag = tag,
304
+ grid = grid,
305
+ })
306
+ end
307
+
308
+ function Ir.float (tag: Tag, wide: boolean): Float
309
+ return table.freeze ({
310
+ op = (if wide then "f64" else "f32") :: "f64",
311
+ fixed = true,
312
+ maxBytes = if wide then 8 else 4,
313
+ tag = tag,
314
+ })
315
+ end
316
+
317
+ --[[
318
+ A varint. Never fixed, even where a caller's values would always fit one group, because what
319
+ decides the length is the value and a shape is what is known before there is one.
320
+ ]]
321
+ function Ir.varint (tag: Tag, signed: boolean): Varint
322
+ return table.freeze ({
323
+ op = (if signed then "vli" else "vlq") :: "vli",
324
+ fixed = false,
325
+ maxBytes = Protocol.VARINT_GROUPS,
326
+ tag = tag,
327
+ })
328
+ end
329
+
330
+ -- A string or a buffer, as its count and then its bytes.
331
+ function Ir.bytes (tag: Tag, count: Span, form: "string" | "buffer", alphabet: string?): Bytes
332
+ local width = if alphabet == nil then BYTE else Bits.width (#alphabet - 1)
333
+
334
+ return table.freeze ({
335
+ op = "bytes" :: "bytes",
336
+ fixed = count.min == count.max,
337
+ maxBytes = Bytes.whole (counted (count)) + Bytes.whole (count.max * width),
338
+ tag = tag,
339
+ count = table.freeze (count),
340
+ alphabet = alphabet,
341
+ form = form,
342
+ })
343
+ end
344
+
345
+ -- Elements of one width with no padding between them, which is the whole point of the 24-bit law.
346
+ function Ir.run (tag: Tag, count: Span, elem: Grid.Grid): Run
347
+ return table.freeze ({
348
+ op = "run" :: "run",
349
+ fixed = count.min == count.max,
350
+ maxBytes = Bytes.whole (counted (count)) + Bytes.whole (count.max * elem.width),
351
+ tag = tag,
352
+ count = table.freeze (count),
353
+ elem = elem,
354
+ })
355
+ end
356
+
357
+ --[[
358
+ Elements the run cannot take, each written whole and each starting on a byte. The worst case
359
+ multiplies the element's bytes rather than its bits, because every element pays its own
360
+ alignment whether or not the count ever varies.
361
+ ]]
362
+ function Ir.list (tag: Tag, count: Span, elem: Shape): List
363
+ return table.freeze ({
364
+ op = "list" :: "list",
365
+ fixed = count.min == count.max and elem.fixed,
366
+ maxBytes = Bytes.whole (counted (count)) + count.max * elem.maxBytes,
367
+ tag = tag,
368
+ count = table.freeze (count),
369
+ elem = elem,
370
+ })
371
+ end
372
+
373
+ --[[
374
+ A map, as its two halves and what it may hold between them.
375
+
376
+ The worst case multiplies the pair rather than either half, since a map of no pairs writes its
377
+ count and nothing else and a map of many writes both of them that many times.
378
+ ]]
379
+ function Ir.pairs (tag: Tag, count: Span, key: Shape, value: Shape): Pairs
380
+ return table.freeze ({
381
+ op = "pairs" :: "pairs",
382
+ fixed = count.min == count.max and key.fixed and value.fixed,
383
+ maxBytes = Bytes.whole (counted (count)) + count.max * (key.maxBytes + value.maxBytes),
384
+ tag = tag,
385
+ count = table.freeze (count),
386
+ key = key,
387
+ value = value,
388
+ })
389
+ end
390
+
391
+ -- A region sequence: the bit region as one word, then everything after it in wire order.
392
+ function Ir.group (
393
+ tag: Tag,
394
+ word: Word?,
395
+ parts: { Part },
396
+ nests: { Nest }?,
397
+ allow: { [string]: boolean }?
398
+ ): Group
399
+ local fixed = true
400
+ local maxBytes = if word == nil then 0 else word.maxBytes
401
+
402
+ for _, part in parts do
403
+ maxBytes += part.of.maxBytes
404
+ if part.of.fixed then continue end
405
+ fixed = false
406
+ end
407
+
408
+ return table.freeze ({
409
+ op = "group" :: "group",
410
+ fixed = fixed,
411
+ maxBytes = maxBytes,
412
+ tag = tag,
413
+ allow = if allow == nil then nil else table.freeze (allow),
414
+ word = word,
415
+ parts = table.freeze (parts),
416
+ nests = if nests == nil then NO_NESTS else table.freeze (nests),
417
+ })
418
+ end
419
+
420
+ --[[
421
+ The computation that puts one of those values back together out of its parts.
422
+
423
+ Going out is a lookup and coming back is a call, which is the engine's asymmetry rather than a
424
+ choice here: its datatypes carry their parts as properties and are built whole or not at all.
425
+ ]]
426
+ export type Build = (from: { any }) -> any
427
+
428
+ -- A value that comes apart into whole-byte fields.
429
+ export type Apart = {
430
+ read op: "apart",
431
+ read fixed: boolean,
432
+ read maxBytes: number,
433
+ read tag: Tag,
434
+ read engine: string,
435
+ read keys: { string },
436
+ read parts: { Shape },
437
+ read push: Build,
438
+ }
439
+
440
+ -- A value and its parts, in the order they are written.
441
+ function Ir.apart (tag: Tag, engine: string, keys: { string }, parts: { Shape }, push: Build): Apart
442
+ local fixed = true
443
+ local maxBytes = 0
444
+
445
+ for _, of in parts do
446
+ maxBytes += of.maxBytes
447
+ if of.fixed then continue end
448
+ fixed = false
449
+ end
450
+
451
+ return table.freeze ({
452
+ op = "apart" :: "apart",
453
+ fixed = fixed,
454
+ maxBytes = maxBytes,
455
+ tag = tag,
456
+ engine = engine,
457
+ keys = table.freeze (keys),
458
+ parts = table.freeze (parts),
459
+ push = push,
460
+ })
461
+ end
462
+
463
+ --[[
464
+ A value that may not be there, and the bit in the enclosing region that says so. The bit is
465
+ named by the leaf it is rather than read back out of the buffer, because the group has already
466
+ decoded the whole region into locals by the time this is reached.
467
+ ]]
468
+ function Ir.opt (tag: Tag, bit: number, inner: Shape): Opt
469
+ return table.freeze ({
470
+ op = "opt" :: "opt",
471
+ fixed = false,
472
+ maxBytes = inner.maxBytes,
473
+ tag = tag,
474
+ bit = bit,
475
+ inner = inner,
476
+ })
477
+ end
478
+
479
+ --[[
480
+ A tag and the arm it names. The width is the ranged int the arm count needs rather than a number
481
+ handed in, because the two could not disagree and one of them would be the copy.
482
+ ]]
483
+ function Ir.branch (tag: Tag, field: string, names: { string }, arms: { Shape }): Branch
484
+ local worst = 0
485
+ for _, arm in arms do
486
+ worst = math.max (worst, arm.maxBytes)
487
+ end
488
+
489
+ local tagWidth = Bits.width (#arms - 1)
490
+
491
+ return table.freeze ({
492
+ op = "branch" :: "branch",
493
+ fixed = false,
494
+ maxBytes = Bytes.whole (tagWidth) + worst,
495
+ tag = tag,
496
+ field = field,
497
+ names = table.freeze (names),
498
+ tagWidth = tagWidth,
499
+ arms = table.freeze (arms),
500
+ })
501
+ end
502
+
503
+ --[[
504
+ An instance, as the index of the slot it takes in the frame's sidecar. Its presence bit is not
505
+ here: one that may be absent is this node inside an `opt`, which is what the surface says about
506
+ every other value that may be absent.
507
+ ]]
508
+ function Ir.slot (tag: Tag, class: string?): Slot
509
+ return table.freeze ({
510
+ op = "slot" :: "slot",
511
+ fixed = false,
512
+ maxBytes = Protocol.VARINT_GROUPS,
513
+ tag = tag,
514
+ class = class,
515
+ })
516
+ end
517
+
518
+ --[[
519
+ A value on its way to and from the one the wire carries. It is the same bytes as its inner node
520
+ and says so by taking both facts off it, which is what makes a transform free to the layout and
521
+ impossible to see in a golden.
522
+ ]]
523
+ function Ir.xform (
524
+ tag: Tag,
525
+ inner: Shape,
526
+ lift: (wire: any) -> any,
527
+ lower: (domain: any) -> any
528
+ ): Xform
529
+ return table.freeze ({
530
+ op = "xform" :: "xform",
531
+ fixed = inner.fixed,
532
+ maxBytes = inner.maxBytes,
533
+ tag = tag,
534
+ inner = inner,
535
+ lift = lift,
536
+ lower = lower,
537
+ })
538
+ end
539
+
540
+ return table.freeze (Ir)
@@ -0,0 +1,125 @@
1
+ --!strict
2
+ --!optimize 2
3
+ -- One declaration as the leaves it takes in a bit region and the join that fills them.
4
+
5
+ local Base = require (script.Parent.Parent.codec.Base)
6
+ local Grid = require (script.Parent.Grid)
7
+ local Ir = require (script.Parent.Ir)
8
+ local Scalars = require (script.Parent.Parent.codec.Scalars)
9
+
10
+ local Joins = {}
11
+
12
+ -- Types ---------------------------------------------------------------
13
+
14
+ --[[
15
+ What a region field comes to: the grids it occupies, in wire order, and the join that says where
16
+ they are and how a value gets in and out of them.
17
+ ]]
18
+ export type Field = {
19
+ read leaves: { Grid.Grid },
20
+ read join: Ir.Join,
21
+ }
22
+
23
+ -- Public --------------------------------------------------------------
24
+
25
+ -- The one grid a declaration indexes through, where it has one.
26
+ function Joins.grid (of: Base.Cell): Grid.Grid?
27
+ local kind, params = of.kind, of.params
28
+
29
+ if kind == "bool" then return Grid.flag () end
30
+ if kind == "int" then return Grid.of (params.min :: number, params.max :: number, 1) end
31
+ if kind == "quant" then
32
+ return Grid.of (params.min :: number, params.max :: number, params.step :: number)
33
+ end
34
+
35
+ if kind == "angle" then
36
+ --[[
37
+ A step short of a turn, since the wrap makes the top point and the bottom the same one,
38
+ and cyclic so a reading is folded into the range rather than clamped to either end.
39
+ ]]
40
+ local step = params.step :: number
41
+ return Grid.of (0, Scalars.TURN - step, step, true)
42
+ end
43
+
44
+ if kind == "enum" then return Grid.named (params.names :: { string }) end
45
+ return nil
46
+ end
47
+
48
+ -- The leaves a declaration takes, where its value is one the language carries.
49
+ function Joins.leaves (of: Base.Cell): { Grid.Grid }?
50
+ local single = Joins.grid (of)
51
+ if single ~= nil then return { single } end
52
+ if of.kind ~= "bitfield" then return nil end
53
+
54
+ local names = of.params.names :: { string }
55
+ local reach = #names
56
+
57
+ local out = table.create (reach) :: { Grid.Grid }
58
+ for place = 1, reach do
59
+ out[place] = Grid.flag ()
60
+ end
61
+ return out
62
+ end
63
+
64
+ -- A declaration, at the leaf it starts on, as the field it becomes.
65
+ function Joins.of (of: Base.Cell, tag: Ir.Tag, at: number, key: string?, nest: number?): Field?
66
+ local leaves = Joins.leaves (of)
67
+ if leaves == nil then return nil end
68
+
69
+ if of.kind ~= "bitfield" then
70
+ return {
71
+ leaves = leaves,
72
+ join = { at = at, count = 1, key = key, nest = nest, tag = tag },
73
+ }
74
+ end
75
+
76
+ --[[
77
+ A bitfield's pair reads and writes by name rather than by position, so the leaves are its
78
+ names in the order the cell records them and nothing about the record's own key order
79
+ reaches the wire. A name a record does not carry is not there, which is what a truth of
80
+ false already means, and a value that is no record has no name to read at all.
81
+ ]]
82
+ local names = of.params.names :: { string }
83
+ local reach = #names
84
+
85
+ local function pull (value: any, into: { number }, opens: number): boolean
86
+ if type (value) ~= "table" then return false end
87
+
88
+ --[[
89
+ A name it does carry has to carry a truth. A flag is a `bool` declared n at a time and
90
+ `bool` turns away everything that is not one, so asking only whether the value is truthy
91
+ would let `{ dead = "no" }` write that the thing is dead. The whole field goes rather
92
+ than the flag, because a bitfield's twentieth bit is still the bitfield.
93
+ ]]
94
+ for place = 1, reach do
95
+ local held = value[names[place] ]
96
+ if held ~= nil and type (held) ~= "boolean" then return false end
97
+ into[opens + place - 1] = if held then 1 else 0
98
+ end
99
+ return true
100
+ end
101
+
102
+ local function push (from: { number }, opens: number): any
103
+ local out = table.create (reach) :: { [string]: boolean }
104
+
105
+ for place = 1, reach do
106
+ out[names[place] ] = from[opens + place - 1] == 1
107
+ end
108
+ return out
109
+ end
110
+
111
+ return {
112
+ leaves = leaves,
113
+ join = {
114
+ at = at,
115
+ count = reach,
116
+ key = key,
117
+ nest = nest,
118
+ tag = tag,
119
+ pull = pull,
120
+ push = push,
121
+ },
122
+ }
123
+ end
124
+
125
+ return table.freeze (Joins)